feat: python

License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>
This commit is contained in:
Henrique Dias 2019-01-07 20:16:16 +00:00
parent 6bcd0000e8
commit ee0e23c166
16 changed files with 138 additions and 156 deletions

View File

@ -3,7 +3,6 @@ package cmd
import ( import (
"strings" "strings"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -16,12 +15,12 @@ var cmdsAddCmd = &cobra.Command{
Short: "Add a command to run on a specific event", Short: "Add a command to run on a specific event",
Long: `Add a command to run on a specific event.`, Long: `Add a command to run on a specific event.`,
Args: cobra.MinimumNArgs(2), Args: cobra.MinimumNArgs(2),
Run: python(func(cmd *cobra.Command, args []string, st *storage.Storage) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
s, err := st.Settings.Get() s, err := d.store.Settings.Get()
checkErr(err) checkErr(err)
command := strings.Join(args[1:], " ") command := strings.Join(args[1:], " ")
s.Commands[args[0]] = append(s.Commands[args[0]], command) s.Commands[args[0]] = append(s.Commands[args[0]], command)
err = st.Settings.Save(s) err = d.store.Settings.Save(s)
checkErr(err) checkErr(err)
printEvents(s.Commands) printEvents(s.Commands)
}, pythonConfig{}), }, pythonConfig{}),

View File

@ -1,7 +1,6 @@
package cmd package cmd
import ( import (
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -15,8 +14,8 @@ var cmdsLsCmd = &cobra.Command{
Short: "List all commands for each event", Short: "List all commands for each event",
Long: `List all commands for each event.`, Long: `List all commands for each event.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
Run: python(func(cmd *cobra.Command, args []string, st *storage.Storage) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
s, err := st.Settings.Get() s, err := d.store.Settings.Get()
checkErr(err) checkErr(err)
evt := mustGetString(cmd, "event") evt := mustGetString(cmd, "event")

View File

@ -3,7 +3,6 @@ package cmd
import ( import (
"strconv" "strconv"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -28,8 +27,8 @@ var cmdsRmCmd = &cobra.Command{
return nil return nil
}, },
Run: python(func(cmd *cobra.Command, args []string, st *storage.Storage) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
s, err := st.Settings.Get() s, err := d.store.Settings.Get()
checkErr(err) checkErr(err)
evt := args[0] evt := args[0]
@ -42,7 +41,7 @@ var cmdsRmCmd = &cobra.Command{
} }
s.Commands[evt] = append(s.Commands[evt][:i], s.Commands[evt][f+1:]...) 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) checkErr(err)
printEvents(s.Commands) printEvents(s.Commands)
}, pythonConfig{}), }, pythonConfig{}),

View File

@ -1,7 +1,6 @@
package cmd package cmd
import ( import (
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -14,10 +13,10 @@ var configCatCmd = &cobra.Command{
Short: "Prints the configuration", Short: "Prints the configuration",
Long: `Prints the configuration.`, Long: `Prints the configuration.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
Run: python(func(cmd *cobra.Command, args []string, st *storage.Storage) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
s, err := st.Settings.Get() s, err := d.store.Settings.Get()
checkErr(err) checkErr(err)
auther, err := st.Auth.Get(s.AuthMethod) auther, err := d.store.Auth.Get(s.AuthMethod)
checkErr(err) checkErr(err)
printSettings(s, auther) printSettings(s, auther)
}, pythonConfig{}), }, pythonConfig{}),

View File

