Move flag parsing to main package
This exposes setup and admin functions in the writefreely package, and uses them in the main application, initialized by the flag parsing that now happens there. This is the first step towards making `writefreely` usable as a standalone package.
This commit is contained in:
parent
788713116f
commit
9023d4eb3f
345
app.go
345
app.go
|
@ -12,7 +12,6 @@ package writefreely
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"flag"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
@ -190,184 +189,8 @@ func pageForReq(app *app, r *http.Request) page.StaticPage {
|
|||
var shttp = http.NewServeMux()
|
||||
var fileRegex = regexp.MustCompile("/([^/]*\\.[^/]*)$")
|
||||
|
||||
func Serve() {
|
||||
// General options usable with other commands
|
||||
debugPtr := flag.Bool("debug", false, "Enables debug logging.")
|
||||
configFile := flag.String("c", "config.ini", "The configuration file to use")
|
||||
|
||||
// Setup actions
|
||||
createConfig := flag.Bool("create-config", false, "Creates a basic configuration and exits")
|
||||
doConfig := flag.Bool("config", false, "Run the configuration process")
|
||||
genKeys := flag.Bool("gen-keys", false, "Generate encryption and authentication keys")
|
||||
createSchema := flag.Bool("init-db", false, "Initialize app database")
|
||||
migrate := flag.Bool("migrate", false, "Migrate the database")
|
||||
|
||||
// Admin actions
|
||||
createAdmin := flag.String("create-admin", "", "Create an admin with the given username:password")
|
||||
createUser := flag.String("create-user", "", "Create a regular user with the given username:password")
|
||||
resetPassUser := flag.String("reset-pass", "", "Reset the given user's password")
|
||||
outputVersion := flag.Bool("v", false, "Output the current version")
|
||||
flag.Parse()
|
||||
|
||||
debugging = *debugPtr
|
||||
|
||||
app := &app{
|
||||
cfgFile: *configFile,
|
||||
}
|
||||
|
||||
if *outputVersion {
|
||||
fmt.Println(serverSoftware + " " + softwareVer)
|
||||
os.Exit(0)
|
||||
} else if *createConfig {
|
||||
log.Info("Creating configuration...")
|
||||
c := config.New()
|
||||
log.Info("Saving configuration %s...", app.cfgFile)
|
||||
err := config.Save(c, app.cfgFile)
|
||||
if err != nil {
|
||||
log.Error("Unable to save configuration: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
} else if *doConfig {
|
||||
d, err := config.Configure(app.cfgFile)
|
||||
if err != nil {
|
||||
log.Error("Unable to configure: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if d.User != nil {
|
||||
app.cfg = d.Config
|
||||
connectToDatabase(app)
|
||||
defer shutdown(app)
|
||||
|
||||
if !app.db.DatabaseInitialized() {
|
||||
err = adminInitDatabase(app)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
} else if *genKeys {
|
||||
errStatus := 0
|
||||
|
||||
// Read keys path from config
|
||||
loadConfig(app)
|
||||
|
||||
// Create keys dir if it doesn't exist yet
|
||||
fullKeysDir := filepath.Join(app.cfg.Server.KeysParentDir, keysDir)
|
||||
if _, err := os.Stat(fullKeysDir); os.IsNotExist(err) {
|
||||
err = os.Mkdir(fullKeysDir, 0700)
|
||||
if err != nil {
|
||||
log.Error("%s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate keys
|
||||
initKeyPaths(app)
|
||||
err := generateKey(emailKeyPath)
|
||||
if err != nil {
|
||||
errStatus = 1
|
||||
}
|
||||
err = generateKey(cookieAuthKeyPath)
|
||||
if err != nil {
|
||||
errStatus = 1
|
||||
}
|
||||
err = generateKey(cookieKeyPath)
|
||||
if err != nil {
|
||||
errStatus = 1
|
||||
}
|
||||
|
||||
os.Exit(errStatus)
|
||||
} else if *createSchema {
|
||||
loadConfig(app)
|
||||
connectToDatabase(app)
|
||||
defer shutdown(app)
|
||||
err := adminInitDatabase(app)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
} else if *createAdmin != "" {
|
||||
err := adminCreateUser(app, *createAdmin, true)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
} else if *createUser != "" {
|
||||
err := adminCreateUser(app, *createUser, false)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
} else if *resetPassUser != "" {
|
||||
// Connect to the database
|
||||
loadConfig(app)
|
||||
connectToDatabase(app)
|
||||
defer shutdown(app)
|
||||
|
||||
// Fetch user
|
||||
u, err := app.db.GetUserForAuth(*resetPassUser)
|
||||
if err != nil {
|
||||
log.Error("Get user: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Prompt for new password
|
||||
prompt := promptui.Prompt{
|
||||
Templates: &promptui.PromptTemplates{
|
||||
Success: "{{ . | bold | faint }}: ",
|
||||
},
|
||||
Label: "New password",
|
||||
Mask: '*',
|
||||
}
|
||||
newPass, err := prompt.Run()
|
||||
if err != nil {
|
||||
log.Error("%s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Do the update
|
||||
log.Info("Updating...")
|
||||
err = adminResetPassword(app, u, newPass)
|
||||
if err != nil {
|
||||
log.Error("%s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Info("Success.")
|
||||
os.Exit(0)
|
||||
} else if *migrate {
|
||||
loadConfig(app)
|
||||
connectToDatabase(app)
|
||||
defer shutdown(app)
|
||||
|
||||
err := migrations.Migrate(migrations.NewDatastore(app.db.DB, app.db.driverName))
|
||||
if err != nil {
|
||||
log.Error("migrate: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
func Serve(app *app, debug bool) {
|
||||
debugging = debug
|
||||
|
||||
log.Info("Initializing...")
|
||||
|
||||
|
@ -375,7 +198,7 @@ func Serve() {
|
|||
|
||||
hostName = app.cfg.App.Host
|
||||
isSingleUser = app.cfg.App.SingleUser
|
||||
app.cfg.Server.Dev = *debugPtr
|
||||
app.cfg.Server.Dev = debugging
|
||||
|
||||
err := initTemplates(app.cfg)
|
||||
if err != nil {
|
||||
|
@ -488,6 +311,165 @@ func Serve() {
|
|||
}
|
||||
}
|
||||
|
||||
// OutputVersion prints out the version of the application.
|
||||
func OutputVersion() {
|
||||
fmt.Println(serverSoftware + " " + softwareVer)
|
||||
}
|
||||
|
||||
// NewApp creates a new app instance.
|
||||
func NewApp(cfgFile string) *app {
|
||||
return &app{
|
||||
cfgFile: cfgFile,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateConfig creates a default configuration and saves it to the app's cfgFile.
|
||||
func CreateConfig(app *app) error {
|
||||
log.Info("Creating configuration...")
|
||||
c := config.New()
|
||||
log.Info("Saving configuration %s...", app.cfgFile)
|
||||
err := config.Save(c, app.cfgFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to save configuration: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DoConfig runs the interactive configuration process.
|
||||
func DoConfig(app *app) {
|
||||
d, err := config.Configure(app.cfgFile)
|
||||
if err != nil {
|
||||
log.Error("Unable to configure: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if d.User != nil {
|
||||
app.cfg = d.Config
|
||||
connectToDatabase(app)
|
||||
defer shutdown(app)
|
||||
|
||||
if !app.db.DatabaseInitialized() {
|
||||
err = adminInitDatabase(app)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// GenerateKeys creates app encryption keys and saves them into the configured KeysParentDir.
|
||||
func GenerateKeys(app *app) error {
|
||||
// Read keys path from config
|
||||
loadConfig(app)
|
||||
|
||||
// Create keys dir if it doesn't exist yet
|
||||
fullKeysDir := filepath.Join(app.cfg.Server.KeysParentDir, keysDir)
|
||||
if _, err := os.Stat(fullKeysDir); os.IsNotExist(err) {
|
||||
err = os.Mkdir(fullKeysDir, 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Generate keys
|
||||
initKeyPaths(app)
|
||||
var keyErrs error
|
||||
err := generateKey(emailKeyPath)
|
||||
if err != nil {
|
||||
keyErrs = err
|
||||
}
|
||||
err = generateKey(cookieAuthKeyPath)
|
||||
if err != nil {
|
||||
keyErrs = err
|
||||
}
|
||||
err = generateKey(cookieKeyPath)
|
||||
if err != nil {
|
||||
keyErrs = err
|
||||
}
|
||||
|
||||
return keyErrs
|
||||
}
|
||||
|
||||
// CreateSchema creates all database tables needed for the application.
|
||||
func CreateSchema(app *app) error {
|
||||
loadConfig(app)
|
||||
connectToDatabase(app)
|
||||
defer shutdown(app)
|
||||
err := adminInitDatabase(app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Migrate runs all necessary database migrations.
|
||||
func Migrate(app *app) error {
|
||||
loadConfig(app)
|
||||
connectToDatabase(app)
|
||||
defer shutdown(app)
|
||||
|
||||
err := migrations.Migrate(migrations.NewDatastore(app.db.DB, app.db.driverName))
|
||||
if err != nil {
|
||||
return fmt.Errorf("migrate: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetPassword runs the interactive password reset process.
|
||||
func ResetPassword(app *app, username string) error {
|
||||
// Connect to the database
|
||||
loadConfig(app)
|
||||
connectToDatabase(app)
|
||||
defer shutdown(app)
|
||||
|
||||
// Fetch user
|
||||
u, err := app.db.GetUserForAuth(username)
|
||||
if err != nil {
|
||||
log.Error("Get user: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Prompt for new password
|
||||
prompt := promptui.Prompt{
|
||||
Templates: &promptui.PromptTemplates{
|
||||
Success: "{{ . | bold | faint }}: ",
|
||||
},
|
||||
Label: "New password",
|
||||
Mask: '*',
|
||||
}
|
||||
newPass, err := prompt.Run()
|
||||
if err != nil {
|
||||
log.Error("%s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Do the update
|
||||
log.Info("Updating...")
|
||||
err = adminResetPassword(app, u, newPass)
|
||||
if err != nil {
|
||||
log.Error("%s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Info("Success.")
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadConfig(app *app) {
|
||||
log.Info("Loading %s configuration...", app.cfgFile)
|
||||
cfg, err := config.Load(app.cfgFile)
|
||||
|
@ -533,7 +515,8 @@ func shutdown(app *app) {
|
|||
app.db.Close()
|
||||
}
|
||||
|
||||
func adminCreateUser(app *app, credStr string, isAdmin bool) error {
|
||||
// CreateUser creates a new admin or normal user from the given username:password string.
|
||||
func CreateUser(app *app, credStr string, isAdmin bool) error {
|
||||
// Create an admin user with --create-admin
|
||||
creds := strings.Split(credStr, ":")
|
||||
if len(creds) != 2 {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright © 2018 A Bunch Tell LLC.
|
||||
* Copyright © 2018-2019 A Bunch Tell LLC.
|
||||
*
|
||||
* This file is part of WriteFreely.
|
||||
*
|
||||
|
@ -11,9 +11,89 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/writeas/web-core/log"
|
||||
"github.com/writeas/writefreely"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
writefreely.Serve()
|
||||
// General options usable with other commands
|
||||
debugPtr := flag.Bool("debug", false, "Enables debug logging.")
|
||||
configFile := flag.String("c", "config.ini", "The configuration file to use")
|
||||
|
||||
// Setup actions
|
||||
createConfig := flag.Bool("create-config", false, "Creates a basic configuration and exits")
|
||||
doConfig := flag.Bool("config", false, "Run the configuration process")
|
||||
genKeys := flag.Bool("gen-keys", false, "Generate encryption and authentication keys")
|
||||
createSchema := flag.Bool("init-db", false, "Initialize app database")
|
||||
migrate := flag.Bool("migrate", false, "Migrate the database")
|
||||
|
||||
// Admin actions
|
||||
createAdmin := flag.String("create-admin", "", "Create an admin with the given username:password")
|
||||
createUser := flag.String("create-user", "", "Create a regular user with the given username:password")
|
||||
resetPassUser := flag.String("reset-pass", "", "Reset the given user's password")
|
||||
outputVersion := flag.Bool("v", false, "Output the current version")
|
||||
flag.Parse()
|
||||
|
||||
app := writefreely.NewApp(*configFile)
|
||||
|
||||
if *outputVersion {
|
||||
writefreely.OutputVersion()
|
||||
os.Exit(0)
|
||||
} else if *createConfig {
|
||||
err := writefreely.CreateConfig(app)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
} else if *doConfig {
|
||||
writefreely.DoConfig(app)
|
||||
os.Exit(0)
|
||||
} else if *genKeys {
|
||||
err := writefreely.GenerateKeys(app)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
} else if *createSchema {
|
||||
err := writefreely.CreateSchema(app)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
} else if *createAdmin != "" {
|
||||
err := writefreely.CreateUser(app, *createAdmin, true)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
} else if *createUser != "" {
|
||||
err := writefreely.CreateUser(app, *createUser, false)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
} else if *resetPassUser != "" {
|
||||
err := writefreely.ResetPassword(app, *resetPassUser)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
} else if *migrate {
|
||||
err := writefreely.Migrate(app)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
writefreely.Serve(app, *debugPtr)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue