[WIP] add viper to manage envvars and cfg files

Former-commit-id: 99d241dc790629791b67b7b2ce20a10341a1c1e7 [formerly 08e8410bffdb50134a2d6ccbe853b25d4c4d3fc6] [formerly f32b18d9acb42ee1988c49b7980a16a606cee142 [formerly 1080bfde68]]
Former-commit-id: 318c4f159697e896bcf824e274907c5c03046bf0 [formerly 82533ae817673dfab8d56533f15a9931c0c74c1e]
Former-commit-id: ece42aa8e8c7480a5a21614b6ca397b71a01e1fa
This commit is contained in:
1138-4EB
2019-01-06 06:11:15 +01:00
parent 2def76df83
commit 90b7f4aaf6
7 changed files with 261 additions and 111 deletions

View File

@@ -3,13 +3,13 @@ package cmd
import (
"crypto/rand"
"crypto/tls"
"errors"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"strconv"
"strings"
"github.com/asdine/storm"
"github.com/filebrowser/filebrowser/v2/auth"
@@ -18,26 +18,13 @@ import (
"github.com/filebrowser/filebrowser/v2/users"
fbhttp "github.com/filebrowser/filebrowser/v2/http"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
// "github.com/spf13/pflag"
v "github.com/spf13/viper"
lumberjack "gopkg.in/natefinch/lumberjack.v2"
)
var (
databasePath string
)
func init() {
rootCmd.PersistentFlags().StringVarP(&databasePath, "database", "d", "./filebrowser.db", "path to the database")
rootCmd.Flags().StringP("address", "a", "", "address to listen on (default comes from database)")
rootCmd.Flags().StringP("log", "l", "", "log output (default comes from database)")
rootCmd.Flags().IntP("port", "p", 0, "port to listen on (default comes from database)")
rootCmd.Flags().StringP("cert", "c", "", "tls certificate (default comes from database)")
rootCmd.Flags().StringP("key", "k", "", "tls key (default comes from database)")
rootCmd.Flags().StringP("scope", "s", "", "scope for users")
}
var rootCmd = &cobra.Command{
Use: "filebrowser",
Short: "A stylish web-based file browser",
@@ -56,17 +43,211 @@ Use the available flags to override the database/default options. These flags
values won't be persisted to the database. To persist configuration to the database
use the command 'filebrowser config set'.`,
Run: func(cmd *cobra.Command, args []string) {
if _, err := os.Stat(databasePath); os.IsNotExist(err) {
quickSetup(cmd)
}
db := getDB()
defer db.Close()
st := getStorage(db)
startServer(cmd, st)
startServer(st)
},
}
var (
cfgFile string
)
// POSSIBLE WORKAROUND TO IDENTIFY WHEN DEFAULT VALUES ARE BEING USED
var defaults = struct {
database string
address string
log string
port int
scope string
admin string
}{
"./filebrowser.db",
"127.0.0.1",
"stderr",
80,
"/srv",
"admin",
}
func init() {
cobra.OnInitialize(initConfig)
//rootCmd.SetVersionTemplate("File Browser {{printf \"version %s\" .Version}}\n")
f := rootCmd.Flags()
pf := rootCmd.PersistentFlags()
pf.StringVarP(&cfgFile, "config", "c", "", "config file (defaults are './.filebrowser[ext]', '$HOME/.filebrowser[ext]' or '/etc/filebrowser/.filebrowser[ext]')")
vaddP(pf, "database", "d", "./filebrowser.db", "path to the database")
vaddP(f, "address", "a", defaults.address, "address to listen on")
vaddP(f, "log", "l", defaults.log, "log output")
vaddP(f, "port", "p", defaults.port, "port to listen on")
vaddP(f, "cert", "t", "", "tls certificate (default comes from database)")
vaddP(f, "key", "k", "", "tls key (default comes from database)")
vaddP(f, "scope", "s", defaults.scope, "scope for users")
vaddP(f, "force", "f", false, "overwrite DB config with runtime params")
vaddP(f, "admin", "f", defaults.admin, "first username")
vaddP(f, "passwd", "f", "", "first username password hash")
vaddP(f, "baseurl", "b", "", "base URL")
// Bind the full flag sets to the configuration
if err := v.BindPFlags(f); err != nil {
panic(err)
}
if err := v.BindPFlags(pf); err != nil {
panic(err)
}
}
// initConfig reads in config file and ENV variables if set.
func initConfig() {
if cfgFile == "" {
// Find home directory.
home, err := homedir.Dir()
if err != nil {
panic(err)
}
v.AddConfigPath(".")
v.AddConfigPath(home)
v.AddConfigPath("/etc/filebrowser/")
v.SetConfigName(".filebrowser")
} else {
// Use config file from the flag.
v.SetConfigFile(cfgFile)
}
v.SetEnvPrefix("FB")
v.AutomaticEnv()
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
if err := v.ReadInConfig(); err != nil {
if _, ok := err.(v.ConfigParseError); ok {
panic(err)
}
log.Println("No config file provided")
} else {
log.Println("Using config file:", v.ConfigFileUsed())
}
log.Println("FORCE:", v.GetBool("force"))
/*
if DB exists
if force false
database has highest priority, if undefined in DB use config params
else
config params overwrite existing and non-existing params in DB
else
(quick)Setup with provided config params
*/
/*
DISPLAY WARNINGS WHEN DEFAULT VALUES ARE USED
This allows to know if a CLI flag was provided:
log.Println(rootCmd.Flags().Changed("database"))
However, that is not enough in order to know if a value came from a config file or from envvars.
This should allow so. But it seems not to work as expected (see spf13/viper#323):
log.Println(v.IsSet("database"))
*/
if _, err := os.Stat(v.GetString("database")); os.IsNotExist(err) {
quickSetup()
}
}
/*
func serverVisitAndReplace(s *settings.Settings) {
rootCmd.Flags().Visit(func(flag *pflag.Flag) {
switch flag.Name {
case "log":
s.Log = v.GetString(flag.Name)
case "address":
s.Server.Address = v.GetString(flag.Name)
case "port":
s.Server.Port = v.GetInt(flag.Name)
case "cert":
s.Server.TLSCert = v.GetString(flag.Name)
case "key":
s.Server.TLSKey = v.GetString(flag.Name)
}
})
}
*/
func quickSetup() {
scope := v.GetString("scope")
if scope == defaults.scope {
log.Println("[WARN] Using default value '/srv' as param 'scope'")
}
db, err := storm.Open(v.GetString("database"))
checkErr(err)
defer db.Close()
set := &settings.Settings{
Key: generateRandomBytes(64), // 256 bit
BaseURL: v.GetString("baseurl"),
Log: v.GetString("log"),
Signup: false,
AuthMethod: auth.MethodJSONAuth,
Server: settings.Server{
Port: v.GetInt("port"),
Address: v.GetString("address"),
TLSCert: v.GetString("cert"),
TLSKey: v.GetString("key"),
},
Defaults: settings.UserDefaults{
Scope: scope,
Locale: "en",
Perm: users.Permissions{
Admin: false,
Execute: true,
Create: true,
Rename: true,
Modify: true,
Delete: true,
Share: true,
Download: true,
},
},
}
// serverVisitAndReplace(set)
st := getStorage(db)
err = st.Settings.Save(set)
checkErr(err)
err = st.Auth.Save(&auth.JSONAuth{})
checkErr(err)
password := v.GetString("password")
if password == "" {
password, err = users.HashPwd("admin")
checkErr(err)
}
user := &users.User{
Username: v.GetString("admin"),
Password: password,
LockPassword: false,
}
set.Defaults.Apply(user)
user.Perm.Admin = true
err = st.Users.Save(user)
checkErr(err)
}
func setupLogger(s *settings.Settings) {
switch s.Log {
case "stdout":
@@ -85,91 +266,11 @@ func setupLogger(s *settings.Settings) {
}
}
func serverVisitAndReplace(cmd *cobra.Command, s *settings.Settings) {
cmd.Flags().Visit(func(flag *pflag.Flag) {
switch flag.Name {
case "log":
s.Log = mustGetString(cmd, flag.Name)
case "address":
s.Server.Address = mustGetString(cmd, flag.Name)
case "port":
s.Server.Port = mustGetInt(cmd, flag.Name)
case "cert":
s.Server.TLSCert = mustGetString(cmd, flag.Name)
case "key":
s.Server.TLSKey = mustGetString(cmd, flag.Name)
}
})
}
func quickSetup(cmd *cobra.Command) {
scope := mustGetString(cmd, "scope")
if scope == "" {
panic(errors.New("scope flag must be set for quick setup"))
}
db, err := storm.Open(databasePath)
checkErr(err)
defer db.Close()
set := &settings.Settings{
Key: generateRandomBytes(64), // 256 bit
BaseURL: "",
Log: "stderr",
Signup: false,
AuthMethod: auth.MethodJSONAuth,
Server: settings.Server{
Port: 0,
Address: "127.0.0.1",
TLSCert: mustGetString(cmd, "cert"),
TLSKey: mustGetString(cmd, "key"),
},
Defaults: settings.UserDefaults{
Scope: scope,
Locale: "en",
Perm: users.Permissions{
Admin: false,
Execute: true,
Create: true,
Rename: true,
Modify: true,
Delete: true,
Share: true,
Download: true,
},
},
}
serverVisitAndReplace(cmd, set)
st := getStorage(db)
err = st.Settings.Save(set)
checkErr(err)
err = st.Auth.Save(&auth.JSONAuth{})
checkErr(err)
password, err := users.HashPwd("admin")
checkErr(err)
user := &users.User{
Username: "admin",
Password: password,
LockPassword: false,
}
set.Defaults.Apply(user)
user.Perm.Admin = true
err = st.Users.Save(user)
checkErr(err)
}
func startServer(cmd *cobra.Command, st *storage.Storage) {
func startServer(st *storage.Storage) {
settings, err := st.Settings.Get()
checkErr(err)
serverVisitAndReplace(cmd, settings)
// serverVisitAndReplace(settings)
setupLogger(settings)
handler, err := fbhttp.NewHandler(st)