@ -5,7 +5,6 @@ import (
"strings" "strings"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -23,7 +22,7 @@ this options can be changed in the future with the command
to the defaults when creating new users and you don't to the defaults when creating new users and you don't
override the options.`, override the options.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
Run: python(func(cmd *cobra.Command, args []string, st *storage.Storage) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
defaults := settings.UserDefaults{} defaults := settings.UserDefaults{}
getUserDefaults(cmd, &defaults, true) getUserDefaults(cmd, &defaults, true)
authMethod, auther := getAuthentication(cmd) authMethod, auther := getAuthentication(cmd)
@ -41,9 +40,9 @@ override the options.`,
}, },
} }
err := st.Settings.Save(s) err := d.store.Settings.Save(s)
checkErr(err) checkErr(err)
err = st.Auth.Save(auther) err = d.store.Auth.Save(auther)
checkErr(err) checkErr(err)
fmt.Printf(` fmt.Printf(`

View File

@ -4,7 +4,6 @@ import (
"strings" "strings"
"github.com/filebrowser/filebrowser/v2/auth" "github.com/filebrowser/filebrowser/v2/auth"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
@ -20,8 +19,8 @@ var configSetCmd = &cobra.Command{
Long: `Updates the configuration. Set the flags for the options Long: `Updates the configuration. Set the flags for the options
you want to change.`, you want to change.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
Run: python(func(cmd *cobra.Command, args []string, st *storage.Storage) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
s, err := st.Settings.Get() s, err := d.store.Settings.Get()
checkErr(err) checkErr(err)
hasAuth := false hasAuth := false
@ -47,14 +46,14 @@ you want to change.`,
var auther auth.Auther var auther auth.Auther
if hasAuth { if hasAuth {
s.AuthMethod, auther = getAuthentication(cmd) s.AuthMethod, auther = getAuthentication(cmd)
err = st.Auth.Save(auther) err = d.store.Auth.Save(auther)
checkErr(err) checkErr(err)
} else { } else {
auther, err = st.Auth.Get(s.AuthMethod) auther, err = d.store.Auth.Get(s.AuthMethod)
checkErr(err) checkErr(err)
} }
err = st.Settings.Save(s) err = d.store.Settings.Save(s)
checkErr(err) checkErr(err)
printSettings(s, auther) printSettings(s, auther)
}, pythonConfig{}), }, pythonConfig{}),

View File

@ -12,7 +12,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/asdine/storm"
"github.com/filebrowser/filebrowser/v2/auth" "github.com/filebrowser/filebrowser/v2/auth"
fbhttp "github.com/filebrowser/filebrowser/v2/http" fbhttp "github.com/filebrowser/filebrowser/v2/http"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
@ -91,10 +90,7 @@ set FB_DATABASE equals to the path.
Also, if the database path doesn't exist, File Browser will enter into 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 the quick setup mode and a new database will be bootstraped and a new
user created with the credentials from options "username" and "password".`, user created with the credentials from options "username" and "password".`,
Run: serveAndListen, Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
}
func serveAndListen(cmd *cobra.Command, args []string) {
switch logMethod := v.GetString("log"); logMethod { switch logMethod := v.GetString("log"); logMethod {
case "stdout": case "stdout":
log.SetOutput(os.Stdout) log.SetOutput(os.Stdout)
@ -111,14 +107,10 @@ func serveAndListen(cmd *cobra.Command, args []string) {
}) })
} }
if _, err := os.Stat(v.GetString("database")); os.IsNotExist(err) { if !d.hadDB {
quickSetup(cmd) quickSetup(d)
} }
db := getDB()
defer db.Close()
st := getStorage(db)
port := v.GetInt("port") port := v.GetInt("port")
address := v.GetString("address") address := v.GetString("address")
cert := v.GetString("cert") cert := v.GetString("cert")
@ -127,7 +119,7 @@ func serveAndListen(cmd *cobra.Command, args []string) {
scope, err := filepath.Abs(scope) scope, err := filepath.Abs(scope)
checkErr(err) checkErr(err)
settings, err := st.Settings.Get() settings, err := d.store.Settings.Get()
checkErr(err) checkErr(err)
// Despite Base URL and Scope being "server" type of // Despite Base URL and Scope being "server" type of
@ -136,10 +128,10 @@ func serveAndListen(cmd *cobra.Command, args []string) {
// to start up the server. // to start up the server.
settings.BaseURL = v.GetString("baseurl") settings.BaseURL = v.GetString("baseurl")
settings.Scope = scope settings.Scope = scope
err = st.Settings.Save(settings) err = d.store.Settings.Save(settings)
checkErr(err) checkErr(err)
handler, err := fbhttp.NewHandler(st) handler, err := fbhttp.NewHandler(d.store)
checkErr(err) checkErr(err)
var listener net.Listener var listener net.Listener
@ -159,13 +151,10 @@ func serveAndListen(cmd *cobra.Command, args []string) {
if err := http.Serve(listener, handler); err != nil { if err := http.Serve(listener, handler); err != nil {
log.Fatal(err) log.Fatal(err)
} }
}, pythonConfig{noDB: true}),
} }
func quickSetup(cmd *cobra.Command) { func quickSetup(d pythonData) {
db, err := storm.Open(v.GetString("database"))
checkErr(err)
defer db.Close()
set := &settings.Settings{ set := &settings.Settings{
Key: generateRandomBytes(64), // 256 bit Key: generateRandomBytes(64), // 256 bit
BaseURL: v.GetString("baseurl"), BaseURL: v.GetString("baseurl"),
@ -187,12 +176,10 @@ func quickSetup(cmd *cobra.Command) {
}, },
} }
st := getStorage(db) err := d.store.Settings.Save(set)
err = st.Settings.Save(set)
checkErr(err) checkErr(err)
err = st.Auth.Save(&auth.JSONAuth{}) err = d.store.Auth.Save(&auth.JSONAuth{})
checkErr(err) checkErr(err)
username := v.GetString("username") username := v.GetString("username")
@ -216,7 +203,7 @@ func quickSetup(cmd *cobra.Command) {
set.Defaults.Apply(user) set.Defaults.Apply(user)
user.Perm.Admin = true user.Perm.Admin = true
err = st.Users.Save(user) err = d.store.Users.Save(user)
checkErr(err) checkErr(err)
} }

