diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 0f222ecd..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
@@ -97,7 +97,7 @@ jobs:
uses: docker/setup-buildx-action@v3
- name: Install Task
uses: go-task/setup-task@v1
- - run: task build-frontend
+ - run: task build:frontend
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
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 901a89b8..8819f48b 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,132 +1,14 @@
version: "2"
linters:
- # inverted configuration with `default: all` and `disable` is not scalable during updates of golangci-lint
- default: none
+ default: standard
enable:
- - bodyclose
- - dogsled
- - dupl
- - errcheck
- - errorlint
- - exhaustive
- - funlen
- - gocheckcompilerdirectives
- - gochecknoinits
- gocritic
- - gocyclo
- - godox
- - goprintffuncname
- - gosec
- govet
- - ineffassign
- - lll
- - misspell
- - mnd
- - nakedret
- - nolintlint
- - prealloc
- revive
- - rowserrcheck
- - staticcheck
- - testifylint
- - unconvert
- - unparam
- - unused
- - whitespace
- settings:
- dupl:
- threshold: 100
- exhaustive:
- default-signifies-exhaustive: false
- funlen:
- lines: 100
- statements: 50
- gocritic:
- disabled-checks:
- - dupImport # https://github.com/go-critic/go-critic/issues/845
- - ifElseChain
- - octalLiteral
- - whyNoLint
- - wrapperFunc
- enabled-tags:
- - diagnostic
- - experimental
- - opinionated
- - performance
- - style
- gocyclo:
- min-complexity: 15
- govet:
- enable:
- - nilness
- - shadow
- lll:
- line-length: 140
- misspell:
- locale: US
- mnd:
- # don't include the "operation" and "assign"
- checks:
- - argument
- - case
- - condition
- - return
- ignored-numbers:
- - "0"
- - "1"
- - "2"
- - "3"
- - "0666"
- - "0700"
- - "0700"
- ignored-functions:
- - strings.SplitN
- - make
- nolintlint:
- allow-unused: false # report any unused nolint directives
- require-explanation: false # require an explanation for nolint directives
- require-specific: true # require nolint directives to be specific about which linter is being skipped
- staticcheck:
- checks:
- - "all"
- - "-QF*"
exclusions:
- generated: lax
presets:
- - comments
- - common-false-positives
- - legacy
- std-error-handling
- rules:
- - linters:
- - gochecknoinits
- path: cmd/.*.go
- - linters:
- - dupl
- - funlen
- - gochecknoinits
- - gocyclo
- - lll
- - scopelint
- path: .*_test.go
- - linters:
- - misspell
- text: "[aA]uther"
- - linters:
- - mnd
- text: strconv.Parse
- paths:
- - frontend/
-
-formatters:
- enable:
- - goimports
- settings:
- goimports:
- local-prefixes:
- - github.com/filebrowser/filebrowser
- exclusions:
- generated: lax
+ - 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 4ea3d811..0f49dca9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,8 +2,209 @@
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)
+
+
+### Bug Fixes
+
+* options should only override if set ([420adea](https://github.com/filebrowser/filebrowser/commit/420adea7e61a1c182cddd6fb2544a0752e5709f7))
+
+## [2.48.0](https://github.com/filebrowser/filebrowser/compare/v2.47.0...v2.48.0) (2025-11-17)
+
+
+### Features
+
+* consistent flags and environment variables ([#5549](https://github.com/filebrowser/filebrowser/issues/5549)) ([0a0cb80](https://github.com/filebrowser/filebrowser/commit/0a0cb8046fce52f1ff926171b34bcdb7cd39aab3))
+
+
+### Bug Fixes
+
+* add tokenExpirationTime to `config init` and troubleshoot docs ([#5546](https://github.com/filebrowser/filebrowser/issues/5546)) ([8c5dc76](https://github.com/filebrowser/filebrowser/commit/8c5dc7641e6f8aadd9e5d5d3b25a2ad9f1ec9a1e))
+* use all available flags in quick setup ([f41585f](https://github.com/filebrowser/filebrowser/commit/f41585f0392d65c08c01ab65b62d3eeb04c03b7d))
+
+
+### Refactorings
+
+* reuse logic for config init and set ([89be0b1](https://github.com/filebrowser/filebrowser/commit/89be0b1873527987dd2dddac746e93b8bc684d46))
+
+## [2.47.0](https://github.com/filebrowser/filebrowser/compare/v2.46.1...v2.47.0) (2025-11-16)
+
+
+### Features
+
+* add TUS settings to the command line ([#5556](https://github.com/filebrowser/filebrowser/issues/5556)) ([e24e1f1](https://github.com/filebrowser/filebrowser/commit/e24e1f1abae9e80add620c4ad65660ca1b575a49))
+* remove importer of v1 config ([#5550](https://github.com/filebrowser/filebrowser/issues/5550)) ([ceb5e72](https://github.com/filebrowser/filebrowser/commit/ceb5e723f3ee2c966bb561a804015246450280ca))
+
+
+### Bug Fixes
+
+* exit 0 when gracefully shutting down ([#5555](https://github.com/filebrowser/filebrowser/issues/5555)) ([5de4099](https://github.com/filebrowser/filebrowser/commit/5de4099cba2cf012d4a213c8eb29c412fc72c151))
+
+## [2.46.1](https://github.com/filebrowser/filebrowser/compare/v2.46.0...v2.46.1) (2025-11-15)
+
+
+### Bug Fixes
+
+* env key replacer and remove unused function ([#5547](https://github.com/filebrowser/filebrowser/issues/5547)) ([13814e1](https://github.com/filebrowser/filebrowser/commit/13814e11197ebd9101940883e3ca85998f86d442))
+* remove duplicated 'hide-defaults' flag (is 'hideDefaults') ([#5548](https://github.com/filebrowser/filebrowser/issues/5548)) ([ffc8504](https://github.com/filebrowser/filebrowser/commit/ffc850454e4cb8f10b970511681d6c627340afc7))
+
+## [2.46.0](https://github.com/filebrowser/filebrowser/compare/v2.45.3...v2.46.0) (2025-11-14)
+
+
+### Features
+
+* add 'hide-dotfiles' as command line parameter ([#3802](https://github.com/filebrowser/filebrowser/issues/3802)) ([0d973d3](https://github.com/filebrowser/filebrowser/commit/0d973d3aad70ceb88950f2cd9c297fc76e7955b1))
+* add context menu ([#3343](https://github.com/filebrowser/filebrowser/issues/3343)) ([1ace579](https://github.com/filebrowser/filebrowser/commit/1ace579a553486bb15af2d11f537414156606434))
+* add option to hide the login button from public-facing pages ([#3922](https://github.com/filebrowser/filebrowser/issues/3922)) ([ac7b49c](https://github.com/filebrowser/filebrowser/commit/ac7b49c1484b4e27a1149310542ccd1e90659ee2))
+* Updates for project File Browser ([#5544](https://github.com/filebrowser/filebrowser/issues/5544)) ([fb5d099](https://github.com/filebrowser/filebrowser/commit/fb5d099f8514516216f407be012d2e3f25de2441))
+
## [2.45.3](https://github.com/filebrowser/filebrowser/compare/v2.45.2...v2.45.3) (2025-11-13)
+This is a test release to ensure the updated workflow works.
+
## [2.45.2](https://github.com/filebrowser/filebrowser/compare/v2.45.1...v2.45.2) (2025-11-13)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d6e694f1..311a2fd7 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -86,7 +86,7 @@ task docs
To start a local server on port `8000` to view the built documentation:
```bash
-task docs-serve
+task docs:serve
```
## Release
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/README.md b/README.md
index b2873b7f..1e15d592 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-[](https://github.com/filebrowser/filebrowser/actions/workflows/main.yaml)
+[](https://github.com/filebrowser/filebrowser/actions/workflows/ci.yaml)
[](https://goreportcard.com/report/github.com/filebrowser/filebrowser/v2)
[](https://github.com/filebrowser/filebrowser/releases/latest)
@@ -19,7 +19,7 @@ This project is a finished product which fulfills its goal: be a single binary w
- It can take a while until someone gets back to you. Please be patient.
- [Issues](https://github.com/filebrowser/filebrowser/issues) are meant to track bugs. Unrelated issues will be converted into [discussions](https://github.com/filebrowser/filebrowser/discussions).
- No new features will be implemented by maintainers. Pull requests for new features will be reviewed on a case by case basis.
-- The priority is triaging issues, addressing security issues, and fixing bug fixes.
+- The priority is triaging issues, addressing security issues and reviewing pull requests meant to solve bugs.
## Contributing
diff --git a/Taskfile.yml b/Taskfile.yml
index 9721317f..378e3409 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -10,14 +10,14 @@ vars:
-v ./CONTRIBUTING.md:/docs/docs/contributing.md
tasks:
- build-frontend:
+ build:frontend:
desc: Build frontend assets
dir: frontend
cmds:
- pnpm install --frozen-lockfile
- pnpm run build
- build-backend:
+ build:backend:
desc: Build backend binary
cmds:
- go build -ldflags='-s -w -X "github.com/filebrowser/filebrowser/v2/version.Version={{.VERSION}}" -X "github.com/filebrowser/filebrowser/v2/version.CommitSHA={{.GIT_COMMIT}}"' -o filebrowser .
@@ -30,16 +30,16 @@ tasks:
build:
desc: Build both frontend and backend
cmds:
- - task: build-frontend
- - task: build-backend
+ - task: build:frontend
+ - task: build:backend
- release-make:
+ release:make:
internal: true
prompt: Do you wish to proceed?
cmds:
- pnpm dlx commit-and-tag-version -s
- release-dry-run:
+ release:dry-run:
internal: true
cmds:
- pnpm dlx commit-and-tag-version --dry-run --skip
@@ -47,10 +47,24 @@ tasks:
release:
desc: Create a new release
cmds:
- - task: release-dry-run
- - task: release-make
+ - task: docs:cli:generate
+ - git add www/docs/cli
+ - |
+ if [[ `git status www/docs/cli --porcelain` ]]; then
+ git commit -m 'chore(docs): update CLI documentation'
+ fi
+ - task: release:dry-run
+ - task: release:make
- docs-image-make:
+ docs:cli:generate:
+ cmds:
+ - rm -rf www/docs/cli
+ - mkdir -p www/docs/cli
+ - go run . docs
+ generates:
+ - www/docs/cli
+
+ docs:docker:generate:
internal: true
cmds:
- docker build -f www/Dockerfile --progress=plain -t filebrowser.site www
@@ -59,11 +73,11 @@ tasks:
desc: Generate documentation
cmds:
- rm -rf www/public
- - task: docs-image-make
+ - task: docs:docker:generate
- docker run --rm {{.SITE_DOCKER_FLAGS}} filebrowser.site build -d "public"
- docs-serve:
+ docs:serve:
desc: Serve documentation
cmds:
- - task: docs-image-make
+ - task: docs:docker:generate
- docker run --rm -it -p 8000:8000 {{.SITE_DOCKER_FLAGS}} filebrowser.site
diff --git a/auth/hook.go b/auth/hook.go
index 2ecb12f3..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"
@@ -103,7 +103,7 @@ func (a *HookAuth) RunCommand() (string, error) {
command[i] = os.Expand(arg, envMapping)
}
- cmd := exec.Command(command[0], command[1:]...) //nolint:gosec
+ cmd := exec.Command(command[0], command[1:]...)
cmd.Env = append(os.Environ(), fmt.Sprintf("USERNAME=%s", a.Cred.Username))
cmd.Env = append(cmd.Env, fmt.Sprintf("PASSWORD=%s", a.Cred.Password))
out, err := cmd.Output()
@@ -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 81f430b3..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"`
@@ -40,7 +44,7 @@ func (a JSONAuth) Auth(r *http.Request, usr users.Store, _ *settings.Settings, s
// If ReCaptcha is enabled, check the code.
if a.ReCaptcha != nil && a.ReCaptcha.Secret != "" {
- ok, err := a.ReCaptcha.Ok(cred.ReCaptcha) //nolint:govet
+ ok, err := a.ReCaptcha.Ok(cred.ReCaptcha)
if err != nil {
return nil, err
@@ -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/cmd_test.go b/cmd/cmd_test.go
new file mode 100644
index 00000000..e4b45c47
--- /dev/null
+++ b/cmd/cmd_test.go
@@ -0,0 +1,35 @@
+package cmd
+
+import (
+ "testing"
+
+ "github.com/samber/lo"
+ "github.com/spf13/cobra"
+)
+
+// TestEnvCollisions ensures that there are no collisions in the produced environment
+// variable names for all commands and their flags.
+func TestEnvCollisions(t *testing.T) {
+ testEnvCollisions(t, rootCmd)
+}
+
+func testEnvCollisions(t *testing.T, cmd *cobra.Command) {
+ for _, cmd := range cmd.Commands() {
+ testEnvCollisions(t, cmd)
+ }
+
+ replacements := generateEnvKeyReplacements(cmd)
+ envVariables := []string{}
+
+ for i := range replacements {
+ if i%2 != 0 {
+ envVariables = append(envVariables, replacements[i])
+ }
+ }
+
+ duplicates := lo.FindDuplicates(envVariables)
+
+ if len(duplicates) > 0 {
+ t.Errorf("Found duplicate environment variable keys for command %q: %v", cmd.Name(), duplicates)
+ }
+}
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 fa901a56..694be178 100644
--- a/cmd/cmds_ls.go
+++ b/cmd/cmds_ls.go
@@ -14,12 +14,13 @@ 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
}
- evt, err := getString(cmd.Flags(), "event")
+
+ evt, err := cmd.Flags().GetString("event")
if err != nil {
return err
}
@@ -32,6 +33,7 @@ var cmdsLsCmd = &cobra.Command{
show["after_"+evt] = s.Commands["after_"+evt]
printEvents(show)
}
+
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 ebb9c69f..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"
)
@@ -30,14 +30,22 @@ var configCmd = &cobra.Command{
func addConfigFlags(flags *pflag.FlagSet) {
addServerFlags(flags)
addUserFlags(flags)
+
flags.BoolP("signup", "s", false, "allow users to signup")
- flags.Bool("create-user-dir", false, "generate user's home directory automatically")
- flags.Uint("minimum-password-length", settings.DefaultMinimumPasswordLength, "minimum password length for new users")
+ flags.Bool("hideLoginButton", false, "hide login button from public pages")
+ flags.Bool("createUserDir", false, "generate user's home directory automatically")
+ flags.Uint("minimumPasswordLength", settings.DefaultMinimumPasswordLength, "minimum password length for new users")
flags.String("shell", "", "shell command to which other commands should be appended")
+ // NB: these are string so they can be presented as octal in the help text
+ // as that's the conventional representation for modes in Unix.
+ flags.String("fileMode", fmt.Sprintf("%O", settings.DefaultFileMode), "mode bits that new files are created with")
+ flags.String("dirMode", fmt.Sprintf("%O", settings.DefaultDirMode), "mode bits that new directories are created with")
+
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")
@@ -49,14 +57,13 @@ func addConfigFlags(flags *pflag.FlagSet) {
flags.String("branding.files", "", "path to directory with images and custom styles")
flags.Bool("branding.disableExternal", false, "disable external links such as GitHub links")
flags.Bool("branding.disableUsedPercentage", false, "disable used disk percentage graph")
- // NB: these are string so they can be presented as octal in the help text
- // as that's the conventional representation for modes in Unix.
- flags.String("file-mode", fmt.Sprintf("%O", settings.DefaultFileMode), "Mode bits that new files are created with")
- flags.String("dir-mode", fmt.Sprintf("%O", settings.DefaultDirMode), "Mode bits that new directories are created with")
+
+ flags.Uint64("tus.chunkSize", settings.DefaultTusChunkSize, "the tus chunk size")
+ flags.Uint16("tus.retryCount", settings.DefaultTusRetryCount, "the tus retry count")
}
func getAuthMethod(flags *pflag.FlagSet, defaults ...interface{}) (settings.AuthMethod, map[string]interface{}, error) {
- methodStr, err := getString(flags, "auth.method")
+ methodStr, err := flags.GetString("auth.method")
if err != nil {
return "", nil, err
}
@@ -87,17 +94,17 @@ func getAuthMethod(flags *pflag.FlagSet, defaults ...interface{}) (settings.Auth
}
func getProxyAuth(flags *pflag.FlagSet, defaultAuther map[string]interface{}) (auth.Auther, error) {
- header, err := getString(flags, "auth.header")
+ header, err := flags.GetString("auth.header")
if err != nil {
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
@@ -109,15 +116,17 @@ func getNoAuth() auth.Auther {
func getJSONAuth(flags *pflag.FlagSet, defaultAuther map[string]interface{}) (auth.Auther, error) {
jsonAuth := &auth.JSONAuth{}
- host, err := getString(flags, "recaptcha.host")
+ host, err := flags.GetString("recaptcha.host")
if err != nil {
return nil, err
}
- key, err := getString(flags, "recaptcha.key")
+
+ key, err := flags.GetString("recaptcha.key")
if err != nil {
return nil, err
}
- secret, err := getString(flags, "recaptcha.secret")
+
+ secret, err := flags.GetString("recaptcha.secret")
if err != nil {
return nil, err
}
@@ -145,17 +154,16 @@ func getJSONAuth(flags *pflag.FlagSet, defaultAuther map[string]interface{}) (au
}
func getHookAuth(flags *pflag.FlagSet, defaultAuther map[string]interface{}) (auth.Auther, error) {
- command, err := getString(flags, "auth.command")
+ command, err := flags.GetString("auth.command")
if err != nil {
return nil, err
}
-
if command == "" {
command = defaultAuther["command"].(string)
}
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
@@ -178,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 {
@@ -192,10 +200,13 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
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, "Auth Method:\t%s\n", set.AuthMethod)
fmt.Fprintf(w, "Shell:\t%s\t\n", strings.Join(set.Shell, " "))
+
fmt.Fprintln(w, "\nBranding:")
fmt.Fprintf(w, "\tName:\t%s\n", set.Branding.Name)
fmt.Fprintf(w, "\tFiles override:\t%s\n", set.Branding.Files)
@@ -203,6 +214,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
fmt.Fprintf(w, "\tDisable used disk percentage graph:\t%t\n", set.Branding.DisableUsedPercentage)
fmt.Fprintf(w, "\tColor:\t%s\n", set.Branding.Color)
fmt.Fprintf(w, "\tTheme:\t%s\n", set.Branding.Theme)
+
fmt.Fprintln(w, "\nServer:")
fmt.Fprintf(w, "\tLog:\t%s\n", ser.Log)
fmt.Fprintf(w, "\tPort:\t%s\n", ser.Port)
@@ -212,19 +224,32 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
fmt.Fprintf(w, "\tAddress:\t%s\n", ser.Address)
fmt.Fprintf(w, "\tTLS Cert:\t%s\n", ser.TLSCert)
fmt.Fprintf(w, "\tTLS Key:\t%s\n", ser.TLSKey)
+ fmt.Fprintf(w, "\tToken Expiration Time:\t%s\n", ser.TokenExpirationTime)
fmt.Fprintf(w, "\tExec Enabled:\t%t\n", ser.EnableExec)
+ fmt.Fprintf(w, "\tThumbnails Enabled:\t%t\n", ser.EnableThumbnails)
+ fmt.Fprintf(w, "\tResize Preview:\t%t\n", ser.ResizePreview)
+ fmt.Fprintf(w, "\tType Detection by Header:\t%t\n", ser.TypeDetectionByHeader)
+
+ fmt.Fprintln(w, "\nTUS:")
+ fmt.Fprintf(w, "\tChunk size:\t%d\n", set.Tus.ChunkSize)
+ fmt.Fprintf(w, "\tRetry count:\t%d\n", set.Tus.RetryCount)
+
fmt.Fprintln(w, "\nDefaults:")
fmt.Fprintf(w, "\tScope:\t%s\n", set.Defaults.Scope)
+ fmt.Fprintf(w, "\tHideDotfiles:\t%t\n", set.Defaults.HideDotfiles)
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, " "))
fmt.Fprintf(w, "\tAce editor syntax highlighting theme:\t%s\n", set.Defaults.AceEditorTheme)
+
fmt.Fprintf(w, "\tSorting:\n")
fmt.Fprintf(w, "\t\tBy:\t%s\n", set.Defaults.Sorting.By)
fmt.Fprintf(w, "\t\tAsc:\t%t\n", set.Defaults.Sorting.Asc)
+
fmt.Fprintf(w, "\tPermissions:\n")
fmt.Fprintf(w, "\t\tAdmin:\t%t\n", set.Defaults.Perm.Admin)
fmt.Fprintf(w, "\t\tExecute:\t%t\n", set.Defaults.Perm.Execute)
@@ -234,6 +259,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
fmt.Fprintf(w, "\t\tDelete:\t%t\n", set.Defaults.Perm.Delete)
fmt.Fprintf(w, "\t\tShare:\t%t\n", set.Defaults.Perm.Share)
fmt.Fprintf(w, "\t\tDownload:\t%t\n", set.Defaults.Perm.Download)
+
w.Flush()
b, err := json.MarshalIndent(auther, "", " ")
@@ -243,3 +269,123 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
fmt.Printf("\nAuther configuration (raw):\n\n%s\n\n", string(b))
return nil
}
+
+func getSettings(flags *pflag.FlagSet, set *settings.Settings, ser *settings.Server, auther auth.Auther, all bool) (auth.Auther, error) {
+ errs := []error{}
+ hasAuth := false
+
+ visit := func(flag *pflag.Flag) {
+ var err error
+
+ switch flag.Name {
+ // Server flags from [addServerFlags]
+ case "address":
+ ser.Address, err = flags.GetString(flag.Name)
+ case "log":
+ ser.Log, err = flags.GetString(flag.Name)
+ case "port":
+ ser.Port, err = flags.GetString(flag.Name)
+ case "cert":
+ ser.TLSCert, err = flags.GetString(flag.Name)
+ case "key":
+ ser.TLSKey, err = flags.GetString(flag.Name)
+ case "root":
+ ser.Root, err = flags.GetString(flag.Name)
+ case "socket":
+ ser.Socket, err = flags.GetString(flag.Name)
+ case "baseURL":
+ ser.BaseURL, err = flags.GetString(flag.Name)
+ case "tokenExpirationTime":
+ ser.TokenExpirationTime, err = flags.GetString(flag.Name)
+ case "disableThumbnails":
+ ser.EnableThumbnails, err = flags.GetBool(flag.Name)
+ ser.EnableThumbnails = !ser.EnableThumbnails
+ case "disablePreviewResize":
+ ser.ResizePreview, err = flags.GetBool(flag.Name)
+ ser.ResizePreview = !ser.ResizePreview
+ case "disableExec":
+ ser.EnableExec, err = flags.GetBool(flag.Name)
+ ser.EnableExec = !ser.EnableExec
+ 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":
+ set.Signup, err = flags.GetBool(flag.Name)
+ case "hideLoginButton":
+ set.HideLoginButton, err = flags.GetBool(flag.Name)
+ case "createUserDir":
+ set.CreateUserDir, err = flags.GetBool(flag.Name)
+ case "minimumPasswordLength":
+ set.MinimumPasswordLength, err = flags.GetUint(flag.Name)
+ case "shell":
+ var shell string
+ shell, err = flags.GetString(flag.Name)
+ if err == nil {
+ set.Shell = convertCmdStrToCmdArray(shell)
+ }
+ case "fileMode":
+ set.FileMode, err = getAndParseFileMode(flags, flag.Name)
+ case "dirMode":
+ 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":
+ set.Branding.Theme, err = flags.GetString(flag.Name)
+ case "branding.color":
+ set.Branding.Color, err = flags.GetString(flag.Name)
+ case "branding.files":
+ set.Branding.Files, err = flags.GetString(flag.Name)
+ case "branding.disableExternal":
+ set.Branding.DisableExternal, err = flags.GetBool(flag.Name)
+ case "branding.disableUsedPercentage":
+ set.Branding.DisableUsedPercentage, err = flags.GetBool(flag.Name)
+ case "tus.chunkSize":
+ set.Tus.ChunkSize, err = flags.GetUint64(flag.Name)
+ case "tus.retryCount":
+ set.Tus.RetryCount, err = flags.GetUint16(flag.Name)
+ }
+
+ if err != nil {
+ errs = append(errs, err)
+ }
+ }
+
+ if all {
+ flags.VisitAll(visit)
+ } else {
+ flags.Visit(visit)
+ }
+
+ err := errors.Join(errs...)
+ if err != nil {
+ return nil, err
+ }
+
+ err = getUserDefaults(flags, &set.Defaults, all)
+ if err != nil {
+ return nil, err
+ }
+
+ if all {
+ set.AuthMethod, auther, err = getAuthentication(flags)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ set.AuthMethod, auther, err = getAuthentication(flags, hasAuth, set, auther)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return auther, nil
+}
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 7763517d..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.hadDB {
- 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{allowNoDB: 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 26db2d27..359d02a3 100644
--- a/cmd/config_init.go
+++ b/cmd/config_init.go
@@ -22,152 +22,31 @@ 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 {
- defaults := settings.UserDefaults{}
+ RunE: withStore(func(cmd *cobra.Command, _ []string, st *store) error {
flags := cmd.Flags()
- err := getUserDefaults(flags, &defaults, true)
- if err != nil {
- return err
- }
- authMethod, auther, err := getAuthentication(flags)
+
+ // Initialize config
+ s := &settings.Settings{Key: generateKey()}
+ ser := &settings.Server{}
+
+ // Fill config with options
+ auther, err := getSettings(flags, s, ser, nil, true)
if err != nil {
return err
}
- key := generateKey()
-
- signup, err := getBool(flags, "signup")
+ // Save updated config
+ err = st.Settings.Save(s)
if err != nil {
return err
}
- createUserDir, err := getBool(flags, "create-user-dir")
+ err = st.Settings.SaveServer(ser)
if err != nil {
return err
}
- minLength, err := getUint(flags, "minimum-password-length")
- if err != nil {
- return err
- }
-
- shell, err := getString(flags, "shell")
- if err != nil {
- return err
- }
-
- brandingName, err := getString(flags, "branding.name")
- if err != nil {
- return err
- }
-
- brandingDisableExternal, err := getBool(flags, "branding.disableExternal")
- if err != nil {
- return err
- }
-
- brandingDisableUsedPercentage, err := getBool(flags, "branding.disableUsedPercentage")
- if err != nil {
- return err
- }
-
- brandingTheme, err := getString(flags, "branding.theme")
- if err != nil {
- return err
- }
-
- brandingFiles, err := getString(flags, "branding.files")
- if err != nil {
- return err
- }
-
- s := &settings.Settings{
- Key: key,
- Signup: signup,
- CreateUserDir: createUserDir,
- MinimumPasswordLength: minLength,
- Shell: convertCmdStrToCmdArray(shell),
- AuthMethod: authMethod,
- Defaults: defaults,
- Branding: settings.Branding{
- Name: brandingName,
- DisableExternal: brandingDisableExternal,
- DisableUsedPercentage: brandingDisableUsedPercentage,
- Theme: brandingTheme,
- Files: brandingFiles,
- },
- }
-
- s.FileMode, err = getMode(flags, "file-mode")
- if err != nil {
- return err
- }
-
- s.DirMode, err = getMode(flags, "dir-mode")
- if err != nil {
- return err
- }
-
- address, err := getString(flags, "address")
- if err != nil {
- return err
- }
-
- socket, err := getString(flags, "socket")
- if err != nil {
- return err
- }
-
- root, err := getString(flags, "root")
- if err != nil {
- return err
- }
-
- baseURL, err := getString(flags, "baseurl")
- if err != nil {
- return err
- }
-
- tlsKey, err := getString(flags, "key")
- if err != nil {
- return err
- }
-
- cert, err := getString(flags, "cert")
- if err != nil {
- return err
- }
-
- port, err := getString(flags, "port")
- if err != nil {
- return err
- }
-
- log, err := getString(flags, "log")
- if err != nil {
- return err
- }
-
- ser := &settings.Server{
- Address: address,
- Socket: socket,
- Root: root,
- BaseURL: baseURL,
- TLSKey: tlsKey,
- TLSCert: cert,
- Port: port,
- Log: log,
- }
-
- err = d.store.Settings.Save(s)
- if err != nil {
- return err
- }
- err = d.store.Settings.SaveServer(ser)
- if err != nil {
- return err
- }
- err = d.store.Auth.Save(auther)
+ err = st.Auth.Save(auther)
if err != nil {
return err
}
@@ -178,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{noDB: true}),
+ }, storeOptions{expectsNoDatabase: true}),
}
diff --git a/cmd/config_set.go b/cmd/config_set.go
index a7f66902..df357a02 100644
--- a/cmd/config_set.go
+++ b/cmd/config_set.go
@@ -2,7 +2,6 @@ package cmd
import (
"github.com/spf13/cobra"
- "github.com/spf13/pflag"
)
func init() {
@@ -16,105 +15,47 @@ 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()
- set, err := d.store.Settings.Get()
+
+ // Read existing config
+ 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
}
- hasAuth := false
- flags.Visit(func(flag *pflag.Flag) {
- if err != nil {
- return
- }
- switch flag.Name {
- case "baseurl":
- ser.BaseURL, err = getString(flags, flag.Name)
- case "root":
- ser.Root, err = getString(flags, flag.Name)
- case "socket":
- ser.Socket, err = getString(flags, flag.Name)
- case "cert":
- ser.TLSCert, err = getString(flags, flag.Name)
- case "key":
- ser.TLSKey, err = getString(flags, flag.Name)
- case "address":
- ser.Address, err = getString(flags, flag.Name)
- case "port":
- ser.Port, err = getString(flags, flag.Name)
- case "log":
- ser.Log, err = getString(flags, flag.Name)
- case "signup":
- set.Signup, err = getBool(flags, flag.Name)
- case "auth.method":
- hasAuth = true
- case "shell":
- var shell string
- shell, err = getString(flags, flag.Name)
- set.Shell = convertCmdStrToCmdArray(shell)
- case "create-user-dir":
- set.CreateUserDir, err = getBool(flags, flag.Name)
- case "minimum-password-length":
- set.MinimumPasswordLength, err = getUint(flags, flag.Name)
- case "branding.name":
- set.Branding.Name, err = getString(flags, flag.Name)
- case "branding.color":
- set.Branding.Color, err = getString(flags, flag.Name)
- case "branding.theme":
- set.Branding.Theme, err = getString(flags, flag.Name)
- case "branding.disableExternal":
- set.Branding.DisableExternal, err = getBool(flags, flag.Name)
- case "branding.disableUsedPercentage":
- set.Branding.DisableUsedPercentage, err = getBool(flags, flag.Name)
- case "branding.files":
- set.Branding.Files, err = getString(flags, flag.Name)
- case "file-mode":
- set.FileMode, err = getMode(flags, flag.Name)
- case "dir-mode":
- set.DirMode, err = getMode(flags, flag.Name)
- }
- })
-
+ auther, err := st.Auth.Get(set.AuthMethod)
if err != nil {
return err
}
- err = getUserDefaults(flags, &set.Defaults, false)
+ // Get updated config
+ auther, err = getSettings(flags, set, ser, auther, false)
if err != nil {
return err
}
- // read the defaults
- auther, err := d.store.Auth.Get(set.AuthMethod)
+ // Save updated config
+ err = st.Auth.Save(auther)
if err != nil {
return err
}
- // check if there are new flags for existing auth method
- set.AuthMethod, auther, err = getAuthentication(flags, hasAuth, set, auther)
+ err = st.Settings.Save(set)
if err != nil {
return err
}
- err = d.store.Auth.Save(auther)
- if err != nil {
- return err
- }
- err = d.store.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 5f26daf9..d65a29be 100644
--- a/cmd/docs.go
+++ b/cmd/docs.go
@@ -3,36 +3,18 @@ package cmd
import (
"bytes"
"fmt"
- "io"
"os"
- "path/filepath"
- "sort"
+ "path"
+ "regexp"
"strings"
"github.com/spf13/cobra"
- "github.com/spf13/pflag"
+ "github.com/spf13/cobra/doc"
)
func init() {
rootCmd.AddCommand(docsCmd)
- docsCmd.Flags().StringP("path", "p", "./docs", "path to save the docs")
-}
-
-func printToc(names []string) {
- for i, name := range names {
- name = strings.TrimSuffix(name, filepath.Ext(name))
- name = strings.Replace(name, "-", " ", -1)
- names[i] = name
- }
-
- sort.Strings(names)
-
- toc := ""
- for _, name := range names {
- toc += "* [" + name + "](cli/" + strings.Replace(name, " ", "-", -1) + ".md)\n"
- }
-
- fmt.Println(toc)
+ docsCmd.Flags().String("out", "www/docs/cli", "directory to write the docs to")
}
var docsCmd = &cobra.Command{
@@ -40,115 +22,61 @@ var docsCmd = &cobra.Command{
Hidden: true,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
- dir, err := getString(cmd.Flags(), "path")
+ outputDir, err := cmd.Flags().GetString("out")
if err != nil {
return err
}
- err = generateDocs(rootCmd, dir)
+ tempDir, err := os.MkdirTemp(os.TempDir(), "filebrowser-docs-")
if err != nil {
return err
}
- names := []string{}
+ defer os.RemoveAll(tempDir)
- err = filepath.Walk(dir, func(_ string, info os.FileInfo, err error) error {
- if err != nil || info.IsDir() {
- return err
- }
+ rootCmd.Root().DisableAutoGenTag = true
- if !strings.HasPrefix(info.Name(), "filebrowser") {
- return nil
- }
-
- names = append(names, info.Name())
- return nil
+ err = doc.GenMarkdownTreeCustom(cmd.Root(), tempDir, func(_ string) string {
+ return ""
+ }, func(s string) string {
+ return s
})
if err != nil {
return err
}
- printToc(names)
- return nil
- },
-}
-
-func generateDocs(cmd *cobra.Command, dir string) error {
- for _, c := range cmd.Commands() {
- if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
- continue
- }
-
- err := generateDocs(c, dir)
+ entries, err := os.ReadDir(tempDir)
if err != nil {
return err
}
- }
- basename := strings.Replace(cmd.CommandPath(), " ", "-", -1) + ".md"
- filename := filepath.Join(dir, basename)
- f, err := os.Create(filename)
- if err != nil {
- return err
- }
- defer f.Close()
- return generateMarkdown(cmd, f)
-}
-
-func generateMarkdown(cmd *cobra.Command, w io.Writer) error {
- cmd.InitDefaultHelpCmd()
- cmd.InitDefaultHelpFlag()
-
- buf := new(bytes.Buffer)
- name := cmd.CommandPath()
-
- short := cmd.Short
- long := cmd.Long
- if long == "" {
- long = short
- }
-
- buf.WriteString("---\ndescription: " + short + "\n---\n\n")
- buf.WriteString("# " + name + "\n\n")
- buf.WriteString("## Synopsis\n\n")
- buf.WriteString(long + "\n\n")
-
- if cmd.Runnable() {
- _, _ = fmt.Fprintf(buf, "```\n%s\n```\n\n", cmd.UseLine())
- }
-
- if cmd.Example != "" {
- buf.WriteString("## Examples\n\n")
- _, _ = fmt.Fprintf(buf, "```\n%s\n```\n\n", cmd.Example)
- }
-
- printOptions(buf, cmd)
- _, err := buf.WriteTo(w)
- return err
-}
-
-func generateFlagsTable(fs *pflag.FlagSet, buf io.StringWriter) {
- _, _ = buf.WriteString("| Name | Shorthand | Usage |\n")
- _, _ = buf.WriteString("|------|-----------|-------|\n")
-
- fs.VisitAll(func(f *pflag.Flag) {
- _, _ = buf.WriteString("|" + f.Name + "|" + f.Shorthand + "|" + f.Usage + "|\n")
- })
-}
-
-func printOptions(buf *bytes.Buffer, cmd *cobra.Command) {
- flags := cmd.NonInheritedFlags()
- flags.SetOutput(buf)
- if flags.HasAvailableFlags() {
- buf.WriteString("## Options\n\n")
- generateFlagsTable(flags, buf)
- buf.WriteString("\n")
- }
-
- parentFlags := cmd.InheritedFlags()
- parentFlags.SetOutput(buf)
- if parentFlags.HasAvailableFlags() {
- buf.WriteString("### Inherited\n\n")
- generateFlagsTable(parentFlags, buf)
- buf.WriteString("\n")
- }
+ headerRegex := regexp.MustCompile(`(?m)^(##)(.*)$`)
+ linkRegex := regexp.MustCompile(`\(filebrowser(.*)\.md\)`)
+
+ fmt.Println("Generated Documents:")
+
+ for _, entry := range entries {
+ srcPath := path.Join(tempDir, entry.Name())
+ dstPath := path.Join(outputDir, strings.ReplaceAll(entry.Name(), "_", "-"))
+
+ data, err := os.ReadFile(srcPath)
+ if err != nil {
+ return err
+ }
+
+ data = headerRegex.ReplaceAll(data, []byte("#$2"))
+ data = linkRegex.ReplaceAllFunc(data, func(b []byte) []byte {
+ return bytes.ReplaceAll(b, []byte("_"), []byte("-"))
+ })
+ data = bytes.ReplaceAll(data, []byte("## SEE ALSO"), []byte("## See Also"))
+
+ err = os.WriteFile(dstPath, data, 0666)
+ if err != nil {
+ return err
+ }
+
+ fmt.Println("- " + dstPath)
+ }
+
+ return nil
+ },
}
diff --git a/cmd/root.go b/cmd/root.go
index a9704cf4..981eec4f 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -13,20 +13,17 @@ import (
"os"
"os/signal"
"path/filepath"
- "strings"
"syscall"
"time"
- homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
- v "github.com/spf13/viper"
+ "github.com/spf13/viper"
lumberjack "gopkg.in/natefinch/lumberjack.v2"
"github.com/filebrowser/filebrowser/v2/auth"
"github.com/filebrowser/filebrowser/v2/diskcache"
- fbErrors "github.com/filebrowser/filebrowser/v2/errors"
"github.com/filebrowser/filebrowser/v2/frontend"
fbhttp "github.com/filebrowser/filebrowser/v2/http"
"github.com/filebrowser/filebrowser/v2/img"
@@ -36,28 +33,67 @@ import (
)
var (
- cfgFile string
+ flagNamesMigrations = map[string]string{
+ "file-mode": "fileMode",
+ "dir-mode": "dirMode",
+ "hide-login-button": "hideLoginButton",
+ "create-user-dir": "createUserDir",
+ "minimum-password-length": "minimumPasswordLength",
+ "socket-perm": "socketPerm",
+ "disable-thumbnails": "disableThumbnails",
+ "disable-preview-resize": "disablePreviewResize",
+ "disable-exec": "disableExec",
+ "disable-type-detection-by-header": "disableTypeDetectionByHeader",
+ "img-processors": "imageProcessors",
+ "cache-dir": "cacheDir",
+ "token-expiration-time": "tokenExpirationTime",
+ "baseurl": "baseURL",
+ }
+
+ warnedFlags = map[string]bool{}
)
+// TODO(remove): remove after July 2026.
+func migrateFlagNames(_ *pflag.FlagSet, name string) pflag.NormalizedName {
+ if newName, ok := flagNamesMigrations[name]; ok {
+
+ if !warnedFlags[name] {
+ warnedFlags[name] = true
+ log.Printf("DEPRECATION NOTICE: Flag --%s has been deprecated, use --%s instead\n", name, newName)
+ }
+
+ name = newName
+ }
+
+ return pflag.NormalizedName(name)
+}
+
func init() {
- cobra.OnInitialize(initConfig)
rootCmd.SilenceUsage = true
+ rootCmd.SetGlobalNormalizationFunc(migrateFlagNames)
+
cobra.MousetrapHelpText = ""
rootCmd.SetVersionTemplate("File Browser version {{printf \"%s\" .Version}}\n")
- flags := rootCmd.Flags()
+ // Flags available across the whole program
persistent := rootCmd.PersistentFlags()
-
- persistent.StringVarP(&cfgFile, "config", "c", "", "config file path")
+ persistent.StringP("config", "c", "", "config file path")
persistent.StringP("database", "d", "./filebrowser.db", "database path")
- flags.Bool("noauth", false, "use the noauth auther when using quick setup")
- flags.String("username", "admin", "username for the first user when using quick config")
- flags.String("password", "", "hashed password for the first user when using quick config")
+ // Runtime flags for the root command
+ flags := rootCmd.Flags()
+ flags.Bool("noauth", false, "use the noauth auther when using quick setup")
+ flags.String("username", "admin", "username for the first user when using quick setup")
+ flags.String("password", "", "hashed password for the first user when using quick setup")
+ flags.Uint32("socketPerm", 0666, "unix socket file permissions")
+ flags.String("cacheDir", "", "file cache directory (disabled if empty)")
+ flags.Int("imageProcessors", 4, "image processors count")
addServerFlags(flags)
}
+// addServerFlags adds server related flags to the given FlagSet. These flags are available
+// in both the root command, config set and config init commands.
func addServerFlags(flags *pflag.FlagSet) {
flags.StringP("address", "a", "127.0.0.1", "address to listen on")
flags.StringP("log", "l", "stdout", "log output")
@@ -66,15 +102,13 @@ func addServerFlags(flags *pflag.FlagSet) {
flags.StringP("key", "k", "", "tls key")
flags.StringP("root", "r", ".", "root to prepend to relative paths")
flags.String("socket", "", "socket to listen to (cannot be used with address, port, cert nor key flags)")
- flags.Uint32("socket-perm", 0666, "unix socket file permissions")
- flags.StringP("baseurl", "b", "", "base url")
- flags.String("cache-dir", "", "file cache directory (disabled if empty)")
- flags.String("token-expiration-time", "2h", "user session timeout")
- flags.Int("img-processors", 4, "image processors count") //nolint:mnd
- flags.Bool("disable-thumbnails", false, "disable image thumbnails")
- flags.Bool("disable-preview-resize", false, "disable resize of image previews")
- flags.Bool("disable-exec", true, "disables Command Runner feature")
- flags.Bool("disable-type-detection-by-header", false, "disables type detection by reading file headers")
+ flags.StringP("baseURL", "b", "", "base url")
+ flags.String("tokenExpirationTime", "2h", "user session timeout")
+ flags.Bool("disableThumbnails", false, "disable image thumbnails")
+ 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{
@@ -89,65 +123,60 @@ it. Don't worry: you don't need to setup a separate database server.
We're using Bolt DB which is a single file database and all managed
by ourselves.
-For this specific command, all the flags you have available (except
-"config" for the configuration file), can be given either through
-environment variables or configuration files.
+For this command, all flags are available as environmental variables,
+except for "--config", which specifies the configuration file to use.
+The environment variables are prefixed by "FB_" followed by the flag name in
+UPPER_SNAKE_CASE. For example, the flag "--disablePreviewResize" is available
+as FB_DISABLE_PREVIEW_RESIZE.
-If you don't set "config", it will look for a configuration file called
-.filebrowser.{json, toml, yaml, yml} in the following directories:
+If "--config" is not specified, File Browser will look for a configuration
+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
-- environment variables
-- configuration file
-- database values
-- defaults
-
-The environment variables are prefixed by "FB_" followed by the option
-name in caps. So to set "database" via an env variable, you should
-set FB_DATABASE.
+- Flags
+- Environment variables
+- Configuration file
+- Database values
+- Defaults
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 {
- log.Println(cfgFile)
-
- if !d.hadDB {
- err := quickSetup(cmd.Flags(), *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
- workersCount, err := cmd.Flags().GetInt("img-processors")
- if err != nil {
- return err
- }
- if workersCount < 1 {
+ imgWorkersCount := v.GetInt("imageProcessors")
+ if imgWorkersCount < 1 {
return errors.New("image resize workers count could not be < 1")
}
- imgSvc := img.New(workersCount)
+ imageService := img.New(imgWorkersCount)
var fileCache diskcache.Interface = diskcache.NewNoOp()
- cacheDir, err := cmd.Flags().GetString("cache-dir")
- if err != nil {
- return err
- }
+ cacheDir := v.GetString("cacheDir")
if cacheDir != "" {
- if err := os.MkdirAll(cacheDir, 0700); err != nil { //nolint:govet
+ if err := os.MkdirAll(cacheDir, 0700); err != nil {
return fmt.Errorf("can't make directory %s: %w", cacheDir, err)
}
fileCache = diskcache.New(afero.NewOsFs(), cacheDir)
}
- server, err := getRunParams(cmd.Flags(), d.store)
+ server, err := getServerSettings(v, st.Storage)
if err != nil {
return err
}
@@ -169,16 +198,13 @@ user created with the credentials from options "username" and "password".`,
if err != nil {
return err
}
- socketPerm, err := cmd.Flags().GetUint32("socket-perm") //nolint:govet
- if err != nil {
- return err
- }
+ socketPerm := v.GetUint32("socketPerm")
err = os.Chmod(server.Socket, os.FileMode(socketPerm))
if err != nil {
return err
}
case server.TLSKey != "" && server.TLSCert != "":
- cer, err := tls.LoadX509KeyPair(server.TLSCert, server.TLSKey) //nolint:govet
+ cer, err := tls.LoadX509KeyPair(server.TLSCert, server.TLSKey)
if err != nil {
return err
}
@@ -201,7 +227,7 @@ user created with the credentials from options "username" and "password".`,
panic(err)
}
- handler, err := fbhttp.NewHandler(imgSvc, fileCache, d.store, server, assetsFs)
+ handler, err := fbhttp.NewHandler(imageService, fileCache, st.Storage, server, assetsFs)
if err != nil {
return err
}
@@ -233,7 +259,7 @@ user created with the credentials from options "username" and "password".`,
sig := <-sigc
log.Println("Got signal:", sig)
- shutdownCtx, shutdownRelease := context.WithTimeout(context.Background(), 10*time.Second) //nolint:mnd
+ shutdownCtx, shutdownRelease := context.WithTimeout(context.Background(), 10*time.Second)
defer shutdownRelease()
if err := srv.Shutdown(shutdownCtx); err != nil {
@@ -241,66 +267,82 @@ user created with the credentials from options "username" and "password".`,
}
log.Println("Graceful shutdown complete.")
- switch sig {
- case syscall.SIGHUP:
- d.err = fbErrors.ErrSighup
- case syscall.SIGINT:
- d.err = fbErrors.ErrSigint
- case syscall.SIGQUIT:
- d.err = fbErrors.ErrSigquit
- case syscall.SIGTERM:
- d.err = fbErrors.ErrSigTerm
- }
-
- return d.err
- }, pythonConfig{allowNoDB: true}),
+ return nil
+ }, storeOptions{allowsNoDatabase: true}),
}
-//nolint:gocyclo
-func getRunParams(flags *pflag.FlagSet, st *storage.Storage) (*settings.Server, error) {
+func getServerSettings(v *viper.Viper, st *storage.Storage) (*settings.Server, error) {
server, err := st.Settings.GetServer()
if err != nil {
return nil, err
}
- if val, set := getStringParamB(flags, "root"); set {
- server.Root = val
- }
-
- if val, set := getStringParamB(flags, "baseurl"); set {
- server.BaseURL = val
- }
-
- if val, set := getStringParamB(flags, "log"); set {
- server.Log = val
- }
-
isSocketSet := false
isAddrSet := false
- if val, set := getStringParamB(flags, "address"); set {
- server.Address = val
- isAddrSet = isAddrSet || set
+ if v.IsSet("address") {
+ server.Address = v.GetString("address")
+ isAddrSet = true
}
- if val, set := getStringParamB(flags, "port"); set {
- server.Port = val
- isAddrSet = isAddrSet || set
+ if v.IsSet("log") {
+ server.Log = v.GetString("log")
}
- if val, set := getStringParamB(flags, "key"); set {
- server.TLSKey = val
- isAddrSet = isAddrSet || set
+ if v.IsSet("port") {
+ server.Port = v.GetString("port")
+ isAddrSet = true
}
- if val, set := getStringParamB(flags, "cert"); set {
- server.TLSCert = val
- isAddrSet = isAddrSet || set
+ if v.IsSet("cert") {
+ server.TLSCert = v.GetString("cert")
+ isAddrSet = true
}
- if val, set := getStringParamB(flags, "socket"); set {
- server.Socket = val
- isSocketSet = isSocketSet || set
+ if v.IsSet("key") {
+ server.TLSKey = v.GetString("key")
+ isAddrSet = true
+ }
+
+ if v.IsSet("root") {
+ server.Root = v.GetString("root")
+ }
+
+ if v.IsSet("socket") {
+ server.Socket = v.GetString("socket")
+ isSocketSet = true
+ }
+
+ 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") {
+ server.TokenExpirationTime = v.GetString("tokenExpirationTime")
+ }
+
+ if v.IsSet("disableThumbnails") {
+ server.EnableThumbnails = !v.GetBool("disableThumbnails")
+ }
+
+ if v.IsSet("disablePreviewResize") {
+ server.ResizePreview = !v.GetBool("disablePreviewResize")
+ }
+
+ if v.IsSet("disableTypeDetectionByHeader") {
+ server.TypeDetectionByHeader = !v.GetBool("disableTypeDetectionByHeader")
+ }
+
+ if v.IsSet("disableImageResolutionCalc") {
+ server.ImageResolutionCal = !v.GetBool("disableImageResolutionCalc")
+ }
+
+ if v.IsSet("disableExec") {
+ server.EnableExec = !v.GetBool("disableExec")
}
if isAddrSet && isSocketSet {
@@ -312,18 +354,6 @@ func getRunParams(flags *pflag.FlagSet, st *storage.Storage) (*settings.Server,
server.Socket = ""
}
- disableThumbnails := getBoolParam(flags, "disable-thumbnails")
- server.EnableThumbnails = !disableThumbnails
-
- disablePreviewResize := getBoolParam(flags, "disable-preview-resize")
- server.ResizePreview = !disablePreviewResize
-
- disableTypeDetectionByHeader := getBoolParam(flags, "disable-type-detection-by-header")
- server.TypeDetectionByHeader = !disableTypeDetectionByHeader
-
- disableExec := getBoolParam(flags, "disable-exec")
- server.EnableExec = !disableExec
-
if server.EnableExec {
log.Println("WARNING: Command Runner feature enabled!")
log.Println("WARNING: This feature has known security vulnerabilities and should not")
@@ -331,71 +361,9 @@ func getRunParams(flags *pflag.FlagSet, st *storage.Storage) (*settings.Server,
log.Println("WARNING: read https://github.com/filebrowser/filebrowser/issues/5199")
}
- if val, set := getStringParamB(flags, "token-expiration-time"); set {
- server.TokenExpirationTime = val
- }
-
return server, nil
}
-// getBoolParamB returns a parameter as a string and a boolean to tell if it is different from the default
-//
-// NOTE: we could simply bind the flags to viper and use IsSet.
-// Although there is a bug on Viper that always returns true on IsSet
-// if a flag is binded. Our alternative way is to manually check
-// the flag and then the value from env/config/gotten by viper.
-// https://github.com/spf13/viper/pull/331
-func getBoolParamB(flags *pflag.FlagSet, key string) (value, ok bool) {
- value, _ = flags.GetBool(key)
-
- // If set on Flags, use it.
- if flags.Changed(key) {
- return value, true
- }
-
- // If set through viper (env, config), return it.
- if v.IsSet(key) {
- return v.GetBool(key), true
- }
-
- // Otherwise use default value on flags.
- return value, false
-}
-
-func getBoolParam(flags *pflag.FlagSet, key string) bool {
- val, _ := getBoolParamB(flags, key)
- return val
-}
-
-// getStringParamB returns a parameter as a string and a boolean to tell if it is different from the default
-//
-// NOTE: we could simply bind the flags to viper and use IsSet.
-// Although there is a bug on Viper that always returns true on IsSet
-// if a flag is binded. Our alternative way is to manually check
-// the flag and then the value from env/config/gotten by viper.
-// https://github.com/spf13/viper/pull/331
-func getStringParamB(flags *pflag.FlagSet, key string) (string, bool) {
- value, _ := flags.GetString(key)
-
- // If set on Flags, use it.
- if flags.Changed(key) {
- return value, true
- }
-
- // If set through viper (env, config), return it.
- if v.IsSet(key) {
- return v.GetString(key), true
- }
-
- // Otherwise use default value on flags.
- return value, false
-}
-
-func getStringParam(flags *pflag.FlagSet, key string) string {
- val, _ := getStringParamB(flags, key)
- return val
-}
-
func setupLog(logMethod string) {
switch logMethod {
case "stdout":
@@ -414,20 +382,22 @@ func setupLog(logMethod string) {
}
}
-func quickSetup(flags *pflag.FlagSet, d pythonData) error {
+func quickSetup(v *viper.Viper, s *storage.Storage) error {
log.Println("Performing quick setup")
set := &settings.Settings{
Key: generateKey(),
Signup: false,
+ HideLoginButton: true,
CreateUserDir: false,
MinimumPasswordLength: settings.DefaultMinimumPasswordLength,
UserHomeBasePath: settings.DefaultUsersHomeBasePath,
Defaults: settings.UserDefaults{
- Scope: ".",
- Locale: "en",
- SingleClick: false,
- AceEditorTheme: getStringParam(flags, "defaults.aceEditorTheme"),
+ Scope: ".",
+ Locale: "en",
+ SingleClick: false,
+ RedirectAfterCopyMove: true,
+ AceEditorTheme: v.GetString("defaults.aceEditorTheme"),
Perm: users.Permissions{
Admin: false,
Execute: true,
@@ -451,39 +421,45 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) error {
}
var err error
- if _, noauth := getStringParamB(flags, "noauth"); 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: getStringParam(flags, "baseurl"),
- Port: getStringParam(flags, "port"),
- Log: getStringParam(flags, "log"),
- TLSKey: getStringParam(flags, "key"),
- TLSCert: getStringParam(flags, "cert"),
- Address: getStringParam(flags, "address"),
- Root: getStringParam(flags, "root"),
+ 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 := getStringParam(flags, "username")
- password := getStringParam(flags, "password")
+ username := v.GetString("username")
+ password := v.GetString("password")
if password == "" {
var pwd string
@@ -514,35 +490,5 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) error {
set.Defaults.Apply(user)
user.Perm.Admin = true
- return d.store.Users.Save(user)
-}
-
-func initConfig() {
- if cfgFile == "" {
- home, err := homedir.Dir()
- if err != nil {
- panic(err)
- }
- v.AddConfigPath(".")
- v.AddConfigPath(home)
- v.AddConfigPath("/etc/filebrowser/")
- v.SetConfigName(".filebrowser")
- } else {
- v.SetConfigFile(cfgFile)
- }
-
- v.SetEnvPrefix("FB")
- v.AutomaticEnv()
- v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
- v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
-
- if err := v.ReadInConfig(); err != nil {
- var configParseError v.ConfigParseError
- if errors.As(err, &configParseError) {
- panic(err)
- }
- cfgFile = "No config file used"
- } else {
- cfgFile = "Using config file: " + v.ConfigFileUsed()
- }
+ 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.go b/cmd/rules.go
index ffa5b1ae..bdb1d1cf 100644
--- a/cmd/rules.go
+++ b/cmd/rules.go
@@ -69,11 +69,12 @@ func runRules(st *storage.Storage, cmd *cobra.Command, usersFn func(*users.User)
}
func getUserIdentifier(flags *pflag.FlagSet) (interface{}, error) {
- id, err := getUint(flags, "id")
+ id, err := flags.GetUint("id")
if err != nil {
return nil, err
}
- username, err := getString(flags, "username")
+
+ username, err := flags.GetString("username")
if err != nil {
return nil, err
}
diff --git a/cmd/rules_add.go b/cmd/rules_add.go
index 9d1f0cf9..3b34d940 100644
--- a/cmd/rules_add.go
+++ b/cmd/rules_add.go
@@ -21,15 +21,19 @@ 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 {
- allow, err := getBool(cmd.Flags(), "allow")
+ RunE: withStore(func(cmd *cobra.Command, args []string, st *store) error {
+ flags := cmd.Flags()
+
+ allow, err := flags.GetBool("allow")
if err != nil {
return err
}
- regex, err := getBool(cmd.Flags(), "regex")
+
+ regex, err := flags.GetBool("regex")
if err != nil {
return err
}
+
exp := args[0]
if regex {
@@ -49,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/upgrade.go b/cmd/upgrade.go
deleted file mode 100644
index 7142b151..00000000
--- a/cmd/upgrade.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package cmd
-
-import (
- "github.com/spf13/cobra"
-
- "github.com/filebrowser/filebrowser/v2/storage/bolt/importer"
-)
-
-func init() {
- rootCmd.AddCommand(upgradeCmd)
-
- upgradeCmd.Flags().String("old.database", "", "")
- upgradeCmd.Flags().String("old.config", "", "")
- _ = upgradeCmd.MarkFlagRequired("old.database")
-}
-
-var upgradeCmd = &cobra.Command{
- Use: "upgrade",
- Short: "Upgrades an old configuration",
- Long: `Upgrades an old configuration. This command DOES NOT
-import share links because they are incompatible with
-this version.`,
- Args: cobra.NoArgs,
- RunE: func(cmd *cobra.Command, _ []string) error {
- flags := cmd.Flags()
- oldDB, err := getString(flags, "old.database")
- if err != nil {
- return err
- }
- oldConf, err := getString(flags, "old.config")
- if err != nil {
- return err
- }
- db, err := getString(flags, "database")
- if err != nil {
- return err
- }
- return importer.Import(oldDB, oldConf, db)
- },
-}
diff --git a/cmd/users.go b/cmd/users.go
index 5b458a5a..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,67 +78,72 @@ 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")
}
-func getViewMode(flags *pflag.FlagSet) (users.ViewMode, error) {
- viewModeStr, err := getString(flags, "viewMode")
+func getAndParseViewMode(flags *pflag.FlagSet) (users.ViewMode, error) {
+ viewModeStr, err := flags.GetString("viewMode")
if err != nil {
return "", err
}
+
viewMode := users.ViewMode(viewModeStr)
if viewMode != users.ListViewMode && viewMode != users.MosaicViewMode {
return "", errors.New("view mode must be \"" + string(users.ListViewMode) + "\" or \"" + string(users.MosaicViewMode) + "\"")
}
+
return viewMode, nil
}
-//nolint:gocyclo
func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all bool) error {
- var visitErr error
+ errs := []error{}
+
visit := func(flag *pflag.Flag) {
- if visitErr != nil {
- return
- }
var err error
switch flag.Name {
case "scope":
- defaults.Scope, err = getString(flags, flag.Name)
+ defaults.Scope, err = flags.GetString(flag.Name)
case "locale":
- defaults.Locale, err = getString(flags, flag.Name)
+ defaults.Locale, err = flags.GetString(flag.Name)
case "viewMode":
- defaults.ViewMode, err = getViewMode(flags)
+ defaults.ViewMode, err = getAndParseViewMode(flags)
case "singleClick":
- defaults.SingleClick, err = getBool(flags, flag.Name)
+ defaults.SingleClick, err = flags.GetBool(flag.Name)
+ case "redirectAfterCopyMove":
+ defaults.RedirectAfterCopyMove, err = flags.GetBool(flag.Name)
case "aceEditorTheme":
- defaults.AceEditorTheme, err = getString(flags, flag.Name)
+ defaults.AceEditorTheme, err = flags.GetString(flag.Name)
case "perm.admin":
- defaults.Perm.Admin, err = getBool(flags, flag.Name)
+ defaults.Perm.Admin, err = flags.GetBool(flag.Name)
case "perm.execute":
- defaults.Perm.Execute, err = getBool(flags, flag.Name)
+ defaults.Perm.Execute, err = flags.GetBool(flag.Name)
case "perm.create":
- defaults.Perm.Create, err = getBool(flags, flag.Name)
+ defaults.Perm.Create, err = flags.GetBool(flag.Name)
case "perm.rename":
- defaults.Perm.Rename, err = getBool(flags, flag.Name)
+ defaults.Perm.Rename, err = flags.GetBool(flag.Name)
case "perm.modify":
- defaults.Perm.Modify, err = getBool(flags, flag.Name)
+ defaults.Perm.Modify, err = flags.GetBool(flag.Name)
case "perm.delete":
- defaults.Perm.Delete, err = getBool(flags, flag.Name)
+ defaults.Perm.Delete, err = flags.GetBool(flag.Name)
case "perm.share":
- defaults.Perm.Share, err = getBool(flags, flag.Name)
+ defaults.Perm.Share, err = flags.GetBool(flag.Name)
case "perm.download":
- defaults.Perm.Download, err = getBool(flags, flag.Name)
+ defaults.Perm.Download, err = flags.GetBool(flag.Name)
case "commands":
defaults.Commands, err = flags.GetStringSlice(flag.Name)
case "sorting.by":
- defaults.Sorting.By, err = getString(flags, flag.Name)
+ defaults.Sorting.By, err = flags.GetString(flag.Name)
case "sorting.asc":
- defaults.Sorting.Asc, err = getBool(flags, flag.Name)
+ defaults.Sorting.Asc, err = flags.GetBool(flag.Name)
+ case "hideDotfiles":
+ defaults.HideDotfiles, err = flags.GetBool(flag.Name)
}
+
if err != nil {
- visitErr = err
+ errs = append(errs, err)
}
}
@@ -146,5 +152,6 @@ func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all
} else {
flags.Visit(visit)
}
- return visitErr
+
+ return errors.Join(errs...)
}
diff --git a/cmd/users_add.go b/cmd/users_add.go
index dce7ff98..daf59aa3 100644
--- a/cmd/users_add.go
+++ b/cmd/users_add.go
@@ -16,12 +16,13 @@ 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 {
- s, err := d.store.Settings.Get()
+ RunE: withStore(func(cmd *cobra.Command, args []string, st *store) error {
+ flags := cmd.Flags()
+ s, err := st.Settings.Get()
if err != nil {
return err
}
- err = getUserDefaults(cmd.Flags(), &s.Defaults, false)
+ err = getUserDefaults(flags, &s.Defaults, false)
if err != nil {
return err
}
@@ -31,39 +32,36 @@ var usersAddCmd = &cobra.Command{
return err
}
- lockPassword, err := getBool(cmd.Flags(), "lockPassword")
- if err != nil {
- return err
- }
-
- dateFormat, err := getBool(cmd.Flags(), "dateFormat")
- if err != nil {
- return err
- }
-
- hideDotfiles, err := getBool(cmd.Flags(), "hideDotfiles")
- if err != nil {
- return err
- }
-
user := &users.User{
- Username: args[0],
- Password: password,
- LockPassword: lockPassword,
- DateFormat: dateFormat,
- HideDotfiles: hideDotfiles,
+ Username: args[0],
+ Password: password,
+ }
+
+ user.LockPassword, err = flags.GetBool("lockPassword")
+ if err != nil {
+ return err
+ }
+
+ user.DateFormat, err = flags.GetBool("dateFormat")
+ if err != nil {
+ return err
+ }
+
+ user.HideDotfiles, err = flags.GetBool("hideDotfiles")
+ if err != nil {
+ return err
}
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
}
@@ -74,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 d20dca68..73effca6 100644
--- a/cmd/users_import.go
+++ b/cmd/users_import.go
@@ -25,7 +25,8 @@ 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 {
return err
@@ -45,13 +46,13 @@ list or set it to 0.`,
}
}
- replace, err := getBool(cmd.Flags(), "replace")
+ replace, err := flags.GetBool("replace")
if err != nil {
return err
}
if replace {
- oldUsers, userImportErr := d.store.Users.Gets("")
+ oldUsers, userImportErr := st.Users.Gets("")
if userImportErr != nil {
return userImportErr
}
@@ -62,20 +63,20 @@ 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
}
}
}
- overwrite, err := getBool(cmd.Flags(), "overwrite")
+ overwrite, err := flags.GetBool("overwrite")
if err != nil {
return err
}
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 {
@@ -87,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 { //nolint:govet
+ if conflictuous, err := st.Users.Get("", user.Username); err == nil {
return usernameConflictError(user.Username, conflictuous.ID, user.ID)
}
}
@@ -97,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 a939e605..e9a484fc 100644
--- a/cmd/users_update.go
+++ b/cmd/users_update.go
@@ -21,19 +21,20 @@ 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 {
- username, id := parseUsernameOrID(args[0])
+ RunE: withStore(func(cmd *cobra.Command, args []string, st *store) error {
flags := cmd.Flags()
- password, err := getString(flags, "password")
- if err != nil {
- return err
- }
- newUsername, err := getString(flags, "username")
+ username, id := parseUsernameOrID(args[0])
+ password, err := flags.GetString("password")
if err != nil {
return err
}
- s, err := d.store.Settings.Get()
+ newUsername, err := flags.GetString("username")
+ if err != nil {
+ return err
+ }
+
+ s, err := st.Settings.Get()
if err != nil {
return err
}
@@ -41,46 +42,50 @@ options you want to change.`,
var (
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)
if err != nil {
return err
}
+
user.Scope = defaults.Scope
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
- user.LockPassword, err = getBool(flags, "lockPassword")
+ user.LockPassword, err = flags.GetBool("lockPassword")
if err != nil {
return err
}
- user.DateFormat, err = getBool(flags, "dateFormat")
+
+ user.DateFormat, err = flags.GetBool("dateFormat")
if err != nil {
return err
}
- user.HideDotfiles, err = getBool(flags, "hideDotfiles")
+
+ user.HideDotfiles, err = flags.GetBool("hideDotfiles")
if err != nil {
return err
}
@@ -96,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 1971afd4..ee637fa3 100644
--- a/cmd/utils.go
+++ b/cmd/utils.go
@@ -12,8 +12,11 @@ import (
"strings"
"github.com/asdine/storm/v3"
+ homedir "github.com/mitchellh/go-homedir"
+ "github.com/samber/lo"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
+ "github.com/spf13/viper"
yaml "gopkg.in/yaml.v3"
"github.com/filebrowser/filebrowser/v2/settings"
@@ -21,42 +24,22 @@ import (
"github.com/filebrowser/filebrowser/v2/storage/bolt"
)
-const dbPerms = 0640
+const databasePermissions = 0640
-func returnErr(err error) error {
- if err != nil {
- return err
- }
- return nil
-}
-
-func getString(flags *pflag.FlagSet, flag string) (string, error) {
- s, err := flags.GetString(flag)
- return s, returnErr(err)
-}
-
-func getMode(flags *pflag.FlagSet, flag string) (fs.FileMode, error) {
- s, err := getString(flags, flag)
+func getAndParseFileMode(flags *pflag.FlagSet, name string) (fs.FileMode, error) {
+ mode, err := flags.GetString(name)
if err != nil {
return 0, err
}
- b, err := strconv.ParseUint(s, 0, 32)
+
+ b, err := strconv.ParseUint(mode, 0, 32)
if err != nil {
return 0, err
}
+
return fs.FileMode(b), nil
}
-func getBool(flags *pflag.FlagSet, flag string) (bool, error) {
- b, err := flags.GetBool(flag)
- return b, returnErr(err)
-}
-
-func getUint(flags *pflag.FlagSet, flag string) (uint, error) {
- b, err := flags.GetUint(flag)
- return b, returnErr(err)
-}
-
func generateKey() []byte {
k, err := settings.GenerateKey()
if err != nil {
@@ -65,20 +48,6 @@ func generateKey() []byte {
return k
}
-type cobraFunc func(cmd *cobra.Command, args []string) error
-type pythonFunc func(cmd *cobra.Command, args []string, data *pythonData) error
-
-type pythonConfig struct {
- noDB bool
- allowNoDB bool
-}
-
-type pythonData struct {
- hadDB bool
- store *storage.Storage
- err error
-}
-
func dbExists(path string) (bool, error) {
stat, err := os.Stat(path)
if err == nil {
@@ -89,7 +58,7 @@ func dbExists(path string) (bool, error) {
d := filepath.Dir(path)
_, err = os.Stat(d)
if os.IsNotExist(err) {
- if err := os.MkdirAll(d, 0700); err != nil { //nolint:govet
+ if err := os.MkdirAll(d, 0700); err != nil {
return false, err
}
return false, nil
@@ -99,42 +68,137 @@ func dbExists(path string) (bool, error) {
return false, err
}
-func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
+// Generate the replacements for all environment variables. This allows to
+// use FB_BRANDING_DISABLE_EXTERNAL environment variables, even when the
+// option name is branding.disableExternal.
+func generateEnvKeyReplacements(cmd *cobra.Command) []string {
+ replacements := []string{}
+
+ cmd.Flags().VisitAll(func(f *pflag.Flag) {
+ oldName := strings.ToUpper(f.Name)
+ newName := strings.ToUpper(lo.SnakeCase(f.Name))
+ replacements = append(replacements, oldName, newName)
+ })
+
+ return replacements
+}
+
+func initViper(cmd *cobra.Command) (*viper.Viper, error) {
+ v := viper.New()
+
+ // Get config file from flag
+ cfgFile, err := cmd.Flags().GetString("config")
+ if err != nil {
+ return nil, err
+ }
+
+ // Configuration file
+ if cfgFile == "" {
+ home, err := homedir.Dir()
+ if err != nil {
+ return nil, err
+ }
+ v.AddConfigPath(".")
+ v.AddConfigPath(home)
+ v.AddConfigPath("/etc/filebrowser/")
+ v.SetConfigName(".filebrowser")
+ } else {
+ v.SetConfigFile(cfgFile)
+ }
+
+ // Environment variables
+ v.SetEnvPrefix("FB")
+ v.AutomaticEnv()
+ v.SetEnvKeyReplacer(strings.NewReplacer(generateEnvKeyReplacements(cmd)...))
+
+ // Bind the flags
+ err = v.BindPFlags(cmd.Flags())
+ if err != nil {
+ return nil, err
+ }
+
+ // Read in configuration
+ if err := v.ReadInConfig(); err != nil {
+ if errors.Is(err, viper.ConfigParseError{}) {
+ return nil, err
+ }
+
+ log.Println("No config file used")
+ } else {
+ log.Printf("Using config file: %s", v.ConfigFileUsed())
+ }
+
+ // Return Viper
+ return v, nil
+}
+
+type store struct {
+ *storage.Storage
+ databaseExisted bool
+}
+
+type storeOptions struct {
+ expectsNoDatabase bool
+ allowsNoDatabase bool
+}
+
+type cobraFunc func(cmd *cobra.Command, args []string) error
+
+// 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 {
- data := &pythonData{hadDB: true}
-
- path := getStringParam(cmd.Flags(), "database")
- absPath, err := filepath.Abs(path)
+ v, err := initViper(cmd)
if err != nil {
- panic(err)
+ return err
}
+
+ path, err := filepath.Abs(v.GetString("database"))
+ if err != nil {
+ return err
+ }
+
exists, err := dbExists(path)
-
- if err != nil {
- panic(err)
- } else if exists && cfg.noDB {
- log.Fatal(absPath + " already exists")
- } else if !exists && !cfg.noDB && !cfg.allowNoDB {
- log.Fatal(absPath + " does not exist. Please run 'filebrowser config init' first.")
- } else if !exists && !cfg.noDB {
- log.Println("Warning: filebrowser.db can't be found. Initialing in " + strings.TrimSuffix(absPath, "filebrowser.db"))
+ switch {
+ case err != nil:
+ return err
+ 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.hadDB = exists
- db, err := storm.Open(path, storm.BoltOptions(dbPerms, nil))
+ log.Println("Using database: " + path)
+
+ db, err := storm.Open(path, storm.BoltOptions(databasePermissions, nil))
if err != nil {
return err
}
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/commitlint.config.js b/commitlint.config.js
deleted file mode 100644
index 23d00367..00000000
--- a/commitlint.config.js
+++ /dev/null
@@ -1,34 +0,0 @@
-module.exports = {
- rules: {
- 'body-leading-blank': [1, 'always'],
- 'body-max-line-length': [2, 'always', 100],
- 'footer-leading-blank': [1, 'always'],
- 'footer-max-line-length': [2, 'always', 100],
- 'header-max-length': [2, 'always', 100],
- 'scope-case': [2, 'always', 'lower-case'],
- 'subject-case': [
- 2,
- 'never',
- ['sentence-case', 'start-case', 'pascal-case', 'upper-case'],
- ],
- 'subject-full-stop': [2, 'never', '.'],
- 'type-case': [2, 'always', 'lower-case'],
- 'type-empty': [2, 'never'],
- 'type-enum': [
- 2,
- 'always',
- [
- 'feat',
- 'fix',
- 'perf',
- 'revert',
- 'refactor',
- 'build',
- 'ci',
- 'test',
- 'chore',
- 'docs',
- ],
- ],
- },
-};
diff --git a/diskcache/file_cache.go b/diskcache/file_cache.go
index cd5e27c7..b2979e4b 100644
--- a/diskcache/file_cache.go
+++ b/diskcache/file_cache.go
@@ -2,7 +2,7 @@ package diskcache
import (
"context"
- "crypto/sha1" //nolint:gosec
+ "crypto/sha1"
"encoding/hex"
"errors"
"fmt"
@@ -103,7 +103,7 @@ func (f *FileCache) getScopedLocks(key string) (lock sync.Locker) {
}
func (f *FileCache) getFileName(key string) string {
- hasher := sha1.New() //nolint:gosec
+ hasher := sha1.New()
_, _ = hasher.Write([]byte(key))
hash := hex.EncodeToString(hasher.Sum(nil))
return fmt.Sprintf("%s/%s/%s", hash[:1], hash[1:3], hash)
diff --git a/diskcache/file_cache_test.go b/diskcache/file_cache_test.go
index 31d58c8e..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) { //nolint:revive
+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/docker/common/defaults/settings.json b/docker/common/defaults/settings.json
index e787ef87..cf7fb4ee 100644
--- a/docker/common/defaults/settings.json
+++ b/docker/common/defaults/settings.json
@@ -5,4 +5,4 @@
"log": "stdout",
"database": "/database/filebrowser.db",
"root": "/srv"
-}
\ No newline at end of file
+}
diff --git a/errors/errors.go b/errors/errors.go
index f8abee59..748354a8 100644
--- a/errors/errors.go
+++ b/errors/errors.go
@@ -1,40 +1,28 @@
-package errors
+package fberrors
import (
"errors"
"fmt"
- "os"
- "syscall"
-)
-
-const (
- ExitCodeSigTerm = 128 + int(syscall.SIGTERM)
- ExitCodeSighup = 128 + int(syscall.SIGHUP)
- ExitCodeSigint = 128 + int(syscall.SIGINT)
- ExitCodeSigquit = 128 + int(syscall.SIGQUIT)
)
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")
- ErrSigTerm = errors.New("exit on signal: sigterm")
- ErrSighup = errors.New("exit on signal: sighup")
- ErrSigint = errors.New("exit on signal: sigint")
- ErrSigquit = errors.New("exit on signal: sigquit")
+ 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 {
@@ -44,44 +32,3 @@ type ErrShortPassword struct {
func (e ErrShortPassword) Error() string {
return fmt.Sprintf("password is too short, minimum length is %d", e.MinimumLength)
}
-
-// GetExitCode returns the exit code for a given error.
-func GetExitCode(err error) int {
- if err == nil {
- return 0
- }
-
- exitCodeMap := map[error]int{
- ErrSigTerm: ExitCodeSigTerm,
- ErrSighup: ExitCodeSighup,
- ErrSigint: ExitCodeSigint,
- ErrSigquit: ExitCodeSigquit,
- }
-
- for e, code := range exitCodeMap {
- if errors.Is(err, e) {
- return code
- }
- }
-
- if exitErr, ok := err.(interface{ ExitCode() int }); ok {
- return exitErr.ExitCode()
- }
-
- var pathErr *os.PathError
- if errors.As(err, &pathErr) {
- return 1
- }
-
- var syscallErr *os.SyscallError
- if errors.As(err, &syscallErr) {
- return 1
- }
-
- var errno syscall.Errno
- if errors.As(err, &errno) {
- return 1
- }
-
- return 1
-}
diff --git a/files/file.go b/files/file.go
index 8e27e549..2ba432dc 100644
--- a/files/file.go
+++ b/files/file.go
@@ -1,8 +1,8 @@
package files
import (
- "crypto/md5" //nolint:gosec
- "crypto/sha1" //nolint:gosec
+ "crypto/md5"
+ "crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
@@ -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 { //nolint:govet
+ 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 {
@@ -183,7 +184,6 @@ func (i *FileInfo) Checksum(algo string) error {
var h hash.Hash
- //nolint:gosec
switch algo {
case "md5":
h = md5.New()
@@ -194,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)
@@ -219,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
@@ -250,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"):
@@ -388,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 {
@@ -435,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)
@@ -452,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/files/mime.go b/files/mime.go
index 33fd93bd..baa4d6d5 100644
--- a/files/mime.go
+++ b/files/mime.go
@@ -600,7 +600,6 @@ var types = map[string]string{
".epub": "application/epub+zip",
}
-//nolint:gochecknoinits
func init() {
for ext, typ := range types {
// skip errors
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 4f4907ee..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.3(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.3':
- resolution: {integrity: sha512-KpR1F/eGAG9D1RZ0/T6zWJs6dh/pRLfY5WupecyYKJ1fjVmDMgTPw9wXmKv2rBjo4zCJiOSiyB8BDP1OUwpMEA==}
+ '@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.3:
- resolution: {integrity: sha512-StMNfZHwPIXQgY3KxPKM0Jsoc8b46mDV3Fn2UlHCBIwRJApjqrSwqeMYgWf0zpN+g857y74pv7GWuBm+UqQe1w==}
+ 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.3(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.3(typescript@5.9.3):
+ vue-tsc@3.2.2(typescript@5.9.3):
dependencies:
- '@volar/typescript': 2.4.23
- '@vue/language-core': 3.1.3(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/ContextMenu.vue b/frontend/src/components/ContextMenu.vue
new file mode 100644
index 00000000..14663fd9
--- /dev/null
+++ b/frontend/src/components/ContextMenu.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
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 5bbf8847..c7961e3e 100644
--- a/frontend/src/components/Sidebar.vue
+++ b/frontend/src/components/Sidebar.vue
@@ -63,6 +63,7 @@
signup,
+ hideLoginButton: () => hideLoginButton,
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 @@
+
+
+
+
error
+
{{ displayError }}
+
+
+
description
+
{{ $t("files.lonely") }}
+
+
+
+
+
+ |
+ {{ header || `Column ${index + 1}` }}
+ |
+
+
+
+
+ |
+ {{ cell }}
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/files/ListingItem.vue b/frontend/src/components/files/ListingItem.vue
index 14d897cf..d75c2f98 100644
--- a/frontend/src/components/files/ListingItem.vue
+++ b/frontend/src/components/files/ListingItem.vue
@@ -20,6 +20,7 @@
:aria-label="name"
:aria-selected="isSelected"
:data-ext="getExtension(name).toLowerCase()"
+ @contextmenu="contextMenu"
>
![]()
{
else click(event);
};
+const contextMenu = (event: MouseEvent) => {
+ event.preventDefault();
+ if (
+ fileStore.selected.length === 0 ||
+ event.ctrlKey ||
+ fileStore.selected.indexOf(props.index) === -1
+ ) {
+ click(event);
+ }
+};
+
const click = (event: Event | KeyboardEvent) => {
if (!singleClick.value && fileStore.selectedCount !== 0)
event.preventDefault();
@@ -253,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;
}
@@ -279,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/Global.vue b/frontend/src/views/settings/Global.vue
index 0a42a293..3d391206 100644
--- a/frontend/src/views/settings/Global.vue
+++ b/frontend/src/views/settings/Global.vue
@@ -18,6 +18,11 @@
{{ t("settings.createUserDir") }}
+
+
+ {{ t("settings.hideLoginButton") }}
+
+
{{ 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 7736fe11..edf46277 100644
--- a/go.mod
+++ b/go.mod
@@ -12,22 +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/pelletier/go-toml/v2 v2.2.4
- github.com/shirou/gopsutil/v4 v4.25.10
+ github.com/samber/lo v1.52.0
+ 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
- go.etcd.io/bbolt v1.4.3
- 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
)
@@ -40,11 +39,12 @@ require (
github.com/bodgit/plumbing v1.3.0 // indirect
github.com/bodgit/sevenzip v1.6.1 // indirect
github.com/bodgit/windows v1.0.1 // indirect
+ github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
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
@@ -58,9 +58,11 @@ require (
github.com/mikelolasagasti/xz v1.0.1 // indirect
github.com/minio/minlz v1.0.1 // indirect
github.com/nwaples/rardecode/v2 v2.2.0 // indirect
+ github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
+ github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sorairolake/lzip-go v0.3.8 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
@@ -68,11 +70,12 @@ require (
github.com/subosito/gotenv v1.6.0 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
+ 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 0b374007..45dff859 100644
--- a/go.sum
+++ b/go.sum
@@ -47,6 +47,7 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -74,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=
@@ -168,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=
@@ -196,12 +197,15 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
-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/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.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=
@@ -210,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=
@@ -262,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=
@@ -275,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=
@@ -315,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=
@@ -329,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=
@@ -356,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=
@@ -368,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 1da1f75c..f0380c50 100644
--- a/http/commands.go
+++ b/http/commands.go
@@ -1,4 +1,4 @@
-package http
+package fbhttp
import (
"bufio"
@@ -28,7 +28,6 @@ var (
cmdNotAllowed = []byte("Command not allowed.")
)
-//nolint:unparam
func wsErr(ws *websocket.Conn, r *http.Request, status int, err error) {
txt := http.StatusText(status)
if err != nil || status >= 400 {
@@ -49,7 +48,7 @@ var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *d
var raw string
for {
- _, msg, err := conn.ReadMessage() //nolint:govet
+ _, msg, err := conn.ReadMessage()
if err != nil {
wsErr(conn, r, http.StatusInternalServerError, err)
return 0, nil
@@ -63,7 +62,7 @@ var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *d
// Fail fast
if !d.server.EnableExec || !d.user.Perm.Execute {
- if err := conn.WriteMessage(websocket.TextMessage, cmdNotAllowed); err != nil { //nolint:govet
+ if err := conn.WriteMessage(websocket.TextMessage, cmdNotAllowed); err != nil {
wsErr(conn, r, http.StatusInternalServerError, err)
}
@@ -72,21 +71,21 @@ var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *d
command, name, err := runner.ParseCommand(d.settings, raw)
if err != nil {
- if err := conn.WriteMessage(websocket.TextMessage, []byte(err.Error())); err != nil { //nolint:govet
+ if err := conn.WriteMessage(websocket.TextMessage, []byte(err.Error())); err != nil {
wsErr(conn, r, http.StatusInternalServerError, err)
}
return 0, nil
}
if !slices.Contains(d.user.Commands, name) {
- if err := conn.WriteMessage(websocket.TextMessage, cmdNotAllowed); err != nil { //nolint:govet
+ if err := conn.WriteMessage(websocket.TextMessage, cmdNotAllowed); err != nil {
wsErr(conn, r, http.StatusInternalServerError, err)
}
return 0, nil
}
- cmd := exec.Command(command[0], command[1:]...) //nolint:gosec
+ cmd := exec.Command(command[0], command[1:]...)
cmd.Dir = d.user.FullPath(r.URL.Path)
stdout, err := cmd.StdoutPipe()
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 1abc6019..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"
@@ -124,12 +124,12 @@ func createPreview(imgSvc ImgService, fileCache FileCache,
options []img.Option
)
- switch {
- case previewSize == PreviewSizeBig:
+ switch previewSize {
+ case PreviewSizeBig:
width = 1080
height = 1080
options = append(options, img.WithMode(img.ResizeModeFit), img.WithQuality(img.QualityMedium))
- case previewSize == PreviewSizeThumb:
+ case PreviewSizeThumb:
width = 256
height = 256
options = append(options, img.WithMode(img.ResizeModeFill), img.WithQuality(img.QualityLow), img.WithFormat(img.FormatJpeg))
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 5e9e01ba..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)
}
@@ -98,8 +105,8 @@ var publicShareHandler = withHashFile(func(w http.ResponseWriter, r *http.Reques
file := d.raw.(*files.FileInfo)
if file.IsDir {
- file.Listing.Sorting = files.Sorting{By: "name", Asc: false}
- file.Listing.ApplySort()
+ file.Sorting = files.Sorting{By: "name", Asc: false}
+ file.ApplySort()
return renderJSON(w, r, file)
}
diff --git a/http/public_test.go b/http/public_test.go
index 9f89cbc0..9a9c7f2e 100644
--- a/http/public_test.go
+++ b/http/public_test.go
@@ -1,4 +1,4 @@
-package http
+package fbhttp
import (
"fmt"
@@ -19,7 +19,7 @@ import (
func TestPublicShareHandlerAuthentication(t *testing.T) {
t.Parallel()
- const passwordBcrypt = "$2y$10$TFAmdCbyd/mEZDe5fUeZJu.MaJQXRTwdqb/IQV.eTn6dWrF58gCSe" //nolint:gosec
+ const passwordBcrypt = "$2y$10$TFAmdCbyd/mEZDe5fUeZJu.MaJQXRTwdqb/IQV.eTn6dWrF58gCSe"
testCases := map[string]struct {
share *share.Link
req *http.Request
@@ -70,7 +70,7 @@ func TestPublicShareHandlerAuthentication(t *testing.T) {
}
t.Cleanup(func() {
- if err := db.Close(); err != nil { //nolint:govet
+ if err := db.Close(); err != nil {
t.Errorf("failed to close db: %v", err)
}
})
diff --git a/http/raw.go b/http/raw.go
index cbddad59..a6857ffb 100644
--- a/http/raw.go
+++ b/http/raw.go
@@ -1,4 +1,4 @@
-package http
+package fbhttp
import (
"errors"
@@ -32,7 +32,7 @@ func parseQueryFiles(r *http.Request, f *files.FileInfo, _ *users.User) ([]strin
fileSlice = append(fileSlice, f.Path)
} else {
for _, name := range names {
- name, err := url.QueryUnescape(strings.Replace(name, "+", "%2B", -1)) //nolint:govet
+ name, err := url.QueryUnescape(strings.ReplaceAll(name, "+", "%2B"))
if err != nil {
return nil, err
}
@@ -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 8785c681..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"
)
@@ -37,14 +37,14 @@ var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d
}
if file.IsDir {
- file.Listing.Sorting = d.user.Sorting
- file.Listing.ApplySort()
+ file.Sorting = d.user.Sorting
+ file.ApplySort()
return renderJSON(w, r, file)
}
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 0abe6b13..a07d322e 100644
--- a/http/settings.go
+++ b/http/settings.go
@@ -1,4 +1,4 @@
-package http
+package fbhttp
import (
"encoding/json"
@@ -10,10 +10,12 @@ import (
type settingsData struct {
Signup bool `json:"signup"`
+ HideLoginButton bool `json:"hideLoginButton"`
CreateUserDir bool `json:"createUserDir"`
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"`
@@ -24,10 +26,12 @@ type settingsData struct {
var settingsGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
data := &settingsData{
Signup: d.settings.Signup,
+ HideLoginButton: d.settings.HideLoginButton,
CreateUserDir: d.settings.CreateUserDir,
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,
@@ -55,6 +59,7 @@ var settingsPutHandler = withAdmin(func(_ http.ResponseWriter, r *http.Request,
d.settings.Tus = req.Tus
d.settings.Shell = req.Shell
d.settings.Commands = req.Commands
+ d.settings.HideLoginButton = req.HideLoginButton
err = d.store.Settings.Save(d.settings)
return errToStatus(err), err
diff --git a/http/share.go b/http/share.go
index d968c05b..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{})
}
@@ -111,7 +111,6 @@ var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request
var expire int64 = 0
if body.Expires != "" {
- //nolint:govet
num, err := strconv.Atoi(body.Expires)
if err != nil {
return http.StatusInternalServerError, err
diff --git a/http/static.go b/http/static.go
index 447471d2..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,
@@ -46,11 +47,12 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys
"ResizePreview": d.server.ResizePreview,
"EnableExec": d.server.EnableExec,
"TusSettings": d.settings.Tus,
+ "HideLoginButton": d.settings.HideLoginButton,
}
if d.settings.Branding.Files != "" {
fPath := filepath.Join(d.settings.Branding.Files, "custom.css")
- _, err := os.Stat(fPath) //nolint:govet
+ _, err := os.Stat(fPath)
if err != nil && !os.IsNotExist(err) {
log.Printf("couldn't load custom styles: %v", err)
@@ -62,7 +64,7 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys
}
if d.settings.AuthMethod == auth.MethodJSONAuth {
- raw, err := d.store.Auth.Get(d.settings.AuthMethod) //nolint:govet
+ raw, err := d.store.Auth.Get(d.settings.AuthMethod)
if err != nil {
return http.StatusInternalServerError, err
}
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/img/service.go b/img/service.go
index 34a84c67..47aaddca 100644
--- a/img/service.go
+++ b/img/service.go
@@ -184,7 +184,7 @@ func (s *Service) Resize(ctx context.Context, in io.Reader, width, height int, o
case ResizeModeFill:
img = imaging.Fill(img, width, height, imaging.Center, config.quality.resampleFilter())
case ResizeModeFit:
- fallthrough //nolint:gocritic
+ fallthrough
default:
img = imaging.Fit(img, width, height, config.quality.resampleFilter())
}
diff --git a/main.go b/main.go
index d17550c9..bde39461 100644
--- a/main.go
+++ b/main.go
@@ -4,11 +4,10 @@ import (
"os"
"github.com/filebrowser/filebrowser/v2/cmd"
- "github.com/filebrowser/filebrowser/v2/errors"
)
func main() {
if err := cmd.Execute(); err != nil {
- os.Exit(errors.GetExitCode(err))
+ os.Exit(1)
}
}
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/runner/runner.go b/runner/runner.go
index 00e7c16a..3408a1ee 100644
--- a/runner/runner.go
+++ b/runner/runner.go
@@ -89,9 +89,9 @@ func (r *Runner) exec(raw, evt, path, dst string, user *users.User) error {
command[i] = os.Expand(arg, envMapping)
}
- cmd := exec.Command(command[0], command[1:]...) //nolint:gosec
+ cmd := exec.Command(command[0], command[1:]...)
cmd.Env = append(os.Environ(), fmt.Sprintf("FILE=%s", path))
- cmd.Env = append(cmd.Env, fmt.Sprintf("SCOPE=%s", user.Scope)) //nolint:gocritic
+ cmd.Env = append(cmd.Env, fmt.Sprintf("SCOPE=%s", user.Scope))
cmd.Env = append(cmd.Env, fmt.Sprintf("TRIGGER=%s", evt))
cmd.Env = append(cmd.Env, fmt.Sprintf("USERNAME=%s", user.Username))
cmd.Env = append(cmd.Env, fmt.Sprintf("DESTINATION=%s", dst))
diff --git a/search/conditions.go b/search/conditions.go
index 970171a1..c7bcd3b9 100644
--- a/search/conditions.go
+++ b/search/conditions.go
@@ -48,8 +48,8 @@ func parseSearch(value string) *searchOptions {
}
// removes the options from the value
- value = strings.Replace(value, "case:insensitive", "", -1)
- value = strings.Replace(value, "case:sensitive", "", -1)
+ value = strings.ReplaceAll(value, "case:insensitive", "")
+ value = strings.ReplaceAll(value, "case:sensitive", "")
value = strings.TrimSpace(value)
types := typeRegexp.FindAllStringSubmatch(value, -1)
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/dir.go b/settings/dir.go
index 25289ee4..0e857950 100644
--- a/settings/dir.go
+++ b/settings/dir.go
@@ -42,7 +42,7 @@ func (s *Settings) MakeUserDir(username, userScope, serverRoot string) (string,
func cleanUsername(s string) string {
// Remove any trailing space to avoid ending on -
s = strings.Trim(s, " ")
- s = strings.Replace(s, "..", "", -1)
+ s = strings.ReplaceAll(s, "..", "")
// Replace all characters which not in the list `0-9A-Za-z@_\-.` with a dash
s = invalidFilenameChars.ReplaceAllString(s, "-")
diff --git a/settings/settings.go b/settings/settings.go
index 787921e9..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
@@ -22,10 +23,12 @@ type AuthMethod string
type Settings struct {
Key []byte `json:"key"`
Signup bool `json:"signup"`
+ HideLoginButton bool `json:"hideLoginButton"`
CreateUserDir bool `json:"createUserDir"`
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"`
@@ -34,6 +37,7 @@ type Settings struct {
MinimumPasswordLength uint `json:"minimumPasswordLength"`
FileMode fs.FileMode `json:"fileMode"`
DirMode fs.FileMode `json:"dirMode"`
+ HideDotfiles bool `json:"hideDotfiles"`
}
// GetRules implements rules.Provider.
@@ -55,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/importer/conf.go b/storage/bolt/importer/conf.go
deleted file mode 100644
index b3ac0ba7..00000000
--- a/storage/bolt/importer/conf.go
+++ /dev/null
@@ -1,187 +0,0 @@
-package importer
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "os"
- "path/filepath"
-
- "github.com/asdine/storm/v3"
- "github.com/pelletier/go-toml/v2"
- "gopkg.in/yaml.v3"
-
- "github.com/filebrowser/filebrowser/v2/auth"
- "github.com/filebrowser/filebrowser/v2/settings"
- "github.com/filebrowser/filebrowser/v2/storage"
- "github.com/filebrowser/filebrowser/v2/users"
-)
-
-type oldDefs struct {
- Commands []string `json:"commands" yaml:"commands" toml:"commands"`
- Scope string `json:"scope" yaml:"scope" toml:"scope"`
- ViewMode string `json:"viewMode" yaml:"viewMode" toml:"viewMode"`
- Locale string `json:"locale" yaml:"locale" toml:"locale"`
- AllowCommands bool `json:"allowCommands" yaml:"allowCommands" toml:"allowCommands"`
- AllowEdit bool `json:"allowEdit" yaml:"allowEdit" toml:"allowEdit"`
- AllowNew bool `json:"allowNew" yaml:"allowNew" toml:"allowNew"`
-}
-
-type oldAuth struct {
- Method string `json:"method" yaml:"method" toml:"method"` // default none proxy
- Header string `json:"header" yaml:"header" toml:"header"`
- Command string `json:"command" yaml:"command" toml:"command"`
-}
-
-type oldConf struct {
- Port string `json:"port" yaml:"port" toml:"port"`
- BaseURL string `json:"baseURL" yaml:"baseURL" toml:"baseURL"`
- Log string `json:"log" yaml:"log" toml:"log"`
- Address string `json:"address" yaml:"address" toml:"address"`
- Defaults oldDefs `json:"defaults" yaml:"defaults" toml:"defaults"`
- ReCaptcha struct {
- Key string `json:"key" yaml:"key" toml:"key"`
- Secret string `json:"secret" yaml:"secret" toml:"secret"`
- Host string `json:"host" yaml:"host" toml:"host"`
- } `json:"recaptcha" yaml:"recaptcha" toml:"recaptcha"`
- Auth oldAuth `json:"auth" yaml:"auth" toml:"auth"`
-}
-
-var defaults = &oldConf{
- Port: "0",
- Log: "stdout",
- Defaults: oldDefs{
- Commands: []string{"git", "svn", "hg"},
- ViewMode: string(users.MosaicViewMode),
- AllowCommands: true,
- AllowEdit: true,
- AllowNew: true,
- Locale: "en",
- },
- Auth: oldAuth{
- Method: "default",
- },
-}
-
-func readConf(path string) (*oldConf, error) {
- cfg := &oldConf{}
- if path != "" {
- ext := filepath.Ext(path)
-
- fd, err := os.Open(path)
- if err != nil {
- return nil, err
- }
- defer fd.Close()
-
- switch ext {
- case ".json":
- err = json.NewDecoder(fd).Decode(cfg)
- case ".toml":
- err = toml.NewDecoder(fd).Decode(cfg)
- case ".yaml", ".yml":
- err = yaml.NewDecoder(fd).Decode(cfg)
- default:
- return nil, errors.New("unsupported config extension " + ext)
- }
-
- if err != nil {
- return nil, err
- }
- } else {
- cfg = defaults
- path, err := filepath.Abs(".")
- if err != nil {
- return nil, err
- }
- cfg.Defaults.Scope = path
- }
- return cfg, nil
-}
-
-func importConf(db *storm.DB, path string, sto *storage.Storage) error {
- cfg, err := readConf(path)
- if err != nil {
- return err
- }
-
- commands := map[string][]string{}
- err = db.Get("config", "commands", &commands)
- if err != nil {
- return err
- }
-
- key := []byte{}
- err = db.Get("config", "key", &key)
- if err != nil {
- return err
- }
-
- s := &settings.Settings{
- Key: key,
- Signup: false,
- Defaults: settings.UserDefaults{
- Scope: cfg.Defaults.Scope,
- Commands: cfg.Defaults.Commands,
- ViewMode: users.ViewMode(cfg.Defaults.ViewMode),
- Locale: cfg.Defaults.Locale,
- Perm: users.Permissions{
- Admin: false,
- Execute: cfg.Defaults.AllowCommands,
- Create: cfg.Defaults.AllowNew,
- Rename: cfg.Defaults.AllowEdit,
- Modify: cfg.Defaults.AllowEdit,
- Delete: cfg.Defaults.AllowEdit,
- Share: true,
- Download: true,
- },
- },
- }
-
- server := &settings.Server{
- BaseURL: cfg.BaseURL,
- Port: cfg.Port,
- Address: cfg.Address,
- Log: cfg.Log,
- }
-
- var auther auth.Auther
- switch cfg.Auth.Method {
- case "proxy":
- auther = &auth.ProxyAuth{Header: cfg.Auth.Header}
- s.AuthMethod = auth.MethodProxyAuth
- case "hook":
- auther = &auth.HookAuth{Command: cfg.Auth.Command}
- s.AuthMethod = auth.MethodHookAuth
- case "none":
- auther = &auth.NoAuth{}
- s.AuthMethod = auth.MethodNoAuth
- default:
- auther = &auth.JSONAuth{
- ReCaptcha: &auth.ReCaptcha{
- Host: cfg.ReCaptcha.Host,
- Key: cfg.ReCaptcha.Key,
- Secret: cfg.ReCaptcha.Secret,
- },
- }
- s.AuthMethod = auth.MethodJSONAuth
- }
-
- err = sto.Auth.Save(auther)
- if err != nil {
- return err
- }
-
- err = sto.Settings.Save(s)
- if err != nil {
- return err
- }
-
- err = sto.Settings.SaveServer(server)
- if err != nil {
- return err
- }
-
- fmt.Println("Configuration successfully imported.")
- return nil
-}
diff --git a/storage/bolt/importer/importer.go b/storage/bolt/importer/importer.go
deleted file mode 100644
index 9c737756..00000000
--- a/storage/bolt/importer/importer.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package importer
-
-import (
- "github.com/asdine/storm/v3"
-
- "github.com/filebrowser/filebrowser/v2/storage/bolt"
-)
-
-// Import imports an old configuration to a newer database.
-func Import(oldDBPath, oldConf, newDBPath string) error {
- oldDB, err := storm.Open(oldDBPath)
- if err != nil {
- return err
- }
- defer oldDB.Close()
-
- newDB, err := storm.Open(newDBPath)
- if err != nil {
- return err
- }
- defer newDB.Close()
-
- sto, err := bolt.NewStorage(newDB)
- if err != nil {
- return err
- }
-
- err = importUsers(oldDB, sto)
- if err != nil {
- return err
- }
-
- err = importConf(oldDB, oldConf, sto)
- if err != nil {
- return err
- }
-
- return err
-}
diff --git a/storage/bolt/importer/users.go b/storage/bolt/importer/users.go
deleted file mode 100644
index 1b442147..00000000
--- a/storage/bolt/importer/users.go
+++ /dev/null
@@ -1,114 +0,0 @@
-package importer
-
-import (
- "encoding/json"
- "fmt"
-
- "github.com/asdine/storm/v3"
- bolt "go.etcd.io/bbolt"
-
- "github.com/filebrowser/filebrowser/v2/rules"
- "github.com/filebrowser/filebrowser/v2/storage"
- "github.com/filebrowser/filebrowser/v2/users"
-)
-
-type oldUser struct {
- ID int `storm:"id,increment"`
- Admin bool `json:"admin"`
- AllowCommands bool `json:"allowCommands"` // Execute commands
- AllowEdit bool `json:"allowEdit"` // Edit/rename files
- AllowNew bool `json:"allowNew"` // Create files and folders
- AllowPublish bool `json:"allowPublish"` // Publish content (to use with static gen)
- LockPassword bool `json:"lockPassword"`
- Commands []string `json:"commands"`
- Locale string `json:"locale"`
- Password string `json:"password"`
- Rules []*rules.Rule `json:"rules"`
- Scope string `json:"filesystem"`
- Username string `json:"username" storm:"index,unique"`
- ViewMode string `json:"viewMode"`
-}
-
-func readOldUsers(db *storm.DB) ([]*oldUser, error) {
- var oldUsers []*oldUser
- err := db.Bolt.View(func(tx *bolt.Tx) error {
- return tx.Bucket([]byte("User")).ForEach(func(_ []byte, v []byte) error {
- if len(v) > 0 && string(v)[0] == '{' {
- user := &oldUser{}
- err := json.Unmarshal(v, user)
-
- if err != nil {
- return err
- }
-
- oldUsers = append(oldUsers, user)
- }
-
- return nil
- })
- })
-
- return oldUsers, err
-}
-
-func convertUsersToNew(old []*oldUser) ([]*users.User, error) {
- list := []*users.User{}
-
- for _, oldUser := range old {
- user := &users.User{
- Username: oldUser.Username,
- Password: oldUser.Password,
- Scope: oldUser.Scope,
- Locale: oldUser.Locale,
- LockPassword: oldUser.LockPassword,
- ViewMode: users.ViewMode(oldUser.ViewMode),
- Commands: oldUser.Commands,
- Rules: []rules.Rule{},
- Perm: users.Permissions{
- Admin: oldUser.Admin,
- Execute: oldUser.AllowCommands,
- Create: oldUser.AllowNew,
- Rename: oldUser.AllowEdit,
- Modify: oldUser.AllowEdit,
- Delete: oldUser.AllowEdit,
- Share: true,
- Download: true,
- },
- }
-
- for _, rule := range oldUser.Rules {
- user.Rules = append(user.Rules, *rule)
- }
-
- err := user.Clean("")
- if err != nil {
- return nil, err
- }
-
- list = append(list, user)
- }
-
- return list, nil
-}
-
-func importUsers(old *storm.DB, sto *storage.Storage) error {
- oldUsers, err := readOldUsers(old)
- if err != nil {
- return err
- }
-
- newUsers, err := convertUsersToNew(oldUsers)
- if err != nil {
- return err
- }
-
- for _, user := range newUsers {
- err = sto.Users.Save(user)
- if err != nil {
- return err
- }
- }
-
- fmt.Printf("%d users successfully imported into the new DB.\n", len(newUsers))
- return nil
-}
diff --git a/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/assets.go b/users/assets.go
index a01aed41..7b09580d 100644
--- a/users/assets.go
+++ b/users/assets.go
@@ -9,7 +9,6 @@ import (
var assets embed.FS
var commonPasswords map[string]struct{}
-//nolint:gochecknoinits
func init() {
// Password list sourced from:
// https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common-Credentials/100k-most-used-passwords-NCSC.txt
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 163082fb..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.
@@ -63,7 +63,7 @@ func (s *Storage) Gets(baseScope string) ([]*User, error) {
}
for _, user := range users {
- if err := user.Clean(baseScope); err != nil { //nolint:govet
+ if err := user.Clean(baseScope); err != nil {
return nil, err
}
}
@@ -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 020faf11..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.
@@ -55,8 +56,6 @@ var checkableFields = []string{
// Clean cleans up a user and verifies if all its fields
// are alright to be saved.
-//
-//nolint:gocyclo
func (u *User) Clean(baseScope string, fields ...string) error {
if len(fields) == 0 {
fields = checkableFields
@@ -66,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 == "" {
@@ -93,7 +92,7 @@ func (u *User) Clean(baseScope string, fields ...string) error {
if u.Fs == nil {
scope := u.Scope
- scope = filepath.Join(baseScope, filepath.Join("/", scope)) //nolint:gocritic
+ scope = filepath.Join(baseScope, filepath.Join("/", scope))
u.Fs = afero.NewBasePathFs(afero.NewOsFs(), scope)
}
diff --git a/www/docs/authentication.md b/www/docs/authentication.md
new file mode 100644
index 00000000..365f9dac
--- /dev/null
+++ b/www/docs/authentication.md
@@ -0,0 +1,49 @@
+# Authentication
+
+There are three possible authentication methods. Each one of them has its own capabilities and specification. If you are interested in contributing with one more authentication method, please [check the guidelines](contributing.md).
+
+## JSON Auth (default)
+
+We call it JSON Authentication but it is just the default authentication method and the one that is provided by default if you don't make any changes. It is set by default, but if you've made changes before you can revert to using JSON auth:
+
+```sh
+filebrowser config set --auth.method=json
+```
+
+This method can also be extended with **reCAPTCHA** verification during login:
+
+```sh
+filebrowser config set --auth.method=json \
+ --recaptcha.key site-key \
+ --recaptcha.secret private-key
+```
+
+By default, we use [Google's reCAPTCHA](https://developers.google.com/recaptcha/docs/display) service. If you live in China, or want to use other provider, you can change the host with the following command:
+
+```sh
+filebrowser config set --recaptcha.host https://recaptcha.net
+```
+
+Where `https://recaptcha.net` is any provider you want.
+
+## Proxy Header
+
+If you have a reverse proxy you want to use to login your users, you do it via our `proxy` authentication method. To configure this method, your proxy must send an HTTP header containing the username of the logged in user:
+
+```sh
+filebrowser config set --auth.method=proxy --auth.header=X-My-Header
+```
+
+Where `X-My-Header` is the HTTP header provided by your proxy with the username.
+
+> [!WARNING]
+>
+> File Browser will blindly trust the provided header. If the proxy can be bypassed, an attacker could simply attach the header and get admin access.
+
+### No Authentication
+
+We also provide a no authentication mechanism for users that want to use File Browser privately such in a home network. By setting this authentication method, the user with **id 1** will be used as the default users. Creating more users won't have any effect.
+
+```sh
+filebrowser config set --auth.method=noauth
+```
diff --git a/www/docs/cli/filebrowser-cmds-add.md b/www/docs/cli/filebrowser-cmds-add.md
new file mode 100644
index 00000000..fc064ea9
--- /dev/null
+++ b/www/docs/cli/filebrowser-cmds-add.md
@@ -0,0 +1,29 @@
+# filebrowser cmds add
+
+Add a command to run on a specific event
+
+## Synopsis
+
+Add a command to run on a specific event.
+
+```
+filebrowser cmds add [flags]
+```
+
+## Options
+
+```
+ -h, --help help for add
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser cmds](filebrowser-cmds.md) - Command runner management utility
+
diff --git a/www/docs/cli/filebrowser-cmds-ls.md b/www/docs/cli/filebrowser-cmds-ls.md
new file mode 100644
index 00000000..136df2e8
--- /dev/null
+++ b/www/docs/cli/filebrowser-cmds-ls.md
@@ -0,0 +1,30 @@
+# filebrowser cmds ls
+
+List all commands for each event
+
+## Synopsis
+
+List all commands for each event.
+
+```
+filebrowser cmds ls [flags]
+```
+
+## Options
+
+```
+ -e, --event string event name, without 'before' or 'after'
+ -h, --help help for ls
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser cmds](filebrowser-cmds.md) - Command runner management utility
+
diff --git a/www/docs/cli/filebrowser-cmds-rm.md b/www/docs/cli/filebrowser-cmds-rm.md
new file mode 100644
index 00000000..837813b9
--- /dev/null
+++ b/www/docs/cli/filebrowser-cmds-rm.md
@@ -0,0 +1,37 @@
+# filebrowser cmds rm
+
+Removes a command from an event hooker
+
+## Synopsis
+
+Removes a command from an event hooker. The provided index
+is the same that's printed when you run 'cmds ls'. Note
+that after each removal/addition, the index of the
+commands change. So be careful when removing them after each
+other.
+
+You can also specify an optional parameter (index_end) so
+you can remove all commands from 'index' to 'index_end',
+including 'index_end'.
+
+```
+filebrowser cmds rm [index_end] [flags]
+```
+
+## Options
+
+```
+ -h, --help help for rm
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser cmds](filebrowser-cmds.md) - Command runner management utility
+
diff --git a/www/docs/cli/filebrowser-cmds.md b/www/docs/cli/filebrowser-cmds.md
new file mode 100644
index 00000000..bc61418b
--- /dev/null
+++ b/www/docs/cli/filebrowser-cmds.md
@@ -0,0 +1,28 @@
+# filebrowser cmds
+
+Command runner management utility
+
+## Synopsis
+
+Command runner management utility.
+
+## Options
+
+```
+ -h, --help help for cmds
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser](filebrowser.md) - A stylish web-based file browser
+* [filebrowser cmds add](filebrowser-cmds-add.md) - Add a command to run on a specific event
+* [filebrowser cmds ls](filebrowser-cmds-ls.md) - List all commands for each event
+* [filebrowser cmds rm](filebrowser-cmds-rm.md) - Removes a command from an event hooker
+
diff --git a/www/docs/cli/filebrowser-completion-bash.md b/www/docs/cli/filebrowser-completion-bash.md
new file mode 100644
index 00000000..e4c8963a
--- /dev/null
+++ b/www/docs/cli/filebrowser-completion-bash.md
@@ -0,0 +1,50 @@
+# filebrowser completion bash
+
+Generate the autocompletion script for bash
+
+## Synopsis
+
+Generate the autocompletion script for the bash shell.
+
+This script depends on the 'bash-completion' package.
+If it is not installed already, you can install it via your OS's package manager.
+
+To load completions in your current shell session:
+
+ source <(filebrowser completion bash)
+
+To load completions for every new session, execute once:
+
+### Linux:
+
+ filebrowser completion bash > /etc/bash_completion.d/filebrowser
+
+### macOS:
+
+ filebrowser completion bash > $(brew --prefix)/etc/bash_completion.d/filebrowser
+
+You will need to start a new shell for this setup to take effect.
+
+
+```
+filebrowser completion bash
+```
+
+## Options
+
+```
+ -h, --help help for bash
+ --no-descriptions disable completion descriptions
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser completion](filebrowser-completion.md) - Generate the autocompletion script for the specified shell
+
diff --git a/www/docs/cli/filebrowser-completion-fish.md b/www/docs/cli/filebrowser-completion-fish.md
new file mode 100644
index 00000000..e8297aa1
--- /dev/null
+++ b/www/docs/cli/filebrowser-completion-fish.md
@@ -0,0 +1,41 @@
+# filebrowser completion fish
+
+Generate the autocompletion script for fish
+
+## Synopsis
+
+Generate the autocompletion script for the fish shell.
+
+To load completions in your current shell session:
+
+ filebrowser completion fish | source
+
+To load completions for every new session, execute once:
+
+ filebrowser completion fish > ~/.config/fish/completions/filebrowser.fish
+
+You will need to start a new shell for this setup to take effect.
+
+
+```
+filebrowser completion fish [flags]
+```
+
+## Options
+
+```
+ -h, --help help for fish
+ --no-descriptions disable completion descriptions
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser completion](filebrowser-completion.md) - Generate the autocompletion script for the specified shell
+
diff --git a/www/docs/cli/filebrowser-completion-powershell.md b/www/docs/cli/filebrowser-completion-powershell.md
new file mode 100644
index 00000000..21f84c4c
--- /dev/null
+++ b/www/docs/cli/filebrowser-completion-powershell.md
@@ -0,0 +1,38 @@
+# filebrowser completion powershell
+
+Generate the autocompletion script for powershell
+
+## Synopsis
+
+Generate the autocompletion script for powershell.
+
+To load completions in your current shell session:
+
+ filebrowser completion powershell | Out-String | Invoke-Expression
+
+To load completions for every new session, add the output of the above command
+to your powershell profile.
+
+
+```
+filebrowser completion powershell [flags]
+```
+
+## Options
+
+```
+ -h, --help help for powershell
+ --no-descriptions disable completion descriptions
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser completion](filebrowser-completion.md) - Generate the autocompletion script for the specified shell
+
diff --git a/www/docs/cli/filebrowser-completion-zsh.md b/www/docs/cli/filebrowser-completion-zsh.md
new file mode 100644
index 00000000..f54794b0
--- /dev/null
+++ b/www/docs/cli/filebrowser-completion-zsh.md
@@ -0,0 +1,52 @@
+# filebrowser completion zsh
+
+Generate the autocompletion script for zsh
+
+## Synopsis
+
+Generate the autocompletion script for the zsh shell.
+
+If shell completion is not already enabled in your environment you will need
+to enable it. You can execute the following once:
+
+ echo "autoload -U compinit; compinit" >> ~/.zshrc
+
+To load completions in your current shell session:
+
+ source <(filebrowser completion zsh)
+
+To load completions for every new session, execute once:
+
+### Linux:
+
+ filebrowser completion zsh > "${fpath[1]}/_filebrowser"
+
+### macOS:
+
+ filebrowser completion zsh > $(brew --prefix)/share/zsh/site-functions/_filebrowser
+
+You will need to start a new shell for this setup to take effect.
+
+
+```
+filebrowser completion zsh [flags]
+```
+
+## Options
+
+```
+ -h, --help help for zsh
+ --no-descriptions disable completion descriptions
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser completion](filebrowser-completion.md) - Generate the autocompletion script for the specified shell
+
diff --git a/www/docs/cli/filebrowser-completion.md b/www/docs/cli/filebrowser-completion.md
new file mode 100644
index 00000000..b2c6bdc1
--- /dev/null
+++ b/www/docs/cli/filebrowser-completion.md
@@ -0,0 +1,31 @@
+# filebrowser completion
+
+Generate the autocompletion script for the specified shell
+
+## Synopsis
+
+Generate the autocompletion script for filebrowser for the specified shell.
+See each sub-command's help for details on how to use the generated script.
+
+
+## Options
+
+```
+ -h, --help help for completion
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser](filebrowser.md) - A stylish web-based file browser
+* [filebrowser completion bash](filebrowser-completion-bash.md) - Generate the autocompletion script for bash
+* [filebrowser completion fish](filebrowser-completion-fish.md) - Generate the autocompletion script for fish
+* [filebrowser completion powershell](filebrowser-completion-powershell.md) - Generate the autocompletion script for powershell
+* [filebrowser completion zsh](filebrowser-completion-zsh.md) - Generate the autocompletion script for zsh
+
diff --git a/www/docs/cli/filebrowser-config-cat.md b/www/docs/cli/filebrowser-config-cat.md
new file mode 100644
index 00000000..952580fd
--- /dev/null
+++ b/www/docs/cli/filebrowser-config-cat.md
@@ -0,0 +1,29 @@
+# filebrowser config cat
+
+Prints the configuration
+
+## Synopsis
+
+Prints the configuration.
+
+```
+filebrowser config cat [flags]
+```
+
+## Options
+
+```
+ -h, --help help for cat
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser config](filebrowser-config.md) - Configuration management utility
+
diff --git a/www/docs/cli/filebrowser-config-export.md b/www/docs/cli/filebrowser-config-export.md
new file mode 100644
index 00000000..141d4e8e
--- /dev/null
+++ b/www/docs/cli/filebrowser-config-export.md
@@ -0,0 +1,31 @@
+# filebrowser config export
+
+Export the configuration to a file
+
+## Synopsis
+
+Export the configuration to a file. The path must be for a
+json or yaml file. This exported configuration can be changed,
+and imported again with 'config import' command.
+
+```
+filebrowser config export [flags]
+```
+
+## Options
+
+```
+ -h, --help help for export
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser config](filebrowser-config.md) - Configuration management utility
+
diff --git a/www/docs/cli/filebrowser-config-import.md b/www/docs/cli/filebrowser-config-import.md
new file mode 100644
index 00000000..871da341
--- /dev/null
+++ b/www/docs/cli/filebrowser-config-import.md
@@ -0,0 +1,36 @@
+# filebrowser config import
+
+Import a configuration file
+
+## Synopsis
+
+Import a configuration file. This will replace all the existing
+configuration. Can be used with or without unexisting databases.
+
+If used with a nonexisting database, a key will be generated
+automatically. Otherwise the key will be kept the same as in the
+database.
+
+The path must be for a json or yaml file.
+
+```
+filebrowser config import [flags]
+```
+
+## Options
+
+```
+ -h, --help help for import
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser config](filebrowser-config.md) - Configuration management utility
+
diff --git a/www/docs/cli/filebrowser-config-init.md b/www/docs/cli/filebrowser-config-init.md
new file mode 100644
index 00000000..0f16efa7
--- /dev/null
+++ b/www/docs/cli/filebrowser-config-init.md
@@ -0,0 +1,89 @@
+# filebrowser config init
+
+Initialize a new database
+
+## Synopsis
+
+Initialize a new database to use with File Browser. All of
+this options can be changed in the future with the command
+'filebrowser config set'. The user related flags apply
+to the defaults when creating new users and you don't
+override the options.
+
+```
+filebrowser config init [flags]
+```
+
+## Options
+
+```
+ --aceEditorTheme string ace editor's syntax highlighting theme for users
+ -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
+ --branding.disableExternal disable external links such as GitHub links
+ --branding.disableUsedPercentage disable used disk percentage graph
+ --branding.files string path to directory with images and custom styles
+ --branding.name string replace 'File Browser' by this name
+ --branding.theme string set the theme
+ -t, --cert string tls certificate
+ --commands strings a list of the commands a user can execute
+ --createUserDir generate user's home directory automatically
+ --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
+ --fileMode string mode bits that new files are created with (default "0o640")
+ -h, --help help for init
+ --hideDotfiles hide dotfiles
+ --hideLoginButton hide login button from public pages
+ -k, --key string tls key
+ --locale string locale for users (default "en")
+ --lockPassword lock password
+ -l, --log string log output (default "stdout")
+ --minimumPasswordLength uint minimum password length for new users (default 12)
+ --perm.admin admin perm for users
+ --perm.create create perm for users (default true)
+ --perm.delete delete perm for users (default true)
+ --perm.download download perm for users (default true)
+ --perm.execute execute perm for users (default true)
+ --perm.modify modify perm for users (default true)
+ --perm.rename rename perm for users (default true)
+ --perm.share share perm for users (default true)
+ -p, --port string port to listen on (default "8080")
+ --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
+ -s, --signup allow users to signup
+ --singleClick use single clicks only
+ --socket string socket to listen to (cannot be used with address, port, cert nor key flags)
+ --sorting.asc sorting by ascending order
+ --sorting.by string sorting mode (name, size or modified) (default "name")
+ --tokenExpirationTime string user session timeout (default "2h")
+ --tus.chunkSize uint the tus chunk size (default 10485760)
+ --tus.retryCount uint16 the tus retry count (default 5)
+ --viewMode string view mode for users (default "list")
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser config](filebrowser-config.md) - Configuration management utility
+
diff --git a/www/docs/cli/filebrowser-config-set.md b/www/docs/cli/filebrowser-config-set.md
new file mode 100644
index 00000000..603bb506
--- /dev/null
+++ b/www/docs/cli/filebrowser-config-set.md
@@ -0,0 +1,86 @@
+# filebrowser config set
+
+Updates the configuration
+
+## Synopsis
+
+Updates the configuration. Set the flags for the options
+you want to change. Other options will remain unchanged.
+
+```
+filebrowser config set [flags]
+```
+
+## Options
+
+```
+ --aceEditorTheme string ace editor's syntax highlighting theme for users
+ -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
+ --branding.disableExternal disable external links such as GitHub links
+ --branding.disableUsedPercentage disable used disk percentage graph
+ --branding.files string path to directory with images and custom styles
+ --branding.name string replace 'File Browser' by this name
+ --branding.theme string set the theme
+ -t, --cert string tls certificate
+ --commands strings a list of the commands a user can execute
+ --createUserDir generate user's home directory automatically
+ --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
+ --fileMode string mode bits that new files are created with (default "0o640")
+ -h, --help help for set
+ --hideDotfiles hide dotfiles
+ --hideLoginButton hide login button from public pages
+ -k, --key string tls key
+ --locale string locale for users (default "en")
+ --lockPassword lock password
+ -l, --log string log output (default "stdout")
+ --minimumPasswordLength uint minimum password length for new users (default 12)
+ --perm.admin admin perm for users
+ --perm.create create perm for users (default true)
+ --perm.delete delete perm for users (default true)
+ --perm.download download perm for users (default true)
+ --perm.execute execute perm for users (default true)
+ --perm.modify modify perm for users (default true)
+ --perm.rename rename perm for users (default true)
+ --perm.share share perm for users (default true)
+ -p, --port string port to listen on (default "8080")
+ --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
+ -s, --signup allow users to signup
+ --singleClick use single clicks only
+ --socket string socket to listen to (cannot be used with address, port, cert nor key flags)
+ --sorting.asc sorting by ascending order
+ --sorting.by string sorting mode (name, size or modified) (default "name")
+ --tokenExpirationTime string user session timeout (default "2h")
+ --tus.chunkSize uint the tus chunk size (default 10485760)
+ --tus.retryCount uint16 the tus retry count (default 5)
+ --viewMode string view mode for users (default "list")
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser config](filebrowser-config.md) - Configuration management utility
+
diff --git a/www/docs/cli/filebrowser-config.md b/www/docs/cli/filebrowser-config.md
new file mode 100644
index 00000000..b87dbf07
--- /dev/null
+++ b/www/docs/cli/filebrowser-config.md
@@ -0,0 +1,30 @@
+# filebrowser config
+
+Configuration management utility
+
+## Synopsis
+
+Configuration management utility.
+
+## Options
+
+```
+ -h, --help help for config
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser](filebrowser.md) - A stylish web-based file browser
+* [filebrowser config cat](filebrowser-config-cat.md) - Prints the configuration
+* [filebrowser config export](filebrowser-config-export.md) - Export the configuration to a file
+* [filebrowser config import](filebrowser-config-import.md) - Import a configuration file
+* [filebrowser config init](filebrowser-config-init.md) - Initialize a new database
+* [filebrowser config set](filebrowser-config-set.md) - Updates the configuration
+
diff --git a/www/docs/cli/filebrowser-hash.md b/www/docs/cli/filebrowser-hash.md
new file mode 100644
index 00000000..9d480547
--- /dev/null
+++ b/www/docs/cli/filebrowser-hash.md
@@ -0,0 +1,29 @@
+# filebrowser hash
+
+Hashes a password
+
+## Synopsis
+
+Hashes a password using bcrypt algorithm.
+
+```
+filebrowser hash [flags]
+```
+
+## Options
+
+```
+ -h, --help help for hash
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser](filebrowser.md) - A stylish web-based file browser
+
diff --git a/www/docs/cli/filebrowser-rules-add.md b/www/docs/cli/filebrowser-rules-add.md
new file mode 100644
index 00000000..2fd2efaf
--- /dev/null
+++ b/www/docs/cli/filebrowser-rules-add.md
@@ -0,0 +1,33 @@
+# filebrowser rules add
+
+Add a global rule or user rule
+
+## Synopsis
+
+Add a global rule or user rule.
+
+```
+filebrowser rules add [flags]
+```
+
+## Options
+
+```
+ -a, --allow indicates this is an allow rule
+ -h, --help help for add
+ -r, --regex indicates this is a regex rule
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+ -i, --id uint id of user to which the rules apply
+ -u, --username string username of user to which the rules apply
+```
+
+## See Also
+
+* [filebrowser rules](filebrowser-rules.md) - Rules management utility
+
diff --git a/www/docs/cli/filebrowser-rules-ls.md b/www/docs/cli/filebrowser-rules-ls.md
new file mode 100644
index 00000000..8a9cc6eb
--- /dev/null
+++ b/www/docs/cli/filebrowser-rules-ls.md
@@ -0,0 +1,31 @@
+# filebrowser rules ls
+
+List global rules or user specific rules
+
+## Synopsis
+
+List global rules or user specific rules.
+
+```
+filebrowser rules ls [flags]
+```
+
+## Options
+
+```
+ -h, --help help for ls
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+ -i, --id uint id of user to which the rules apply
+ -u, --username string username of user to which the rules apply
+```
+
+## See Also
+
+* [filebrowser rules](filebrowser-rules.md) - Rules management utility
+
diff --git a/www/docs/cli/filebrowser-rules-rm.md b/www/docs/cli/filebrowser-rules-rm.md
new file mode 100644
index 00000000..1870afbb
--- /dev/null
+++ b/www/docs/cli/filebrowser-rules-rm.md
@@ -0,0 +1,40 @@
+# filebrowser rules rm
+
+Remove a global rule or user rule
+
+## Synopsis
+
+Remove a global rule or user rule. The provided index
+is the same that's printed when you run 'rules ls'. Note
+that after each removal/addition, the index of the
+commands change. So be careful when removing them after each
+other.
+
+You can also specify an optional parameter (index_end) so
+you can remove all commands from 'index' to 'index_end',
+including 'index_end'.
+
+```
+filebrowser rules rm [index_end] [flags]
+```
+
+## Options
+
+```
+ -h, --help help for rm
+ --index uint index of rule to remove
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+ -i, --id uint id of user to which the rules apply
+ -u, --username string username of user to which the rules apply
+```
+
+## See Also
+
+* [filebrowser rules](filebrowser-rules.md) - Rules management utility
+
diff --git a/www/docs/cli/filebrowser-rules.md b/www/docs/cli/filebrowser-rules.md
new file mode 100644
index 00000000..cc8386d0
--- /dev/null
+++ b/www/docs/cli/filebrowser-rules.md
@@ -0,0 +1,34 @@
+# filebrowser rules
+
+Rules management utility
+
+## Synopsis
+
+On each subcommand you'll have available at least two flags:
+"username" and "id". You must either set only one of them
+or none. If you set one of them, the command will apply to
+an user, otherwise it will be applied to the global set or
+rules.
+
+## Options
+
+```
+ -h, --help help for rules
+ -i, --id uint id of user to which the rules apply
+ -u, --username string username of user to which the rules apply
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser](filebrowser.md) - A stylish web-based file browser
+* [filebrowser rules add](filebrowser-rules-add.md) - Add a global rule or user rule
+* [filebrowser rules ls](filebrowser-rules-ls.md) - List global rules or user specific rules
+* [filebrowser rules rm](filebrowser-rules-rm.md) - Remove a global rule or user rule
+
diff --git a/www/docs/cli/filebrowser-users-add.md b/www/docs/cli/filebrowser-users-add.md
new file mode 100644
index 00000000..1de95e4d
--- /dev/null
+++ b/www/docs/cli/filebrowser-users-add.md
@@ -0,0 +1,49 @@
+# filebrowser users add
+
+Create a new user
+
+## Synopsis
+
+Create a new user and add it to the database.
+
+```
+filebrowser users add [flags]
+```
+
+## Options
+
+```
+ --aceEditorTheme string ace editor's syntax highlighting theme for users
+ --commands strings a list of the commands a user can execute
+ --dateFormat use date format (true for absolute time, false for relative)
+ -h, --help help for add
+ --hideDotfiles hide dotfiles
+ --locale string locale for users (default "en")
+ --lockPassword lock password
+ --perm.admin admin perm for users
+ --perm.create create perm for users (default true)
+ --perm.delete delete perm for users (default true)
+ --perm.download download perm for users (default true)
+ --perm.execute execute perm for users (default true)
+ --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
+ --sorting.by string sorting mode (name, size or modified) (default "name")
+ --viewMode string view mode for users (default "list")
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser users](filebrowser-users.md) - Users management utility
+
diff --git a/www/docs/cli/filebrowser-users-export.md b/www/docs/cli/filebrowser-users-export.md
new file mode 100644
index 00000000..e513a453
--- /dev/null
+++ b/www/docs/cli/filebrowser-users-export.md
@@ -0,0 +1,30 @@
+# filebrowser users export
+
+Export all users to a file.
+
+## Synopsis
+
+Export all users to a json or yaml file. Please indicate the
+path to the file where you want to write the users.
+
+```
+filebrowser users export [flags]
+```
+
+## Options
+
+```
+ -h, --help help for export
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser users](filebrowser-users.md) - Users management utility
+
diff --git a/www/docs/cli/filebrowser-users-find.md b/www/docs/cli/filebrowser-users-find.md
new file mode 100644
index 00000000..19ed3924
--- /dev/null
+++ b/www/docs/cli/filebrowser-users-find.md
@@ -0,0 +1,29 @@
+# filebrowser users find
+
+Find a user by username or id
+
+## Synopsis
+
+Find a user by username or id. If no flag is set, all users will be printed.
+
+```
+filebrowser users find [flags]
+```
+
+## Options
+
+```
+ -h, --help help for find
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser users](filebrowser-users.md) - Users management utility
+
diff --git a/www/docs/cli/filebrowser-users-import.md b/www/docs/cli/filebrowser-users-import.md
new file mode 100644
index 00000000..efc185fc
--- /dev/null
+++ b/www/docs/cli/filebrowser-users-import.md
@@ -0,0 +1,34 @@
+# filebrowser users import
+
+Import users from a file
+
+## Synopsis
+
+Import users from a file. The path must be for a json or yaml
+file. You can use this command to import new users to your
+installation. For that, just don't place their ID on the files
+list or set it to 0.
+
+```
+filebrowser users import [flags]
+```
+
+## Options
+
+```
+ -h, --help help for import
+ --overwrite overwrite users with the same id/username combo
+ --replace replace the entire user base
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser users](filebrowser-users.md) - Users management utility
+
diff --git a/www/docs/cli/filebrowser-users-ls.md b/www/docs/cli/filebrowser-users-ls.md
new file mode 100644
index 00000000..59a4f8fe
--- /dev/null
+++ b/www/docs/cli/filebrowser-users-ls.md
@@ -0,0 +1,25 @@
+# filebrowser users ls
+
+List all users.
+
+```
+filebrowser users ls [flags]
+```
+
+## Options
+
+```
+ -h, --help help for ls
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser users](filebrowser-users.md) - Users management utility
+
diff --git a/www/docs/cli/filebrowser-users-rm.md b/www/docs/cli/filebrowser-users-rm.md
new file mode 100644
index 00000000..0e3b3035
--- /dev/null
+++ b/www/docs/cli/filebrowser-users-rm.md
@@ -0,0 +1,29 @@
+# filebrowser users rm
+
+Delete a user by username or id
+
+## Synopsis
+
+Delete a user by username or id
+
+```
+filebrowser users rm [flags]
+```
+
+## Options
+
+```
+ -h, --help help for rm
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser users](filebrowser-users.md) - Users management utility
+
diff --git a/www/docs/cli/filebrowser-users-update.md b/www/docs/cli/filebrowser-users-update.md
new file mode 100644
index 00000000..4ba7d608
--- /dev/null
+++ b/www/docs/cli/filebrowser-users-update.md
@@ -0,0 +1,52 @@
+# filebrowser users update
+
+Updates an existing user
+
+## Synopsis
+
+Updates an existing user. Set the flags for the
+options you want to change.
+
+```
+filebrowser users update [flags]
+```
+
+## Options
+
+```
+ --aceEditorTheme string ace editor's syntax highlighting theme for users
+ --commands strings a list of the commands a user can execute
+ --dateFormat use date format (true for absolute time, false for relative)
+ -h, --help help for update
+ --hideDotfiles hide dotfiles
+ --locale string locale for users (default "en")
+ --lockPassword lock password
+ -p, --password string new password
+ --perm.admin admin perm for users
+ --perm.create create perm for users (default true)
+ --perm.delete delete perm for users (default true)
+ --perm.download download perm for users (default true)
+ --perm.execute execute perm for users (default true)
+ --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
+ --sorting.by string sorting mode (name, size or modified) (default "name")
+ -u, --username string new username
+ --viewMode string view mode for users (default "list")
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser users](filebrowser-users.md) - Users management utility
+
diff --git a/www/docs/cli/filebrowser-users.md b/www/docs/cli/filebrowser-users.md
new file mode 100644
index 00000000..63d82bc1
--- /dev/null
+++ b/www/docs/cli/filebrowser-users.md
@@ -0,0 +1,32 @@
+# filebrowser users
+
+Users management utility
+
+## Synopsis
+
+Users management utility.
+
+## Options
+
+```
+ -h, --help help for users
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser](filebrowser.md) - A stylish web-based file browser
+* [filebrowser users add](filebrowser-users-add.md) - Create a new user
+* [filebrowser users export](filebrowser-users-export.md) - Export all users to a file.
+* [filebrowser users find](filebrowser-users-find.md) - Find a user by username or id
+* [filebrowser users import](filebrowser-users-import.md) - Import users from a file
+* [filebrowser users ls](filebrowser-users-ls.md) - List all users.
+* [filebrowser users rm](filebrowser-users-rm.md) - Delete a user by username or id
+* [filebrowser users update](filebrowser-users-update.md) - Updates an existing user
+
diff --git a/www/docs/cli/filebrowser-version.md b/www/docs/cli/filebrowser-version.md
new file mode 100644
index 00000000..ec3596c5
--- /dev/null
+++ b/www/docs/cli/filebrowser-version.md
@@ -0,0 +1,25 @@
+# filebrowser version
+
+Print the version number
+
+```
+filebrowser version [flags]
+```
+
+## Options
+
+```
+ -h, --help help for version
+```
+
+## Options inherited from parent commands
+
+```
+ -c, --config string config file path
+ -d, --database string database path (default "./filebrowser.db")
+```
+
+## See Also
+
+* [filebrowser](filebrowser.md) - A stylish web-based file browser
+
diff --git a/www/docs/cli/filebrowser.md b/www/docs/cli/filebrowser.md
new file mode 100644
index 00000000..5d18e8d2
--- /dev/null
+++ b/www/docs/cli/filebrowser.md
@@ -0,0 +1,87 @@
+# filebrowser
+
+A stylish web-based file browser
+
+## Synopsis
+
+File Browser CLI lets you create the database to use with File Browser,
+manage your users and all the configurations without accessing the
+web interface.
+
+If you've never run File Browser, you'll need to have a database for
+it. Don't worry: you don't need to setup a separate database server.
+We're using Bolt DB which is a single file database and all managed
+by ourselves.
+
+For this command, all flags are available as environmental variables,
+except for "--config", which specifies the configuration file to use.
+The environment variables are prefixed by "FB_" followed by the flag name in
+UPPER_SNAKE_CASE. For example, the flag "--disablePreviewResize" is available
+as FB_DISABLE_PREVIEW_RESIZE.
+
+If "--config" is not specified, File Browser will look for a configuration
+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
+- Environment variables
+- Configuration file
+- Database values
+- Defaults
+
+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".
+
+```
+filebrowser [flags]
+```
+
+## Options
+
+```
+ -a, --address string address to listen on (default "127.0.0.1")
+ -b, --baseURL string base url
+ --cacheDir string file cache directory (disabled if empty)
+ -t, --cert string tls certificate
+ -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
+ -h, --help help for filebrowser
+ --imageProcessors int image processors count (default 4)
+ -k, --key string tls key
+ -l, --log string log output (default "stdout")
+ --noauth use the noauth auther when using quick setup
+ --password string hashed password for the first user when using quick setup
+ -p, --port string port to listen on (default "8080")
+ -r, --root string root to prepend to relative paths (default ".")
+ --socket string socket to listen to (cannot be used with address, port, cert nor key flags)
+ --socketPerm uint32 unix socket file permissions (default 438)
+ --tokenExpirationTime string user session timeout (default "2h")
+ --username string username for the first user when using quick setup (default "admin")
+```
+
+## See Also
+
+* [filebrowser cmds](filebrowser-cmds.md) - Command runner management utility
+* [filebrowser completion](filebrowser-completion.md) - Generate the autocompletion script for the specified shell
+* [filebrowser config](filebrowser-config.md) - Configuration management utility
+* [filebrowser hash](filebrowser-hash.md) - Hashes a password
+* [filebrowser rules](filebrowser-rules.md) - Rules management utility
+* [filebrowser users](filebrowser-users.md) - Users management utility
+* [filebrowser version](filebrowser-version.md) - Print the version number
+
diff --git a/www/docs/command-execution.md b/www/docs/command-execution.md
new file mode 100644
index 00000000..454c67b6
--- /dev/null
+++ b/www/docs/command-execution.md
@@ -0,0 +1,54 @@
+# Command Execution
+
+> [!CAUTION]
+>
+> The **hook runner** and **interactive shell** functionalities have been disabled for all existent and new installations by default from version v2.33.8 and onwards, due to continuous and known security vulnerabilities. You should only use this feature if you are aware of all of the security risks involved. For more up to date information, consult issue [#5199](https://github.com/filebrowser/filebrowser/issues/5199).
+
+## Hook Runner
+
+The hook runner is a feature that enables you to execute any shell command you want before or after a certain event. Right now, these are the events:
+
+* Copy
+* Rename
+* Upload
+* Delete
+* Save
+
+Also, during the execution of the commands set for those hooks, there will be some environment variables available to help you perform your commands:
+
+* `FILE` with the full absolute path to the changed file.
+* `SCOPE` with the path to user's scope.
+* `TRIGGER` with the name of the event.
+* `USERNAME` with the user's username.
+* `DESTINATION` with the absolute path to the destination. Only used for **copy** and **rename.**
+
+At this moment, you can edit the commands via the command line interface, using the following commands \(please check the flag `--help` to know more about them\):
+
+```bash
+filebrowser cmds add before_copy "echo $FILE"
+filebrowser cmds rm before_copy 0
+filebrowser cmds ls
+```
+
+Or you can use the web interface to manage them via **Settings** → **Global Settings**.
+
+## Interactive Shell
+
+Within File Browser you can toggle the shell (`< >` icon at the top right) and this will open a shell command window at the bottom of the screen. This functionality can be turned on using the environment variable `FB_DISABLE_EXEC=false` or the flag `--disable-exec=false`.
+
+By default no commands are available as the command list is empty. To enable commands these need to either be done on a per-user basis (including for the Admin user).
+
+You can do this by adding them in Settings > User Management > (edit user) > Commands or to *apply to all new users created from that point forward* they can be set in Settings > Global Settings
+
+> [!NOTE]
+>
+> If using a proxy manager then remember to enable websockets support for the File Browser proxy
+
+> [!NOTE]
+>
+> If using Docker and you want to add a new command that is not in the base image then you will need to build a custom Docker image using `filebrowser/filebrowser` as a base image. For example to add 7z:
+>
+> ```docker
+> FROM filebrowser/filebrowser
+> RUN sudo apt install p7zip-full
+> ```
diff --git a/www/docs/configuration.md b/www/docs/configuration.md
deleted file mode 100644
index 157a54e8..00000000
--- a/www/docs/configuration.md
+++ /dev/null
@@ -1,158 +0,0 @@
-# Configuration
-
-Most of the configuration can be understood through the command line interface documentation. To access it, you need to install File Browser and run `filebrowser --help`. In this page, we cover some specific, more complex, topics.
-
-## Flags as Environment Variables
-
-In some situations, it is easier to use environment variables instead of flags. For example, if you're using our provided Docker image, it's easier for you to use environment variables to customize the settings instead of flags.
-
-All flags should be available as environment variables prefixed with `FB_`. For example, the flag `--disable-thumbnails` is available as `FB_DISABLE_THUMBNAILS`.
-
-## Custom Branding
-
-You can customize File Browser to use your own branding. This includes the following:
-
-- **Name**: the name of the instance that shows up on the tab title, login pages, and some other places.
-- **Disable External Links**: disables all external links, except to the documentation.
-- **Disable Used Percentage**: disables the disk usage information on the sidebar.
-- **Branding Folder**: directory which can contain two items:
- - `custom.css`, containing a global stylesheet to apply to all users.
- - `img`, a directory which can replace all the [default logotypes](https://github.com/filebrowser/filebrowser/tree/master/frontend/public/img) from the application.
-
-This can be configured by the administrator user, under **Settings → Global Settings**. You can also update the configuration directly using the CLI:
-
-```sh
-filebrowser config set --branding.name "My Name" \
- --branding.files "/abs/path/to/my/dir" \
- --branding.disableExternal
-```
-
-> [!NOTE]
->
-> If you are using Docker, you need to mount a volume with the `branding` directory in order for it to be accessible from within the container.
-
-### Custom Icons
-
-To replace the default logotype and favicons, you need to create an `img` directory under the branding directory. The structure of this directory must mimic the one from the [default logotypes](https://github.com/filebrowser/filebrowser/tree/master/frontend/public/img):
-
-```
-img/
- logo.svg
- icons/
- favicon.ico
- favicon.svg
- (...)
-```
-
-Note that there are different versions of the same favicon in multiple sizes. To replace all of them, you need to add versions for all of them. You can use the [Real Favicon Generator](https://realfavicongenerator.net/) to generate these for you from your base image.
-
-> [!NOTE]
->
-> The icons are cached by the browser, so you may not see your changes immediately. You can address this by clearing your browser's cache.
-
-## Authentication Method
-
-Right now, there are three possible authentication methods. Each one of them has its own capabilities and specification. If you are interested in contributing with one more authentication method, please [check the guidelines](contributing.md).
-
-### JSON Auth (default)
-
-We call it JSON Authentication but it is just the default authentication method and the one that is provided by default if you don't make any changes. It is set by default, but if you've made changes before you can revert to using JSON auth:
-
-```sh
-filebrowser config set --auth.method=json
-```
-
-This method can also be extended with **reCAPTCHA** verification during login:
-
-```sh
-filebrowser config set --auth.method=json \
- --recaptcha.key site-key \
- --recaptcha.secret private-key
-```
-
-By default, we use [Google's reCAPTCHA](https://developers.google.com/recaptcha/docs/display) service. If you live in China, or want to use other provider, you can change the host with the following command:
-
-```sh
-filebrowser config set --recaptcha.host https://recaptcha.net
-```
-
-Where `https://recaptcha.net` is any provider you want.
-
-### Proxy Header
-
-If you have a reverse proxy you want to use to login your users, you do it via our `proxy` authentication method. To configure this method, your proxy must send an HTTP header containing the username of the logged in user:
-
-```sh
-filebrowser config set --auth.method=proxy --auth.header=X-My-Header
-```
-
-Where `X-My-Header` is the HTTP header provided by your proxy with the username.
-
-> [!WARNING]
->
-> File Browser will blindly trust the provided header. If the proxy can be bypassed, an attacker could simply attach the header and get admin access.
-
-### No Authentication
-
-We also provide a no authentication mechanism for users that want to use File Browser privately such in a home network. By setting this authentication method, the user with **id 1** will be used as the default users. Creating more users won't have any effect.
-
-```sh
-filebrowser config set --auth.method=noauth
-```
-
-## Command Runner
-
-> [!CAUTION]
->
-> The **command execution** functionality has been disabled for all existent and new installations by default from version v2.33.8 and onwards, due to continuous and known security vulnerabilities. You should only use this feature if you are aware of all of the security risks involved. For more up to date information, consult issue [#5199](https://github.com/filebrowser/filebrowser/issues/5199).
-
-The command runner is a feature that enables you to execute any shell command you want before or after a certain event. Right now, these are the events:
-
-* Copy
-* Rename
-* Upload
-* Delete
-* Save
-
-Also, during the execution of the commands set for those hooks, there will be some environment variables available to help you perform your commands:
-
-* `FILE` with the full absolute path to the changed file.
-* `SCOPE` with the path to user's scope.
-* `TRIGGER` with the name of the event.
-* `USERNAME` with the user's username.
-* `DESTINATION` with the absolute path to the destination. Only used for **copy** and **rename.**
-
-At this moment, you can edit the commands via the command line interface, using the following commands \(please check the flag `--help` to know more about them\):
-
-```bash
-filebrowser cmds add before_copy "echo $FILE"
-filebrowser cmds rm before_copy 0
-filebrowser cmds ls
-```
-
-Or you can use the web interface to manage them via **Settings** → **Global Settings**.
-
-## Command Execution
-
-> [!CAUTION]
->
-> The **command execution** functionality has been disabled for all existent and new installations by default from version v2.33.8 and onwards, due to continuous and known security vulnerabilities. You should only use this feature if you are aware of all of the security risks involved. For more up to date information, consult issue [#5199](https://github.com/filebrowser/filebrowser/issues/5199).
-
-Within File Browser you can toggle the shell (`< >` icon at the top right) and this will open a shell command window at the bottom of the screen. This functionality can be turned on using the environment variable `FB_DISABLE_EXEC=false` or the flag `--disable-exec=false`.
-
-By default no commands are available as the command list is empty. To enable commands these need to either be done on a per-user basis (including for the Admin user).
-
-You can do this by adding them in Settings > User Management > (edit user) > Commands or to *apply to all new users created from that point forward* they can be set in Settings > Global Settings
-
-> [!NOTE]
->
-> If using a proxy manager then remember to enable websockets support for the File Browser proxy
-
-> [!NOTE]
->
-> If using Docker and you want to add a new command that is not in the base image then you will need to build a custom Docker image using `filebrowser/filebrowser` as a base image. For example to add 7z:
->
-> ```docker
-> FROM filebrowser/filebrowser
-> RUN sudo apt install p7zip-full
-> ```
diff --git a/www/docs/customization.md b/www/docs/customization.md
new file mode 100644
index 00000000..054da7ab
--- /dev/null
+++ b/www/docs/customization.md
@@ -0,0 +1,45 @@
+# Customization
+
+You can customize the styles, branding and icons of your File Browser instance in order to give it a personal touch.
+
+## Custom Branding
+
+You can customize File Browser to use your own branding. This includes the following:
+
+- **Name**: the name of the instance that shows up on the tab title, login pages, and some other places.
+- **Disable External Links**: disables all external links, except to the documentation.
+- **Disable Used Percentage**: disables the disk usage information on the sidebar.
+- **Branding Folder**: directory which can contain two items:
+ - `custom.css`, containing a global stylesheet to apply to all users.
+ - `img`, a directory which can replace all the [default logotypes](https://github.com/filebrowser/filebrowser/tree/master/frontend/public/img) from the application.
+
+This can be configured by the administrator user, under **Settings → Global Settings**. You can also update the configuration directly using the [CLI](cli/filebrowser-config-set.md):
+
+```sh
+filebrowser config set --branding.name "My Name" \
+ --branding.files "/abs/path/to/my/dir" \
+ --branding.disableExternal
+```
+
+> [!NOTE]
+>
+> If you are using Docker, you need to mount a volume with the `branding` directory in order for it to be accessible from within the container.
+
+### Custom Icons
+
+To replace the default logotype and favicons, you need to create an `img` directory under the branding directory. The structure of this directory must mimic the one from the [default logotypes](https://github.com/filebrowser/filebrowser/tree/master/frontend/public/img):
+
+```
+img/
+ logo.svg
+ icons/
+ favicon.ico
+ favicon.svg
+ (...)
+```
+
+Note that there are different versions of the same favicon in multiple sizes. To replace all of them, you need to add versions for all of them. You can use the [Real Favicon Generator](https://realfavicongenerator.net/) to generate these for you from your base image.
+
+> [!NOTE]
+>
+> The icons are cached by the browser, so you may not see your changes immediately. You can address this by clearing your browser's cache.
diff --git a/www/docs/installation.md b/www/docs/installation.md
index 741e8c1a..6acb8db6 100644
--- a/www/docs/installation.md
+++ b/www/docs/installation.md
@@ -88,6 +88,6 @@ Your instance is now up and running. File Browser will automatically bootstrap a
>
> The automatically generated password for the user `admin` is only displayed once. If you fail to remember it, you will need to manually delete the database and start File Browser again.
-Although this is the fastest way to bootstrap an instance, we recommend you to take a look at other possible options, by checking `config init --help` and `config set --help`, to make the installation as safe and customized as it can be.
+Although this is the fastest way to bootstrap an instance, we recommend you to take a look at other possible options, by checking [`config init`](cli/filebrowser-config-init.md) and [`config set`](cli/filebrowser-config-set.md), to make the installation as safe and customized as it can be.
If your goal is to have a public-facing deployment, we recommend taking a look at the [deployment](deployment.md) page for more information on how you can secure your installation.
diff --git a/www/docs/troubleshooting.md b/www/docs/troubleshooting.md
new file mode 100644
index 00000000..a994957e
--- /dev/null
+++ b/www/docs/troubleshooting.md
@@ -0,0 +1,9 @@
+# Troubleshooting
+
+## Session Timeout
+
+By default, user sessions expire after **2 hours**. If you're uploading large files over slower connections, you may need to increase this timeout to prevent sessions from expiring mid-upload. You can configure the session timeout using the `tokenExpirationTime` setting.
+
+You can either set this option during runtime by using the flag `--tokenExpirationTime`, the environment variable `FB_TOKEN_EXPIRATION_TIME`, or in your configuration file. If you want to persist this to the configuration, please use [`filebrowser config set`](cli/filebrowser-config-set.md).
+
+Valid duration formats include `"2h"`, `"30m"`, `"24h"`, or combinations like `"2h30m"`.
diff --git a/www/mkdocs.yml b/www/mkdocs.yml
index d489dcd5..c71c6798 100644
--- a/www/mkdocs.yml
+++ b/www/mkdocs.yml
@@ -34,8 +34,6 @@ theme:
features:
- navigation.tabs
- navigation.tabs.sticky
- - navigation.sections
- - navigation.expand
- navigation.indexes
- navigation.top
- navigation.instant
@@ -44,7 +42,6 @@ theme:
- search.share
- content.code.copy
- toc.follow
- - toc.integrate
icon:
repo: fontawesome/brands/github
@@ -97,10 +94,45 @@ extra:
nav:
- Home: index.md
- - Getting Started:
+ - Documentation:
- Installation: installation.md
- - Configuration: configuration.md
+ - Configuration:
+ - customization.md
+ - authentication.md
+ - command-execution.md
+ - Troubleshooting: troubleshooting.md
- Deployment: deployment.md
+ - Command Line Usage:
+ - cli/filebrowser.md
+ - cli/filebrowser-cmds.md
+ - cli/filebrowser-cmds-add.md
+ - cli/filebrowser-cmds-ls.md
+ - cli/filebrowser-cmds-rm.md
+ - cli/filebrowser-completion.md
+ - cli/filebrowser-completion-bash.md
+ - cli/filebrowser-completion-fish.md
+ - cli/filebrowser-completion-powershell.md
+ - cli/filebrowser-completion-zsh.md
+ - cli/filebrowser-config.md
+ - cli/filebrowser-config-cat.md
+ - cli/filebrowser-config-export.md
+ - cli/filebrowser-config-import.md
+ - cli/filebrowser-config-init.md
+ - cli/filebrowser-config-set.md
+ - cli/filebrowser-hash.md
+ - cli/filebrowser-rules.md
+ - cli/filebrowser-rules-add.md
+ - cli/filebrowser-rules-ls.md
+ - cli/filebrowser-rules-rm.md
+ - cli/filebrowser-users.md
+ - cli/filebrowser-users-add.md
+ - cli/filebrowser-users-export.md
+ - cli/filebrowser-users-find.md
+ - cli/filebrowser-users-import.md
+ - cli/filebrowser-users-ls.md
+ - cli/filebrowser-users-rm.md
+ - cli/filebrowser-users-update.md
+ - cli/filebrowser-version.md
- Contributing:
- Contributing: contributing.md
- Code of Conduct: code-of-conduct.md