From a33eab2a8c1882c75153cb8dc014c3d9c7dc6b85 Mon Sep 17 00:00:00 2001 From: Graham Steffaniak <42989099+gtsteffaniak@users.noreply.github.com> Date: Mon, 9 Oct 2023 17:24:48 -0500 Subject: [PATCH] update settings and views (#41) Co-authored-by: Graham Steffaniak --- backend/auth/hook.go | 17 +- backend/auth/json.go | 1 - backend/benchmark_results.txt | 39 --- backend/cmd/hash.go | 25 -- backend/cmd/root.go | 17 +- backend/cmd/users_add.go | 5 +- backend/filebrowser.yaml | 8 +- backend/http/auth.go | 37 +-- backend/http/search.go | 1 - backend/http/share.go | 2 +- backend/http/static.go | 1 - backend/http/users.go | 6 - backend/index/conditions.go | 33 ++ backend/index/search_index.go | 66 ++-- backend/settings/config.go | 13 +- backend/settings/storage.go | 3 +- backend/settings/structs.go | 2 +- backend/settings/testingConfig.yaml | 3 +- backend/storage/bolt/users.go | 9 +- backend/users/password.go | 3 + backend/users/storage.go | 2 +- backend/users/users.go | 82 ++--- backend/version/version.go | 2 +- configuration.md | 6 + frontend/package-lock.json | 48 +-- frontend/package.json | 2 +- frontend/public/index.html | 4 +- frontend/public/themes/dark.css | 220 ------------- frontend/src/App.vue | 10 +- frontend/src/components/Search.vue | 13 +- frontend/src/components/Sidebar.vue | 5 +- frontend/src/components/settings/ViewMode.vue | 24 ++ frontend/src/css/base.css | 1 + frontend/src/css/dark.css | 292 ++++++++++++++++++ frontend/src/css/listing.css | 173 +++++++++-- frontend/src/css/mobile.css | 15 +- frontend/src/utils/auth.js | 3 - frontend/src/utils/constants.js | 2 - frontend/src/views/Layout.vue | 20 +- frontend/src/views/Settings.vue | 16 +- frontend/src/views/bars/Default.vue | 24 +- frontend/src/views/bars/ListingBar.vue | 23 +- frontend/src/views/files/Listing.vue | 34 +- frontend/src/views/settings/Global.vue | 11 - frontend/src/views/settings/Profile.vue | 24 +- 45 files changed, 746 insertions(+), 601 deletions(-) delete mode 100644 backend/benchmark_results.txt delete mode 100644 backend/cmd/hash.go delete mode 100644 frontend/public/themes/dark.css create mode 100644 frontend/src/components/settings/ViewMode.vue create mode 100644 frontend/src/css/dark.css diff --git a/backend/auth/hook.go b/backend/auth/hook.go index 9ed4bf6d..c1902947 100644 --- a/backend/auth/hook.go +++ b/backend/auth/hook.go @@ -146,14 +146,11 @@ func (a *HookAuth) SaveUser() (*users.User, error) { } if u == nil { - pass, err := users.HashPwd(a.Cred.Password) - if err != nil { - return nil, err - } + log.Println("creds", a.Cred.Password) // create user with the provided credentials d := &users.User{ Username: a.Cred.Username, - Password: pass, + Password: a.Cred.Password, Scope: a.Settings.UserDefaults.Scope, Locale: a.Settings.UserDefaults.Locale, ViewMode: a.Settings.UserDefaults.ViewMode, @@ -178,16 +175,6 @@ func (a *HookAuth) SaveUser() (*users.User, error) { } } else if p := !users.CheckPwd(a.Cred.Password, u.Password); len(a.Fields.Values) > 1 || p { u = a.GetUser(u) - - // update the password when it doesn't match the current - if p { - pass, err := users.HashPwd(a.Cred.Password) - if err != nil { - return nil, err - } - u.Password = pass - } - // update user with provided fields err := a.Users.Update(u) if err != nil { diff --git a/backend/auth/json.go b/backend/auth/json.go index d3734789..7344cd04 100644 --- a/backend/auth/json.go +++ b/backend/auth/json.go @@ -30,7 +30,6 @@ func (a JSONAuth) Auth(r *http.Request, usr users.Store) (*users.User, error) { if r.Body == nil { return nil, os.ErrPermission } - err := json.NewDecoder(r.Body).Decode(&cred) if err != nil { return nil, os.ErrPermission diff --git a/backend/benchmark_results.txt b/backend/benchmark_results.txt deleted file mode 100644 index 7d33abb9..00000000 --- a/backend/benchmark_results.txt +++ /dev/null @@ -1,39 +0,0 @@ - - == Running benchmark == -? 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 1.318s -? github.com/gtsteffaniak/filebrowser/errors [no test files] -? github.com/gtsteffaniak/filebrowser/files [no test files] -PASS -ok github.com/gtsteffaniak/filebrowser/fileutils 1.176s -2023/09/29 17:01:53 h: 401 -2023/09/29 17:01:53 h: 401 -2023/09/29 17:01:53 h: 401 -2023/09/29 17:01:53 h: 401 -2023/09/29 17:01:53 h: 401 -2023/09/29 17:01:53 h: 401 -PASS -ok github.com/gtsteffaniak/filebrowser/http 1.055s -PASS -ok github.com/gtsteffaniak/filebrowser/img 0.771s -goos: darwin -goarch: arm64 -pkg: github.com/gtsteffaniak/filebrowser/index -BenchmarkFillIndex-10 10 6355542 ns/op 12084 B/op 449 allocs/op -PASS -ok github.com/gtsteffaniak/filebrowser/index 0.653s -PASS -ok github.com/gtsteffaniak/filebrowser/rules 0.549s -PASS -ok github.com/gtsteffaniak/filebrowser/runner 0.661s -PASS -ok github.com/gtsteffaniak/filebrowser/settings 0.665s -? 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.654s -? github.com/gtsteffaniak/filebrowser/version [no test files] diff --git a/backend/cmd/hash.go b/backend/cmd/hash.go deleted file mode 100644 index 41718afa..00000000 --- a/backend/cmd/hash.go +++ /dev/null @@ -1,25 +0,0 @@ -package cmd - -import ( - "fmt" - - "github.com/spf13/cobra" - - "github.com/gtsteffaniak/filebrowser/users" -) - -func init() { - rootCmd.AddCommand(hashCmd) -} - -var hashCmd = &cobra.Command{ - Use: "hash ", - Short: "Hashes a password", - Long: `Hashes a password using bcrypt algorithm.`, - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - pwd, err := users.HashPwd(args[0]) - checkErr(err) - fmt.Println(pwd) - }, -} diff --git a/backend/cmd/root.go b/backend/cmd/root.go index ea70fd51..9a34a8e7 100644 --- a/backend/cmd/root.go +++ b/backend/cmd/root.go @@ -137,12 +137,23 @@ func quickSetup(d pythonData) { log.Fatal("username and password cannot be empty during quick setup") } user := &users.User{ - Username: username, - Password: password, - LockPassword: false, + Username: username, + Password: password, } settings.GlobalConfiguration.UserDefaults.Apply(user) user.Perm.Admin = true + user.DarkMode = true + user.ViewMode = "normal" + user.LockPassword = false + user.Perm = users.Permissions{ + Create: true, + Rename: true, + Modify: true, + Delete: true, + Share: true, + Download: true, + Admin: true, + } err = d.store.Users.Save(user) checkErr(err) } diff --git a/backend/cmd/users_add.go b/backend/cmd/users_add.go index 5eeb20e0..7a8349b6 100644 --- a/backend/cmd/users_add.go +++ b/backend/cmd/users_add.go @@ -16,12 +16,9 @@ var usersAddCmd = &cobra.Command{ Long: `Create a new user and add it to the database.`, Args: cobra.ExactArgs(2), //nolint:gomnd Run: python(func(cmd *cobra.Command, args []string, d pythonData) { - password, err := users.HashPwd(args[1]) - checkErr(err) - user := &users.User{ Username: args[0], - Password: password, + Password: args[1], LockPassword: mustGetBool(cmd.Flags(), "lockPassword"), } servSettings, err := d.store.Settings.GetServer() diff --git a/backend/filebrowser.yaml b/backend/filebrowser.yaml index bdf2dc63..08a8312c 100644 --- a/backend/filebrowser.yaml +++ b/backend/filebrowser.yaml @@ -3,9 +3,11 @@ server: baseURL: "/" root: "/Users/steffag/git/go" auth: - method: noauth + method: password signup: true userDefaults: + darkMode: true + disableSettings: false scope: "." hideDotfiles: true singleClick: false @@ -16,6 +18,4 @@ userDefaults: modify: true delete: true share: true - download: true -frontend: - theme: dark \ No newline at end of file + download: true \ No newline at end of file diff --git a/backend/http/auth.go b/backend/http/auth.go index 2a42e05b..a2f67895 100644 --- a/backend/http/auth.go +++ b/backend/http/auth.go @@ -12,6 +12,7 @@ import ( "github.com/golang-jwt/jwt/v4/request" "github.com/gtsteffaniak/filebrowser/errors" + "github.com/gtsteffaniak/filebrowser/settings" "github.com/gtsteffaniak/filebrowser/users" ) @@ -19,20 +20,8 @@ const ( TokenExpirationTime = time.Hour * 2 ) -type userInfo struct { - ID uint `json:"id"` - Locale string `json:"locale"` - ViewMode string `json:"viewMode"` - SingleClick bool `json:"singleClick"` - Perm users.Permissions `json:"perm"` - Commands []string `json:"commands"` - LockPassword bool `json:"lockPassword"` - HideDotfiles bool `json:"hideDotfiles"` - DateFormat bool `json:"dateFormat"` -} - type authToken struct { - User userInfo `json:"user"` + User users.User `json:"user"` jwt.RegisteredClaims } @@ -143,15 +132,9 @@ var signupHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, user := &users.User{ Username: info.Username, + Password: info.Password, } - - pwd, err := users.HashPwd(info.Password) - if err != nil { - return http.StatusInternalServerError, err - } - - user.Password = pwd - + settings.GlobalConfiguration.UserDefaults.Apply(user) userHome, err := d.settings.MakeUserDir(user.Username, user.Scope, d.server.Root) if err != nil { log.Printf("create user: failed to mkdir user home dir: [%s]", userHome) @@ -176,17 +159,7 @@ var renewHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User) (int, error) { claims := &authToken{ - User: userInfo{ - ID: user.ID, - Locale: user.Locale, - ViewMode: user.ViewMode, - SingleClick: user.SingleClick, - Perm: user.Perm, - LockPassword: user.LockPassword, - Commands: user.Commands, - HideDotfiles: user.HideDotfiles, - DateFormat: user.DateFormat, - }, + User: *user, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(time.Now()), ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpirationTime)), diff --git a/backend/http/search.go b/backend/http/search.go index 306506de..3ce8cc33 100644 --- a/backend/http/search.go +++ b/backend/http/search.go @@ -9,7 +9,6 @@ import ( var searchHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { response := []map[string]interface{}{} query := r.URL.Query().Get("query") - // Retrieve the User-Agent and X-Auth headers from the request sessionId := r.Header.Get("SessionId") index := *index.GetIndex() diff --git a/backend/http/share.go b/backend/http/share.go index 36f2e4c0..408cde49 100644 --- a/backend/http/share.go +++ b/backend/http/share.go @@ -129,7 +129,7 @@ var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request var token string if len(hash) > 0 { - tokenBuffer := make([]byte, 96) //nolint:gomnd + tokenBuffer := make([]byte, 24) //nolint:gomnd if _, err := rand.Read(tokenBuffer); err != nil { return http.StatusInternalServerError, err } diff --git a/backend/http/static.go b/backend/http/static.go index aef7bd0d..fa06de3a 100644 --- a/backend/http/static.go +++ b/backend/http/static.go @@ -40,7 +40,6 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys "LoginPage": auther.LoginPage(), "CSS": false, "ReCaptcha": false, - "Theme": d.settings.Frontend.Theme, "EnableThumbs": d.server.EnableThumbnails, "ResizePreview": d.server.ResizePreview, "EnableExec": d.server.EnableExec, diff --git a/backend/http/users.go b/backend/http/users.go index cdcc58f1..20765293 100644 --- a/backend/http/users.go +++ b/backend/http/users.go @@ -124,11 +124,6 @@ var userPostHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d * return http.StatusBadRequest, errors.ErrEmptyPassword } - req.Data.Password, err = users.HashPwd(req.Data.Password) - if err != nil { - return http.StatusInternalServerError, err - } - userHome, err := d.settings.MakeUserDir(req.Data.Username, req.Data.Scope, d.server.Root) if err != nil { log.Printf("create user: failed to mkdir user home dir: [%s]", userHome) @@ -184,7 +179,6 @@ var userPutHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request if !d.user.Perm.Admin && d.user.LockPassword { return http.StatusForbidden, nil } - req.Data.Password, err = users.HashPwd(req.Data.Password) if err != nil { return http.StatusInternalServerError, err diff --git a/backend/index/conditions.go b/backend/index/conditions.go index 1a1cff28..61c174c7 100644 --- a/backend/index/conditions.go +++ b/backend/index/conditions.go @@ -1,6 +1,7 @@ package index import ( + "mime" "regexp" "strconv" "strings" @@ -120,3 +121,35 @@ func updateSize(given string) int { return size } } + +func IsMatchingType(extension string, matchType string) bool { + mimetype := mime.TypeByExtension(extension) + if strings.HasPrefix(mimetype, matchType) { + return true + } + switch matchType { + case "doc": + return isDoc(extension) + case "archive": + return isArchive(extension) + } + return false +} + +func isDoc(extension string) bool { + for _, typefile := range documentTypes { + if extension == typefile { + return true + } + } + return false +} + +func isArchive(extension string) bool { + for _, typefile := range compressedFile { + if extension == typefile { + return true + } + } + return false +} diff --git a/backend/index/search_index.go b/backend/index/search_index.go index 34f644a4..d3ace10e 100644 --- a/backend/index/search_index.go +++ b/backend/index/search_index.go @@ -2,7 +2,6 @@ package index import ( "math/rand" - "mime" "os" "path/filepath" "sort" @@ -61,10 +60,11 @@ func (si *Index) Search(search string, scope string, sourceSession string) ([]st continue } if isDir { - pathName = pathName + "/" + fileListTypes[pathName+"/"] = fileType + } else { + fileListTypes[pathName] = fileType } matching = append(matching, pathName) - fileListTypes[pathName] = fileType count++ } } @@ -88,39 +88,33 @@ func scopedPathNameFilter(pathName string, scope string) string { return pathName } +var fileTypes = map[string]bool{ + "audio": false, + "image": false, + "video": false, + "doc": false, + "archive": false, + "dir": false, +} + func containsSearchTerm(pathName string, searchTerm string, options SearchOptions, isDir bool) (bool, map[string]bool) { conditions := options.Conditions path := getLastPathComponent(pathName) // Convert to lowercase once - lowerSearchTerm := searchTerm if !conditions["exact"] { path = strings.ToLower(path) - lowerSearchTerm = strings.ToLower(searchTerm) + searchTerm = strings.ToLower(searchTerm) } - if strings.Contains(path, lowerSearchTerm) { - // Reuse the fileTypes map and clear its values - fileTypes := map[string]bool{ - "audio": false, - "image": false, - "video": false, - "doc": false, - "archive": false, - "dir": false, - } + if strings.Contains(path, searchTerm) { // Calculate fileSize only if needed var fileSize int64 - if conditions["larger"] || conditions["smaller"] { - fileSize = getFileSize(pathName) - } matchesAllConditions := true extension := filepath.Ext(path) - mimetype := mime.TypeByExtension(extension) - fileTypes["audio"] = strings.HasPrefix(mimetype, "audio") - fileTypes["image"] = strings.HasPrefix(mimetype, "image") - fileTypes["video"] = strings.HasPrefix(mimetype, "video") - fileTypes["doc"] = isDoc(extension) - fileTypes["archive"] = isArchive(extension) + for k := range fileTypes { + fileTypes[k] = IsMatchingType(extension, k) + } fileTypes["dir"] = isDir + for t, v := range conditions { if t == "exact" { continue @@ -128,8 +122,14 @@ func containsSearchTerm(pathName string, searchTerm string, options SearchOption var matchesCondition bool switch t { case "larger": + if fileSize == 0 { + fileSize = getFileSize(pathName) + } matchesCondition = fileSize > int64(options.LargerThan)*bytesInMegabyte case "smaller": + if fileSize == 0 { + fileSize = getFileSize(pathName) + } matchesCondition = fileSize < int64(options.SmallerThan)*bytesInMegabyte default: matchesCondition = v == fileTypes[t] @@ -144,15 +144,6 @@ func containsSearchTerm(pathName string, searchTerm string, options SearchOption return false, map[string]bool{} } -func isDoc(extension string) bool { - for _, typefile := range documentTypes { - if extension == typefile { - return true - } - } - return false -} - func getFileSize(filepath string) int64 { fileInfo, err := os.Stat(rootPath + "/" + filepath) if err != nil { @@ -161,15 +152,6 @@ func getFileSize(filepath string) int64 { return fileInfo.Size() } -func isArchive(extension string) bool { - for _, typefile := range compressedFile { - if extension == typefile { - return true - } - } - return false -} - func getLastPathComponent(path string) string { // Use filepath.Base to extract the last component of the path return filepath.Base(path) diff --git a/backend/settings/config.go b/backend/settings/config.go index cca470db..b7f7ef51 100644 --- a/backend/settings/config.go +++ b/backend/settings/config.go @@ -61,14 +61,18 @@ func setDefaults() Settings { }, Auth: Auth{ Method: "password", + Signup: true, Recaptcha: Recaptcha{ Host: "", }, }, UserDefaults: UserDefaults{ - Scope: ".", - LockPassword: false, - HideDotfiles: true, + Scope: ".", + LockPassword: false, + HideDotfiles: true, + DarkMode: false, + DisableSettings: false, + Locale: "en", Permissions: users.Permissions{ Create: true, Rename: true, @@ -76,6 +80,7 @@ func setDefaults() Settings { Delete: true, Share: true, Download: true, + Admin: false, }, }, } @@ -83,6 +88,8 @@ func setDefaults() Settings { // Apply applies the default options to a user. func (d *UserDefaults) Apply(u *users.User) { + u.DisableSettings = d.DisableSettings + u.DarkMode = d.DarkMode u.Scope = d.Scope u.Locale = d.Locale u.ViewMode = d.ViewMode diff --git a/backend/settings/storage.go b/backend/settings/storage.go index 19c8fdcc..f54acc62 100644 --- a/backend/settings/storage.go +++ b/backend/settings/storage.go @@ -3,7 +3,6 @@ package settings import ( "github.com/gtsteffaniak/filebrowser/errors" "github.com/gtsteffaniak/filebrowser/rules" - "github.com/gtsteffaniak/filebrowser/users" ) // StorageBackend is a settings storage backend. @@ -59,7 +58,7 @@ func (s *Storage) Save(set *Settings) error { } if set.UserDefaults.ViewMode == "" { - set.UserDefaults.ViewMode = users.MosaicViewMode + set.UserDefaults.ViewMode = "normal" } if set.Rules == nil { diff --git a/backend/settings/structs.go b/backend/settings/structs.go index 298a1b6d..6afa9fda 100644 --- a/backend/settings/structs.go +++ b/backend/settings/structs.go @@ -61,13 +61,13 @@ type Frontend struct { DisableExternal bool `json:"disableExternal"` DisableUsedPercentage bool `json:"disableUsedPercentage"` Files string `json:"files"` - Theme string `json:"theme"` Color string `json:"color"` } // UserDefaults is a type that holds the default values // for some fields on User. type UserDefaults struct { + DarkMode bool `json:"darkMode"` LockPassword bool `json:"lockPassword"` DisableSettings bool `json:"disableSettings,omitempty"` Scope string `json:"scope"` diff --git a/backend/settings/testingConfig.yaml b/backend/settings/testingConfig.yaml index 9e874e0c..686eea6d 100644 --- a/backend/settings/testingConfig.yaml +++ b/backend/settings/testingConfig.yaml @@ -28,12 +28,13 @@ frontend: disableExternal: false disableUsedPercentage: true files: "" - theme: "" color: "" userDefaults: scope: "" locale: "" viewMode: "" + darkMode: true + disableSettings: false singleClick: true sorting: by: "" diff --git a/backend/storage/bolt/users.go b/backend/storage/bolt/users.go index 47876706..c4837136 100644 --- a/backend/storage/bolt/users.go +++ b/backend/storage/bolt/users.go @@ -2,6 +2,7 @@ package bolt import ( "fmt" + "log" "reflect" "github.com/asdine/storm/v3" @@ -73,7 +74,13 @@ func (st usersBackend) Update(user *users.User, fields ...string) error { } func (st usersBackend) Save(user *users.User) error { - err := st.db.Save(user) + log.Println("userinfo", user.Password) + pass, err := users.HashPwd(user.Password) + if err != nil { + return err + } + user.Password = pass + err = st.db.Save(user) if err == storm.ErrAlreadyExists { return errors.ErrExist } diff --git a/backend/users/password.go b/backend/users/password.go index d7ef250a..e5b5f72b 100644 --- a/backend/users/password.go +++ b/backend/users/password.go @@ -1,11 +1,14 @@ package users import ( + "log" + "golang.org/x/crypto/bcrypt" ) // HashPwd hashes a password. func HashPwd(password string) (string, error) { + log.Println("hashing password", password) bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) return string(bytes), err } diff --git a/backend/users/storage.go b/backend/users/storage.go index de939586..31aaa7ba 100644 --- a/backend/users/storage.go +++ b/backend/users/storage.go @@ -73,7 +73,7 @@ func (s *Storage) Gets(baseScope string) ([]*User, error) { // Update updates a user in the database. func (s *Storage) Update(user *User, fields ...string) error { - err := user.Clean("", fields...) + err := user.Clean("") if err != nil { return err } diff --git a/backend/users/users.go b/backend/users/users.go index 84a0cab9..50ef124b 100644 --- a/backend/users/users.go +++ b/backend/users/users.go @@ -6,16 +6,10 @@ import ( "github.com/spf13/afero" - "github.com/gtsteffaniak/filebrowser/errors" "github.com/gtsteffaniak/filebrowser/files" "github.com/gtsteffaniak/filebrowser/rules" ) -var ( - ListViewMode = "list" - MosaicViewMode = "mosaic" -) - type Permissions struct { Admin bool `json:"admin"` Execute bool `json:"execute"` @@ -29,21 +23,23 @@ type Permissions struct { // User describes a user. type User struct { - ID uint `storm:"id,increment" json:"id"` - Username string `storm:"unique" json:"username"` - Password string `json:"password"` - Scope string `json:"scope"` - Locale string `json:"locale"` - LockPassword bool `json:"lockPassword"` - ViewMode string `json:"viewMode"` - SingleClick bool `json:"singleClick"` - Perm Permissions `json:"perm"` - Commands []string `json:"commands"` - Sorting files.Sorting `json:"sorting"` - Fs afero.Fs `json:"-" yaml:"-"` - Rules []rules.Rule `json:"rules"` - HideDotfiles bool `json:"hideDotfiles"` - DateFormat bool `json:"dateFormat"` + DarkMode bool `json:"darkMode"` + DisableSettings bool `json:"disableSettings"` + ID uint `storm:"id,increment" json:"id"` + Username string `storm:"unique" json:"username"` + Password string `json:"password"` + Scope string `json:"scope"` + Locale string `json:"locale"` + LockPassword bool `json:"lockPassword"` + ViewMode string `json:"viewMode"` + SingleClick bool `json:"singleClick"` + Perm Permissions `json:"perm"` + Commands []string `json:"commands"` + Sorting files.Sorting `json:"sorting"` + Fs afero.Fs `json:"-" yaml:"-"` + Rules []rules.Rule `json:"rules"` + HideDotfiles bool `json:"hideDotfiles"` + DateFormat bool `json:"dateFormat"` } // GetRules implements rules.Provider. @@ -51,53 +47,11 @@ func (u *User) GetRules() []rules.Rule { return u.Rules } -var checkableFields = []string{ - "Username", - "Password", - "Scope", - "ViewMode", - "Commands", - "Sorting", - "Rules", -} - // Clean cleans up a user and verifies if all its fields // are alright to be saved. // //nolint:gocyclo -func (u *User) Clean(baseScope string, fields ...string) error { - if len(fields) == 0 { - fields = checkableFields - } - - for _, field := range fields { - switch field { - case "Username": - if u.Username == "" { - return errors.ErrEmptyUsername - } - case "Password": - if u.Password == "" { - return errors.ErrEmptyPassword - } - case "ViewMode": - if u.ViewMode == "" { - u.ViewMode = ListViewMode - } - case "Commands": - if u.Commands == nil { - u.Commands = []string{} - } - case "Sorting": - if u.Sorting.By == "" { - u.Sorting.By = "name" - } - case "Rules": - if u.Rules == nil { - u.Rules = []rules.Rule{} - } - } - } +func (u *User) Clean(baseScope string) error { if u.Fs == nil { scope := u.Scope diff --git a/backend/version/version.go b/backend/version/version.go index 99e9b441..fb0df987 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.2.0)" + Version = "(0.2.1)" // CommitSHA is the commmit sha. CommitSHA = "(unknown)" ) diff --git a/configuration.md b/configuration.md index dc5b5762..e8444243 100644 --- a/configuration.md +++ b/configuration.md @@ -40,6 +40,8 @@ frontend: theme: "" color: "" userDefaults: + settingsAllowed: true + darkMode: false scope: "" locale: "" viewMode: "" @@ -176,6 +178,10 @@ UserDefaults: ### UserDefaults configuration settings +- `darkMode`: Determines whether dark mode is enabled for the user (true or false) + +- `settingsAllowed`: Determines whether settings page is enabled for the user (true or false) + - `scope`: This is a scope of the permissions, "." or "./" means all directories, "./downloads" would mean only the downloads folder. - `locale`: This is the locale configuration. diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 172990b6..5ae6d569 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -35,7 +35,7 @@ "devDependencies": { "@vue/cli-service": "^5.0.8", "compression-webpack-plugin": "^10.0.0", - "eslint": "^8.50.0", + "eslint": "^8.51.0", "eslint-plugin-vue": "^9.17.0", "vue-template-compiler": "^2.6.10" } @@ -293,9 +293,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz", - "integrity": "sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", + "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -347,9 +347,9 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz", - "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", + "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3025,15 +3025,15 @@ } }, "node_modules/eslint": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz", - "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", + "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.50.0", + "@eslint/js": "8.51.0", "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -3774,12 +3774,12 @@ } }, "node_modules/flat-cache": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", - "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", "dev": true, "dependencies": { - "flatted": "^3.2.7", + "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" }, @@ -3970,9 +3970,9 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" }, "node_modules/globals": { - "version": "13.22.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz", - "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==", + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -4747,9 +4747,9 @@ } }, "node_modules/keyv": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", - "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "dependencies": { "json-buffer": "3.0.1" @@ -7974,9 +7974,9 @@ } }, "node_modules/vue-eslint-parser": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.1.tgz", - "integrity": "sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz", + "integrity": "sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==", "dev": true, "dependencies": { "debug": "^4.3.4", diff --git a/frontend/package.json b/frontend/package.json index b61cfece..e5288f61 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -39,7 +39,7 @@ "devDependencies": { "@vue/cli-service": "^5.0.8", "compression-webpack-plugin": "^10.0.0", - "eslint": "^8.50.0", + "eslint": "^8.51.0", "eslint-plugin-vue": "^9.17.0", "vue-template-compiler": "^2.6.10" }, diff --git a/frontend/public/index.html b/frontend/public/index.html index 00bd9650..d8cbd347 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -128,8 +128,8 @@ - [{[ if .Theme -]}] - + [{[ if .darkMode -]}] + [{[ end ]}] [{[ if .CSS -]}] diff --git a/frontend/public/themes/dark.css b/frontend/public/themes/dark.css deleted file mode 100644 index 42eb58ff..00000000 --- a/frontend/public/themes/dark.css +++ /dev/null @@ -1,220 +0,0 @@ -:root { - --background: #141D24; - --surfacePrimary: #20292F; - --surfaceSecondary: #3A4147; - --divider: rgba(255, 255, 255, 0.12); - --textPrimary: rgba(255, 255, 255, 0.87); - --textSecondary: rgba(255, 255, 255, 0.6); -} - -body { - background: var(--background); - color: var(--textPrimary); -} - -#loading { - background: var(--background); -} - -#login { - background: var(--background); -} - -header { - background: var(--surfacePrimary); -} - -@supports (backdrop-filter: none) { - header { - background: transparent; - backdrop-filter: blur(16px) invert(0.1); - } -} - -#search #input { - background: var(--surfaceSecondary); - 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); -} -#search #result { - background: var(--background); - color: var(--textPrimary); -} -#search .boxes h3 { - color: var(--textPrimary); -} - -.action { - color: var(--textPrimary) !important; -} -.action:hover { - background-color: rgba(255, 255, 255, .1); -} - -.action .counter { - border-color: var(--surfacePrimary); -} - -nav > div { - border-color: var(--divider); -} - -.breadcrumbs { - border-color: var(--divider); - color: var(--textPrimary) !important; -} -.breadcrumbs span { - color: var(--textPrimary) !important; -} -.breadcrumbs a:hover { - background-color: rgba(255, 255, 255, .1); -} - -#listing .item { - background: var(--surfacePrimary); - color: var(--textPrimary); - border-color: var(--divider) !important; -} - -#listing .item .modified { - color: var(--textSecondary); -} -#listing h2, -#listing.list .header span { - color: var(--textPrimary) !important; -} -#listing.list .header span { - color: var(--textPrimary); -} - -#listing.list .item.header { - background: var(--background); -} - -.message { - color: var(--textPrimary); -} - -.card { - background: var(--surfacePrimary); - color: var(--textPrimary); -} -.button--flat:hover { - background: var(--surfaceSecondary); -} - -.dashboard #nav ul li { - color: var(--textSecondary); -} -.dashboard #nav ul li:hover { - background: var(--surfaceSecondary); -} -#result-list { - background-color:#292929; -} - -.card h3, -.dashboard #nav, -.dashboard p label { - color: var(--textPrimary); -} -.card#share input, -.card#share select, -.input { - background: var(--surfaceSecondary); - color: var(--textPrimary); -} - -.input:hover, -.input:focus { - border-color: rgba(255, 255, 255, 0.15); -} -.input--red { - background: #73302D; -} - -.input--green { - background: #147A41; -} - -.dashboard #nav .wrapper, -.collapsible { - border-color: var(--divider); -} -.collapsible > label * { - color: var(--textPrimary); -} - -table th { - color: var(--textSecondary); -} - -.file-list li:hover { - background: var(--surfaceSecondary); -} -.file-list li:before { - color: var(--textSecondary); -} - -.shell { - background: var(--surfacePrimary); - color: var(--textPrimary); -} -.shell__result { - border-top: 1px solid var(--divider); -} - -#editor-container { - background: var(--background); -} - -#editor-container .bar { - background: var(--surfacePrimary); -} -nav { - background: var(--surfaceSecondary) !important; -} - -#file-selection { - background: var(--surfaceSecondary) !important; -} -#file-selection span { - color: var(--textPrimary) !important; -} -#dropdown { - background: var(--surfaceSecondary) !important; -} - -.share__box { - background: var(--surfacePrimary) !important; - color: var(--textPrimary); -} - -.share__box__element { - border-top-color: var(--divider); -} - -.helpButton { - background: var(--background); -} -.sizeInputWrapper { - background: var(--background); - color: white -} -.button-group button { - 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 5c4ac2b0..83193bb3 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,18 +1,20 @@ diff --git a/frontend/src/components/Search.vue b/frontend/src/components/Search.vue index c3cfe898..bed88010 100644 --- a/frontend/src/components/Search.vue +++ b/frontend/src/components/Search.vue @@ -1,5 +1,5 @@