diff --git a/app.go b/app.go index 8182f54..e9ff9b1 100644 --- a/app.go +++ b/app.go @@ -11,6 +11,7 @@ import ( "os/signal" "regexp" "syscall" + "time" "github.com/gorilla/mux" "github.com/gorilla/schema" @@ -127,6 +128,8 @@ func Serve() { debugging = *debugPtr + app := &app{} + if *createConfig { log.Info("Creating configuration...") c := config.New() @@ -138,11 +141,31 @@ func Serve() { } os.Exit(0) } else if *doConfig { - err := config.Configure() + d, err := config.Configure() if err != nil { log.Error("Unable to configure: %v", err) os.Exit(1) } + if d != nil { + app.cfg = d.Config + connectToDatabase(app) + defer shutdown(app) + + u := &User{ + Username: d.User.Username, + HashedPass: d.User.HashedPass, + Created: time.Now().Truncate(time.Second).UTC(), + } + + // Create blog + log.Info("Creating user %s...\n", u.Username) + err = app.db.CreateUser(u, app.cfg.App.SiteName) + if err != nil { + log.Error("Unable to create user: %s", err) + os.Exit(1) + } + log.Info("Done!") + } os.Exit(0) } @@ -154,9 +177,7 @@ func Serve() { log.Error("Unable to load configuration: %v", err) os.Exit(1) } - app := &app{ - cfg: cfg, - } + app.cfg = cfg hostName = cfg.App.Host isSingleUser = cfg.App.SingleUser @@ -193,15 +214,8 @@ func Serve() { app.cfg.Database.Database = "writeas" } - log.Info("Connecting to database...") - db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=true", app.cfg.Database.User, app.cfg.Database.Password, app.cfg.Database.Host, app.cfg.Database.Port, app.cfg.Database.Database)) - if err != nil { - log.Error("\n%s\n", err) - os.Exit(1) - } - app.db = &datastore{db} + connectToDatabase(app) defer shutdown(app) - app.db.SetMaxOpenConns(50) r := mux.NewRouter() handler := NewHandler(app) @@ -238,6 +252,17 @@ func Serve() { http.ListenAndServe(fmt.Sprintf(":%d", app.cfg.Server.Port), nil) } +func connectToDatabase(app *app) { + log.Info("Connecting to database...") + db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=true", app.cfg.Database.User, app.cfg.Database.Password, app.cfg.Database.Host, app.cfg.Database.Port, app.cfg.Database.Database)) + if err != nil { + log.Error("%s", err) + os.Exit(1) + } + app.db = &datastore{db} + app.db.SetMaxOpenConns(50) +} + func shutdown(app *app) { log.Info("Closing database connection...") app.db.Close() diff --git a/config/data.go b/config/data.go new file mode 100644 index 0000000..d9694f4 --- /dev/null +++ b/config/data.go @@ -0,0 +1,6 @@ +package config + +type UserCreation struct { + Username string + HashedPass []byte +} diff --git a/config/setup.go b/config/setup.go index f1478a9..3729f8b 100644 --- a/config/setup.go +++ b/config/setup.go @@ -5,15 +5,24 @@ import ( "github.com/fatih/color" "github.com/manifoldco/promptui" "github.com/mitchellh/go-wordwrap" + "github.com/writeas/web-core/auth" "strconv" ) -func Configure() error { - c, err := Load() +type SetupData struct { + User *UserCreation + Config *Config +} + +func Configure() (*SetupData, error) { + data := &SetupData{} + var err error + + data.Config, err = Load() var action string if err != nil { fmt.Println("No configuration yet. Creating new.") - c = New() + data.Config = New() action = "generate" } else { fmt.Println("Configuration loaded.") @@ -42,13 +51,13 @@ func Configure() error { Templates: tmpls, Label: "Local port", Validate: validatePort, - Default: fmt.Sprintf("%d", c.Server.Port), + Default: fmt.Sprintf("%d", data.Config.Server.Port), } port, err := prompt.Run() if err != nil { - return err + return data, err } - c.Server.Port, _ = strconv.Atoi(port) // Ignore error, as we've already validated number + data.Config.Server.Port, _ = strconv.Atoi(port) // Ignore error, as we've already validated number fmt.Println() title(" Database setup ") @@ -58,58 +67,58 @@ func Configure() error { Templates: tmpls, Label: "Username", Validate: validateNonEmpty, - Default: c.Database.User, + Default: data.Config.Database.User, } - c.Database.User, err = prompt.Run() + data.Config.Database.User, err = prompt.Run() if err != nil { - return err + return data, err } prompt = promptui.Prompt{ Templates: tmpls, Label: "Password", Validate: validateNonEmpty, - Default: c.Database.Password, + Default: data.Config.Database.Password, Mask: '*', } - c.Database.Password, err = prompt.Run() + data.Config.Database.Password, err = prompt.Run() if err != nil { - return err + return data, err } prompt = promptui.Prompt{ Templates: tmpls, Label: "Database name", Validate: validateNonEmpty, - Default: c.Database.Database, + Default: data.Config.Database.Database, } - c.Database.Database, err = prompt.Run() + data.Config.Database.Database, err = prompt.Run() if err != nil { - return err + return data, err } prompt = promptui.Prompt{ Templates: tmpls, Label: "Host", Validate: validateNonEmpty, - Default: c.Database.Host, + Default: data.Config.Database.Host, } - c.Database.Host, err = prompt.Run() + data.Config.Database.Host, err = prompt.Run() if err != nil { - return err + return data, err } prompt = promptui.Prompt{ Templates: tmpls, Label: "Port", Validate: validatePort, - Default: fmt.Sprintf("%d", c.Database.Port), + Default: fmt.Sprintf("%d", data.Config.Database.Port), } dbPort, err := prompt.Run() if err != nil { - return err + return data, err } - c.Database.Port, _ = strconv.Atoi(dbPort) // Ignore error, as we've already validated number + data.Config.Database.Port, _ = strconv.Atoi(dbPort) // Ignore error, as we've already validated number fmt.Println() title(" App setup ") @@ -122,41 +131,68 @@ func Configure() error { } _, usersType, err := selPrompt.Run() if err != nil { - return err + 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 + } } - c.App.SingleUser = usersType == "Single user" - // TODO: if c.App.SingleUser { - // prompt for username - // prompt for password - // create blog siteNameLabel := "Instance name" - if c.App.SingleUser { + if data.Config.App.SingleUser { siteNameLabel = "Blog name" } prompt = promptui.Prompt{ Templates: tmpls, Label: siteNameLabel, Validate: validateNonEmpty, - Default: c.App.SiteName, + Default: data.Config.App.SiteName, } - c.App.SiteName, err = prompt.Run() + data.Config.App.SiteName, err = prompt.Run() if err != nil { - return err + return data, err } prompt = promptui.Prompt{ Templates: tmpls, Label: "Public URL", Validate: validateDomain, - Default: c.App.Host, + Default: data.Config.App.Host, } - c.App.Host, err = prompt.Run() + data.Config.App.Host, err = prompt.Run() if err != nil { - return err + return data, err } - if !c.App.SingleUser { + if !data.Config.App.SingleUser { selPrompt = promptui.Select{ Templates: selTmpls, Label: "Registration", @@ -164,20 +200,20 @@ func Configure() error { } _, regType, err := selPrompt.Run() if err != nil { - return err + return data, err } - c.App.OpenRegistration = regType == "Open" + data.Config.App.OpenRegistration = regType == "Open" prompt = promptui.Prompt{ Templates: tmpls, Label: "Max blogs per user", - Default: fmt.Sprintf("%d", c.App.MaxBlogs), + Default: fmt.Sprintf("%d", data.Config.App.MaxBlogs), } maxBlogs, err := prompt.Run() if err != nil { - return err + return data, err } - c.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{ @@ -187,11 +223,11 @@ func Configure() error { } _, fedType, err := selPrompt.Run() if err != nil { - return err + return data, err } - c.App.Federation = fedType == "Enabled" + data.Config.App.Federation = fedType == "Enabled" - if c.App.Federation { + if data.Config.App.Federation { selPrompt = promptui.Select{ Templates: selTmpls, Label: "Federation usage stats", @@ -199,9 +235,9 @@ func Configure() error { } _, fedStatsType, err := selPrompt.Run() if err != nil { - return err + return data, err } - c.App.PublicStats = fedStatsType == "Public" + data.Config.App.PublicStats = fedStatsType == "Public" selPrompt = promptui.Select{ Templates: selTmpls, @@ -210,10 +246,10 @@ func Configure() error { } _, fedStatsType, err = selPrompt.Run() if err != nil { - return err + return data, err } - c.App.Private = fedStatsType == "Private" + data.Config.App.Private = fedStatsType == "Private" } - return Save(c) + return data, Save(data.Config) }