diff --git a/cmd/cmds_add.go b/cmd/cmds_add.go index 1a6f4ba2..3fcd7141 100644 --- a/cmd/cmds_add.go +++ b/cmd/cmds_add.go @@ -15,18 +15,13 @@ var cmdsAddCmd = &cobra.Command{ Short: "Add a command to run on a specific event", Long: `Add a command to run on a specific event.`, Args: cobra.MinimumNArgs(2), - Run: func(cmd *cobra.Command, args []string) { - db := getDB() - defer db.Close() - st := getStorage(db) - s, err := st.Settings.Get() + Run: python(func(cmd *cobra.Command, args []string, d pythonData) { + s, err := d.store.Settings.Get() checkErr(err) - command := strings.Join(args[1:], " ") - s.Commands[args[0]] = append(s.Commands[args[0]], command) - err = st.Settings.Save(s) + err = d.store.Settings.Save(s) checkErr(err) printEvents(s.Commands) - }, + }, pythonConfig{}), } diff --git a/cmd/cmds_ls.go b/cmd/cmds_ls.go index e8a03ae1..f688ede6 100644 --- a/cmd/cmds_ls.go +++ b/cmd/cmds_ls.go @@ -14,11 +14,8 @@ var cmdsLsCmd = &cobra.Command{ Short: "List all commands for each event", Long: `List all commands for each event.`, Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - db := getDB() - defer db.Close() - st := getStorage(db) - s, err := st.Settings.Get() + Run: python(func(cmd *cobra.Command, args []string, d pythonData) { + s, err := d.store.Settings.Get() checkErr(err) evt := mustGetString(cmd, "event") @@ -30,5 +27,5 @@ var cmdsLsCmd = &cobra.Command{ show["after_"+evt] = s.Commands["after_"+evt] printEvents(show) } - }, + }, pythonConfig{}), } diff --git a/cmd/cmds_rm.go b/cmd/cmds_rm.go index 002bdb6a..e0a32d39 100644 --- a/cmd/cmds_rm.go +++ b/cmd/cmds_rm.go @@ -27,11 +27,8 @@ var cmdsRmCmd = &cobra.Command{ return nil }, - Run: func(cmd *cobra.Command, args []string) { - db := getDB() - defer db.Close() - st := getStorage(db) - s, err := st.Settings.Get() + Run: python(func(cmd *cobra.Command, args []string, d pythonData) { + s, err := d.store.Settings.Get() checkErr(err) evt := args[0] @@ -44,8 +41,8 @@ var cmdsRmCmd = &cobra.Command{ } s.Commands[evt] = append(s.Commands[evt][:i], s.Commands[evt][f+1:]...) - err = st.Settings.Save(s) + err = d.store.Settings.Save(s) checkErr(err) printEvents(s.Commands) - }, + }, pythonConfig{}), } diff --git a/cmd/config_cat.go b/cmd/config_cat.go index f58d4444..fcc45583 100644 --- a/cmd/config_cat.go +++ b/cmd/config_cat.go @@ -13,14 +13,11 @@ var configCatCmd = &cobra.Command{ Short: "Prints the configuration", Long: `Prints the configuration.`, Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - db := getDB() - defer db.Close() - st := getStorage(db) - s, err := st.Settings.Get() + Run: python(func(cmd *cobra.Command, args []string, d pythonData) { + s, err := d.store.Settings.Get() checkErr(err) - auther, err := st.Auth.Get(s.AuthMethod) + auther, err := d.store.Auth.Get(s.AuthMethod) checkErr(err) printSettings(s, auther) - }, + }, pythonConfig{}), } diff --git a/cmd/config_init.go b/cmd/config_init.go index e22713e0..b46d669a 100644 --- a/cmd/config_init.go +++ b/cmd/config_init.go @@ -1,15 +1,11 @@ package cmd import ( - "errors" "fmt" - "os" "strings" - "github.com/asdine/storm" "github.com/filebrowser/filebrowser/v2/settings" "github.com/spf13/cobra" - v "github.com/spf13/viper" ) func init() { @@ -26,20 +22,11 @@ this options can be changed in the future with the command to the defaults when creating new users and you don't override the options.`, Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - databasePath := v.GetString("database") - if _, err := os.Stat(databasePath); err == nil { - panic(errors.New(databasePath + " already exists")) - } - + Run: python(func(cmd *cobra.Command, args []string, d pythonData) { defaults := settings.UserDefaults{} getUserDefaults(cmd, &defaults, true) authMethod, auther := getAuthentication(cmd) - db, err := storm.Open(databasePath) - checkErr(err) - defer db.Close() - st := getStorage(db) s := &settings.Settings{ Key: generateRandomBytes(64), // 256 bit Signup: mustGetBool(cmd, "signup"), @@ -53,9 +40,9 @@ override the options.`, }, } - err = st.Settings.Save(s) + err := d.store.Settings.Save(s) checkErr(err) - err = st.Auth.Save(auther) + err = d.store.Auth.Save(auther) checkErr(err) fmt.Printf(` @@ -64,5 +51,5 @@ 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) - }, + }, pythonConfig{noDB: true}), } diff --git a/cmd/config_set.go b/cmd/config_set.go index ea0f0159..0e68adf7 100644 --- a/cmd/config_set.go +++ b/cmd/config_set.go @@ -19,12 +19,8 @@ var configSetCmd = &cobra.Command{ Long: `Updates the configuration. Set the flags for the options you want to change.`, Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - db := getDB() - defer db.Close() - - st := getStorage(db) - s, err := st.Settings.Get() + Run: python(func(cmd *cobra.Command, args []string, d pythonData) { + s, err := d.store.Settings.Get() checkErr(err) hasAuth := false @@ -50,15 +46,15 @@ you want to change.`, var auther auth.Auther if hasAuth { s.AuthMethod, auther = getAuthentication(cmd) - err = st.Auth.Save(auther) + err = d.store.Auth.Save(auther) checkErr(err) } else { - auther, err = st.Auth.Get(s.AuthMethod) + auther, err = d.store.Auth.Get(s.AuthMethod) checkErr(err) } - err = st.Settings.Save(s) + err = d.store.Settings.Save(s) checkErr(err) printSettings(s, auther) - }, + }, pythonConfig{}), } diff --git a/cmd/root.go b/cmd/root.go index e31f635f..7acb9aa2 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -12,7 +12,6 @@ import ( "strconv" "strings" - "github.com/asdine/storm" "github.com/filebrowser/filebrowser/v2/auth" fbhttp "github.com/filebrowser/filebrowser/v2/http" "github.com/filebrowser/filebrowser/v2/settings" @@ -91,81 +90,71 @@ set FB_DATABASE equals to the path. Also, if the database path doesn't exist, File Browser will enter into 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: serveAndListen, + Run: python(func(cmd *cobra.Command, args []string, d pythonData) { + switch logMethod := v.GetString("log"); logMethod { + case "stdout": + log.SetOutput(os.Stdout) + case "stderr": + log.SetOutput(os.Stderr) + case "": + log.SetOutput(ioutil.Discard) + default: + log.SetOutput(&lumberjack.Logger{ + Filename: logMethod, + MaxSize: 100, + MaxAge: 14, + MaxBackups: 10, + }) + } + + if !d.hadDB { + quickSetup(d) + } + + port := v.GetInt("port") + address := v.GetString("address") + cert := v.GetString("cert") + key := v.GetString("key") + scope := v.GetString("scope") + + scope, err := filepath.Abs(scope) + checkErr(err) + settings, err := d.store.Settings.Get() + checkErr(err) + + // Despite Base URL and Scope being "server" type of + // variables, we persist them to the database because + // they are needed during the execution and not only + // to start up the server. + settings.BaseURL = v.GetString("baseurl") + settings.Scope = scope + err = d.store.Settings.Save(settings) + checkErr(err) + + handler, err := fbhttp.NewHandler(d.store) + checkErr(err) + + var listener net.Listener + + if key != "" && cert != "" { + cer, err := tls.LoadX509KeyPair(cert, key) + checkErr(err) + config := &tls.Config{Certificates: []tls.Certificate{cer}} + listener, err = tls.Listen("tcp", address+":"+strconv.Itoa(port), config) + checkErr(err) + } else { + listener, err = net.Listen("tcp", address+":"+strconv.Itoa(port)) + checkErr(err) + } + + log.Println("Listening on", listener.Addr().String()) + if err := http.Serve(listener, handler); err != nil { + log.Fatal(err) + } + }, pythonConfig{noDB: true}), } -func serveAndListen(cmd *cobra.Command, args []string) { - switch logMethod := v.GetString("log"); logMethod { - case "stdout": - log.SetOutput(os.Stdout) - case "stderr": - log.SetOutput(os.Stderr) - case "": - log.SetOutput(ioutil.Discard) - default: - log.SetOutput(&lumberjack.Logger{ - Filename: logMethod, - MaxSize: 100, - MaxAge: 14, - MaxBackups: 10, - }) - } - - if _, err := os.Stat(v.GetString("database")); os.IsNotExist(err) { - quickSetup(cmd) - } - - db := getDB() - defer db.Close() - st := getStorage(db) - - port := v.GetInt("port") - address := v.GetString("address") - cert := v.GetString("cert") - key := v.GetString("key") - scope := v.GetString("scope") - - scope, err := filepath.Abs(scope) - checkErr(err) - settings, err := st.Settings.Get() - checkErr(err) - - // Despite Base URL and Scope being "server" type of - // variables, we persist them to the database because - // they are needed during the execution and not only - // to start up the server. - settings.BaseURL = v.GetString("baseurl") - settings.Scope = scope - err = st.Settings.Save(settings) - checkErr(err) - - handler, err := fbhttp.NewHandler(st) - checkErr(err) - - var listener net.Listener - - if key != "" && cert != "" { - cer, err := tls.LoadX509KeyPair(cert, key) - checkErr(err) - config := &tls.Config{Certificates: []tls.Certificate{cer}} - listener, err = tls.Listen("tcp", address+":"+strconv.Itoa(port), config) - checkErr(err) - } else { - listener, err = net.Listen("tcp", address+":"+strconv.Itoa(port)) - checkErr(err) - } - - log.Println("Listening on", listener.Addr().String()) - if err := http.Serve(listener, handler); err != nil { - log.Fatal(err) - } -} - -func quickSetup(cmd *cobra.Command) { - db, err := storm.Open(v.GetString("database")) - checkErr(err) - defer db.Close() - +func quickSetup(d pythonData) { set := &settings.Settings{ Key: generateRandomBytes(64), // 256 bit BaseURL: v.GetString("baseurl"), @@ -187,12 +176,10 @@ func quickSetup(cmd *cobra.Command) { }, } - st := getStorage(db) - - err = st.Settings.Save(set) + err := d.store.Settings.Save(set) checkErr(err) - err = st.Auth.Save(&auth.JSONAuth{}) + err = d.store.Auth.Save(&auth.JSONAuth{}) checkErr(err) username := v.GetString("username") @@ -216,7 +203,7 @@ func quickSetup(cmd *cobra.Command) { set.Defaults.Apply(user) user.Perm.Admin = true - err = st.Users.Save(user) + err = d.store.Users.Save(user) checkErr(err) } diff --git a/cmd/rule_rm.go b/cmd/rule_rm.go index 264fdb19..20e9d1e8 100644 --- a/cmd/rule_rm.go +++ b/cmd/rule_rm.go @@ -4,7 +4,6 @@ import ( "strconv" "github.com/filebrowser/filebrowser/v2/settings" - "github.com/filebrowser/filebrowser/v2/storage" "github.com/filebrowser/filebrowser/v2/users" "github.com/spf13/cobra" ) @@ -32,7 +31,7 @@ var rulesRmCommand = &cobra.Command{ return nil }, - Run: func(cmd *cobra.Command, args []string) { + Run: python(func(cmd *cobra.Command, args []string, d pythonData) { i, err := strconv.Atoi(args[0]) checkErr(err) f := i @@ -41,18 +40,18 @@ var rulesRmCommand = &cobra.Command{ checkErr(err) } - user := func(u *users.User, st *storage.Storage) { + user := func(u *users.User) { u.Rules = append(u.Rules[:i], u.Rules[f+1:]...) - err := st.Users.Save(u) + err := d.store.Users.Save(u) checkErr(err) } - global := func(s *settings.Settings, st *storage.Storage) { + global := func(s *settings.Settings) { s.Rules = append(s.Rules[:i], s.Rules[f+1:]...) - err := st.Settings.Save(s) + err := d.store.Settings.Save(s) checkErr(err) } - runRules(cmd, user, global) - }, + runRules(d.store, cmd, user, global) + }, pythonConfig{}), } diff --git a/cmd/rules.go b/cmd/rules.go index 7a651e53..619a2b03 100644 --- a/cmd/rules.go +++ b/cmd/rules.go @@ -32,18 +32,14 @@ rules.`, }, } -func runRules(cmd *cobra.Command, users func(*users.User, *storage.Storage), global func(*settings.Settings, *storage.Storage)) { - db := getDB() - defer db.Close() - st := getStorage(db) - +func runRules(st *storage.Storage, cmd *cobra.Command, users func(*users.User), global func(*settings.Settings)) { id := getUserIdentifier(cmd) if id != nil { user, err := st.Users.Get("", id) checkErr(err) if users != nil { - users(user, st) + users(user) } printRules(user.Rules, id) @@ -54,7 +50,7 @@ func runRules(cmd *cobra.Command, users func(*users.User, *storage.Storage), glo checkErr(err) if global != nil { - global(settings, st) + global(settings) } printRules(settings.Rules, id) diff --git a/cmd/rules_add.go b/cmd/rules_add.go index 2b25b1e8..e3cfb7a3 100644 --- a/cmd/rules_add.go +++ b/cmd/rules_add.go @@ -5,7 +5,6 @@ import ( "github.com/filebrowser/filebrowser/v2/rules" "github.com/filebrowser/filebrowser/v2/settings" - "github.com/filebrowser/filebrowser/v2/storage" "github.com/filebrowser/filebrowser/v2/users" "github.com/spf13/cobra" ) @@ -21,7 +20,7 @@ var rulesAddCmd = &cobra.Command{ Short: "Add a global rule or user rule", Long: `Add a global rule or user rule.`, Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { + Run: python(func(cmd *cobra.Command, args []string, d pythonData) { allow := mustGetBool(cmd, "allow") regex := mustGetBool(cmd, "regex") exp := args[0] @@ -41,18 +40,18 @@ var rulesAddCmd = &cobra.Command{ rule.Path = exp } - user := func(u *users.User, st *storage.Storage) { + user := func(u *users.User) { u.Rules = append(u.Rules, rule) - err := st.Users.Save(u) + err := d.store.Users.Save(u) checkErr(err) } - global := func(s *settings.Settings, st *storage.Storage) { + global := func(s *settings.Settings) { s.Rules = append(s.Rules, rule) - err := st.Settings.Save(s) + err := d.store.Settings.Save(s) checkErr(err) } - runRules(cmd, user, global) - }, + runRules(d.store, cmd, user, global) + }, pythonConfig{}), } diff --git a/cmd/rules_ls.go b/cmd/rules_ls.go index 72cb9431..e0e5f8f8 100644 --- a/cmd/rules_ls.go +++ b/cmd/rules_ls.go @@ -13,7 +13,7 @@ var rulesLsCommand = &cobra.Command{ Short: "List global rules or user specific rules", Long: `List global rules or user specific rules.`, Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - runRules(cmd, nil, nil) - }, + Run: python(func(cmd *cobra.Command, args []string, d pythonData) { + runRules(d.store, cmd, nil, nil) + }, pythonConfig{}), } diff --git a/cmd/users_add.go b/cmd/users_add.go index 06906d66..a8f798d8 100644 --- a/cmd/users_add.go +++ b/cmd/users_add.go @@ -15,12 +15,8 @@ var usersAddCmd = &cobra.Command{ Short: "Create a new user", Long: `Create a new user and add it to the database.`, Args: cobra.ExactArgs(2), - Run: func(cmd *cobra.Command, args []string) { - db := getDB() - defer db.Close() - st := getStorage(db) - - s, err := st.Settings.Get() + Run: python(func(cmd *cobra.Command, args []string, d pythonData) { + s, err := d.store.Settings.Get() checkErr(err) getUserDefaults(cmd, &s.Defaults, false) @@ -34,8 +30,8 @@ var usersAddCmd = &cobra.Command{ } s.Defaults.Apply(user) - err = st.Users.Save(user) + err = d.store.Users.Save(user) checkErr(err) printUsers([]*users.User{user}) - }, + }, pythonConfig{}), } diff --git a/cmd/users_find.go b/cmd/users_find.go index 070959c6..2199aba9 100644 --- a/cmd/users_find.go +++ b/cmd/users_find.go @@ -25,11 +25,7 @@ var usersLsCmd = &cobra.Command{ Run: findUsers, } -var findUsers = func(cmd *cobra.Command, args []string) { - db := getDB() - defer db.Close() - st := getStorage(db) - +var findUsers = python(func(cmd *cobra.Command, args []string, d pythonData) { var ( list []*users.User user *users.User @@ -39,16 +35,16 @@ var findUsers = func(cmd *cobra.Command, args []string) { if len(args) == 1 { username, id := parseUsernameOrID(args[0]) if username != "" { - user, err = st.Users.Get("", username) + user, err = d.store.Users.Get("", username) } else { - user, err = st.Users.Get("", id) + user, err = d.store.Users.Get("", id) } list = []*users.User{user} } else { - list, err = st.Users.Gets("") + list, err = d.store.Users.Gets("") } checkErr(err) printUsers(list) -} +}, pythonConfig{}) diff --git a/cmd/users_rm.go b/cmd/users_rm.go index e942c62b..e3fef01b 100644 --- a/cmd/users_rm.go +++ b/cmd/users_rm.go @@ -15,21 +15,17 @@ var usersRmCmd = &cobra.Command{ Short: "Delete a user by username or id", Long: `Delete a user by username or id`, Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - db := getDB() - defer db.Close() - st := getStorage(db) - + Run: python(func(cmd *cobra.Command, args []string, d pythonData) { username, id := parseUsernameOrID(args[0]) var err error if username != "" { - err = st.Users.Delete(username) + err = d.store.Users.Delete(username) } else { - err = st.Users.Delete(id) + err = d.store.Users.Delete(id) } checkErr(err) fmt.Println("user deleted successfully") - }, + }, pythonConfig{}), } diff --git a/cmd/users_update.go b/cmd/users_update.go index fb6f65a5..09898cd3 100644 --- a/cmd/users_update.go +++ b/cmd/users_update.go @@ -20,12 +20,8 @@ var usersUpdateCmd = &cobra.Command{ Long: `Updates an existing user. Set the flags for the options you want to change.`, Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - db := getDB() - defer db.Close() - st := getStorage(db) - - set, err := st.Settings.Get() + Run: python(func(cmd *cobra.Command, args []string, d pythonData) { + set, err := d.store.Settings.Get() checkErr(err) username, id := parseUsernameOrID(args[0]) @@ -35,9 +31,9 @@ options you want to change.`, var user *users.User if id != 0 { - user, err = st.Users.Get(set.Scope, id) + user, err = d.store.Users.Get(set.Scope, id) } else { - user, err = st.Users.Get(set.Scope, username) + user, err = d.store.Users.Get(set.Scope, username) } checkErr(err) @@ -68,8 +64,8 @@ options you want to change.`, checkErr(err) } - err = st.Users.Update(user) + err = d.store.Users.Update(user) checkErr(err) printUsers([]*users.User{user}) - }, + }, pythonConfig{}), } diff --git a/cmd/utils.go b/cmd/utils.go index b5f84915..daea6687 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -2,7 +2,7 @@ package cmd import ( "crypto/rand" - "errors" + "log" "os" "github.com/asdine/storm" @@ -61,21 +61,6 @@ func mustGetUint(cmd *cobra.Command, flag string) uint { return b } -func getDB() *storm.DB { - databasePath := v.GetString("database") - if _, err := os.Stat(databasePath); err != nil { - panic(errors.New(databasePath + " does not exist. Please run 'filebrowser init' first.")) - } - - db, err := storm.Open(databasePath) - checkErr(err) - return db -} - -func getStorage(db *storm.DB) *storage.Storage { - return bolt.NewStorage(db) -} - func generateRandomBytes(n int) []byte { b := make([]byte, n) _, err := rand.Read(b) @@ -83,3 +68,42 @@ func generateRandomBytes(n int) []byte { // Note that err == nil only if we read len(b) bytes. return b } + +type cobraFunc func(cmd *cobra.Command, args []string) +type pythonFunc func(cmd *cobra.Command, args []string, data pythonData) + +type pythonConfig struct { + noDB bool +} + +type pythonData struct { + hadDB bool + store *storage.Storage +} + +func python(fn pythonFunc, cfg pythonConfig) cobraFunc { + return func(cmd *cobra.Command, args []string) { + data := pythonData{hadDB: true} + + path := v.GetString("database") + _, err := os.Stat(path) + + if os.IsNotExist(err) { + data.hadDB = false + + if !cfg.noDB { + log.Fatal(path + " does not exid.store. Please run 'filebrowser config init' fird.store.") + } + } else if err != nil { + panic(err) + } else if err == nil && cfg.noDB { + log.Fatal(path + " already exists") + } + + db, err := storm.Open(path) + checkErr(err) + defer db.Close() + data.store = bolt.NewStorage(db) + fn(cmd, args, data) + } +}