diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b389be5f..846b08f8 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -13,7 +13,7 @@ jobs: name: Lint Frontend runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: pnpm/action-setup@v4 with: package_json_file: "frontend/package.json" @@ -31,7 +31,7 @@ jobs: name: Lint Backend runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: go-version: "1.25.x" @@ -43,7 +43,7 @@ jobs: name: Test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: go-version: "1.25.x" @@ -53,7 +53,7 @@ jobs: name: Build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: actions/setup-go@v6 @@ -77,7 +77,7 @@ jobs: if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: actions/setup-go@v6 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8eec9747..5b21ccba 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Install Task @@ -28,25 +28,25 @@ jobs: if: github.event_name == 'push' && github.ref == 'refs/heads/master' name: Build and Release Docs permissions: - contents: read - deployments: write - pull-requests: write + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Install Task uses: go-task/setup-task@v1 - name: Build site run: task docs - - name: Deploy to Cloudflare Pages - uses: cloudflare/wrangler-action@v3 + - name: Upload static files as artifact + uses: actions/upload-pages-artifact@v4 with: - apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - command: pages deploy www/public --project-name=${{ secrets.CLOUDFLARE_PROJECT_NAME }} - gitHubToken: ${{ secrets.GITHUB_TOKEN }} - + path: www/public + - name: Deploy to GitHub Pages + uses: actions/deploy-pages@v4 diff --git a/.golangci.yml b/.golangci.yml index 32d944f3..8819f48b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -2,8 +2,13 @@ version: "2" linters: default: standard + enable: + - gocritic + - govet + - revive exclusions: presets: - std-error-handling + - comments paths: - frontend/ diff --git a/.goreleaser.yml b/.goreleaser.yml index 57b7b44a..be192ef8 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -17,6 +17,7 @@ builds: - linux - windows - freebsd + - openbsd goarch: - amd64 - "386" @@ -30,6 +31,12 @@ builds: ignore: - goos: darwin goarch: "386" + # Experimental, may not work properly + - goos: openbsd + goarch: riscv64 + # Broken as of Go 1.24, deprecated as of Go 1.26 + - goos: windows + goarch: arm - goos: freebsd goarch: arm diff --git a/CHANGELOG.md b/CHANGELOG.md index a02ee24a..0f49dca9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,149 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [2.55.0](https://github.com/filebrowser/filebrowser/compare/v2.54.0...v2.55.0) (2026-01-18) + + +### Features + +* added cut, copy, paste and show command palette functions in header ([#5648](https://github.com/filebrowser/filebrowser/issues/5648)) ([785b7ab](https://github.com/filebrowser/filebrowser/commit/785b7abb7ba7a86cc0deae1052c319ff714c222c)) +* update translations ([#5677](https://github.com/filebrowser/filebrowser/issues/5677)) ([e7ea1ad](https://github.com/filebrowser/filebrowser/commit/e7ea1ad27d3d17e249489d3338be40bfea15e2a1)) + + +### Bug Fixes + +* prevent context menu clicks from clearing file selection ([#5681](https://github.com/filebrowser/filebrowser/issues/5681)) ([59ca0c3](https://github.com/filebrowser/filebrowser/commit/59ca0c340afc7774747c70ede9a5a5a3c9349d6b)) +* request current password when deleting users ([#5667](https://github.com/filebrowser/filebrowser/issues/5667)) ([cfa6c58](https://github.com/filebrowser/filebrowser/commit/cfa6c5864e5e7673aa9f3180e4964e0db92cc4da)) +* retain file selection when closing the editor ([#5693](https://github.com/filebrowser/filebrowser/issues/5693)) ([4094fb3](https://github.com/filebrowser/filebrowser/commit/4094fb359babac70e88d0ed4bfe3bd100744aad6)) + +## [2.54.0](https://github.com/filebrowser/filebrowser/compare/v2.53.1...v2.54.0) (2026-01-10) + + +### Features + +* add "redirect after copy/move" user setting ([#5662](https://github.com/filebrowser/filebrowser/issues/5662)) ([fda8a99](https://github.com/filebrowser/filebrowser/commit/fda8a992929b1466e75fb2813f2c4e293c12d244)) +* force file sync while uploading file ([#5668](https://github.com/filebrowser/filebrowser/issues/5668)) ([4fd18a3](https://github.com/filebrowser/filebrowser/commit/4fd18a382c31bbe7059d6733ffa371e70051865b)) +* update translations ([#5659](https://github.com/filebrowser/filebrowser/issues/5659)) ([464b581](https://github.com/filebrowser/filebrowser/commit/464b581953139c17e3276b774e381e4052827125)) + + +### Bug Fixes + +* clear selection by clicking on empty area ([#5663](https://github.com/filebrowser/filebrowser/issues/5663)) ([208535a](https://github.com/filebrowser/filebrowser/commit/208535a8cc23254de0013dfab9008486707ee6c2)) +* hide "change password form" in noauth setting ([#5652](https://github.com/filebrowser/filebrowser/issues/5652)) ([219582c](https://github.com/filebrowser/filebrowser/commit/219582c0b03fd90979b1d1398dba7919d086a23f)) + +## [2.53.1](https://github.com/filebrowser/filebrowser/compare/v2.53.0...v2.53.1) (2026-01-03) + + +### Bug Fixes + +* download path encoding file paths ([#5655](https://github.com/filebrowser/filebrowser/issues/5655)) ([ffa893e](https://github.com/filebrowser/filebrowser/commit/ffa893e9ac387a49dba5917a41df7c3b7ce120fc)) +* request a password to change sensitive user data ([#5629](https://github.com/filebrowser/filebrowser/issues/5629)) ([b8151a0](https://github.com/filebrowser/filebrowser/commit/b8151a038a1ea55afae8073b439b74e364cac12f)) + +## [2.53.0](https://github.com/filebrowser/filebrowser/compare/v2.52.0...v2.53.0) (2025-12-29) + + +### Features + +* add "disable image resolution calculation" flag ([#5638](https://github.com/filebrowser/filebrowser/issues/5638)) ([a2d80c6](https://github.com/filebrowser/filebrowser/commit/a2d80c62c1c17962e566f68fb7cac6960ed3e4cb)) +* support streaming response for search results ([#5630](https://github.com/filebrowser/filebrowser/issues/5630)) ([20bfd13](https://github.com/filebrowser/filebrowser/commit/20bfd131c6a4fca48a645b52171c2d1cc3ce92b7)) +* update translations ([a12a612](https://github.com/filebrowser/filebrowser/commit/a12a612970d6cc3dfbca1b35ef3a60a887a4effb)) +* update translations ([#5626](https://github.com/filebrowser/filebrowser/issues/5626)) ([f899756](https://github.com/filebrowser/filebrowser/commit/f89975603e29b9f1fc05aec58afb42bbd56ed696)) +* update translations ([#5631](https://github.com/filebrowser/filebrowser/issues/5631)) ([032d6c7](https://github.com/filebrowser/filebrowser/commit/032d6c7520a64686c9d9b1218562256f629b4703)) + + +### Bug Fixes + +* conversion of backslashes in file paths for archive creation ([#5637](https://github.com/filebrowser/filebrowser/issues/5637)) ([9595f39](https://github.com/filebrowser/filebrowser/commit/9595f3939c1c129ed875a47adcc4fbcfad9a0e65)) +* Don't crash on invalid config import ([#5640](https://github.com/filebrowser/filebrowser/issues/5640)) ([79d1aa9](https://github.com/filebrowser/filebrowser/commit/79d1aa9229b076ee8e3b71d6cf061fc90738f4da)) +* fix nil deref in config set command ([#5641](https://github.com/filebrowser/filebrowser/issues/5641)) ([60b1ee8](https://github.com/filebrowser/filebrowser/commit/60b1ee8bb9e18b21d7f2c04cb1cc90046cecd3e1)) + +## [2.52.0](https://github.com/filebrowser/filebrowser/compare/v2.51.2...v2.52.0) (2025-12-13) + + +### Features + +* sync translations with Transifex ([7fa3432](https://github.com/filebrowser/filebrowser/commit/7fa3432f25610bbb55a718bc709b9a7bf41d92f0)) +* update translations ([#5615](https://github.com/filebrowser/filebrowser/issues/5615)) ([3fdca6d](https://github.com/filebrowser/filebrowser/commit/3fdca6dfd9a18c3f4895b4ef3cbd216824dbb57a)) + + +### Bug Fixes + +* display the directory name in the shared folder view ([#5617](https://github.com/filebrowser/filebrowser/issues/5617)) ([6d4c867](https://github.com/filebrowser/filebrowser/commit/6d4c86767239dad4f09f30f48678f2f3a716eb12)) +* hide the context menu when changing the route ([#5613](https://github.com/filebrowser/filebrowser/issues/5613)) ([cf96657](https://github.com/filebrowser/filebrowser/commit/cf966578d8c6beab111b74f495bac6bdec173f41)) + +## [2.51.2](https://github.com/filebrowser/filebrowser/compare/v2.51.1...v2.51.2) (2025-12-07) + + +### Bug Fixes + +* **frontend:** add missing i18n strings ([c171599](https://github.com/filebrowser/filebrowser/commit/c1715992bda46517f801c1aa496df8a3b42a4e4d)) + +## [2.51.1](https://github.com/filebrowser/filebrowser/compare/v2.51.0...v2.51.1) (2025-12-07) + + +### Bug Fixes + +* **frontend:** csv viewer i18n strings ([4cbb4b7](https://github.com/filebrowser/filebrowser/commit/4cbb4b73af816104475f15c1d996640b56203602)) +* prevent the right-click from selecting multiple items when the "single-click" option is active ([#5608](https://github.com/filebrowser/filebrowser/issues/5608)) ([152f830](https://github.com/filebrowser/filebrowser/commit/152f8302f7cda21bde37692b175c22c124233f45)) + +## [2.51.0](https://github.com/filebrowser/filebrowser/compare/v2.50.0...v2.51.0) (2025-12-06) + + +### Features + +* update translations ([2d88c06](https://github.com/filebrowser/filebrowser/commit/2d88c067611e936056dbbf04247f1c1c709b2a09)) + + +### Bug Fixes + +* added column separator select (comma, semicolon and both) in CSV viewer ([#5604](https://github.com/filebrowser/filebrowser/issues/5604)) ([204a3f0](https://github.com/filebrowser/filebrowser/commit/204a3f0eeaa0c68781b60651bf27c4b27eac44e6)) + + +### Refactorings + +* cleanup package names ([#5605](https://github.com/filebrowser/filebrowser/issues/5605)) ([f029c30](https://github.com/filebrowser/filebrowser/commit/f029c3005e450cfbebb074c42dbdf65db9c8d56a)) + +## [2.50.0](https://github.com/filebrowser/filebrowser/compare/v2.49.0...v2.50.0) (2025-11-30) + + +### Features + +* configurable logout page URL for proxy/hook auth ([#3884](https://github.com/filebrowser/filebrowser/issues/3884)) ([b9ac45d](https://github.com/filebrowser/filebrowser/commit/b9ac45d5dac4b4eb2ba364629090fbf306cffd2b)) +* render CSVs as table ([#5569](https://github.com/filebrowser/filebrowser/issues/5569)) ([982405e](https://github.com/filebrowser/filebrowser/commit/982405ec944f94baf43594b0ed2f06329ff4e9ed)) +* update frontend/src/i18n/hr.json ([279a5cc](https://github.com/filebrowser/filebrowser/commit/279a5ccd1e8d7bde4568b63cb3c506af48b6c618)) +* update translations ([78e0395](https://github.com/filebrowser/filebrowser/commit/78e039596070a3a9e643a693cc99960c69dcfe92)) + + +### Bug Fixes + +* do not close editor if save failed ([701522a](https://github.com/filebrowser/filebrowser/commit/701522a0600cfa542469540ed764630c0ba1a732)), closes [#5591](https://github.com/filebrowser/filebrowser/issues/5591) + +## [2.49.0](https://github.com/filebrowser/filebrowser/compare/v2.48.2...v2.49.0) (2025-11-22) + + +### Features + +* add "copy download link to clipboard" button to Share prompt ([#5173](https://github.com/filebrowser/filebrowser/issues/5173)) ([d48f566](https://github.com/filebrowser/filebrowser/commit/d48f5665d6975c4cbbdf9be20dc2e0106db02f01)) +* add Bulgarian language ([8db2411](https://github.com/filebrowser/filebrowser/commit/8db2411cd43a23ae3292a817e3524cfdb5ae9b86)) +* Updates for project File Browser ([#5566](https://github.com/filebrowser/filebrowser/issues/5566)) ([54306bd](https://github.com/filebrowser/filebrowser/commit/54306bdc8700fac489326ae81e28ac5db0580d13)) + + +### Bug Fixes + +* display friendly error message for password validation on signup ([#5563](https://github.com/filebrowser/filebrowser/issues/5563)) ([6d5aa35](https://github.com/filebrowser/filebrowser/commit/6d5aa355e433d613e5a3ae137f410c63baeddf0f)) + +## [2.48.2](https://github.com/filebrowser/filebrowser/compare/v2.48.1...v2.48.2) (2025-11-18) + + +### Bug Fixes + +* add transitionary support for FB_BASEURL ([984ea7b](https://github.com/filebrowser/filebrowser/commit/984ea7b569e3bd33b6f91ebdf63684a618d51e94)) + + +### Refactorings + +* rename python for clarification ([fd7b70c](https://github.com/filebrowser/filebrowser/commit/fd7b70cf38ac67c8c9ff79f2e7fde5e2ec45a1de)) + ## [2.48.1](https://github.com/filebrowser/filebrowser/compare/v2.48.0...v2.48.1) (2025-11-17) diff --git a/Dockerfile b/Dockerfile index 8bd825da..92bbe1d4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ ## Multistage build: First stage fetches dependencies -FROM alpine:3.22 AS fetcher +FROM alpine:3.23 AS fetcher # install and copy ca-certificates, mailcap, and tini-static; download JSON.sh RUN apk update && \ diff --git a/Dockerfile.s6 b/Dockerfile.s6 index 2a1218b2..8b363cb3 100644 --- a/Dockerfile.s6 +++ b/Dockerfile.s6 @@ -1,4 +1,4 @@ -FROM ghcr.io/linuxserver/baseimage-alpine:3.22 +FROM ghcr.io/linuxserver/baseimage-alpine:3.23 RUN apk update && \ apk --no-cache add ca-certificates mailcap jq libcap diff --git a/auth/hook.go b/auth/hook.go index ec3c5bfb..0c5efac5 100644 --- a/auth/hook.go +++ b/auth/hook.go @@ -11,7 +11,7 @@ import ( "slices" "strings" - fbErrors "github.com/filebrowser/filebrowser/v2/errors" + fberrors "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/users" @@ -146,7 +146,7 @@ func (a *HookAuth) GetValues(s string) { // SaveUser updates the existing user or creates a new one when not found func (a *HookAuth) SaveUser() (*users.User, error) { u, err := a.Users.Get(a.Server.Root, a.Cred.Username) - if err != nil && !errors.Is(err, fbErrors.ErrNotExist) { + if err != nil && !errors.Is(err, fberrors.ErrNotExist) { return nil, err } @@ -158,16 +158,17 @@ func (a *HookAuth) SaveUser() (*users.User, error) { // 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, + 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, + RedirectAfterCopyMove: a.Settings.Defaults.RedirectAfterCopyMove, + Sorting: a.Settings.Defaults.Sorting, + Perm: a.Settings.Defaults.Perm, + Commands: a.Settings.Defaults.Commands, + HideDotfiles: a.Settings.Defaults.HideDotfiles, } u = a.GetUser(d) @@ -219,13 +220,14 @@ func (a *HookAuth) GetUser(d *users.User) *users.User { Download: isAdmin || a.Fields.GetBoolean("user.perm.download", d.Perm.Download), } user := users.User{ - ID: d.ID, - Username: d.Username, - 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))), - SingleClick: a.Fields.GetBoolean("user.singleClick", d.SingleClick), + ID: d.ID, + Username: d.Username, + 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))), + SingleClick: a.Fields.GetBoolean("user.singleClick", d.SingleClick), + RedirectAfterCopyMove: a.Fields.GetBoolean("user.redirectAfterCopyMove", d.RedirectAfterCopyMove), Sorting: files.Sorting{ Asc: a.Fields.GetBoolean("user.sorting.asc", d.Sorting.Asc), By: a.Fields.GetString("user.sorting.by", d.Sorting.By), @@ -251,6 +253,7 @@ var validHookFields = []string{ "user.locale", "user.viewMode", "user.singleClick", + "user.redirectAfterCopyMove", "user.sorting.by", "user.sorting.asc", "user.commands", diff --git a/auth/json.go b/auth/json.go index f779c476..2284dc7f 100644 --- a/auth/json.go +++ b/auth/json.go @@ -14,6 +14,10 @@ import ( // MethodJSONAuth is used to identify json auth. const MethodJSONAuth settings.AuthMethod = "json" +// dummyHash is used to prevent user enumeration timing attacks. +// It MUST be a valid bcrypt hash. +const dummyHash = "$2a$10$O4mEMeOL/nit6zqe.WQXauLRbRlzb3IgLHsa26Pf0N/GiU9b.wK1m" + type jsonCred struct { Password string `json:"password"` Username string `json:"username"` @@ -52,7 +56,17 @@ func (a JSONAuth) Auth(r *http.Request, usr users.Store, _ *settings.Settings, s } u, err := usr.Get(srv.Root, cred.Username) - if err != nil || !users.CheckPwd(cred.Password, u.Password) { + + hash := dummyHash + if err == nil { + hash = u.Password + } + + if !users.CheckPwd(cred.Password, hash) { + return nil, os.ErrPermission + } + + if err != nil { return nil, os.ErrPermission } diff --git a/auth/proxy.go b/auth/proxy.go index 301aa292..3f4a627c 100644 --- a/auth/proxy.go +++ b/auth/proxy.go @@ -4,7 +4,7 @@ import ( "errors" "net/http" - fbErrors "github.com/filebrowser/filebrowser/v2/errors" + fberrors "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/users" ) @@ -21,7 +21,7 @@ type ProxyAuth struct { func (a ProxyAuth) Auth(r *http.Request, usr users.Store, setting *settings.Settings, srv *settings.Server) (*users.User, error) { username := r.Header.Get(a.Header) user, err := usr.Get(srv.Root, username) - if errors.Is(err, fbErrors.ErrNotExist) { + if errors.Is(err, fberrors.ErrNotExist) { return a.createUser(usr, setting, srv, username) } return user, err diff --git a/cmd/cmds_add.go b/cmd/cmds_add.go index a4d17061..a209b83f 100644 --- a/cmd/cmds_add.go +++ b/cmd/cmds_add.go @@ -15,18 +15,18 @@ var cmdsAddCmd = &cobra.Command{ Short: "Add a command to run on a specific event", Long: `Add a command to run on a specific event.`, Args: cobra.MinimumNArgs(2), - RunE: python(func(_ *cobra.Command, args []string, d *pythonData) error { - s, err := d.store.Settings.Get() + RunE: withStore(func(_ *cobra.Command, args []string, st *store) error { + s, err := st.Settings.Get() if err != nil { return err } command := strings.Join(args[1:], " ") s.Commands[args[0]] = append(s.Commands[args[0]], command) - err = d.store.Settings.Save(s) + err = st.Settings.Save(s) if err != nil { return err } printEvents(s.Commands) return nil - }, pythonConfig{}), + }, storeOptions{}), } diff --git a/cmd/cmds_ls.go b/cmd/cmds_ls.go index ad700eb7..694be178 100644 --- a/cmd/cmds_ls.go +++ b/cmd/cmds_ls.go @@ -14,8 +14,8 @@ var cmdsLsCmd = &cobra.Command{ Short: "List all commands for each event", Long: `List all commands for each event.`, Args: cobra.NoArgs, - RunE: python(func(cmd *cobra.Command, _ []string, d *pythonData) error { - s, err := d.store.Settings.Get() + RunE: withStore(func(cmd *cobra.Command, _ []string, st *store) error { + s, err := st.Settings.Get() if err != nil { return err } @@ -35,5 +35,5 @@ var cmdsLsCmd = &cobra.Command{ } return nil - }, pythonConfig{}), + }, storeOptions{}), } diff --git a/cmd/cmds_rm.go b/cmd/cmds_rm.go index 34089388..861f495f 100644 --- a/cmd/cmds_rm.go +++ b/cmd/cmds_rm.go @@ -35,8 +35,8 @@ including 'index_end'.`, return nil }, - RunE: python(func(_ *cobra.Command, args []string, d *pythonData) error { - s, err := d.store.Settings.Get() + RunE: withStore(func(_ *cobra.Command, args []string, st *store) error { + s, err := st.Settings.Get() if err != nil { return err } @@ -55,11 +55,11 @@ including 'index_end'.`, } s.Commands[evt] = append(s.Commands[evt][:i], s.Commands[evt][f+1:]...) - err = d.store.Settings.Save(s) + err = st.Settings.Save(s) if err != nil { return err } printEvents(s.Commands) return nil - }, pythonConfig{}), + }, storeOptions{}), } diff --git a/cmd/config.go b/cmd/config.go index 550ab5c9..5b3314ed 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -2,7 +2,7 @@ package cmd import ( "encoding/json" - nerrors "errors" + "errors" "fmt" "os" "strings" @@ -12,7 +12,7 @@ import ( "github.com/spf13/pflag" "github.com/filebrowser/filebrowser/v2/auth" - "github.com/filebrowser/filebrowser/v2/errors" + fberrors "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/settings" ) @@ -45,6 +45,7 @@ func addConfigFlags(flags *pflag.FlagSet) { 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("auth.logoutPage", "", "url of custom logout page") 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") @@ -98,12 +99,12 @@ func getProxyAuth(flags *pflag.FlagSet, defaultAuther map[string]interface{}) (a return nil, err } - if header == "" { + if header == "" && defaultAuther != nil { header = defaultAuther["header"].(string) } if header == "" { - return nil, nerrors.New("you must set the flag 'auth.header' for method 'proxy'") + return nil, errors.New("you must set the flag 'auth.header' for method 'proxy'") } return &auth.ProxyAuth{Header: header}, nil @@ -162,7 +163,7 @@ func getHookAuth(flags *pflag.FlagSet, defaultAuther map[string]interface{}) (au } if command == "" { - return nil, nerrors.New("you must set the flag 'auth.command' for method 'hook'") + return nil, errors.New("you must set the flag 'auth.command' for method 'hook'") } return &auth.HookAuth{Command: command}, nil @@ -185,7 +186,7 @@ func getAuthentication(flags *pflag.FlagSet, defaults ...interface{}) (settings. case auth.MethodHookAuth: auther, err = getHookAuth(flags, defaultAuther) default: - return "", nil, errors.ErrInvalidAuthMethod + return "", nil, fberrors.ErrInvalidAuthMethod } if err != nil { @@ -201,6 +202,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut fmt.Fprintf(w, "Sign up:\t%t\n", set.Signup) fmt.Fprintf(w, "Hide Login Button:\t%t\n", set.HideLoginButton) fmt.Fprintf(w, "Create User Dir:\t%t\n", set.CreateUserDir) + fmt.Fprintf(w, "Logout Page:\t%s\n", set.LogoutPage) fmt.Fprintf(w, "Minimum Password Length:\t%d\n", set.MinimumPasswordLength) fmt.Fprintf(w, "Auth Method:\t%s\n", set.AuthMethod) fmt.Fprintf(w, "Shell:\t%s\t\n", strings.Join(set.Shell, " ")) @@ -238,6 +240,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut 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, "\tRedirect after Copy/Move:\t%t\n", set.Defaults.RedirectAfterCopyMove) fmt.Fprintf(w, "\tFile Creation Mode:\t%O\n", set.FileMode) fmt.Fprintf(w, "\tDirectory Creation Mode:\t%O\n", set.DirMode) fmt.Fprintf(w, "\tCommands:\t%s\n", strings.Join(set.Defaults.Commands, " ")) @@ -306,6 +309,9 @@ func getSettings(flags *pflag.FlagSet, set *settings.Settings, ser *settings.Ser case "disableTypeDetectionByHeader": ser.TypeDetectionByHeader, err = flags.GetBool(flag.Name) ser.TypeDetectionByHeader = !ser.TypeDetectionByHeader + case "disableImageResolutionCalc": + ser.ImageResolutionCal, err = flags.GetBool(flag.Name) + ser.ImageResolutionCal = !ser.ImageResolutionCal // Settings flags from [addConfigFlags] case "signup": @@ -328,6 +334,8 @@ func getSettings(flags *pflag.FlagSet, set *settings.Settings, ser *settings.Ser set.DirMode, err = getAndParseFileMode(flags, flag.Name) case "auth.method": hasAuth = true + case "auth.logoutPage": + set.LogoutPage, err = flags.GetString(flag.Name) case "branding.name": set.Branding.Name, err = flags.GetString(flag.Name) case "branding.theme": @@ -357,7 +365,7 @@ func getSettings(flags *pflag.FlagSet, set *settings.Settings, ser *settings.Ser flags.Visit(visit) } - err := nerrors.Join(errs...) + err := errors.Join(errs...) if err != nil { return nil, err } diff --git a/cmd/config_cat.go b/cmd/config_cat.go index 39b1f664..b8d2f48f 100644 --- a/cmd/config_cat.go +++ b/cmd/config_cat.go @@ -13,19 +13,19 @@ var configCatCmd = &cobra.Command{ Short: "Prints the configuration", Long: `Prints the configuration.`, Args: cobra.NoArgs, - RunE: python(func(_ *cobra.Command, _ []string, d *pythonData) error { - set, err := d.store.Settings.Get() + RunE: withStore(func(_ *cobra.Command, _ []string, st *store) error { + set, err := st.Settings.Get() if err != nil { return err } - ser, err := d.store.Settings.GetServer() + ser, err := st.Settings.GetServer() if err != nil { return err } - auther, err := d.store.Auth.Get(set.AuthMethod) + auther, err := st.Auth.Get(set.AuthMethod) if err != nil { return err } return printSettings(ser, set, auther) - }, pythonConfig{}), + }, storeOptions{}), } diff --git a/cmd/config_export.go b/cmd/config_export.go index 9877fb63..b19c10b6 100644 --- a/cmd/config_export.go +++ b/cmd/config_export.go @@ -15,18 +15,18 @@ var configExportCmd = &cobra.Command{ json or yaml file. This exported configuration can be changed, and imported again with 'config import' command.`, Args: jsonYamlArg, - RunE: python(func(_ *cobra.Command, args []string, d *pythonData) error { - settings, err := d.store.Settings.Get() + RunE: withStore(func(_ *cobra.Command, args []string, st *store) error { + settings, err := st.Settings.Get() if err != nil { return err } - server, err := d.store.Settings.GetServer() + server, err := st.Settings.GetServer() if err != nil { return err } - auther, err := d.store.Auth.Get(settings.AuthMethod) + auther, err := st.Auth.Get(settings.AuthMethod) if err != nil { return err } @@ -42,5 +42,5 @@ and imported again with 'config import' command.`, return err } return nil - }, pythonConfig{}), + }, storeOptions{}), } diff --git a/cmd/config_import.go b/cmd/config_import.go index 63d394d7..9a838721 100644 --- a/cmd/config_import.go +++ b/cmd/config_import.go @@ -34,11 +34,11 @@ database. The path must be for a json or yaml file.`, Args: jsonYamlArg, - RunE: python(func(_ *cobra.Command, args []string, d *pythonData) error { + RunE: withStore(func(_ *cobra.Command, args []string, st *store) error { var key []byte var err error - if d.databaseExisted { - settings, settingErr := d.store.Settings.Get() + if st.databaseExisted { + settings, settingErr := st.Settings.Get() if settingErr != nil { return settingErr } @@ -53,13 +53,17 @@ The path must be for a json or yaml file.`, return err } + if file.Settings == nil || file.Server == nil { + return errors.New("invalid configuration file: 'settings' or 'server' fields are missing. Please ensure you are importing a file generated by the 'config export' command") + } + file.Settings.Key = key - err = d.store.Settings.Save(file.Settings) + err = st.Settings.Save(file.Settings) if err != nil { return err } - err = d.store.Settings.SaveServer(file.Server) + err = st.Settings.SaveServer(file.Server) if err != nil { return err } @@ -98,13 +102,13 @@ The path must be for a json or yaml file.`, return autherErr } - err = d.store.Auth.Save(auther) + err = st.Auth.Save(auther) if err != nil { return err } return printSettings(file.Server, file.Settings, auther) - }, pythonConfig{allowsNoDatabase: true}), + }, storeOptions{allowsNoDatabase: true}), } func getAuther(sample auth.Auther, data interface{}) (interface{}, error) { diff --git a/cmd/config_init.go b/cmd/config_init.go index 2787f080..359d02a3 100644 --- a/cmd/config_init.go +++ b/cmd/config_init.go @@ -22,7 +22,7 @@ this options can be changed in the future with the command to the defaults when creating new users and you don't override the options.`, Args: cobra.NoArgs, - RunE: python(func(cmd *cobra.Command, _ []string, d *pythonData) error { + RunE: withStore(func(cmd *cobra.Command, _ []string, st *store) error { flags := cmd.Flags() // Initialize config @@ -36,17 +36,17 @@ override the options.`, } // Save updated config - err = d.store.Settings.Save(s) + err = st.Settings.Save(s) if err != nil { return err } - err = d.store.Settings.SaveServer(ser) + err = st.Settings.SaveServer(ser) if err != nil { return err } - err = d.store.Auth.Save(auther) + err = st.Auth.Save(auther) if err != nil { return err } @@ -57,5 +57,5 @@ Now add your first user via 'filebrowser users add' and then you just need to call the main command to boot up the server. `) return printSettings(ser, s, auther) - }, pythonConfig{expectsNoDatabase: true}), + }, storeOptions{expectsNoDatabase: true}), } diff --git a/cmd/config_set.go b/cmd/config_set.go index d25b6596..df357a02 100644 --- a/cmd/config_set.go +++ b/cmd/config_set.go @@ -15,21 +15,21 @@ var configSetCmd = &cobra.Command{ Long: `Updates the configuration. Set the flags for the options you want to change. Other options will remain unchanged.`, Args: cobra.NoArgs, - RunE: python(func(cmd *cobra.Command, _ []string, d *pythonData) error { + RunE: withStore(func(cmd *cobra.Command, _ []string, st *store) error { flags := cmd.Flags() // Read existing config - set, err := d.store.Settings.Get() + set, err := st.Settings.Get() if err != nil { return err } - ser, err := d.store.Settings.GetServer() + ser, err := st.Settings.GetServer() if err != nil { return err } - auther, err := d.store.Auth.Get(set.AuthMethod) + auther, err := st.Auth.Get(set.AuthMethod) if err != nil { return err } @@ -41,21 +41,21 @@ you want to change. Other options will remain unchanged.`, } // Save updated config - err = d.store.Auth.Save(auther) + err = st.Auth.Save(auther) if err != nil { return err } - err = d.store.Settings.Save(set) + err = st.Settings.Save(set) if err != nil { return err } - err = d.store.Settings.SaveServer(ser) + err = st.Settings.SaveServer(ser) if err != nil { return err } return printSettings(ser, set, auther) - }, pythonConfig{}), + }, storeOptions{}), } diff --git a/cmd/docs.go b/cmd/docs.go index 7f4f536e..d65a29be 100644 --- a/cmd/docs.go +++ b/cmd/docs.go @@ -35,7 +35,7 @@ var docsCmd = &cobra.Command{ rootCmd.Root().DisableAutoGenTag = true - err = doc.GenMarkdownTreeCustom(cmd.Root(), tempDir, func(f string) string { + err = doc.GenMarkdownTreeCustom(cmd.Root(), tempDir, func(_ string) string { return "" }, func(s string) string { return s diff --git a/cmd/root.go b/cmd/root.go index 6a44e2f3..981eec4f 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -54,12 +54,12 @@ var ( ) // TODO(remove): remove after July 2026. -func migrateFlagNames(f *pflag.FlagSet, name string) pflag.NormalizedName { +func migrateFlagNames(_ *pflag.FlagSet, name string) pflag.NormalizedName { if newName, ok := flagNamesMigrations[name]; ok { if !warnedFlags[name] { warnedFlags[name] = true - fmt.Printf("WARNING: Flag --%s has been deprecated, use --%s instead\n", name, newName) + log.Printf("DEPRECATION NOTICE: Flag --%s has been deprecated, use --%s instead\n", name, newName) } name = newName @@ -108,6 +108,7 @@ func addServerFlags(flags *pflag.FlagSet) { flags.Bool("disablePreviewResize", false, "disable resize of image previews") flags.Bool("disableExec", true, "disables Command Runner feature") flags.Bool("disableTypeDetectionByHeader", false, "disables type detection by reading file headers") + flags.Bool("disableImageResolutionCalc", false, "disables image resolution calculation by reading image files") } var rootCmd = &cobra.Command{ @@ -135,6 +136,11 @@ file named .filebrowser.{json, toml, yaml, yml} in the following directories: - $HOME/ - /etc/filebrowser/ +**Note:** Only the options listed below can be set via the config file or +environment variables. Other configuration options live exclusively in the +database and so they must be set by the "config set" or "config +import" commands. + The precedence of the configuration values are as follows: - Flags @@ -146,23 +152,23 @@ The precedence of the configuration values are as follows: Also, if the database path doesn't exist, File Browser will enter into the quick setup mode and a new database will be bootstrapped and a new user created with the credentials from options "username" and "password".`, - RunE: python(func(cmd *cobra.Command, _ []string, d *pythonData) error { - if !d.databaseExisted { - err := quickSetup(*d) + RunE: withViperAndStore(func(_ *cobra.Command, _ []string, v *viper.Viper, st *store) error { + if !st.databaseExisted { + err := quickSetup(v, st.Storage) if err != nil { return err } } // build img service - imgWorkersCount := d.viper.GetInt("imageProcessors") + imgWorkersCount := v.GetInt("imageProcessors") if imgWorkersCount < 1 { return errors.New("image resize workers count could not be < 1") } imageService := img.New(imgWorkersCount) var fileCache diskcache.Interface = diskcache.NewNoOp() - cacheDir := d.viper.GetString("cacheDir") + cacheDir := v.GetString("cacheDir") if cacheDir != "" { if err := os.MkdirAll(cacheDir, 0700); err != nil { return fmt.Errorf("can't make directory %s: %w", cacheDir, err) @@ -170,7 +176,7 @@ user created with the credentials from options "username" and "password".`, fileCache = diskcache.New(afero.NewOsFs(), cacheDir) } - server, err := getServerSettings(d.viper, d.store) + server, err := getServerSettings(v, st.Storage) if err != nil { return err } @@ -192,7 +198,7 @@ user created with the credentials from options "username" and "password".`, if err != nil { return err } - socketPerm := d.viper.GetUint32("socketPerm") + socketPerm := v.GetUint32("socketPerm") err = os.Chmod(server.Socket, os.FileMode(socketPerm)) if err != nil { return err @@ -221,7 +227,7 @@ user created with the credentials from options "username" and "password".`, panic(err) } - handler, err := fbhttp.NewHandler(imageService, fileCache, d.store, server, assetsFs) + handler, err := fbhttp.NewHandler(imageService, fileCache, st.Storage, server, assetsFs) if err != nil { return err } @@ -262,7 +268,7 @@ user created with the credentials from options "username" and "password".`, log.Println("Graceful shutdown complete.") return nil - }, pythonConfig{allowsNoDatabase: true}), + }, storeOptions{allowsNoDatabase: true}), } func getServerSettings(v *viper.Viper, st *storage.Storage) (*settings.Server, error) { @@ -309,6 +315,10 @@ func getServerSettings(v *viper.Viper, st *storage.Storage) (*settings.Server, e if v.IsSet("baseURL") { server.BaseURL = v.GetString("baseURL") + // TODO(remove): remove after July 2026. + } else if v := os.Getenv("FB_BASEURL"); v != "" { + log.Println("DEPRECATION NOTICE: Environment variable FB_BASEURL has been deprecated, use FB_BASE_URL instead") + server.BaseURL = v } if v.IsSet("tokenExpirationTime") { @@ -327,6 +337,10 @@ func getServerSettings(v *viper.Viper, st *storage.Storage) (*settings.Server, e server.TypeDetectionByHeader = !v.GetBool("disableTypeDetectionByHeader") } + if v.IsSet("disableImageResolutionCalc") { + server.ImageResolutionCal = !v.GetBool("disableImageResolutionCalc") + } + if v.IsSet("disableExec") { server.EnableExec = !v.GetBool("disableExec") } @@ -368,7 +382,7 @@ func setupLog(logMethod string) { } } -func quickSetup(d pythonData) error { +func quickSetup(v *viper.Viper, s *storage.Storage) error { log.Println("Performing quick setup") set := &settings.Settings{ @@ -379,10 +393,11 @@ func quickSetup(d pythonData) error { MinimumPasswordLength: settings.DefaultMinimumPasswordLength, UserHomeBasePath: settings.DefaultUsersHomeBasePath, Defaults: settings.UserDefaults{ - Scope: ".", - Locale: "en", - SingleClick: false, - AceEditorTheme: d.viper.GetString("defaults.aceEditorTheme"), + Scope: ".", + Locale: "en", + SingleClick: false, + RedirectAfterCopyMove: true, + AceEditorTheme: v.GetString("defaults.aceEditorTheme"), Perm: users.Permissions{ Admin: false, Execute: true, @@ -406,44 +421,45 @@ func quickSetup(d pythonData) error { } var err error - if d.viper.GetBool("noauth") { + if v.GetBool("noauth") { set.AuthMethod = auth.MethodNoAuth - err = d.store.Auth.Save(&auth.NoAuth{}) + err = s.Auth.Save(&auth.NoAuth{}) } else { set.AuthMethod = auth.MethodJSONAuth - err = d.store.Auth.Save(&auth.JSONAuth{}) + err = s.Auth.Save(&auth.JSONAuth{}) } if err != nil { return err } - err = d.store.Settings.Save(set) + err = s.Settings.Save(set) if err != nil { return err } ser := &settings.Server{ - BaseURL: d.viper.GetString("baseURL"), - Port: d.viper.GetString("port"), - Log: d.viper.GetString("log"), - TLSKey: d.viper.GetString("key"), - TLSCert: d.viper.GetString("cert"), - Address: d.viper.GetString("address"), - Root: d.viper.GetString("root"), - TokenExpirationTime: d.viper.GetString("tokenExpirationTime"), - EnableThumbnails: !d.viper.GetBool("disableThumbnails"), - ResizePreview: !d.viper.GetBool("disablePreviewResize"), - EnableExec: !d.viper.GetBool("disableExec"), - TypeDetectionByHeader: !d.viper.GetBool("disableTypeDetectionByHeader"), + BaseURL: v.GetString("baseURL"), + Port: v.GetString("port"), + Log: v.GetString("log"), + TLSKey: v.GetString("key"), + TLSCert: v.GetString("cert"), + Address: v.GetString("address"), + Root: v.GetString("root"), + TokenExpirationTime: v.GetString("tokenExpirationTime"), + EnableThumbnails: !v.GetBool("disableThumbnails"), + ResizePreview: !v.GetBool("disablePreviewResize"), + EnableExec: !v.GetBool("disableExec"), + TypeDetectionByHeader: !v.GetBool("disableTypeDetectionByHeader"), + ImageResolutionCal: !v.GetBool("disableImageResolutionCalc"), } - err = d.store.Settings.SaveServer(ser) + err = s.Settings.SaveServer(ser) if err != nil { return err } - username := d.viper.GetString("username") - password := d.viper.GetString("password") + username := v.GetString("username") + password := v.GetString("password") if password == "" { var pwd string @@ -474,5 +490,5 @@ func quickSetup(d pythonData) error { set.Defaults.Apply(user) user.Perm.Admin = true - return d.store.Users.Save(user) + return s.Users.Save(user) } diff --git a/cmd/rule_rm.go b/cmd/rule_rm.go index 26e801ae..8ed8f151 100644 --- a/cmd/rule_rm.go +++ b/cmd/rule_rm.go @@ -40,7 +40,7 @@ including 'index_end'.`, return nil }, - RunE: python(func(cmd *cobra.Command, args []string, d *pythonData) error { + RunE: withStore(func(cmd *cobra.Command, args []string, st *store) error { i, err := strconv.Atoi(args[0]) if err != nil { return err @@ -55,14 +55,14 @@ including 'index_end'.`, user := func(u *users.User) error { u.Rules = append(u.Rules[:i], u.Rules[f+1:]...) - return d.store.Users.Save(u) + return st.Users.Save(u) } global := func(s *settings.Settings) error { s.Rules = append(s.Rules[:i], s.Rules[f+1:]...) - return d.store.Settings.Save(s) + return st.Settings.Save(s) } - return runRules(d.store, cmd, user, global) - }, pythonConfig{}), + return runRules(st.Storage, cmd, user, global) + }, storeOptions{}), } diff --git a/cmd/rules_add.go b/cmd/rules_add.go index d58a6987..3b34d940 100644 --- a/cmd/rules_add.go +++ b/cmd/rules_add.go @@ -21,7 +21,7 @@ var rulesAddCmd = &cobra.Command{ Short: "Add a global rule or user rule", Long: `Add a global rule or user rule.`, Args: cobra.ExactArgs(1), - RunE: python(func(cmd *cobra.Command, args []string, d *pythonData) error { + RunE: withStore(func(cmd *cobra.Command, args []string, st *store) error { flags := cmd.Flags() allow, err := flags.GetBool("allow") @@ -53,14 +53,14 @@ var rulesAddCmd = &cobra.Command{ user := func(u *users.User) error { u.Rules = append(u.Rules, rule) - return d.store.Users.Save(u) + return st.Users.Save(u) } global := func(s *settings.Settings) error { s.Rules = append(s.Rules, rule) - return d.store.Settings.Save(s) + return st.Settings.Save(s) } - return runRules(d.store, cmd, user, global) - }, pythonConfig{}), + return runRules(st.Storage, cmd, user, global) + }, storeOptions{}), } diff --git a/cmd/rules_ls.go b/cmd/rules_ls.go index 67a279dc..9aa073d0 100644 --- a/cmd/rules_ls.go +++ b/cmd/rules_ls.go @@ -13,7 +13,7 @@ var rulesLsCommand = &cobra.Command{ Short: "List global rules or user specific rules", Long: `List global rules or user specific rules.`, Args: cobra.NoArgs, - RunE: python(func(cmd *cobra.Command, _ []string, d *pythonData) error { - return runRules(d.store, cmd, nil, nil) - }, pythonConfig{}), + RunE: withStore(func(cmd *cobra.Command, _ []string, st *store) error { + return runRules(st.Storage, cmd, nil, nil) + }, storeOptions{}), } diff --git a/cmd/users.go b/cmd/users.go index 86434a42..66487862 100644 --- a/cmd/users.go +++ b/cmd/users.go @@ -30,13 +30,14 @@ func printUsers(usrs []*users.User) { fmt.Fprintln(w, "ID\tUsername\tScope\tLocale\tV. Mode\tS.Click\tAdmin\tExecute\tCreate\tRename\tModify\tDelete\tShare\tDownload\tPwd Lock") for _, u := range usrs { - fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t\n", + fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t\n", u.ID, u.Username, u.Scope, u.Locale, u.ViewMode, u.SingleClick, + u.RedirectAfterCopyMove, u.Perm.Admin, u.Perm.Execute, u.Perm.Create, @@ -77,6 +78,7 @@ func addUserFlags(flags *pflag.FlagSet) { flags.String("locale", "en", "locale for users") flags.String("viewMode", string(users.ListViewMode), "view mode for users") flags.Bool("singleClick", false, "use single clicks only") + flags.Bool("redirectAfterCopyMove", false, "redirect to destination after copy/move") flags.Bool("dateFormat", false, "use date format (true for absolute time, false for relative)") flags.Bool("hideDotfiles", false, "hide dotfiles") flags.String("aceEditorTheme", "", "ace editor's syntax highlighting theme for users") @@ -110,6 +112,8 @@ func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all defaults.ViewMode, err = getAndParseViewMode(flags) case "singleClick": defaults.SingleClick, err = flags.GetBool(flag.Name) + case "redirectAfterCopyMove": + defaults.RedirectAfterCopyMove, err = flags.GetBool(flag.Name) case "aceEditorTheme": defaults.AceEditorTheme, err = flags.GetString(flag.Name) case "perm.admin": diff --git a/cmd/users_add.go b/cmd/users_add.go index bfc70069..daf59aa3 100644 --- a/cmd/users_add.go +++ b/cmd/users_add.go @@ -16,9 +16,9 @@ var usersAddCmd = &cobra.Command{ Short: "Create a new user", Long: `Create a new user and add it to the database.`, Args: cobra.ExactArgs(2), - RunE: python(func(cmd *cobra.Command, args []string, d *pythonData) error { + RunE: withStore(func(cmd *cobra.Command, args []string, st *store) error { flags := cmd.Flags() - s, err := d.store.Settings.Get() + s, err := st.Settings.Get() if err != nil { return err } @@ -54,14 +54,14 @@ var usersAddCmd = &cobra.Command{ s.Defaults.Apply(user) - servSettings, err := d.store.Settings.GetServer() + servSettings, err := st.Settings.GetServer() if err != nil { return err } // since getUserDefaults() polluted s.Defaults.Scope // which makes the Scope not the one saved in the db // we need the right s.Defaults.Scope here - s2, err := d.store.Settings.Get() + s2, err := st.Settings.Get() if err != nil { return err } @@ -72,11 +72,11 @@ var usersAddCmd = &cobra.Command{ } user.Scope = userHome - err = d.store.Users.Save(user) + err = st.Users.Save(user) if err != nil { return err } printUsers([]*users.User{user}) return nil - }, pythonConfig{}), + }, storeOptions{}), } diff --git a/cmd/users_export.go b/cmd/users_export.go index d6009a37..9bbec6d8 100644 --- a/cmd/users_export.go +++ b/cmd/users_export.go @@ -14,8 +14,8 @@ var usersExportCmd = &cobra.Command{ Long: `Export all users to a json or yaml file. Please indicate the path to the file where you want to write the users.`, Args: jsonYamlArg, - RunE: python(func(_ *cobra.Command, args []string, d *pythonData) error { - list, err := d.store.Users.Gets("") + RunE: withStore(func(_ *cobra.Command, args []string, st *store) error { + list, err := st.Users.Gets("") if err != nil { return err } @@ -25,5 +25,5 @@ path to the file where you want to write the users.`, return err } return nil - }, pythonConfig{}), + }, storeOptions{}), } diff --git a/cmd/users_find.go b/cmd/users_find.go index 0dea071a..09bc8d47 100644 --- a/cmd/users_find.go +++ b/cmd/users_find.go @@ -26,7 +26,7 @@ var usersLsCmd = &cobra.Command{ RunE: findUsers, } -var findUsers = python(func(_ *cobra.Command, args []string, d *pythonData) error { +var findUsers = withStore(func(_ *cobra.Command, args []string, st *store) error { var ( list []*users.User user *users.User @@ -36,14 +36,14 @@ var findUsers = python(func(_ *cobra.Command, args []string, d *pythonData) erro if len(args) == 1 { username, id := parseUsernameOrID(args[0]) if username != "" { - user, err = d.store.Users.Get("", username) + user, err = st.Users.Get("", username) } else { - user, err = d.store.Users.Get("", id) + user, err = st.Users.Get("", id) } list = []*users.User{user} } else { - list, err = d.store.Users.Gets("") + list, err = st.Users.Gets("") } if err != nil { @@ -51,4 +51,4 @@ var findUsers = python(func(_ *cobra.Command, args []string, d *pythonData) erro } printUsers(list) return nil -}, pythonConfig{}) +}, storeOptions{}) diff --git a/cmd/users_import.go b/cmd/users_import.go index d08889df..73effca6 100644 --- a/cmd/users_import.go +++ b/cmd/users_import.go @@ -25,7 +25,7 @@ file. You can use this command to import new users to your installation. For that, just don't place their ID on the files list or set it to 0.`, Args: jsonYamlArg, - RunE: python(func(cmd *cobra.Command, args []string, d *pythonData) error { + RunE: withStore(func(cmd *cobra.Command, args []string, st *store) error { flags := cmd.Flags() fd, err := os.Open(args[0]) if err != nil { @@ -52,7 +52,7 @@ list or set it to 0.`, } if replace { - oldUsers, userImportErr := d.store.Users.Gets("") + oldUsers, userImportErr := st.Users.Gets("") if userImportErr != nil { return userImportErr } @@ -63,7 +63,7 @@ list or set it to 0.`, } for _, user := range oldUsers { - err = d.store.Users.Delete(user.ID) + err = st.Users.Delete(user.ID) if err != nil { return err } @@ -76,7 +76,7 @@ list or set it to 0.`, } for _, user := range list { - onDB, err := d.store.Users.Get("", user.ID) + onDB, err := st.Users.Get("", user.ID) // User exists in DB. if err == nil { @@ -88,7 +88,7 @@ list or set it to 0.`, // with the new username. If there is, print an error and cancel the // operation if user.Username != onDB.Username { - if conflictuous, err := d.store.Users.Get("", user.Username); err == nil { + if conflictuous, err := st.Users.Get("", user.Username); err == nil { return usernameConflictError(user.Username, conflictuous.ID, user.ID) } } @@ -98,13 +98,13 @@ list or set it to 0.`, user.ID = 0 } - err = d.store.Users.Save(user) + err = st.Users.Save(user) if err != nil { return err } } return nil - }, pythonConfig{}), + }, storeOptions{}), } func usernameConflictError(username string, originalID, newID uint) error { diff --git a/cmd/users_rm.go b/cmd/users_rm.go index 55b973f4..492a55c3 100644 --- a/cmd/users_rm.go +++ b/cmd/users_rm.go @@ -15,14 +15,14 @@ var usersRmCmd = &cobra.Command{ Short: "Delete a user by username or id", Long: `Delete a user by username or id`, Args: cobra.ExactArgs(1), - RunE: python(func(_ *cobra.Command, args []string, d *pythonData) error { + RunE: withStore(func(_ *cobra.Command, args []string, st *store) error { username, id := parseUsernameOrID(args[0]) var err error if username != "" { - err = d.store.Users.Delete(username) + err = st.Users.Delete(username) } else { - err = d.store.Users.Delete(id) + err = st.Users.Delete(id) } if err != nil { @@ -30,5 +30,5 @@ var usersRmCmd = &cobra.Command{ } fmt.Println("user deleted successfully") return nil - }, pythonConfig{}), + }, storeOptions{}), } diff --git a/cmd/users_update.go b/cmd/users_update.go index 59854a81..e9a484fc 100644 --- a/cmd/users_update.go +++ b/cmd/users_update.go @@ -21,7 +21,7 @@ var usersUpdateCmd = &cobra.Command{ Long: `Updates an existing user. Set the flags for the options you want to change.`, Args: cobra.ExactArgs(1), - RunE: python(func(cmd *cobra.Command, args []string, d *pythonData) error { + RunE: withStore(func(cmd *cobra.Command, args []string, st *store) error { flags := cmd.Flags() username, id := parseUsernameOrID(args[0]) password, err := flags.GetString("password") @@ -34,7 +34,7 @@ options you want to change.`, return err } - s, err := d.store.Settings.Get() + s, err := st.Settings.Get() if err != nil { return err } @@ -43,22 +43,23 @@ options you want to change.`, user *users.User ) if id != 0 { - user, err = d.store.Users.Get("", id) + user, err = st.Users.Get("", id) } else { - user, err = d.store.Users.Get("", username) + user, err = st.Users.Get("", username) } if err != nil { return 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, + Scope: user.Scope, + Locale: user.Locale, + ViewMode: user.ViewMode, + SingleClick: user.SingleClick, + RedirectAfterCopyMove: user.RedirectAfterCopyMove, + Perm: user.Perm, + Sorting: user.Sorting, + Commands: user.Commands, } err = getUserDefaults(flags, &defaults, false) @@ -70,6 +71,7 @@ options you want to change.`, user.Locale = defaults.Locale user.ViewMode = defaults.ViewMode user.SingleClick = defaults.SingleClick + user.RedirectAfterCopyMove = defaults.RedirectAfterCopyMove user.Perm = defaults.Perm user.Commands = defaults.Commands user.Sorting = defaults.Sorting @@ -99,11 +101,11 @@ options you want to change.`, } } - err = d.store.Users.Update(user) + err = st.Users.Update(user) if err != nil { return err } printUsers([]*users.User{user}) return nil - }, pythonConfig{}), + }, storeOptions{}), } diff --git a/cmd/utils.go b/cmd/utils.go index a136db10..ee637fa3 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -36,6 +36,7 @@ func getAndParseFileMode(flags *pflag.FlagSet, name string) (fs.FileMode, error) if err != nil { return 0, err } + return fs.FileMode(b), nil } @@ -131,55 +132,46 @@ func initViper(cmd *cobra.Command) (*viper.Viper, error) { return v, nil } -type cobraFunc func(cmd *cobra.Command, args []string) error -type pythonFunc func(cmd *cobra.Command, args []string, data *pythonData) error +type store struct { + *storage.Storage + databaseExisted bool +} -type pythonConfig struct { +type storeOptions struct { expectsNoDatabase bool allowsNoDatabase bool } -type pythonData struct { - databaseExisted bool - viper *viper.Viper - store *storage.Storage -} +type cobraFunc func(cmd *cobra.Command, args []string) error -func python(fn pythonFunc, cfg pythonConfig) cobraFunc { +// withViperAndStore initializes Viper and the storage.Store and passes them to the callback function. +// This function should only be used by [withStore] and the root command. No other command should call +// this function directly. +func withViperAndStore(fn func(cmd *cobra.Command, args []string, v *viper.Viper, store *store) error, options storeOptions) cobraFunc { return func(cmd *cobra.Command, args []string) error { v, err := initViper(cmd) if err != nil { return err } - data := &pythonData{databaseExisted: true} - path := v.GetString("database") - - // Only make the viper instance available to the root command (filebrowser). - // This is to make sure that we don't make the mistake of using it somewhere - // else. - if cmd.Name() == "filebrowser" { - data.viper = v - } - - absPath, err := filepath.Abs(path) + path, err := filepath.Abs(v.GetString("database")) if err != nil { return err } exists, err := dbExists(path) - if err != nil { + switch { + case err != nil: return err - } else if exists && cfg.expectsNoDatabase { - log.Fatal(absPath + " already exists") - } else if !exists && !cfg.expectsNoDatabase && !cfg.allowsNoDatabase { - log.Fatal(absPath + " does not exist. Please run 'filebrowser config init' first.") - } else if !exists && !cfg.expectsNoDatabase { - log.Println("Warning: filebrowser.db can't be found. Initialing in " + strings.TrimSuffix(absPath, "filebrowser.db")) + case exists && options.expectsNoDatabase: + log.Fatal(path + " already exists") + case !exists && !options.expectsNoDatabase && !options.allowsNoDatabase: + log.Fatal(path + " does not exist. Please run 'filebrowser config init' first.") + case !exists && !options.expectsNoDatabase: + log.Println("WARNING: filebrowser.db can't be found. Initialing in " + strings.TrimSuffix(path, "filebrowser.db")) } - log.Println("Using database: " + absPath) - data.databaseExisted = exists + log.Println("Using database: " + path) db, err := storm.Open(path, storm.BoltOptions(databasePermissions, nil)) if err != nil { @@ -187,15 +179,26 @@ func python(fn pythonFunc, cfg pythonConfig) cobraFunc { } defer db.Close() - data.store, err = bolt.NewStorage(db) + storage, err := bolt.NewStorage(db) if err != nil { return err } - return fn(cmd, args, data) + store := &store{ + Storage: storage, + databaseExisted: exists, + } + + return fn(cmd, args, v, store) } } +func withStore(fn func(cmd *cobra.Command, args []string, store *store) error, options storeOptions) cobraFunc { + return withViperAndStore(func(cmd *cobra.Command, args []string, _ *viper.Viper, store *store) error { + return fn(cmd, args, store) + }, options) +} + func marshal(filename string, data interface{}) error { fd, err := os.Create(filename) if err != nil { diff --git a/diskcache/file_cache_test.go b/diskcache/file_cache_test.go index 9a41052e..c6c750c0 100644 --- a/diskcache/file_cache_test.go +++ b/diskcache/file_cache_test.go @@ -25,12 +25,12 @@ func TestFileCache(t *testing.T) { // store new key err := cache.Store(ctx, key, []byte(value)) require.NoError(t, err) - checkValue(t, ctx, fs, filepath.Join(cacheRoot, cachedFilePath), cache, key, value) + checkValue(ctx, t, fs, filepath.Join(cacheRoot, cachedFilePath), cache, key, value) // update existing key err = cache.Store(ctx, key, []byte(newValue)) require.NoError(t, err) - checkValue(t, ctx, fs, filepath.Join(cacheRoot, cachedFilePath), cache, key, newValue) + checkValue(ctx, t, fs, filepath.Join(cacheRoot, cachedFilePath), cache, key, newValue) // delete key err = cache.Delete(ctx, key) @@ -40,7 +40,7 @@ func TestFileCache(t *testing.T) { require.False(t, exists) } -func checkValue(t *testing.T, ctx context.Context, fs afero.Fs, fileFullPath string, cache *FileCache, key, wantValue string) { +func checkValue(ctx context.Context, t *testing.T, fs afero.Fs, fileFullPath string, cache *FileCache, key, wantValue string) { t.Helper() // check actual file content b, err := afero.ReadFile(fs, fileFullPath) diff --git a/errors/errors.go b/errors/errors.go index 5fd760c2..748354a8 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -1,4 +1,4 @@ -package errors +package fberrors import ( "errors" @@ -6,22 +6,23 @@ import ( ) var ( - ErrEmptyKey = errors.New("empty key") - ErrExist = errors.New("the resource already exists") - ErrNotExist = errors.New("the resource does not exist") - ErrEmptyPassword = errors.New("password is empty") - ErrEasyPassword = errors.New("password is too easy") - ErrEmptyUsername = errors.New("username is empty") - ErrEmptyRequest = errors.New("empty request") - ErrScopeIsRelative = errors.New("scope is a relative path") - ErrInvalidDataType = errors.New("invalid data type") - ErrIsDirectory = errors.New("file is directory") - ErrInvalidOption = errors.New("invalid option") - ErrInvalidAuthMethod = errors.New("invalid auth method") - ErrPermissionDenied = errors.New("permission denied") - ErrInvalidRequestParams = errors.New("invalid request params") - ErrSourceIsParent = errors.New("source is parent") - ErrRootUserDeletion = errors.New("user with id 1 can't be deleted") + ErrEmptyKey = errors.New("empty key") + ErrExist = errors.New("the resource already exists") + ErrNotExist = errors.New("the resource does not exist") + ErrEmptyPassword = errors.New("password is empty") + ErrEasyPassword = errors.New("password is too easy") + ErrEmptyUsername = errors.New("username is empty") + ErrEmptyRequest = errors.New("empty request") + ErrScopeIsRelative = errors.New("scope is a relative path") + ErrInvalidDataType = errors.New("invalid data type") + ErrIsDirectory = errors.New("file is directory") + ErrInvalidOption = errors.New("invalid option") + ErrInvalidAuthMethod = errors.New("invalid auth method") + ErrPermissionDenied = errors.New("permission denied") + ErrInvalidRequestParams = errors.New("invalid request params") + ErrSourceIsParent = errors.New("source is parent") + ErrRootUserDeletion = errors.New("user with id 1 can't be deleted") + ErrCurrentPasswordIncorrect = errors.New("the current password is incorrect") ) type ErrShortPassword struct { diff --git a/files/file.go b/files/file.go index 0f96440e..2ba432dc 100644 --- a/files/file.go +++ b/files/file.go @@ -23,7 +23,7 @@ import ( "github.com/spf13/afero" - fbErrors "github.com/filebrowser/filebrowser/v2/errors" + fberrors "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/rules" ) @@ -60,6 +60,7 @@ type FileOptions struct { Modify bool Expand bool ReadHeader bool + CalcImgRes bool Token string Checker rules.Checker Content bool @@ -90,13 +91,13 @@ func NewFileInfo(opts *FileOptions) (*FileInfo, error) { if opts.Expand { if file.IsDir { - if err := file.readListing(opts.Checker, opts.ReadHeader); err != nil { + if err := file.readListing(opts.Checker, opts.ReadHeader, opts.CalcImgRes); err != nil { return nil, err } return file, nil } - err = file.detectType(opts.Modify, opts.Content, true) + err = file.detectType(opts.Modify, opts.Content, true, opts.CalcImgRes) if err != nil { return nil, err } @@ -168,7 +169,7 @@ func stat(opts *FileOptions) (*FileInfo, error) { // algorithm. The checksums data is saved on File object. func (i *FileInfo) Checksum(algo string) error { if i.IsDir { - return fbErrors.ErrIsDirectory + return fberrors.ErrIsDirectory } if i.Checksums == nil { @@ -193,7 +194,7 @@ func (i *FileInfo) Checksum(algo string) error { case "sha512": h = sha512.New() default: - return fbErrors.ErrInvalidOption + return fberrors.ErrInvalidOption } _, err = io.Copy(h, reader) @@ -218,7 +219,7 @@ func (i *FileInfo) RealPath() string { return i.Path } -func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error { +func (i *FileInfo) detectType(modify, saveContent, readHeader bool, calcImgRes bool) error { if IsNamedPipe(i.Mode) { i.Type = "blob" return nil @@ -249,11 +250,13 @@ func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error { return nil case strings.HasPrefix(mimetype, "image"): i.Type = "image" - resolution, err := calculateImageResolution(i.Fs, i.Path) - if err != nil { - log.Printf("Error calculating image resolution: %v", err) - } else { - i.Resolution = resolution + if calcImgRes { + resolution, err := calculateImageResolution(i.Fs, i.Path) + if err != nil { + log.Printf("Error calculating image resolution: %v", err) + } else { + i.Resolution = resolution + } } return nil case strings.HasSuffix(mimetype, "pdf"): @@ -387,7 +390,7 @@ func (i *FileInfo) addSubtitle(fPath string) { i.Subtitles = append(i.Subtitles, fPath) } -func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error { +func (i *FileInfo) readListing(checker rules.Checker, readHeader bool, calcImgRes bool) error { afs := &afero.Afero{Fs: i.Fs} dir, err := afs.ReadDir(i.Path) if err != nil { @@ -434,7 +437,7 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error { currentDir: dir, } - if !file.IsDir && strings.HasPrefix(mime.TypeByExtension(file.Extension), "image/") { + if !file.IsDir && strings.HasPrefix(mime.TypeByExtension(file.Extension), "image/") && calcImgRes { resolution, err := calculateImageResolution(file.Fs, file.Path) if err != nil { log.Printf("Error calculating resolution for image %s: %v", file.Path, err) @@ -451,7 +454,7 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error { if isInvalidLink { file.Type = "invalid_link" } else { - err := file.detectType(true, false, readHeader) + err := file.detectType(true, false, readHeader, calcImgRes) if err != nil { return err } diff --git a/frontend/index.html b/frontend/index.html index b3de6804..19308a95 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -39,6 +39,7 @@ DisableUsedPercentage: false, EnableExec: true, EnableThumbs: true, + LogoutPage: "", LoginPage: true, Name: "", NoAuth: false, diff --git a/frontend/package.json b/frontend/package.json index ade91ff1..44144b3a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -71,5 +71,5 @@ "vite-plugin-compression2": "^2.3.1", "vue-tsc": "^3.1.3" }, - "packageManager": "pnpm@10.22.0+sha512.bf049efe995b28f527fd2b41ae0474ce29186f7edcb3bf545087bd61fbbebb2bf75362d1307fda09c2d288e1e499787ac12d4fcb617a974718a6051f2eee741c" + "packageManager": "pnpm@10.28.0+sha512.05df71d1421f21399e053fde567cea34d446fa02c76571441bfc1c7956e98e363088982d940465fd34480d4d90a0668bc12362f8aa88000a64e83d0b0e47be48" } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index db9de505..d3b79699 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -10,22 +10,22 @@ importers: dependencies: '@chenfengyuan/vue-number-input': specifier: ^2.0.1 - version: 2.0.1(vue@3.5.24(typescript@5.9.3)) + version: 2.0.1(vue@3.5.26(typescript@5.9.3)) '@vueuse/core': specifier: ^14.0.0 - version: 14.0.0(vue@3.5.24(typescript@5.9.3)) + version: 14.1.0(vue@3.5.26(typescript@5.9.3)) '@vueuse/integrations': specifier: ^14.0.0 - version: 14.0.0(focus-trap@7.6.2)(jwt-decode@4.0.0)(vue@3.5.24(typescript@5.9.3)) + version: 14.1.0(focus-trap@7.6.2)(jwt-decode@4.0.0)(vue@3.5.26(typescript@5.9.3)) ace-builds: specifier: ^1.43.2 - version: 1.43.4 + version: 1.43.5 dayjs: specifier: ^1.11.13 version: 1.11.19 dompurify: specifier: ^3.2.6 - version: 3.3.0 + version: 3.3.1 epubjs: specifier: ^0.3.93 version: 0.3.93 @@ -40,10 +40,10 @@ importers: version: 4.0.0 lodash-es: specifier: ^4.17.21 - version: 4.17.21 + version: 4.17.22 marked: specifier: ^17.0.0 - version: 17.0.0 + version: 17.0.1 material-icons: specifier: ^1.13.14 version: 1.13.14 @@ -52,13 +52,13 @@ importers: version: 8.0.1 pinia: specifier: ^3.0.4 - version: 3.0.4(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3)) + version: 3.0.4(typescript@5.9.3)(vue@3.5.26(typescript@5.9.3)) pretty-bytes: specifier: ^7.1.0 version: 7.1.0 qrcode.vue: specifier: ^3.6.0 - version: 3.6.0(vue@3.5.24(typescript@5.9.3)) + version: 3.6.0(vue@3.5.26(typescript@5.9.3)) tus-js-client: specifier: ^4.3.1 version: 4.3.1 @@ -73,95 +73,95 @@ importers: version: 0.2.30 videojs-mobile-ui: specifier: ^1.1.1 - version: 1.1.1(video.js@8.23.4) + version: 1.1.3(video.js@8.23.4) vue: specifier: ^3.5.17 - version: 3.5.24(typescript@5.9.3) + version: 3.5.26(typescript@5.9.3) vue-final-modal: specifier: ^4.5.5 - version: 4.5.5(@vueuse/core@14.0.0(vue@3.5.24(typescript@5.9.3)))(@vueuse/integrations@14.0.0(focus-trap@7.6.2)(jwt-decode@4.0.0)(vue@3.5.24(typescript@5.9.3)))(focus-trap@7.6.2)(vue@3.5.24(typescript@5.9.3)) + version: 4.5.5(@vueuse/core@14.1.0(vue@3.5.26(typescript@5.9.3)))(@vueuse/integrations@14.1.0(focus-trap@7.6.2)(jwt-decode@4.0.0)(vue@3.5.26(typescript@5.9.3)))(focus-trap@7.6.2)(vue@3.5.26(typescript@5.9.3)) vue-i18n: specifier: ^11.1.10 - version: 11.1.12(vue@3.5.24(typescript@5.9.3)) + version: 11.2.8(vue@3.5.26(typescript@5.9.3)) vue-lazyload: specifier: ^3.0.0 version: 3.0.0 vue-reader: specifier: ^1.2.17 - version: 1.3.3 + version: 1.3.4 vue-router: specifier: ^4.5.1 - version: 4.6.3(vue@3.5.24(typescript@5.9.3)) + version: 4.6.4(vue@3.5.26(typescript@5.9.3)) vue-toastification: specifier: ^2.0.0-rc.5 - version: 2.0.0-rc.5(vue@3.5.24(typescript@5.9.3)) + version: 2.0.0-rc.5(vue@3.5.26(typescript@5.9.3)) devDependencies: '@intlify/unplugin-vue-i18n': specifier: ^11.0.1 - version: 11.0.1(@vue/compiler-dom@3.5.24)(eslint@9.39.1)(rollup@4.52.5)(typescript@5.9.3)(vue-i18n@11.1.12(vue@3.5.24(typescript@5.9.3)))(vue@3.5.24(typescript@5.9.3)) + version: 11.0.3(@vue/compiler-dom@3.5.26)(eslint@9.39.2)(rollup@4.55.1)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3)) '@tsconfig/node24': specifier: ^24.0.2 - version: 24.0.2 + version: 24.0.4 '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 '@types/node': specifier: ^24.10.1 - version: 24.10.1 + version: 24.10.9 '@typescript-eslint/eslint-plugin': specifier: ^8.37.0 - version: 8.46.4(@typescript-eslint/parser@8.37.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) + version: 8.53.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) '@vitejs/plugin-legacy': specifier: ^7.2.1 - version: 7.2.1(terser@5.44.1)(vite@7.2.2(@types/node@24.10.1)(terser@5.44.1)(yaml@2.7.0)) + version: 7.2.1(terser@5.46.0)(vite@7.3.1(@types/node@24.10.9)(terser@5.46.0)(yaml@2.8.2)) '@vitejs/plugin-vue': specifier: ^6.0.1 - version: 6.0.1(vite@7.2.2(@types/node@24.10.1)(terser@5.44.1)(yaml@2.7.0))(vue@3.5.24(typescript@5.9.3)) + version: 6.0.3(vite@7.3.1(@types/node@24.10.9)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3)) '@vue/eslint-config-prettier': specifier: ^10.2.0 - version: 10.2.0(eslint@9.39.1)(prettier@3.6.2) + version: 10.2.0(eslint@9.39.2)(prettier@3.8.0) '@vue/eslint-config-typescript': specifier: ^14.6.0 - version: 14.6.0(eslint-plugin-vue@10.5.1(@typescript-eslint/parser@8.37.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(vue-eslint-parser@10.2.0(eslint@9.39.1)))(eslint@9.39.1)(typescript@5.9.3) + version: 14.6.0(eslint-plugin-vue@10.7.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(vue-eslint-parser@10.2.0(eslint@9.39.2)))(eslint@9.39.2)(typescript@5.9.3) '@vue/tsconfig': specifier: ^0.8.1 - version: 0.8.1(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3)) + version: 0.8.1(typescript@5.9.3)(vue@3.5.26(typescript@5.9.3)) autoprefixer: specifier: ^10.4.21 - version: 10.4.22(postcss@8.5.6) + version: 10.4.23(postcss@8.5.6) eslint: specifier: ^9.31.0 - version: 9.39.1 + version: 9.39.2 eslint-config-prettier: specifier: ^10.1.5 - version: 10.1.8(eslint@9.39.1) + version: 10.1.8(eslint@9.39.2) eslint-plugin-prettier: specifier: ^5.5.1 - version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.1))(eslint@9.39.1)(prettier@3.6.2) + version: 5.5.5(eslint-config-prettier@10.1.8(eslint@9.39.2))(eslint@9.39.2)(prettier@3.8.0) eslint-plugin-vue: specifier: ^10.5.1 - version: 10.5.1(@typescript-eslint/parser@8.37.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(vue-eslint-parser@10.2.0(eslint@9.39.1)) + version: 10.7.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(vue-eslint-parser@10.2.0(eslint@9.39.2)) postcss: specifier: ^8.5.6 version: 8.5.6 prettier: specifier: ^3.6.2 - version: 3.6.2 + version: 3.8.0 terser: specifier: ^5.43.1 - version: 5.44.1 + version: 5.46.0 typescript: specifier: ^5.9.3 version: 5.9.3 vite: specifier: ^7.2.2 - version: 7.2.2(@types/node@24.10.1)(terser@5.44.1)(yaml@2.7.0) + version: 7.3.1(@types/node@24.10.9)(terser@5.46.0)(yaml@2.8.2) vite-plugin-compression2: specifier: ^2.3.1 - version: 2.3.1(rollup@4.52.5) + version: 2.4.0(rollup@4.55.1) vue-tsc: specifier: ^3.1.3 - version: 3.1.4(typescript@5.9.3) + version: 3.2.2(typescript@5.9.3) packages: @@ -665,158 +665,314 @@ packages: peerDependencies: vue: ^3.0.0 - '@esbuild/aix-ppc64@0.25.11': - resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.11': - resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.11': - resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.11': - resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.11': - resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.11': - resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.11': - resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.11': - resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.11': - resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.11': - resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.11': - resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.11': - resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.11': - resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.11': - resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.11': - resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.11': - resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.11': - resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.11': - resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.11': - resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.11': - resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.11': - resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.11': - resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.11': - resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.11': - resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.11': - resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.11': - resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -827,6 +983,12 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.12.2': resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -843,12 +1005,12 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.3.1': - resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + '@eslint/eslintrc@3.3.3': + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.39.1': - resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': @@ -875,8 +1037,8 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@intlify/bundle-utils@11.0.1': - resolution: {integrity: sha512-5l10G5wE2cQRsZMS9y0oSFMOLW5IG/SgbkIUltqnwF1EMRrRbUAHFiPabXdGTHeexCsMTcxj/1w9i0rzjJU9IQ==} + '@intlify/bundle-utils@11.0.3': + resolution: {integrity: sha512-dURCDz1rQXwAb1+Hv4NDit6aZSRaAt4zUYBPEeaDCe3FSs8dMtdF6kEvgd9JwsYFSTAHcvbTs2CqwBjjt9Ltsw==} engines: {node: '>= 20'} peerDependencies: petite-vue-i18n: '*' @@ -887,20 +1049,24 @@ packages: vue-i18n: optional: true - '@intlify/core-base@11.1.12': - resolution: {integrity: sha512-whh0trqRsSqVLNEUCwU59pyJZYpU8AmSWl8M3Jz2Mv5ESPP6kFh4juas2NpZ1iCvy7GlNRffUD1xr84gceimjg==} + '@intlify/core-base@11.2.8': + resolution: {integrity: sha512-nBq6Y1tVkjIUsLsdOjDSJj4AsjvD0UG3zsg9Fyc+OivwlA/oMHSKooUy9tpKj0HqZ+NWFifweHavdljlBLTwdA==} engines: {node: '>= 16'} - '@intlify/message-compiler@11.1.12': - resolution: {integrity: sha512-Fv9iQSJoJaXl4ZGkOCN1LDM3trzze0AS2zRz2EHLiwenwL6t0Ki9KySYlyr27yVOj5aVz0e55JePO+kELIvfdQ==} + '@intlify/message-compiler@11.2.8': + resolution: {integrity: sha512-A5n33doOjmHsBtCN421386cG1tWp5rpOjOYPNsnpjIJbQ4POF0QY2ezhZR9kr0boKwaHjbOifvyQvHj2UTrDFQ==} engines: {node: '>= 16'} - '@intlify/shared@11.1.12': - resolution: {integrity: sha512-Om86EjuQtA69hdNj3GQec9ZC0L0vPSAnXzB3gP/gyJ7+mA7t06d9aOAiqMZ+xEOsumGP4eEBlfl8zF2LOTzf2A==} + '@intlify/shared@11.2.2': + resolution: {integrity: sha512-OtCmyFpSXxNu/oET/aN6HtPCbZ01btXVd0f3w00YsHOb13Kverk1jzA2k47pAekM55qbUw421fvPF1yxZ+gicw==} engines: {node: '>= 16'} - '@intlify/unplugin-vue-i18n@11.0.1': - resolution: {integrity: sha512-nH5NJdNjy/lO6Ne8LDtZzv4SbpVsMhPE+LbvBDmMeIeJDiino8sOJN2QB3MXzTliYTnqe3aB9Fw5+LJ/XVaXCg==} + '@intlify/shared@11.2.8': + resolution: {integrity: sha512-l6e4NZyUgv8VyXXH4DbuucFOBmxLF56C/mqh2tvApbzl2Hrhi1aTDcuv5TKdxzfHYmpO3UB0Cz04fgDT9vszfw==} + engines: {node: '>= 16'} + + '@intlify/unplugin-vue-i18n@11.0.3': + resolution: {integrity: sha512-iQuik0nXfdVZ5ab+IEyBFEuvMQ213zfbUpBXaEdHPk8DV+qB2CT/SdFuDhfUDRRBZc/e0qoLlfmc9urhnRYVWw==} engines: {node: '>= 20'} peerDependencies: petite-vue-i18n: '*' @@ -965,8 +1131,8 @@ packages: resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@rolldown/pluginutils@1.0.0-beta.29': - resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==} + '@rolldown/pluginutils@1.0.0-beta.53': + resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} @@ -977,118 +1143,133 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.52.5': - resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} + '@rollup/rollup-android-arm-eabi@4.55.1': + resolution: {integrity: sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.52.5': - resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} + '@rollup/rollup-android-arm64@4.55.1': + resolution: {integrity: sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.52.5': - resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} + '@rollup/rollup-darwin-arm64@4.55.1': + resolution: {integrity: sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.52.5': - resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} + '@rollup/rollup-darwin-x64@4.55.1': + resolution: {integrity: sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.52.5': - resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} + '@rollup/rollup-freebsd-arm64@4.55.1': + resolution: {integrity: sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.52.5': - resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} + '@rollup/rollup-freebsd-x64@4.55.1': + resolution: {integrity: sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.52.5': - resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.55.1': + resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.52.5': - resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} + '@rollup/rollup-linux-arm-musleabihf@4.55.1': + resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.52.5': - resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} + '@rollup/rollup-linux-arm64-gnu@4.55.1': + resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.52.5': - resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} + '@rollup/rollup-linux-arm64-musl@4.55.1': + resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.52.5': - resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} + '@rollup/rollup-linux-loong64-gnu@4.55.1': + resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.52.5': - resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} + '@rollup/rollup-linux-loong64-musl@4.55.1': + resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.55.1': + resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.52.5': - resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} + '@rollup/rollup-linux-ppc64-musl@4.55.1': + resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.55.1': + resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.52.5': - resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} + '@rollup/rollup-linux-riscv64-musl@4.55.1': + resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.52.5': - resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} + '@rollup/rollup-linux-s390x-gnu@4.55.1': + resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.52.5': - resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} + '@rollup/rollup-linux-x64-gnu@4.55.1': + resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.52.5': - resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} + '@rollup/rollup-linux-x64-musl@4.55.1': + resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.52.5': - resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} + '@rollup/rollup-openbsd-x64@4.55.1': + resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.55.1': + resolution: {integrity: sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.52.5': - resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} + '@rollup/rollup-win32-arm64-msvc@4.55.1': + resolution: {integrity: sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.52.5': - resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} + '@rollup/rollup-win32-ia32-msvc@4.55.1': + resolution: {integrity: sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.52.5': - resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} + '@rollup/rollup-win32-x64-gnu@4.55.1': + resolution: {integrity: sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.52.5': - resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} + '@rollup/rollup-win32-x64-msvc@4.55.1': + resolution: {integrity: sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==} cpu: [x64] os: [win32] - '@tsconfig/node24@24.0.2': - resolution: {integrity: sha512-CNeOLUPI9PjbBc1DSIqb3GF/u+3kX/TDe9DKCzoI62mYI4dEDrMQ0r/9+SfYACP4UNMbiTlz7n8H7Rx/xTisQg==} + '@tsconfig/node24@24.0.4': + resolution: {integrity: sha512-2A933l5P5oCbv6qSxHs7ckKwobs8BDAe9SJ/Xr2Hy+nDlwmLE1GhFh/g/vXGRZWgxBg9nX/5piDtHR9Dkw/XuA==} '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -1106,8 +1287,8 @@ packages: '@types/lodash@4.17.13': resolution: {integrity: sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==} - '@types/node@24.10.1': - resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + '@types/node@24.10.9': + resolution: {integrity: sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==} '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -1123,11 +1304,11 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/eslint-plugin@8.46.4': - resolution: {integrity: sha512-R48VhmTJqplNyDxCyqqVkFSZIx1qX6PzwqgcXn1olLrzxcSBDlOsbtcnQuQhNtnNiJ4Xe5gREI1foajYaYU2Vg==} + '@typescript-eslint/eslint-plugin@8.53.0': + resolution: {integrity: sha512-eEXsVvLPu8Z4PkFibtuFJLJOTAV/nPdgtSjkGoPpddpFk3/ym2oy97jynY6ic2m6+nc5M8SE1e9v/mHKsulcJg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.46.4 + '@typescript-eslint/parser': ^8.53.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' @@ -1144,8 +1325,14 @@ packages: peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/project-service@8.46.4': - resolution: {integrity: sha512-nPiRSKuvtTN+no/2N1kt2tUh/HoFzeEgOm9fQ6XQk4/ApGqjx0zFIIaLJ6wooR1HIoozvj2j6vTi/1fgAz7UYQ==} + '@typescript-eslint/project-service@8.49.0': + resolution: {integrity: sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.53.0': + resolution: {integrity: sha512-Bl6Gdr7NqkqIP5yP9z1JU///Nmes4Eose6L1HwpuVHwScgDPPuEWbUVhvlZmb8hy0vX9syLk5EGNL700WcBlbg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' @@ -1154,8 +1341,12 @@ packages: resolution: {integrity: sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.46.4': - resolution: {integrity: sha512-tMDbLGXb1wC+McN1M6QeDx7P7c0UWO5z9CXqp7J8E+xGcJuUuevWKxuG8j41FoweS3+L41SkyKKkia16jpX7CA==} + '@typescript-eslint/scope-manager@8.49.0': + resolution: {integrity: sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/scope-manager@8.53.0': + resolution: {integrity: sha512-kWNj3l01eOGSdVBnfAF2K1BTh06WS0Yet6JUgb9Cmkqaz3Jlu0fdVUjj9UI8gPidBWSMqDIglmEXifSgDT/D0g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.37.0': @@ -1164,8 +1355,14 @@ packages: peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/tsconfig-utils@8.46.4': - resolution: {integrity: sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A==} + '@typescript-eslint/tsconfig-utils@8.49.0': + resolution: {integrity: sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/tsconfig-utils@8.53.0': + resolution: {integrity: sha512-K6Sc0R5GIG6dNoPdOooQ+KtvT5KCKAvTcY8h2rIuul19vxH5OTQk7ArKkd4yTzkw66WnNY0kPPzzcmWA+XRmiA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' @@ -1177,8 +1374,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/type-utils@8.46.4': - resolution: {integrity: sha512-V4QC8h3fdT5Wro6vANk6eojqfbv5bpwHuMsBcJUJkqs2z5XnYhJzyz9Y02eUmF9u3PgXEUiOt4w4KHR3P+z0PQ==} + '@typescript-eslint/type-utils@8.53.0': + resolution: {integrity: sha512-BBAUhlx7g4SmcLhn8cnbxoxtmS7hcq39xKCgiutL3oNx1TaIp+cny51s8ewnKMpVUKQUGb41RAUWZ9kxYdovuw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1188,8 +1385,12 @@ packages: resolution: {integrity: sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.46.4': - resolution: {integrity: sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w==} + '@typescript-eslint/types@8.49.0': + resolution: {integrity: sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/types@8.53.0': + resolution: {integrity: sha512-Bmh9KX31Vlxa13+PqPvt4RzKRN1XORYSLlAE+sO1i28NkisGbTtSLFVB3l7PWdHtR3E0mVMuC7JilWJ99m2HxQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.37.0': @@ -1198,8 +1399,14 @@ packages: peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/typescript-estree@8.46.4': - resolution: {integrity: sha512-7oV2qEOr1d4NWNmpXLR35LvCfOkTNymY9oyW+lUHkmCno7aOmIf/hMaydnJBUTBMRCOGZh8YjkFOc8dadEoNGA==} + '@typescript-eslint/typescript-estree@8.49.0': + resolution: {integrity: sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/typescript-estree@8.53.0': + resolution: {integrity: sha512-pw0c0Gdo7Z4xOG987u3nJ8akL9093yEEKv8QTJ+Bhkghj1xyj8cgPaavlr9rq8h7+s6plUJ4QJYw2gCZodqmGw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' @@ -1211,8 +1418,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@8.46.4': - resolution: {integrity: sha512-AbSv11fklGXV6T28dp2Me04Uw90R2iJ30g2bgLz529Koehrmkbs1r7paFqr1vPCZi7hHwYxYtxfyQMRC8QaVSg==} + '@typescript-eslint/utils@8.53.0': + resolution: {integrity: sha512-XDY4mXTez3Z1iRDI5mbRhH4DFSt46oaIFsLg+Zn97+sYrXACziXSQcSelMybnVZ5pa1P6xYkPr5cMJyunM1ZDA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1222,8 +1429,12 @@ packages: resolution: {integrity: sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.46.4': - resolution: {integrity: sha512-/++5CYLQqsO9HFGLI7APrxBJYo+5OCMpViuhV8q5/Qa3o5mMrF//eQHks+PXcsAVaLdn817fMuS7zqoXNNZGaw==} + '@typescript-eslint/visitor-keys@8.49.0': + resolution: {integrity: sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/visitor-keys@8.53.0': + resolution: {integrity: sha512-LZ2NqIHFhvFwxG0qZeLL9DvdNAHPGCY5dIRwBhyYeU+LfLhcStE1ImjsuTG/WaVh3XysGaeLW8Rqq7cGkPCFvw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@videojs/http-streaming@3.17.2': @@ -1246,33 +1457,33 @@ packages: terser: ^5.16.0 vite: ^7.0.0 - '@vitejs/plugin-vue@6.0.1': - resolution: {integrity: sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==} + '@vitejs/plugin-vue@6.0.3': + resolution: {integrity: sha512-TlGPkLFLVOY3T7fZrwdvKpjprR3s4fxRln0ORDo1VQ7HHyxJwTlrjKU3kpVWTlaAjIEuCTokmjkZnr8Tpc925w==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 vue: ^3.2.25 - '@volar/language-core@2.4.23': - resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==} + '@volar/language-core@2.4.27': + resolution: {integrity: sha512-DjmjBWZ4tJKxfNC1F6HyYERNHPYS7L7OPFyCrestykNdUZMFYzI9WTyvwPcaNaHlrEUwESHYsfEw3isInncZxQ==} - '@volar/source-map@2.4.23': - resolution: {integrity: sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==} + '@volar/source-map@2.4.27': + resolution: {integrity: sha512-ynlcBReMgOZj2i6po+qVswtDUeeBRCTgDurjMGShbm8WYZgJ0PA4RmtebBJ0BCYol1qPv3GQF6jK7C9qoVc7lg==} - '@volar/typescript@2.4.23': - resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==} + '@volar/typescript@2.4.27': + resolution: {integrity: sha512-eWaYCcl/uAPInSK2Lze6IqVWaBu/itVqR5InXcHXFyles4zO++Mglt3oxdgj75BDcv1Knr9Y93nowS8U3wqhxg==} - '@vue/compiler-core@3.5.24': - resolution: {integrity: sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==} + '@vue/compiler-core@3.5.26': + resolution: {integrity: sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==} - '@vue/compiler-dom@3.5.24': - resolution: {integrity: sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==} + '@vue/compiler-dom@3.5.26': + resolution: {integrity: sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==} - '@vue/compiler-sfc@3.5.24': - resolution: {integrity: sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==} + '@vue/compiler-sfc@3.5.26': + resolution: {integrity: sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==} - '@vue/compiler-ssr@3.5.24': - resolution: {integrity: sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==} + '@vue/compiler-ssr@3.5.26': + resolution: {integrity: sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==} '@vue/devtools-api@6.6.4': resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} @@ -1303,30 +1514,25 @@ packages: typescript: optional: true - '@vue/language-core@3.1.4': - resolution: {integrity: sha512-n/58wm8SkmoxMWkUNUH/PwoovWe4hmdyPJU2ouldr3EPi1MLoS7iDN46je8CsP95SnVBs2axInzRglPNKvqMcg==} + '@vue/language-core@3.2.2': + resolution: {integrity: sha512-5DAuhxsxBN9kbriklh3Q5AMaJhyOCNiQJvCskN9/30XOpdLiqZU9Q+WvjArP17ubdGEyZtBzlIeG5nIjEbNOrQ==} + + '@vue/reactivity@3.5.26': + resolution: {integrity: sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ==} + + '@vue/runtime-core@3.5.26': + resolution: {integrity: sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q==} + + '@vue/runtime-dom@3.5.26': + resolution: {integrity: sha512-XLLd/+4sPC2ZkN/6+V4O4gjJu6kSDbHAChvsyWgm1oGbdSO3efvGYnm25yCjtFm/K7rrSDvSfPDgN1pHgS4VNQ==} + + '@vue/server-renderer@3.5.26': + resolution: {integrity: sha512-TYKLXmrwWKSodyVuO1WAubucd+1XlLg4set0YoV+Hu8Lo79mp/YMwWV5mC5FgtsDxX3qo1ONrxFaTP1OQgy1uA==} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + vue: 3.5.26 - '@vue/reactivity@3.5.24': - resolution: {integrity: sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==} - - '@vue/runtime-core@3.5.24': - resolution: {integrity: sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==} - - '@vue/runtime-dom@3.5.24': - resolution: {integrity: sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==} - - '@vue/server-renderer@3.5.24': - resolution: {integrity: sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==} - peerDependencies: - vue: 3.5.24 - - '@vue/shared@3.5.24': - resolution: {integrity: sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==} + '@vue/shared@3.5.26': + resolution: {integrity: sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==} '@vue/tsconfig@0.8.1': resolution: {integrity: sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==} @@ -1339,13 +1545,13 @@ packages: vue: optional: true - '@vueuse/core@14.0.0': - resolution: {integrity: sha512-d6tKRWkZE8IQElX2aHBxXOMD478fHIYV+Dzm2y9Ag122ICBpNKtGICiXKOhWU3L1kKdttDD9dCMS4bGP3jhCTQ==} + '@vueuse/core@14.1.0': + resolution: {integrity: sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw==} peerDependencies: vue: ^3.5.0 - '@vueuse/integrations@14.0.0': - resolution: {integrity: sha512-5A0X7q9qyLtM3xyghq5nK/NEESf7cpcZlkQgXTMuW4JWiAMYxc1ImdhhGrk4negFBsq3ejvAlRmLdNrkcTzk1Q==} + '@vueuse/integrations@14.1.0': + resolution: {integrity: sha512-eNQPdisnO9SvdydTIXnTE7c29yOsJBD/xkwEyQLdhDC/LKbqrFpXHb3uS//7NcIrQO3fWVuvMGp8dbK6mNEMCA==} peerDependencies: async-validator: ^4 axios: ^1 @@ -1386,11 +1592,11 @@ packages: universal-cookie: optional: true - '@vueuse/metadata@14.0.0': - resolution: {integrity: sha512-6yoGqbJcMldVCevkFiHDBTB1V5Hq+G/haPlGIuaFZHpXC0HADB0EN1ryQAAceiW+ryS3niUwvdFbGiqHqBrfVA==} + '@vueuse/metadata@14.1.0': + resolution: {integrity: sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA==} - '@vueuse/shared@14.0.0': - resolution: {integrity: sha512-mTCA0uczBgurRlwVaQHfG0Ja7UdGe4g9mwffiJmvLiTtp1G4AQyIjej6si/k8c8pUwTfVpNufck+23gXptPAkw==} + '@vueuse/shared@14.1.0': + resolution: {integrity: sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw==} peerDependencies: vue: ^3.5.0 @@ -1403,8 +1609,8 @@ packages: resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==} engines: {node: '>=10.0.0'} - ace-builds@1.43.4: - resolution: {integrity: sha512-8hAxVfo2ImICd69BWlZwZlxe9rxDGDjuUhh+WeWgGDvfBCE+r3lkynkQvIovDz4jcMi8O7bsEaFygaDT+h9sBA==} + ace-builds@1.43.5: + resolution: {integrity: sha512-iH5FLBKdB7SVn9GR37UgA/tpQS8OTWIxWAuq3Ofaw+Qbc69FfPXsXd9jeW7KRG2xKpKMqBDnu0tHBrCWY5QI7A==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -1422,8 +1628,8 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - alien-signals@3.1.0: - resolution: {integrity: sha512-yufC6VpSy8tK3I0lO67pjumo5JvDQVQyr38+3OHqe6CHl1t2VZekKZ7EKKZSqk0cRmE7U7tfZbpXiKNzuc+ckg==} + alien-signals@3.1.2: + resolution: {integrity: sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==} ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} @@ -1432,8 +1638,8 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - autoprefixer@10.4.22: - resolution: {integrity: sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==} + autoprefixer@10.4.23: + resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: @@ -1461,6 +1667,10 @@ packages: resolution: {integrity: sha512-73lC1ugzwoaWCLJ1LvOgrR5xsMLTqSKIEoMHVtL9E/HNk0PXtTM76ZIm84856/SF7Nv8mPZxKoBsgpm0tR1u1Q==} hasBin: true + baseline-browser-mapping@2.9.7: + resolution: {integrity: sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==} + hasBin: true + birpc@2.8.0: resolution: {integrity: sha512-Bz2a4qD/5GRhiHSwj30c/8kC8QGj12nNDwz3D4ErQ4Xhy35dsSDvF+RA/tWpjyU0pdGtSDiEk6B5fBGE1qNVhw==} @@ -1489,6 +1699,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -1499,6 +1714,9 @@ packages: caniuse-lite@1.0.30001754: resolution: {integrity: sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==} + caniuse-lite@1.0.30001760: + resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -1544,8 +1762,8 @@ packages: engines: {node: '>=4'} hasBin: true - csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} custom-error-instance@2.1.1: resolution: {integrity: sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg==} @@ -1572,14 +1790,17 @@ packages: dom-walk@0.1.2: resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==} - dompurify@3.3.0: - resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==} + dompurify@3.3.1: + resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==} electron-to-chromium@1.5.250: resolution: {integrity: sha512-/5UMj9IiGDMOFBnN4i7/Ry5onJrAGSbOGo3s9FEKmwobGq6xw832ccET0CE3CkkMBZ8GJSlUIesZofpyurqDXw==} - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + + entities@7.0.0: + resolution: {integrity: sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==} engines: {node: '>=0.12'} epubjs@0.3.93: @@ -1596,8 +1817,13 @@ packages: resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} engines: {node: '>=0.12'} - esbuild@0.25.11: - resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} engines: {node: '>=18'} hasBin: true @@ -1620,8 +1846,8 @@ packages: peerDependencies: eslint: '>=7.0.0' - eslint-plugin-prettier@5.5.4: - resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==} + eslint-plugin-prettier@5.5.5: + resolution: {integrity: sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: '@types/eslint': '>=8.0.0' @@ -1634,8 +1860,8 @@ packages: eslint-config-prettier: optional: true - eslint-plugin-vue@10.5.1: - resolution: {integrity: sha512-SbR9ZBUFKgvWAbq3RrdCtWaW0IKm6wwUiApxf3BVTNfqUIo4IQQmreMg2iHFJJ6C/0wss3LXURBJ1OwS/MhFcQ==} + eslint-plugin-vue@10.7.0: + resolution: {integrity: sha512-r2XFCK4qlo1sxEoAMIoTTX0PZAdla0JJDt1fmYiworZUX67WeEGqm+JbyAg3M+pGiJ5U6Mp5WQbontXWtIW7TA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@stylistic/eslint-plugin': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 @@ -1660,8 +1886,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.39.1: - resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1728,8 +1954,8 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fastq@1.18.0: - resolution: {integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==} + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -1874,8 +2100,8 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true jsesc@3.1.0: @@ -1897,8 +2123,8 @@ packages: engines: {node: '>=6'} hasBin: true - jsonc-eslint-parser@2.4.0: - resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} + jsonc-eslint-parser@2.4.2: + resolution: {integrity: sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} jszip@3.10.1: @@ -1928,8 +2154,8 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash-es@4.17.21: - resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + lodash-es@4.17.22: + resolution: {integrity: sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==} lodash._baseiteratee@4.7.0: resolution: {integrity: sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ==} @@ -1973,8 +2199,8 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - marked@17.0.0: - resolution: {integrity: sha512-KkDYEWEEiYJw/KC+DVm1zzlpMQSMIu6YRltkcCvwheCp8HWPXCk9JwOmHJKBlGfzcpzcIt6x3sMnTsRm/51oDg==} + marked@17.0.1: + resolution: {integrity: sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==} engines: {node: '>= 20'} hasBin: true @@ -2038,10 +2264,6 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - normalize-range@0.1.2: - resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} - engines: {node: '>=0.10.0'} - normalize.css@8.0.1: resolution: {integrity: sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==} @@ -2114,8 +2336,8 @@ packages: resolution: {integrity: sha512-afRERtHn54AlwaF2/+LFszyAANTCggGilmcmILUzEjvs3XgFZT+xE6+QWQcAGmu4xajy+Xtj7acLOPdx5/eXWQ==} hasBin: true - postcss-selector-parser@6.1.2: - resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + postcss-selector-parser@7.1.1: + resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} engines: {node: '>=4'} postcss-value-parser@4.2.0: @@ -2129,12 +2351,12 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + prettier-linter-helpers@1.0.1: + resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} engines: {node: '>=6.0.0'} - prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + prettier@3.8.0: + resolution: {integrity: sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA==} engines: {node: '>=14'} hasBin: true @@ -2207,15 +2429,15 @@ packages: resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} engines: {node: '>= 4'} - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rollup@4.52.5: - resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} + rollup@4.55.1: + resolution: {integrity: sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2282,21 +2504,21 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - synckit@0.11.11: - resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} + synckit@0.11.12: + resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} engines: {node: ^14.18.0 || >=16.0.0} systemjs@6.15.1: resolution: {integrity: sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA==} - tabbable@6.3.0: - resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==} + tabbable@6.4.0: + resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} tar-mini@0.2.0: resolution: {integrity: sha512-+qfUHz700DWnRutdUsxRRVZ38G1Qr27OetwaMYTdg8hcPxf46U0S1Zf76dQMWRBmusOt2ZCK5kbIaiLkoGO7WQ==} - terser@5.44.1: - resolution: {integrity: sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==} + terser@5.46.0: + resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==} engines: {node: '>=10'} hasBin: true @@ -2308,8 +2530,8 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - ts-api-utils@2.1.0: - resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' @@ -2356,8 +2578,8 @@ packages: resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} engines: {node: '>=4'} - unplugin@2.3.10: - resolution: {integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==} + unplugin@2.3.11: + resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} engines: {node: '>=18.12.0'} update-browserslist-db@1.1.4: @@ -2366,6 +2588,12 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + update-browserslist-db@1.2.2: + resolution: {integrity: sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -2393,8 +2621,8 @@ packages: videojs-hotkeys@0.2.30: resolution: {integrity: sha512-G8kEQZPapoWDoEajh2Nroy4bCN1qVEul5AuzZqBS7ZCG45K7hqTYKgf1+fmYvG8m8u84sZmVMUvSWZBjaFW66Q==} - videojs-mobile-ui@1.1.1: - resolution: {integrity: sha512-q7vx74++bqu2763Tc/GG4qFcMt42emC8uXe/z+zFVpBIiysgAf89AgorE6m30YHWtVJWgbRIyzFVYNOxCk9qow==} + videojs-mobile-ui@1.1.3: + resolution: {integrity: sha512-LyWIRZBQWRouUQ9i9HBBvhwFL1uZkg3Adt52nT+e297dwpOgTaELO7gCXgvj5f6a2AfEb8a5AQMY4SLeIf8cqw==} engines: {node: '>=14', npm: '>=6'} peerDependencies: video.js: ^8 @@ -2402,11 +2630,11 @@ packages: videojs-vtt.js@0.15.5: resolution: {integrity: sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ==} - vite-plugin-compression2@2.3.1: - resolution: {integrity: sha512-bnhLTsurtvOiiP6EMISIKVsOMCeTAjE6FJbyqQus3W4mtAxF7pCuC4puUIAiCgNs98tOCpqo6GIXJXTLufzIaw==} + vite-plugin-compression2@2.4.0: + resolution: {integrity: sha512-8J4CBF1+dM1I06azba/eXJuJHinLF0Am7lUvRH8AZpu0otJoBaDEnxrIEr5iPZJSwH0AEglJGYCveh7pN52jCg==} - vite@7.2.2: - resolution: {integrity: sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==} + vite@7.3.1: + resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -2462,8 +2690,8 @@ packages: focus-trap: '>=7.2.0' vue: '>=3.2.0' - vue-i18n@11.1.12: - resolution: {integrity: sha512-BnstPj3KLHLrsqbVU2UOrPmr0+Mv11bsUZG0PyCOzsawCivk8W00GMXHeVUWIDOgNaScCuZah47CZFE+Wnl8mw==} + vue-i18n@11.2.8: + resolution: {integrity: sha512-vJ123v/PXCZntd6Qj5Jumy7UBmIuE92VrtdX+AXr+1WzdBHojiBxnAxdfctUFL+/JIN+VQH4BhsfTtiGsvVObg==} engines: {node: '>= 16'} peerDependencies: vue: ^3.0.0 @@ -2471,11 +2699,11 @@ packages: vue-lazyload@3.0.0: resolution: {integrity: sha512-h2keL/Rj550dLgesgOtXJS9qOiSMmuJNeVlfNAYV1/IYwOQYaWk5mFJlwRxmZDK9YC5gECcFLYYj7z1lKSf9ug==} - vue-reader@1.3.3: - resolution: {integrity: sha512-Rpd9szXrnlyK1TxBsy/5He8W077ALFxrDLzOiD6HIzsJNj4miKs6nDljj4HdSliKbZ7LYvUMOitW9TL+5J5Ecg==} + vue-reader@1.3.4: + resolution: {integrity: sha512-QYTX9hlrV71gL/1vMejcBLLS9Ool29XMZcLQwvL0Ep1F//o0ymzYbKX2Lre+4BUBkVq49/GmmGCmAJACsJL9tw==} - vue-router@4.6.3: - resolution: {integrity: sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==} + vue-router@4.6.4: + resolution: {integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==} peerDependencies: vue: ^3.5.0 @@ -2484,14 +2712,14 @@ packages: peerDependencies: vue: ^3.0.2 - vue-tsc@3.1.4: - resolution: {integrity: sha512-GsRJxttj4WkmXW/zDwYPGMJAN3np/4jTzoDFQTpTsI5Vg/JKMWamBwamlmLihgSVHO66y9P7GX+uoliYxeI4Hw==} + vue-tsc@3.2.2: + resolution: {integrity: sha512-r9YSia/VgGwmbbfC06hDdAatH634XJ9nVl6Zrnz1iK4ucp8Wu78kawplXnIDa3MSu1XdQQePTHLXYwPDWn+nyQ==} hasBin: true peerDependencies: typescript: '>=5.0.0' - vue@3.5.24: - resolution: {integrity: sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==} + vue@3.5.26: + resolution: {integrity: sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -2517,13 +2745,13 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml-eslint-parser@1.2.3: - resolution: {integrity: sha512-4wZWvE398hCP7O8n3nXKu/vdq1HcH01ixYlCREaJL5NUMwQ0g3MaGFUBNSlmBtKmhbtVG/Cm6lyYmSVTEVil8A==} + yaml-eslint-parser@1.3.2: + resolution: {integrity: sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg==} engines: {node: ^14.17.0 || >=16.0.0} - yaml@2.7.0: - resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} - engines: {node: '>= 14'} + yaml@2.8.2: + resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} + engines: {node: '>= 14.6'} hasBin: true yocto-queue@0.1.0: @@ -3186,91 +3414,174 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@chenfengyuan/vue-number-input@2.0.1(vue@3.5.24(typescript@5.9.3))': + '@chenfengyuan/vue-number-input@2.0.1(vue@3.5.26(typescript@5.9.3))': dependencies: - vue: 3.5.24(typescript@5.9.3) + vue: 3.5.26(typescript@5.9.3) - '@esbuild/aix-ppc64@0.25.11': + '@esbuild/aix-ppc64@0.25.12': optional: true - '@esbuild/android-arm64@0.25.11': + '@esbuild/aix-ppc64@0.27.2': optional: true - '@esbuild/android-arm@0.25.11': + '@esbuild/android-arm64@0.25.12': optional: true - '@esbuild/android-x64@0.25.11': + '@esbuild/android-arm64@0.27.2': optional: true - '@esbuild/darwin-arm64@0.25.11': + '@esbuild/android-arm@0.25.12': optional: true - '@esbuild/darwin-x64@0.25.11': + '@esbuild/android-arm@0.27.2': optional: true - '@esbuild/freebsd-arm64@0.25.11': + '@esbuild/android-x64@0.25.12': optional: true - '@esbuild/freebsd-x64@0.25.11': + '@esbuild/android-x64@0.27.2': optional: true - '@esbuild/linux-arm64@0.25.11': + '@esbuild/darwin-arm64@0.25.12': optional: true - '@esbuild/linux-arm@0.25.11': + '@esbuild/darwin-arm64@0.27.2': optional: true - '@esbuild/linux-ia32@0.25.11': + '@esbuild/darwin-x64@0.25.12': optional: true - '@esbuild/linux-loong64@0.25.11': + '@esbuild/darwin-x64@0.27.2': optional: true - '@esbuild/linux-mips64el@0.25.11': + '@esbuild/freebsd-arm64@0.25.12': optional: true - '@esbuild/linux-ppc64@0.25.11': + '@esbuild/freebsd-arm64@0.27.2': optional: true - '@esbuild/linux-riscv64@0.25.11': + '@esbuild/freebsd-x64@0.25.12': optional: true - '@esbuild/linux-s390x@0.25.11': + '@esbuild/freebsd-x64@0.27.2': optional: true - '@esbuild/linux-x64@0.25.11': + '@esbuild/linux-arm64@0.25.12': optional: true - '@esbuild/netbsd-arm64@0.25.11': + '@esbuild/linux-arm64@0.27.2': optional: true - '@esbuild/netbsd-x64@0.25.11': + '@esbuild/linux-arm@0.25.12': optional: true - '@esbuild/openbsd-arm64@0.25.11': + '@esbuild/linux-arm@0.27.2': optional: true - '@esbuild/openbsd-x64@0.25.11': + '@esbuild/linux-ia32@0.25.12': optional: true - '@esbuild/openharmony-arm64@0.25.11': + '@esbuild/linux-ia32@0.27.2': optional: true - '@esbuild/sunos-x64@0.25.11': + '@esbuild/linux-loong64@0.25.12': optional: true - '@esbuild/win32-arm64@0.25.11': + '@esbuild/linux-loong64@0.27.2': optional: true - '@esbuild/win32-ia32@0.25.11': + '@esbuild/linux-mips64el@0.25.12': optional: true - '@esbuild/win32-x64@0.25.11': + '@esbuild/linux-mips64el@0.27.2': optional: true - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1)': + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.27.2': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.27.2': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.27.2': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/linux-x64@0.27.2': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.27.2': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.27.2': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.27.2': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.27.2': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.27.2': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.27.2': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.27.2': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.27.2': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@esbuild/win32-x64@0.27.2': + optional: true + + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2)': dependencies: - eslint: 9.39.1 + eslint: 9.39.2 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)': + dependencies: + eslint: 9.39.2 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -3291,7 +3602,7 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.1': + '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 debug: 4.4.3 @@ -3299,13 +3610,13 @@ snapshots: globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.1.0 + js-yaml: 4.1.1 minimatch: 3.1.2 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - '@eslint/js@9.39.1': {} + '@eslint/js@9.39.2': {} '@eslint/object-schema@2.1.7': {} @@ -3325,49 +3636,51 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@intlify/bundle-utils@11.0.1(vue-i18n@11.1.12(vue@3.5.24(typescript@5.9.3)))': + '@intlify/bundle-utils@11.0.3(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))': dependencies: - '@intlify/message-compiler': 11.1.12 - '@intlify/shared': 11.1.12 + '@intlify/message-compiler': 11.2.8 + '@intlify/shared': 11.2.2 acorn: 8.15.0 - esbuild: 0.25.11 + esbuild: 0.25.12 escodegen: 2.1.0 estree-walker: 2.0.2 - jsonc-eslint-parser: 2.4.0 + jsonc-eslint-parser: 2.4.2 source-map-js: 1.2.1 - yaml-eslint-parser: 1.2.3 + yaml-eslint-parser: 1.3.2 optionalDependencies: - vue-i18n: 11.1.12(vue@3.5.24(typescript@5.9.3)) + vue-i18n: 11.2.8(vue@3.5.26(typescript@5.9.3)) - '@intlify/core-base@11.1.12': + '@intlify/core-base@11.2.8': dependencies: - '@intlify/message-compiler': 11.1.12 - '@intlify/shared': 11.1.12 + '@intlify/message-compiler': 11.2.8 + '@intlify/shared': 11.2.8 - '@intlify/message-compiler@11.1.12': + '@intlify/message-compiler@11.2.8': dependencies: - '@intlify/shared': 11.1.12 + '@intlify/shared': 11.2.8 source-map-js: 1.2.1 - '@intlify/shared@11.1.12': {} + '@intlify/shared@11.2.2': {} - '@intlify/unplugin-vue-i18n@11.0.1(@vue/compiler-dom@3.5.24)(eslint@9.39.1)(rollup@4.52.5)(typescript@5.9.3)(vue-i18n@11.1.12(vue@3.5.24(typescript@5.9.3)))(vue@3.5.24(typescript@5.9.3))': + '@intlify/shared@11.2.8': {} + + '@intlify/unplugin-vue-i18n@11.0.3(@vue/compiler-dom@3.5.26)(eslint@9.39.2)(rollup@4.55.1)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) - '@intlify/bundle-utils': 11.0.1(vue-i18n@11.1.12(vue@3.5.24(typescript@5.9.3))) - '@intlify/shared': 11.1.12 - '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.1.12)(@vue/compiler-dom@3.5.24)(vue-i18n@11.1.12(vue@3.5.24(typescript@5.9.3)))(vue@3.5.24(typescript@5.9.3)) - '@rollup/pluginutils': 5.3.0(rollup@4.52.5) - '@typescript-eslint/scope-manager': 8.46.4 - '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.9.3) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) + '@intlify/bundle-utils': 11.0.3(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3))) + '@intlify/shared': 11.2.2 + '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.2.2)(@vue/compiler-dom@3.5.26)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3)) + '@rollup/pluginutils': 5.3.0(rollup@4.55.1) + '@typescript-eslint/scope-manager': 8.49.0 + '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) debug: 4.4.3 fast-glob: 3.3.3 pathe: 2.0.3 picocolors: 1.1.1 - unplugin: 2.3.10 - vue: 3.5.24(typescript@5.9.3) + unplugin: 2.3.11 + vue: 3.5.26(typescript@5.9.3) optionalDependencies: - vue-i18n: 11.1.12(vue@3.5.24(typescript@5.9.3)) + vue-i18n: 11.2.8(vue@3.5.26(typescript@5.9.3)) transitivePeerDependencies: - '@vue/compiler-dom' - eslint @@ -3375,14 +3688,14 @@ snapshots: - supports-color - typescript - '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.1.12)(@vue/compiler-dom@3.5.24)(vue-i18n@11.1.12(vue@3.5.24(typescript@5.9.3)))(vue@3.5.24(typescript@5.9.3))': + '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.2.2)(@vue/compiler-dom@3.5.26)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))': dependencies: '@babel/parser': 7.28.5 optionalDependencies: - '@intlify/shared': 11.1.12 - '@vue/compiler-dom': 3.5.24 - vue: 3.5.24(typescript@5.9.3) - vue-i18n: 11.1.12(vue@3.5.24(typescript@5.9.3)) + '@intlify/shared': 11.2.2 + '@vue/compiler-dom': 3.5.26 + vue: 3.5.26(typescript@5.9.3) + vue-i18n: 11.2.8(vue@3.5.26(typescript@5.9.3)) '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -3418,87 +3731,96 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.18.0 + fastq: 1.19.1 '@pkgr/core@0.2.9': {} - '@rolldown/pluginutils@1.0.0-beta.29': {} + '@rolldown/pluginutils@1.0.0-beta.53': {} - '@rollup/pluginutils@5.3.0(rollup@4.52.5)': + '@rollup/pluginutils@5.3.0(rollup@4.55.1)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.52.5 + rollup: 4.55.1 - '@rollup/rollup-android-arm-eabi@4.52.5': + '@rollup/rollup-android-arm-eabi@4.55.1': optional: true - '@rollup/rollup-android-arm64@4.52.5': + '@rollup/rollup-android-arm64@4.55.1': optional: true - '@rollup/rollup-darwin-arm64@4.52.5': + '@rollup/rollup-darwin-arm64@4.55.1': optional: true - '@rollup/rollup-darwin-x64@4.52.5': + '@rollup/rollup-darwin-x64@4.55.1': optional: true - '@rollup/rollup-freebsd-arm64@4.52.5': + '@rollup/rollup-freebsd-arm64@4.55.1': optional: true - '@rollup/rollup-freebsd-x64@4.52.5': + '@rollup/rollup-freebsd-x64@4.55.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + '@rollup/rollup-linux-arm-gnueabihf@4.55.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.52.5': + '@rollup/rollup-linux-arm-musleabihf@4.55.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.52.5': + '@rollup/rollup-linux-arm64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.52.5': + '@rollup/rollup-linux-arm64-musl@4.55.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.52.5': + '@rollup/rollup-linux-loong64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.52.5': + '@rollup/rollup-linux-loong64-musl@4.55.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.52.5': + '@rollup/rollup-linux-ppc64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.52.5': + '@rollup/rollup-linux-ppc64-musl@4.55.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.52.5': + '@rollup/rollup-linux-riscv64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.52.5': + '@rollup/rollup-linux-riscv64-musl@4.55.1': optional: true - '@rollup/rollup-linux-x64-musl@4.52.5': + '@rollup/rollup-linux-s390x-gnu@4.55.1': optional: true - '@rollup/rollup-openharmony-arm64@4.52.5': + '@rollup/rollup-linux-x64-gnu@4.55.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.52.5': + '@rollup/rollup-linux-x64-musl@4.55.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.52.5': + '@rollup/rollup-openbsd-x64@4.55.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.52.5': + '@rollup/rollup-openharmony-arm64@4.55.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.52.5': + '@rollup/rollup-win32-arm64-msvc@4.55.1': optional: true - '@tsconfig/node24@24.0.2': {} + '@rollup/rollup-win32-ia32-msvc@4.55.1': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.55.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.55.1': + optional: true + + '@tsconfig/node24@24.0.4': {} '@types/estree@1.0.8': {} @@ -3514,7 +3836,7 @@ snapshots: '@types/lodash@4.17.13': {} - '@types/node@24.10.1': + '@types/node@24.10.9': dependencies: undici-types: 7.16.0 @@ -3523,48 +3845,47 @@ snapshots: '@types/web-bluetooth@0.0.21': {} - '@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.37.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.37.0(eslint@9.39.2)(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.37.0 - '@typescript-eslint/type-utils': 8.37.0(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/utils': 8.37.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.37.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.37.0(eslint@9.39.2)(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.37.0 - eslint: 9.39.1 + eslint: 9.39.2 graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.9.3) + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.46.4(@typescript-eslint/parser@8.37.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.53.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.37.0(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.46.4 - '@typescript-eslint/type-utils': 8.46.4(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.4(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.46.4 - eslint: 9.39.1 - graphemer: 1.4.0 + '@typescript-eslint/parser': 8.37.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.53.0 + '@typescript-eslint/type-utils': 8.53.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.53.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.53.0 + eslint: 9.39.2 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.9.3) + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.37.0(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.37.0 '@typescript-eslint/types': 8.37.0 '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.37.0 debug: 4.4.3 - eslint: 9.39.1 + eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -3578,10 +3899,19 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.46.4(typescript@5.9.3)': + '@typescript-eslint/project-service@8.49.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.46.4(typescript@5.9.3) - '@typescript-eslint/types': 8.46.4 + '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3) + '@typescript-eslint/types': 8.49.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.53.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.53.0(typescript@5.9.3) + '@typescript-eslint/types': 8.53.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -3592,46 +3922,57 @@ snapshots: '@typescript-eslint/types': 8.37.0 '@typescript-eslint/visitor-keys': 8.37.0 - '@typescript-eslint/scope-manager@8.46.4': + '@typescript-eslint/scope-manager@8.49.0': dependencies: - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/visitor-keys': 8.46.4 + '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/visitor-keys': 8.49.0 + + '@typescript-eslint/scope-manager@8.53.0': + dependencies: + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/visitor-keys': 8.53.0 '@typescript-eslint/tsconfig-utils@8.37.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.46.4(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.49.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.37.0(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.53.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.37.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.37.0 '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.37.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.37.0(eslint@9.39.2)(typescript@5.9.3) debug: 4.4.3 - eslint: 9.39.1 - ts-api-utils: 2.1.0(typescript@5.9.3) + eslint: 9.39.2 + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.46.4(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.53.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.4(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/typescript-estree': 8.53.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.53.0(eslint@9.39.2)(typescript@5.9.3) debug: 4.4.3 - eslint: 9.39.1 - ts-api-utils: 2.1.0(typescript@5.9.3) + eslint: 9.39.2 + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color '@typescript-eslint/types@8.37.0': {} - '@typescript-eslint/types@8.46.4': {} + '@typescript-eslint/types@8.49.0': {} + + '@typescript-eslint/types@8.53.0': {} '@typescript-eslint/typescript-estree@8.37.0(typescript@5.9.3)': dependencies: @@ -3644,45 +3985,59 @@ snapshots: is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.3 - ts-api-utils: 2.1.0(typescript@5.9.3) + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.46.4(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.49.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.46.4(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.46.4(typescript@5.9.3) - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/visitor-keys': 8.46.4 + '@typescript-eslint/project-service': 8.49.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3) + '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/visitor-keys': 8.49.0 debug: 4.4.3 - fast-glob: 3.3.3 - is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.3 - ts-api-utils: 2.1.0(typescript@5.9.3) + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.37.0(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.53.0(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) + '@typescript-eslint/project-service': 8.53.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.53.0(typescript@5.9.3) + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/visitor-keys': 8.53.0 + debug: 4.4.3 + minimatch: 9.0.5 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.37.0(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) '@typescript-eslint/scope-manager': 8.37.0 '@typescript-eslint/types': 8.37.0 '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.9.3) - eslint: 9.39.1 + eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.46.4(eslint@9.39.1)(typescript@5.9.3)': + '@typescript-eslint/utils@8.53.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) - '@typescript-eslint/scope-manager': 8.46.4 - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.9.3) - eslint: 9.39.1 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@typescript-eslint/scope-manager': 8.53.0 + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/typescript-estree': 8.53.0(typescript@5.9.3) + eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -3692,9 +4047,14 @@ snapshots: '@typescript-eslint/types': 8.37.0 eslint-visitor-keys: 4.2.1 - '@typescript-eslint/visitor-keys@8.46.4': + '@typescript-eslint/visitor-keys@8.49.0': dependencies: - '@typescript-eslint/types': 8.46.4 + '@typescript-eslint/types': 8.49.0 + eslint-visitor-keys: 4.2.1 + + '@typescript-eslint/visitor-keys@8.53.0': + dependencies: + '@typescript-eslint/types': 8.53.0 eslint-visitor-keys: 4.2.1 '@videojs/http-streaming@3.17.2(video.js@8.23.4)': @@ -3719,7 +4079,7 @@ snapshots: global: 4.4.0 is-function: 1.0.2 - '@vitejs/plugin-legacy@7.2.1(terser@5.44.1)(vite@7.2.2(@types/node@24.10.1)(terser@5.44.1)(yaml@2.7.0))': + '@vitejs/plugin-legacy@7.2.1(terser@5.46.0)(vite@7.3.1(@types/node@24.10.9)(terser@5.46.0)(yaml@2.8.2))': dependencies: '@babel/core': 7.28.5 '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.5) @@ -3733,58 +4093,58 @@ snapshots: magic-string: 0.30.21 regenerator-runtime: 0.14.1 systemjs: 6.15.1 - terser: 5.44.1 - vite: 7.2.2(@types/node@24.10.1)(terser@5.44.1)(yaml@2.7.0) + terser: 5.46.0 + vite: 7.3.1(@types/node@24.10.9)(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@6.0.1(vite@7.2.2(@types/node@24.10.1)(terser@5.44.1)(yaml@2.7.0))(vue@3.5.24(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.3(vite@7.3.1(@types/node@24.10.9)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))': dependencies: - '@rolldown/pluginutils': 1.0.0-beta.29 - vite: 7.2.2(@types/node@24.10.1)(terser@5.44.1)(yaml@2.7.0) - vue: 3.5.24(typescript@5.9.3) + '@rolldown/pluginutils': 1.0.0-beta.53 + vite: 7.3.1(@types/node@24.10.9)(terser@5.46.0)(yaml@2.8.2) + vue: 3.5.26(typescript@5.9.3) - '@volar/language-core@2.4.23': + '@volar/language-core@2.4.27': dependencies: - '@volar/source-map': 2.4.23 + '@volar/source-map': 2.4.27 - '@volar/source-map@2.4.23': {} + '@volar/source-map@2.4.27': {} - '@volar/typescript@2.4.23': + '@volar/typescript@2.4.27': dependencies: - '@volar/language-core': 2.4.23 + '@volar/language-core': 2.4.27 path-browserify: 1.0.1 vscode-uri: 3.1.0 - '@vue/compiler-core@3.5.24': + '@vue/compiler-core@3.5.26': dependencies: '@babel/parser': 7.28.5 - '@vue/shared': 3.5.24 - entities: 4.5.0 + '@vue/shared': 3.5.26 + entities: 7.0.0 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.24': + '@vue/compiler-dom@3.5.26': dependencies: - '@vue/compiler-core': 3.5.24 - '@vue/shared': 3.5.24 + '@vue/compiler-core': 3.5.26 + '@vue/shared': 3.5.26 - '@vue/compiler-sfc@3.5.24': + '@vue/compiler-sfc@3.5.26': dependencies: '@babel/parser': 7.28.5 - '@vue/compiler-core': 3.5.24 - '@vue/compiler-dom': 3.5.24 - '@vue/compiler-ssr': 3.5.24 - '@vue/shared': 3.5.24 + '@vue/compiler-core': 3.5.26 + '@vue/compiler-dom': 3.5.26 + '@vue/compiler-ssr': 3.5.26 + '@vue/shared': 3.5.26 estree-walker: 2.0.2 magic-string: 0.30.21 postcss: 8.5.6 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.24': + '@vue/compiler-ssr@3.5.26': dependencies: - '@vue/compiler-dom': 3.5.24 - '@vue/shared': 3.5.24 + '@vue/compiler-dom': 3.5.26 + '@vue/shared': 3.5.26 '@vue/devtools-api@6.6.4': {} @@ -3806,96 +4166,94 @@ snapshots: dependencies: rfdc: 1.4.1 - '@vue/eslint-config-prettier@10.2.0(eslint@9.39.1)(prettier@3.6.2)': + '@vue/eslint-config-prettier@10.2.0(eslint@9.39.2)(prettier@3.8.0)': dependencies: - eslint: 9.39.1 - eslint-config-prettier: 10.1.8(eslint@9.39.1) - eslint-plugin-prettier: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.1))(eslint@9.39.1)(prettier@3.6.2) - prettier: 3.6.2 + eslint: 9.39.2 + eslint-config-prettier: 10.1.8(eslint@9.39.2) + eslint-plugin-prettier: 5.5.5(eslint-config-prettier@10.1.8(eslint@9.39.2))(eslint@9.39.2)(prettier@3.8.0) + prettier: 3.8.0 transitivePeerDependencies: - '@types/eslint' - '@vue/eslint-config-typescript@14.6.0(eslint-plugin-vue@10.5.1(@typescript-eslint/parser@8.37.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(vue-eslint-parser@10.2.0(eslint@9.39.1)))(eslint@9.39.1)(typescript@5.9.3)': + '@vue/eslint-config-typescript@14.6.0(eslint-plugin-vue@10.7.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(vue-eslint-parser@10.2.0(eslint@9.39.2)))(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/utils': 8.37.0(eslint@9.39.1)(typescript@5.9.3) - eslint: 9.39.1 - eslint-plugin-vue: 10.5.1(@typescript-eslint/parser@8.37.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(vue-eslint-parser@10.2.0(eslint@9.39.1)) + '@typescript-eslint/utils': 8.37.0(eslint@9.39.2)(typescript@5.9.3) + eslint: 9.39.2 + eslint-plugin-vue: 10.7.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(vue-eslint-parser@10.2.0(eslint@9.39.2)) fast-glob: 3.3.3 - typescript-eslint: 8.37.0(eslint@9.39.1)(typescript@5.9.3) - vue-eslint-parser: 10.2.0(eslint@9.39.1) + typescript-eslint: 8.37.0(eslint@9.39.2)(typescript@5.9.3) + vue-eslint-parser: 10.2.0(eslint@9.39.2) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@vue/language-core@3.1.4(typescript@5.9.3)': + '@vue/language-core@3.2.2': dependencies: - '@volar/language-core': 2.4.23 - '@vue/compiler-dom': 3.5.24 - '@vue/shared': 3.5.24 - alien-signals: 3.1.0 + '@volar/language-core': 2.4.27 + '@vue/compiler-dom': 3.5.26 + '@vue/shared': 3.5.26 + alien-signals: 3.1.2 muggle-string: 0.4.1 path-browserify: 1.0.1 picomatch: 4.0.3 + + '@vue/reactivity@3.5.26': + dependencies: + '@vue/shared': 3.5.26 + + '@vue/runtime-core@3.5.26': + dependencies: + '@vue/reactivity': 3.5.26 + '@vue/shared': 3.5.26 + + '@vue/runtime-dom@3.5.26': + dependencies: + '@vue/reactivity': 3.5.26 + '@vue/runtime-core': 3.5.26 + '@vue/shared': 3.5.26 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.26(vue@3.5.26(typescript@5.9.3))': + dependencies: + '@vue/compiler-ssr': 3.5.26 + '@vue/shared': 3.5.26 + vue: 3.5.26(typescript@5.9.3) + + '@vue/shared@3.5.26': {} + + '@vue/tsconfig@0.8.1(typescript@5.9.3)(vue@3.5.26(typescript@5.9.3))': optionalDependencies: typescript: 5.9.3 + vue: 3.5.26(typescript@5.9.3) - '@vue/reactivity@3.5.24': - dependencies: - '@vue/shared': 3.5.24 - - '@vue/runtime-core@3.5.24': - dependencies: - '@vue/reactivity': 3.5.24 - '@vue/shared': 3.5.24 - - '@vue/runtime-dom@3.5.24': - dependencies: - '@vue/reactivity': 3.5.24 - '@vue/runtime-core': 3.5.24 - '@vue/shared': 3.5.24 - csstype: 3.1.3 - - '@vue/server-renderer@3.5.24(vue@3.5.24(typescript@5.9.3))': - dependencies: - '@vue/compiler-ssr': 3.5.24 - '@vue/shared': 3.5.24 - vue: 3.5.24(typescript@5.9.3) - - '@vue/shared@3.5.24': {} - - '@vue/tsconfig@0.8.1(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3))': - optionalDependencies: - typescript: 5.9.3 - vue: 3.5.24(typescript@5.9.3) - - '@vueuse/core@14.0.0(vue@3.5.24(typescript@5.9.3))': + '@vueuse/core@14.1.0(vue@3.5.26(typescript@5.9.3))': dependencies: '@types/web-bluetooth': 0.0.21 - '@vueuse/metadata': 14.0.0 - '@vueuse/shared': 14.0.0(vue@3.5.24(typescript@5.9.3)) - vue: 3.5.24(typescript@5.9.3) + '@vueuse/metadata': 14.1.0 + '@vueuse/shared': 14.1.0(vue@3.5.26(typescript@5.9.3)) + vue: 3.5.26(typescript@5.9.3) - '@vueuse/integrations@14.0.0(focus-trap@7.6.2)(jwt-decode@4.0.0)(vue@3.5.24(typescript@5.9.3))': + '@vueuse/integrations@14.1.0(focus-trap@7.6.2)(jwt-decode@4.0.0)(vue@3.5.26(typescript@5.9.3))': dependencies: - '@vueuse/core': 14.0.0(vue@3.5.24(typescript@5.9.3)) - '@vueuse/shared': 14.0.0(vue@3.5.24(typescript@5.9.3)) - vue: 3.5.24(typescript@5.9.3) + '@vueuse/core': 14.1.0(vue@3.5.26(typescript@5.9.3)) + '@vueuse/shared': 14.1.0(vue@3.5.26(typescript@5.9.3)) + vue: 3.5.26(typescript@5.9.3) optionalDependencies: focus-trap: 7.6.2 jwt-decode: 4.0.0 - '@vueuse/metadata@14.0.0': {} + '@vueuse/metadata@14.1.0': {} - '@vueuse/shared@14.0.0(vue@3.5.24(typescript@5.9.3))': + '@vueuse/shared@14.1.0(vue@3.5.26(typescript@5.9.3))': dependencies: - vue: 3.5.24(typescript@5.9.3) + vue: 3.5.26(typescript@5.9.3) '@xmldom/xmldom@0.7.13': {} '@xmldom/xmldom@0.8.11': {} - ace-builds@1.43.4: {} + ace-builds@1.43.5: {} acorn-jsx@5.3.2(acorn@8.15.0): dependencies: @@ -3917,7 +4275,7 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - alien-signals@3.1.0: {} + alien-signals@3.1.2: {} ansi-styles@4.3.0: dependencies: @@ -3925,12 +4283,11 @@ snapshots: argparse@2.0.1: {} - autoprefixer@10.4.22(postcss@8.5.6): + autoprefixer@10.4.23(postcss@8.5.6): dependencies: - browserslist: 4.28.0 - caniuse-lite: 1.0.30001754 + browserslist: 4.28.1 + caniuse-lite: 1.0.30001760 fraction.js: 5.3.4 - normalize-range: 0.1.2 picocolors: 1.1.1 postcss: 8.5.6 postcss-value-parser: 4.2.0 @@ -3963,6 +4320,8 @@ snapshots: baseline-browser-mapping@2.8.26: {} + baseline-browser-mapping@2.9.7: {} + birpc@2.8.0: {} boolbase@1.0.0: {} @@ -3993,12 +4352,22 @@ snapshots: node-releases: 2.0.27 update-browserslist-db: 1.1.4(browserslist@4.28.0) + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.9.7 + caniuse-lite: 1.0.30001760 + electron-to-chromium: 1.5.267 + node-releases: 2.0.27 + update-browserslist-db: 1.2.2(browserslist@4.28.1) + buffer-from@1.1.2: {} callsites@3.1.0: {} caniuse-lite@1.0.30001754: {} + caniuse-lite@1.0.30001760: {} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -4041,7 +4410,7 @@ snapshots: cssesc@3.0.0: {} - csstype@3.1.3: {} + csstype@3.2.3: {} custom-error-instance@2.1.1: {} @@ -4060,13 +4429,15 @@ snapshots: dom-walk@0.1.2: {} - dompurify@3.3.0: + dompurify@3.3.1: optionalDependencies: '@types/trusted-types': 2.0.7 electron-to-chromium@1.5.250: {} - entities@4.5.0: {} + electron-to-chromium@1.5.267: {} + + entities@7.0.0: {} epubjs@0.3.93: dependencies: @@ -4098,34 +4469,63 @@ snapshots: d: 1.0.2 ext: 1.7.0 - esbuild@0.25.11: + esbuild@0.25.12: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.11 - '@esbuild/android-arm': 0.25.11 - '@esbuild/android-arm64': 0.25.11 - '@esbuild/android-x64': 0.25.11 - '@esbuild/darwin-arm64': 0.25.11 - '@esbuild/darwin-x64': 0.25.11 - '@esbuild/freebsd-arm64': 0.25.11 - '@esbuild/freebsd-x64': 0.25.11 - '@esbuild/linux-arm': 0.25.11 - '@esbuild/linux-arm64': 0.25.11 - '@esbuild/linux-ia32': 0.25.11 - '@esbuild/linux-loong64': 0.25.11 - '@esbuild/linux-mips64el': 0.25.11 - '@esbuild/linux-ppc64': 0.25.11 - '@esbuild/linux-riscv64': 0.25.11 - '@esbuild/linux-s390x': 0.25.11 - '@esbuild/linux-x64': 0.25.11 - '@esbuild/netbsd-arm64': 0.25.11 - '@esbuild/netbsd-x64': 0.25.11 - '@esbuild/openbsd-arm64': 0.25.11 - '@esbuild/openbsd-x64': 0.25.11 - '@esbuild/openharmony-arm64': 0.25.11 - '@esbuild/sunos-x64': 0.25.11 - '@esbuild/win32-arm64': 0.25.11 - '@esbuild/win32-ia32': 0.25.11 - '@esbuild/win32-x64': 0.25.11 + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + esbuild@0.27.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 escalade@3.2.0: {} @@ -4139,31 +4539,31 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-prettier@10.1.8(eslint@9.39.1): + eslint-config-prettier@10.1.8(eslint@9.39.2): dependencies: - eslint: 9.39.1 + eslint: 9.39.2 - eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.1))(eslint@9.39.1)(prettier@3.6.2): + eslint-plugin-prettier@5.5.5(eslint-config-prettier@10.1.8(eslint@9.39.2))(eslint@9.39.2)(prettier@3.8.0): dependencies: - eslint: 9.39.1 - prettier: 3.6.2 - prettier-linter-helpers: 1.0.0 - synckit: 0.11.11 + eslint: 9.39.2 + prettier: 3.8.0 + prettier-linter-helpers: 1.0.1 + synckit: 0.11.12 optionalDependencies: - eslint-config-prettier: 10.1.8(eslint@9.39.1) + eslint-config-prettier: 10.1.8(eslint@9.39.2) - eslint-plugin-vue@10.5.1(@typescript-eslint/parser@8.37.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(vue-eslint-parser@10.2.0(eslint@9.39.1)): + eslint-plugin-vue@10.7.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(vue-eslint-parser@10.2.0(eslint@9.39.2)): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) - eslint: 9.39.1 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + eslint: 9.39.2 natural-compare: 1.4.0 nth-check: 2.1.1 - postcss-selector-parser: 6.1.2 + postcss-selector-parser: 7.1.1 semver: 7.7.3 - vue-eslint-parser: 10.2.0(eslint@9.39.1) + vue-eslint-parser: 10.2.0(eslint@9.39.2) xml-name-validator: 4.0.0 optionalDependencies: - '@typescript-eslint/parser': 8.37.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.37.0(eslint@9.39.2)(typescript@5.9.3) eslint-scope@8.4.0: dependencies: @@ -4174,15 +4574,15 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.39.1: + eslint@9.39.2: dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.39.1 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 @@ -4273,9 +4673,9 @@ snapshots: fast-levenshtein@2.0.6: {} - fastq@1.18.0: + fastq@1.19.1: dependencies: - reusify: 1.0.4 + reusify: 1.1.0 fdir@6.5.0(picomatch@4.0.3): optionalDependencies: @@ -4305,7 +4705,7 @@ snapshots: focus-trap@7.6.2: dependencies: - tabbable: 6.3.0 + tabbable: 6.4.0 fraction.js@5.3.4: {} @@ -4384,7 +4784,7 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@4.1.0: + js-yaml@4.1.1: dependencies: argparse: 2.0.1 @@ -4398,7 +4798,7 @@ snapshots: json5@2.2.3: {} - jsonc-eslint-parser@2.4.0: + jsonc-eslint-parser@2.4.2: dependencies: acorn: 8.15.0 eslint-visitor-keys: 3.4.3 @@ -4439,7 +4839,7 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash-es@4.17.21: {} + lodash-es@4.17.22: {} lodash._baseiteratee@4.7.0: dependencies: @@ -4487,7 +4887,7 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - marked@17.0.0: {} + marked@17.0.1: {} marks-pane@1.0.9: {} @@ -4540,8 +4940,6 @@ snapshots: node-releases@2.0.27: {} - normalize-range@0.1.2: {} - normalize.css@8.0.1: {} nth-check@2.1.1: @@ -4591,10 +4989,10 @@ snapshots: picomatch@4.0.3: {} - pinia@3.0.4(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3)): + pinia@3.0.4(typescript@5.9.3)(vue@3.5.26(typescript@5.9.3)): dependencies: '@vue/devtools-api': 7.7.8 - vue: 3.5.24(typescript@5.9.3) + vue: 3.5.26(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -4602,7 +5000,7 @@ snapshots: dependencies: '@babel/runtime': 7.28.4 - postcss-selector-parser@6.1.2: + postcss-selector-parser@7.1.1: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 @@ -4617,11 +5015,11 @@ snapshots: prelude-ls@1.2.1: {} - prettier-linter-helpers@1.0.0: + prettier-linter-helpers@1.0.1: dependencies: fast-diff: 1.3.0 - prettier@3.6.2: {} + prettier@3.8.0: {} pretty-bytes@7.1.0: {} @@ -4637,9 +5035,9 @@ snapshots: punycode@2.3.1: {} - qrcode.vue@3.6.0(vue@3.5.24(typescript@5.9.3)): + qrcode.vue@3.6.0(vue@3.5.26(typescript@5.9.3)): dependencies: - vue: 3.5.24(typescript@5.9.3) + vue: 3.5.26(typescript@5.9.3) querystringify@2.2.0: {} @@ -4690,36 +5088,39 @@ snapshots: retry@0.12.0: {} - reusify@1.0.4: {} + reusify@1.1.0: {} rfdc@1.4.1: {} - rollup@4.52.5: + rollup@4.55.1: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.52.5 - '@rollup/rollup-android-arm64': 4.52.5 - '@rollup/rollup-darwin-arm64': 4.52.5 - '@rollup/rollup-darwin-x64': 4.52.5 - '@rollup/rollup-freebsd-arm64': 4.52.5 - '@rollup/rollup-freebsd-x64': 4.52.5 - '@rollup/rollup-linux-arm-gnueabihf': 4.52.5 - '@rollup/rollup-linux-arm-musleabihf': 4.52.5 - '@rollup/rollup-linux-arm64-gnu': 4.52.5 - '@rollup/rollup-linux-arm64-musl': 4.52.5 - '@rollup/rollup-linux-loong64-gnu': 4.52.5 - '@rollup/rollup-linux-ppc64-gnu': 4.52.5 - '@rollup/rollup-linux-riscv64-gnu': 4.52.5 - '@rollup/rollup-linux-riscv64-musl': 4.52.5 - '@rollup/rollup-linux-s390x-gnu': 4.52.5 - '@rollup/rollup-linux-x64-gnu': 4.52.5 - '@rollup/rollup-linux-x64-musl': 4.52.5 - '@rollup/rollup-openharmony-arm64': 4.52.5 - '@rollup/rollup-win32-arm64-msvc': 4.52.5 - '@rollup/rollup-win32-ia32-msvc': 4.52.5 - '@rollup/rollup-win32-x64-gnu': 4.52.5 - '@rollup/rollup-win32-x64-msvc': 4.52.5 + '@rollup/rollup-android-arm-eabi': 4.55.1 + '@rollup/rollup-android-arm64': 4.55.1 + '@rollup/rollup-darwin-arm64': 4.55.1 + '@rollup/rollup-darwin-x64': 4.55.1 + '@rollup/rollup-freebsd-arm64': 4.55.1 + '@rollup/rollup-freebsd-x64': 4.55.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.55.1 + '@rollup/rollup-linux-arm-musleabihf': 4.55.1 + '@rollup/rollup-linux-arm64-gnu': 4.55.1 + '@rollup/rollup-linux-arm64-musl': 4.55.1 + '@rollup/rollup-linux-loong64-gnu': 4.55.1 + '@rollup/rollup-linux-loong64-musl': 4.55.1 + '@rollup/rollup-linux-ppc64-gnu': 4.55.1 + '@rollup/rollup-linux-ppc64-musl': 4.55.1 + '@rollup/rollup-linux-riscv64-gnu': 4.55.1 + '@rollup/rollup-linux-riscv64-musl': 4.55.1 + '@rollup/rollup-linux-s390x-gnu': 4.55.1 + '@rollup/rollup-linux-x64-gnu': 4.55.1 + '@rollup/rollup-linux-x64-musl': 4.55.1 + '@rollup/rollup-openbsd-x64': 4.55.1 + '@rollup/rollup-openharmony-arm64': 4.55.1 + '@rollup/rollup-win32-arm64-msvc': 4.55.1 + '@rollup/rollup-win32-ia32-msvc': 4.55.1 + '@rollup/rollup-win32-x64-gnu': 4.55.1 + '@rollup/rollup-win32-x64-msvc': 4.55.1 fsevents: 2.3.3 run-parallel@1.2.0: @@ -4769,17 +5170,17 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - synckit@0.11.11: + synckit@0.11.12: dependencies: '@pkgr/core': 0.2.9 systemjs@6.15.1: {} - tabbable@6.3.0: {} + tabbable@6.4.0: {} tar-mini@0.2.0: {} - terser@5.44.1: + terser@5.46.0: dependencies: '@jridgewell/source-map': 0.3.11 acorn: 8.15.0 @@ -4795,7 +5196,7 @@ snapshots: dependencies: is-number: 7.0.0 - ts-api-utils@2.1.0(typescript@5.9.3): + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -4815,13 +5216,13 @@ snapshots: type@2.7.3: {} - typescript-eslint@8.37.0(eslint@9.39.1)(typescript@5.9.3): + typescript-eslint@8.37.0(eslint@9.39.2)(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/parser': 8.37.0(eslint@9.39.1)(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.37.0(eslint@9.39.2)(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.37.0(eslint@9.39.1)(typescript@5.9.3) - eslint: 9.39.1 + '@typescript-eslint/utils': 8.37.0(eslint@9.39.2)(typescript@5.9.3) + eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -4841,7 +5242,7 @@ snapshots: unicode-property-aliases-ecmascript@2.2.0: {} - unplugin@2.3.10: + unplugin@2.3.11: dependencies: '@jridgewell/remapping': 2.3.5 acorn: 8.15.0 @@ -4854,6 +5255,12 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + update-browserslist-db@1.2.2(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -4893,7 +5300,7 @@ snapshots: videojs-hotkeys@0.2.30: {} - videojs-mobile-ui@1.1.1(video.js@8.23.4): + videojs-mobile-ui@1.1.3(video.js@8.23.4): dependencies: global: 4.4.0 video.js: 8.23.4 @@ -4902,33 +5309,33 @@ snapshots: dependencies: global: 4.4.0 - vite-plugin-compression2@2.3.1(rollup@4.52.5): + vite-plugin-compression2@2.4.0(rollup@4.55.1): dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.52.5) + '@rollup/pluginutils': 5.3.0(rollup@4.55.1) tar-mini: 0.2.0 transitivePeerDependencies: - rollup - vite@7.2.2(@types/node@24.10.1)(terser@5.44.1)(yaml@2.7.0): + vite@7.3.1(@types/node@24.10.9)(terser@5.46.0)(yaml@2.8.2): dependencies: - esbuild: 0.25.11 + esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.52.5 + rollup: 4.55.1 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.10.1 + '@types/node': 24.10.9 fsevents: 2.3.3 - terser: 5.44.1 - yaml: 2.7.0 + terser: 5.46.0 + yaml: 2.8.2 vscode-uri@3.1.0: {} - vue-eslint-parser@10.2.0(eslint@9.39.1): + vue-eslint-parser@10.2.0(eslint@9.39.2): dependencies: debug: 4.4.3 - eslint: 9.39.1 + eslint: 9.39.2 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 @@ -4937,48 +5344,48 @@ snapshots: transitivePeerDependencies: - supports-color - vue-final-modal@4.5.5(@vueuse/core@14.0.0(vue@3.5.24(typescript@5.9.3)))(@vueuse/integrations@14.0.0(focus-trap@7.6.2)(jwt-decode@4.0.0)(vue@3.5.24(typescript@5.9.3)))(focus-trap@7.6.2)(vue@3.5.24(typescript@5.9.3)): + vue-final-modal@4.5.5(@vueuse/core@14.1.0(vue@3.5.26(typescript@5.9.3)))(@vueuse/integrations@14.1.0(focus-trap@7.6.2)(jwt-decode@4.0.0)(vue@3.5.26(typescript@5.9.3)))(focus-trap@7.6.2)(vue@3.5.26(typescript@5.9.3)): dependencies: - '@vueuse/core': 14.0.0(vue@3.5.24(typescript@5.9.3)) - '@vueuse/integrations': 14.0.0(focus-trap@7.6.2)(jwt-decode@4.0.0)(vue@3.5.24(typescript@5.9.3)) + '@vueuse/core': 14.1.0(vue@3.5.26(typescript@5.9.3)) + '@vueuse/integrations': 14.1.0(focus-trap@7.6.2)(jwt-decode@4.0.0)(vue@3.5.26(typescript@5.9.3)) focus-trap: 7.6.2 - vue: 3.5.24(typescript@5.9.3) + vue: 3.5.26(typescript@5.9.3) - vue-i18n@11.1.12(vue@3.5.24(typescript@5.9.3)): + vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)): dependencies: - '@intlify/core-base': 11.1.12 - '@intlify/shared': 11.1.12 + '@intlify/core-base': 11.2.8 + '@intlify/shared': 11.2.8 '@vue/devtools-api': 6.6.4 - vue: 3.5.24(typescript@5.9.3) + vue: 3.5.26(typescript@5.9.3) vue-lazyload@3.0.0: {} - vue-reader@1.3.3: + vue-reader@1.3.4: dependencies: epubjs: 0.3.93 - vue-router@4.6.3(vue@3.5.24(typescript@5.9.3)): + vue-router@4.6.4(vue@3.5.26(typescript@5.9.3)): dependencies: '@vue/devtools-api': 6.6.4 - vue: 3.5.24(typescript@5.9.3) + vue: 3.5.26(typescript@5.9.3) - vue-toastification@2.0.0-rc.5(vue@3.5.24(typescript@5.9.3)): + vue-toastification@2.0.0-rc.5(vue@3.5.26(typescript@5.9.3)): dependencies: - vue: 3.5.24(typescript@5.9.3) + vue: 3.5.26(typescript@5.9.3) - vue-tsc@3.1.4(typescript@5.9.3): + vue-tsc@3.2.2(typescript@5.9.3): dependencies: - '@volar/typescript': 2.4.23 - '@vue/language-core': 3.1.4(typescript@5.9.3) + '@volar/typescript': 2.4.27 + '@vue/language-core': 3.2.2 typescript: 5.9.3 - vue@3.5.24(typescript@5.9.3): + vue@3.5.26(typescript@5.9.3): dependencies: - '@vue/compiler-dom': 3.5.24 - '@vue/compiler-sfc': 3.5.24 - '@vue/runtime-dom': 3.5.24 - '@vue/server-renderer': 3.5.24(vue@3.5.24(typescript@5.9.3)) - '@vue/shared': 3.5.24 + '@vue/compiler-dom': 3.5.26 + '@vue/compiler-sfc': 3.5.26 + '@vue/runtime-dom': 3.5.26 + '@vue/server-renderer': 3.5.26(vue@3.5.26(typescript@5.9.3)) + '@vue/shared': 3.5.26 optionalDependencies: typescript: 5.9.3 @@ -4994,12 +5401,11 @@ snapshots: yallist@3.1.1: {} - yaml-eslint-parser@1.2.3: + yaml-eslint-parser@1.3.2: dependencies: eslint-visitor-keys: 3.4.3 - lodash: 4.17.21 - yaml: 2.7.0 + yaml: 2.8.2 - yaml@2.7.0: {} + yaml@2.8.2: {} yocto-queue@0.1.0: {} diff --git a/frontend/src/api/pub.ts b/frontend/src/api/pub.ts index 964cb030..5070cae3 100644 --- a/frontend/src/api/pub.ts +++ b/frontend/src/api/pub.ts @@ -41,12 +41,12 @@ export function download( let url = `${baseURL}/api/public/dl/${hash}`; if (files.length === 1) { - url += encodeURIComponent(files[0]) + "?"; + url += files[0] + "?"; } else { let arg = ""; for (const file of files) { - arg += encodeURIComponent(file) + ","; + arg += file + ","; } arg = arg.substring(0, arg.length - 1); diff --git a/frontend/src/api/search.ts b/frontend/src/api/search.ts index 6fa02d06..63cbdaa1 100644 --- a/frontend/src/api/search.ts +++ b/frontend/src/api/search.ts @@ -1,7 +1,12 @@ -import { fetchURL, removePrefix } from "./utils"; +import { fetchURL, removePrefix, StatusError } from "./utils"; import url from "../utils/url"; -export default async function search(base: string, query: string) { +export default async function search( + base: string, + query: string, + signal: AbortSignal, + callback: (item: ResourceItem) => void +) { base = removePrefix(base); query = encodeURIComponent(query); @@ -9,19 +14,60 @@ export default async function search(base: string, query: string) { base += "/"; } - const res = await fetchURL(`/api/search${base}?query=${query}`, {}); + const res = await fetchURL(`/api/search${base}?query=${query}`, { signal }); + if (!res.body) { + throw new StatusError("000 No connection", 0); + } + try { + // Try streaming approach first (modern browsers) + if (res.body && typeof res.body.pipeThrough === "function") { + const reader = res.body.pipeThrough(new TextDecoderStream()).getReader(); + let buffer = ""; + while (true) { + const { done, value } = await reader.read(); + if (value) { + buffer += value; + } + const lines = buffer.split(/\n/); + let lastLine = lines.pop(); + // Save incomplete last line + if (!lastLine) { + lastLine = ""; + } + buffer = lastLine; - let data = await res.json(); - - data = data.map((item: ResourceItem & { dir: boolean }) => { - item.url = `/files${base}` + url.encodePath(item.path); - - if (item.dir) { - item.url += "/"; + for (const line of lines) { + if (line) { + const item = JSON.parse(line) as ResourceItem; + item.url = `/files${base}` + url.encodePath(item.path); + if (item.isDir) { + item.url += "/"; + } + callback(item); + } + } + if (done) break; + } + } else { + // Fallback for browsers without streaming support (e.g., Safari) + const text = await res.text(); + const lines = text.split(/\n/); + for (const line of lines) { + if (line) { + const item = JSON.parse(line) as ResourceItem; + item.url = `/files${base}` + url.encodePath(item.path); + if (item.isDir) { + item.url += "/"; + } + callback(item); + } + } } - - return item; - }); - - return data; + } catch (e) { + // Check if the error is an intentional cancellation + if (e instanceof Error && e.name === "AbortError") { + throw new StatusError("000 No connection", 0, true); + } + throw e; + } } diff --git a/frontend/src/api/users.ts b/frontend/src/api/users.ts index 78096b49..dc45e084 100644 --- a/frontend/src/api/users.ts +++ b/frontend/src/api/users.ts @@ -8,12 +8,13 @@ export async function get(id: number) { return fetchJSON(`/api/users/${id}`, {}); } -export async function create(user: IUser) { +export async function create(user: IUser, currentPassword: string) { const res = await fetchURL(`/api/users`, { method: "POST", body: JSON.stringify({ what: "user", which: [], + current_password: currentPassword, data: user, }), }); @@ -25,19 +26,30 @@ export async function create(user: IUser) { throw new StatusError(await res.text(), res.status); } -export async function update(user: Partial, which = ["all"]) { +export async function update( + user: Partial, + which = ["all"], + currentPassword: string | null = null +) { await fetchURL(`/api/users/${user.id}`, { method: "PUT", body: JSON.stringify({ what: "user", which: which, + ...(currentPassword != null ? { current_password: currentPassword } : {}), data: user, }), }); } -export async function remove(id: number) { +export async function remove( + id: number, + currentPassword: string | null = null +) { await fetchURL(`/api/users/${id}`, { method: "DELETE", + body: JSON.stringify({ + ...(currentPassword != null ? { current_password: currentPassword } : {}), + }), }); } diff --git a/frontend/src/components/Search.vue b/frontend/src/components/Search.vue index 08b40e3e..57d5ba0d 100644 --- a/frontend/src/components/Search.vue +++ b/frontend/src/components/Search.vue @@ -5,10 +5,11 @@ v-if="active" class="action" @click="close" - :aria-label="$t('buttons.close')" - :title="$t('buttons.close')" + :aria-label="closeButtonTitle" + :title="closeButtonTitle" > - arrow_back + stop_circle + arrow_back search + autorenew + + + {{ results.length }} +
@@ -57,9 +67,6 @@
-

