refactor: make some options more explicit

This commit is contained in:
Henrique Dias 2025-11-17 07:40:09 +01:00
parent 5560ea93b1
commit 35c7db20e5
No known key found for this signature in database
6 changed files with 103 additions and 65 deletions

View File

@ -30,6 +30,7 @@ var configCmd = &cobra.Command{
func addConfigFlags(flags *pflag.FlagSet) {
addServerFlags(flags)
addUserFlags(flags)
flags.BoolP("signup", "s", false, "allow users to signup")
flags.Bool("hideLoginButton", false, "hide login button from public pages")
flags.Bool("createUserDir", false, "generate user's home directory automatically")
@ -47,7 +48,6 @@ func addConfigFlags(flags *pflag.FlagSet) {
flags.String("branding.name", "", "replace 'File Browser' by this name")
flags.String("branding.theme", "", "set the theme")
flags.String("branding.color", "", "set the theme color")
flags.String("branding.files", "", "path to directory with images and custom styles")
flags.Bool("branding.disableExternal", false, "disable external links such as GitHub links")
flags.Bool("branding.disableUsedPercentage", false, "disable used disk percentage graph")
@ -204,6 +204,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
fmt.Fprintf(w, "Minimum Password Length:\t%d\n", set.MinimumPasswordLength)
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", set.Branding.Name)
fmt.Fprintf(w, "\tFiles override:\t%s\n", set.Branding.Files)
@ -211,6 +212,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
fmt.Fprintf(w, "\tDisable used disk percentage graph:\t%t\n", set.Branding.DisableUsedPercentage)
fmt.Fprintf(w, "\tColor:\t%s\n", set.Branding.Color)
fmt.Fprintf(w, "\tTheme:\t%s\n", set.Branding.Theme)
fmt.Fprintln(w, "\nServer:")
fmt.Fprintf(w, "\tLog:\t%s\n", ser.Log)
fmt.Fprintf(w, "\tPort:\t%s\n", ser.Port)
@ -221,9 +223,14 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
fmt.Fprintf(w, "\tTLS Cert:\t%s\n", ser.TLSCert)
fmt.Fprintf(w, "\tTLS Key:\t%s\n", ser.TLSKey)
fmt.Fprintf(w, "\tExec Enabled:\t%t\n", ser.EnableExec)
fmt.Fprintf(w, "\tThumbnails Enabled:\t%t\n", ser.EnableThumbnails)
fmt.Fprintf(w, "\tResize Preview:\t%t\n", ser.ResizePreview)
fmt.Fprintf(w, "\tType Detection by Header:\t%t\n", ser.TypeDetectionByHeader)
fmt.Fprintln(w, "\nTUS:")
fmt.Fprintf(w, "\tChunk size:\t%d\n", set.Tus.ChunkSize)
fmt.Fprintf(w, "\tRetry count:\t%d\n", set.Tus.RetryCount)
fmt.Fprintln(w, "\nDefaults:")
fmt.Fprintf(w, "\tScope:\t%s\n", set.Defaults.Scope)
fmt.Fprintf(w, "\tHideDotfiles:\t%t\n", set.Defaults.HideDotfiles)
@ -234,9 +241,11 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
fmt.Fprintf(w, "\tDirectory Creation Mode:\t%O\n", set.DirMode)
fmt.Fprintf(w, "\tCommands:\t%s\n", strings.Join(set.Defaults.Commands, " "))
fmt.Fprintf(w, "\tAce editor syntax highlighting theme:\t%s\n", set.Defaults.AceEditorTheme)
fmt.Fprintf(w, "\tSorting:\n")
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", set.Defaults.Perm.Admin)
fmt.Fprintf(w, "\t\tExecute:\t%t\n", set.Defaults.Perm.Execute)
@ -246,6 +255,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
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

@ -37,7 +37,7 @@ The path must be for a json or yaml file.`,
RunE: python(func(_ *cobra.Command, args []string, d *pythonData) error {
var key []byte
var err error
if d.hadDB {
if d.databaseExisted {
settings, settingErr := d.store.Settings.Get()
if settingErr != nil {
return settingErr
@ -104,7 +104,7 @@ The path must be for a json or yaml file.`,
}
return printSettings(file.Server, file.Settings, auther)
}, pythonConfig{allowNoDB: true}),
}, pythonConfig{allowsNoDatabase: true}),
}
func getAuther(sample auth.Auther, data interface{}) (interface{}, error) {

View File

@ -176,5 +176,5 @@ Now add your first user via 'filebrowser users add' and then you just
need to call the main command to boot up the server.
`)
return printSettings(ser, s, auther)
}, pythonConfig{noDB: true}),
}, pythonConfig{expectsNoDatabase: true}),
}

View File

@ -37,28 +37,47 @@ you want to change. Other options will remain unchanged.`,
}
switch flag.Name {
case "baseURL":
ser.BaseURL, err = flags.GetString(flag.Name)
case "root":
ser.Root, err = flags.GetString(flag.Name)
case "socket":
ser.Socket, err = flags.GetString(flag.Name)
// Server flags from [addServerFlags]
case "address":
ser.Address, err = flags.GetString(flag.Name)
case "log":
ser.Log, err = flags.GetString(flag.Name)
case "port":
ser.Port, err = flags.GetString(flag.Name)
case "cert":
ser.TLSCert, err = flags.GetString(flag.Name)
case "key":
ser.TLSKey, err = flags.GetString(flag.Name)
case "address":
ser.Address, err = flags.GetString(flag.Name)
case "port":
ser.Port, err = flags.GetString(flag.Name)
case "log":
ser.Log, err = flags.GetString(flag.Name)
case "hideLoginButton":
set.HideLoginButton, err = flags.GetBool(flag.Name)
case "root":
ser.Root, err = flags.GetString(flag.Name)
case "socket":
ser.Socket, err = flags.GetString(flag.Name)
case "baseURL":
ser.BaseURL, err = flags.GetString(flag.Name)
case "tokenExpirationTime":
ser.TokenExpirationTime, err = flags.GetString(flag.Name)
case "disableThumbnails":
ser.EnableThumbnails, err = flags.GetBool(flag.Name)
ser.EnableThumbnails = !ser.EnableThumbnails
case "disablePreviewResize":
ser.ResizePreview, err = flags.GetBool(flag.Name)
ser.ResizePreview = !ser.ResizePreview
case "disableExec":
ser.EnableExec, err = flags.GetBool(flag.Name)
ser.EnableExec = !ser.EnableExec
case "disableTypeDetectionByHeader":
ser.TypeDetectionByHeader, err = flags.GetBool(flag.Name)
ser.TypeDetectionByHeader = !ser.TypeDetectionByHeader
// Settings flags from [addConfigFlags]
case "signup":
set.Signup, err = flags.GetBool(flag.Name)
case "auth.method":
hasAuth = true
case "hideLoginButton":
set.HideLoginButton, err = flags.GetBool(flag.Name)
case "createUserDir":
set.CreateUserDir, err = flags.GetBool(flag.Name)
case "minimumPasswordLength":
set.MinimumPasswordLength, err = flags.GetUint(flag.Name)
case "shell":
var shell string
shell, err = flags.GetString(flag.Name)
@ -66,22 +85,20 @@ you want to change. Other options will remain unchanged.`,
return
}
set.Shell = convertCmdStrToCmdArray(shell)
case "createUserDir":
set.CreateUserDir, err = flags.GetBool(flag.Name)
case "minimumPasswordLength":
set.MinimumPasswordLength, err = flags.GetUint(flag.Name)
case "auth.method":
hasAuth = true
case "branding.name":
set.Branding.Name, err = flags.GetString(flag.Name)
case "branding.color":
set.Branding.Color, err = flags.GetString(flag.Name)
case "branding.theme":
set.Branding.Theme, err = flags.GetString(flag.Name)
case "branding.color":
set.Branding.Color, err = flags.GetString(flag.Name)
case "branding.files":
set.Branding.Files, err = flags.GetString(flag.Name)
case "branding.disableExternal":
set.Branding.DisableExternal, err = flags.GetBool(flag.Name)
case "branding.disableUsedPercentage":
set.Branding.DisableUsedPercentage, err = flags.GetBool(flag.Name)
case "branding.files":
set.Branding.Files, err = flags.GetString(flag.Name)
case "fileMode":
set.FileMode, err = getAndParseFileMode(flags, flag.Name)
case "dirMode":

View File

@ -69,24 +69,30 @@ func migrateFlagNames(f *pflag.FlagSet, name string) pflag.NormalizedName {
func init() {
rootCmd.SilenceUsage = true
rootCmd.SetGlobalNormalizationFunc(migrateFlagNames)
cobra.MousetrapHelpText = ""
rootCmd.SetVersionTemplate("File Browser version {{printf \"%s\" .Version}}\n")
flags := rootCmd.Flags()
// Flags available across the whole program
persistent := rootCmd.PersistentFlags()
persistent.StringP("config", "c", "", "config file path")
persistent.StringP("database", "d", "./filebrowser.db", "database path")
// Runtime flags for the root command
flags := rootCmd.Flags()
flags.Bool("noauth", false, "use the noauth auther when using quick setup")
flags.String("username", "admin", "username for the first user when using quick setup")
flags.String("password", "", "hashed password for the first user when using quick setup")
flags.Uint32("socketPerm", 0666, "unix socket file permissions")
flags.String("cacheDir", "", "file cache directory (disabled if empty)")
flags.Int("imageProcessors", 4, "image processors count")
addServerFlags(flags)
rootCmd.SetGlobalNormalizationFunc(migrateFlagNames)
}
// addServerFlags adds server related flags to the given FlagSet. These flags are available
// in both the root command, config set and config init commands.
func addServerFlags(flags *pflag.FlagSet) {
flags.StringP("address", "a", "127.0.0.1", "address to listen on")
flags.StringP("log", "l", "stdout", "log output")
@ -95,11 +101,8 @@ func addServerFlags(flags *pflag.FlagSet) {
flags.StringP("key", "k", "", "tls key")
flags.StringP("root", "r", ".", "root to prepend to relative paths")
flags.String("socket", "", "socket to listen to (cannot be used with address, port, cert nor key flags)")
flags.Uint32("socketPerm", 0666, "unix socket file permissions")
flags.StringP("baseURL", "b", "", "base url")
flags.String("cacheDir", "", "file cache directory (disabled if empty)")
flags.String("tokenExpirationTime", "2h", "user session timeout")
flags.Int("imageProcessors", 4, "image processors count")
flags.Bool("disableThumbnails", false, "disable image thumbnails")
flags.Bool("disablePreviewResize", false, "disable resize of image previews")
flags.Bool("disableExec", true, "disables Command Runner feature")
@ -118,10 +121,11 @@ it. Don't worry: you don't need to setup a separate database server.
We're using Bolt DB which is a single file database and all managed
by ourselves.
All flags are available as environmental variables, except for "--config",
which specifies the configuration file to use. The environment variables
are prefixed by "FB_" followed by the flag name in UPPER_SNAKE_CASE. For
example, the flag "--disablePreviewResize" is as FB_DISABLE_PREVIEW_RESIZE.
For this command, all flags are available as environmental variables,
except for "--config", which specifies the configuration file to use.
The environment variables are prefixed by "FB_" followed by the flag name in
UPPER_SNAKE_CASE. For example, the flag "--disablePreviewResize" is available
as FB_DISABLE_PREVIEW_RESIZE.
If "--config" is not specified, File Browser will look for a configuration
file named .filebrowser.{json, toml, yaml, yml} in the following directories:
@ -142,8 +146,8 @@ Also, if the database path doesn't exist, File Browser will enter into
the quick setup mode and a new database will be bootstrapped and a new
user created with the credentials from options "username" and "password".`,
RunE: python(func(cmd *cobra.Command, _ []string, d *pythonData) error {
if !d.hadDB {
err := quickSetup(d.viper, *d)
if !d.databaseExisted {
err := quickSetup(*d)
if err != nil {
return err
}
@ -257,7 +261,7 @@ user created with the credentials from options "username" and "password".`,
log.Println("Graceful shutdown complete.")
return nil
}, pythonConfig{allowNoDB: true}),
}, pythonConfig{allowsNoDatabase: true}),
}
func getServerSettings(v *viper.Viper, st *storage.Storage) (*settings.Server, error) {
@ -356,7 +360,7 @@ func setupLog(logMethod string) {
}
}
func quickSetup(v *viper.Viper, d pythonData) error {
func quickSetup(d pythonData) error {
log.Println("Performing quick setup")
set := &settings.Settings{
@ -370,7 +374,7 @@ func quickSetup(v *viper.Viper, d pythonData) error {
Scope: ".",
Locale: "en",
SingleClick: false,
AceEditorTheme: v.GetString("defaults.aceEditorTheme"),
AceEditorTheme: d.viper.GetString("defaults.aceEditorTheme"),
Perm: users.Permissions{
Admin: false,
Execute: true,
@ -394,7 +398,7 @@ func quickSetup(v *viper.Viper, d pythonData) error {
}
var err error
if _, noauth := vGetStringIsSet(v, "noauth"); noauth {
if _, noauth := vGetStringIsSet(d.viper, "noauth"); noauth {
set.AuthMethod = auth.MethodNoAuth
err = d.store.Auth.Save(&auth.NoAuth{})
} else {
@ -411,13 +415,13 @@ func quickSetup(v *viper.Viper, d pythonData) error {
}
ser := &settings.Server{
BaseURL: v.GetString("baseURL"),
Port: v.GetString("port"),
Log: v.GetString("log"),
TLSKey: v.GetString("key"),
TLSCert: v.GetString("cert"),
Address: v.GetString("address"),
Root: v.GetString("root"),
BaseURL: d.viper.GetString("baseURL"),
Port: d.viper.GetString("port"),
Log: d.viper.GetString("log"),
TLSKey: d.viper.GetString("key"),
TLSCert: d.viper.GetString("cert"),
Address: d.viper.GetString("address"),
Root: d.viper.GetString("root"),
}
err = d.store.Settings.SaveServer(ser)
@ -425,8 +429,8 @@ func quickSetup(v *viper.Viper, d pythonData) error {
return err
}
username := v.GetString("username")
password := v.GetString("password")
username := d.viper.GetString("username")
password := d.viper.GetString("password")
if password == "" {
var pwd string

View File

@ -135,14 +135,14 @@ type cobraFunc func(cmd *cobra.Command, args []string) error
type pythonFunc func(cmd *cobra.Command, args []string, data *pythonData) error
type pythonConfig struct {
noDB bool
allowNoDB bool
expectsNoDatabase bool
allowsNoDatabase bool
}
type pythonData struct {
hadDB bool
viper *viper.Viper
store *storage.Storage
databaseExisted bool
viper *viper.Viper
store *storage.Storage
}
func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
@ -152,9 +152,16 @@ func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
return err
}
data := &pythonData{hadDB: true, viper: v}
data := &pythonData{databaseExisted: true}
path := v.GetString("database")
// Only make the viper instance available to the root command (filebrowser).
// This is to make sure that we don't make the mistake of using it somewhere
// else.
if cmd.Name() == "filebrowser" {
data.viper = v
}
absPath, err := filepath.Abs(path)
if err != nil {
return err
@ -163,16 +170,16 @@ func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
exists, err := dbExists(path)
if err != nil {
return err
} else if exists && cfg.noDB {
} else if exists && cfg.expectsNoDatabase {
log.Fatal(absPath + " already exists")
} else if !exists && !cfg.noDB && !cfg.allowNoDB {
} else if !exists && !cfg.expectsNoDatabase && !cfg.allowsNoDatabase {
log.Fatal(absPath + " does not exist. Please run 'filebrowser config init' first.")
} else if !exists && !cfg.noDB {
} else if !exists && !cfg.expectsNoDatabase {
log.Println("Warning: filebrowser.db can't be found. Initialing in " + strings.TrimSuffix(absPath, "filebrowser.db"))
}
log.Println("Using database: " + absPath)
data.hadDB = exists
data.databaseExisted = exists
db, err := storm.Open(path, storm.BoltOptions(databasePermissions, nil))
if err != nil {