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.
This commit is contained in:
parent
1d5c396327
commit
07fe366c15
5
app.go
5
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)
|
||||
|
|
496
config/setup.go
496
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),
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
if strings.Contains(configSections, "server"){
|
||||
|
||||
if isStandalone {
|
||||
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
|
||||
}
|
||||
|
||||
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 = ""
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
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 = ""
|
||||
// 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),
|
||||
}
|
||||
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 {
|
||||
data.Config.Server.Port = 443
|
||||
|
||||
// Configure for SQLite
|
||||
data.Config.UseSQLite(isNewCfg)
|
||||
|
||||
prompt = promptui.Prompt{
|
||||
Templates: tmpls,
|
||||
Label: "Certificate path",
|
||||
Label: "Filename",
|
||||
Validate: validateNonEmpty,
|
||||
Default: data.Config.Server.TLSCertPath,
|
||||
Default: data.Config.Database.FileName,
|
||||
}
|
||||
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()
|
||||
data.Config.Database.FileName, err = prompt.Run()
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
data.Config.Server.TLSCertPath = ""
|
||||
data.Config.Server.TLSKeyPath = ""
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
if strings.Contains(configSections, "app"){
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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",
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
title(" App setup ")
|
||||
fmt.Println()
|
||||
|
||||
selPrompt = promptui.Select{
|
||||
Templates: selTmpls,
|
||||
Label: "Registration",
|
||||
Items: []string{"Open", "Closed"},
|
||||
Label: "Site type",
|
||||
Items: []string{"Single user blog", "Multi-user instance"},
|
||||
}
|
||||
_, regType, err := selPrompt.Run()
|
||||
_, usersType, err := selPrompt.Run()
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
data.Config.App.OpenRegistration = regType == "Open"
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
siteNameLabel := "Instance name"
|
||||
if data.Config.App.SingleUser {
|
||||
siteNameLabel = "Blog name"
|
||||
}
|
||||
prompt = promptui.Prompt{
|
||||
Templates: tmpls,
|
||||
Label: "Max blogs per user",
|
||||
Default: fmt.Sprintf("%d", data.Config.App.MaxBlogs),
|
||||
Label: siteNameLabel,
|
||||
Validate: validateNonEmpty,
|
||||
Default: data.Config.App.SiteName,
|
||||
}
|
||||
maxBlogs, err := prompt.Run()
|
||||
data.Config.App.SiteName, err = prompt.Run()
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
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 {
|
||||
|
||||
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
|
||||
}
|
||||
data.Config.App.MaxBlogs, _ = strconv.Atoi(maxBlogs) // Ignore error, as we've already validated number
|
||||
}
|
||||
|
||||
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"},
|
||||
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"
|
||||
}
|
||||
_, fedStatsType, err = selPrompt.Run()
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
data.Config.App.Private = fedStatsType == "Private"
|
||||
}
|
||||
|
||||
return data, Save(data.Config, fname)
|
||||
|
|
Loading…
Reference in New Issue