Merge pull request #127 from writeas/shorter-config-process

Shorter config process
pull/137/head
Matt Baer 6 years ago committed by GitHub
commit 18bafadc43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 15
      app.go
  2. 5
      cmd/writefreely/main.go
  3. 457
      config/setup.go

@ -443,8 +443,19 @@ func CreateConfig(app *App) error {
} }
// DoConfig runs the interactive configuration process. // DoConfig runs the interactive configuration process.
func DoConfig(app *App) { func DoConfig(app *App, configSections string) {
d, err := config.Configure(app.cfgFile) if configSections == "" {
configSections = "server db app"
}
// 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\"")
os.Exit(1)
}
}
d, err := config.Configure(app.cfgFile, configSections)
if err != nil { if err != nil {
log.Error("Unable to configure: %v", err) log.Error("Unable to configure: %v", err)
os.Exit(1) os.Exit(1)

@ -28,6 +28,9 @@ func main() {
// Setup actions // Setup actions
createConfig := flag.Bool("create-config", false, "Creates a basic configuration and exits") createConfig := flag.Bool("create-config", false, "Creates a basic configuration and exits")
doConfig := flag.Bool("config", false, "Run the configuration process") doConfig := flag.Bool("config", false, "Run the configuration process")
configSections := flag.String("sections", "server db app", "Which sections of the configuration to go through (requires --config), "+
"valid values are any combination of 'server', 'db' and 'app' "+
"example: writefreely --config --sections \"db app\"")
genKeys := flag.Bool("gen-keys", false, "Generate encryption and authentication keys") genKeys := flag.Bool("gen-keys", false, "Generate encryption and authentication keys")
createSchema := flag.Bool("init-db", false, "Initialize app database") createSchema := flag.Bool("init-db", false, "Initialize app database")
migrate := flag.Bool("migrate", false, "Migrate the database") migrate := flag.Bool("migrate", false, "Migrate the database")
@ -52,7 +55,7 @@ func main() {
} }
os.Exit(0) os.Exit(0)
} else if *doConfig { } else if *doConfig {
writefreely.DoConfig(app) writefreely.DoConfig(app, *configSections)
os.Exit(0) os.Exit(0)
} else if *genKeys { } else if *genKeys {
err := writefreely.GenerateKeyFiles(app) err := writefreely.GenerateKeyFiles(app)

@ -17,6 +17,7 @@ import (
"github.com/mitchellh/go-wordwrap" "github.com/mitchellh/go-wordwrap"
"github.com/writeas/web-core/auth" "github.com/writeas/web-core/auth"
"strconv" "strconv"
"strings"
) )
type SetupData struct { type SetupData struct {
@ -24,7 +25,7 @@ type SetupData struct {
Config *Config Config *Config
} }
func Configure(fname string) (*SetupData, error) { func Configure(fname string, configSections string) (*SetupData, error) {
data := &SetupData{} data := &SetupData{}
var err error var err error
if fname == "" { if fname == "" {
@ -52,9 +53,6 @@ func Configure(fname 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(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() fmt.Println()
title(" Server setup ")
fmt.Println()
tmpls := &promptui.PromptTemplates{ tmpls := &promptui.PromptTemplates{
Success: "{{ . | bold | faint }}: ", Success: "{{ . | bold | faint }}: ",
} }
@ -62,300 +60,313 @@ func Configure(fname string) (*SetupData, error) {
Selected: fmt.Sprintf(`{{.Label}} {{ . | faint }}`), Selected: fmt.Sprintf(`{{.Label}} {{ . | faint }}`),
} }
// Environment selection var selPrompt promptui.Select
selPrompt := promptui.Select{ var prompt promptui.Prompt
Templates: selTmpls,
Label: "Environment",
Items: []string{"Development", "Production, standalone", "Production, behind reverse proxy"},
}
_, envType, err := selPrompt.Run()
if err != nil {
return data, err
}
isDevEnv := envType == "Development"
isStandalone := envType == "Production, standalone"
data.Config.Server.Dev = isDevEnv if strings.Contains(configSections, "server") {
title(" Server setup ")
fmt.Println()
var prompt promptui.Prompt // Environment selection
if isDevEnv || !isStandalone { selPrompt = promptui.Select{
// Running in dev environment or behind reverse proxy; ask for port Templates: selTmpls,
prompt = promptui.Prompt{ Label: "Environment",
Templates: tmpls, Items: []string{"Development", "Production, standalone", "Production, behind reverse proxy"},
Label: "Local port",
Validate: validatePort,
Default: fmt.Sprintf("%d", data.Config.Server.Port),
} }
port, err := prompt.Run() _, envType, err := selPrompt.Run()
if err != nil { if err != nil {
return data, err return data, err
} }
data.Config.Server.Port, _ = strconv.Atoi(port) // Ignore error, as we've already validated number isDevEnv := envType == "Development"
isStandalone := envType == "Production, standalone"
data.Config.Server.Dev = isDevEnv
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 isStandalone { if strings.Contains(configSections, "db") {
title(" Database setup ")
fmt.Println()
selPrompt = promptui.Select{ selPrompt = promptui.Select{
Templates: selTmpls, Templates: selTmpls,
Label: "Web server mode", Label: "Database driver",
Items: []string{"Insecure (port 80)", "Secure (port 443)"}, Items: []string{"MySQL", "SQLite"},
} }
sel, _, err := selPrompt.Run() sel, _, err := selPrompt.Run()
if err != nil { if err != nil {
return data, err return data, err
} }
if sel == 0 { if sel == 0 {
data.Config.Server.Port = 80 // Configure for MySQL
data.Config.Server.TLSCertPath = "" data.Config.UseMySQL(isNewCfg)
data.Config.Server.TLSKeyPath = ""
} else if sel == 1 {
data.Config.Server.Port = 443
prompt = promptui.Prompt{ prompt = promptui.Prompt{
Templates: tmpls, Templates: tmpls,
Label: "Certificate path", Label: "Username",
Validate: validateNonEmpty, 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 { if err != nil {
return data, err return data, err
} }
prompt = promptui.Prompt{ prompt = promptui.Prompt{
Templates: tmpls, Templates: tmpls,
Label: "Key path", Label: "Password",
Validate: validateNonEmpty, 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 { if err != nil {
return data, err return data, err
} }
}
} else {
data.Config.Server.TLSCertPath = ""
data.Config.Server.TLSKeyPath = ""
}
fmt.Println() prompt = promptui.Prompt{
title(" Database setup ") Templates: tmpls,
fmt.Println() Label: "Database name",
Validate: validateNonEmpty,
Default: data.Config.Database.Database,
}
data.Config.Database.Database, err = prompt.Run()
if err != nil {
return data, err
}
selPrompt = promptui.Select{ prompt = promptui.Prompt{
Templates: selTmpls, Templates: tmpls,
Label: "Database driver", Label: "Host",
Items: []string{"MySQL", "SQLite"}, Validate: validateNonEmpty,
} Default: data.Config.Database.Host,
sel, _, err := selPrompt.Run() }
if err != nil { data.Config.Database.Host, err = prompt.Run()
return data, err if err != nil {
} return data, err
}
if sel == 0 { prompt = promptui.Prompt{
// Configure for MySQL Templates: tmpls,
data.Config.UseMySQL(isNewCfg) 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{ prompt = promptui.Prompt{
Templates: tmpls, Templates: tmpls,
Label: "Username", Label: "Filename",
Validate: validateNonEmpty, Validate: validateNonEmpty,
Default: data.Config.Database.User, Default: data.Config.Database.FileName,
} }
data.Config.Database.User, err = prompt.Run() data.Config.Database.FileName, err = prompt.Run()
if err != nil { if err != nil {
return data, err return data, err
}
} }
prompt = promptui.Prompt{ fmt.Println()
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{ if strings.Contains(configSections, "app") {
Templates: tmpls, title(" App setup ")
Label: "Database name", fmt.Println()
Validate: validateNonEmpty,
Default: data.Config.Database.Database,
}
data.Config.Database.Database, err = prompt.Run()
if err != nil {
return data, err
}
prompt = promptui.Prompt{ selPrompt = promptui.Select{
Templates: tmpls, Templates: selTmpls,
Label: "Host", Label: "Site type",
Validate: validateNonEmpty, Items: []string{"Single user blog", "Multi-user instance"},
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() _, usersType, err := selPrompt.Run()
if err != nil { if err != nil {
return data, err return data, err
} }
data.Config.Database.Port, _ = strconv.Atoi(dbPort) // Ignore error, as we've already validated number data.Config.App.SingleUser = usersType == "Single user blog"
} else if sel == 1 {
// Configure for SQLite
data.Config.UseSQLite(isNewCfg)
prompt = promptui.Prompt{ if data.Config.App.SingleUser {
Templates: tmpls, data.User = &UserCreation{}
Label: "Filename",
Validate: validateNonEmpty,
Default: data.Config.Database.FileName,
}
data.Config.Database.FileName, err = prompt.Run()
if err != nil {
return data, err
}
}
fmt.Println() // prompt for username
title(" App setup ") prompt = promptui.Prompt{
fmt.Println() Templates: tmpls,
Label: "Admin username",
Validate: validateNonEmpty,
}
data.User.Username, err = prompt.Run()
if err != nil {
return data, err
}
selPrompt = promptui.Select{ // prompt for password
Templates: selTmpls, prompt = promptui.Prompt{
Label: "Site type", Templates: tmpls,
Items: []string{"Single user blog", "Multi-user instance"}, Label: "Admin password",
} Validate: validateNonEmpty,
_, usersType, err := selPrompt.Run() }
if err != nil { newUserPass, err := prompt.Run()
return data, err if err != nil {
} return data, err
data.Config.App.SingleUser = usersType == "Single user blog" }
if data.Config.App.SingleUser { data.User.HashedPass, err = auth.HashPass([]byte(newUserPass))
data.User = &UserCreation{} if err != nil {
return data, err
}
}
// prompt for username siteNameLabel := "Instance name"
if data.Config.App.SingleUser {
siteNameLabel = "Blog name"
}
prompt = promptui.Prompt{ prompt = promptui.Prompt{
Templates: tmpls, Templates: tmpls,
Label: "Admin username", Label: siteNameLabel,
Validate: validateNonEmpty, Validate: validateNonEmpty,
Default: data.Config.App.SiteName,
} }
data.User.Username, err = prompt.Run() data.Config.App.SiteName, err = prompt.Run()
if err != nil { if err != nil {
return data, err return data, err
} }
// prompt for password
prompt = promptui.Prompt{ prompt = promptui.Prompt{
Templates: tmpls, Templates: tmpls,
Label: "Admin password", Label: "Public URL",
Validate: validateNonEmpty, Validate: validateDomain,
} Default: data.Config.App.Host,
newUserPass, err := prompt.Run()
if err != nil {
return data, err
} }
data.Config.App.Host, err = prompt.Run()
data.User.HashedPass, err = auth.HashPass([]byte(newUserPass))
if err != nil { if err != nil {
return data, err return data, err
} }
}
siteNameLabel := "Instance name" if !data.Config.App.SingleUser {
if data.Config.App.SingleUser { selPrompt = promptui.Select{
siteNameLabel = "Blog name" Templates: selTmpls,
} Label: "Registration",
prompt = promptui.Prompt{ Items: []string{"Open", "Closed"},
Templates: tmpls, }
Label: siteNameLabel, _, regType, err := selPrompt.Run()
Validate: validateNonEmpty, if err != nil {
Default: data.Config.App.SiteName, return data, err
} }
data.Config.App.SiteName, err = prompt.Run() data.Config.App.OpenRegistration = regType == "Open"
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{ prompt = promptui.Prompt{
Templates: tmpls, Templates: tmpls,
Label: "Max blogs per user", Label: "Max blogs per user",
Default: fmt.Sprintf("%d", data.Config.App.MaxBlogs), Default: fmt.Sprintf("%d", data.Config.App.MaxBlogs),
} }
maxBlogs, err := prompt.Run() maxBlogs, err := prompt.Run()
if err != nil { if err != nil {
return data, err 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{ selPrompt = promptui.Select{
Templates: selTmpls, Templates: selTmpls,
Label: "Federation usage stats", Label: "Federation",
Items: []string{"Public", "Private"}, Items: []string{"Enabled", "Disabled"},
} }
_, fedStatsType, err := selPrompt.Run() _, fedType, err := selPrompt.Run()
if err != nil { if err != nil {
return data, err return data, err
} }
data.Config.App.PublicStats = fedStatsType == "Public" data.Config.App.Federation = fedType == "Enabled"
selPrompt = promptui.Select{ if data.Config.App.Federation {
Templates: selTmpls, selPrompt = promptui.Select{
Label: "Instance metadata privacy", Templates: selTmpls,
Items: []string{"Public", "Private"}, Label: "Federation usage stats",
} Items: []string{"Public", "Private"},
_, fedStatsType, err = selPrompt.Run() }
if err != nil { _, fedStatsType, err := selPrompt.Run()
return data, err 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) return data, Save(data.Config, fname)

Loading…
Cancel
Save