Merge pull request #1 from filebrowser/master

update 20190109
This commit is contained in:
光子 2019-01-09 09:32:52 +08:00 committed by GitHub
commit b4b578b90f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 252 additions and 201 deletions

View File

@ -3,7 +3,7 @@ project_name: filebrowser
build: build:
env: env:
- CGO_ENABLED=0 - CGO_ENABLED=0
main: cli/main.go main: main.go
binary: filebrowser binary: filebrowser
goos: goos:
- darwin - darwin
@ -50,8 +50,9 @@ dockers:
goos: linux goos: linux
goarch: amd64 goarch: amd64
goarm: '' goarm: ''
image: filebrowser/filebrowser image_templates:
- "filebrowser/filebrowser:latest"
- "filebrowser/filebrowser:{{ .Tag }}"
skip_push: true skip_push: true
tag_templates: extra_files:
- "{{ .Tag }}" - .docker.json
- latest

View File

@ -32,7 +32,7 @@ jobs:
repo: filebrowser/filebrowser repo: filebrowser/filebrowser
branch: master branch: master
- stage: release - stage: release
script: ./wizard.sh -r script: ./wizard.sh -r "$TRAVIS_TAG"
if: tag IS present if: tag IS present
deploy: deploy:
provider: releases provider: releases

View File

@ -2,11 +2,10 @@ FROM scratch
COPY --from=filebrowser/dev /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt COPY --from=filebrowser/dev /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
VOLUME /tmp
VOLUME /srv VOLUME /srv
EXPOSE 80 EXPOSE 80
COPY .filebrowser.docker.json /.filebrowser.json COPY .docker.json /.filebrowser.json
COPY filebrowser /filebrowser COPY filebrowser /filebrowser
ENTRYPOINT [ "/filebrowser" ] ENTRYPOINT [ "/filebrowser" ]

View File

@ -15,5 +15,5 @@ type NoAuth struct{}
// Auth uses authenticates user 1. // Auth uses authenticates user 1.
func (a NoAuth) Auth(r *http.Request, sto *users.Storage, root string) (*users.User, error) { func (a NoAuth) Auth(r *http.Request, sto *users.Storage, root string) (*users.User, error) {
return sto.Get(root, 1) return sto.Get(root, uint(1))
} }

View File

