You've already forked filebrowser
mirror of
https://github.com/filebrowser/filebrowser.git
synced 2025-11-26 14:25:26 +08:00
[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:
309
cmd/root.go
309
cmd/root.go
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user