View File

@ -4,7 +4,6 @@ import (
"strconv" "strconv"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -32,7 +31,7 @@ var rulesRmCommand = &cobra.Command{
return nil 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]) i, err := strconv.Atoi(args[0])
checkErr(err) checkErr(err)
f := i f := i
@ -41,18 +40,18 @@ var rulesRmCommand = &cobra.Command{
checkErr(err) 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:]...) u.Rules = append(u.Rules[:i], u.Rules[f+1:]...)
err := st.Users.Save(u) err := d.store.Users.Save(u)
checkErr(err) 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:]...) s.Rules = append(s.Rules[:i], s.Rules[f+1:]...)
err := st.Settings.Save(s) err := d.store.Settings.Save(s)
checkErr(err) checkErr(err)
} }
runRules(cmd, user, global) runRules(d.store, cmd, user, global)
}, }, pythonConfig{}),
} }

View File

@ -32,18 +32,14 @@ rules.`,
}, },
} }
func runRules(cmd *cobra.Command, users func(*users.User, *storage.Storage), global func(*settings.Settings, *storage.Storage)) { func runRules(st *storage.Storage, cmd *cobra.Command, users func(*users.User), global func(*settings.Settings)) {
db := getDB()
defer db.Close()
st := getStorage(db)
id := getUserIdentifier(cmd) id := getUserIdentifier(cmd)
if id != nil { if id != nil {
user, err := st.Users.Get("", id) user, err := st.Users.Get("", id)
checkErr(err) checkErr(err)
if users != nil { if users != nil {
users(user, st) users(user)
} }
printRules(user.Rules, id) printRules(user.Rules, id)
@ -54,7 +50,7 @@ func runRules(cmd *cobra.Command, users func(*users.User, *storage.Storage), glo
checkErr(err) checkErr(err)
if global != nil { if global != nil {
global(settings, st) global(settings)
} }
printRules(settings.Rules, id) printRules(settings.Rules, id)

View File

@ -5,7 +5,6 @@ import (
"github.com/filebrowser/filebrowser/v2/rules" "github.com/filebrowser/filebrowser/v2/rules"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -21,7 +20,7 @@ var rulesAddCmd = &cobra.Command{
Short: "Add a global rule or user rule", Short: "Add a global rule or user rule",
Long: `Add a global rule or user rule.`, Long: `Add a global rule or user rule.`,
Args: cobra.ExactArgs(1), 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") allow := mustGetBool(cmd, "allow")
regex := mustGetBool(cmd, "regex") regex := mustGetBool(cmd, "regex")
exp := args[0] exp := args[0]
@ -41,18 +40,18 @@ var rulesAddCmd = &cobra.Command{
rule.Path = exp rule.Path = exp
} }
user := func(u *users.User, st *storage.Storage) { user := func(u *users.User) {
u.Rules = append(u.Rules, rule) u.Rules = append(u.Rules, rule)
err := st.Users.Save(u) err := d.store.Users.Save(u)
checkErr(err) checkErr(err)
} }
global := func(s *settings.Settings, st *storage.Storage) { global := func(s *settings.Settings) {
s.Rules = append(s.Rules, rule) s.Rules = append(s.Rules, rule)
err := st.Settings.Save(s) err := d.store.Settings.Save(s)
checkErr(err) checkErr(err)
} }
runRules(cmd, user, global) runRules(d.store, cmd, user, global)
}, }, pythonConfig{}),
} }

View File

@ -13,7 +13,7 @@ var rulesLsCommand = &cobra.Command{
Short: "List global rules or user specific rules", Short: "List global rules or user specific rules",
Long: `List global rules or user specific rules.`, Long: `List global rules or user specific rules.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
runRules(cmd, nil, nil) runRules(d.store, cmd, nil, nil)
}, }, pythonConfig{}),
} }

