mirror of
https://github.com/writefreely/writefreely
synced 2024-11-10 19:34:19 +00:00
1d5c396327
Add --sections flag to app.go according to T657, parse them into a string array (check for invalid arguments and abort) and pass them to Configure(). For now Configure() doesn't do anything with them yet.
362 lines
8.6 KiB
Go
362 lines
8.6 KiB
Go
/*
|
|
* Copyright © 2018 A Bunch Tell LLC.
|
|
*
|
|
* This file is part of WriteFreely.
|
|
*
|
|
* WriteFreely is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License, included
|
|
* in the LICENSE file in this source code package.
|
|
*/
|
|
|
|
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/fatih/color"
|
|
"github.com/manifoldco/promptui"
|
|
"github.com/mitchellh/go-wordwrap"
|
|
"github.com/writeas/web-core/auth"
|
|
"strconv"
|
|
)
|
|
|
|
type SetupData struct {
|
|
User *UserCreation
|
|
Config *Config
|
|
}
|
|
|
|
func Configure(fname string, configSections []string) (*SetupData, error) {
|
|
data := &SetupData{}
|
|
var err error
|
|
if fname == "" {
|
|
fname = FileName
|
|
}
|
|
|
|
data.Config, err = Load(fname)
|
|
var action string
|
|
isNewCfg := false
|
|
if err != nil {
|
|
fmt.Printf("No %s configuration yet. Creating new.\n", fname)
|
|
data.Config = New()
|
|
action = "generate"
|
|
isNewCfg = true
|
|
} else {
|
|
fmt.Printf("Loaded configuration %s.\n", fname)
|
|
action = "update"
|
|
}
|
|
title := color.New(color.Bold, color.BgGreen).PrintFunc()
|
|
|
|
intro := color.New(color.Bold, color.FgWhite).PrintlnFunc()
|
|
fmt.Println()
|
|
intro(" ✍ WriteFreely Configuration ✍")
|
|
fmt.Println()
|
|
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 }}: ",
|
|
}
|
|
selTmpls := &promptui.SelectTemplates{
|
|
Selected: fmt.Sprintf(`{{.Label}} {{ . | faint }}`),
|
|
}
|
|
|
|
// Environment selection
|
|
selPrompt := promptui.Select{
|
|
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
|
|
|
|
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 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()
|
|
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 {
|
|
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",
|
|
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"},
|
|
}
|
|
_, 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"
|
|
}
|
|
|
|
return data, Save(data.Config, fname)
|
|
}
|