- autorenew -

@@ -70,10 +77,11 @@ import { useLayoutStore } from "@/stores/layout"; import url from "@/utils/url"; import { search } from "@/api"; -import { computed, inject, onMounted, ref, watch } from "vue"; +import { computed, inject, onMounted, ref, watch, onUnmounted } from "vue"; import { useI18n } from "vue-i18n"; import { useRoute } from "vue-router"; import { storeToRefs } from "pinia"; +import { StatusError } from "@/api/utils"; const boxes = { image: { label: "images", icon: "insert_photo" }, @@ -84,6 +92,7 @@ const boxes = { const layoutStore = useLayoutStore(); const fileStore = useFileStore(); +let searchAbortController = new AbortController(); const { currentPromptName } = storeToRefs(layoutStore); @@ -124,9 +133,7 @@ watch(currentPromptName, (newVal, oldVal) => { }); watch(prompt, () => { - if (results.value.length) { - reset(); - } + reset(); }); // ...mapState(useFileStore, ["isListing"]), @@ -149,6 +156,10 @@ const filteredResults = computed(() => { return results.value.slice(0, resultsCount.value); }); +const closeButtonTitle = computed(() => { + return ongoing.value ? t("buttons.stopSearch") : t("buttons.close"); +}); + onMounted(() => { if (result.value === null) { return; @@ -164,14 +175,23 @@ onMounted(() => { }); }); +onUnmounted(() => { + abortLastSearch(); +}); + const open = () => { !active.value && layoutStore.showHover("search"); }; const close = (event: Event) => { - event.stopPropagation(); - event.preventDefault(); - layoutStore.closeHovers(); + if (ongoing.value) { + abortLastSearch(); + ongoing.value = false; + } else { + event.stopPropagation(); + event.preventDefault(); + layoutStore.closeHovers(); + } }; const keyup = (event: KeyboardEvent) => { @@ -188,11 +208,16 @@ const init = (string: string) => { }; const reset = () => { + abortLastSearch(); ongoing.value = false; resultsCount.value = 50; results.value = []; }; +const abortLastSearch = () => { + searchAbortController.abort(); +}; + const submit = async (event: Event) => { event.preventDefault(); @@ -208,8 +233,16 @@ const submit = async (event: Event) => { ongoing.value = true; try { - results.value = await search(path, prompt.value); + abortLastSearch(); + searchAbortController = new AbortController(); + results.value = []; + await search(path, prompt.value, searchAbortController.signal, (item) => + results.value.push(item) + ); } catch (error: any) { + if (error instanceof StatusError && error.is_canceled) { + return; + } $showError(error); } diff --git a/frontend/src/components/Sidebar.vue b/frontend/src/components/Sidebar.vue index 531ec583..c7961e3e 100644 --- a/frontend/src/components/Sidebar.vue +++ b/frontend/src/components/Sidebar.vue @@ -129,6 +129,7 @@ import { disableExternal, disableUsedPercentage, noAuth, + logoutPage, loginPage, } from "@/utils/constants"; import { files as api } from "@/api"; @@ -159,7 +160,7 @@ export default { version: () => version, disableExternal: () => disableExternal, disableUsedPercentage: () => disableUsedPercentage, - canLogout: () => !noAuth && loginPage, + canLogout: () => !noAuth && (loginPage || logoutPage !== "/login"), }, methods: { ...mapActions(useLayoutStore, ["closeHovers", "showHover"]), diff --git a/frontend/src/components/files/CsvViewer.vue b/frontend/src/components/files/CsvViewer.vue new file mode 100644 index 00000000..b926d390 --- /dev/null +++ b/frontend/src/components/files/CsvViewer.vue @@ -0,0 +1,252 @@ + + + + + diff --git a/frontend/src/components/files/ListingItem.vue b/frontend/src/components/files/ListingItem.vue index 5db88aba..d75c2f98 100644 --- a/frontend/src/components/files/ListingItem.vue +++ b/frontend/src/components/files/ListingItem.vue @@ -265,7 +265,15 @@ const click = (event: Event | KeyboardEvent) => { } if (fileStore.selected.indexOf(props.index) !== -1) { - fileStore.removeSelected(props.index); + if ( + (event as KeyboardEvent).ctrlKey || + (event as KeyboardEvent).metaKey || + fileStore.multiple + ) { + fileStore.removeSelected(props.index); + } else { + fileStore.selected = [props.index]; + } return; } @@ -291,7 +299,6 @@ const click = (event: Event | KeyboardEvent) => { } if ( - !singleClick.value && !(event as KeyboardEvent).ctrlKey && !(event as KeyboardEvent).metaKey && !fileStore.multiple diff --git a/frontend/src/components/prompts/Copy.vue b/frontend/src/components/prompts/Copy.vue index 9b5e4c63..09040e0a 100644 --- a/frontend/src/components/prompts/Copy.vue +++ b/frontend/src/components/prompts/Copy.vue @@ -109,7 +109,8 @@ export default { return; } - this.$router.push({ path: this.dest }); + if (this.user.redirectAfterCopyMove) + this.$router.push({ path: this.dest }); }) .catch((e) => { buttons.done("copy"); diff --git a/frontend/src/components/prompts/CreateFilePath.vue b/frontend/src/components/prompts/CreateFilePath.vue index ec470f3a..166240d0 100644 --- a/frontend/src/components/prompts/CreateFilePath.vue +++ b/frontend/src/components/prompts/CreateFilePath.vue @@ -18,7 +18,7 @@ + diff --git a/frontend/src/views/files/Preview.vue b/frontend/src/views/files/Preview.vue index 69cbc489..09562856 100644 --- a/frontend/src/views/files/Preview.vue +++ b/frontend/src/views/files/Preview.vue @@ -6,7 +6,7 @@ @mousemove="toggleNavigation" @touchstart="toggleNavigation" > - + {{ name }} + {{ size }}% + (false); const autoPlay = ref(false); const previousRaw = ref(""); const nextRaw = ref(""); +const csvContent = ref(""); +const csvError = ref(""); const player = ref(null); @@ -248,6 +264,8 @@ const authStore = useAuthStore(); const fileStore = useFileStore(); const layoutStore = useLayoutStore(); +const { t } = useI18n(); + const route = useRoute(); const router = useRouter(); @@ -279,6 +297,7 @@ const isPdf = computed(() => fileStore.req?.extension.toLowerCase() == ".pdf"); const isEpub = computed( () => fileStore.req?.extension.toLowerCase() == ".epub" ); +const isCsv = computed(() => fileStore.req?.extension.toLowerCase() == ".csv"); const isResizeEnabled = computed(() => resizePreview); @@ -366,6 +385,18 @@ const updatePreview = async () => { const dirs = route.fullPath.split("/"); name.value = decodeURIComponent(dirs[dirs.length - 1]); + // Load CSV content if it's a CSV file + if (isCsv.value && fileStore.req) { + csvContent.value = ""; + csvError.value = ""; + + if (fileStore.req.size > CSV_MAX_SIZE) { + csvError.value = t("files.csvTooLarge"); + } else { + csvContent.value = fileStore.req.content ?? ""; + } + } + if (!listing.value) { try { const path = url.removeLastDir(route.path); @@ -435,4 +466,8 @@ const close = () => { }; const download = () => window.open(downloadUrl.value); + +const editAsText = () => { + router.push({ path: route.path, query: { edit: "true" } }); +}; diff --git a/frontend/src/views/settings/Profile.vue b/frontend/src/views/settings/Profile.vue index 1b079473..ffb20f0d 100644 --- a/frontend/src/views/settings/Profile.vue +++ b/frontend/src/views/settings/Profile.vue @@ -15,6 +15,14 @@ {{ t("settings.singleClick") }}

+

+ + {{ t("settings.redirectAfterCopyMove") }} +

{{ t("settings.setDateFormat") }} @@ -44,7 +52,7 @@ -

+
+
@@ -92,6 +109,7 @@ import AceEditorTheme from "@/components/settings/AceEditorTheme.vue"; import Languages from "@/components/settings/Languages.vue"; import { computed, inject, onMounted, ref } from "vue"; import { useI18n } from "vue-i18n"; +import { authMethod, noAuth } from "@/utils/constants"; const layoutStore = useLayoutStore(); const authStore = useAuthStore(); @@ -102,8 +120,11 @@ const $showError = inject("$showError")!; const password = ref(""); const passwordConf = ref(""); +const currentPassword = ref(""); +const isCurrentPasswordRequired = ref(false); const hideDotfiles = ref(false); const singleClick = ref(false); +const redirectAfterCopyMove = ref(false); const dateFormat = ref(false); const locale = ref(""); const aceEditorTheme = ref(""); @@ -128,9 +149,12 @@ onMounted(async () => { locale.value = authStore.user.locale; hideDotfiles.value = authStore.user.hideDotfiles; singleClick.value = authStore.user.singleClick; + redirectAfterCopyMove.value = authStore.user.redirectAfterCopyMove; dateFormat.value = authStore.user.dateFormat; aceEditorTheme.value = authStore.user.aceEditorTheme; layoutStore.loading = false; + isCurrentPasswordRequired.value = authMethod == "json"; + return true; }); @@ -140,6 +164,7 @@ const updatePassword = async (event: Event) => { if ( password.value !== passwordConf.value || password.value === "" || + currentPassword.value === "" || authStore.user === null ) { return; @@ -151,7 +176,7 @@ const updatePassword = async (event: Event) => { id: authStore.user.id, password: password.value, }; - await api.update(data, ["password"]); + await api.update(data, ["password"], currentPassword.value); authStore.updateUser(data); $showSuccess(t("settings.passwordUpdated")); } catch (e: any) { @@ -172,6 +197,7 @@ const updateSettings = async (event: Event) => { locale: locale.value, hideDotfiles: hideDotfiles.value, singleClick: singleClick.value, + redirectAfterCopyMove: redirectAfterCopyMove.value, dateFormat: dateFormat.value, aceEditorTheme: aceEditorTheme.value, }; @@ -180,6 +206,7 @@ const updateSettings = async (event: Event) => { "locale", "hideDotfiles", "singleClick", + "redirectAfterCopyMove", "dateFormat", "aceEditorTheme", ]); diff --git a/frontend/src/views/settings/User.vue b/frontend/src/views/settings/User.vue index a0da68ce..77786e2a 100644 --- a/frontend/src/views/settings/User.vue +++ b/frontend/src/views/settings/User.vue @@ -15,6 +15,19 @@ :isDefault="false" :isNew="isNew" /> + +

+ + +

@@ -58,11 +71,14 @@ import { computed, inject, onMounted, ref, watch } from "vue"; import { useRoute, useRouter } from "vue-router"; import { useI18n } from "vue-i18n"; import { StatusError } from "@/api/utils"; +import { authMethod } from "@/utils/constants"; const error = ref(); const originalUser = ref(); const user = ref(); const createUserDir = ref(false); +const currentPassword = ref(""); +const isCurrentPasswordRequired = ref(false); const $showError = inject("$showError")!; const $showSuccess = inject("$showSuccess")!; @@ -91,6 +107,7 @@ const fetchData = async () => { try { if (isNew.value) { const { defaults, createUserDir: _createUserDir } = await settings.get(); + isCurrentPasswordRequired.value = authMethod == "json"; createUserDir.value = _createUserDir; user.value = { ...defaults, @@ -101,6 +118,8 @@ const fetchData = async () => { id: 0, }; } else { + const { authMethod } = await settings.get(); + isCurrentPasswordRequired.value = authMethod == "json"; const id = Array.isArray(route.params.id) ? route.params.id.join("") : route.params.id; @@ -124,7 +143,7 @@ const deleteUser = async (e: Event) => { return false; } try { - await api.remove(user.value.id); + await api.remove(user.value.id, currentPassword.value); router.push({ path: "/settings/users" }); $showSuccess(t("settings.userDeleted")); } catch (err) { @@ -151,11 +170,11 @@ const save = async (event: Event) => { ...user.value, }; - const loc = await api.create(newUser); + const loc = await api.create(newUser, currentPassword.value); router.push({ path: loc || "/settings/users" }); $showSuccess(t("settings.userCreated")); } else { - await api.update(user.value); + await api.update(user.value, ["all"], currentPassword.value); if (user.value.id === authStore.user?.id) { authStore.updateUser(user.value); diff --git a/go.mod b/go.mod index abaeff87..edf46277 100644 --- a/go.mod +++ b/go.mod @@ -12,21 +12,21 @@ require ( github.com/gorilla/mux v1.8.1 github.com/gorilla/websocket v1.5.3 github.com/jellydator/ttlcache/v3 v3.4.0 - github.com/maruel/natural v1.2.1 + github.com/maruel/natural v1.3.0 github.com/marusama/semaphore/v2 v2.5.0 github.com/mholt/archives v0.1.5 github.com/mitchellh/go-homedir v1.1.0 github.com/samber/lo v1.52.0 - github.com/shirou/gopsutil/v4 v4.25.10 + github.com/shirou/gopsutil/v4 v4.25.12 github.com/spf13/afero v1.15.0 - github.com/spf13/cobra v1.10.1 + github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce - golang.org/x/crypto v0.44.0 - golang.org/x/image v0.33.0 - golang.org/x/text v0.31.0 + golang.org/x/crypto v0.47.0 + golang.org/x/image v0.35.0 + golang.org/x/text v0.33.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -44,7 +44,7 @@ require ( github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // 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/ebitengine/purego v0.9.0 // indirect + github.com/ebitengine/purego v0.9.1 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-errors/errors v1.5.1 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -73,9 +73,9 @@ require ( go.etcd.io/bbolt v1.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect - golang.org/x/net v0.46.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.40.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 551f034e..45dff859 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ github.com/dsoprea/go-utility/v2 v2.0.0-20221003142440-7a1927d49d9d/go.mod h1:LV github.com/dsoprea/go-utility/v2 v2.0.0-20221003160719-7bc88537c05e/go.mod h1:VZ7cB0pTjm1ADBWhJUOHESu4ZYy9JN+ZPqjfiW09EPU= github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 h1:DilThiXje0z+3UQ5YjYiSRRzVdtamFpvBQXKwMglWqw= github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349/go.mod h1:4GC5sXji84i/p+irqghpPFZBF8tRN/Q7+700G0/DLe8= -github.com/ebitengine/purego v0.9.0 h1:mh0zpKBIXDceC63hpvPuGLiJ8ZAa3DfrFTudmfi8A4k= -github.com/ebitengine/purego v0.9.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A= +github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= @@ -169,8 +169,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 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/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/maruel/natural v1.2.1 h1:G/y4pwtTA07lbQsMefvsmEO0VN0NfqpxprxXDM4R/4o= -github.com/maruel/natural v1.2.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= +github.com/maruel/natural v1.3.0 h1:VsmCsBmEyrR46RomtgHs5hbKADGRVtliHTyCOLFBpsg= +github.com/maruel/natural v1.3.0/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= 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/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ= @@ -204,8 +204,8 @@ github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDc github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw= github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= -github.com/shirou/gopsutil/v4 v4.25.10 h1:at8lk/5T1OgtuCp+AwrDofFRjnvosn0nkN2OLQ6g8tA= -github.com/shirou/gopsutil/v4 v4.25.10/go.mod h1:+kSwyC8DRUD9XXEHCAFjK+0nuArFJM0lva+StQAcskM= +github.com/shirou/gopsutil/v4 v4.25.12 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY= +github.com/shirou/gopsutil/v4 v4.25.12/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU= github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik= github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= @@ -214,8 +214,8 @@ github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= -github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -266,8 +266,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= -golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -279,8 +279,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH 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.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ= -golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc= +golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I= +golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk= 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= @@ -319,8 +319,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -333,8 +333,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -360,8 +360,8 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -372,8 +372,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/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.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= 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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/http/auth.go b/http/auth.go index 2e4518f0..4eceeafe 100644 --- a/http/auth.go +++ b/http/auth.go @@ -1,4 +1,4 @@ -package http +package fbhttp import ( "encoding/json" @@ -12,7 +12,9 @@ import ( "github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5/request" - fbErrors "github.com/filebrowser/filebrowser/v2/errors" + fbAuth "github.com/filebrowser/filebrowser/v2/auth" + fberrors "github.com/filebrowser/filebrowser/v2/errors" + "github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/users" ) @@ -21,17 +23,18 @@ const ( ) type userInfo struct { - ID uint `json:"id"` - Locale string `json:"locale"` - ViewMode users.ViewMode `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"` - Username string `json:"username"` - AceEditorTheme string `json:"aceEditorTheme"` + ID uint `json:"id"` + Locale string `json:"locale"` + ViewMode users.ViewMode `json:"viewMode"` + SingleClick bool `json:"singleClick"` + RedirectAfterCopyMove bool `json:"redirectAfterCopyMove"` + Perm users.Permissions `json:"perm"` + Commands []string `json:"commands"` + LockPassword bool `json:"lockPassword"` + HideDotfiles bool `json:"hideDotfiles"` + DateFormat bool `json:"dateFormat"` + Username string `json:"username"` + AceEditorTheme string `json:"aceEditorTheme"` } type authToken struct { @@ -61,6 +64,22 @@ func (e extractor) ExtractToken(r *http.Request) (string, error) { return "", request.ErrNoTokenInRequest } +func renewableErr(err error, d *data) bool { + if d.settings.AuthMethod != fbAuth.MethodProxyAuth || err == nil { + return false + } + + if d.settings.LogoutPage == settings.DefaultLogoutPage { + return false + } + + if !errors.Is(err, jwt.ErrTokenExpired) { + return false + } + + return true +} + func withUser(fn handleFunc) handleFunc { return func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { keyFunc := func(_ *jwt.Token) (interface{}, error) { @@ -68,13 +87,9 @@ func withUser(fn handleFunc) handleFunc { } var tk authToken - token, err := request.ParseFromRequest(r, &extractor{}, keyFunc, request.WithClaims(&tk)) - if err != nil || !token.Valid { - return http.StatusUnauthorized, nil - } - - err = jwt.NewValidator(jwt.WithExpirationRequired()).Validate(tk) - if err != nil { + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}), jwt.WithExpirationRequired()) + token, err := request.ParseFromRequest(r, &extractor{}, keyFunc, request.WithClaims(&tk), request.WithParser(p)) + if (err != nil || !token.Valid) && !renewableErr(err, d) { return http.StatusUnauthorized, nil } @@ -171,7 +186,7 @@ var signupHandler = func(_ http.ResponseWriter, r *http.Request, d *data) (int, log.Printf("new user: %s, home dir: [%s].", user.Username, userHome) err = d.store.Users.Save(user) - if errors.Is(err, fbErrors.ErrExist) { + if errors.Is(err, fberrors.ErrExist) { return http.StatusConflict, err } else if err != nil { return http.StatusInternalServerError, err @@ -190,17 +205,18 @@ func renewHandler(tokenExpireTime time.Duration) handleFunc { func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User, tokenExpirationTime time.Duration) (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, - Username: user.Username, - AceEditorTheme: user.AceEditorTheme, + ID: user.ID, + Locale: user.Locale, + ViewMode: user.ViewMode, + SingleClick: user.SingleClick, + RedirectAfterCopyMove: user.RedirectAfterCopyMove, + Perm: user.Perm, + LockPassword: user.LockPassword, + Commands: user.Commands, + HideDotfiles: user.HideDotfiles, + DateFormat: user.DateFormat, + Username: user.Username, + AceEditorTheme: user.AceEditorTheme, }, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(time.Now()), diff --git a/http/commands.go b/http/commands.go index 6229d597..f0380c50 100644 --- a/http/commands.go +++ b/http/commands.go @@ -1,4 +1,4 @@ -package http +package fbhttp import ( "bufio" diff --git a/http/data.go b/http/data.go index 3f949ad0..d591053e 100644 --- a/http/data.go +++ b/http/data.go @@ -1,4 +1,4 @@ -package http +package fbhttp import ( "log" diff --git a/http/headers.go b/http/headers.go index a768a46c..a18e7b53 100644 --- a/http/headers.go +++ b/http/headers.go @@ -1,6 +1,6 @@ //go:build !dev -package http +package fbhttp // global headers to append to every response var globalHeaders = map[string]string{ diff --git a/http/http.go b/http/http.go index 2d87535f..bb57f395 100644 --- a/http/http.go +++ b/http/http.go @@ -1,4 +1,4 @@ -package http +package fbhttp import ( "io/fs" @@ -11,8 +11,9 @@ import ( ) type modifyRequest struct { - What string `json:"what"` // Answer to: what data type? - Which []string `json:"which"` // Answer to: which fields? + What string `json:"what"` // Answer to: what data type? + Which []string `json:"which"` // Answer to: which fields? + CurrentPassword string `json:"current_password"` // Answer to: user logged password } func NewHandler( diff --git a/http/preview.go b/http/preview.go index cb64ba9c..e57d597b 100644 --- a/http/preview.go +++ b/http/preview.go @@ -1,5 +1,5 @@ //go:generate go-enum --sql --marshal --names --file $GOFILE -package http +package fbhttp import ( "bytes" diff --git a/http/preview_enum.go b/http/preview_enum.go index 50e3372c..2bb1a078 100644 --- a/http/preview_enum.go +++ b/http/preview_enum.go @@ -1,7 +1,7 @@ // Code generated by go-enum // DO NOT EDIT! -package http +package fbhttp import ( "database/sql/driver" diff --git a/http/public.go b/http/public.go index 92c8102d..6dcdaff1 100644 --- a/http/public.go +++ b/http/public.go @@ -1,4 +1,4 @@ -package http +package fbhttp import ( "errors" @@ -41,6 +41,7 @@ var withHashFile = func(fn handleFunc) handleFunc { Modify: d.user.Perm.Modify, Expand: false, ReadHeader: d.server.TypeDetectionByHeader, + CalcImgRes: d.server.TypeDetectionByHeader, Checker: d, Token: link.Token, }) @@ -74,6 +75,12 @@ var withHashFile = func(fn handleFunc) handleFunc { return errToStatus(err), err } + if file.IsDir { + // extract name from the last directory in the path + name := filepath.Base(strings.TrimRight(link.Path, string(filepath.Separator))) + file.Name = name + } + d.raw = file return fn(w, r, d) } diff --git a/http/public_test.go b/http/public_test.go index f102cc07..9a9c7f2e 100644 --- a/http/public_test.go +++ b/http/public_test.go @@ -1,4 +1,4 @@ -package http +package fbhttp import ( "fmt" diff --git a/http/raw.go b/http/raw.go index f2548155..a6857ffb 100644 --- a/http/raw.go +++ b/http/raw.go @@ -1,4 +1,4 @@ -package http +package fbhttp import ( "errors" @@ -123,6 +123,7 @@ func getFiles(d *data, path, commonPath string) ([]archives.FileInfo, error) { if path != commonPath { nameInArchive := strings.TrimPrefix(path, commonPath) nameInArchive = strings.TrimPrefix(nameInArchive, string(filepath.Separator)) + nameInArchive = filepath.ToSlash(nameInArchive) archiveFiles = append(archiveFiles, archives.FileInfo{ FileInfo: info, diff --git a/http/resource.go b/http/resource.go index fba422df..0a8da271 100644 --- a/http/resource.go +++ b/http/resource.go @@ -1,4 +1,4 @@ -package http +package fbhttp import ( "context" @@ -17,7 +17,7 @@ import ( "github.com/shirou/gopsutil/v4/disk" "github.com/spf13/afero" - fbErrors "github.com/filebrowser/filebrowser/v2/errors" + fberrors "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/fileutils" ) @@ -44,7 +44,7 @@ var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d if checksum := r.URL.Query().Get("checksum"); checksum != "" { err := file.Checksum(checksum) - if errors.Is(err, fbErrors.ErrInvalidOption) { + if errors.Is(err, fberrors.ErrInvalidOption) { return http.StatusBadRequest, nil } else if err != nil { return http.StatusInternalServerError, err @@ -238,7 +238,7 @@ func checkParent(src, dst string) error { rel = filepath.ToSlash(rel) if !strings.HasPrefix(rel, "../") && rel != ".." && rel != "." { - return fbErrors.ErrSourceIsParent + return fberrors.ErrSourceIsParent } return nil @@ -280,6 +280,12 @@ func writeFile(afs afero.Fs, dst string, in io.Reader, fileMode, dirMode fs.File return nil, err } + // Sync the file to ensure all data is written to storage. + // to prevent file corruption. + if err := file.Sync(); err != nil { + return nil, err + } + // Gets the info about the file. info, err := file.Stat() if err != nil { @@ -304,13 +310,13 @@ func patchAction(ctx context.Context, action, src, dst string, d *data, fileCach switch action { case "copy": if !d.user.Perm.Create { - return fbErrors.ErrPermissionDenied + return fberrors.ErrPermissionDenied } return fileutils.Copy(d.user.Fs, src, dst, d.settings.FileMode, d.settings.DirMode) case "rename": if !d.user.Perm.Rename { - return fbErrors.ErrPermissionDenied + return fberrors.ErrPermissionDenied } src = path.Clean("/" + src) dst = path.Clean("/" + dst) @@ -335,7 +341,7 @@ func patchAction(ctx context.Context, action, src, dst string, d *data, fileCach return fileutils.MoveFile(d.user.Fs, src, dst, d.settings.FileMode, d.settings.DirMode) default: - return fmt.Errorf("unsupported action %s: %w", action, fbErrors.ErrInvalidRequestParams) + return fmt.Errorf("unsupported action %s: %w", action, fberrors.ErrInvalidRequestParams) } } diff --git a/http/search.go b/http/search.go index 1c78b781..48a44c9b 100644 --- a/http/search.go +++ b/http/search.go @@ -1,28 +1,82 @@ -package http +package fbhttp import ( + "context" + "encoding/json" + "errors" "net/http" "os" + "sync" + "time" "github.com/filebrowser/filebrowser/v2/search" ) +const searchPingInterval = 5 + var searchHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { - response := []map[string]interface{}{} + response := make(chan map[string]interface{}) + ctx, cancel := context.WithCancelCause(r.Context()) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + // Avoid connection timeout + timeout := time.NewTimer(searchPingInterval * time.Second) + defer timeout.Stop() + for { + var err error + var infoBytes []byte + select { + case info := <-response: + if info == nil { + return + } + infoBytes, err = json.Marshal(info) + case <-timeout.C: + // Send a heartbeat packet + infoBytes = nil + case <-ctx.Done(): + return + } + if err != nil { + cancel(err) + return + } + _, err = w.Write(infoBytes) + if err == nil { + _, err = w.Write([]byte("\n")) + } + if err != nil { + cancel(err) + return + } + if flusher, ok := w.(http.Flusher); ok { + flusher.Flush() + } + } + }() query := r.URL.Query().Get("query") - err := search.Search(d.user.Fs, r.URL.Path, query, d, func(path string, f os.FileInfo) error { - response = append(response, map[string]interface{}{ + err := search.Search(ctx, d.user.Fs, r.URL.Path, query, d, func(path string, f os.FileInfo) error { + select { + case <-ctx.Done(): + case response <- map[string]interface{}{ "dir": f.IsDir(), "path": path, - }) - - return nil + }: + } + return context.Cause(ctx) }) - - if err != nil { + close(response) + wg.Wait() + if err == nil { + err = context.Cause(ctx) + } + // ignore cancellation errors from user aborts + if err != nil && !errors.Is(err, context.Canceled) { return http.StatusInternalServerError, err } - return renderJSON(w, r, response) + return 0, nil }) diff --git a/http/settings.go b/http/settings.go index 500e1c2c..a07d322e 100644 --- a/http/settings.go +++ b/http/settings.go @@ -1,4 +1,4 @@ -package http +package fbhttp import ( "encoding/json" @@ -15,6 +15,7 @@ type settingsData struct { MinimumPasswordLength uint `json:"minimumPasswordLength"` UserHomeBasePath string `json:"userHomeBasePath"` Defaults settings.UserDefaults `json:"defaults"` + AuthMethod settings.AuthMethod `json:"authMethod"` Rules []rules.Rule `json:"rules"` Branding settings.Branding `json:"branding"` Tus settings.Tus `json:"tus"` @@ -30,6 +31,7 @@ var settingsGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, MinimumPasswordLength: d.settings.MinimumPasswordLength, UserHomeBasePath: d.settings.UserHomeBasePath, Defaults: d.settings.Defaults, + AuthMethod: d.settings.AuthMethod, Rules: d.settings.Rules, Branding: d.settings.Branding, Tus: d.settings.Tus, diff --git a/http/share.go b/http/share.go index 670629bd..509a7b21 100644 --- a/http/share.go +++ b/http/share.go @@ -1,4 +1,4 @@ -package http +package fbhttp import ( "crypto/rand" @@ -14,7 +14,7 @@ import ( "golang.org/x/crypto/bcrypt" - fbErrors "github.com/filebrowser/filebrowser/v2/errors" + fberrors "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/share" ) @@ -38,7 +38,7 @@ var shareListHandler = withPermShare(func(w http.ResponseWriter, r *http.Request } else { s, err = d.store.Share.FindByUserID(d.user.ID) } - if errors.Is(err, fbErrors.ErrNotExist) { + if errors.Is(err, fberrors.ErrNotExist) { return renderJSON(w, r, []*share.Link{}) } @@ -58,7 +58,7 @@ var shareListHandler = withPermShare(func(w http.ResponseWriter, r *http.Request var shareGetsHandler = withPermShare(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { s, err := d.store.Share.Gets(r.URL.Path, d.user.ID) - if errors.Is(err, fbErrors.ErrNotExist) { + if errors.Is(err, fberrors.ErrNotExist) { return renderJSON(w, r, []*share.Link{}) } diff --git a/http/static.go b/http/static.go index be2c135c..d996cc07 100644 --- a/http/static.go +++ b/http/static.go @@ -1,4 +1,4 @@ -package http +package fbhttp import ( "encoding/json" @@ -38,6 +38,7 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys "Signup": d.settings.Signup, "NoAuth": d.settings.AuthMethod == auth.MethodNoAuth, "AuthMethod": d.settings.AuthMethod, + "LogoutPage": d.settings.LogoutPage, "LoginPage": auther.LoginPage(), "CSS": false, "ReCaptcha": false, diff --git a/http/subtitle.go b/http/subtitle.go index 71fe8ae4..07c2dcfc 100644 --- a/http/subtitle.go +++ b/http/subtitle.go @@ -1,4 +1,4 @@ -package http +package fbhttp import ( "bytes" diff --git a/http/tus_handlers.go b/http/tus_handlers.go index 05671cf7..498d776f 100644 --- a/http/tus_handlers.go +++ b/http/tus_handlers.go @@ -1,4 +1,4 @@ -package http +package fbhttp import ( "context" @@ -256,6 +256,12 @@ func tusPatchHandler() handleFunc { return http.StatusInternalServerError, fmt.Errorf("could not write to file: %w", err) } + // Sync the file to ensure all data is written to storage + // to prevent file corruption. + if err := openFile.Sync(); err != nil { + return http.StatusInternalServerError, fmt.Errorf("could not sync file: %w", err) + } + newOffset := uploadOffset + bytesWritten w.Header().Set("Upload-Offset", strconv.FormatInt(newOffset, 10)) diff --git a/http/users.go b/http/users.go index 4185e82e..5604dc38 100644 --- a/http/users.go +++ b/http/users.go @@ -1,4 +1,4 @@ -package http +package fbhttp import ( "encoding/json" @@ -12,7 +12,8 @@ import ( "golang.org/x/text/cases" "golang.org/x/text/language" - fbErrors "github.com/filebrowser/filebrowser/v2/errors" + "github.com/filebrowser/filebrowser/v2/auth" + fberrors "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/users" ) @@ -36,7 +37,7 @@ func getUserID(r *http.Request) (uint, error) { func getUser(_ http.ResponseWriter, r *http.Request) (*modifyUserRequest, error) { if r.Body == nil { - return nil, fbErrors.ErrEmptyRequest + return nil, fberrors.ErrEmptyRequest } req := &modifyUserRequest{} @@ -46,7 +47,7 @@ func getUser(_ http.ResponseWriter, r *http.Request) (*modifyUserRequest, error) } if req.What != "user" { - return nil, fbErrors.ErrInvalidDataType + return nil, fberrors.ErrInvalidDataType } return req, nil @@ -87,7 +88,7 @@ var usersGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d * var userGetHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { u, err := d.store.Users.Get(d.server.Root, d.raw.(uint)) - if errors.Is(err, fbErrors.ErrNotExist) { + if errors.Is(err, fberrors.ErrNotExist) { return http.StatusNotFound, err } @@ -102,7 +103,25 @@ var userGetHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request return renderJSON(w, r, u) }) -var userDeleteHandler = withSelfOrAdmin(func(_ http.ResponseWriter, _ *http.Request, d *data) (int, error) { +var userDeleteHandler = withSelfOrAdmin(func(_ http.ResponseWriter, r *http.Request, d *data) (int, error) { + if r.Body == nil { + return http.StatusBadRequest, fberrors.ErrEmptyRequest + } + + var body struct { + CurrentPassword string `json:"current_password"` + } + + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + return http.StatusBadRequest, err + } + + if d.settings.AuthMethod == auth.MethodJSONAuth { + if !users.CheckPwd(body.CurrentPassword, d.user.Password) { + return http.StatusBadRequest, fberrors.ErrCurrentPasswordIncorrect + } + } + err := d.store.Users.Delete(d.raw.(uint)) if err != nil { return errToStatus(err), err @@ -117,12 +136,18 @@ var userPostHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d * return http.StatusBadRequest, err } + if d.settings.AuthMethod == auth.MethodJSONAuth { + if !users.CheckPwd(req.CurrentPassword, d.user.Password) { + return http.StatusBadRequest, fberrors.ErrCurrentPasswordIncorrect + } + } + if len(req.Which) != 0 { return http.StatusBadRequest, nil } if req.Data.Password == "" { - return http.StatusBadRequest, fbErrors.ErrEmptyPassword + return http.StatusBadRequest, fberrors.ErrEmptyPassword } req.Data.Password, err = users.ValidateAndHashPwd(req.Data.Password, d.settings.MinimumPasswordLength) @@ -153,6 +178,27 @@ var userPutHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request return http.StatusBadRequest, err } + if d.settings.AuthMethod == auth.MethodJSONAuth { + var sensibleFields = map[string]struct{}{ + "all": {}, + "username": {}, + "password": {}, + "scope": {}, + "lockPassword": {}, + "commands": {}, + "perm": {}, + } + + for _, field := range req.Which { + if _, ok := sensibleFields[field]; ok { + if !users.CheckPwd(req.CurrentPassword, d.user.Password) { + return http.StatusBadRequest, fberrors.ErrCurrentPasswordIncorrect + } + break + } + } + } + if req.Data.ID != d.raw.(uint) { return http.StatusBadRequest, nil } diff --git a/http/utils.go b/http/utils.go index 03615263..c76cecf4 100644 --- a/http/utils.go +++ b/http/utils.go @@ -1,4 +1,4 @@ -package http +package fbhttp import ( "encoding/json" diff --git a/renovate.json b/renovate.json index 2cc079cc..ae7193e8 100644 --- a/renovate.json +++ b/renovate.json @@ -10,5 +10,6 @@ "postUpdateOptions": [ "gomodUpdateImportPaths", "gomodTidy" - ] + ], + "schedule": "* * * * 0,6" } diff --git a/search/search.go b/search/search.go index 380dcde3..c58cc22f 100644 --- a/search/search.go +++ b/search/search.go @@ -1,6 +1,7 @@ package search import ( + "context" "os" "path" "path/filepath" @@ -18,13 +19,17 @@ type searchOptions struct { } // Search searches for a query in a fs. -func Search(fs afero.Fs, scope, query string, checker rules.Checker, found func(path string, f os.FileInfo) error) error { +func Search(ctx context.Context, + fs afero.Fs, scope, query string, checker rules.Checker, found func(path string, f os.FileInfo) error) error { search := parseSearch(query) scope = filepath.ToSlash(filepath.Clean(scope)) scope = path.Join("/", scope) return afero.Walk(fs, scope, func(fPath string, f os.FileInfo, _ error) error { + if ctx.Err() != nil { + return context.Cause(ctx) + } fPath = filepath.ToSlash(filepath.Clean(fPath)) fPath = path.Join("/", fPath) relativePath := strings.TrimPrefix(fPath, scope) diff --git a/settings/defaults.go b/settings/defaults.go index 5b6c3f2a..46024179 100644 --- a/settings/defaults.go +++ b/settings/defaults.go @@ -8,16 +8,17 @@ import ( // 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"` - Perm users.Permissions `json:"perm"` - Commands []string `json:"commands"` - HideDotfiles bool `json:"hideDotfiles"` - DateFormat bool `json:"dateFormat"` - AceEditorTheme string `json:"aceEditorTheme"` + Scope string `json:"scope"` + Locale string `json:"locale"` + ViewMode users.ViewMode `json:"viewMode"` + SingleClick bool `json:"singleClick"` + RedirectAfterCopyMove bool `json:"redirectAfterCopyMove"` + Sorting files.Sorting `json:"sorting"` + Perm users.Permissions `json:"perm"` + Commands []string `json:"commands"` + HideDotfiles bool `json:"hideDotfiles"` + DateFormat bool `json:"dateFormat"` + AceEditorTheme string `json:"aceEditorTheme"` } // Apply applies the default options to a user. @@ -26,6 +27,7 @@ func (d *UserDefaults) Apply(u *users.User) { u.Locale = d.Locale u.ViewMode = d.ViewMode u.SingleClick = d.SingleClick + u.RedirectAfterCopyMove = d.RedirectAfterCopyMove u.Perm = d.Perm u.Sorting = d.Sorting u.Commands = d.Commands diff --git a/settings/settings.go b/settings/settings.go index c263bf36..d71be16a 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -11,6 +11,7 @@ import ( ) const DefaultUsersHomeBasePath = "/users" +const DefaultLogoutPage = "/login" const DefaultMinimumPasswordLength = 12 const DefaultFileMode = 0640 const DefaultDirMode = 0750 @@ -27,6 +28,7 @@ type Settings struct { UserHomeBasePath string `json:"userHomeBasePath"` Defaults UserDefaults `json:"defaults"` AuthMethod AuthMethod `json:"authMethod"` + LogoutPage string `json:"logoutPage"` Branding Branding `json:"branding"` Tus Tus `json:"tus"` Commands map[string][]string `json:"commands"` @@ -57,6 +59,7 @@ type Server struct { ResizePreview bool `json:"resizePreview"` EnableExec bool `json:"enableExec"` TypeDetectionByHeader bool `json:"typeDetectionByHeader"` + ImageResolutionCal bool `json:"imageResolutionCalculation"` AuthHook string `json:"authHook"` TokenExpirationTime string `json:"tokenExpirationTime"` } diff --git a/settings/storage.go b/settings/storage.go index d401996d..573024f7 100644 --- a/settings/storage.go +++ b/settings/storage.go @@ -1,7 +1,7 @@ package settings import ( - "github.com/filebrowser/filebrowser/v2/errors" + fberrors "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/rules" "github.com/filebrowser/filebrowser/v2/users" ) @@ -30,24 +30,34 @@ func (s *Storage) Get() (*Settings, error) { if err != nil { return nil, err } + if set.UserHomeBasePath == "" { set.UserHomeBasePath = DefaultUsersHomeBasePath } + + if set.LogoutPage == "" { + set.LogoutPage = DefaultLogoutPage + } + if set.MinimumPasswordLength == 0 { set.MinimumPasswordLength = DefaultMinimumPasswordLength } + if set.Tus == (Tus{}) { set.Tus = Tus{ ChunkSize: DefaultTusChunkSize, RetryCount: DefaultTusRetryCount, } } + if set.FileMode == 0 { set.FileMode = DefaultFileMode } + if set.DirMode == 0 { set.DirMode = DefaultDirMode } + return set, nil } @@ -62,7 +72,7 @@ var defaultEvents = []string{ // Save saves the settings for the current instance. func (s *Storage) Save(set *Settings) error { if len(set.Key) == 0 { - return errors.ErrEmptyKey + return fberrors.ErrEmptyKey } if set.Defaults.Locale == "" { diff --git a/share/storage.go b/share/storage.go index 398c192d..76ba26eb 100644 --- a/share/storage.go +++ b/share/storage.go @@ -3,7 +3,7 @@ package share import ( "time" - "github.com/filebrowser/filebrowser/v2/errors" + fberrors "github.com/filebrowser/filebrowser/v2/errors" ) // StorageBackend is the interface to implement for a share storage. @@ -79,7 +79,7 @@ func (s *Storage) GetByHash(hash string) (*Link, error) { if err := s.Delete(link.Hash); err != nil { return nil, err } - return nil, errors.ErrNotExist + return nil, fberrors.ErrNotExist } return link, nil diff --git a/storage/bolt/auth.go b/storage/bolt/auth.go index cf15a8fe..1de95192 100644 --- a/storage/bolt/auth.go +++ b/storage/bolt/auth.go @@ -4,7 +4,7 @@ import ( "github.com/asdine/storm/v3" "github.com/filebrowser/filebrowser/v2/auth" - "github.com/filebrowser/filebrowser/v2/errors" + fberrors "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/settings" ) @@ -25,7 +25,7 @@ func (s authBackend) Get(t settings.AuthMethod) (auth.Auther, error) { case auth.MethodNoAuth: auther = &auth.NoAuth{} default: - return nil, errors.ErrInvalidAuthMethod + return nil, fberrors.ErrInvalidAuthMethod } return auther, get(s.db, "auther", auther) diff --git a/storage/bolt/share.go b/storage/bolt/share.go index 373533f1..c5b91483 100644 --- a/storage/bolt/share.go +++ b/storage/bolt/share.go @@ -6,7 +6,7 @@ import ( "github.com/asdine/storm/v3" "github.com/asdine/storm/v3/q" - fbErrors "github.com/filebrowser/filebrowser/v2/errors" + fberrors "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/share" ) @@ -18,7 +18,7 @@ func (s shareBackend) All() ([]*share.Link, error) { var v []*share.Link err := s.db.All(&v) if errors.Is(err, storm.ErrNotFound) { - return v, fbErrors.ErrNotExist + return v, fberrors.ErrNotExist } return v, err @@ -28,7 +28,7 @@ func (s shareBackend) FindByUserID(id uint) ([]*share.Link, error) { var v []*share.Link err := s.db.Select(q.Eq("UserID", id)).Find(&v) if errors.Is(err, storm.ErrNotFound) { - return v, fbErrors.ErrNotExist + return v, fberrors.ErrNotExist } return v, err @@ -38,7 +38,7 @@ func (s shareBackend) GetByHash(hash string) (*share.Link, error) { var v share.Link err := s.db.One("Hash", hash, &v) if errors.Is(err, storm.ErrNotFound) { - return nil, fbErrors.ErrNotExist + return nil, fberrors.ErrNotExist } return &v, err @@ -48,7 +48,7 @@ func (s shareBackend) GetPermanent(path string, id uint) (*share.Link, error) { var v share.Link err := s.db.Select(q.Eq("Path", path), q.Eq("Expire", 0), q.Eq("UserID", id)).First(&v) if errors.Is(err, storm.ErrNotFound) { - return nil, fbErrors.ErrNotExist + return nil, fberrors.ErrNotExist } return &v, err @@ -58,7 +58,7 @@ func (s shareBackend) Gets(path string, id uint) ([]*share.Link, error) { var v []*share.Link err := s.db.Select(q.Eq("Path", path), q.Eq("UserID", id)).Find(&v) if errors.Is(err, storm.ErrNotFound) { - return v, fbErrors.ErrNotExist + return v, fberrors.ErrNotExist } return v, err diff --git a/storage/bolt/users.go b/storage/bolt/users.go index dc901c34..974c0a48 100644 --- a/storage/bolt/users.go +++ b/storage/bolt/users.go @@ -7,7 +7,7 @@ import ( "github.com/asdine/storm/v3" - fbErrors "github.com/filebrowser/filebrowser/v2/errors" + fberrors "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/users" ) @@ -25,14 +25,14 @@ func (st usersBackend) GetBy(i interface{}) (user *users.User, err error) { case string: arg = "Username" default: - return nil, fbErrors.ErrInvalidDataType + return nil, fberrors.ErrInvalidDataType } err = st.db.One(arg, i, user) if err != nil { if errors.Is(err, storm.ErrNotFound) { - return nil, fbErrors.ErrNotExist + return nil, fberrors.ErrNotExist } return nil, err } @@ -44,7 +44,7 @@ func (st usersBackend) Gets() ([]*users.User, error) { var allUsers []*users.User err := st.db.All(&allUsers) if errors.Is(err, storm.ErrNotFound) { - return nil, fbErrors.ErrNotExist + return nil, fberrors.ErrNotExist } if err != nil { @@ -76,7 +76,7 @@ func (st usersBackend) Update(user *users.User, fields ...string) error { func (st usersBackend) Save(user *users.User) error { err := st.db.Save(user) if errors.Is(err, storm.ErrAlreadyExists) { - return fbErrors.ErrExist + return fberrors.ErrExist } return err } diff --git a/storage/bolt/utils.go b/storage/bolt/utils.go index e275a0d1..b16bccff 100644 --- a/storage/bolt/utils.go +++ b/storage/bolt/utils.go @@ -5,13 +5,13 @@ import ( "github.com/asdine/storm/v3" - fbErrors "github.com/filebrowser/filebrowser/v2/errors" + fberrors "github.com/filebrowser/filebrowser/v2/errors" ) func get(db *storm.DB, name string, to interface{}) error { err := db.Get("config", name, to) if errors.Is(err, storm.ErrNotFound) { - return fbErrors.ErrNotExist + return fberrors.ErrNotExist } return err diff --git a/users/password.go b/users/password.go index 3a01053d..2e355ad4 100644 --- a/users/password.go +++ b/users/password.go @@ -6,17 +6,17 @@ import ( "golang.org/x/crypto/bcrypt" - fbErrors "github.com/filebrowser/filebrowser/v2/errors" + fberrors "github.com/filebrowser/filebrowser/v2/errors" ) // ValidateAndHashPwd validates and hashes a password. func ValidateAndHashPwd(password string, minimumLength uint) (string, error) { if uint(len(password)) < minimumLength { - return "", fbErrors.ErrShortPassword{MinimumLength: minimumLength} + return "", fberrors.ErrShortPassword{MinimumLength: minimumLength} } if _, ok := commonPasswords[password]; ok { - return "", fbErrors.ErrEasyPassword + return "", fberrors.ErrEasyPassword } return HashPwd(password) diff --git a/users/storage.go b/users/storage.go index 1dd0da3f..43c65fe4 100644 --- a/users/storage.go +++ b/users/storage.go @@ -4,7 +4,7 @@ import ( "sync" "time" - "github.com/filebrowser/filebrowser/v2/errors" + fberrors "github.com/filebrowser/filebrowser/v2/errors" ) // StorageBackend is the interface to implement for a users storage. @@ -109,16 +109,16 @@ func (s *Storage) Delete(id interface{}) error { return err } if user.ID == 1 { - return errors.ErrRootUserDeletion + return fberrors.ErrRootUserDeletion } return s.back.DeleteByUsername(id) case uint: if id == 1 { - return errors.ErrRootUserDeletion + return fberrors.ErrRootUserDeletion } return s.back.DeleteByID(id) default: - return errors.ErrInvalidDataType + return fberrors.ErrInvalidDataType } } diff --git a/users/users.go b/users/users.go index 987a94f2..7181b299 100644 --- a/users/users.go +++ b/users/users.go @@ -5,7 +5,7 @@ import ( "github.com/spf13/afero" - "github.com/filebrowser/filebrowser/v2/errors" + fberrors "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/rules" ) @@ -20,22 +20,23 @@ const ( // 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 ViewMode `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"` - AceEditorTheme string `json:"aceEditorTheme"` + 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 ViewMode `json:"viewMode"` + SingleClick bool `json:"singleClick"` + RedirectAfterCopyMove bool `json:"redirectAfterCopyMove"` + 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"` + AceEditorTheme string `json:"aceEditorTheme"` } // GetRules implements rules.Provider. @@ -64,11 +65,11 @@ func (u *User) Clean(baseScope string, fields ...string) error { switch field { case "Username": if u.Username == "" { - return errors.ErrEmptyUsername + return fberrors.ErrEmptyUsername } case "Password": if u.Password == "" { - return errors.ErrEmptyPassword + return fberrors.ErrEmptyPassword } case "ViewMode": if u.ViewMode == "" { diff --git a/www/docs/cli/filebrowser-config-init.md b/www/docs/cli/filebrowser-config-init.md index 6ea484e6..0f16efa7 100644 --- a/www/docs/cli/filebrowser-config-init.md +++ b/www/docs/cli/filebrowser-config-init.md @@ -21,6 +21,7 @@ filebrowser config init [flags] -a, --address string address to listen on (default "127.0.0.1") --auth.command string command for auth.method=hook --auth.header string HTTP header for auth.method=proxy + --auth.logoutPage string url of custom logout page --auth.method string authentication type (default "json") -b, --baseURL string base url --branding.color string set the theme color @@ -35,6 +36,7 @@ filebrowser config init [flags] --dateFormat use date format (true for absolute time, false for relative) --dirMode string mode bits that new directories are created with (default "0o750") --disableExec disables Command Runner feature (default true) + --disableImageResolutionCalc disables image resolution calculation by reading image files --disablePreviewResize disable resize of image previews --disableThumbnails disable image thumbnails --disableTypeDetectionByHeader disables type detection by reading file headers @@ -59,6 +61,7 @@ filebrowser config init [flags] --recaptcha.host string use another host for ReCAPTCHA. recaptcha.net might be useful in China (default "https://www.google.com") --recaptcha.key string ReCaptcha site key --recaptcha.secret string ReCaptcha secret + --redirectAfterCopyMove redirect to destination after copy/move -r, --root string root to prepend to relative paths (default ".") --scope string scope for users (default ".") --shell string shell command to which other commands should be appended diff --git a/www/docs/cli/filebrowser-config-set.md b/www/docs/cli/filebrowser-config-set.md index 93515ab4..603bb506 100644 --- a/www/docs/cli/filebrowser-config-set.md +++ b/www/docs/cli/filebrowser-config-set.md @@ -18,6 +18,7 @@ filebrowser config set [flags] -a, --address string address to listen on (default "127.0.0.1") --auth.command string command for auth.method=hook --auth.header string HTTP header for auth.method=proxy + --auth.logoutPage string url of custom logout page --auth.method string authentication type (default "json") -b, --baseURL string base url --branding.color string set the theme color @@ -32,6 +33,7 @@ filebrowser config set [flags] --dateFormat use date format (true for absolute time, false for relative) --dirMode string mode bits that new directories are created with (default "0o750") --disableExec disables Command Runner feature (default true) + --disableImageResolutionCalc disables image resolution calculation by reading image files --disablePreviewResize disable resize of image previews --disableThumbnails disable image thumbnails --disableTypeDetectionByHeader disables type detection by reading file headers @@ -56,6 +58,7 @@ filebrowser config set [flags] --recaptcha.host string use another host for ReCAPTCHA. recaptcha.net might be useful in China (default "https://www.google.com") --recaptcha.key string ReCaptcha site key --recaptcha.secret string ReCaptcha secret + --redirectAfterCopyMove redirect to destination after copy/move -r, --root string root to prepend to relative paths (default ".") --scope string scope for users (default ".") --shell string shell command to which other commands should be appended diff --git a/www/docs/cli/filebrowser-users-add.md b/www/docs/cli/filebrowser-users-add.md index c6c5b53d..1de95e4d 100644 --- a/www/docs/cli/filebrowser-users-add.md +++ b/www/docs/cli/filebrowser-users-add.md @@ -28,6 +28,7 @@ filebrowser users add [flags] --perm.modify modify perm for users (default true) --perm.rename rename perm for users (default true) --perm.share share perm for users (default true) + --redirectAfterCopyMove redirect to destination after copy/move --scope string scope for users (default ".") --singleClick use single clicks only --sorting.asc sorting by ascending order diff --git a/www/docs/cli/filebrowser-users-update.md b/www/docs/cli/filebrowser-users-update.md index 996bdac7..4ba7d608 100644 --- a/www/docs/cli/filebrowser-users-update.md +++ b/www/docs/cli/filebrowser-users-update.md @@ -30,6 +30,7 @@ filebrowser users update [flags] --perm.modify modify perm for users (default true) --perm.rename rename perm for users (default true) --perm.share share perm for users (default true) + --redirectAfterCopyMove redirect to destination after copy/move --scope string scope for users (default ".") --singleClick use single clicks only --sorting.asc sorting by ascending order diff --git a/www/docs/cli/filebrowser.md b/www/docs/cli/filebrowser.md index 8383ec97..5d18e8d2 100644 --- a/www/docs/cli/filebrowser.md +++ b/www/docs/cli/filebrowser.md @@ -26,6 +26,11 @@ file named .filebrowser.{json, toml, yaml, yml} in the following directories: - $HOME/ - /etc/filebrowser/ +**Note:** Only the options listed below can be set via the config file or +environment variables. Other configuration options live exclusively in the +database and so they must be set by the "config set" or "config +import" commands. + The precedence of the configuration values are as follows: - Flags @@ -52,6 +57,7 @@ filebrowser [flags] -c, --config string config file path -d, --database string database path (default "./filebrowser.db") --disableExec disables Command Runner feature (default true) + --disableImageResolutionCalc disables image resolution calculation by reading image files --disablePreviewResize disable resize of image previews --disableThumbnails disable image thumbnails --disableTypeDetectionByHeader disables type detection by reading file headers