From 07fe366c15836cd24d688848ecb501556716acce Mon Sep 17 00:00:00 2001 From: Michael Demetriou Date: Thu, 20 Jun 2019 23:41:03 +0300 Subject: [PATCH] Fix T657: add --sections argument to allow partial configuration. Use the split argument list (slice) just for validation purposes as it's substantially easier to do `.contains` in a string instead of a slice. As such, pass the `configSections` arguments to `Configure()` and check the existence of each one before showing the options to the user. An empty argument list is replaced by "server db app" so everything is there negating the need to check anything else in `Configure()`. In the same vein the default is "server db app". The parsing is done in `app.go` alongside the other flags instead of `main.go` as described in T657. --- app.go | 5 +- config/setup.go | 464 +++++++++++++++++++++++++----------------------- 2 files changed, 239 insertions(+), 230 deletions(-) diff --git a/app.go b/app.go index f7c8d08..23286b0 100644 --- a/app.go +++ b/app.go @@ -235,15 +235,14 @@ func Serve() { if *configSections == "" { *configSections = "server db app" } - configSectionsArray := strings.Split(*configSections, " ") - // let's check there aren't any garbage in the list + configSectionsArray := strings.Split(*configSections, " ") for _, element := range configSectionsArray { if element != "server" && element != "db" && element != "app" { log.Error("Invalid argument to --sections. Valid arguments are only \"server\", \"db\" and \"app\"") } } - d, err := config.Configure(app.cfgFile, configSectionsArray) + d, err := config.Configure(app.cfgFile, *configSections) if err != nil { log.Error("Unable to configure: %v", err) os.Exit(1) diff --git a/config/setup.go b/config/setup.go index 5d806ea..7f5030b 100644 --- a/config/setup.go +++ b/config/setup.go @@ -17,6 +17,7 @@ import ( "github.com/mitchellh/go-wordwrap" "github.com/writeas/web-core/auth" "strconv" + "strings" ) type SetupData struct { @@ -24,7 +25,7 @@ type SetupData struct { Config *Config } -func Configure(fname string, configSections []string) (*SetupData, error) { +func Configure(fname string, configSections string) (*SetupData, error) { data := &SetupData{} var err error if fname == "" { @@ -52,9 +53,6 @@ func Configure(fname string, configSections []string) (*SetupData, error) { fmt.Println(wordwrap.WrapString(" This quick configuration process will "+action+" the application's config file, "+fname+".\n\n It validates your input along the way, so you can be sure any future errors aren't caused by a bad configuration. If you'd rather configure your server manually, instead run: writefreely --create-config and edit that file.", 75)) fmt.Println() - title(" Server setup ") - fmt.Println() - tmpls := &promptui.PromptTemplates{ Success: "{{ . | bold | faint }}: ", } @@ -78,284 +76,296 @@ func Configure(fname string, configSections []string) (*SetupData, error) { data.Config.Server.Dev = isDevEnv var prompt promptui.Prompt - if isDevEnv || !isStandalone { - // Running in dev environment or behind reverse proxy; ask for port - prompt = promptui.Prompt{ - Templates: tmpls, - Label: "Local port", - Validate: validatePort, - Default: fmt.Sprintf("%d", data.Config.Server.Port), + + if strings.Contains(configSections, "server"){ + + title(" Server setup ") + fmt.Println() + + if isDevEnv || !isStandalone { + // Running in dev environment or behind reverse proxy; ask for port + prompt = promptui.Prompt{ + Templates: tmpls, + Label: "Local port", + Validate: validatePort, + Default: fmt.Sprintf("%d", data.Config.Server.Port), + } + port, err := prompt.Run() + if err != nil { + return data, err + } + data.Config.Server.Port, _ = strconv.Atoi(port) // Ignore error, as we've already validated number } - port, err := prompt.Run() - if err != nil { - return data, err + + if isStandalone { + selPrompt = promptui.Select{ + Templates: selTmpls, + Label: "Web server mode", + Items: []string{"Insecure (port 80)", "Secure (port 443)"}, + } + sel, _, err := selPrompt.Run() + if err != nil { + return data, err + } + if sel == 0 { + data.Config.Server.Port = 80 + data.Config.Server.TLSCertPath = "" + data.Config.Server.TLSKeyPath = "" + } else if sel == 1 { + data.Config.Server.Port = 443 + + prompt = promptui.Prompt{ + Templates: tmpls, + Label: "Certificate path", + Validate: validateNonEmpty, + Default: data.Config.Server.TLSCertPath, + } + data.Config.Server.TLSCertPath, err = prompt.Run() + if err != nil { + return data, err + } + + prompt = promptui.Prompt{ + Templates: tmpls, + Label: "Key path", + Validate: validateNonEmpty, + Default: data.Config.Server.TLSKeyPath, + } + data.Config.Server.TLSKeyPath, err = prompt.Run() + if err != nil { + return data, err + } + } + } else { + data.Config.Server.TLSCertPath = "" + data.Config.Server.TLSKeyPath = "" } - data.Config.Server.Port, _ = strconv.Atoi(port) // Ignore error, as we've already validated number + + fmt.Println() } - - if isStandalone { + if strings.Contains(configSections, "db"){ + title(" Database setup ") + fmt.Println() + selPrompt = promptui.Select{ Templates: selTmpls, - Label: "Web server mode", - Items: []string{"Insecure (port 80)", "Secure (port 443)"}, + Label: "Database driver", + Items: []string{"MySQL", "SQLite"}, } sel, _, err := selPrompt.Run() if err != nil { return data, err } + if sel == 0 { - data.Config.Server.Port = 80 - data.Config.Server.TLSCertPath = "" - data.Config.Server.TLSKeyPath = "" - } else if sel == 1 { - data.Config.Server.Port = 443 - + // Configure for MySQL + data.Config.UseMySQL(isNewCfg) + prompt = promptui.Prompt{ Templates: tmpls, - Label: "Certificate path", + Label: "Username", Validate: validateNonEmpty, - Default: data.Config.Server.TLSCertPath, + Default: data.Config.Database.User, } - data.Config.Server.TLSCertPath, err = prompt.Run() + data.Config.Database.User, err = prompt.Run() if err != nil { return data, err } - + prompt = promptui.Prompt{ Templates: tmpls, - Label: "Key path", + Label: "Password", Validate: validateNonEmpty, - Default: data.Config.Server.TLSKeyPath, + Default: data.Config.Database.Password, + Mask: '*', } - data.Config.Server.TLSKeyPath, err = prompt.Run() + data.Config.Database.Password, err = prompt.Run() + if err != nil { + return data, err + } + + prompt = promptui.Prompt{ + Templates: tmpls, + Label: "Database name", + Validate: validateNonEmpty, + Default: data.Config.Database.Database, + } + data.Config.Database.Database, err = prompt.Run() + if err != nil { + return data, err + } + + prompt = promptui.Prompt{ + Templates: tmpls, + Label: "Host", + Validate: validateNonEmpty, + Default: data.Config.Database.Host, + } + data.Config.Database.Host, err = prompt.Run() + if err != nil { + return data, err + } + + prompt = promptui.Prompt{ + Templates: tmpls, + Label: "Port", + Validate: validatePort, + Default: fmt.Sprintf("%d", data.Config.Database.Port), + } + dbPort, err := prompt.Run() + if err != nil { + return data, err + } + data.Config.Database.Port, _ = strconv.Atoi(dbPort) // Ignore error, as we've already validated number + } else if sel == 1 { + // Configure for SQLite + data.Config.UseSQLite(isNewCfg) + + prompt = promptui.Prompt{ + Templates: tmpls, + Label: "Filename", + Validate: validateNonEmpty, + Default: data.Config.Database.FileName, + } + data.Config.Database.FileName, err = prompt.Run() if err != nil { return data, err } } - } else { - data.Config.Server.TLSCertPath = "" - data.Config.Server.TLSKeyPath = "" - } - - fmt.Println() - title(" Database setup ") - fmt.Println() - - selPrompt = promptui.Select{ - Templates: selTmpls, - Label: "Database driver", - Items: []string{"MySQL", "SQLite"}, - } - sel, _, err := selPrompt.Run() - if err != nil { - return data, err + + fmt.Println() } + if strings.Contains(configSections, "app"){ - if sel == 0 { - // Configure for MySQL - data.Config.UseMySQL(isNewCfg) - - prompt = promptui.Prompt{ - Templates: tmpls, - Label: "Username", - Validate: validateNonEmpty, - Default: data.Config.Database.User, - } - data.Config.Database.User, err = prompt.Run() - if err != nil { - return data, err - } - - prompt = promptui.Prompt{ - Templates: tmpls, - Label: "Password", - Validate: validateNonEmpty, - Default: data.Config.Database.Password, - Mask: '*', - } - data.Config.Database.Password, err = prompt.Run() - if err != nil { - return data, err - } - - prompt = promptui.Prompt{ - Templates: tmpls, - Label: "Database name", - Validate: validateNonEmpty, - Default: data.Config.Database.Database, - } - data.Config.Database.Database, err = prompt.Run() - if err != nil { - return data, err - } - - prompt = promptui.Prompt{ - Templates: tmpls, - Label: "Host", - Validate: validateNonEmpty, - Default: data.Config.Database.Host, - } - data.Config.Database.Host, err = prompt.Run() - if err != nil { - return data, err - } - - prompt = promptui.Prompt{ - Templates: tmpls, - Label: "Port", - Validate: validatePort, - Default: fmt.Sprintf("%d", data.Config.Database.Port), + title(" App setup ") + fmt.Println() + + selPrompt = promptui.Select{ + Templates: selTmpls, + Label: "Site type", + Items: []string{"Single user blog", "Multi-user instance"}, } - dbPort, err := prompt.Run() + _, usersType, err := selPrompt.Run() if err != nil { return data, err } - data.Config.Database.Port, _ = strconv.Atoi(dbPort) // Ignore error, as we've already validated number - } else if sel == 1 { - // Configure for SQLite - data.Config.UseSQLite(isNewCfg) - - prompt = promptui.Prompt{ - Templates: tmpls, - Label: "Filename", - Validate: validateNonEmpty, - Default: data.Config.Database.FileName, + data.Config.App.SingleUser = usersType == "Single user blog" + + if data.Config.App.SingleUser { + data.User = &UserCreation{} + + // prompt for username + prompt = promptui.Prompt{ + Templates: tmpls, + Label: "Admin username", + Validate: validateNonEmpty, + } + data.User.Username, err = prompt.Run() + if err != nil { + return data, err + } + + // prompt for password + prompt = promptui.Prompt{ + Templates: tmpls, + Label: "Admin password", + Validate: validateNonEmpty, + } + newUserPass, err := prompt.Run() + if err != nil { + return data, err + } + + data.User.HashedPass, err = auth.HashPass([]byte(newUserPass)) + if err != nil { + return data, err + } } - data.Config.Database.FileName, err = prompt.Run() - if err != nil { - return data, err + + siteNameLabel := "Instance name" + if data.Config.App.SingleUser { + siteNameLabel = "Blog name" } - } - - fmt.Println() - title(" App setup ") - fmt.Println() - - selPrompt = promptui.Select{ - Templates: selTmpls, - Label: "Site type", - Items: []string{"Single user blog", "Multi-user instance"}, - } - _, usersType, err := selPrompt.Run() - if err != nil { - return data, err - } - data.Config.App.SingleUser = usersType == "Single user blog" - - if data.Config.App.SingleUser { - data.User = &UserCreation{} - - // prompt for username prompt = promptui.Prompt{ Templates: tmpls, - Label: "Admin username", + Label: siteNameLabel, Validate: validateNonEmpty, + Default: data.Config.App.SiteName, } - data.User.Username, err = prompt.Run() + data.Config.App.SiteName, err = prompt.Run() if err != nil { return data, err } - - // prompt for password + prompt = promptui.Prompt{ Templates: tmpls, - Label: "Admin password", - Validate: validateNonEmpty, + Label: "Public URL", + Validate: validateDomain, + Default: data.Config.App.Host, } - newUserPass, err := prompt.Run() + data.Config.App.Host, err = prompt.Run() if err != nil { return data, err } - - data.User.HashedPass, err = auth.HashPass([]byte(newUserPass)) - if err != nil { - return data, err - } - } - - siteNameLabel := "Instance name" - if data.Config.App.SingleUser { - siteNameLabel = "Blog name" - } - prompt = promptui.Prompt{ - Templates: tmpls, - Label: siteNameLabel, - Validate: validateNonEmpty, - Default: data.Config.App.SiteName, - } - data.Config.App.SiteName, err = prompt.Run() - if err != nil { - return data, err - } - - prompt = promptui.Prompt{ - Templates: tmpls, - Label: "Public URL", - Validate: validateDomain, - Default: data.Config.App.Host, - } - data.Config.App.Host, err = prompt.Run() - if err != nil { - return data, err - } - - if !data.Config.App.SingleUser { - selPrompt = promptui.Select{ - Templates: selTmpls, - Label: "Registration", - Items: []string{"Open", "Closed"}, - } - _, regType, err := selPrompt.Run() - if err != nil { - return data, err - } - data.Config.App.OpenRegistration = regType == "Open" - - prompt = promptui.Prompt{ - Templates: tmpls, - Label: "Max blogs per user", - Default: fmt.Sprintf("%d", data.Config.App.MaxBlogs), - } - maxBlogs, err := prompt.Run() - if err != nil { - return data, err + + if !data.Config.App.SingleUser { + selPrompt = promptui.Select{ + Templates: selTmpls, + Label: "Registration", + Items: []string{"Open", "Closed"}, + } + _, regType, err := selPrompt.Run() + if err != nil { + return data, err + } + data.Config.App.OpenRegistration = regType == "Open" + + prompt = promptui.Prompt{ + Templates: tmpls, + Label: "Max blogs per user", + Default: fmt.Sprintf("%d", data.Config.App.MaxBlogs), + } + maxBlogs, err := prompt.Run() + if err != nil { + return data, err + } + data.Config.App.MaxBlogs, _ = strconv.Atoi(maxBlogs) // Ignore error, as we've already validated number } - data.Config.App.MaxBlogs, _ = strconv.Atoi(maxBlogs) // Ignore error, as we've already validated number - } - - selPrompt = promptui.Select{ - Templates: selTmpls, - Label: "Federation", - Items: []string{"Enabled", "Disabled"}, - } - _, fedType, err := selPrompt.Run() - if err != nil { - return data, err - } - data.Config.App.Federation = fedType == "Enabled" - - if data.Config.App.Federation { + selPrompt = promptui.Select{ Templates: selTmpls, - Label: "Federation usage stats", - Items: []string{"Public", "Private"}, + Label: "Federation", + Items: []string{"Enabled", "Disabled"}, } - _, fedStatsType, err := selPrompt.Run() + _, fedType, err := selPrompt.Run() if err != nil { return data, err } - data.Config.App.PublicStats = fedStatsType == "Public" - - selPrompt = promptui.Select{ - Templates: selTmpls, - Label: "Instance metadata privacy", - Items: []string{"Public", "Private"}, - } - _, fedStatsType, err = selPrompt.Run() - if err != nil { - return data, err + data.Config.App.Federation = fedType == "Enabled" + + if data.Config.App.Federation { + selPrompt = promptui.Select{ + Templates: selTmpls, + Label: "Federation usage stats", + Items: []string{"Public", "Private"}, + } + _, fedStatsType, err := selPrompt.Run() + if err != nil { + return data, err + } + data.Config.App.PublicStats = fedStatsType == "Public" + + selPrompt = promptui.Select{ + Templates: selTmpls, + Label: "Instance metadata privacy", + Items: []string{"Public", "Private"}, + } + _, fedStatsType, err = selPrompt.Run() + if err != nil { + return data, err + } + data.Config.App.Private = fedStatsType == "Private" } - data.Config.App.Private = fedStatsType == "Private" } return data, Save(data.Config, fname)