@ -12,6 +12,7 @@ func init() {
var cmdsCmd = &cobra.Command{ var cmdsCmd = &cobra.Command{
Use: "cmds", Use: "cmds",
Version: rootCmd.Version,
Short: "Command runner management utility", Short: "Command runner management utility",
Long: `Command runner management utility.`, Long: `Command runner management utility.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,

View File

@ -13,7 +13,15 @@ func init() {
var cmdsRmCmd = &cobra.Command{ var cmdsRmCmd = &cobra.Command{
Use: "rm <event> <index> [index_end]", Use: "rm <event> <index> [index_end]",
Short: "Removes a command from an event hooker", Short: "Removes a command from an event hooker",
Long: `Removes a command from an event hooker.`, Long: `Removes a command from an event hooker. The provided index
is the same that's printed when you run 'cmds ls'. Note
that after each removal/addition, the index of the
commands change. So be careful when removing them after each
other.
You can also specify an optional parameter (index_end) so
you can remove all commands from 'index' to 'index_end',
including 'index_end'.`,
Args: func(cmd *cobra.Command, args []string) error { Args: func(cmd *cobra.Command, args []string) error {
if err := cobra.RangeArgs(2, 3)(cmd, args); err != nil { if err := cobra.RangeArgs(2, 3)(cmd, args); err != nil {
return err return err

View File

@ -21,6 +21,7 @@ func init() {
var configCmd = &cobra.Command{ var configCmd = &cobra.Command{
Use: "config", Use: "config",
Version: rootCmd.Version,
Short: "Configuration management utility", Short: "Configuration management utility",
Long: `Configuration management utility.`, Long: `Configuration management utility.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,

View File

@ -9,8 +9,11 @@ func init() {
} }
var configExportCmd = &cobra.Command{ var configExportCmd = &cobra.Command{
Use: "export <filename>", Use: "export <path>",
Short: "Export the configuration to a file.", Short: "Export the configuration to a file",
Long: `Export the configuration to a file. The path must be for a
json or yaml file. This exported configuration can be changed,
and imported again with 'config import' command.`,
Args: jsonYamlArg, Args: jsonYamlArg,
Run: python(func(cmd *cobra.Command, args []string, d pythonData) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
settings, err := d.store.Settings.Get() settings, err := d.store.Settings.Get()

View File

@ -22,12 +22,16 @@ type settingsFile struct {
} }
var configImportCmd = &cobra.Command{ var configImportCmd = &cobra.Command{
Use: "import <filename>", Use: "import <path>",
Short: `Import a configuration file. This will replace all the existing Short: "Import a configuration file",
Long: `Import a configuration file. This will replace all the existing
configuration. Can be used with or without unexisting databases. configuration. Can be used with or without unexisting databases.
If used with a nonexisting database, a key will be generated If used with a nonexisting database, a key will be generated
automatically. Otherwise the key will be kept the same as in the automatically. Otherwise the key will be kept the same as in the
database.`, database.
The path must be for a json or yaml file.`,
Args: jsonYamlArg, Args: jsonYamlArg,
Run: python(func(cmd *cobra.Command, args []string, d pythonData) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
var key []byte var key []byte

View File

@ -18,7 +18,7 @@ var configInitCmd = &cobra.Command{
Short: "Initialize a new database", Short: "Initialize a new database",
Long: `Initialize a new database to use with File Browser. All of Long: `Initialize a new database to use with File Browser. All of
this options can be changed in the future with the command this options can be changed in the future with the command
"filebrowser config set". The user related flags apply 'filebrowser config set'. The user related flags apply
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,

View File

@ -17,7 +17,7 @@ var configSetCmd = &cobra.Command{
Use: "set", Use: "set",
Short: "Updates the configuration", Short: "Updates the configuration",
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. Other options will remain unchanged.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
Run: python(func(cmd *cobra.Command, args []string, d pythonData) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -9,8 +9,8 @@ import (
"sort" "sort"
"strings" "strings"
"github.com/spf13/pflag"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag"
) )
func init() { func init() {

View File

@ -13,6 +13,7 @@ func init() {
var hashCmd = &cobra.Command{ var hashCmd = &cobra.Command{
Use: "hash <password>", Use: "hash <password>",
Version: rootCmd.Version,
Short: "Hashes a password", Short: "Hashes a password",
Long: `Hashes a password using bcrypt algorithm.`, Long: `Hashes a password using bcrypt algorithm.`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),

View File

@ -2,7 +2,6 @@ package cmd
import ( import (
"crypto/tls" "crypto/tls"
"errors"
"io/ioutil" "io/ioutil"
"log" "log"
"net" "net"
@ -16,7 +15,8 @@ import (
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage" "github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
"github.com/mitchellh/go-homedir" "github.com/filebrowser/filebrowser/v2/version"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
v "github.com/spf13/viper" v "github.com/spf13/viper"
@ -29,11 +29,15 @@ var (
func init() { func init() {
cobra.OnInitialize(initConfig) cobra.OnInitialize(initConfig)
rootCmd.SetVersionTemplate("File Browser version {{printf \"%s\" .Version}}\n")
flags := rootCmd.Flags() flags := rootCmd.Flags()
persistent := rootCmd.PersistentFlags() persistent := rootCmd.PersistentFlags()
persistent.StringVarP(&cfgFile, "config", "c", "", "config file path") persistent.StringVarP(&cfgFile, "config", "c", "", "config file path")
persistent.StringP("database", "d", "./filebrowser.db", "database path") persistent.StringP("database", "d", "./filebrowser.db", "database path")
flags.Bool("noauth", false, "use the noauth auther when using quick setup")
flags.String("username", "admin", "username for the first user when using quick config") flags.String("username", "admin", "username for the first user when using quick config")
flags.String("password", "", "hashed password for the first user when using quick config (default \"admin\")") flags.String("password", "", "hashed password for the first user when using quick config (default \"admin\")")
@ -50,44 +54,9 @@ func addServerFlags(flags *pflag.FlagSet) {
flags.StringP("baseurl", "b", "", "base url") flags.StringP("baseurl", "b", "", "base url")
} }
// NOTE: we could simply bind the flags to viper and use IsSet.
// Although there is a bug on Viper that always returns true on IsSet
// if a flag is binded. Our alternative way is to manually check
// the flag and then the value from env/config/gotten by viper.
// https://github.com/spf13/viper/pull/331
func getStringViperFlag(flags *pflag.FlagSet, key string) (string, bool) {
value := ""
set := false
// If set on Flags, use it.
flags.Visit(func(flag *pflag.Flag) {
if flag.Name == key {
set = true
value, _ = flags.GetString(key)
}
})
if set {
return value, set
}
// If set through viper (env, config), return it.
if v.IsSet(key) {
return v.GetString(key), true
}
// Otherwise use default value on flags.
value, _ = flags.GetString(key)
return value, false
}
func mustGetStringViperFlag(flags *pflag.FlagSet, key string) string {
val, _ := getStringViperFlag(flags, key)
return val
}
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
Use: "filebrowser", Use: "filebrowser",
Version: version.Version,
Short: "A stylish web-based file browser", Short: "A stylish web-based file browser",
Long: `File Browser CLI lets you create the database to use with File Browser, Long: `File Browser CLI lets you create the database to use with File Browser,
manage your users and all the configurations without acessing the manage your users and all the configurations without acessing the
@ -111,46 +80,50 @@ If you don't set "config", it will look for a configuration file called
The precedence of the configuration values are as follows: The precedence of the configuration values are as follows:
- flag - flags
- environment variable - environment variables
- configuration file - configuration file
- database values
- defaults - defaults
The environment variables are prefixed by "FB_" followed by the option The environment variables are prefixed by "FB_" followed by the option
name in caps. So to set "database" via an env variable, you should name in caps. So to set "database" via an env variable, you should
set FB_DATABASE equals to the path. set FB_DATABASE.
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: python(func(cmd *cobra.Command, args []string, d pythonData) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
log.Println(cfgFile)
if !d.hadDB { if !d.hadDB {
quickSetup(cmd.Flags(), d) quickSetup(cmd.Flags(), d)
} }
server := getServerWithViper(cmd.Flags(), d.store) server := getRunParams(cmd.Flags(), d.store)
setupLog(server.Log) setupLog(server.Log)
root, err := filepath.Abs(server.Root) root, err := filepath.Abs(server.Root)
checkErr(err) checkErr(err)
server.Root = root server.Root = root
handler, err := fbhttp.NewHandler(d.store, server) adr := server.Address + ":" + server.Port
checkErr(err)
var listener net.Listener var listener net.Listener
if server.TLSKey != "" && server.TLSCert != "" { if server.TLSKey != "" && server.TLSCert != "" {
cer, err := tls.LoadX509KeyPair(server.TLSCert, server.TLSKey) cer, err := tls.LoadX509KeyPair(server.TLSCert, server.TLSKey)
checkErr(err) checkErr(err)
config := &tls.Config{Certificates: []tls.Certificate{cer}} listener, err = tls.Listen("tcp", adr, &tls.Config{Certificates: []tls.Certificate{cer}})
listener, err = tls.Listen("tcp", server.Address+":"+server.Port, config)
checkErr(err) checkErr(err)
} else { } else {
listener, err = net.Listen("tcp", server.Address+":"+server.Port) listener, err = net.Listen("tcp", adr)
checkErr(err) checkErr(err)
} }
handler, err := fbhttp.NewHandler(d.store, server)
checkErr(err)
log.Println("Listening on", listener.Addr().String()) log.Println("Listening on", listener.Addr().String())
if err := http.Serve(listener, handler); err != nil { if err := http.Serve(listener, handler); err != nil {
log.Fatal(err) log.Fatal(err)
@ -158,41 +131,70 @@ user created with the credentials from options "username" and "password".`,
}, pythonConfig{allowNoDB: true}), }, pythonConfig{allowNoDB: true}),
} }
func getServerWithViper(flags *pflag.FlagSet, st *storage.Storage) *settings.Server { func getRunParams(flags *pflag.FlagSet, st *storage.Storage) *settings.Server {
server, err := st.Settings.GetServer() server, err := st.Settings.GetServer()
checkErr(err) checkErr(err)
if val, set := getStringViperFlag(flags, "root"); set { if val, set := getParamB(flags, "root"); set {
server.Root = val server.Root = val
} }
if val, set := getStringViperFlag(flags, "baseurl"); set { if val, set := getParamB(flags, "baseurl"); set {
server.BaseURL = val server.BaseURL = val
} }
if val, set := getStringViperFlag(flags, "address"); set { if val, set := getParamB(flags, "address"); set {
server.Address = val server.Address = val
} }
if val, set := getStringViperFlag(flags, "port"); set { if val, set := getParamB(flags, "port"); set {
server.Port = val server.Port = val
} }
if val, set := getStringViperFlag(flags, "log"); set { if val, set := getParamB(flags, "log"); set {
server.Log = val server.Log = val
} }
if val, set := getStringViperFlag(flags, "key"); set { if val, set := getParamB(flags, "key"); set {
server.TLSKey = val server.TLSKey = val
} }
if val, set := getStringViperFlag(flags, "cert"); set { if val, set := getParamB(flags, "cert"); set {
server.TLSCert = val server.TLSCert = val
} }
return server return server
} }
// getParamB returns a parameter as a string and a boolean to tell if it is different from the default
//
// NOTE: we could simply bind the flags to viper and use IsSet.
// Although there is a bug on Viper that always returns true on IsSet
// if a flag is binded. Our alternative way is to manually check
// the flag and then the value from env/config/gotten by viper.
// https://github.com/spf13/viper/pull/331
func getParamB(flags *pflag.FlagSet, key string) (string, bool) {
value, _ := flags.GetString(key)
// If set on Flags, use it.
if flags.Changed(key) {
return value, true
}
// If set through viper (env, config), return it.
if v.IsSet(key) {
return v.GetString(key), true
}
// Otherwise use default value on flags.
return value, false
}
func getParam(flags *pflag.FlagSet, key string) string {
val, _ := getParamB(flags, key)
return val
}
func setupLog(logMethod string) { func setupLog(logMethod string) {
switch logMethod { switch logMethod {
case "stdout": case "stdout":
@ -209,14 +211,12 @@ func setupLog(logMethod string) {
MaxBackups: 10, MaxBackups: 10,
}) })
} }
} }
func quickSetup(flags *pflag.FlagSet, d pythonData) { func quickSetup(flags *pflag.FlagSet, d pythonData) {
set := &settings.Settings{ set := &settings.Settings{
Key: generateRandomBytes(64), // 256 bit Key: generateRandomBytes(64), // 256 bit
Signup: false, Signup: false,
AuthMethod: auth.MethodJSONAuth,
Defaults: settings.UserDefaults{ Defaults: settings.UserDefaults{
Scope: ".", Scope: ".",
Locale: "en", Locale: "en",
@ -233,27 +233,34 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) {
}, },
} }
ser := &settings.Server{ var err error
BaseURL: mustGetStringViperFlag(flags, "baseurl"), if _, noauth := getParamB(flags, "noauth"); noauth {
Port: mustGetStringViperFlag(flags, "port"), set.AuthMethod = auth.MethodNoAuth
Log: mustGetStringViperFlag(flags, "log"), err = d.store.Auth.Save(&auth.NoAuth{})
TLSKey: mustGetStringViperFlag(flags, "key"), } else {
TLSCert: mustGetStringViperFlag(flags, "cert"), set.AuthMethod = auth.MethodJSONAuth
Address: mustGetStringViperFlag(flags, "address"), err = d.store.Auth.Save(&auth.JSONAuth{})
Root: mustGetStringViperFlag(flags, "root"),
} }
err := d.store.Settings.Save(set)
checkErr(err) checkErr(err)
err = d.store.Settings.Save(set)
checkErr(err)
ser := &settings.Server{
BaseURL: getParam(flags, "baseurl"),
Port: getParam(flags, "port"),
Log: getParam(flags, "log"),
TLSKey: getParam(flags, "key"),
TLSCert: getParam(flags, "cert"),
Address: getParam(flags, "address"),
Root: getParam(flags, "root"),
}
err = d.store.Settings.SaveServer(ser) err = d.store.Settings.SaveServer(ser)
checkErr(err) checkErr(err)
err = d.store.Auth.Save(&auth.JSONAuth{}) username := getParam(flags, "username")
checkErr(err) password := getParam(flags, "password")
username := mustGetStringViperFlag(flags, "username")
password := mustGetStringViperFlag(flags, "password")
if password == "" { if password == "" {
password, err = users.HashPwd("admin") password, err = users.HashPwd("admin")
@ -261,7 +268,7 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) {
} }
if username == "" || password == "" { if username == "" || password == "" {
checkErr(errors.New("username and password cannot be empty during quick setup")) log.Fatal("username and password cannot be empty during quick setup")
} }
user := &users.User{ user := &users.User{
@ -297,7 +304,9 @@ func initConfig() {
if _, ok := err.(v.ConfigParseError); ok { if _, ok := err.(v.ConfigParseError); ok {
panic(err) panic(err)
} }
// TODO: log.Println("No config file provided") cfgFile = "No config file used"
} else {
cfgFile = "Using config file: " + v.ConfigFileUsed()
} }
// else TODO: log.Println("Using config file:", v.ConfigFileUsed())
} }

View File

@ -17,7 +17,15 @@ func init() {
var rulesRmCommand = &cobra.Command{ var rulesRmCommand = &cobra.Command{
Use: "rm <index> [index_end]", Use: "rm <index> [index_end]",
Short: "Remove a global rule or user rule", Short: "Remove a global rule or user rule",
Long: `Remove a global rule or user rule.`, Long: `Remove a global rule or user rule. The provided index
is the same that's printed when you run 'rules ls'. Note
that after each removal/addition, the index of the
commands change. So be careful when removing them after each
other.
You can also specify an optional parameter (index_end) so
you can remove all commands from 'index' to 'index_end',
including 'index_end'.`,
Args: func(cmd *cobra.Command, args []string) error { Args: func(cmd *cobra.Command, args []string) error {
if err := cobra.RangeArgs(1, 2)(cmd, args); err != nil { if err := cobra.RangeArgs(1, 2)(cmd, args); err != nil {
return err return err

View File

@ -19,6 +19,7 @@ func init() {
var rulesCmd = &cobra.Command{ var rulesCmd = &cobra.Command{
Use: "rules", Use: "rules",
Version: rootCmd.Version,
Short: "Rules management utility", Short: "Rules management utility",
Long: `On each subcommand you'll have available at least two flags: Long: `On each subcommand you'll have available at least two flags:
"username" and "id". You must either set only one of them "username" and "id". You must either set only one of them
@ -42,14 +43,14 @@ func runRules(st *storage.Storage, cmd *cobra.Command, users func(*users.User),
return return
} }
settings, err := st.Settings.Get() s, err := st.Settings.Get()
checkErr(err) checkErr(err)
if global != nil { if global != nil {
global(settings) global(s)
} }
printRules(settings.Rules, id) printRules(s.Rules, id)
} }
func getUserIdentifier(flags *pflag.FlagSet) interface{} { func getUserIdentifier(flags *pflag.FlagSet) interface{} {

View File

@ -15,6 +15,7 @@ func init() {
var upgradeCmd = &cobra.Command{ var upgradeCmd = &cobra.Command{
Use: "upgrade", Use: "upgrade",
Version: rootCmd.Version,
Short: "Upgrades an old configuration", Short: "Upgrades an old configuration",
Long: `Upgrades an old configuration. This command DOES NOT Long: `Upgrades an old configuration. This command DOES NOT
import share links because they are incompatible with import share links because they are incompatible with
@ -24,7 +25,7 @@ this version.`,
flags := cmd.Flags() flags := cmd.Flags()
oldDB := mustGetString(flags, "old.database") oldDB := mustGetString(flags, "old.database")
oldConf := mustGetString(flags, "old.config") oldConf := mustGetString(flags, "old.config")
err := importer.Import(oldDB, oldConf, mustGetStringViperFlag(flags, "database")) err := importer.Import(oldDB, oldConf, getParam(flags, "database"))
checkErr(err) checkErr(err)
}, },
} }

View File

@ -19,6 +19,7 @@ func init() {
var usersCmd = &cobra.Command{ var usersCmd = &cobra.Command{
Use: "users", Use: "users",
Version: rootCmd.Version,
Short: "Users management utility", Short: "Users management utility",
Long: `Users management utility.`, Long: `Users management utility.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,

View File

@ -9,8 +9,10 @@ func init() {
} }
var usersExportCmd = &cobra.Command{ var usersExportCmd = &cobra.Command{
Use: "export <filename>", Use: "export <path>",
Short: "Export all users.", Short: "Export all users to a file.",
Long: `Export all users to a json or yaml file. Please indicate the
path to the file where you want to write the users.`,
Args: jsonYamlArg, Args: jsonYamlArg,
Run: python(func(cmd *cobra.Command, args []string, d pythonData) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
list, err := d.store.Users.Gets("") list, err := d.store.Users.Gets("")

View File

@ -16,8 +16,12 @@ func init() {
} }
var usersImportCmd = &cobra.Command{ var usersImportCmd = &cobra.Command{
Use: "import <filename>", Use: "import <path>",
Short: "Import users from a file.", Short: "Import users from a file",
Long: `Import users from a file. The path must be for a json or yaml
file. You can use this command to import new users to your
installation. For that, just don't place their ID on the files
list or set it to 0.`,
Args: jsonYamlArg, Args: jsonYamlArg,
Run: python(func(cmd *cobra.Command, args []string, d pythonData) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
fd, err := os.Open(args[0]) fd, err := os.Open(args[0])

View File

@ -26,8 +26,10 @@ options you want to change.`,
password := mustGetString(flags, "password") password := mustGetString(flags, "password")
newUsername := mustGetString(flags, "username") newUsername := mustGetString(flags, "username")
var err error var (
var user *users.User err error
user *users.User
)
if id != 0 { if id != 0 {
user, err = d.store.Users.Get("", id) user, err = d.store.Users.Get("", id)

View File

@ -19,8 +19,7 @@ import (
func checkErr(err error) { func checkErr(err error) {
if err != nil { if err != nil {
fmt.Println(err) log.Fatal(err)
os.Exit(1)
} }
} }
@ -67,7 +66,7 @@ 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} data := pythonData{hadDB: true}
path := mustGetStringViperFlag(cmd.Flags(), "database") path := getParam(cmd.Flags(), "database")
_, err := os.Stat(path) _, err := os.Stat(path)
if os.IsNotExist(err) { if os.IsNotExist(err) {

View File

@ -1,20 +1,32 @@
package cmd package cmd
import ( import (
"fmt" "text/template"
"github.com/filebrowser/filebrowser/v2/version"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func init() { func init() {
rootCmd.AddCommand(versionCmd) rootCmd.AddCommand(versionCmd)
cmdsCmd.AddCommand(versionCmd)
configCmd.AddCommand(versionCmd)
hashCmd.AddCommand(versionCmd)
upgradeCmd.AddCommand(versionCmd)
rulesCmd.AddCommand(versionCmd)
usersCmd.AddCommand(versionCmd)
} }
var versionCmd = &cobra.Command{ var versionCmd = &cobra.Command{
Use: "version", Use: "version",
Short: "Print the version number", Short: "Print the version number of File Browser",
Long: `All software has versions. This is File Browser's`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
fmt.Println("File Browser Version " + version.Version) // https://github.com/spf13/cobra/issues/724
t := template.New("version")
template.Must(t.Parse(rootCmd.VersionTemplate()))
err := t.Execute(rootCmd.OutOrStdout(), rootCmd)
if err != nil {
rootCmd.Println(err)
}
}, },
} }

View File

@ -7,7 +7,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/dgrijalva/jwt-go" jwt "github.com/dgrijalva/jwt-go"
"github.com/dgrijalva/jwt-go/request" "github.com/dgrijalva/jwt-go/request"
"github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/errors"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"

View File

@ -9,7 +9,7 @@ import (
"strings" "strings"
"text/template" "text/template"
"github.com/GeertJohan/go.rice" rice "github.com/GeertJohan/go.rice"
"github.com/filebrowser/filebrowser/v2/auth" "github.com/filebrowser/filebrowser/v2/auth"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage" "github.com/filebrowser/filebrowser/v2/storage"

View File

@ -13,8 +13,8 @@ import (
"github.com/asdine/storm" "github.com/asdine/storm"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage" "github.com/filebrowser/filebrowser/v2/storage"
"github.com/pelletier/go-toml" toml "github.com/pelletier/go-toml"
"gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
) )
type oldDefs struct { type oldDefs struct {
@ -62,14 +62,14 @@ var defaults = &oldConf{
}, },
} }
func importConf(db *storm.DB, path string, sto *storage.Storage) error { func readConf(path string) (*oldConf, error) {
cfg := &oldConf{} cfg := &oldConf{}
if path != "" { if path != "" {
ext := filepath.Ext(path) ext := filepath.Ext(path)
fd, err := os.Open(path) fd, err := os.Open(path)
if err != nil { if err != nil {
return err return nil, err
} }
defer fd.Close() defer fd.Close()
@ -81,23 +81,32 @@ func importConf(db *storm.DB, path string, sto *storage.Storage) error {
case ".yaml", ".yml": case ".yaml", ".yml":
err = yaml.NewDecoder(fd).Decode(cfg) err = yaml.NewDecoder(fd).Decode(cfg)
default: default:
return errors.New("unsupported config extension " + ext) return nil, errors.New("unsupported config extension " + ext)
} }
if err != nil { if err != nil {
return err return nil, err
} }
} else { } else {
cfg = defaults cfg = defaults
path, err := filepath.Abs(".") path, err := filepath.Abs(".")
if err != nil { if err != nil {
return err return nil, err
} }
cfg.Defaults.Scope = path cfg.Defaults.Scope = path
} }
return cfg, nil
}
func importConf(db *storm.DB, path string, sto *storage.Storage) error {
cfg, err := readConf(path)
if err != nil {
return err
}
commands := map[string][]string{} commands := map[string][]string{}
err := db.Get("config", "commands", &commands) err = db.Get("config", "commands", &commands)
if err != nil { if err != nil {
return err return err
} }

View File

@ -12,32 +12,29 @@ type usersBackend struct {
db *storm.DB db *storm.DB
} }
func (st usersBackend) GetByID(id uint) (*users.User, error) { func (st usersBackend) GetBy(i interface{}) (user *users.User, err error) {
user := &users.User{} user = &users.User{}
err := st.db.One("ID", id, user)
var arg string
switch i.(type) {
case uint:
arg = "ID"
case string:
arg = "Username"
default:
return nil, errors.ErrInvalidDataType
}
err = st.db.One(arg, i, user)
if err != nil {
if err == storm.ErrNotFound { if err == storm.ErrNotFound {
return nil, errors.ErrNotExist return nil, errors.ErrNotExist
} }
if err != nil {
return nil, err return nil, err
} }
return user, nil return
}
func (st usersBackend) GetByUsername(username string) (*users.User, error) {
user := &users.User{}
err := st.db.One("Username", username, user)
if err == storm.ErrNotFound {
return nil, errors.ErrNotExist
}
if err != nil {
return nil, err
}
return user, nil
} }
func (st usersBackend) Gets() ([]*users.User, error) { func (st usersBackend) Gets() ([]*users.User, error) {
@ -82,7 +79,7 @@ func (st usersBackend) DeleteByID(id uint) error {
} }
func (st usersBackend) DeleteByUsername(username string) error { func (st usersBackend) DeleteByUsername(username string) error {
user, err := st.GetByUsername(username) user, err := st.GetBy(username)
if err != nil { if err != nil {
return err return err
} }

View File

@ -9,8 +9,7 @@ import (
// StorageBackend is the interface to implement for a users storage. // StorageBackend is the interface to implement for a users storage.
type StorageBackend interface { type StorageBackend interface {
GetByID(uint) (*User, error) GetBy(interface{}) (*User, error)
GetByUsername(string) (*User, error)
Gets() ([]*User, error) Gets() ([]*User, error)
Save(u *User) error Save(u *User) error
Update(u *User, fields ...string) error Update(u *User, fields ...string) error
@ -36,27 +35,13 @@ func NewStorage(back StorageBackend) *Storage {
// Get allows you to get a user by its name or username. The provided // Get allows you to get a user by its name or username. The provided
// id must be a string for username lookup or a uint for id lookup. If id // id must be a string for username lookup or a uint for id lookup. If id
// is neither, a ErrInvalidDataType will be returned. // is neither, a ErrInvalidDataType will be returned.
func (s *Storage) Get(baseScope string, id interface{}) (*User, error) { func (s *Storage) Get(baseScope string, id interface{}) (user *User, err error) {
var ( user, err = s.back.GetBy(id)
user *User
err error
)
switch id.(type) {
case string:
user, err = s.back.GetByUsername(id.(string))
case uint:
user, err = s.back.GetByID(id.(uint))
default:
return nil, errors.ErrInvalidDataType
}
if err != nil { if err != nil {
return nil, err return
} }
user.Clean(baseScope) user.Clean(baseScope)
return user, err return
} }
// Gets gets a list of all users. // Gets gets a list of all users.

View File

@ -131,7 +131,7 @@ pushRicebox () {
git clone git@github.com:filebrowser/caddy caddy git clone git@github.com:filebrowser/caddy caddy
cd caddy cd caddy
cp $base/http/rice-box.go ./ cp ../http/rice-box.go ./
sed -i 's/package lib/package caddy/g' ./rice-box.go sed -i 's/package lib/package caddy/g' ./rice-box.go
git checkout -b update-rice-box origin/master git checkout -b update-rice-box origin/master
git config --local user.name "Filebrowser Bot" git config --local user.name "Filebrowser Bot"
@ -238,8 +238,11 @@ release () {
exit 1 exit 1
fi fi
set +e
git rev-parse --verify --quiet release git rev-parse --verify --quiet release
if [ $? -ne 0 ]; then exitcode=$?
set -e
if [ $exitcode -ne 0 ]; then
git checkout -b release "$semver" git checkout -b release "$semver"
else else
git checkout release git checkout release