feat: merge server db conf with flags/env/config

This commit is contained in:
Henrique Dias 2019-01-08 11:33:49 +00:00
parent fc06f642f2
commit 388810b6d8
9 changed files with 194 additions and 118 deletions

View File

@ -27,6 +27,7 @@ var configCmd = &cobra.Command{
}
func addConfigFlags(flags *pflag.FlagSet) {
addServerFlags(flags)
addUserFlags(flags)
flags.BoolP("signup", "s", false, "allow users to signup")
flags.String("shell", "", "shell command to which other commands should be appended")
@ -84,33 +85,41 @@ func getAuthentication(cmd *cobra.Command) (settings.AuthMethod, auth.Auther) {
return method, auther
}
func printSettings(s *settings.Settings, auther auth.Auther) {
func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Auther) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintf(w, "Sign up:\t%t\n", s.Signup)
fmt.Fprintf(w, "Auth method:\t%s\n", s.AuthMethod)
fmt.Fprintf(w, "Shell:\t%s\t\n", strings.Join(s.Shell, " "))
fmt.Fprintf(w, "Sign up:\t%t\n", set.Signup)
fmt.Fprintf(w, "Auth method:\t%s\n", set.AuthMethod)
fmt.Fprintf(w, "Shell:\t%s\t\n", strings.Join(set.Shell, " "))
fmt.Fprintln(w, "\nBranding:")
fmt.Fprintf(w, "\tName:\t%s\n", s.Branding.Name)
fmt.Fprintf(w, "\tFiles override:\t%s\n", s.Branding.Files)
fmt.Fprintf(w, "\tDisable external links:\t%t\n", s.Branding.DisableExternal)
fmt.Fprintf(w, "\tName:\t%s\n", set.Branding.Name)
fmt.Fprintf(w, "\tFiles override:\t%s\n", set.Branding.Files)
fmt.Fprintf(w, "\tDisable external links:\t%t\n", set.Branding.DisableExternal)
fmt.Fprintln(w, "\nServer:")
fmt.Fprintf(w, "\tLog:\t%s\n", ser.Log)
fmt.Fprintf(w, "\tPort:\t%s\n", ser.Port)
fmt.Fprintf(w, "\tBase URL:\t%s\n", ser.BaseURL)
fmt.Fprintf(w, "\tRoot:\t%s\n", ser.Root)
fmt.Fprintf(w, "\tAddress:\t%s\n", ser.Address)
fmt.Fprintf(w, "\tTLS Cert:\t%s\n", ser.TLSCert)
fmt.Fprintf(w, "\tTLS Key:\t%s\n", ser.TLSKey)
fmt.Fprintln(w, "\nDefaults:")
fmt.Fprintf(w, "\tScope:\t%s\n", s.Defaults.Scope)
fmt.Fprintf(w, "\tLocale:\t%s\n", s.Defaults.Locale)
fmt.Fprintf(w, "\tView mode:\t%s\n", s.Defaults.ViewMode)
fmt.Fprintf(w, "\tCommands:\t%s\n", strings.Join(s.Defaults.Commands, " "))
fmt.Fprintf(w, "\tScope:\t%s\n", set.Defaults.Scope)
fmt.Fprintf(w, "\tLocale:\t%s\n", set.Defaults.Locale)
fmt.Fprintf(w, "\tView mode:\t%s\n", set.Defaults.ViewMode)
fmt.Fprintf(w, "\tCommands:\t%s\n", strings.Join(set.Defaults.Commands, " "))
fmt.Fprintf(w, "\tSorting:\n")
fmt.Fprintf(w, "\t\tBy:\t%s\n", s.Defaults.Sorting.By)
fmt.Fprintf(w, "\t\tAsc:\t%t\n", s.Defaults.Sorting.Asc)
fmt.Fprintf(w, "\t\tBy:\t%s\n", set.Defaults.Sorting.By)
fmt.Fprintf(w, "\t\tAsc:\t%t\n", set.Defaults.Sorting.Asc)
fmt.Fprintf(w, "\tPermissions:\n")
fmt.Fprintf(w, "\t\tAdmin:\t%t\n", s.Defaults.Perm.Admin)
fmt.Fprintf(w, "\t\tExecute:\t%t\n", s.Defaults.Perm.Execute)
fmt.Fprintf(w, "\t\tCreate:\t%t\n", s.Defaults.Perm.Create)
fmt.Fprintf(w, "\t\tRename:\t%t\n", s.Defaults.Perm.Rename)
fmt.Fprintf(w, "\t\tModify:\t%t\n", s.Defaults.Perm.Modify)
fmt.Fprintf(w, "\t\tDelete:\t%t\n", s.Defaults.Perm.Delete)
fmt.Fprintf(w, "\t\tShare:\t%t\n", s.Defaults.Perm.Share)
fmt.Fprintf(w, "\t\tDownload:\t%t\n", s.Defaults.Perm.Download)
fmt.Fprintf(w, "\t\tAdmin:\t%t\n", set.Defaults.Perm.Admin)
fmt.Fprintf(w, "\t\tExecute:\t%t\n", set.Defaults.Perm.Execute)
fmt.Fprintf(w, "\t\tCreate:\t%t\n", set.Defaults.Perm.Create)
fmt.Fprintf(w, "\t\tRename:\t%t\n", set.Defaults.Perm.Rename)
fmt.Fprintf(w, "\t\tModify:\t%t\n", set.Defaults.Perm.Modify)
fmt.Fprintf(w, "\t\tDelete:\t%t\n", set.Defaults.Perm.Delete)
fmt.Fprintf(w, "\t\tShare:\t%t\n", set.Defaults.Perm.Share)
fmt.Fprintf(w, "\t\tDownload:\t%t\n", set.Defaults.Perm.Download)
w.Flush()
b, err := json.MarshalIndent(auther, "", " ")

View File

@ -14,10 +14,12 @@ var configCatCmd = &cobra.Command{
Long: `Prints the configuration.`,
Args: cobra.NoArgs,
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
s, err := d.store.Settings.Get()
set, err := d.store.Settings.Get()
checkErr(err)
auther, err := d.store.Auth.Get(s.AuthMethod)
ser, err := d.store.Settings.GetServer()
checkErr(err)
printSettings(s, auther)
auther, err := d.store.Auth.Get(set.AuthMethod)
checkErr(err)
printSettings(ser, set, auther)
}, pythonConfig{}),
}

