First version
This commit is contained in:
commit
407a8d5052
140
cmd/hoster_cli/hoster.go
Normal file
140
cmd/hoster_cli/hoster.go
Normal file
@ -0,0 +1,140 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hoster/internals/conf"
|
||||
"hoster/internals/helpers"
|
||||
"hoster/internals/templates"
|
||||
"hoster/internals/types"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gookit/goutil/fsutil"
|
||||
"github.com/gookit/goutil/sysutil/cmdr"
|
||||
"github.com/kataras/golog"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func main() {
|
||||
conf.LoadConfig("config.yml")
|
||||
|
||||
app := &cli.App{
|
||||
Name: "hoster",
|
||||
Usage: "Hoster",
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "new",
|
||||
Usage: "Setup a new hosting project.",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
Aliases: []string{"n"},
|
||||
Usage: "The name of the project",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "domain",
|
||||
Aliases: []string{"d"},
|
||||
Usage: "The description of the project",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
name := c.String("name")
|
||||
domain := c.String("domain")
|
||||
projectRoot := conf.App.MustString("projects.root")
|
||||
sitesAvail := conf.App.MustString("nginx.sitesAvailable")
|
||||
sitesEnabled := conf.App.MustString("nginx.sitesEnabled")
|
||||
nginxFile := filepath.Join(sitesAvail, name)
|
||||
|
||||
projectFolder := filepath.Join(projectRoot, name)
|
||||
|
||||
existingProjects, err := helpers.FindHostConfigs(sitesEnabled)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
usedPorts := []int{}
|
||||
for _, project := range existingProjects {
|
||||
usedPorts = append(usedPorts, project.InternalPort)
|
||||
}
|
||||
|
||||
port := conf.App.Int("nginx.portRangeStart")
|
||||
freePort := 0
|
||||
for port <= conf.App.Int("nginx.portRangeEnd") {
|
||||
if !slices.Contains(usedPorts, port) {
|
||||
freePort = port
|
||||
break
|
||||
}
|
||||
port++
|
||||
}
|
||||
|
||||
if freePort == 0 {
|
||||
return fmt.Errorf("no free port available")
|
||||
}
|
||||
|
||||
if !fsutil.IsDir(projectFolder) {
|
||||
golog.Infof("Creating project folder '%s'", projectFolder)
|
||||
err := os.MkdirAll(projectFolder, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if fsutil.IsFile(nginxFile) {
|
||||
return fmt.Errorf("the nginx config file '%s' already exists", nginxFile)
|
||||
}
|
||||
|
||||
// Set owner and group of project folder
|
||||
err = os.Chown(projectFolder, conf.App.Int("projects.owner"), conf.App.Int("projects.group"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
project := types.Project{
|
||||
Name: name,
|
||||
Domain: domain,
|
||||
InternalPort: freePort,
|
||||
}
|
||||
|
||||
// Create nginx config file
|
||||
err = templates.Execute("nginx", nginxFile, project)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Symlink nginx config file
|
||||
err = os.Symlink(nginxFile, filepath.Join(sitesEnabled, name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Execute certbot command: "systemctl stop nginx && sudo certbot certonly --standalone --preferred-challenges http -d {domain} && sudo systemctl start nginx"
|
||||
|
||||
com := cmdr.NewCmd("systemctl").
|
||||
WithArgs([]string{"stop", "nginx"})
|
||||
out := com.SafeOutput()
|
||||
fmt.Println(out)
|
||||
|
||||
com = cmdr.NewCmd("certbot").
|
||||
WithArgs([]string{"certonly", "--standalone", "--preferred-challenges", "http", "-d", domain})
|
||||
out = com.SafeOutput()
|
||||
fmt.Println(out)
|
||||
|
||||
com = cmdr.NewCmd("systemctl").
|
||||
WithArgs([]string{"start", "nginx"})
|
||||
out = com.SafeOutput()
|
||||
fmt.Println(out)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
golog.Fatal(err)
|
||||
}
|
||||
}
|
11
config.yml
Normal file
11
config.yml
Normal file
@ -0,0 +1,11 @@
|
||||
nginx:
|
||||
sitesAvailable: /home/pk/golang/hoster/fake/sites-available
|
||||
sitesEnabled: /home/pk/golang/hoster/fake/sites-enabled
|
||||
portRangeStart: 10000
|
||||
portRangeEnd: 30000
|
||||
projects:
|
||||
root: /home/pk/golang/hoster/fake
|
||||
owner: 1000
|
||||
group: 1000
|
||||
templates:
|
||||
nginx: host.tpl
|
31
go.mod
Normal file
31
go.mod
Normal file
@ -0,0 +1,31 @@
|
||||
module hoster
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/gookit/config/v2 v2.2.5
|
||||
github.com/gookit/goutil v0.6.16
|
||||
github.com/kataras/golog v0.1.12
|
||||
github.com/urfave/cli/v2 v2.27.4
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/fatih/color v1.14.1 // indirect
|
||||
github.com/goccy/go-yaml v1.11.2 // indirect
|
||||
github.com/gookit/color v1.5.4 // indirect
|
||||
github.com/kataras/pio v0.0.13 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/term v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
)
|
69
go.sum
Normal file
69
go.sum
Normal file
@ -0,0 +1,69 @@
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
|
||||
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/goccy/go-yaml v1.11.2 h1:joq77SxuyIs9zzxEjgyLBugMQ9NEgTWxXfz2wVqwAaQ=
|
||||
github.com/goccy/go-yaml v1.11.2/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||
github.com/gookit/config/v2 v2.2.5 h1:RECbYYbtherywmzn3LNeu9NA5ZqhD7MSKEMsJ7l+MpU=
|
||||
github.com/gookit/config/v2 v2.2.5/go.mod h1:NeX+yiNYn6Ei10eJvCQFXuHEPIE/IPS8bqaFIsszzaM=
|
||||
github.com/gookit/goutil v0.6.16 h1:9fRMCF4X9abdRD5+2HhBS/GwafjBlTUBjRtA5dgkvuw=
|
||||
github.com/gookit/goutil v0.6.16/go.mod h1:op2q8AoPDFSiY2+qkHxcBWQMYxOLQ1GbLXqe7vrwscI=
|
||||
github.com/gookit/ini/v2 v2.2.3 h1:nSbN+x9OfQPcMObTFP+XuHt8ev6ndv/fWWqxFhPMu2E=
|
||||
github.com/gookit/ini/v2 v2.2.3/go.mod h1:Vu6p7P7xcfmb8KYu3L0ek8bqu/Im63N81q208SCCZY4=
|
||||
github.com/kataras/golog v0.1.12 h1:Bu7I/G4ilJlbfzjmU39O9N+2uO1pBcMK045fzZ4ytNg=
|
||||
github.com/kataras/golog v0.1.12/go.mod h1:wrGSbOiBqbQSQznleVNX4epWM8rl9SJ/rmEacl0yqy4=
|
||||
github.com/kataras/pio v0.0.13 h1:x0rXVX0fviDTXOOLOmr4MUxOabu1InVSTu5itF8CXCM=
|
||||
github.com/kataras/pio v0.0.13/go.mod h1:k3HNuSw+eJ8Pm2lA4lRhg3DiCjVgHlP8hmXApSej3oM=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8=
|
||||
github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
22
host.tpl
Normal file
22
host.tpl
Normal file
@ -0,0 +1,22 @@
|
||||
server {
|
||||
listen 443 ssl http2; listen [::]:443 ssl http2;
|
||||
server_name {{.Domain}};
|
||||
|
||||
ssl on;
|
||||
ssl_certificate /etc/letsencrypt/live/{{.Domain}}/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/{{.Domain}}/privkey.pem;
|
||||
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
http2_idle_timeout 5m; # up from 3m default
|
||||
client_max_body_size 20000M;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:{{.InternalPort}}/;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection upgrade;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
}
|
24
internals/conf/loader.go
Normal file
24
internals/conf/loader.go
Normal file
@ -0,0 +1,24 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"github.com/gookit/config/v2"
|
||||
"github.com/gookit/config/v2/yaml"
|
||||
"github.com/kataras/golog"
|
||||
)
|
||||
|
||||
var cfgPath string
|
||||
var App *config.Config
|
||||
|
||||
func LoadConfig(path string) *config.Config {
|
||||
App = config.New("appCfg")
|
||||
cfgPath = path
|
||||
App.AddDriver(yaml.Driver)
|
||||
|
||||
err := App.LoadFiles(path)
|
||||
|
||||
if err != nil {
|
||||
golog.Fatalf("Error loading config: %v", err)
|
||||
}
|
||||
|
||||
return App
|
||||
}
|
69
internals/helpers/findHostConfigs.go
Normal file
69
internals/helpers/findHostConfigs.go
Normal file
@ -0,0 +1,69 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"hoster/internals/types"
|
||||
"io/fs"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gookit/goutil/fsutil"
|
||||
)
|
||||
|
||||
func FindHostConfigs(path string) ([]types.Project, error) {
|
||||
projects := []types.Project{}
|
||||
|
||||
fsutil.FindInDir(path, func(filePath string, de fs.DirEntry) error {
|
||||
project := types.Project{}
|
||||
parseHostFile(filePath, &project)
|
||||
|
||||
if project.Domain != "" {
|
||||
projects = append(projects, project)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return projects, nil
|
||||
}
|
||||
|
||||
var portRegex = regexp.MustCompile(`listen\s+(\d+)`)
|
||||
var serverNameRegex = regexp.MustCompile(`server_name\s+(.+);`)
|
||||
|
||||
func parseHostFile(filePath string, project *types.Project) error {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
|
||||
if strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, "listen ") {
|
||||
port := portRegex.FindStringSubmatch(line)
|
||||
project.InternalPort, err = strconv.Atoi(port[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, "server_name ") {
|
||||
serverName := serverNameRegex.FindStringSubmatch(line)
|
||||
project.Domain = serverName[1]
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
7
internals/helpers/root.go
Normal file
7
internals/helpers/root.go
Normal file
@ -0,0 +1,7 @@
|
||||
package helpers
|
||||
|
||||
import "os"
|
||||
|
||||
func IsRootUser() bool {
|
||||
return os.Geteuid() == 0
|
||||
}
|
40
internals/templates/templates.go
Normal file
40
internals/templates/templates.go
Normal file
@ -0,0 +1,40 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"hoster/internals/conf"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
|
||||
"github.com/gookit/goutil/fsutil"
|
||||
)
|
||||
|
||||
func Execute(name string, outPath string, context any) error {
|
||||
tplPath := filepath.Join(conf.App.MustString(fmt.Sprintf("templates.%s", name)))
|
||||
|
||||
if !fsutil.IsFile(tplPath) {
|
||||
return fmt.Errorf("template file \"%s\" doesn't exist", tplPath)
|
||||
}
|
||||
|
||||
outFile, err := os.Create(outPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer outFile.Close()
|
||||
|
||||
tpl, err := os.ReadFile(tplPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = template.Must(template.New("tpl").Parse(string(tpl))).Execute(outFile, context)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
8
internals/types/project.go
Normal file
8
internals/types/project.go
Normal file
@ -0,0 +1,8 @@
|
||||
package types
|
||||
|
||||
type Project struct {
|
||||
Name string
|
||||
Domain string
|
||||
HostConfigFile string
|
||||
InternalPort int
|
||||
}
|
Loading…
Reference in New Issue
Block a user