diff --git a/install/fs/docker-compose.yml b/install/fs/docker-compose.yml index 47fd82f8..2adcee58 100644 --- a/install/fs/docker-compose.yml +++ b/install/fs/docker-compose.yml @@ -1,6 +1,6 @@ services: pangolin: - image: fosrl/pangolin:latest + image: fosrl/pangolin:{{.PangolinVersion}} container_name: pangolin restart: unless-stopped volumes: @@ -12,7 +12,7 @@ services: retries: 5 gerbil: - image: fosrl/gerbil:latest + image: fosrl/gerbil:{{.GerbilVersion}} container_name: gerbil restart: unless-stopped depends_on: diff --git a/install/go.mod b/install/go.mod index 3de61fa9..85cf49e4 100644 --- a/install/go.mod +++ b/install/go.mod @@ -1,3 +1,8 @@ module installer -go 1.23.0 \ No newline at end of file +go 1.23.0 + +require ( + golang.org/x/sys v0.29.0 // indirect + golang.org/x/term v0.28.0 // indirect +) diff --git a/install/go.sum b/install/go.sum index e69de29b..f05f63b4 100644 --- a/install/go.sum +++ b/install/go.sum @@ -0,0 +1,4 @@ +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= diff --git a/install/main.go b/install/main.go index 480ff934..486137a1 100644 --- a/install/main.go +++ b/install/main.go @@ -10,27 +10,37 @@ import ( "path/filepath" "runtime" "strings" + "syscall" "text/template" "unicode" + + "golang.org/x/term" ) +func loadVersions(config *Config) { + config.PangolinVersion = "1.0.0-beta.4" + config.GerbilVersion = "1.0.0-beta.1" +} + //go:embed fs/* var configFiles embed.FS type Config struct { - BaseDomain string `yaml:"baseDomain"` - DashboardDomain string `yaml:"dashboardUrl"` - LetsEncryptEmail string `yaml:"letsEncryptEmail"` - AdminUserEmail string `yaml:"adminUserEmail"` - AdminUserPassword string `yaml:"adminUserPassword"` - DisableSignupWithoutInvite bool `yaml:"disableSignupWithoutInvite"` - DisableUserCreateOrg bool `yaml:"disableUserCreateOrg"` - EnableEmail bool `yaml:"enableEmail"` - EmailSMTPHost string `yaml:"emailSMTPHost"` - EmailSMTPPort int `yaml:"emailSMTPPort"` - EmailSMTPUser string `yaml:"emailSMTPUser"` - EmailSMTPPass string `yaml:"emailSMTPPass"` - EmailNoReply string `yaml:"emailNoReply"` + PangolinVersion string + GerbilVersion string + BaseDomain string + DashboardDomain string + LetsEncryptEmail string + AdminUserEmail string + AdminUserPassword string + DisableSignupWithoutInvite bool + DisableUserCreateOrg bool + EnableEmail bool + EmailSMTPHost string + EmailSMTPPort int + EmailSMTPUser string + EmailSMTPPass string + EmailNoReply string } func main() { @@ -45,6 +55,9 @@ func main() { // check if there is already a config file if _, err := os.Stat("config/config.yml"); err != nil { config := collectUserInput(reader) + + loadVersions(&config) + if err := createConfigFiles(config); err != nil { fmt.Printf("Error creating config files: %v\n", err) os.Exit(1) @@ -82,6 +95,24 @@ func readString(reader *bufio.Reader, prompt string, defaultValue string) string return input } +func readPassword(prompt string) string { + fmt.Print(prompt + ": ") + + // Read password without echo + password, err := term.ReadPassword(int(syscall.Stdin)) + fmt.Println() // Add a newline since ReadPassword doesn't add one + + if err != nil { + return "" + } + + input := strings.TrimSpace(string(password)) + if input == "" { + return readPassword(prompt) + } + return input +} + func readBool(reader *bufio.Reader, prompt string, defaultValue bool) bool { defaultStr := "no" if defaultValue { @@ -114,16 +145,23 @@ func collectUserInput(reader *bufio.Reader) Config { fmt.Println("\n=== Admin User Configuration ===") config.AdminUserEmail = readString(reader, "Enter admin user email", "admin@"+config.BaseDomain) for { - config.AdminUserPassword = readString(reader, "Enter admin user password", "") - if valid, message := validatePassword(config.AdminUserPassword); valid { - break + pass1 := readPassword("Create admin user password") + pass2 := readPassword("Confirm admin user password") + + if pass1 != pass2 { + fmt.Println("Passwords do not match") } else { - fmt.Println("Invalid password:", message) - fmt.Println("Password requirements:") - fmt.Println("- At least one uppercase English letter") - fmt.Println("- At least one lowercase English letter") - fmt.Println("- At least one digit") - fmt.Println("- At least one special character") + config.AdminUserPassword = pass1 + if valid, message := validatePassword(config.AdminUserPassword); valid { + break + } else { + fmt.Println("Invalid password:", message) + fmt.Println("Password requirements:") + fmt.Println("- At least one uppercase English letter") + fmt.Println("- At least one lowercase English letter") + fmt.Println("- At least one digit") + fmt.Println("- At least one special character") + } } }