View File

@ -1,7 +1,6 @@
package cmd package cmd
import ( import (
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -16,8 +15,8 @@ var usersAddCmd = &cobra.Command{
Short: "Create a new user", Short: "Create a new user",
Long: `Create a new user and add it to the database.`, Long: `Create a new user and add it to the database.`,
Args: cobra.ExactArgs(2), Args: cobra.ExactArgs(2),
Run: python(func(cmd *cobra.Command, args []string, st *storage.Storage) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
s, err := st.Settings.Get() s, err := d.store.Settings.Get()
checkErr(err) checkErr(err)
getUserDefaults(cmd, &s.Defaults, false) getUserDefaults(cmd, &s.Defaults, false)
@ -31,7 +30,7 @@ var usersAddCmd = &cobra.Command{
} }
s.Defaults.Apply(user) s.Defaults.Apply(user)
err = st.Users.Save(user) err = d.store.Users.Save(user)
checkErr(err) checkErr(err)
printUsers([]*users.User{user}) printUsers([]*users.User{user})
}, pythonConfig{}), }, pythonConfig{}),

View File

@ -1,7 +1,6 @@
package cmd package cmd
import ( import (
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -26,7 +25,7 @@ var usersLsCmd = &cobra.Command{
Run: findUsers, Run: findUsers,
} }
var findUsers = python(func(cmd *cobra.Command, args []string, st *storage.Storage) { var findUsers = python(func(cmd *cobra.Command, args []string, d pythonData) {
var ( var (
list []*users.User list []*users.User
user *users.User user *users.User
@ -36,14 +35,14 @@ var findUsers = python(func(cmd *cobra.Command, args []string, st *storage.Stora
if len(args) == 1 { if len(args) == 1 {
username, id := parseUsernameOrID(args[0]) username, id := parseUsernameOrID(args[0])
if username != "" { if username != "" {
user, err = st.Users.Get("", username) user, err = d.store.Users.Get("", username)
} else { } else {
user, err = st.Users.Get("", id) user, err = d.store.Users.Get("", id)
} }
list = []*users.User{user} list = []*users.User{user}
} else { } else {
list, err = st.Users.Gets("") list, err = d.store.Users.Gets("")
} }
checkErr(err) checkErr(err)

View File

@ -3,7 +3,6 @@ package cmd
import ( import (
"fmt" "fmt"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -16,14 +15,14 @@ var usersRmCmd = &cobra.Command{
Short: "Delete a user by username or id", Short: "Delete a user by username or id",
Long: `Delete a user by username or id`, Long: `Delete a user by username or id`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
Run: python(func(cmd *cobra.Command, args []string, st *storage.Storage) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
username, id := parseUsernameOrID(args[0]) username, id := parseUsernameOrID(args[0])
var err error var err error
if username != "" { if username != "" {
err = st.Users.Delete(username) err = d.store.Users.Delete(username)
} else { } else {
err = st.Users.Delete(id) err = d.store.Users.Delete(id)
} }
checkErr(err) checkErr(err)

View File

@ -2,7 +2,6 @@ package cmd
import ( import (
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -21,8 +20,8 @@ var usersUpdateCmd = &cobra.Command{
Long: `Updates an existing user. Set the flags for the Long: `Updates an existing user. Set the flags for the
options you want to change.`, options you want to change.`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
Run: python(func(cmd *cobra.Command, args []string, st *storage.Storage) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
set, err := st.Settings.Get() set, err := d.store.Settings.Get()
checkErr(err) checkErr(err)
username, id := parseUsernameOrID(args[0]) username, id := parseUsernameOrID(args[0])
@ -32,9 +31,9 @@ options you want to change.`,
var user *users.User var user *users.User
if id != 0 { if id != 0 {
user, err = st.Users.Get(set.Scope, id) user, err = d.store.Users.Get(set.Scope, id)
} else { } else {
user, err = st.Users.Get(set.Scope, username) user, err = d.store.Users.Get(set.Scope, username)
} }
checkErr(err) checkErr(err)
@ -65,7 +64,7 @@ options you want to change.`,
checkErr(err) checkErr(err)
} }
err = st.Users.Update(user) err = d.store.Users.Update(user)
checkErr(err) checkErr(err)
printUsers([]*users.User{user}) printUsers([]*users.User{user})
}, pythonConfig{}), }, pythonConfig{}),

View File

@ -65,7 +65,7 @@ func mustGetUint(cmd *cobra.Command, flag string) uint {
func getDB() *storm.DB { func getDB() *storm.DB {
databasePath := v.GetString("database") databasePath := v.GetString("database")
if _, err := os.Stat(databasePath); err != nil { if _, err := os.Stat(databasePath); err != nil {
panic(errors.New(databasePath + " does not exist. Please run 'filebrowser init' first.")) panic(errors.New(databasePath + " does not exid.store. Please run 'filebrowser init' fird.store."))
} }
db, err := storm.Open(databasePath) db, err := storm.Open(databasePath)
@ -86,21 +86,32 @@ func generateRandomBytes(n int) []byte {
} }
type cobraFunc func(cmd *cobra.Command, args []string) type cobraFunc func(cmd *cobra.Command, args []string)
type pythonFunc func(cmd *cobra.Command, args []string, st *storage.Storage) type pythonFunc func(cmd *cobra.Command, args []string, data pythonData)
type pythonConfig struct { type pythonConfig struct {
noDB bool noDB bool
} }
type pythonData struct {
hadDB bool
store *storage.Storage
}
func python(fn pythonFunc, cfg pythonConfig) cobraFunc { func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
return func(cmd *cobra.Command, args []string) { return func(cmd *cobra.Command, args []string) {
data := pythonData{hadDB: true}
path := v.GetString("database") path := v.GetString("database")
_, err := os.Stat(path) _, err := os.Stat(path)
if err != nil && !os.IsNotExist(err) { 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) panic(err)
} else if err != nil && !cfg.noDB {
log.Fatal(path + " does not exist. Please run 'filebrowser config init' first.")
} else if err == nil && cfg.noDB { } else if err == nil && cfg.noDB {
log.Fatal(path + " already exists") log.Fatal(path + " already exists")
} }
@ -108,7 +119,7 @@ func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
db, err := storm.Open(path) db, err := storm.Open(path)
checkErr(err) checkErr(err)
defer db.Close() defer db.Close()
sto := bolt.NewStorage(db) data.store = bolt.NewStorage(db)
fn(cmd, args, sto) fn(cmd, args, data)
} }
} }