View File

@ -16,12 +16,16 @@ var configExportCmd = &cobra.Command{
settings, err := d.store.Settings.Get()
checkErr(err)
server, err := d.store.Settings.GetServer()
checkErr(err)
auther, err := d.store.Auth.Get(settings.AuthMethod)
checkErr(err)
data := &settingsFile{
Settings: settings,
Auther: auther,
Server: server,
}
err = marshal(args[0], data)

View File

@ -3,6 +3,7 @@ package cmd
import (
"encoding/json"
"errors"
"path/filepath"
"reflect"
"github.com/filebrowser/filebrowser/v2/auth"
@ -16,6 +17,7 @@ func init() {
type settingsFile struct {
Settings *settings.Settings `json:"settings"`
Server *settings.Server `json:"server"`
Auther interface{} `json:"auther"`
}
@ -45,23 +47,31 @@ database.`,
err = d.store.Settings.Save(file.Settings)
checkErr(err)
autherInterf := cleanUpInterfaceMap(file.Auther.(map[interface{}]interface{}))
err = d.store.Settings.SaveServer(file.Server)
checkErr(err)
var rawAuther interface{}
rawAuther = file.Auther
if filepath.Ext(args[0]) != ".json" {
rawAuther = cleanUpInterfaceMap(file.Auther.(map[interface{}]interface{}))
}
var auther auth.Auther
switch file.Settings.AuthMethod {
case auth.MethodJSONAuth:
auther = getAuther(auth.JSONAuth{}, autherInterf).(*auth.JSONAuth)
auther = getAuther(auth.JSONAuth{}, rawAuther).(*auth.JSONAuth)
case auth.MethodNoAuth:
auther = getAuther(auth.NoAuth{}, autherInterf).(*auth.NoAuth)
auther = getAuther(auth.NoAuth{}, rawAuther).(*auth.NoAuth)
case auth.MethodProxyAuth:
auther = getAuther(auth.ProxyAuth{}, autherInterf).(*auth.ProxyAuth)
auther = getAuther(auth.ProxyAuth{}, rawAuther).(*auth.ProxyAuth)
default:
checkErr(errors.New("invalid auth method"))
}
err = d.store.Auth.Save(auther)
checkErr(err)
printSettings(file.Settings, auther)
printSettings(file.Server, file.Settings, auther)
}, pythonConfig{allowNoDB: true}),
}

View File

@ -40,8 +40,20 @@ override the options.`,
},
}
ser := &settings.Server{
Address: mustGetString(cmd, "address"),
Root: mustGetString(cmd, "root"),
BaseURL: mustGetString(cmd, "baseurl"),
TLSKey: mustGetString(cmd, "key"),
TLSCert: mustGetString(cmd, "cert"),
Port: mustGetString(cmd, "port"),
Log: mustGetString(cmd, "log"),
}
err := d.store.Settings.Save(s)
checkErr(err)
err = d.store.Settings.SaveServer(ser)
checkErr(err)
err = d.store.Auth.Save(auther)
checkErr(err)
@ -50,6 +62,6 @@ Congratulations! You've set up your database to use with File Browser.
Now add your first user via 'filebrowser users new' and then you just
need to call the main command to boot up the server.
`)
printSettings(s, auther)
printSettings(ser, s, auther)
}, pythonConfig{noDB: true}),
}

View File

@ -20,41 +20,60 @@ var configSetCmd = &cobra.Command{
you want to change.`,
Args: cobra.NoArgs,
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
s, err := d.store.Settings.Get()
set, err := d.store.Settings.Get()
checkErr(err)
ser, err := d.store.Settings.GetServer()
checkErr(err)
hasAuth := false
cmd.Flags().Visit(func(flag *pflag.Flag) {
switch flag.Name {
case "baseurl":
ser.BaseURL = mustGetString(cmd, flag.Name)
case "root":
ser.Root = mustGetString(cmd, flag.Name)
case "cert":
ser.TLSCert = mustGetString(cmd, flag.Name)
case "key":
ser.TLSKey = mustGetString(cmd, flag.Name)
case "address":
ser.Address = mustGetString(cmd, flag.Name)
case "port":
ser.Port = mustGetString(cmd, flag.Name)
case "log":
ser.Log = mustGetString(cmd, flag.Name)
case "signup":
s.Signup = mustGetBool(cmd, flag.Name)
set.Signup = mustGetBool(cmd, flag.Name)
case "auth.method":
hasAuth = true
case "shell":
s.Shell = strings.Split(strings.TrimSpace(mustGetString(cmd, flag.Name)), " ")
set.Shell = strings.Split(strings.TrimSpace(mustGetString(cmd, flag.Name)), " ")
case "branding.name":
s.Branding.Name = mustGetString(cmd, flag.Name)
set.Branding.Name = mustGetString(cmd, flag.Name)
case "branding.disableExternal":
s.Branding.DisableExternal = mustGetBool(cmd, flag.Name)
set.Branding.DisableExternal = mustGetBool(cmd, flag.Name)
case "branding.files":
s.Branding.Files = mustGetString(cmd, flag.Name)
set.Branding.Files = mustGetString(cmd, flag.Name)
}
})
getUserDefaults(cmd, &s.Defaults, false)
getUserDefaults(cmd, &set.Defaults, false)
var auther auth.Auther
if hasAuth {
s.AuthMethod, auther = getAuthentication(cmd)
set.AuthMethod, auther = getAuthentication(cmd)
err = d.store.Auth.Save(auther)
checkErr(err)
} else {
auther, err = d.store.Auth.Get(s.AuthMethod)
auther, err = d.store.Auth.Get(set.AuthMethod)
checkErr(err)
}
err = d.store.Settings.Save(s)
err = d.store.Settings.Save(set)
checkErr(err)
printSettings(s, auther)
err = d.store.Settings.SaveServer(ser)
checkErr(err)
printSettings(ser, set, auther)
}, pythonConfig{}),
}

