diff --git a/CHANGELOG.md b/CHANGELOG.md index eb752368..5eb36e2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,20 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. + +# v0.2.0 + + - improved UI + - more unified coehisive look + - Adjusted header bar look and icon behavior + - The shell is dead. + - If you need to use custom commands, exec into the docker container. + - The json config file is dead. + - All configuration is done via advanced `filebrowser.yaml` + - The only flag that is allowed is flag to specify config file. + - Removed old code to migrate database versions + - Removed all unused cmd code + # v0.1.4 - various UI fixes - Added download button back to toolbar diff --git a/Dockerfile b/Dockerfile index a0f628a7..070266d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,7 @@ RUN apk --no-cache add \ VOLUME /srv EXPOSE 8080 WORKDIR / -COPY --from=base /app/.filebrowser.json /.filebrowser.json +COPY --from=base /app/filebrowser.yaml /filebrowser.yaml COPY --from=base /app/filebrowser /filebrowser COPY --from=nbuild /app/dist/ /frontend/dist/ ENTRYPOINT [ "./filebrowser" ] \ No newline at end of file diff --git a/README.md b/README.md index d63ca03d..1b0a8af6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ ## Filebrowser -**Note: Intended to be used in docker only.** +> **NOTE** +Intended for docker use only + +> **Warning** +Starting with v0.2.0, *ALL* configuration is done via `filebrowser.yaml` configuration file. `.filebrowser.json` and any flags other than `-c` and `-config` during execution WILL NO LONGER WORK. This is by design, in order to use the v0.2.0 You can mount your directory and initialize a new DB with a new default `filebrowser.yaml` which you can tweak and use in the future. Or you can copy and paste the default startup `filebrowser.yaml` below. This fork makes the following significant changes to filebrowser for origin: @@ -9,7 +13,7 @@ This fork makes the following significant changes to filebrowser for origin: - [x] Realtime results as you type - [x] Works with file type filter - [x] better desktop search view - 1. [ ] Preview enhancements + 1. [x] Preview enhancements - Preview default view is constrained to files subwindow, which can be toggled to fullscreen. 1. [x] Improved and simplified GUI @@ -20,11 +24,8 @@ This fork makes the following significant changes to filebrowser for origin: - [x] Uses latest npm and node version - [x] Removes deprecated npm packages - [x] Updates golang dependencies - - [ ] Remove all unnecessary packages, replaces with generic functions. - 1. [ ] Moved all configurations to filebrowser.json. - no more flags or binary operations to db - 1. [ ] File browsing uses index first for better performance - - file details shown only when toggled or needed + - [x] Remove all unnecessary packages, replaces with generic functions. + 1. [x] **IMPORTANT** Moved all configurations to `filebrowser.yaml`. no more flags or binary operations to db ## About @@ -44,14 +45,14 @@ Once this is fully complete, the only updates to th ## Look -General UI desktop (dark mode): -![image](https://github.com/gtsteffaniak/filebrowser/assets/42989099/11346953-f3eb-4f2f-a833-1d615e0e38bc) +This is how desktop search looks in 0.2.0 +![darkmode-capture1](https://github.com/gtsteffaniak/filebrowser/assets/42989099/35cdeb3b-ab79-4b04-8001-8f51f6ea06bb) -[General UI mobile (dark mode)](https://github.com/gtsteffaniak/filebrowser/assets/42989099/634d3ba6-7ac0-425b-8a83-419743e92fec) +![darkmode2](https://github.com/gtsteffaniak/filebrowser/assets/42989099/8d426356-26cf-407b-b078-bf58f198e799) -![image](https://github.com/gtsteffaniak/filebrowser/assets/42989099/c8dc8af1-6869-4736-9092-c47f735bbdc0) +However [mobile search](https://github.com/gtsteffaniak/filebrowser/assets/42989099/37e8f03b-4f5a-4689-aa6c-5cd858a858e9) still appears very similar to filebrowser/filebrowsers original implementation. -[However, mobile search still appears very similar to filebrowser/filebrowsers original implementation](https://github.com/gtsteffaniak/filebrowser/assets/42989099/e179b821-f4e2-4568-b895-4e00de371637) +[Mobile web also looks similar](https://github.com/gtsteffaniak/filebrowser/assets/42989099/b04d3c1f-154b-45ba-927c-2112926ad3a9) ## Performance @@ -124,14 +125,9 @@ volumes: ## Configuration -Note: still a WIP migrating configuration to json. +All configuration is now done via a single configuration file: `filebrowser.yaml`, here is an example [configuration file](./backend/filebrowser.yaml). +### background -All configuration is now done via the filebrowser.json config file. -This was chosen because it works best with a docker first use case. - -Previously the primary way to configure filebrowser was via flags. -But this quickly became cumbersome if you had many configurations to make - -The other method to configure was via `filebrowser config` commands which -would write configurations to a db if it existed already. -When considering +The original project filebrowser/filebrowser used multiple different ways to configure the server. +This was confusing and difficult to work with from a user and from a developer's perspective. +So I completely redesigned the program to use one single human-readable config file. diff --git a/backend/.filebrowser.json b/backend/.filebrowser.json deleted file mode 100644 index 33b9a3dd..00000000 --- a/backend/.filebrowser.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - - "port": 8080, - "baseURL": "", - "address": "", - "log": "stdout", - "database": "./database.db", - "root": "/srv" - -} \ No newline at end of file diff --git a/backend/auth/auth.go b/backend/auth/auth.go index c3b143a8..a1121c6f 100644 --- a/backend/auth/auth.go +++ b/backend/auth/auth.go @@ -3,14 +3,13 @@ package auth import ( "net/http" - "github.com/gtsteffaniak/filebrowser/settings" "github.com/gtsteffaniak/filebrowser/users" ) // Auther is the authentication interface. type Auther interface { // Auth is called to authenticate a request. - Auth(r *http.Request, usr users.Store, stg *settings.Settings, srv *settings.Server) (*users.User, error) + Auth(r *http.Request, usr users.Store) (*users.User, error) // LoginPage indicates if this auther needs a login page. LoginPage() bool } diff --git a/backend/auth/hook.go b/backend/auth/hook.go index 7e165f07..0ae5d143 100644 --- a/backend/auth/hook.go +++ b/backend/auth/hook.go @@ -15,9 +15,6 @@ import ( "github.com/gtsteffaniak/filebrowser/users" ) -// MethodHookAuth is used to identify hook auth. -const MethodHookAuth = "hook" - type hookCred struct { Password string `json:"password"` Username string `json:"username"` @@ -34,7 +31,7 @@ type HookAuth struct { } // Auth authenticates the user via a json in content body. -func (a *HookAuth) Auth(r *http.Request, usr users.Store, stg *settings.Settings, srv *settings.Server) (*users.User, error) { +func (a *HookAuth) Auth(r *http.Request, usr users.Store) (*users.User, error) { var cred hookCred if r.Body == nil { @@ -47,8 +44,8 @@ func (a *HookAuth) Auth(r *http.Request, usr users.Store, stg *settings.Settings } a.Users = usr - a.Settings = stg - a.Server = srv + a.Settings = &settings.GlobalConfiguration + a.Server = &settings.GlobalConfiguration.Server a.Cred = cred action, err := a.RunCommand() @@ -153,19 +150,18 @@ func (a *HookAuth) SaveUser() (*users.User, error) { if err != nil { return nil, err } - // create user with the provided credentials d := &users.User{ Username: a.Cred.Username, Password: pass, - Scope: a.Settings.Defaults.Scope, - Locale: a.Settings.Defaults.Locale, - ViewMode: a.Settings.Defaults.ViewMode, - SingleClick: a.Settings.Defaults.SingleClick, - Sorting: a.Settings.Defaults.Sorting, - Perm: a.Settings.Defaults.Perm, - Commands: a.Settings.Defaults.Commands, - HideDotfiles: a.Settings.Defaults.HideDotfiles, + Scope: a.Settings.UserDefaults.Scope, + Locale: a.Settings.UserDefaults.Locale, + ViewMode: a.Settings.UserDefaults.ViewMode, + SingleClick: a.Settings.UserDefaults.SingleClick, + Sorting: a.Settings.UserDefaults.Sorting, + Perm: a.Settings.UserDefaults.Perm, + Commands: a.Settings.UserDefaults.Commands, + HideDotfiles: a.Settings.UserDefaults.HideDotfiles, } u = a.GetUser(d) @@ -222,7 +218,7 @@ func (a *HookAuth) GetUser(d *users.User) *users.User { Password: d.Password, Scope: a.Fields.GetString("user.scope", d.Scope), Locale: a.Fields.GetString("user.locale", d.Locale), - ViewMode: users.ViewMode(a.Fields.GetString("user.viewMode", string(d.ViewMode))), + ViewMode: d.ViewMode, SingleClick: a.Fields.GetBoolean("user.singleClick", d.SingleClick), Sorting: files.Sorting{ Asc: a.Fields.GetBoolean("user.sorting.asc", d.Sorting.Asc), diff --git a/backend/auth/json.go b/backend/auth/json.go index bcd8af52..d3734789 100644 --- a/backend/auth/json.go +++ b/backend/auth/json.go @@ -11,9 +11,6 @@ import ( "github.com/gtsteffaniak/filebrowser/users" ) -// MethodJSONAuth is used to identify json auth. -const MethodJSONAuth = "json" - type jsonCred struct { Password string `json:"password"` Username string `json:"username"` @@ -26,7 +23,8 @@ type JSONAuth struct { } // Auth authenticates the user via a json in content body. -func (a JSONAuth) Auth(r *http.Request, usr users.Store, stg *settings.Settings, srv *settings.Server) (*users.User, error) { +func (a JSONAuth) Auth(r *http.Request, usr users.Store) (*users.User, error) { + config := &settings.GlobalConfiguration var cred jsonCred if r.Body == nil { @@ -51,7 +49,7 @@ func (a JSONAuth) Auth(r *http.Request, usr users.Store, stg *settings.Settings, } } - u, err := usr.Get(srv.Root, cred.Username) + u, err := usr.Get(config.Server.Root, cred.Username) if err != nil || !users.CheckPwd(cred.Password, u.Password) { return nil, os.ErrPermission } diff --git a/backend/auth/none.go b/backend/auth/none.go index 6549780f..43688a26 100644 --- a/backend/auth/none.go +++ b/backend/auth/none.go @@ -14,8 +14,8 @@ const MethodNoAuth = "noauth" type NoAuth struct{} // Auth uses authenticates user 1. -func (a NoAuth) Auth(r *http.Request, usr users.Store, stg *settings.Settings, srv *settings.Server) (*users.User, error) { - return usr.Get(srv.Root, uint(1)) +func (a NoAuth) Auth(r *http.Request, usr users.Store) (*users.User, error) { + return usr.Get(settings.GlobalConfiguration.Server.Root, uint(1)) } // LoginPage tells that no auth doesn't require a login page. diff --git a/backend/auth/proxy.go b/backend/auth/proxy.go index a14e92a6..cf3e5936 100644 --- a/backend/auth/proxy.go +++ b/backend/auth/proxy.go @@ -4,8 +4,9 @@ import ( "net/http" "os" - "github.com/gtsteffaniak/filebrowser/errors" "github.com/gtsteffaniak/filebrowser/settings" + + "github.com/gtsteffaniak/filebrowser/errors" "github.com/gtsteffaniak/filebrowser/users" ) @@ -18,9 +19,9 @@ type ProxyAuth struct { } // Auth authenticates the user via an HTTP header. -func (a ProxyAuth) Auth(r *http.Request, usr users.Store, stg *settings.Settings, srv *settings.Server) (*users.User, error) { +func (a ProxyAuth) Auth(r *http.Request, usr users.Store) (*users.User, error) { username := r.Header.Get(a.Header) - user, err := usr.Get(srv.Root, username) + user, err := usr.Get(settings.GlobalConfiguration.Server.Root, username) if err == errors.ErrNotExist { return nil, os.ErrPermission } diff --git a/backend/benchmark_results.txt b/backend/benchmark_results.txt new file mode 100644 index 00000000..655b1f9c --- /dev/null +++ b/backend/benchmark_results.txt @@ -0,0 +1,42 @@ + + == Running benchmark == +/usr/local/go/bin/go +? github.com/gtsteffaniak/filebrowser [no test files] +? github.com/gtsteffaniak/filebrowser/auth [no test files] +? github.com/gtsteffaniak/filebrowser/cmd [no test files] +PASS +ok github.com/gtsteffaniak/filebrowser/diskcache 0.004s +? github.com/gtsteffaniak/filebrowser/errors [no test files] +? github.com/gtsteffaniak/filebrowser/files [no test files] +PASS +ok github.com/gtsteffaniak/filebrowser/fileutils 0.004s +2023/09/02 19:15:20 h: 401 +2023/09/02 19:15:20 h: 401 +2023/09/02 19:15:20 h: 401 +2023/09/02 19:15:20 h: 401 +2023/09/02 19:15:20 h: 401 +2023/09/02 19:15:20 h: 401 +PASS +ok github.com/gtsteffaniak/filebrowser/http 0.094s +PASS +ok github.com/gtsteffaniak/filebrowser/img 0.122s +PASS +ok github.com/gtsteffaniak/filebrowser/rules 0.002s +PASS +ok github.com/gtsteffaniak/filebrowser/runner 0.004s +goos: linux +goarch: amd64 +pkg: github.com/gtsteffaniak/filebrowser/search +cpu: 11th Gen Intel(R) Core(TM) i5-11320H @ 3.20GHz +BenchmarkSearchAllIndexes-8 10 5176084 ns/op 2743632 B/op 42785 allocs/op +BenchmarkFillIndex-8 10 3263308 ns/op 18485 B/op 453 allocs/op +PASS +ok github.com/gtsteffaniak/filebrowser/search 0.109s +PASS +ok github.com/gtsteffaniak/filebrowser/settings 0.004s +? github.com/gtsteffaniak/filebrowser/share [no test files] +? github.com/gtsteffaniak/filebrowser/storage [no test files] +? github.com/gtsteffaniak/filebrowser/storage/bolt [no test files] +PASS +ok github.com/gtsteffaniak/filebrowser/users 0.004s +? github.com/gtsteffaniak/filebrowser/version [no test files] diff --git a/backend/cmd/cmd.go b/backend/cmd/cmd.go deleted file mode 100644 index 18f52337..00000000 --- a/backend/cmd/cmd.go +++ /dev/null @@ -1,12 +0,0 @@ -package cmd - -import ( - "log" -) - -// Execute executes the commands. -func Execute() { - if err := rootCmd.Execute(); err != nil { - log.Fatal(err) - } -} diff --git a/backend/cmd/cmds.go b/backend/cmd/cmds.go deleted file mode 100644 index cf951995..00000000 --- a/backend/cmd/cmds.go +++ /dev/null @@ -1,26 +0,0 @@ -package cmd - -import ( - "fmt" - - "github.com/spf13/cobra" -) - -func init() { - rootCmd.AddCommand(cmdsCmd) -} - -var cmdsCmd = &cobra.Command{ - Use: "cmds", - Short: "Command runner management utility", - Long: `Command runner management utility.`, - Args: cobra.NoArgs, -} - -func printEvents(m map[string][]string) { - for evt, cmds := range m { - for i, cmd := range cmds { - fmt.Printf("%s(%d): %s\n", evt, i, cmd) - } - } -} diff --git a/backend/cmd/cmds_add.go b/backend/cmd/cmds_add.go deleted file mode 100644 index fd2c0dc5..00000000 --- a/backend/cmd/cmds_add.go +++ /dev/null @@ -1,27 +0,0 @@ -package cmd - -import ( - "strings" - - "github.com/spf13/cobra" -) - -func init() { - cmdsCmd.AddCommand(cmdsAddCmd) -} - -var cmdsAddCmd = &cobra.Command{ - Use: "add ", - Short: "Add a command to run on a specific event", - Long: `Add a command to run on a specific event.`, - Args: cobra.MinimumNArgs(2), //nolint:gomnd - 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 = d.store.Settings.Save(s) - checkErr(err) - printEvents(s.Commands) - }, pythonConfig{}), -} diff --git a/backend/cmd/cmds_ls.go b/backend/cmd/cmds_ls.go deleted file mode 100644 index 71f36382..00000000 --- a/backend/cmd/cmds_ls.go +++ /dev/null @@ -1,31 +0,0 @@ -package cmd - -import ( - "github.com/spf13/cobra" -) - -func init() { - cmdsCmd.AddCommand(cmdsLsCmd) - cmdsLsCmd.Flags().StringP("event", "e", "", "event name, without 'before' or 'after'") -} - -var cmdsLsCmd = &cobra.Command{ - Use: "ls", - Short: "List all commands for each event", - Long: `List all commands for each event.`, - Args: cobra.NoArgs, - Run: python(func(cmd *cobra.Command, args []string, d pythonData) { - s, err := d.store.Settings.Get() - checkErr(err) - evt := mustGetString(cmd.Flags(), "event") - - if evt == "" { - printEvents(s.Commands) - } else { - show := map[string][]string{} - show["before_"+evt] = s.Commands["before_"+evt] - show["after_"+evt] = s.Commands["after_"+evt] - printEvents(show) - } - }, pythonConfig{}), -} diff --git a/backend/cmd/cmds_rm.go b/backend/cmd/cmds_rm.go deleted file mode 100644 index e1f78863..00000000 --- a/backend/cmd/cmds_rm.go +++ /dev/null @@ -1,56 +0,0 @@ -package cmd - -import ( - "strconv" - - "github.com/spf13/cobra" -) - -func init() { - cmdsCmd.AddCommand(cmdsRmCmd) -} - -var cmdsRmCmd = &cobra.Command{ - Use: "rm [index_end]", - Short: "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 { - if err := cobra.RangeArgs(2, 3)(cmd, args); err != nil { //nolint:gomnd - return err - } - - for _, arg := range args[1:] { - if _, err := strconv.Atoi(arg); err != nil { - return err - } - } - - return nil - }, - Run: python(func(cmd *cobra.Command, args []string, d pythonData) { - s, err := d.store.Settings.Get() - checkErr(err) - evt := args[0] - - i, err := strconv.Atoi(args[1]) - checkErr(err) - f := i - if len(args) == 3 { //nolint:gomnd - f, err = strconv.Atoi(args[2]) - checkErr(err) - } - - s.Commands[evt] = append(s.Commands[evt][:i], s.Commands[evt][f+1:]...) - err = d.store.Settings.Save(s) - checkErr(err) - printEvents(s.Commands) - }, pythonConfig{}), -} diff --git a/backend/cmd/config.go b/backend/cmd/config.go deleted file mode 100644 index b93563b9..00000000 --- a/backend/cmd/config.go +++ /dev/null @@ -1,186 +0,0 @@ -package cmd - -import ( - "encoding/json" - nerrors "errors" - "fmt" - "os" - "strings" - "text/tabwriter" - - "github.com/spf13/cobra" - "github.com/spf13/pflag" - - "github.com/gtsteffaniak/filebrowser/auth" - "github.com/gtsteffaniak/filebrowser/errors" - "github.com/gtsteffaniak/filebrowser/settings" -) - -func init() { - rootCmd.AddCommand(configCmd) -} - -var configCmd = &cobra.Command{ - Use: "config", - Short: "Configuration management utility", - Long: `Configuration management utility.`, - Args: cobra.NoArgs, -} - -func addConfigFlags(flags *pflag.FlagSet) { - addServerFlags(flags) - addUserFlags(flags) - flags.BoolP("signup", "s", false, "allow users to signup") - flags.String("shell", "", "shell command to which other commands should be appended") - - flags.String("auth.method", string(auth.MethodJSONAuth), "authentication type") - flags.String("auth.header", "", "HTTP header for auth.method=proxy") - flags.String("auth.command", "", "command for auth.method=hook") - - flags.String("recaptcha.host", "https://www.google.com", "use another host for ReCAPTCHA. recaptcha.net might be useful in China") - flags.String("recaptcha.key", "", "ReCaptcha site key") - flags.String("recaptcha.secret", "", "ReCaptcha secret") - - flags.String("branding.name", "", "replace 'File Browser' by this name") - flags.String("branding.color", "", "set the theme color") - flags.String("branding.files", "", "path to directory with images and custom styles") - flags.Bool("branding.disableExternal", false, "disable external links such as GitHub links") - flags.Bool("branding.disableUsedPercentage", false, "disable used disk percentage graph") -} - -//nolint:gocyclo -func getAuthentication(flags *pflag.FlagSet, defaults ...interface{}) (string, auth.Auther) { - method := mustGetString(flags, "auth.method") - - var defaultAuther map[string]interface{} - if len(defaults) > 0 { - if hasAuth := defaults[0]; hasAuth != true { - for _, arg := range defaults { - switch def := arg.(type) { - case *settings.Settings: - method = def.AuthMethod - case auth.Auther: - ms, err := json.Marshal(def) - checkErr(err) - err = json.Unmarshal(ms, &defaultAuther) - checkErr(err) - } - } - } - } - - var auther auth.Auther - if method == auth.MethodProxyAuth { - header := mustGetString(flags, "auth.header") - - if header == "" { - header = defaultAuther["header"].(string) - } - - if header == "" { - checkErr(nerrors.New("you must set the flag 'auth.header' for method 'proxy'")) - } - - auther = &auth.ProxyAuth{Header: header} - } - - if method == auth.MethodNoAuth { - auther = &auth.NoAuth{} - } - - if method == auth.MethodJSONAuth { - jsonAuth := &auth.JSONAuth{} - host := mustGetString(flags, "recaptcha.host") - key := mustGetString(flags, "recaptcha.key") - secret := mustGetString(flags, "recaptcha.secret") - - if key == "" { - if kmap, ok := defaultAuther["recaptcha"].(map[string]interface{}); ok { - key = kmap["key"].(string) - } - } - - if secret == "" { - if smap, ok := defaultAuther["recaptcha"].(map[string]interface{}); ok { - secret = smap["secret"].(string) - } - } - - if key != "" && secret != "" { - jsonAuth.ReCaptcha = &auth.ReCaptcha{ - Host: host, - Key: key, - Secret: secret, - } - } - auther = jsonAuth - } - - if method == auth.MethodHookAuth { - command := mustGetString(flags, "auth.command") - - if command == "" { - command = defaultAuther["command"].(string) - } - - if command == "" { - checkErr(nerrors.New("you must set the flag 'auth.command' for method 'hook'")) - } - - auther = &auth.HookAuth{Command: command} - } - - if auther == nil { - panic(errors.ErrInvalidAuthMethod) - } - - return method, auther -} - -func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Auther) { - w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) //nolint:gomnd - - fmt.Fprintf(w, "Sign up:\t%t\n", set.Signup) - fmt.Fprintf(w, "Create User Dir:\t%t\n", set.CreateUserDir) - fmt.Fprintf(w, "Auth method:\t%s\n", set.AuthMethod) - fmt.Fprintf(w, "Shell:\t%s\t\n", strings.Join(set.Shell, " ")) - fmt.Fprintln(w, "\nBranding:") - fmt.Fprintf(w, "\tName:\t%s\n", set.Branding.Name) - fmt.Fprintf(w, "\tFiles override:\t%s\n", set.Branding.Files) - fmt.Fprintf(w, "\tDisable external links:\t%t\n", set.Branding.DisableExternal) - 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.Fprintln(w, "\nServer:") - fmt.Fprintf(w, "\tLog:\t%s\n", ser.Log) - fmt.Fprintf(w, "\tPort:\t%s\n", ser.Port) - fmt.Fprintf(w, "\tBase URL:\t%s\n", ser.BaseURL) - fmt.Fprintf(w, "\tRoot:\t%s\n", ser.Root) - fmt.Fprintf(w, "\tSocket:\t%s\n", ser.Socket) - fmt.Fprintf(w, "\tAddress:\t%s\n", ser.Address) - fmt.Fprintf(w, "\tTLS Cert:\t%s\n", ser.TLSCert) - fmt.Fprintf(w, "\tTLS Key:\t%s\n", ser.TLSKey) - fmt.Fprintf(w, "\tExec Enabled:\t%t\n", ser.EnableExec) - fmt.Fprintln(w, "\nDefaults:") - fmt.Fprintf(w, "\tScope:\t%s\n", set.Defaults.Scope) - fmt.Fprintf(w, "\tLocale:\t%s\n", set.Defaults.Locale) - fmt.Fprintf(w, "\tView mode:\t%s\n", set.Defaults.ViewMode) - fmt.Fprintf(w, "\tSingle Click:\t%t\n", set.Defaults.SingleClick) - fmt.Fprintf(w, "\tCommands:\t%s\n", strings.Join(set.Defaults.Commands, " ")) - fmt.Fprintf(w, "\tSorting:\n") - fmt.Fprintf(w, "\t\tBy:\t%s\n", set.Defaults.Sorting.By) - fmt.Fprintf(w, "\t\tAsc:\t%t\n", set.Defaults.Sorting.Asc) - fmt.Fprintf(w, "\tPermissions:\n") - fmt.Fprintf(w, "\t\tAdmin:\t%t\n", set.Defaults.Perm.Admin) - fmt.Fprintf(w, "\t\tExecute:\t%t\n", set.Defaults.Perm.Execute) - fmt.Fprintf(w, "\t\tCreate:\t%t\n", set.Defaults.Perm.Create) - fmt.Fprintf(w, "\t\tRename:\t%t\n", set.Defaults.Perm.Rename) - fmt.Fprintf(w, "\t\tModify:\t%t\n", set.Defaults.Perm.Modify) - fmt.Fprintf(w, "\t\tDelete:\t%t\n", set.Defaults.Perm.Delete) - fmt.Fprintf(w, "\t\tShare:\t%t\n", set.Defaults.Perm.Share) - fmt.Fprintf(w, "\t\tDownload:\t%t\n", set.Defaults.Perm.Download) - w.Flush() - - b, err := json.MarshalIndent(auther, "", " ") - checkErr(err) - fmt.Printf("\nAuther configuration (raw):\n\n%s\n\n", string(b)) -} diff --git a/backend/cmd/config_cat.go b/backend/cmd/config_cat.go deleted file mode 100644 index eca56dd1..00000000 --- a/backend/cmd/config_cat.go +++ /dev/null @@ -1,25 +0,0 @@ -package cmd - -import ( - "github.com/spf13/cobra" -) - -func init() { - configCmd.AddCommand(configCatCmd) -} - -var configCatCmd = &cobra.Command{ - Use: "cat", - Short: "Prints the configuration", - Long: `Prints the configuration.`, - Args: cobra.NoArgs, - Run: python(func(cmd *cobra.Command, args []string, d pythonData) { - set, err := d.store.Settings.Get() - checkErr(err) - ser, err := d.store.Settings.GetServer() - checkErr(err) - auther, err := d.store.Auth.Get(set.AuthMethod) - checkErr(err) - printSettings(ser, set, auther) - }, pythonConfig{}), -} diff --git a/backend/cmd/config_export.go b/backend/cmd/config_export.go deleted file mode 100644 index 9d6450f9..00000000 --- a/backend/cmd/config_export.go +++ /dev/null @@ -1,37 +0,0 @@ -package cmd - -import ( - "github.com/spf13/cobra" -) - -func init() { - configCmd.AddCommand(configExportCmd) -} - -var configExportCmd = &cobra.Command{ - Use: "export ", - 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, - Run: python(func(cmd *cobra.Command, args []string, d pythonData) { - settings, err := d.store.Settings.Get() - checkErr(err) - - server, err := d.store.Settings.GetServer() - checkErr(err) - - auther, err := d.store.Auth.Get(settings.AuthMethod) - checkErr(err) - - data := &settingsFile{ - Settings: settings, - Auther: auther, - Server: server, - } - - err = marshal(args[0], data) - checkErr(err) - }, pythonConfig{}), -} diff --git a/backend/cmd/config_import.go b/backend/cmd/config_import.go deleted file mode 100644 index 98309612..00000000 --- a/backend/cmd/config_import.go +++ /dev/null @@ -1,94 +0,0 @@ -package cmd - -import ( - "encoding/json" - "errors" - "path/filepath" - "reflect" - - "github.com/spf13/cobra" - - "github.com/gtsteffaniak/filebrowser/auth" - "github.com/gtsteffaniak/filebrowser/settings" -) - -func init() { - configCmd.AddCommand(configImportCmd) -} - -type settingsFile struct { - Settings *settings.Settings `json:"settings"` - Server *settings.Server `json:"server"` - Auther interface{} `json:"auther"` -} - -var configImportCmd = &cobra.Command{ - Use: "import ", - 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. - -If used with a nonexisting database, a key will be generated -automatically. Otherwise the key will be kept the same as in the -database. - -The path must be for a json or yaml file.`, - Args: jsonYamlArg, - Run: python(func(cmd *cobra.Command, args []string, d pythonData) { - var key []byte - if d.hadDB { - settings, err := d.store.Settings.Get() - checkErr(err) - key = settings.Key - } else { - key = generateKey() - } - - file := settingsFile{} - err := unmarshal(args[0], &file) - checkErr(err) - - file.Settings.Key = key - err = d.store.Settings.Save(file.Settings) - checkErr(err) - - err = d.store.Settings.SaveServer(file.Server) - checkErr(err) - - var rawAuther interface{} - if filepath.Ext(args[0]) != ".json" { //nolint:goconst - rawAuther = cleanUpInterfaceMap(file.Auther.(map[interface{}]interface{})) - } else { - rawAuther = file.Auther - } - - var auther auth.Auther - switch file.Settings.AuthMethod { - case auth.MethodJSONAuth: - auther = getAuther(auth.JSONAuth{}, rawAuther).(*auth.JSONAuth) - case auth.MethodNoAuth: - auther = getAuther(auth.NoAuth{}, rawAuther).(*auth.NoAuth) - case auth.MethodProxyAuth: - auther = getAuther(auth.ProxyAuth{}, rawAuther).(*auth.ProxyAuth) - case auth.MethodHookAuth: - auther = getAuther(&auth.HookAuth{}, rawAuther).(*auth.HookAuth) - default: - checkErr(errors.New("invalid auth method")) - } - - err = d.store.Auth.Save(auther) - checkErr(err) - - printSettings(file.Server, file.Settings, auther) - }, pythonConfig{allowNoDB: true}), -} - -func getAuther(sample auth.Auther, data interface{}) interface{} { - authType := reflect.TypeOf(sample) - auther := reflect.New(authType).Interface() - bytes, err := json.Marshal(data) - checkErr(err) - err = json.Unmarshal(bytes, &auther) - checkErr(err) - return auther -} diff --git a/backend/cmd/config_init.go b/backend/cmd/config_init.go deleted file mode 100644 index 4f3b4050..00000000 --- a/backend/cmd/config_init.go +++ /dev/null @@ -1,69 +0,0 @@ -package cmd - -import ( - "fmt" - - "github.com/spf13/cobra" - - "github.com/gtsteffaniak/filebrowser/settings" -) - -func init() { - configCmd.AddCommand(configInitCmd) - addConfigFlags(configInitCmd.Flags()) -} - -var configInitCmd = &cobra.Command{ - Use: "init", - Short: "Initialize a new database", - Long: `Initialize a new database to use with File Browser. All of -this options can be changed in the future with the command -'filebrowser config set'. The user related flags apply -to the defaults when creating new users and you don't -override the options.`, - Args: cobra.NoArgs, - Run: python(func(cmd *cobra.Command, args []string, d pythonData) { - defaults := settings.UserDefaults{} - flags := cmd.Flags() - getUserDefaults(flags, &defaults, true) - authMethod, auther := getAuthentication(flags) - - s := &settings.Settings{ - Key: generateKey(), - Signup: mustGetBool(flags, "signup"), - Shell: convertCmdStrToCmdArray(mustGetString(flags, "shell")), - AuthMethod: authMethod, - Defaults: defaults, - Branding: settings.Branding{ - Name: mustGetString(flags, "branding.name"), - DisableExternal: mustGetBool(flags, "branding.disableExternal"), - DisableUsedPercentage: mustGetBool(flags, "branding.DisableUsedPercentage"), - Files: mustGetString(flags, "branding.files"), - }, - } - - ser := &settings.Server{ - Address: mustGetString(flags, "address"), - Socket: mustGetString(flags, "socket"), - Root: mustGetString(flags, "root"), - BaseURL: mustGetString(flags, "baseurl"), - TLSKey: mustGetString(flags, "key"), - TLSCert: mustGetString(flags, "cert"), - Port: mustGetString(flags, "port"), - Log: mustGetString(flags, "log"), - } - err := d.store.Settings.Save(s) - checkErr(err) - err = d.store.Settings.SaveServer(ser) - checkErr(err) - err = d.store.Auth.Save(auther) - checkErr(err) - - fmt.Printf(` -Congratulations! You've set up your database to use with File Browser. -Now add your first user via 'filebrowser users add' and then you just -need to call the main command to boot up the server. -`) - printSettings(ser, s, auther) - }, pythonConfig{noDB: true}), -} diff --git a/backend/cmd/config_set.go b/backend/cmd/config_set.go deleted file mode 100644 index 5a02288f..00000000 --- a/backend/cmd/config_set.go +++ /dev/null @@ -1,82 +0,0 @@ -package cmd - -import ( - "github.com/spf13/cobra" - "github.com/spf13/pflag" -) - -func init() { - configCmd.AddCommand(configSetCmd) - addConfigFlags(configSetCmd.Flags()) -} - -var configSetCmd = &cobra.Command{ - Use: "set", - Short: "Updates the configuration", - Long: `Updates the configuration. Set the flags for the options -you want to change. Other options will remain unchanged.`, - Args: cobra.NoArgs, - Run: python(func(cmd *cobra.Command, args []string, d pythonData) { - flags := cmd.Flags() - set, err := d.store.Settings.Get() - checkErr(err) - - ser, err := d.store.Settings.GetServer() - checkErr(err) - - hasAuth := false - flags.Visit(func(flag *pflag.Flag) { - switch flag.Name { - case "baseurl": - ser.BaseURL = mustGetString(flags, flag.Name) - case "root": - ser.Root = mustGetString(flags, flag.Name) - case "socket": - ser.Socket = mustGetString(flags, flag.Name) - case "cert": - ser.TLSCert = mustGetString(flags, flag.Name) - case "key": - ser.TLSKey = mustGetString(flags, flag.Name) - case "address": - ser.Address = mustGetString(flags, flag.Name) - case "port": - ser.Port = mustGetString(flags, flag.Name) - case "log": - ser.Log = mustGetString(flags, flag.Name) - case "signup": - set.Signup = mustGetBool(flags, flag.Name) - case "auth.method": - hasAuth = true - case "shell": - set.Shell = convertCmdStrToCmdArray(mustGetString(flags, flag.Name)) - case "branding.name": - set.Branding.Name = mustGetString(flags, flag.Name) - case "branding.color": - set.Branding.Color = mustGetString(flags, flag.Name) - case "branding.disableExternal": - set.Branding.DisableExternal = mustGetBool(flags, flag.Name) - case "branding.disableUsedPercentage": - set.Branding.DisableUsedPercentage = mustGetBool(flags, flag.Name) - case "branding.files": - set.Branding.Files = mustGetString(flags, flag.Name) - } - }) - - getUserDefaults(flags, &set.Defaults, false) - - // read the defaults - auther, err := d.store.Auth.Get(set.AuthMethod) - checkErr(err) - - // check if there are new flags for existing auth method - set.AuthMethod, auther = getAuthentication(flags, hasAuth, set, auther) - - err = d.store.Auth.Save(auther) - checkErr(err) - err = d.store.Settings.Save(set) - checkErr(err) - err = d.store.Settings.SaveServer(ser) - checkErr(err) - printSettings(ser, set, auther) - }, pythonConfig{}), -} diff --git a/backend/cmd/docs.go b/backend/cmd/docs.go deleted file mode 100644 index 9ebb9573..00000000 --- a/backend/cmd/docs.go +++ /dev/null @@ -1,139 +0,0 @@ -package cmd - -import ( - "bytes" - "fmt" - "io" - "os" - "path/filepath" - "sort" - "strings" - - "github.com/spf13/cobra" - "github.com/spf13/pflag" -) - -func init() { - rootCmd.AddCommand(docsCmd) - docsCmd.Flags().StringP("path", "p", "./docs", "path to save the docs") -} - -func printToc(names []string) { - for i, name := range names { - name = strings.TrimSuffix(name, filepath.Ext(name)) - name = strings.Replace(name, "-", " ", -1) - names[i] = name - } - - sort.Strings(names) - - toc := "" - for _, name := range names { - toc += "* [" + name + "](cli/" + strings.Replace(name, " ", "-", -1) + ".md)\n" - } - - fmt.Println(toc) -} - -var docsCmd = &cobra.Command{ - Use: "docs", - Hidden: true, - Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - dir := mustGetString(cmd.Flags(), "path") - generateDocs(rootCmd, dir) - names := []string{} - - err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - if err != nil || info.IsDir() { - return err - } - - if !strings.HasPrefix(info.Name(), "filebrowser") { - return nil - } - - names = append(names, info.Name()) - return nil - }) - - checkErr(err) - printToc(names) - }, -} - -func generateDocs(cmd *cobra.Command, dir string) { - for _, c := range cmd.Commands() { - if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() { - continue - } - - generateDocs(c, dir) - } - - basename := strings.Replace(cmd.CommandPath(), " ", "-", -1) + ".md" - filename := filepath.Join(dir, basename) - f, err := os.Create(filename) - checkErr(err) - defer f.Close() - generateMarkdown(cmd, f) -} - -func generateMarkdown(cmd *cobra.Command, w io.Writer) { - cmd.InitDefaultHelpCmd() - cmd.InitDefaultHelpFlag() - - buf := new(bytes.Buffer) - name := cmd.CommandPath() - - short := cmd.Short - long := cmd.Long - if long == "" { - long = short - } - - buf.WriteString("---\ndescription: " + short + "\n---\n\n") - buf.WriteString("# " + name + "\n\n") - buf.WriteString("## Synopsis\n\n") - buf.WriteString(long + "\n\n") - - if cmd.Runnable() { - buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", cmd.UseLine())) - } - - if len(cmd.Example) > 0 { - buf.WriteString("## Examples\n\n") - buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", cmd.Example)) - } - - printOptions(buf, cmd) - _, err := buf.WriteTo(w) - checkErr(err) -} - -func generateFlagsTable(fs *pflag.FlagSet, buf io.StringWriter) { - _, _ = buf.WriteString("| Name | Shorthand | Usage |\n") - _, _ = buf.WriteString("|------|-----------|-------|\n") - - fs.VisitAll(func(f *pflag.Flag) { - _, _ = buf.WriteString("|" + f.Name + "|" + f.Shorthand + "|" + f.Usage + "|\n") - }) -} - -func printOptions(buf *bytes.Buffer, cmd *cobra.Command) { - flags := cmd.NonInheritedFlags() - flags.SetOutput(buf) - if flags.HasAvailableFlags() { - buf.WriteString("## Options\n\n") - generateFlagsTable(flags, buf) - buf.WriteString("\n") - } - - parentFlags := cmd.InheritedFlags() - parentFlags.SetOutput(buf) - if parentFlags.HasAvailableFlags() { - buf.WriteString("### Inherited\n\n") - generateFlagsTable(parentFlags, buf) - buf.WriteString("\n") - } -} diff --git a/backend/cmd/root.go b/backend/cmd/root.go index 9191df22..483affb0 100644 --- a/backend/cmd/root.go +++ b/backend/cmd/root.go @@ -2,7 +2,7 @@ package cmd import ( "crypto/tls" - "errors" + "flag" "io" "io/fs" "log" @@ -10,15 +10,13 @@ import ( "net/http" "os" "os/signal" - "path/filepath" "strconv" - "strings" "syscall" + "github.com/spf13/pflag" + "github.com/spf13/afero" "github.com/spf13/cobra" - "github.com/spf13/pflag" - v "github.com/spf13/viper" lumberjack "gopkg.in/natefinch/lumberjack.v2" "github.com/gtsteffaniak/filebrowser/auth" @@ -32,7 +30,7 @@ import ( ) var ( - cfgFile string + configFile string ) type dirFS struct { @@ -44,106 +42,28 @@ func (d dirFS) Open(name string) (fs.File, error) { } func init() { - cobra.OnInitialize(initConfig) - cobra.MousetrapHelpText = "" - rootCmd.SetVersionTemplate("File Browser version {{printf \"%s\" .Version}}\n") - - flags := rootCmd.Flags() - - persistent := rootCmd.PersistentFlags() - - persistent.StringVarP(&cfgFile, "config", "c", "", "config file 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("password", "", "hashed password for the first user when using quick config (default \"admin\")") - - addServerFlags(flags) -} - -func getEnvVariableAsUint32(key string) uint32 { - valueStr := os.Getenv(key) - value, err := strconv.ParseUint(valueStr, 10, 32) - if err != nil { - return 5 // default value every 5 minutes - } - return uint32(value) -} - -func addServerFlags(flags *pflag.FlagSet) { - flags.StringP("address", "a", "127.0.0.1", "address to listen on") - flags.StringP("log", "l", "stdout", "log output") - flags.StringP("port", "p", "8080", "port to listen on") - flags.StringP("cert", "t", "", "tls certificate") - flags.StringP("key", "k", "", "tls key") - flags.StringP("root", "r", ".", "root to prepend to relative paths") - flags.String("socket", "", "socket to listen to (cannot be used with address, port, cert nor key flags)") - flags.Uint32("socket-perm", 0666, "unix socket file permissions") //nolint:gomnd - flags.StringP("baseurl", "b", "", "base url") - flags.String("cache-dir", "", "file cache directory (disabled if empty)") - flags.Int("img-processors", 4, "image processors count") //nolint:gomnd - flags.Bool("disable-thumbnails", false, "disable image thumbnails") - flags.Bool("disable-preview-resize", true, "disable resize of image previews") - flags.Bool("disable-exec", false, "disables Command Runner feature") - flags.Bool("disable-type-detection-by-header", false, "disables type detection by reading file headers") + // Define a flag for the config option (-c or --config) + configFlag := pflag.StringP("config", "c", "filebrowser.yaml", "Path to the config file") + // Bind the flags to the pflag command line parser + pflag.CommandLine.AddGoFlagSet(flag.CommandLine) + pflag.Parse() + log.Println("Initializing with config file:", *configFlag) + settings.Initialize(*configFlag) } var rootCmd = &cobra.Command{ - Use: "filebrowser", - Short: "A stylish web-based 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 -web interface. - -If you've never run File Browser, you'll need to have a database for -it. Don't worry: you don't need to setup a separate database server. -We're using Bolt DB which is a single file database and all managed -by ourselves. - -For this specific command, all the flags you have available (except -"config" for the configuration file), can be given either through -environment variables or configuration files. - -If you don't set "config", it will look for a configuration file called -.filebrowser.{json, toml, yaml, yml} in the following directories: - -- ./ -- $HOME/ -- /etc/filebrowser/ - -The precedence of the configuration values are as follows: - -- flags -- environment variables -- configuration file -- database values -- defaults - -The environment variables are prefixed by "FB_" followed by the option -name in caps. So to set "database" via an env variable, you should -set FB_DATABASE. - -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".`, + Use: "filebrowser", Run: python(func(cmd *cobra.Command, args []string, d pythonData) { - log.Println(cfgFile) - + serverConfig := settings.GlobalConfiguration.Server if !d.hadDB { - quickSetup(cmd.Flags(), d) + quickSetup(d) } - - // build img service - workersCount, err := cmd.Flags().GetInt("img-processors") - checkErr(err) - if workersCount < 1 { + if serverConfig.NumImageProcessors < 1 { log.Fatal("Image resize workers count could not be < 1") } - imgSvc := img.New(workersCount) - + imgSvc := img.New(serverConfig.NumImageProcessors) var fileCache diskcache.Interface = diskcache.NewNoOp() - cacheDir, err := cmd.Flags().GetString("cache-dir") - checkErr(err) + cacheDir := "/tmp" if cacheDir != "" { if err := os.MkdirAll(cacheDir, 0700); err != nil { //nolint:govet,gomnd log.Fatalf("can't make directory %s: %s", cacheDir, err) @@ -151,51 +71,38 @@ user created with the credentials from options "username" and "password".`, fileCache = diskcache.New(afero.NewOsFs(), cacheDir) } // initialize indexing and schedule indexing ever n minutes (default 5) - indexingInterval := getEnvVariableAsUint32("INDEXING_INTERVAL") - go search.InitializeIndex(indexingInterval) - - server := getRunParams(cmd.Flags(), d.store) - setupLog(server.Log) - - root, err := filepath.Abs(server.Root) + go search.InitializeIndex(serverConfig.IndexingInterval) + _, err := os.Stat(serverConfig.Root) checkErr(err) - server.Root = root - - adr := server.Address + ":" + server.Port - var listener net.Listener - + address := serverConfig.Address + ":" + strconv.Itoa(serverConfig.Port) switch { - case server.Socket != "": - listener, err = net.Listen("unix", server.Socket) + case serverConfig.Socket != "": + listener, err = net.Listen("unix", serverConfig.Socket) checkErr(err) socketPerm, err := cmd.Flags().GetUint32("socket-perm") //nolint:govet checkErr(err) - err = os.Chmod(server.Socket, os.FileMode(socketPerm)) + err = os.Chmod(serverConfig.Socket, os.FileMode(socketPerm)) checkErr(err) - case server.TLSKey != "" && server.TLSCert != "": - cer, err := tls.LoadX509KeyPair(server.TLSCert, server.TLSKey) //nolint:govet + case serverConfig.TLSKey != "" && serverConfig.TLSCert != "": + cer, err := tls.LoadX509KeyPair(serverConfig.TLSCert, serverConfig.TLSKey) //nolint:govet checkErr(err) - listener, err = tls.Listen("tcp", adr, &tls.Config{ + listener, err = tls.Listen("tcp", address, &tls.Config{ MinVersion: tls.VersionTLS12, Certificates: []tls.Certificate{cer}}, ) checkErr(err) default: - listener, err = net.Listen("tcp", adr) + listener, err = net.Listen("tcp", address) checkErr(err) } - sigc := make(chan os.Signal, 1) signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) go cleanupHandler(listener, sigc) - assetsFs := dirFS{Dir: http.Dir("frontend/dist")} - handler, err := fbhttp.NewHandler(imgSvc, fileCache, d.store, server, assetsFs) + handler, err := fbhttp.NewHandler(imgSvc, fileCache, d.store, &serverConfig, assetsFs) checkErr(err) - defer listener.Close() - log.Println("Listening on", listener.Addr().String()) //nolint: gosec if err := http.Serve(listener, handler); err != nil { @@ -204,6 +111,12 @@ user created with the credentials from options "username" and "password".`, }, pythonConfig{allowNoDB: true}), } +func StartFilebrowser() { + if err := rootCmd.Execute(); err != nil { + log.Fatal(err) + } +} + func cleanupHandler(listener net.Listener, c chan os.Signal) { //nolint:interfacer sig := <-c log.Printf("Caught signal %s: shutting down.", sig) @@ -212,103 +125,12 @@ func cleanupHandler(listener net.Listener, c chan os.Signal) { //nolint:interfac } //nolint:gocyclo -func getRunParams(flags *pflag.FlagSet, st *storage.Storage) *settings.Server { +func getRunParams(st *storage.Storage) *settings.Server { server, err := st.Settings.GetServer() checkErr(err) - - if val, set := getParamB(flags, "root"); set { - server.Root = val - } - - if val, set := getParamB(flags, "baseurl"); set { - server.BaseURL = val - } - - if val, set := getParamB(flags, "log"); set { - server.Log = val - } - - isSocketSet := false - isAddrSet := false - - if val, set := getParamB(flags, "address"); set { - server.Address = val - isAddrSet = isAddrSet || set - } - - if val, set := getParamB(flags, "port"); set { - server.Port = val - isAddrSet = isAddrSet || set - } - - if val, set := getParamB(flags, "key"); set { - server.TLSKey = val - isAddrSet = isAddrSet || set - } - - if val, set := getParamB(flags, "cert"); set { - server.TLSCert = val - isAddrSet = isAddrSet || set - } - - if val, set := getParamB(flags, "socket"); set { - server.Socket = val - isSocketSet = isSocketSet || set - } - - if isAddrSet && isSocketSet { - checkErr(errors.New("--socket flag cannot be used with --address, --port, --key nor --cert")) - } - - // Do not use saved Socket if address was manually set. - if isAddrSet && server.Socket != "" { - server.Socket = "" - } - - _, disableThumbnails := getParamB(flags, "disable-thumbnails") - server.EnableThumbnails = !disableThumbnails - - _, disablePreviewResize := getParamB(flags, "disable-preview-resize") - server.ResizePreview = !disablePreviewResize - - _, disableTypeDetectionByHeader := getParamB(flags, "disable-type-detection-by-header") - server.TypeDetectionByHeader = !disableTypeDetectionByHeader - - _, disableExec := getParamB(flags, "disable-exec") - server.EnableExec = !disableExec - 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) { switch logMethod { case "stdout": @@ -327,101 +149,31 @@ func setupLog(logMethod string) { } } -func quickSetup(flags *pflag.FlagSet, d pythonData) { - set := &settings.Settings{ - Key: generateKey(), - Signup: false, - CreateUserDir: false, - UserHomeBasePath: settings.DefaultUsersHomeBasePath, - Defaults: settings.UserDefaults{ - Scope: ".", - Locale: "en", - SingleClick: false, - Perm: users.Permissions{ - Admin: false, - Execute: true, - Create: true, - Rename: true, - Modify: true, - Delete: true, - Share: true, - Download: true, - }, - }, - AuthMethod: "", - Branding: settings.Branding{}, - Commands: nil, - Shell: nil, - Rules: nil, - } - +func quickSetup(d pythonData) { + settings.GlobalConfiguration.Key = generateKey() var err error - if _, noauth := getParamB(flags, "noauth"); noauth { - set.AuthMethod = auth.MethodNoAuth + if settings.GlobalConfiguration.Auth.Method == "noauth" { err = d.store.Auth.Save(&auth.NoAuth{}) } else { - set.AuthMethod = auth.MethodJSONAuth + settings.GlobalConfiguration.Auth.Method = "password" err = d.store.Auth.Save(&auth.JSONAuth{}) } - - err = d.store.Settings.Save(set) + err = d.store.Settings.Save(&settings.GlobalConfiguration) 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(&settings.GlobalConfiguration.Server) checkErr(err) - - username := getParam(flags, "username") - password := getParam(flags, "password") - - if password == "" { - password, err = users.HashPwd("admin") - checkErr(err) - } - + username := settings.GlobalConfiguration.AdminUsername + password := settings.GlobalConfiguration.AdminPassword if username == "" || password == "" { log.Fatal("username and password cannot be empty during quick setup") } - user := &users.User{ Username: username, Password: password, LockPassword: false, } - - set.Defaults.Apply(user) + settings.GlobalConfiguration.UserDefaults.Apply(user) user.Perm.Admin = true - err = d.store.Users.Save(user) checkErr(err) } - -func initConfig() { - if cfgFile == "" { - v.AddConfigPath(".") - v.AddConfigPath("/etc/filebrowser/") - v.SetConfigName(".filebrowser") - } else { - v.SetConfigFile(cfgFile) - } - - v.SetEnvPrefix("FB") - v.AutomaticEnv() - v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - if err := v.ReadInConfig(); err != nil { - if _, ok := err.(v.ConfigParseError); ok { - panic(err) - } - cfgFile = "No config file used" - } else { - cfgFile = "Using config file: " + v.ConfigFileUsed() - } -} diff --git a/backend/cmd/upgrade.go b/backend/cmd/upgrade.go deleted file mode 100644 index be9769c2..00000000 --- a/backend/cmd/upgrade.go +++ /dev/null @@ -1,31 +0,0 @@ -package cmd - -import ( - "github.com/spf13/cobra" - - "github.com/gtsteffaniak/filebrowser/storage/bolt/importer" -) - -func init() { - rootCmd.AddCommand(upgradeCmd) - - upgradeCmd.Flags().String("old.database", "", "") - upgradeCmd.Flags().String("old.config", "", "") - _ = upgradeCmd.MarkFlagRequired("old.database") -} - -var upgradeCmd = &cobra.Command{ - Use: "upgrade", - Short: "Upgrades an old configuration", - Long: `Upgrades an old configuration. This command DOES NOT -import share links because they are incompatible with -this version.`, - Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - flags := cmd.Flags() - oldDB := mustGetString(flags, "old.database") - oldConf := mustGetString(flags, "old.config") - err := importer.Import(oldDB, oldConf, getParam(flags, "database")) - checkErr(err) - }, -} diff --git a/backend/cmd/users.go b/backend/cmd/users.go index 3c835a18..1f50cf74 100644 --- a/backend/cmd/users.go +++ b/backend/cmd/users.go @@ -79,8 +79,8 @@ func addUserFlags(flags *pflag.FlagSet) { flags.Bool("singleClick", false, "use single clicks only") } -func getViewMode(flags *pflag.FlagSet) users.ViewMode { - viewMode := users.ViewMode(mustGetString(flags, "viewMode")) +func getViewMode(flags *pflag.FlagSet) string { + viewMode := settings.GlobalConfiguration.UserDefaults.ViewMode if viewMode != users.ListViewMode && viewMode != users.MosaicViewMode { checkErr(errors.New("view mode must be \"" + string(users.ListViewMode) + "\" or \"" + string(users.MosaicViewMode) + "\"")) } diff --git a/backend/cmd/users_add.go b/backend/cmd/users_add.go index f133a464..0ba430eb 100644 --- a/backend/cmd/users_add.go +++ b/backend/cmd/users_add.go @@ -19,7 +19,6 @@ var usersAddCmd = &cobra.Command{ Run: python(func(cmd *cobra.Command, args []string, d pythonData) { s, err := d.store.Settings.Get() checkErr(err) - getUserDefaults(cmd.Flags(), &s.Defaults, false) password, err := users.HashPwd(args[1]) checkErr(err) @@ -30,7 +29,7 @@ var usersAddCmd = &cobra.Command{ LockPassword: mustGetBool(cmd.Flags(), "lockPassword"), } - s.Defaults.Apply(user) + s.UserDefaults.Apply(user) servSettings, err := d.store.Settings.GetServer() checkErr(err) diff --git a/backend/cmd/users_update.go b/backend/cmd/users_update.go index 09bb4b8d..707077e5 100644 --- a/backend/cmd/users_update.go +++ b/backend/cmd/users_update.go @@ -3,7 +3,6 @@ package cmd import ( "github.com/spf13/cobra" - "github.com/gtsteffaniak/filebrowser/settings" "github.com/gtsteffaniak/filebrowser/users" ) @@ -37,27 +36,15 @@ options you want to change.`, } else { user, err = d.store.Users.Get("", username) } - checkErr(err) - - defaults := settings.UserDefaults{ - Scope: user.Scope, - Locale: user.Locale, - ViewMode: user.ViewMode, - SingleClick: user.SingleClick, - Perm: user.Perm, - Sorting: user.Sorting, - Commands: user.Commands, - } - getUserDefaults(flags, &defaults, false) - user.Scope = defaults.Scope - user.Locale = defaults.Locale - user.ViewMode = defaults.ViewMode - user.SingleClick = defaults.SingleClick - user.Perm = defaults.Perm - user.Commands = defaults.Commands - user.Sorting = defaults.Sorting - user.LockPassword = mustGetBool(flags, "lockPassword") + user.Scope = user.Scope + user.Locale = user.Locale + user.ViewMode = user.ViewMode + user.SingleClick = user.SingleClick + user.Perm = user.Perm + user.Commands = user.Commands + user.Sorting = user.Sorting + user.LockPassword = user.LockPassword if newUsername != "" { user.Username = newUsername diff --git a/backend/cmd/utils.go b/backend/cmd/utils.go index ef0b9a7c..bb9eea37 100644 --- a/backend/cmd/utils.go +++ b/backend/cmd/utils.go @@ -10,9 +10,9 @@ import ( "strings" "github.com/asdine/storm/v3" + "github.com/goccy/go-yaml" "github.com/spf13/cobra" "github.com/spf13/pflag" - yaml "gopkg.in/yaml.v2" "github.com/gtsteffaniak/filebrowser/settings" "github.com/gtsteffaniak/filebrowser/storage" @@ -85,8 +85,7 @@ func dbExists(path string) (bool, error) { func python(fn pythonFunc, cfg pythonConfig) cobraFunc { return func(cmd *cobra.Command, args []string) { data := pythonData{hadDB: true} - - path := getParam(cmd.Flags(), "database") + path := settings.GlobalConfiguration.Server.Database exists, err := dbExists(path) if err != nil { @@ -100,6 +99,7 @@ func python(fn pythonFunc, cfg pythonConfig) cobraFunc { data.hadDB = exists db, err := storm.Open(path) checkErr(err) + defer db.Close() data.store, err = bolt.NewStorage(db) checkErr(err) @@ -118,8 +118,8 @@ func marshal(filename string, data interface{}) error { encoder.SetIndent("", " ") return encoder.Encode(data) case ".yml", ".yaml": //nolint:goconst - encoder := yaml.NewEncoder(fd) - return encoder.Encode(data) + _, err := yaml.Marshal(fd) + return err default: return errors.New("invalid format: " + ext) } diff --git a/backend/filebrowser.yaml b/backend/filebrowser.yaml new file mode 100644 index 00000000..173bb397 --- /dev/null +++ b/backend/filebrowser.yaml @@ -0,0 +1,20 @@ +server: + port: 8080 + baseURL: "/" +auth: + method: noauth + signup: true +userDefaults: + scope: "." + hideDotfiles: true + singleClick: false + permissions: + admin: false + create: true + rename: true + modify: true + delete: true + share: true + download: true +frontend: + theme: dark \ No newline at end of file diff --git a/backend/go.mod b/backend/go.mod index 8ad814af..107622c5 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -1,32 +1,29 @@ module github.com/gtsteffaniak/filebrowser -go 1.20 +go 1.21.0 require ( github.com/asdine/storm/v3 v3.2.1 github.com/disintegration/imaging v1.6.2 github.com/dsoprea/go-exif/v3 v3.0.1 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 + github.com/goccy/go-yaml v1.11.0 github.com/golang-jwt/jwt/v4 v4.5.0 + github.com/google/go-cmp v0.5.9 github.com/gorilla/mux v1.8.0 - github.com/gorilla/websocket v1.5.0 github.com/maruel/natural v1.1.0 github.com/marusama/semaphore/v2 v2.5.0 github.com/mholt/archiver/v3 v3.5.1 - github.com/pelletier/go-toml/v2 v2.0.9 - github.com/shirou/gopsutil/v3 v3.23.7 + github.com/shirou/gopsutil/v3 v3.23.8 github.com/spf13/afero v1.9.5 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.16.0 github.com/stretchr/testify v1.8.4 github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce - go.etcd.io/bbolt v1.3.7 golang.org/x/crypto v0.12.0 - golang.org/x/image v0.11.0 - golang.org/x/text v0.12.0 + golang.org/x/image v0.12.0 + golang.org/x/text v0.13.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 - gopkg.in/yaml.v2 v2.4.0 ) require ( @@ -35,29 +32,27 @@ require ( github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/fatih/color v1.10.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect github.com/golang/snappy v0.0.2 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.11.4 // indirect github.com/klauspost/pgzip v1.2.5 // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect github.com/nwaples/rardecode v1.1.0 // indirect github.com/pierrec/lz4/v4 v4.1.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/spf13/cast v1.5.1 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/subosito/gotenv v1.4.2 // indirect github.com/ulikunitz/xz v0.5.9 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.etcd.io/bbolt v1.3.4 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.11.0 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/backend/go.sum b/backend/go.sum index 2c3368f4..525f9e3f 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -86,11 +86,10 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= +github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= @@ -101,6 +100,14 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54= +github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= @@ -131,8 +138,8 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -171,12 +178,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -193,26 +196,26 @@ github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/maruel/natural v1.1.0 h1:2z1NgP/Vae+gYrtC0VuvrTJ6U35OuyUqDdfluLqMWuQ= github.com/maruel/natural v1.1.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ= github.com/marusama/semaphore/v2 v2.5.0 h1:o/1QJD9DBYOWRnDhPwDVAXQn6mQYD0gZaS1Tpx6DJGM= github.com/marusama/semaphore/v2 v2.5.0/go.mod h1:z9nMiNUekt/LTpTUQdpp+4sJeYqUGpwMHfW0Z8V8fnQ= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= -github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= -github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -223,24 +226,17 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4= -github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4= +github.com/shirou/gopsutil/v3 v3.23.8 h1:xnATPiybo6GgdRoC4YoGnxXZFRc3dqQTGi73oLvvBrE= +github.com/shirou/gopsutil/v3 v3.23.8/go.mod h1:7hmCaBn+2ZwaZOr6jmPBZDfawwMGuo1id3C6aM8EDqQ= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= -github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -252,10 +248,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= -github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= -github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= @@ -272,9 +266,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= -go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -304,8 +297,8 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMk golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo= -golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= +golang.org/x/image v0.12.0 h1:w13vZbU4o5rKOFFR8y7M+c4A5jXDC0uXTdHYRP8X2DQ= +golang.org/x/image v0.12.0/go.mod h1:Lu90jvHG7GfemOIcldsh9A2hS01ocl6oNO7ype5mEnk= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -403,6 +396,7 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -430,11 +424,9 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -449,8 +441,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -506,6 +498,7 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -595,15 +588,13 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/backend/http/auth.go b/backend/http/auth.go index e5cb2a7e..88146217 100644 --- a/backend/http/auth.go +++ b/backend/http/auth.go @@ -22,7 +22,7 @@ const ( type userInfo struct { ID uint `json:"id"` Locale string `json:"locale"` - ViewMode users.ViewMode `json:"viewMode"` + ViewMode string `json:"viewMode"` SingleClick bool `json:"singleClick"` Perm users.Permissions `json:"perm"` Commands []string `json:"commands"` @@ -102,12 +102,12 @@ func withAdmin(fn handleFunc) handleFunc { } var loginHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { - auther, err := d.store.Auth.Get(d.settings.AuthMethod) + auther, err := d.store.Auth.Get(d.settings.Auth.Method) if err != nil { return http.StatusInternalServerError, err } - user, err := auther.Auth(r, d.store.Users, d.settings, d.server) + user, err := auther.Auth(r, d.store.Users) if err == os.ErrPermission { return http.StatusForbidden, nil } else if err != nil { @@ -145,7 +145,7 @@ var signupHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, Username: info.Username, } - d.settings.Defaults.Apply(user) + d.settings.UserDefaults.Apply(user) pwd, err := users.HashPwd(info.Password) if err != nil { diff --git a/backend/http/commands.go b/backend/http/commands.go deleted file mode 100644 index cf886541..00000000 --- a/backend/http/commands.go +++ /dev/null @@ -1,111 +0,0 @@ -package http - -import ( - "bufio" - "io" - "log" - "net/http" - "os/exec" - "strings" - "time" - - "github.com/gorilla/websocket" - - "github.com/gtsteffaniak/filebrowser/runner" -) - -const ( - WSWriteDeadline = 10 * time.Second -) - -var upgrader = websocket.Upgrader{ - ReadBufferSize: 1024, - WriteBufferSize: 1024, -} - -var ( - cmdNotAllowed = []byte("Command not allowed.") -) - -//nolint:unparam -func wsErr(ws *websocket.Conn, r *http.Request, status int, err error) { - txt := http.StatusText(status) - if err != nil || status >= 400 { - log.Printf("%s: %v %s %v", r.URL.Path, status, r.RemoteAddr, err) - } - if err := ws.WriteControl(websocket.CloseInternalServerErr, []byte(txt), time.Now().Add(WSWriteDeadline)); err != nil { - log.Print(err) - } -} - -var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { - conn, err := upgrader.Upgrade(w, r, nil) - if err != nil { - return http.StatusInternalServerError, err - } - defer conn.Close() - - var raw string - - for { - _, msg, err := conn.ReadMessage() //nolint:govet - if err != nil { - wsErr(conn, r, http.StatusInternalServerError, err) - return 0, nil - } - - raw = strings.TrimSpace(string(msg)) - if raw != "" { - break - } - } - - command, err := runner.ParseCommand(d.settings, raw) - if err != nil { - if err := conn.WriteMessage(websocket.TextMessage, []byte(err.Error())); err != nil { //nolint:govet - wsErr(conn, r, http.StatusInternalServerError, err) - } - return 0, nil - } - - if !d.server.EnableExec || !d.user.CanExecute(command[0]) { - if err := conn.WriteMessage(websocket.TextMessage, cmdNotAllowed); err != nil { //nolint:govet - wsErr(conn, r, http.StatusInternalServerError, err) - } - - return 0, nil - } - - cmd := exec.Command(command[0], command[1:]...) //nolint:gosec - cmd.Dir = d.user.FullPath(r.URL.Path) - - stdout, err := cmd.StdoutPipe() - if err != nil { - wsErr(conn, r, http.StatusInternalServerError, err) - return 0, nil - } - - stderr, err := cmd.StderrPipe() - if err != nil { - wsErr(conn, r, http.StatusInternalServerError, err) - return 0, nil - } - - if err := cmd.Start(); err != nil { - wsErr(conn, r, http.StatusInternalServerError, err) - return 0, nil - } - - s := bufio.NewScanner(io.MultiReader(stdout, stderr)) - for s.Scan() { - if err := conn.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil { - log.Print(err) - } - } - - if err := cmd.Wait(); err != nil { - wsErr(conn, r, http.StatusInternalServerError, err) - } - - return 0, nil -}) diff --git a/backend/http/http.go b/backend/http/http.go index 2ef52c10..f12470fd 100644 --- a/backend/http/http.go +++ b/backend/http/http.go @@ -32,58 +32,44 @@ func NewHandler( }) }) index, static := getStaticHandlers(store, server, assetsFs) - // NOTE: This fixes the issue where it would redirect if people did not put a // trailing slash in the end. I hate this decision since this allows some awful // URLs https://www.gorillatoolkit.org/pkg/mux#Router.SkipClean r = r.SkipClean(true) - monkey := func(fn handleFunc, prefix string) http.Handler { return handle(fn, prefix, store, server) } - r.HandleFunc("/health", healthHandler) r.PathPrefix("/static").Handler(static) r.NotFoundHandler = index - api := r.PathPrefix("/api").Subrouter() - api.Handle("/login", monkey(loginHandler, "")) api.Handle("/signup", monkey(signupHandler, "")) api.Handle("/renew", monkey(renewHandler, "")) - users := api.PathPrefix("/users").Subrouter() users.Handle("", monkey(usersGetHandler, "")).Methods("GET") users.Handle("", monkey(userPostHandler, "")).Methods("POST") users.Handle("/{id:[0-9]+}", monkey(userPutHandler, "")).Methods("PUT") users.Handle("/{id:[0-9]+}", monkey(userGetHandler, "")).Methods("GET") users.Handle("/{id:[0-9]+}", monkey(userDeleteHandler, "")).Methods("DELETE") - api.PathPrefix("/resources").Handler(monkey(resourceGetHandler, "/api/resources")).Methods("GET") api.PathPrefix("/resources").Handler(monkey(resourceDeleteHandler(fileCache), "/api/resources")).Methods("DELETE") api.PathPrefix("/resources").Handler(monkey(resourcePostHandler(fileCache), "/api/resources")).Methods("POST") api.PathPrefix("/resources").Handler(monkey(resourcePutHandler, "/api/resources")).Methods("PUT") api.PathPrefix("/resources").Handler(monkey(resourcePatchHandler(fileCache), "/api/resources")).Methods("PATCH") - api.PathPrefix("/usage").Handler(monkey(diskUsage, "/api/usage")).Methods("GET") - api.Path("/shares").Handler(monkey(shareListHandler, "/api/shares")).Methods("GET") api.PathPrefix("/share").Handler(monkey(shareGetsHandler, "/api/share")).Methods("GET") api.PathPrefix("/share").Handler(monkey(sharePostHandler, "/api/share")).Methods("POST") api.PathPrefix("/share").Handler(monkey(shareDeleteHandler, "/api/share")).Methods("DELETE") - api.Handle("/settings", monkey(settingsGetHandler, "")).Methods("GET") api.Handle("/settings", monkey(settingsPutHandler, "")).Methods("PUT") - api.PathPrefix("/raw").Handler(monkey(rawHandler, "/api/raw")).Methods("GET") api.PathPrefix("/preview/{size}/{path:.*}"). Handler(monkey(previewHandler(imgSvc, fileCache, server.EnableThumbnails, server.ResizePreview), "/api/preview")).Methods("GET") - api.PathPrefix("/command").Handler(monkey(commandsHandler, "/api/command")).Methods("GET") api.PathPrefix("/search").Handler(monkey(searchHandler, "/api/search")).Methods("GET") - public := api.PathPrefix("/public").Subrouter() public.PathPrefix("/dl").Handler(monkey(publicDlHandler, "/api/public/dl/")).Methods("GET") public.PathPrefix("/share").Handler(monkey(publicShareHandler, "/api/public/share/")).Methods("GET") - return stripPrefix(server.BaseURL, r), nil } diff --git a/backend/http/settings.go b/backend/http/settings.go index c412148b..9591df5d 100644 --- a/backend/http/settings.go +++ b/backend/http/settings.go @@ -14,7 +14,7 @@ type settingsData struct { UserHomeBasePath string `json:"userHomeBasePath"` Defaults settings.UserDefaults `json:"defaults"` Rules []rules.Rule `json:"rules"` - Branding settings.Branding `json:"branding"` + Frontend settings.Frontend `json:"frontend"` Shell []string `json:"shell"` Commands map[string][]string `json:"commands"` } @@ -24,9 +24,9 @@ var settingsGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, Signup: d.settings.Signup, CreateUserDir: d.settings.CreateUserDir, UserHomeBasePath: d.settings.UserHomeBasePath, - Defaults: d.settings.Defaults, + Defaults: d.settings.UserDefaults, Rules: d.settings.Rules, - Branding: d.settings.Branding, + Frontend: d.settings.Frontend, Shell: d.settings.Shell, Commands: d.settings.Commands, } @@ -44,12 +44,11 @@ var settingsPutHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d.settings.Signup = req.Signup d.settings.CreateUserDir = req.CreateUserDir d.settings.UserHomeBasePath = req.UserHomeBasePath - d.settings.Defaults = req.Defaults + d.settings.UserDefaults = req.Defaults d.settings.Rules = req.Rules - d.settings.Branding = req.Branding + d.settings.Frontend = req.Frontend d.settings.Shell = req.Shell d.settings.Commands = req.Commands - err = d.store.Settings.Save(d.settings) return errToStatus(err), err }) diff --git a/backend/http/static.go b/backend/http/static.go index 500bef8b..b0e1685e 100644 --- a/backend/http/static.go +++ b/backend/http/static.go @@ -21,33 +21,33 @@ import ( func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys fs.FS, file, contentType string) (int, error) { w.Header().Set("Content-Type", contentType) - auther, err := d.store.Auth.Get(d.settings.AuthMethod) + auther, err := d.store.Auth.Get(d.settings.Auth.Method) if err != nil { return http.StatusInternalServerError, err } data := map[string]interface{}{ - "Name": d.settings.Branding.Name, - "DisableExternal": d.settings.Branding.DisableExternal, - "DisableUsedPercentage": d.settings.Branding.DisableUsedPercentage, - "Color": d.settings.Branding.Color, + "Name": d.settings.Frontend.Name, + "DisableExternal": d.settings.Frontend.DisableExternal, + "DisableUsedPercentage": d.settings.Frontend.DisableUsedPercentage, + "Color": d.settings.Frontend.Color, "BaseURL": d.server.BaseURL, "Version": version.Version, "StaticURL": path.Join(d.server.BaseURL, "/static"), "Signup": d.settings.Signup, - "NoAuth": d.settings.AuthMethod == auth.MethodNoAuth, - "AuthMethod": d.settings.AuthMethod, + "NoAuth": d.settings.Auth.Method == "noauth", + "AuthMethod": d.settings.Auth.Method, "LoginPage": auther.LoginPage(), "CSS": false, "ReCaptcha": false, - "Theme": d.settings.Branding.Theme, + "Theme": d.settings.Frontend.Theme, "EnableThumbs": d.server.EnableThumbnails, "ResizePreview": d.server.ResizePreview, "EnableExec": d.server.EnableExec, } - if d.settings.Branding.Files != "" { - fPath := filepath.Join(d.settings.Branding.Files, "custom.css") + if d.settings.Frontend.Files != "" { + fPath := filepath.Join(d.settings.Frontend.Files, "custom.css") _, err := os.Stat(fPath) //nolint:govet if err != nil && !os.IsNotExist(err) { @@ -59,8 +59,8 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys } } - if d.settings.AuthMethod == auth.MethodJSONAuth { - raw, err := d.store.Auth.Get(d.settings.AuthMethod) //nolint:govet + if d.settings.Auth.Method == "password" { + raw, err := d.store.Auth.Get(d.settings.Auth.Method) //nolint:govet if err != nil { return http.StatusInternalServerError, err } @@ -115,15 +115,15 @@ func getStaticHandlers(store *storage.Storage, server *settings.Server, assetsFs const maxAge = 86400 // 1 day w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%v", maxAge)) - if d.settings.Branding.Files != "" { + if d.settings.Frontend.Files != "" { if strings.HasPrefix(r.URL.Path, "img/") { - fPath := filepath.Join(d.settings.Branding.Files, r.URL.Path) + fPath := filepath.Join(d.settings.Frontend.Files, r.URL.Path) if _, err := os.Stat(fPath); err == nil { http.ServeFile(w, r, fPath) return 0, nil } - } else if r.URL.Path == "custom.css" && d.settings.Branding.Files != "" { - http.ServeFile(w, r, filepath.Join(d.settings.Branding.Files, "custom.css")) + } else if r.URL.Path == "custom.css" && d.settings.Frontend.Files != "" { + http.ServeFile(w, r, filepath.Join(d.settings.Frontend.Files, "custom.css")) return 0, nil } } diff --git a/backend/main.go b/backend/main.go index 0859625e..9f606708 100644 --- a/backend/main.go +++ b/backend/main.go @@ -5,5 +5,5 @@ import ( ) func main() { - cmd.Execute() + cmd.StartFilebrowser() } diff --git a/backend/run_benchmark.sh b/backend/run_benchmark.sh index 00d8aac8..b9e2a2f0 100755 --- a/backend/run_benchmark.sh +++ b/backend/run_benchmark.sh @@ -1,6 +1,5 @@ #!/bin/sh ## TEST file used by docker testing containers -touch render.yml checkExit() { if [ "$?" -ne 0 ];then exit 1 @@ -10,7 +9,7 @@ checkExit() { if command -v go &> /dev/null then printf "\n == Running benchmark == \n" - go test -bench=. -benchmem ./... + go test -bench=. -benchtime=10x -benchmem ./... checkExit else echo "ERROR: unable to perform tests" diff --git a/backend/run_tests.sh b/backend/run_tests.sh index f20b80b0..fc278bb2 100755 --- a/backend/run_tests.sh +++ b/backend/run_tests.sh @@ -1,6 +1,5 @@ #!/bin/sh ## TEST file used by docker testing containers -touch render.yml checkExit() { if [ "$?" -ne 0 ];then exit 1 diff --git a/backend/search/search_test.go b/backend/search/search_test.go index 2db409a4..7e8a44da 100644 --- a/backend/search/search_test.go +++ b/backend/search/search_test.go @@ -61,7 +61,7 @@ func BenchmarkSearchAllIndexes(b *testing.B) { indexes = make(map[string][]string) // Create mock data - createMockData(500, 3) // 1000 dirs, 3 files per dir + createMockData(50, 3) // 1000 dirs, 3 files per dir // Generate 100 random search terms searchTerms := generateRandomSearchTerms(100) @@ -71,10 +71,9 @@ func BenchmarkSearchAllIndexes(b *testing.B) { for i := 0; i < b.N; i++ { // Execute the SearchAllIndexes function for _, term := range searchTerms { - SearchAllIndexes(term, "/") + SearchAllIndexes(term, "/", "test") } } - printBenchmarkResults(b) } func BenchmarkFillIndex(b *testing.B) { @@ -82,14 +81,13 @@ func BenchmarkFillIndex(b *testing.B) { b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { - createMockData(10000, 10) // 1000 dirs, 3 files per dir + createMockData(50, 3) // 1000 dirs, 3 files per dir } - printBenchmarkResults(b) } func createMockData(numDirs, numFilesPerDir int) { for i := 0; i < numDirs; i++ { - dirName := "srv/" + getRandomTerm() + dirName := generateRandomPath(rand.Intn(3) + 1) addToIndex("/", dirName, true) for j := 0; j < numFilesPerDir; j++ { fileName := "file-" + getRandomTerm() + getRandomExtension() @@ -98,6 +96,15 @@ func createMockData(numDirs, numFilesPerDir int) { } } +func generateRandomPath(levels int) string { + rand.Seed(time.Now().UnixNano()) + dirName := "srv" + for i := 0; i < levels; i++ { + dirName += "/" + getRandomTerm() + } + return dirName +} + func getRandomTerm() string { wordbank := []string{ "hi", "test", "other", "name", @@ -161,11 +168,3 @@ func formatMemory(bytes int64) string { } return fmt.Sprintf("%d %s", bytes, sizes[i]) } - -// Output the benchmark results with human-readable units -func printBenchmarkResults(b *testing.B) { - averageTimePerIteration := b.Elapsed() / time.Duration(b.N) - fmt.Printf("\nIterations : %d\n", b.N) - fmt.Printf("Total time : %s\n", formatDuration(b.Elapsed())) - fmt.Printf("Avg time per op : %s\n", formatDuration(averageTimePerIteration)) -} diff --git a/backend/settings/config.go b/backend/settings/config.go new file mode 100644 index 00000000..df16dd34 --- /dev/null +++ b/backend/settings/config.go @@ -0,0 +1,83 @@ +package settings + +import ( + "log" + "os" + + "github.com/goccy/go-yaml" + "github.com/gtsteffaniak/filebrowser/users" +) + +var GlobalConfiguration Settings + +func Initialize(configFile string) { + yamlData := loadConfigFile(configFile) + GlobalConfiguration = setDefaults() + err := yaml.Unmarshal(yamlData, &GlobalConfiguration) + if err != nil { + log.Fatalf("Error unmarshaling YAML data: %v", err) + } + GlobalConfiguration.UserDefaults.Perm = GlobalConfiguration.UserDefaults.Permissions + GlobalConfiguration.Server.Root = "/srv" // hardcoded for now. TODO allow changing +} + +func loadConfigFile(configFile string) []byte { + // Open and read the YAML file + yamlFile, err := os.Open(configFile) + if err != nil { + log.Printf("ERROR: opening config file\n %v\n WARNING: Using default config only\n If this was a mistake, please make sure the file exists and is accessible by the filebrowser binary.\n\n", err) + setDefaults() + return []byte{} + } + defer yamlFile.Close() + + stat, err := yamlFile.Stat() + if err != nil { + log.Fatalf("Error getting file information: %s", err.Error()) + } + + yamlData := make([]byte, stat.Size()) + _, err = yamlFile.Read(yamlData) + if err != nil { + log.Fatalf("Error reading YAML data: %v", err) + } + return yamlData +} + +func setDefaults() Settings { + return Settings{ + Signup: true, + AdminUsername: "admin", + AdminPassword: "admin", + Server: Server{ + EnableThumbnails: true, + EnableExec: false, + IndexingInterval: 5, + Port: 8080, + NumImageProcessors: 4, + BaseURL: "", + Database: "database.db", + Log: "stdout", + Root: "/srv", + }, + Auth: Auth{ + Method: "password", + Recaptcha: Recaptcha{ + Host: "", + }, + }, + UserDefaults: UserDefaults{ + Scope: ".", + LockPassword: false, + HideDotfiles: true, + Permissions: users.Permissions{ + Create: true, + Rename: true, + Modify: true, + Delete: true, + Share: true, + Download: true, + }, + }, + } +} diff --git a/backend/settings/settings.go b/backend/settings/settings.go index dde86d73..0f7c3e94 100644 --- a/backend/settings/settings.go +++ b/backend/settings/settings.go @@ -35,3 +35,7 @@ func GenerateKey() ([]byte, error) { return b, nil } + +func GetSettingsConfig(nameType string, Value string) string { + return nameType + Value +} diff --git a/backend/settings/settings_test.go b/backend/settings/settings_test.go new file mode 100644 index 00000000..7eefacd7 --- /dev/null +++ b/backend/settings/settings_test.go @@ -0,0 +1,46 @@ +package settings + +import ( + "log" + "testing" + + "github.com/goccy/go-yaml" + "github.com/google/go-cmp/cmp" +) + +func TestConfigLoadChanged(t *testing.T) { + configYml = "./testingConfig.yaml" + yamlData := loadConfigFile() + // Marshal the YAML data to a more human-readable format + newConfig := setDefaults() + GlobalConfiguration := setDefaults() + + err := yaml.Unmarshal(yamlData, &newConfig) + if err != nil { + log.Fatalf("Error unmarshaling YAML data: %v", err) + } + // Use go-cmp to compare the two structs + if diff := cmp.Diff(newConfig, GlobalConfiguration); diff == "" { + t.Errorf("No change when there should have been (-want +got):\n%s", diff) + } +} + +func TestConfigLoadSpecificValues(t *testing.T) { + configYml = "./testingConfig.yaml" + yamlData := loadConfigFile() + // Marshal the YAML data to a more human-readable format + newConfig := setDefaults() + GlobalConfiguration := setDefaults() + + err := yaml.Unmarshal(yamlData, &newConfig) + if err != nil { + log.Fatalf("Error unmarshaling YAML data: %v", err) + } + + if GlobalConfiguration.Auth.Method == newConfig.Auth.Method { + log.Fatalf("Differences should have been found, but were not on Auth method") + } + if GlobalConfiguration.UserDefaults.HideDotfiles == newConfig.UserDefaults.HideDotfiles { + log.Fatalf("Differences should have been found, but were not on Auth method") + } +} diff --git a/backend/settings/storage.go b/backend/settings/storage.go index a027b4b3..19c8fdcc 100644 --- a/backend/settings/storage.go +++ b/backend/settings/storage.go @@ -50,16 +50,16 @@ func (s *Storage) Save(set *Settings) error { return errors.ErrEmptyKey } - if set.Defaults.Locale == "" { - set.Defaults.Locale = "en" + if set.UserDefaults.Locale == "" { + set.UserDefaults.Locale = "en" } - if set.Defaults.Commands == nil { - set.Defaults.Commands = []string{} + if set.UserDefaults.Commands == nil { + set.UserDefaults.Commands = []string{} } - if set.Defaults.ViewMode == "" { - set.Defaults.ViewMode = users.MosaicViewMode + if set.UserDefaults.ViewMode == "" { + set.UserDefaults.ViewMode = users.MosaicViewMode } if set.Rules == nil { diff --git a/backend/settings/structs.go b/backend/settings/structs.go index 5669684a..d998de11 100644 --- a/backend/settings/structs.go +++ b/backend/settings/structs.go @@ -1,7 +1,6 @@ package settings import ( - "github.com/gtsteffaniak/filebrowser/files" "github.com/gtsteffaniak/filebrowser/rules" "github.com/gtsteffaniak/filebrowser/users" ) @@ -24,26 +23,35 @@ type Settings struct { Signup bool `json:"signup"` CreateUserDir bool `json:"createUserDir"` UserHomeBasePath string `json:"userHomeBasePath"` - Defaults UserDefaults `json:"defaults"` Commands map[string][]string `json:"commands"` Shell []string `json:"shell"` + AdminUsername string `json:"adminUsername"` + AdminPassword string `json:"adminPassword"` Rules []rules.Rule `json:"rules"` Server Server `json:"server"` - AuthMethod string `json:"authMethod"` - Auth struct { - Header string `json:"header"` - Method string `json:"method"` - Command string `json:"command"` - Signup bool `json:"signup"` - Shell string `json:"shell"` - } `json:"auth"` + Auth Auth `json:"auth"` + Frontend Frontend `json:"frontend"` + UserDefaults UserDefaults `json:"userDefaults"` +} - Branding Branding `json:"branding"` +type Auth struct { + Recaptcha Recaptcha `json:"recaptcha"` + Header string `json:"header"` + Method string `json:"method"` + Command string `json:"command"` + Signup bool `json:"signup"` + Shell string `json:"shell"` +} - UserDefaults UserDefaults `json:"userDefaults"` +type Recaptcha struct { + Host string `json:"host"` + Key string `json:"key"` + Secret string `json:"secret"` } type Server struct { + IndexingInterval uint32 `json:"indexingInterval"` + NumImageProcessors int `json:"numImageProcessors"` Socket string `json:"socket"` TLSKey string `json:"tlsKey"` TLSCert string `json:"tlsCert"` @@ -52,16 +60,15 @@ type Server struct { EnableExec bool `json:"enableExec"` TypeDetectionByHeader bool `json:"typeDetectionByHeader"` AuthHook string `json:"authHook"` - Port string `json:"port"` + Port int `json:"port"` BaseURL string `json:"baseURL"` Address string `json:"address"` Log string `json:"log"` Database string `json:"database"` Root string `json:"root"` - EnablePreviewResize bool `json:"disable-preview-resize"` } -type Branding struct { +type Frontend struct { Name string `json:"name"` DisableExternal bool `json:"disableExternal"` DisableUsedPercentage bool `json:"disableUsedPercentage"` @@ -73,55 +80,18 @@ type Branding struct { // UserDefaults is a type that holds the default values // for some fields on User. type UserDefaults struct { - Scope string `json:"scope"` - Locale string `json:"locale"` - ViewMode users.ViewMode `json:"viewMode"` - SingleClick bool `json:"singleClick"` - Sorting files.Sorting `json:"sorting"` + LockPassword bool `json:"lockPassword"` + Scope string `json:"scope"` + Locale string `json:"locale"` + ViewMode string `json:"viewMode"` + SingleClick bool `json:"singleClick"` + Sorting struct { + By string `json:"by"` + Asc bool `json:"asc"` + } `json:"sorting"` Perm users.Permissions `json:"perm"` + Permissions users.Permissions `json:"permissions"` Commands []string `json:"commands"` HideDotfiles bool `json:"hideDotfiles"` DateFormat bool `json:"dateFormat"` } - -//{ -// "server":{ -// "port":8080, -// "baseURL":"", -// "address":"", -// "log":"stdout", -// "database":"./database.db", -// "root":"/srv", -// "disable-thumbnails":false, -// "disable-preview-resize":false, -// "disable-exec":false, -// "disable-type-detection-by-header":false -// }, -// "auth":{ -// "header":"", -// "method":"", -// "command":"", -// "signup":false, -// "shell":"" -// }, -// "branding":{ -// "name":"", -// "color":"", -// "files":"", -// "disableExternal":"", -// "disableUsedPercentage":"" -// }, -// "permissions":{ -// "Admin":false, -// "Execute":true, -// "Create":true, -// "Rename":true, -// "Modify":true, -// "Delete":true, -// "Share":true, -// "Download":true -// }, -// "commands":{}, -// "shell":{}, -// "rules":{} -// } diff --git a/backend/settings/testingConfig.yaml b/backend/settings/testingConfig.yaml new file mode 100644 index 00000000..a45f930e --- /dev/null +++ b/backend/settings/testingConfig.yaml @@ -0,0 +1,52 @@ +server: + indexingInterval: 5 + numImageProcessors: 4 + socket: "" + tlsKey: "" + tlsCert: "" + enableThumbnails: false + resizePreview: true + typeDetectionByHeader: true + port: 8080 + baseURL: "/" + address: "" + log: "stdout" + database: "database.db" + root: "/srv" +auth: + recaptcha: + host: "" + key: "" + secret: "" + header: "" + method: json + command: "" + signup: false + shell: "" +frontend: + name: "" + disableExternal: false + disableUsedPercentage: true + files: "" + theme: "" + color: "" +userDefaults: + scope: "" + locale: "" + viewMode: "" + singleClick: true + sorting: + by: "" + asc: true + permissions: + admin: true + execute: true + create: true + rename: true + modify: true + delete: true + share: true + download: true + commands: [] + hideDotfiles: false + dateFormat: false diff --git a/backend/storage/bolt/auth.go b/backend/storage/bolt/auth.go index abba4e1d..e3f6977b 100644 --- a/backend/storage/bolt/auth.go +++ b/backend/storage/bolt/auth.go @@ -2,7 +2,6 @@ package bolt import ( "github.com/asdine/storm/v3" - "github.com/gtsteffaniak/filebrowser/auth" "github.com/gtsteffaniak/filebrowser/errors" ) @@ -13,20 +12,18 @@ type authBackend struct { func (s authBackend) Get(t string) (auth.Auther, error) { var auther auth.Auther - switch t { - case auth.MethodJSONAuth: + case "password": auther = &auth.JSONAuth{} - case auth.MethodProxyAuth: + case "proxy": auther = &auth.ProxyAuth{} - case auth.MethodHookAuth: + case "hook": auther = &auth.HookAuth{} - case auth.MethodNoAuth: + case "noauth": auther = &auth.NoAuth{} default: return nil, errors.ErrInvalidAuthMethod } - return auther, get(s.db, "auther", auther) } diff --git a/backend/storage/bolt/config.go b/backend/storage/bolt/config.go index 67f9eed9..cb4914c6 100644 --- a/backend/storage/bolt/config.go +++ b/backend/storage/bolt/config.go @@ -2,7 +2,6 @@ package bolt import ( "github.com/asdine/storm/v3" - "github.com/gtsteffaniak/filebrowser/settings" ) @@ -20,7 +19,10 @@ func (s settingsBackend) Save(set *settings.Settings) error { } func (s settingsBackend) GetServer() (*settings.Server, error) { - server := &settings.Server{} + server := &settings.Server{ + Port: 8080, + NumImageProcessors: 1, + } return server, get(s.db, "server", server) } diff --git a/backend/storage/bolt/importer/conf.go b/backend/storage/bolt/importer/conf.go deleted file mode 100644 index 939559c2..00000000 --- a/backend/storage/bolt/importer/conf.go +++ /dev/null @@ -1,187 +0,0 @@ -package importer - -import ( - "encoding/json" - "errors" - "fmt" - "os" - "path/filepath" - - "github.com/asdine/storm/v3" - "github.com/pelletier/go-toml/v2" - "gopkg.in/yaml.v2" - - "github.com/gtsteffaniak/filebrowser/auth" - "github.com/gtsteffaniak/filebrowser/settings" - "github.com/gtsteffaniak/filebrowser/storage" - "github.com/gtsteffaniak/filebrowser/users" -) - -type oldDefs struct { - Commands []string `json:"commands" yaml:"commands" toml:"commands"` - Scope string `json:"scope" yaml:"scope" toml:"scope"` - ViewMode string `json:"viewMode" yaml:"viewMode" toml:"viewMode"` - Locale string `json:"locale" yaml:"locale" toml:"locale"` - AllowCommands bool `json:"allowCommands" yaml:"allowCommands" toml:"allowCommands"` - AllowEdit bool `json:"allowEdit" yaml:"allowEdit" toml:"allowEdit"` - AllowNew bool `json:"allowNew" yaml:"allowNew" toml:"allowNew"` -} - -type oldAuth struct { - Method string `json:"method" yaml:"method" toml:"method"` // default none proxy - Header string `json:"header" yaml:"header" toml:"header"` - Command string `json:"command" yaml:"command" toml:"command"` -} - -type oldConf struct { - Port string `json:"port" yaml:"port" toml:"port"` - BaseURL string `json:"baseURL" yaml:"baseURL" toml:"baseURL"` - Log string `json:"log" yaml:"log" toml:"log"` - Address string `json:"address" yaml:"address" toml:"address"` - Defaults oldDefs `json:"defaults" yaml:"defaults" toml:"defaults"` - ReCaptcha struct { - Key string `json:"key" yaml:"key" toml:"key"` - Secret string `json:"secret" yaml:"secret" toml:"secret"` - Host string `json:"host" yaml:"host" toml:"host"` - } `json:"recaptcha" yaml:"recaptcha" toml:"recaptcha"` - Auth oldAuth `json:"auth" yaml:"auth" toml:"auth"` -} - -var defaults = &oldConf{ - Port: "0", - Log: "stdout", - Defaults: oldDefs{ - Commands: []string{"git", "svn", "hg"}, - ViewMode: string(users.MosaicViewMode), - AllowCommands: true, - AllowEdit: true, - AllowNew: true, - Locale: "en", - }, - Auth: oldAuth{ - Method: "default", - }, -} - -func readConf(path string) (*oldConf, error) { - cfg := &oldConf{} - if path != "" { - ext := filepath.Ext(path) - - fd, err := os.Open(path) - if err != nil { - return nil, err - } - defer fd.Close() - - switch ext { - case ".json": - err = json.NewDecoder(fd).Decode(cfg) - case ".toml": - err = toml.NewDecoder(fd).Decode(cfg) - case ".yaml", ".yml": - err = yaml.NewDecoder(fd).Decode(cfg) - default: - return nil, errors.New("unsupported config extension " + ext) - } - - if err != nil { - return nil, err - } - } else { - cfg = defaults - path, err := filepath.Abs(".") - if err != nil { - return nil, err - } - 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{} - err = db.Get("config", "commands", &commands) - if err != nil { - return err - } - - key := []byte{} - err = db.Get("config", "key", &key) - if err != nil { - return err - } - - s := &settings.Settings{ - Key: key, - Signup: false, - Defaults: settings.UserDefaults{ - Scope: cfg.Defaults.Scope, - Commands: cfg.Defaults.Commands, - ViewMode: users.ViewMode(cfg.Defaults.ViewMode), - Locale: cfg.Defaults.Locale, - Perm: users.Permissions{ - Admin: false, - Execute: cfg.Defaults.AllowCommands, - Create: cfg.Defaults.AllowNew, - Rename: cfg.Defaults.AllowEdit, - Modify: cfg.Defaults.AllowEdit, - Delete: cfg.Defaults.AllowEdit, - Share: true, - Download: true, - }, - }, - } - - server := &settings.Server{ - BaseURL: cfg.BaseURL, - Port: cfg.Port, - Address: cfg.Address, - Log: cfg.Log, - } - - var auther auth.Auther - switch cfg.Auth.Method { - case "proxy": - auther = &auth.ProxyAuth{Header: cfg.Auth.Header} - s.AuthMethod = string(auth.MethodProxyAuth) - case "hook": - auther = &auth.HookAuth{Command: cfg.Auth.Command} - s.AuthMethod = string(auth.MethodHookAuth) - case "none": - auther = &auth.NoAuth{} - s.AuthMethod = string(auth.MethodNoAuth) - default: - auther = &auth.JSONAuth{ - ReCaptcha: &auth.ReCaptcha{ - Host: cfg.ReCaptcha.Host, - Key: cfg.ReCaptcha.Key, - Secret: cfg.ReCaptcha.Secret, - }, - } - s.AuthMethod = string(auth.MethodJSONAuth) - } - - err = sto.Auth.Save(auther) - if err != nil { - return err - } - - err = sto.Settings.Save(s) - if err != nil { - return err - } - - err = sto.Settings.SaveServer(server) - if err != nil { - return err - } - - fmt.Println("Configuration successfully imported.") - return nil -} diff --git a/backend/storage/bolt/importer/importer.go b/backend/storage/bolt/importer/importer.go deleted file mode 100644 index 1ecc70eb..00000000 --- a/backend/storage/bolt/importer/importer.go +++ /dev/null @@ -1,39 +0,0 @@ -package importer - -import ( - "github.com/asdine/storm/v3" - - "github.com/gtsteffaniak/filebrowser/storage/bolt" -) - -// Import imports an old configuration to a newer database. -func Import(oldDBPath, oldConf, newDBPath string) error { - oldDB, err := storm.Open(oldDBPath) - if err != nil { - return err - } - defer oldDB.Close() - - newDB, err := storm.Open(newDBPath) - if err != nil { - return err - } - defer newDB.Close() - - sto, err := bolt.NewStorage(newDB) - if err != nil { - return err - } - - err = importUsers(oldDB, sto) - if err != nil { - return err - } - - err = importConf(oldDB, oldConf, sto) - if err != nil { - return err - } - - return err -} diff --git a/backend/storage/bolt/importer/users.go b/backend/storage/bolt/importer/users.go deleted file mode 100644 index 4606fc2e..00000000 --- a/backend/storage/bolt/importer/users.go +++ /dev/null @@ -1,114 +0,0 @@ -package importer - -import ( - "encoding/json" - "fmt" - - "github.com/asdine/storm/v3" - bolt "go.etcd.io/bbolt" - - "github.com/gtsteffaniak/filebrowser/rules" - "github.com/gtsteffaniak/filebrowser/storage" - "github.com/gtsteffaniak/filebrowser/users" -) - -type oldUser struct { - ID int `storm:"id,increment"` - Admin bool `json:"admin"` - AllowCommands bool `json:"allowCommands"` // Execute commands - AllowEdit bool `json:"allowEdit"` // Edit/rename files - AllowNew bool `json:"allowNew"` // Create files and folders - AllowPublish bool `json:"allowPublish"` // Publish content (to use with static gen) - LockPassword bool `json:"lockPassword"` - Commands []string `json:"commands"` - Locale string `json:"locale"` - Password string `json:"password"` - Rules []*rules.Rule `json:"rules"` - Scope string `json:"filesystem"` - Username string `json:"username" storm:"index,unique"` - ViewMode string `json:"viewMode"` -} - -func readOldUsers(db *storm.DB) ([]*oldUser, error) { - var oldUsers []*oldUser - err := db.Bolt.View(func(tx *bolt.Tx) error { - return tx.Bucket([]byte("User")).ForEach(func(k []byte, v []byte) error { - if len(v) > 0 && string(v)[0] == '{' { - user := &oldUser{} - err := json.Unmarshal(v, user) - - if err != nil { - return err - } - - oldUsers = append(oldUsers, user) - } - - return nil - }) - }) - - return oldUsers, err -} - -func convertUsersToNew(old []*oldUser) ([]*users.User, error) { - list := []*users.User{} - - for _, oldUser := range old { - user := &users.User{ - Username: oldUser.Username, - Password: oldUser.Password, - Scope: oldUser.Scope, - Locale: oldUser.Locale, - LockPassword: oldUser.LockPassword, - ViewMode: users.ViewMode(oldUser.ViewMode), - Commands: oldUser.Commands, - Rules: []rules.Rule{}, - Perm: users.Permissions{ - Admin: oldUser.Admin, - Execute: oldUser.AllowCommands, - Create: oldUser.AllowNew, - Rename: oldUser.AllowEdit, - Modify: oldUser.AllowEdit, - Delete: oldUser.AllowEdit, - Share: true, - Download: true, - }, - } - - for _, rule := range oldUser.Rules { - user.Rules = append(user.Rules, *rule) - } - - err := user.Clean("") - if err != nil { - return nil, err - } - - list = append(list, user) - } - - return list, nil -} - -func importUsers(old *storm.DB, sto *storage.Storage) error { - oldUsers, err := readOldUsers(old) - if err != nil { - return err - } - - newUsers, err := convertUsersToNew(oldUsers) - if err != nil { - return err - } - - for _, user := range newUsers { - err = sto.Users.Save(user) - if err != nil { - return err - } - } - - fmt.Printf("%d users successfully imported into the new DB.\n", len(newUsers)) - return nil -} diff --git a/backend/test_output.txt b/backend/test_output.txt new file mode 100644 index 00000000..42fde92b --- /dev/null +++ b/backend/test_output.txt @@ -0,0 +1,113 @@ + + == Running tests == +/usr/local/go/bin/go +? github.com/gtsteffaniak/filebrowser [no test files] +? github.com/gtsteffaniak/filebrowser/auth [no test files] +? github.com/gtsteffaniak/filebrowser/cmd [no test files] +? github.com/gtsteffaniak/filebrowser/errors [no test files] +? github.com/gtsteffaniak/filebrowser/files [no test files] +=== RUN TestFileCache +--- PASS: TestFileCache (0.00s) +PASS +ok github.com/gtsteffaniak/filebrowser/diskcache (cached) +=== RUN TestCommonPrefix +=== RUN TestCommonPrefix/sub_folder +=== RUN TestCommonPrefix/relative_path +=== RUN TestCommonPrefix/no_common_path +=== RUN TestCommonPrefix/same_lvl +--- PASS: TestCommonPrefix (0.00s) + --- PASS: TestCommonPrefix/sub_folder (0.00s) + --- PASS: TestCommonPrefix/relative_path (0.00s) + --- PASS: TestCommonPrefix/no_common_path (0.00s) + --- PASS: TestCommonPrefix/same_lvl (0.00s) +PASS +ok github.com/gtsteffaniak/filebrowser/fileutils (cached) +? github.com/gtsteffaniak/filebrowser/settings [no test files] +? github.com/gtsteffaniak/filebrowser/share [no test files] +? github.com/gtsteffaniak/filebrowser/storage [no test files] +? github.com/gtsteffaniak/filebrowser/storage/bolt [no test files] +2023/09/02 13:07:17 Error opening YAML file: open filebrowser.yaml: no such file or directory +FAIL github.com/gtsteffaniak/filebrowser/http 0.008s +=== RUN TestService_Resize +=== RUN TestService_Resize/convert_to_png +=== RUN TestService_Resize/convert_to_tiff +=== RUN TestService_Resize/resize_bmp +=== RUN TestService_Resize/resize_with_medium_quality +=== RUN TestService_Resize/resize_with_low_quality +=== RUN TestService_Resize/get_thumbnail_from_file_with_APP0_JFIF +=== RUN TestService_Resize/fill_upscale +=== RUN TestService_Resize/fit_upscale +=== RUN TestService_Resize/convert_to_gif +=== RUN TestService_Resize/convert_to_bmp +=== RUN TestService_Resize/resize_tiff +=== RUN TestService_Resize/resize_with_high_quality +=== RUN TestService_Resize/fill_downscale +=== RUN TestService_Resize/keep_original_format +=== RUN TestService_Resize/convert_to_unknown +=== RUN TestService_Resize/get_thumbnail_from_file_without_APP0_JFIF +=== RUN TestService_Resize/resize_for_higher_quality_levels +=== RUN TestService_Resize/broken_file +=== RUN TestService_Resize/fit_downscale +=== RUN TestService_Resize/convert_to_jpeg +=== RUN TestService_Resize/resize_png +=== RUN TestService_Resize/resize_gif +=== RUN TestService_Resize/resize_with_unknown_quality +=== RUN TestService_Resize/resize_from_file_without_IFD1_thumbnail +--- PASS: TestService_Resize (1.36s) + --- PASS: TestService_Resize/convert_to_png (0.01s) + --- PASS: TestService_Resize/convert_to_tiff (0.01s) + --- PASS: TestService_Resize/resize_bmp (0.01s) + --- PASS: TestService_Resize/resize_with_medium_quality (0.01s) + --- PASS: TestService_Resize/resize_with_low_quality (0.01s) + --- PASS: TestService_Resize/get_thumbnail_from_file_with_APP0_JFIF (0.02s) + --- PASS: TestService_Resize/fill_upscale (0.01s) + --- PASS: TestService_Resize/fit_upscale (0.00s) + --- PASS: TestService_Resize/convert_to_gif (0.01s) + --- PASS: TestService_Resize/convert_to_bmp (0.01s) + --- PASS: TestService_Resize/resize_tiff (0.01s) + --- PASS: TestService_Resize/resize_with_high_quality (0.01s) + --- PASS: TestService_Resize/fill_downscale (0.01s) + --- PASS: TestService_Resize/keep_original_format (0.01s) + --- PASS: TestService_Resize/convert_to_unknown (0.01s) + --- PASS: TestService_Resize/get_thumbnail_from_file_without_APP0_JFIF (0.03s) + --- PASS: TestService_Resize/resize_for_higher_quality_levels (0.03s) + --- PASS: TestService_Resize/broken_file (0.00s) + --- PASS: TestService_Resize/fit_downscale (0.01s) + --- PASS: TestService_Resize/convert_to_jpeg (0.01s) + --- PASS: TestService_Resize/resize_png (0.02s) + --- PASS: TestService_Resize/resize_gif (0.02s) + --- PASS: TestService_Resize/resize_with_unknown_quality (0.01s) + --- PASS: TestService_Resize/resize_from_file_without_IFD1_thumbnail (1.09s) +=== RUN TestService_FormatFromExtension +=== RUN TestService_FormatFromExtension/gif +=== RUN TestService_FormatFromExtension/tiff +=== RUN TestService_FormatFromExtension/bmp +=== RUN TestService_FormatFromExtension/unknown +=== RUN TestService_FormatFromExtension/jpg +=== RUN TestService_FormatFromExtension/jpeg +=== RUN TestService_FormatFromExtension/png +--- PASS: TestService_FormatFromExtension (0.00s) + --- PASS: TestService_FormatFromExtension/gif (0.00s) + --- PASS: TestService_FormatFromExtension/tiff (0.00s) + --- PASS: TestService_FormatFromExtension/bmp (0.00s) + --- PASS: TestService_FormatFromExtension/unknown (0.00s) + --- PASS: TestService_FormatFromExtension/jpg (0.00s) + --- PASS: TestService_FormatFromExtension/jpeg (0.00s) + --- PASS: TestService_FormatFromExtension/png (0.00s) +PASS +ok github.com/gtsteffaniak/filebrowser/img (cached) +=== RUN TestMatchHidden +--- PASS: TestMatchHidden (0.00s) +PASS +ok github.com/gtsteffaniak/filebrowser/rules (cached) +2023/09/02 13:07:17 Error opening YAML file: open filebrowser.yaml: no such file or directory +FAIL github.com/gtsteffaniak/filebrowser/runner 0.007s +=== RUN TestParseSearch +--- PASS: TestParseSearch (0.00s) +PASS +ok github.com/gtsteffaniak/filebrowser/search (cached) +? github.com/gtsteffaniak/filebrowser/version [no test files] +testing: warning: no tests to run +PASS +ok github.com/gtsteffaniak/filebrowser/users (cached) [no tests to run] +FAIL diff --git a/backend/users/permissions.go b/backend/users/permissions.go deleted file mode 100644 index 29e0a5b9..00000000 --- a/backend/users/permissions.go +++ /dev/null @@ -1,13 +0,0 @@ -package users - -// Permissions describe a user's permissions. -type Permissions struct { - Admin bool `json:"admin"` - Execute bool `json:"execute"` - Create bool `json:"create"` - Rename bool `json:"rename"` - Modify bool `json:"modify"` - Delete bool `json:"delete"` - Share bool `json:"share"` - Download bool `json:"download"` -} diff --git a/backend/users/users.go b/backend/users/users.go index 91dcea68..84a0cab9 100644 --- a/backend/users/users.go +++ b/backend/users/users.go @@ -11,14 +11,22 @@ import ( "github.com/gtsteffaniak/filebrowser/rules" ) -// ViewMode describes a view mode. -type ViewMode string - -const ( - ListViewMode ViewMode = "list" - MosaicViewMode ViewMode = "mosaic" +var ( + ListViewMode = "list" + MosaicViewMode = "mosaic" ) +type Permissions struct { + Admin bool `json:"admin"` + Execute bool `json:"execute"` + Create bool `json:"create"` + Rename bool `json:"rename"` + Modify bool `json:"modify"` + Delete bool `json:"delete"` + Share bool `json:"share"` + Download bool `json:"download"` +} + // User describes a user. type User struct { ID uint `storm:"id,increment" json:"id"` @@ -27,7 +35,7 @@ type User struct { Scope string `json:"scope"` Locale string `json:"locale"` LockPassword bool `json:"lockPassword"` - ViewMode ViewMode `json:"viewMode"` + ViewMode string `json:"viewMode"` SingleClick bool `json:"singleClick"` Perm Permissions `json:"perm"` Commands []string `json:"commands"` diff --git a/backend/version/version.go b/backend/version/version.go index 03dbd250..99e9b441 100644 --- a/backend/version/version.go +++ b/backend/version/version.go @@ -2,7 +2,7 @@ package version var ( // Version is the current File Browser version. - Version = "(0.1.4)" + Version = "(0.2.0)" // CommitSHA is the commmit sha. CommitSHA = "(unknown)" ) diff --git a/frontend/public/img/icons/android-chrome-192x192.png b/frontend/public/img/icons/android-chrome-192x192.png deleted file mode 100644 index e9131e42..00000000 Binary files a/frontend/public/img/icons/android-chrome-192x192.png and /dev/null differ diff --git a/frontend/public/img/icons/android-chrome-512x512.png b/frontend/public/img/icons/android-chrome-512x512.png deleted file mode 100644 index d4d3f2e2..00000000 Binary files a/frontend/public/img/icons/android-chrome-512x512.png and /dev/null differ diff --git a/frontend/public/img/icons/apple-touch-icon.png b/frontend/public/img/icons/apple-touch-icon.png deleted file mode 100644 index 7ea77eb8..00000000 Binary files a/frontend/public/img/icons/apple-touch-icon.png and /dev/null differ diff --git a/frontend/public/img/icons/favicon-16x16.png b/frontend/public/img/icons/favicon-16x16.png deleted file mode 100644 index b02a47ff..00000000 Binary files a/frontend/public/img/icons/favicon-16x16.png and /dev/null differ diff --git a/frontend/public/img/icons/favicon-32x32.png b/frontend/public/img/icons/favicon-32x32.png index d5672cc1..bb33be2b 100644 Binary files a/frontend/public/img/icons/favicon-32x32.png and b/frontend/public/img/icons/favicon-32x32.png differ diff --git a/frontend/public/img/icons/favicon.ico b/frontend/public/img/icons/favicon.ico index 1d6f60cd..d78d8bdb 100644 Binary files a/frontend/public/img/icons/favicon.ico and b/frontend/public/img/icons/favicon.ico differ diff --git a/frontend/public/img/icons/mstile-144x144.png b/frontend/public/img/icons/mstile-144x144.png deleted file mode 100644 index 0c2745c0..00000000 Binary files a/frontend/public/img/icons/mstile-144x144.png and /dev/null differ diff --git a/frontend/public/img/icons/mstile-150x150.png b/frontend/public/img/icons/mstile-150x150.png index 7130a1e3..bb33be2b 100644 Binary files a/frontend/public/img/icons/mstile-150x150.png and b/frontend/public/img/icons/mstile-150x150.png differ diff --git a/frontend/public/img/icons/mstile-310x150.png b/frontend/public/img/icons/mstile-310x150.png deleted file mode 100644 index 6e0dc0a1..00000000 Binary files a/frontend/public/img/icons/mstile-310x150.png and /dev/null differ diff --git a/frontend/public/img/icons/mstile-310x310.png b/frontend/public/img/icons/mstile-310x310.png deleted file mode 100644 index 56d4963a..00000000 Binary files a/frontend/public/img/icons/mstile-310x310.png and /dev/null differ diff --git a/frontend/public/img/icons/mstile-70x70.png b/frontend/public/img/icons/mstile-70x70.png deleted file mode 100644 index 556d2cd1..00000000 Binary files a/frontend/public/img/icons/mstile-70x70.png and /dev/null differ diff --git a/frontend/public/themes/dark.css b/frontend/public/themes/dark.css index 86ac857c..42eb58ff 100644 --- a/frontend/public/themes/dark.css +++ b/frontend/public/themes/dark.css @@ -33,13 +33,14 @@ header { #search #input { background: var(--surfaceSecondary); - border-color: var(--surfacePrimary); + border-color: var(--surfaceSecondary); } #search #input input::placeholder { color: var(--textSecondary); } #search.active #input { background: var(--surfacePrimary); + border-color: white; } #search.active input { color: var(--textPrimary); @@ -131,8 +132,8 @@ nav > div { .input { background: var(--surfaceSecondary); color: var(--textPrimary); - border: 1px solid rgba(255, 255, 255, 0.05); } + .input:hover, .input:focus { border-color: rgba(255, 255, 255, 0.15); @@ -213,3 +214,7 @@ nav { background: var(--background); color: white } +#result-desktop #result-list { + background: #2a3137; + max-height: unset; +} \ No newline at end of file diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 84d859e4..5c4ac2b0 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,7 +1,5 @@ \ No newline at end of file + diff --git a/frontend/src/components/Shell.vue b/frontend/src/components/Shell.vue deleted file mode 100644 index 47c3d67c..00000000 --- a/frontend/src/components/Shell.vue +++ /dev/null @@ -1,125 +0,0 @@ - - - diff --git a/frontend/src/components/Sidebar.vue b/frontend/src/components/Sidebar.vue index 44159b63..b10624a3 100644 --- a/frontend/src/components/Sidebar.vue +++ b/frontend/src/components/Sidebar.vue @@ -16,7 +16,7 @@ note_add {{ $t("sidebar.newFile") }} - diff --git a/frontend/src/components/header/HeaderBar.vue b/frontend/src/components/header/HeaderBar.vue index 8995be9f..f7f8db15 100644 --- a/frontend/src/components/header/HeaderBar.vue +++ b/frontend/src/components/header/HeaderBar.vue @@ -1,24 +1,12 @@ + \ No newline at end of file diff --git a/frontend/src/views/Settings.vue b/frontend/src/views/Settings.vue index 7dabc1c7..55c03f29 100644 --- a/frontend/src/views/Settings.vue +++ b/frontend/src/views/Settings.vue @@ -1,7 +1,5 @@