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(".hoster.yml") app := &cli.App{ Name: "hoster", Usage: "Hoster", Version: "1.0.0", 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(sitesAvail) 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) } }