View File

@ -18,6 +18,7 @@ import (
"github.com/filebrowser/filebrowser/v2/users"
"github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
v "github.com/spf13/viper"
lumberjack "gopkg.in/natefinch/lumberjack.v2"
)
@ -28,29 +29,61 @@ var (
func init() {
cobra.OnInitialize(initConfig)
flags := rootCmd.Flags()
persistent := rootCmd.PersistentFlags()
f := rootCmd.Flags()
pf := rootCmd.PersistentFlags()
persistent.StringVarP(&cfgFile, "config", "c", "", "config file path")
persistent.StringP("database", "d", "./filebrowser.db", "database path")
flags.String("username", "admin", "username for the first user when using quick config")
flags.String("password", "", "hashed password for the first user when using quick config (default \"admin\")")
pf.StringVarP(&cfgFile, "config", "c", "", "config file path")
vaddP(pf, "database", "d", "./filebrowser.db", "path to the database")
vaddP(f, "address", "a", "127.0.0.1", "address to listen on")
vaddP(f, "log", "l", "stdout", "log output")
vaddP(f, "port", "p", "8080", "port to listen on")
vaddP(f, "cert", "t", "", "tls certificate")
vaddP(f, "key", "k", "", "tls key")
vaddP(f, "root", "r", ".", "root to prepend to relative paths")
vaddP(f, "baseurl", "b", "", "base url")
vadd(f, "username", "admin", "username for the first user when using quick config")
vadd(f, "password", "", "hashed password for the first user when using quick config (default \"admin\")")
addServerFlags(flags)
}
if err := v.BindPFlags(f); err != nil {
panic(err)
func addServerFlags(flags *pflag.FlagSet) {
flags.StringP("address", "a", "127.0.0.1", "address to listen on")
flags.StringP("log", "l", "stdout", "log output")
flags.StringP("port", "p", "8080", "port to listen on")
flags.StringP("cert", "t", "", "tls certificate")
flags.StringP("key", "k", "", "tls key")
flags.StringP("root", "r", ".", "root to prepend to relative paths")
flags.StringP("baseurl", "b", "", "base url")
}
// NOTE: we could simply bind the flags to viper and use IsSet.
// Although there is a bug on Viper that always returns true on IsSet
// if a flag is binded. Our alternative way is to manually check
// the flag and then the value from env/config/gotten by viper.
// https://github.com/spf13/viper/pull/331
func getStringViperFlag(flags *pflag.FlagSet, key string) (string, bool) {
value := ""
set := false
// If set on Flags, use it.
flags.Visit(func(flag *pflag.Flag) {
if flag.Name == key {
set = true
value, _ = flags.GetString(key)
}
})
if set {
return value, set
}
if err := v.BindPFlags(pf); err != nil {
panic(err)
// If set through viper (env, config), return it.
if v.IsSet(key) {
return v.GetString(key), true
}
// Otherwise use default value on flags.
value, _ = flags.GetString(key)
return value, false
}
func mustGetStringViperFlag(flags *pflag.FlagSet, key string) string {
val, _ := getStringViperFlag(flags, key)
return val
}
var rootCmd = &cobra.Command{
@ -92,10 +125,10 @@ the quick setup mode and a new database will be bootstraped and a new
user created with the credentials from options "username" and "password".`,
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
if !d.hadDB {
quickSetup(d)
quickSetup(cmd.Flags(), d)
}
server := getServer(d.store)
server := getServerWithViper(cmd.Flags(), d.store)
setupLog(server.Log)
handler, err := fbhttp.NewHandler(d.store, server)
@ -121,27 +154,40 @@ user created with the credentials from options "username" and "password".`,
}, pythonConfig{allowNoDB: true}),
}
// TODO: get server settings and only replace
// them if set on Viper. Although viper.IsSet
// is bugged and if binded to a pflag, it will
// always return true.
// Also, when doing that, add this options to
// config init, config import, printConfig
// and config set since the DB values will actually
// be used. For now, despite being stored in the DB,
// they won't be used.
func getServer(st *storage.Storage) *settings.Server {
root := v.GetString("root")
root, err := filepath.Abs(root)
func getServerWithViper(flags *pflag.FlagSet, st *storage.Storage) *settings.Server {
server, err := st.Settings.GetServer()
checkErr(err)
server := &settings.Server{}
server.BaseURL = v.GetString("baseurl")
server.Root = root
server.Address = v.GetString("address")
server.Port = v.GetString("port")
server.TLSKey = v.GetString("key")
server.TLSCert = v.GetString("cert")
server.Log = v.GetString("log")
if val, set := getStringViperFlag(flags, "root"); set {
root, err := filepath.Abs(val)
checkErr(err)
server.Root = root
}
if val, set := getStringViperFlag(flags, "baseurl"); set {
server.BaseURL = val
}
if val, set := getStringViperFlag(flags, "address"); set {
server.Address = val
}
if val, set := getStringViperFlag(flags, "port"); set {
server.Port = val
}
if val, set := getStringViperFlag(flags, "log"); set {
server.Log = val
}
if val, set := getStringViperFlag(flags, "key"); set {
server.TLSKey = val
}
if val, set := getStringViperFlag(flags, "cert"); set {
server.TLSCert = val
}
return server
}
@ -164,7 +210,7 @@ func setupLog(logMethod string) {
}
func quickSetup(d pythonData) {
func quickSetup(flags *pflag.FlagSet, d pythonData) {
set := &settings.Settings{
Key: generateRandomBytes(64), // 256 bit
Signup: false,
@ -186,12 +232,13 @@ func quickSetup(d pythonData) {
}
ser := &settings.Server{
BaseURL: v.GetString("baseurl"),
Log: v.GetString("log"),
TLSKey: v.GetString("key"),
TLSCert: v.GetString("cert"),
Address: v.GetString("address"),
Root: v.GetString("root"),
BaseURL: mustGetStringViperFlag(flags, "baseurl"),
Port: mustGetStringViperFlag(flags, "port"),
Log: mustGetStringViperFlag(flags, "log"),
TLSKey: mustGetStringViperFlag(flags, "key"),
TLSCert: mustGetStringViperFlag(flags, "cert"),
Address: mustGetStringViperFlag(flags, "address"),
Root: mustGetStringViperFlag(flags, "root"),
}
err := d.store.Settings.Save(set)
@ -203,8 +250,8 @@ func quickSetup(d pythonData) {
err = d.store.Auth.Save(&auth.JSONAuth{})
checkErr(err)
username := v.GetString("username")
password := v.GetString("password")
username := mustGetStringViperFlag(flags, "username")
password := mustGetStringViperFlag(flags, "password")
if password == "" {
password, err = users.HashPwd("admin")

View File

@ -3,7 +3,6 @@ package cmd
import (
"github.com/filebrowser/filebrowser/v2/storage/bolt/importer"
"github.com/spf13/cobra"
v "github.com/spf13/viper"
)
func init() {
@ -25,7 +24,7 @@ this version.`,
oldDB := mustGetString(cmd, "old.database")
oldConf := mustGetString(cmd, "old.config")
err := importer.Import(oldDB, oldConf, v.GetString("database"))
err := importer.Import(oldDB, oldConf, mustGetStringViperFlag(cmd.Flags(), "database"))
checkErr(err)
},
}

View File

@ -13,35 +13,9 @@ import (
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/storage/bolt"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
v "github.com/spf13/viper"
yaml "gopkg.in/yaml.v2"
)
func vaddP(f *pflag.FlagSet, k, p string, i interface{}, u string) {
switch y := i.(type) {
case bool:
f.BoolP(k, p, y, u)
case int:
f.IntP(k, p, y, u)
case string:
f.StringP(k, p, y, u)
}
v.SetDefault(k, i)
}
func vadd(f *pflag.FlagSet, k string, i interface{}, u string) {
switch y := i.(type) {
case bool:
f.Bool(k, y, u)
case int:
f.Int(k, y, u)
case string:
f.String(k, y, u)
}
v.SetDefault(k, i)
}
func checkErr(err error) {
if err != nil {
fmt.Println(err)
@ -92,7 +66,7 @@ func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
return func(cmd *cobra.Command, args []string) {
data := pythonData{hadDB: true}
path := v.GetString("database")
path := mustGetStringViperFlag(cmd.Flags(), "database")
_, err := os.Stat(path)
if os.IsNotExist(err) {