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) { func addConfigFlags(flags *pflag.FlagSet) {
addServerFlags(flags) addServerFlags(flags)
addUserFlags(flags) addUserFlags(flags)
flags.BoolP("signup", "s", false, "allow users to signup") flags.BoolP("signup", "s", false, "allow users to signup")
flags.Bool("hideLoginButton", false, "hide login button from public pages") flags.Bool("hideLoginButton", false, "hide login button from public pages")
flags.Bool("createUserDir", false, "generate user's home directory automatically") 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.name", "", "replace 'File Browser' by this name")
flags.String("branding.theme", "", "set the theme") flags.String("branding.theme", "", "set the theme")
flags.String("branding.color", "", "set the theme color") flags.String("branding.color", "", "set the theme color")
flags.String("branding.files", "", "path to directory with images and custom styles") 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.disableExternal", false, "disable external links such as GitHub links")
flags.Bool("branding.disableUsedPercentage", false, "disable used disk percentage graph") 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, "Minimum Password Length:\t%d\n", set.MinimumPasswordLength)
fmt.Fprintf(w, "Auth Method:\t%s\n", set.AuthMethod) fmt.Fprintf(w, "Auth Method:\t%s\n", set.AuthMethod)
fmt.Fprintf(w, "Shell:\t%s\t\n", strings.Join(set.Shell, " ")) fmt.Fprintf(w, "Shell:\t%s\t\n", strings.Join(set.Shell, " "))
fmt.Fprintln(w, "\nBranding:") fmt.Fprintln(w, "\nBranding:")
fmt.Fprintf(w, "\tName:\t%s\n", set.Branding.Name) fmt.Fprintf(w, "\tName:\t%s\n", set.Branding.Name)
fmt.Fprintf(w, "\tFiles override:\t%s\n", set.Branding.Files) 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, "\tDisable used disk percentage graph:\t%t\n", set.Branding.DisableUsedPercentage)
fmt.Fprintf(w, "\tColor:\t%s\n", set.Branding.Color) fmt.Fprintf(w, "\tColor:\t%s\n", set.Branding.Color)
fmt.Fprintf(w, "\tTheme:\t%s\n", set.Branding.Theme) fmt.Fprintf(w, "\tTheme:\t%s\n", set.Branding.Theme)
fmt.Fprintln(w, "\nServer:") fmt.Fprintln(w, "\nServer:")
fmt.Fprintf(w, "\tLog:\t%s\n", ser.Log) fmt.Fprintf(w, "\tLog:\t%s\n", ser.Log)
fmt.Fprintf(w, "\tPort:\t%s\n", ser.Port) 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 Cert:\t%s\n", ser.TLSCert)
fmt.Fprintf(w, "\tTLS Key:\t%s\n", ser.TLSKey) fmt.Fprintf(w, "\tTLS Key:\t%s\n", ser.TLSKey)
fmt.Fprintf(w, "\tExec Enabled:\t%t\n", ser.EnableExec) 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.Fprintln(w, "\nTUS:")
fmt.Fprintf(w, "\tChunk size:\t%d\n", set.Tus.ChunkSize) fmt.Fprintf(w, "\tChunk size:\t%d\n", set.Tus.ChunkSize)
fmt.Fprintf(w, "\tRetry count:\t%d\n", set.Tus.RetryCount) fmt.Fprintf(w, "\tRetry count:\t%d\n", set.Tus.RetryCount)
fmt.Fprintln(w, "\nDefaults:") fmt.Fprintln(w, "\nDefaults:")
fmt.Fprintf(w, "\tScope:\t%s\n", set.Defaults.Scope) fmt.Fprintf(w, "\tScope:\t%s\n", set.Defaults.Scope)
fmt.Fprintf(w, "\tHideDotfiles:\t%t\n", set.Defaults.HideDotfiles) 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, "\tDirectory Creation Mode:\t%O\n", set.DirMode)
fmt.Fprintf(w, "\tCommands:\t%s\n", strings.Join(set.Defaults.Commands, " ")) 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, "\tAce editor syntax highlighting theme:\t%s\n", set.Defaults.AceEditorTheme)
fmt.Fprintf(w, "\tSorting:\n") fmt.Fprintf(w, "\tSorting:\n")
fmt.Fprintf(w, "\t\tBy:\t%s\n", set.Defaults.Sorting.By) 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, "\t\tAsc:\t%t\n", set.Defaults.Sorting.Asc)
fmt.Fprintf(w, "\tPermissions:\n") fmt.Fprintf(w, "\tPermissions:\n")
fmt.Fprintf(w, "\t\tAdmin:\t%t\n", set.Defaults.Perm.Admin) 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\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\tDelete:\t%t\n", set.Defaults.Perm.Delete)
fmt.Fprintf(w, "\t\tShare:\t%t\n", set.Defaults.Perm.Share) fmt.Fprintf(w, "\t\tShare:\t%t\n", set.Defaults.Perm.Share)
fmt.Fprintf(w, "\t\tDownload:\t%t\n", set.Defaults.Perm.Download) fmt.Fprintf(w, "\t\tDownload:\t%t\n", set.Defaults.Perm.Download)
w.Flush() w.Flush()
b, err := json.MarshalIndent(auther, "", " ") 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 { RunE: python(func(_ *cobra.Command, args []string, d *pythonData) error {
var key []byte var key []byte
var err error var err error
if d.hadDB { if d.databaseExisted {
settings, settingErr := d.store.Settings.Get() settings, settingErr := d.store.Settings.Get()
if settingErr != nil { if settingErr != nil {
return settingErr return settingErr
@ -104,7 +104,7 @@ The path must be for a json or yaml file.`,
} }
return printSettings(file.Server, file.Settings, auther) return printSettings(file.Server, file.Settings, auther)
}, pythonConfig{allowNoDB: true}), }, pythonConfig{allowsNoDatabase: true}),
} }
func getAuther(sample auth.Auther, data interface{}) (interface{}, error) { 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. need to call the main command to boot up the server.
`) `)
return printSettings(ser, s, auther) 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 { switch flag.Name {
case "baseURL": // Server flags from [addServerFlags]
ser.BaseURL, err = flags.GetString(flag.Name) case "address":
case "root": ser.Address, err = flags.GetString(flag.Name)
ser.Root, err = flags.GetString(flag.Name) case "log":
case "socket": ser.Log, err = flags.GetString(flag.Name)
ser.Socket, err = flags.GetString(flag.Name) case "port":
ser.Port, err = flags.GetString(flag.Name)
case "cert": case "cert":
ser.TLSCert, err = flags.GetString(flag.Name) ser.TLSCert, err = flags.GetString(flag.Name)
case "key": case "key":
ser.TLSKey, err = flags.GetString(flag.Name) ser.TLSKey, err = flags.GetString(flag.Name)
case "address": case "root":
ser.Address, err = flags.GetString(flag.Name) ser.Root, err = flags.GetString(flag.Name)
case "port": case "socket":
ser.Port, err = flags.GetString(flag.Name) ser.Socket, err = flags.GetString(flag.Name)
case "log": case "baseURL":
ser.Log, err = flags.GetString(flag.Name) ser.BaseURL, err = flags.GetString(flag.Name)
case "hideLoginButton": case "tokenExpirationTime":
set.HideLoginButton, err = flags.GetBool(flag.Name) 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": case "signup":
set.Signup, err = flags.GetBool(flag.Name) set.Signup, err = flags.GetBool(flag.Name)
case "auth.method": case "hideLoginButton":
hasAuth = true 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": case "shell":
var shell string var shell string
shell, err = flags.GetString(flag.Name) shell, err = flags.GetString(flag.Name)
@ -66,22 +85,20 @@ you want to change. Other options will remain unchanged.`,
return return
} }
set.Shell = convertCmdStrToCmdArray(shell) set.Shell = convertCmdStrToCmdArray(shell)
case "createUserDir": case "auth.method":
set.CreateUserDir, err = flags.GetBool(flag.Name) hasAuth = true
case "minimumPasswordLength":
set.MinimumPasswordLength, err = flags.GetUint(flag.Name)
case "branding.name": case "branding.name":
set.Branding.Name, err = flags.GetString(flag.Name) set.Branding.Name, err = flags.GetString(flag.Name)
case "branding.color":
set.Branding.Color, err = flags.GetString(flag.Name)
case "branding.theme": case "branding.theme":
set.Branding.Theme, err = flags.GetString(flag.Name) 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": case "branding.disableExternal":
set.Branding.DisableExternal, err = flags.GetBool(flag.Name) set.Branding.DisableExternal, err = flags.GetBool(flag.Name)
case "branding.disableUsedPercentage": case "branding.disableUsedPercentage":
set.Branding.DisableUsedPercentage, err = flags.GetBool(flag.Name) set.Branding.DisableUsedPercentage, err = flags.GetBool(flag.Name)
case "branding.files":
set.Branding.Files, err = flags.GetString(flag.Name)
case "fileMode": case "fileMode":
set.FileMode, err = getAndParseFileMode(flags, flag.Name) set.FileMode, err = getAndParseFileMode(flags, flag.Name)
case "dirMode": case "dirMode":

View File

@ -69,24 +69,30 @@ func migrateFlagNames(f *pflag.FlagSet, name string) pflag.NormalizedName {
func init() { func init() {
rootCmd.SilenceUsage = true rootCmd.SilenceUsage = true
rootCmd.SetGlobalNormalizationFunc(migrateFlagNames)
cobra.MousetrapHelpText = "" cobra.MousetrapHelpText = ""
rootCmd.SetVersionTemplate("File Browser version {{printf \"%s\" .Version}}\n") rootCmd.SetVersionTemplate("File Browser version {{printf \"%s\" .Version}}\n")
flags := rootCmd.Flags() // Flags available across the whole program
persistent := rootCmd.PersistentFlags() persistent := rootCmd.PersistentFlags()
persistent.StringP("config", "c", "", "config file path") persistent.StringP("config", "c", "", "config file path")
persistent.StringP("database", "d", "./filebrowser.db", "database 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.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("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.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) 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) { func addServerFlags(flags *pflag.FlagSet) {
flags.StringP("address", "a", "127.0.0.1", "address to listen on") flags.StringP("address", "a", "127.0.0.1", "address to listen on")
flags.StringP("log", "l", "stdout", "log output") flags.StringP("log", "l", "stdout", "log output")
@ -95,11 +101,8 @@ func addServerFlags(flags *pflag.FlagSet) {
flags.StringP("key", "k", "", "tls key") flags.StringP("key", "k", "", "tls key")
flags.StringP("root", "r", ".", "root to prepend to relative paths") 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.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.StringP("baseURL", "b", "", "base url")
flags.String("cacheDir", "", "file cache directory (disabled if empty)")
flags.String("tokenExpirationTime", "2h", "user session timeout") flags.String("tokenExpirationTime", "2h", "user session timeout")
flags.Int("imageProcessors", 4, "image processors count")
flags.Bool("disableThumbnails", false, "disable image thumbnails") flags.Bool("disableThumbnails", false, "disable image thumbnails")
flags.Bool("disablePreviewResize", false, "disable resize of image previews") flags.Bool("disablePreviewResize", false, "disable resize of image previews")
flags.Bool("disableExec", true, "disables Command Runner feature") 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 We're using Bolt DB which is a single file database and all managed
by ourselves. by ourselves.
All flags are available as environmental variables, except for "--config", For this command, all flags are available as environmental variables,
which specifies the configuration file to use. The environment variables except for "--config", which specifies the configuration file to use.
are prefixed by "FB_" followed by the flag name in UPPER_SNAKE_CASE. For The environment variables are prefixed by "FB_" followed by the flag name in
example, the flag "--disablePreviewResize" is as FB_DISABLE_PREVIEW_RESIZE. 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 If "--config" is not specified, File Browser will look for a configuration
file named .filebrowser.{json, toml, yaml, yml} in the following directories: 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 the quick setup mode and a new database will be bootstrapped and a new
user created with the credentials from options "username" and "password".`, user created with the credentials from options "username" and "password".`,
RunE: python(func(cmd *cobra.Command, _ []string, d *pythonData) error { RunE: python(func(cmd *cobra.Command, _ []string, d *pythonData) error {
if !d.hadDB { if !d.databaseExisted {
err := quickSetup(d.viper, *d) err := quickSetup(*d)
if err != nil { if err != nil {
return err return err
} }
@ -257,7 +261,7 @@ user created with the credentials from options "username" and "password".`,
log.Println("Graceful shutdown complete.") log.Println("Graceful shutdown complete.")
return nil return nil
}, pythonConfig{allowNoDB: true}), }, pythonConfig{allowsNoDatabase: true}),
} }
func getServerSettings(v *viper.Viper, st *storage.Storage) (*settings.Server, error) { 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") log.Println("Performing quick setup")
set := &settings.Settings{ set := &settings.Settings{
@ -370,7 +374,7 @@ func quickSetup(v *viper.Viper, d pythonData) error {
Scope: ".", Scope: ".",
Locale: "en", Locale: "en",
SingleClick: false, SingleClick: false,
AceEditorTheme: v.GetString("defaults.aceEditorTheme"), AceEditorTheme: d.viper.GetString("defaults.aceEditorTheme"),
Perm: users.Permissions{ Perm: users.Permissions{
Admin: false, Admin: false,
Execute: true, Execute: true,
@ -394,7 +398,7 @@ func quickSetup(v *viper.Viper, d pythonData) error {
} }
var err error var err error
if _, noauth := vGetStringIsSet(v, "noauth"); noauth { if _, noauth := vGetStringIsSet(d.viper, "noauth"); noauth {
set.AuthMethod = auth.MethodNoAuth set.AuthMethod = auth.MethodNoAuth
err = d.store.Auth.Save(&auth.NoAuth{}) err = d.store.Auth.Save(&auth.NoAuth{})
} else { } else {
@ -411,13 +415,13 @@ func quickSetup(v *viper.Viper, d pythonData) error {
} }
ser := &settings.Server{ ser := &settings.Server{
BaseURL: v.GetString("baseURL"), BaseURL: d.viper.GetString("baseURL"),
Port: v.GetString("port"), Port: d.viper.GetString("port"),
Log: v.GetString("log"), Log: d.viper.GetString("log"),
TLSKey: v.GetString("key"), TLSKey: d.viper.GetString("key"),
TLSCert: v.GetString("cert"), TLSCert: d.viper.GetString("cert"),
Address: v.GetString("address"), Address: d.viper.GetString("address"),
Root: v.GetString("root"), Root: d.viper.GetString("root"),
} }
err = d.store.Settings.SaveServer(ser) err = d.store.Settings.SaveServer(ser)
@ -425,8 +429,8 @@ func quickSetup(v *viper.Viper, d pythonData) error {
return err return err
} }
username := v.GetString("username") username := d.viper.GetString("username")
password := v.GetString("password") password := d.viper.GetString("password")
if password == "" { if password == "" {
var pwd string 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 pythonFunc func(cmd *cobra.Command, args []string, data *pythonData) error
type pythonConfig struct { type pythonConfig struct {
noDB bool expectsNoDatabase bool
allowNoDB bool allowsNoDatabase bool
} }
type pythonData struct { type pythonData struct {
hadDB bool databaseExisted bool
viper *viper.Viper viper *viper.Viper
store *storage.Storage store *storage.Storage
} }
func python(fn pythonFunc, cfg pythonConfig) cobraFunc { func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
@ -152,9 +152,16 @@ func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
return err return err
} }
data := &pythonData{hadDB: true, viper: v} data := &pythonData{databaseExisted: true}
path := v.GetString("database") 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) absPath, err := filepath.Abs(path)
if err != nil { if err != nil {
return err return err
@ -163,16 +170,16 @@ func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
exists, err := dbExists(path) exists, err := dbExists(path)
if err != nil { if err != nil {
return err return err
} else if exists && cfg.noDB { } else if exists && cfg.expectsNoDatabase {
log.Fatal(absPath + " already exists") 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.") 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("Warning: filebrowser.db can't be found. Initialing in " + strings.TrimSuffix(absPath, "filebrowser.db"))
} }
log.Println("Using database: " + absPath) log.Println("Using database: " + absPath)
data.hadDB = exists data.databaseExisted = exists
db, err := storm.Open(path, storm.BoltOptions(databasePermissions, nil)) db, err := storm.Open(path, storm.BoltOptions(databasePermissions, nil))
if err != nil { if err != nil {