diff --git a/.dockerignore b/.dockerignore index c8e50a27..f7796e79 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,7 @@ -* -!docker/* -!healthcheck.sh -!docker_config.json -!filebrowser \ No newline at end of file +.venv +dist +.idea +frontend/node_modules +frontend/dist +filebrowser.db +docs/index.md \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c6937227..827fbfd1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,4 +2,4 @@ # Unless a later match takes precedence, @o1egl will be requested for # review when someone opens a pull request. -* @o1egl \ No newline at end of file +* @o1egl @hacdias diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index f026f3ce..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve ---- - -**Description** - - -**Expected behaviour** - - -**What is happening instead?** - - -**Additional context** - - -**How to reproduce?** - - -**Files** - diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..9c02689f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,53 @@ +name: Bug Report +description: Report a bug in FileBrowser. +labels: [bug, triage] +body: + - type: checkboxes + attributes: + label: Checklist + description: Please verify that you've followed these steps + options: + - label: This is a bug report, not a question. + required: true + - label: I have searched on the [issue tracker](https://github.com/filebrowser/filebrowser/issues?q=is%3Aissue) for my bug. + required: true + - label: I am running the latest [FileBrowser version](https://github.com/filebrowser/filebrowser/releases) or have an issue updating. + required: true + - type: textarea + id: version + attributes: + label: Version + render: Text + description: | + Enter the version of FileBrowser you are using. + validations: + required: true + - type: textarea + attributes: + label: Description + description: | + A clear and concise description of what the issue is about. What are you trying to do? + validations: + required: true + - type: textarea + attributes: + label: What did you expect to happen? + validations: + required: true + - type: textarea + attributes: + label: What actually happened? + validations: + required: true + - type: textarea + attributes: + label: Reproduction Steps + description: | + Tell us how to reproduce this issue. How can someone who is starting from scratch reproduce this behavior as minimally as possible? + validations: + required: true + - type: textarea + attributes: + label: Files + description: | + A list of relevant files for this issue. Large files can be uploaded one-by-one or in a tarball/zipfile. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..b712a879 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: GitHub Discussions + url: https://github.com/filebrowser/filebrowser/discussions + about: Please ask questions and discuss features here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 4bdb0367..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project ---- - -**Is your feature request related to a problem? Please describe.** - - -**Describe the solution you'd like** - - -**Describe alternatives you've considered** - - -**Additional context** - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 781aa085..a8efcb42 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,19 +1,16 @@ -**Description** - +## Description -:rotating_light: Before submitting your PR, please indicate which issues are either fixed or closed by this PR. See [GitHub Help: Closing issues using keywords](https://help.github.com/articles/closing-issues-via-commit-messages/). + -- [ ] DO make sure you are requesting to **pull a topic/feature/bugfix branch** (right side). Don't request your master! -- [ ] DO make sure you are making a pull request against the **master branch** (left side). Also you should start *your branch* off *our master*. -- [ ] DO make sure that File Browser can be successfully built. See [builds](https://github.com/filebrowser/community/blob/master/builds.md) and [development](https://github.com/filebrowser/community/blob/master/development.md). -- [ ] AVOID breaking the continuous integration build. +## Additional Information -**Further comments** - -:heart: Thank you! ---> +## Checklist + +Before submitting your PR, please indicate which issues are either fixed or closed by this PR. See [GitHub Help: Closing issues using keywords](https://help.github.com/articles/closing-issues-via-commit-messages/). + +- [ ] I am aware the project is currently in maintenance-only mode. See [README](https://github.com/filebrowser/community/blob/master/README.md) +- [ ] I am aware that translations MUST be made through [Transifex](https://app.transifex.com/file-browser/file-browser/) and that this PR is NOT a translation update +- [ ] I am making a PR against the `master` branch. +- [ ] I am sure File Browser can be successfully built. See [builds](https://github.com/filebrowser/community/blob/master/builds.md) and [development](https://github.com/filebrowser/community/blob/master/development.md). diff --git a/.github/workflows/site-pr.yml b/.github/workflows/site-pr.yml new file mode 100644 index 00000000..c3075c93 --- /dev/null +++ b/.github/workflows/site-pr.yml @@ -0,0 +1,20 @@ +name: Build Site + +on: + pull_request: + paths: + - 'www' + - '*.md' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Build site + run: make site diff --git a/.github/workflows/site-publish.yml b/.github/workflows/site-publish.yml new file mode 100644 index 00000000..e3618a99 --- /dev/null +++ b/.github/workflows/site-publish.yml @@ -0,0 +1,32 @@ +name: Build and Deploy Site + +on: + push: + branches: + - master + +jobs: + deploy: + permissions: + contents: read + deployments: write + pull-requests: write + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Build site + run: make site + + - name: Deploy to Cloudflare Pages + uses: cloudflare/wrangler-action@v3 + 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 }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml deleted file mode 100644 index 45a7e9e5..00000000 --- a/.github/workflows/stale.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: 'Close stale issues and PRs' -permissions: - issues: write - pull-requests: write - -on: - schedule: - - cron: '30 1 * * *' - -jobs: - stale: - runs-on: ubuntu-latest - steps: - - uses: actions/stale@v9 - with: - stale-pr-message: 'This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' - close-pr-message: 'This PR was closed because it has been stalled for 5 days with no activity.' - stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' - close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.' - days-before-stale: 30 - days-before-close: 5 - exempt-issue-labels: 'feature ☘,enhancement ⚙,bug 🐞' - exempt-pr-labels: 'need-help,wip' - operations-per-run: 100 \ No newline at end of file diff --git a/.gitignore b/.gitignore index f229b066..47f34732 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ rice-box.go /filebrowser /filebrowser.exe /dist +.venv .DS_Store node_modules diff --git a/.golangci.yml b/.golangci.yml index 0fa292ed..901a89b8 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,121 +1,132 @@ -linters-settings: - dupl: - threshold: 100 - exhaustive: - default-signifies-exhaustive: false - funlen: - lines: 100 - statements: 50 - goconst: - min-len: 2 - min-occurrences: 2 - gocritic: - enabled-tags: - - diagnostic - - experimental - - opinionated - - performance - - style - disabled-checks: - - dupImport # https://github.com/go-critic/go-critic/issues/845 - - ifElseChain - - octalLiteral - - whyNoLint - - wrapperFunc - gocyclo: - min-complexity: 15 - goimports: - local-prefixes: github.com/filebrowser/filebrowser - gomnd: - # don't include the "operation" and "assign" - checks: - - argument - - case - - condition - - return - ignored-numbers: - - '0' - - '1' - - '2' - - '3' - ignored-functions: - - strings.SplitN - govet: - enable: - - nilness - - shadow - lll: - line-length: 140 - misspell: - locale: US - 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 +version: "2" linters: - # please, do not use `enable-all`: it's deprecated and will be removed soon. - # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint - disable-all: true + # inverted configuration with `default: all` and `disable` is not scalable during updates of golangci-lint + default: none enable: - bodyclose - dogsled - dupl - errcheck - errorlint - - exportloopref - exhaustive - funlen - gocheckcompilerdirectives - gochecknoinits - - goconst - gocritic - gocyclo - godox - - goimports - - gomnd - goprintffuncname - gosec - - gosimple - govet - ineffassign - lll - misspell + - mnd - nakedret - nolintlint - prealloc - revive - rowserrcheck - staticcheck - - stylecheck - testifylint - - typecheck - 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/ -issues: - exclude-dirs: - - frontend/ - exclude-rules: - - path: cmd/.*.go - linters: - - gochecknoinits - - path: .*_test.go - linters: - - lll - - gochecknoinits - - gocyclo - - funlen - - dupl - - scopelint - - text: "Auther" - linters: - - misspell - - text: "strconv.Parse" - linters: - - gomnd - -run: - timeout: 5m \ No newline at end of file +formatters: + enable: + - goimports + settings: + goimports: + local-prefixes: + - github.com/filebrowser/filebrowser + exclusions: + generated: lax + paths: + - frontend/ diff --git a/.goreleaser.yml b/.goreleaser.yml index f5715c5c..89324e5f 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -14,7 +14,7 @@ builds: goarm: [5, 6, 7] ignore: - goos: darwin - goarch: 386 + goarch: "386" - goos: freebsd goarch: arm archives: @@ -68,7 +68,7 @@ dockers: - --platform=linux/arm/v6 goos: linux goarch: arm - goarm: '6' + goarm: "6" image_templates: - git.linuxcrack.zip/midou/filebrowser-fork:{{ .Tag }}-armv6 - git.linuxcrack.zip/midou/filebrowser-fork:v{{ .Major }}-armv6 @@ -85,7 +85,7 @@ dockers: - --platform=linux/arm/v7 goos: linux goarch: arm - goarm: '7' + goarm: "7" image_templates: - git.linuxcrack.zip/midou/filebrowser-fork:{{ .Tag }}-armv7 - git.linuxcrack.zip/midou/filebrowser-fork:v{{ .Major }}-armv7 diff --git a/.tx/config b/.tx/config deleted file mode 100644 index f7363d04..00000000 --- a/.tx/config +++ /dev/null @@ -1,10 +0,0 @@ -[main] -host = https://www.transifex.com -lang_map = pt_BR: pt-br, zh_CN: zh-cn, zh_HK: zh-hk, zh_TW: zh-tw, nl_BE: nl-be, sv_SE: sv-se, cz-CS: cz_cs - -[file-browser.file-browser] -file_filter = frontend/src/i18n/.json -minimum_perc = 50 -source_file = frontend/src/i18n/en.json -source_lang = en -type = KEYVALUEJSON diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d414dcb..c804baeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,214 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.36.3](https://github.com/filebrowser/filebrowser/compare/v2.36.2...v2.36.3) (2025-07-06) + + +### Bug Fixes + +* log error if branding file exists but cannot be loaded ([3645b57](https://github.com/filebrowser/filebrowser/commit/3645b578cddb9fc8f25a00e0153fb600ad1b9266)) + +### [2.36.2](https://github.com/filebrowser/filebrowser/compare/v2.36.1...v2.36.2) (2025-07-06) + + +### Bug Fixes + +* lookup directory name if blank when downloading shared directory ([046d619](https://github.com/filebrowser/filebrowser/commit/046d6193c57b4df0e3dc583b6518b43d29d302c9)) + +### [2.36.1](https://github.com/filebrowser/filebrowser/compare/v2.36.0...v2.36.1) (2025-07-03) + + +### Bug Fixes + +* remove associated shares when deleting file/folder ([e99e0b3](https://github.com/filebrowser/filebrowser/commit/e99e0b3028e1c8a50e1744bb07ecc8e809bdb8e6)) + +## [2.36.0](https://github.com/filebrowser/filebrowser/compare/v2.35.0...v2.36.0) (2025-07-02) + + +### Features + +* update icons, remove deprecated Microsoft Tiles ([04166e8](https://github.com/filebrowser/filebrowser/commit/04166e81e52d38b1f66ba3313ccb1291c239eea2)) + +## [2.35.0](https://github.com/filebrowser/filebrowser/compare/v2.34.2...v2.35.0) (2025-06-30) + + +### Features + +* Long press selects item in single click mode ([8d75220](https://github.com/filebrowser/filebrowser/commit/8d7522049ced83f28f0933b55772c32e3ad04627)) + + +### Bug Fixes + +* shell value must be joined by blank space ([4403cd3](https://github.com/filebrowser/filebrowser/commit/4403cd35720dbda5a8bb1013b92582accf3317bc)) +* update documentation links ([38d0366](https://github.com/filebrowser/filebrowser/commit/38d0366acf88352b5a9a97c45837b0f865efae0b)) + +### [2.34.2](https://github.com/filebrowser/filebrowser/compare/v2.34.1...v2.34.2) (2025-06-29) + + +### Bug Fixes + +* mitigate unprotected shares ([2b5d6cb](https://github.com/filebrowser/filebrowser/commit/2b5d6cbb996a61a769acc56af0acc12eec2d8d8f)) + +### [2.34.1](https://github.com/filebrowser/filebrowser/compare/v2.34.0...v2.34.1) (2025-06-29) + + +### Bug Fixes + +* exclude to-be-moved folder from move dialog ([#5235](https://github.com/filebrowser/filebrowser/issues/5235)) ([7354eb6](https://github.com/filebrowser/filebrowser/commit/7354eb6cf966244141277c2808988855c004f908)) +* passthrough the minimum password length ([#5236](https://github.com/filebrowser/filebrowser/issues/5236)) ([bf37f88](https://github.com/filebrowser/filebrowser/commit/bf37f88c32222ad9c186482bb97338a9c9b4a93c)) + +## [2.34.0](https://github.com/filebrowser/filebrowser/compare/v2.33.10...v2.34.0) (2025-06-29) + + +### Features + +* Translate frontend/src/i18n/en.json in fa ([0acd69c](https://github.com/filebrowser/filebrowser/commit/0acd69c537ce2909ff62c4bb6980982524ece221)) +* Translate frontend/src/i18n/en.json in fa ([#5233](https://github.com/filebrowser/filebrowser/issues/5233)) ([09f679f](https://github.com/filebrowser/filebrowser/commit/09f679fae43398f5b87d21acc9d974d4d053392f)) +* update translations for project File Browser ([#5226](https://github.com/filebrowser/filebrowser/issues/5226)) ([a5ea2a2](https://github.com/filebrowser/filebrowser/commit/a5ea2a266bef619d1c4322266d1aa7d397d2c856)) + + +### Bug Fixes + +* abort ongoing requests when changing pages ([#3927](https://github.com/filebrowser/filebrowser/issues/3927)) ([93c4b2e](https://github.com/filebrowser/filebrowser/commit/93c4b2e03c5176da01a7e00a03c03ffcce279bc8)) +* add configurable minimum password length ([#5225](https://github.com/filebrowser/filebrowser/issues/5225)) ([464b644](https://github.com/filebrowser/filebrowser/commit/464b644adf22a2178414a6f1e4fa286276de81d2)) +* do not expose the name of the root directory ([#5224](https://github.com/filebrowser/filebrowser/issues/5224)) ([0892559](https://github.com/filebrowser/filebrowser/commit/089255997a653c284cd4249990b58bed00086c61)) +* Graceful shutdown ([8230eb7](https://github.com/filebrowser/filebrowser/commit/8230eb7ab51ccbd00b03f5b9d6964fa4aae331d4)) + + +### Reverts + +* Revert "docs: change cloudflare environment (#5231)" (#5232) ([9e273cd](https://github.com/filebrowser/filebrowser/commit/9e273cd9475d57b9500034e8b341ff8b620bcab8)), closes [#5231](https://github.com/filebrowser/filebrowser/issues/5231) [#5232](https://github.com/filebrowser/filebrowser/issues/5232) + + +### Build + +* add an arm64 target for the site image ([#5229](https://github.com/filebrowser/filebrowser/issues/5229)) ([f5e531c](https://github.com/filebrowser/filebrowser/commit/f5e531c8ae0b9b18717e184856ace0ce19beef82)) +* bump golangci-lint to 2.1.6 ([1d494ff](https://github.com/filebrowser/filebrowser/commit/1d494ff3159ef939cfb4980ccde6f27df3e738b5)) +* **deps:** bump brace-expansion from 1.1.11 to 1.1.12 in /tools ([#5228](https://github.com/filebrowser/filebrowser/issues/5228)) ([5a07291](https://github.com/filebrowser/filebrowser/commit/5a072913062a6b2b0e5c74a02ca7710218ed3e5e)) +* **deps:** bump github.com/go-viper/mapstructure/v2 ([f32f273](https://github.com/filebrowser/filebrowser/commit/f32f27383d1fafa074f038cc873bd37b7f20ee27)) +* **deps:** bump github.com/go-viper/mapstructure/v2 in /tools ([5331969](https://github.com/filebrowser/filebrowser/commit/5331969163f5ae1fd2389f665059fc9e4a98db15)) +* publish docs to cloudflare pages ([#5230](https://github.com/filebrowser/filebrowser/issues/5230)) ([8861933](https://github.com/filebrowser/filebrowser/commit/8861933cf845b104e072f35e5f37d7c26097c9dc)) + +### [2.33.10](https://github.com/filebrowser/filebrowser/compare/v2.33.9...v2.33.10) (2025-06-26) + + +### Bug Fixes + +* correctly check if command is allowed when using shell ([4d830f7](https://github.com/filebrowser/filebrowser/commit/4d830f707fc4314741fd431e70c2ce50cd5a3108)) +* correctly split shell ([f84a6db](https://github.com/filebrowser/filebrowser/commit/f84a6db680b6df1c7c8f06f1816f7e4c9e963668)) +* ignore linting error ([e735491](https://github.com/filebrowser/filebrowser/commit/e735491c57b12c3b19dd2e4b570723df78f4eb44)) + +### [2.33.9](https://github.com/filebrowser/filebrowser/compare/v2.33.8...v2.33.9) (2025-06-26) + + +### Bug Fixes + +* check exact match on command allow list ([e2e1e49](https://github.com/filebrowser/filebrowser/commit/e2e1e4913085cca8917e0f69171dc28d3c6af1b6)) +* remove auth token from /api/command ([d5b39a1](https://github.com/filebrowser/filebrowser/commit/d5b39a14fd3fc0d1c364116b41289484df7c27b2)) +* remove unused import ([c232d41](https://github.com/filebrowser/filebrowser/commit/c232d41f903d3026ec290bbe819b6c59a933048e)) + +### [2.33.8](https://github.com/filebrowser/filebrowser/compare/v2.33.7...v2.33.8) (2025-06-25) + +### [2.33.7](https://github.com/filebrowser/filebrowser/compare/v2.33.6...v2.33.7) (2025-06-25) + + +### Bug Fixes + +* correctly parse negative boolean flags ([221451a](https://github.com/filebrowser/filebrowser/commit/221451a5179c8f139819a315b80d0ecb0e7220c3)) +* linting issues ([4bfbf33](https://github.com/filebrowser/filebrowser/commit/4bfbf332499fc8aea5f6df6aae1efa0de918d1ae)) +* linting issues ([e74c958](https://github.com/filebrowser/filebrowser/commit/e74c95886226c0ee429af1860eed21dd1f8601aa)) + +### [2.33.6](https://github.com/filebrowser/filebrowser/compare/v2.33.5...v2.33.6) (2025-06-24) + + +### Bug Fixes + +* remove incorrect default for password flag ([23bd8f6](https://github.com/filebrowser/filebrowser/commit/23bd8f67155081d707d4799393d3b1e2bebeaa34)) + +### [2.33.5](https://github.com/filebrowser/filebrowser/compare/v2.33.4...v2.33.5) (2025-06-24) + + +### Features + +* update languages for project File Browser ([#5190](https://github.com/filebrowser/filebrowser/issues/5190)) ([f330764](https://github.com/filebrowser/filebrowser/commit/f33076462a133935ca97fb6c7345303fe350e167)) + + +### Bug Fixes + +* actually register the czech language ([#5189](https://github.com/filebrowser/filebrowser/issues/5189)) ([0268506](https://github.com/filebrowser/filebrowser/commit/0268506f80d33d2d31e38055e12530241d27a11b)) + +### [2.33.4](https://github.com/filebrowser/filebrowser/compare/v2.33.3...v2.33.4) (2025-06-22) + + +### Features + +* translation updates for project File Browser ([#5179](https://github.com/filebrowser/filebrowser/issues/5179)) ([f714e71](https://github.com/filebrowser/filebrowser/commit/f714e71a356c2301f394d651c9b6c467440508e3)) + +### [2.33.3](https://github.com/filebrowser/filebrowser/compare/v2.33.2...v2.33.3) (2025-06-22) + + +### Bug Fixes + +* keep command behavior in Dockerfile ([7c0c782](https://github.com/filebrowser/filebrowser/commit/7c0c7820efbbed2f0499353cc76ecb85d00ff7c3)) +* update search hotkey in help prompt ([#5178](https://github.com/filebrowser/filebrowser/issues/5178)) ([2741616](https://github.com/filebrowser/filebrowser/commit/2741616473636d40b7e9f14c9906ada08d328c3c)) + +### [2.33.2](https://github.com/filebrowser/filebrowser/compare/v2.33.1...v2.33.2) (2025-06-21) + + +### Bug Fixes + +* create user dir on signup ([0ca8059](https://github.com/filebrowser/filebrowser/commit/0ca8059d8dea4fe079146471ce4f24acc96021f2)) + +### [2.33.1](https://github.com/filebrowser/filebrowser/compare/v2.33.0...v2.33.1) (2025-06-21) + + +### Bug Fixes + +* downloadUrl of file preview ([#3728](https://github.com/filebrowser/filebrowser/issues/3728)) ([8a14018](https://github.com/filebrowser/filebrowser/commit/8a14018861fe581672bbd27cdc3ae5691f70a108)) +* remove auth query parameter from download and preview links ([cbb7124](https://github.com/filebrowser/filebrowser/commit/cbb712484d3bdabc033acaf3b696ef4f5865813d)) +* search uses ctrl+shift+f instead of hijacking browser's ctrl+f ([#4638](https://github.com/filebrowser/filebrowser/issues/4638)) ([a02b297](https://github.com/filebrowser/filebrowser/commit/a02b2972ebde2a58806ad1377bad46e748b63166)) + +## [2.33.0](https://github.com/filebrowser/filebrowser/compare/v2.32.3...v2.33.0) (2025-06-18) + + +### Features + +* improved docker image volumes and permissions ([#5160](https://github.com/filebrowser/filebrowser/issues/5160)) ([2e26393](https://github.com/filebrowser/filebrowser/commit/2e26393a022df0eaa9e08727407aba8b997aa728)) + +### [2.32.3](https://github.com/filebrowser/filebrowser/compare/v2.32.2...v2.32.3) (2025-06-17) + +### [2.32.2](https://github.com/filebrowser/filebrowser/compare/v2.32.1...v2.32.2) (2025-06-17) + + +### Features + +* updated for project File Browser ([#5159](https://github.com/filebrowser/filebrowser/issues/5159)) ([c34c0af](https://github.com/filebrowser/filebrowser/commit/c34c0afecf3242b16ad5d5584cd90a6ad323361c)) + +### [2.32.1](https://github.com/filebrowser/filebrowser/compare/v2.32.0...v2.32.1) (2025-06-16) + + +### Features + +* add Vietnamese translation ([#3840](https://github.com/filebrowser/filebrowser/issues/3840)) ([56b80b6](https://github.com/filebrowser/filebrowser/commit/56b80b6d9b4710538765ba7df5da1f03898f6b81)) +* improve pt-br translations with new keys and refinements ([#4903](https://github.com/filebrowser/filebrowser/issues/4903)) ([a882fb6](https://github.com/filebrowser/filebrowser/commit/a882fb6c85ab6ccc845ed0bf3908d8e5e60ce346)) +* update translation ko.json ([#3852](https://github.com/filebrowser/filebrowser/issues/3852)) ([d9ebd65](https://github.com/filebrowser/filebrowser/commit/d9ebd65ffcf9b2166fec708d51849796d12b16e0)) + + +### Bug Fixes + +* err shadowing lint ([c606a01](https://github.com/filebrowser/filebrowser/commit/c606a01a2d20932fb32ee896234d57631f8c47e4)) +* generate random admin password on quick setup ([a46acba](https://github.com/filebrowser/filebrowser/commit/a46acba5f92ee044661880d6ae349e289d984328)), closes [#3646](https://github.com/filebrowser/filebrowser/issues/3646) +* imports lint ([54b91b8](https://github.com/filebrowser/filebrowser/commit/54b91b8ff0b8ee1f02f72425ab97d27a5d942fc3)) +* set videojs locale ([#3742](https://github.com/filebrowser/filebrowser/issues/3742)) ([71a8f56](https://github.com/filebrowser/filebrowser/commit/71a8f5662c207e3cd4ee714a5b5a961121f510cd)) + + +### Build + +* **deps-dev:** bump vite from 6.0.11 to 6.1.6 in /frontend ([#3886](https://github.com/filebrowser/filebrowser/issues/3886)) ([5355629](https://github.com/filebrowser/filebrowser/commit/5355629fd1e7bd85ee3222fca22da899ba23ea95)) +* **deps:** bump golang.org/x/crypto from 0.31.0 to 0.35.0 ([#3865](https://github.com/filebrowser/filebrowser/issues/3865)) ([0ba9505](https://github.com/filebrowser/filebrowser/commit/0ba9505a19cb369653fc9f8260dc02fcc6587629)) +* **deps:** bump golang.org/x/net from 0.33.0 to 0.38.0 ([#3869](https://github.com/filebrowser/filebrowser/issues/3869)) ([cfea84f](https://github.com/filebrowser/filebrowser/commit/cfea84fd5e7ec9c1d2366293e5db12baaa4e3a81)) +* **deps:** bump vue-i18n from 11.0.1 to 11.1.2 in /frontend ([#3786](https://github.com/filebrowser/filebrowser/issues/3786)) ([35d1c09](https://github.com/filebrowser/filebrowser/commit/35d1c092434b80b22c89a614a02122e9f5965b39)) + ## [2.32.0](https://github.com/filebrowser/filebrowser/compare/v2.31.2...v2.32.0) (2025-01-31) diff --git a/CODE-OF-CONDUCT.md b/CODE-OF-CONDUCT.md new file mode 100644 index 00000000..28bdc607 --- /dev/null +++ b/CODE-OF-CONDUCT.md @@ -0,0 +1,46 @@ +# Code of Conduct + +## Contributor Covenant Code of Conduct + +### Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +### Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +### Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +### Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +### Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hacdias@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +### Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.4, available at [https://contributor-covenant.org/version/1/4](https://contributor-covenant.org/version/1/4). + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..1cfc8a92 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,91 @@ +# Contributing + +If you're interested in contributing to this project, this is the best place to start. Before contributing to this project, please take a bit of time to read our [Code of Conduct](code-of-conduct.md). Also, note that this project is open-source and licensed under [Apache License 2.0](LICENSE). + +## Project Structure + +The backend side of the application is written in [Go](https://golang.org/), while the frontend (located on a subdirectory of the same name) is written in [Vue.js](https://vuejs.org/). Due to the tight coupling required by some features, basic knowledge of both Go and Vue.js is recommended. + +* Learn Go: [https://github.com/golang/go/wiki/Learn](https://github.com/golang/go/wiki/Learn) +* Learn Vue.js: [https://vuejs.org/guide/introduction.html](https://vuejs.org/guide/introduction.html) + +We encourage you to use git to manage your fork. To clone the main repository, just run: + +```bash +git clone https://github.com/filebrowser/filebrowser +``` + +## Build + +### Frontend + +We are using [Node.js](https://nodejs.org/en/) on the frontend to manage the build process. The steps to build it are: + +```bash +# From the root of the repo, go to frontend/ +cd frontend + +# Install the dependencies +pnpm install + +# Build the frontend +pnpm run build +``` + +This will install the dependencies and build the frontend so you can then embed it into the Go app. Although, if you want to play with it, you'll get bored of building it after every change you do. So, you can run the command below to watch for changes: + +```bash +pnpm run dev +``` + +### Backend + +First of all, you need to download the required dependencies. We are using the built-in `go mod` tool for dependency management. To get the modules, run: + +```bash +go mod download +``` + +The magic of File Browser is that the static assets are bundled into the final binary. For that, we use [Go embed.FS](https://golang.org/pkg/embed/). The files from `frontend/dist` will be embedded during the build process. + +To build File Browser is just like any other Go program: + +```bash +go build +``` + +To create a development build use the "dev" tag, this way the content inside the frontend folder will not be embedded in the binary but will be reloaded at every change: + +```bash +go build -tags dev +``` + +## Translations + +Translations are managed on Transifex, which is an online website where everyone can contribute and translate strings for our project. It automatically syncs with the main language file \(in English\) and,, for you to contribute, you just need to: + +1. Go to our Transifex web page: [app.transifex.com/file-browser/file-browser](https://app.transifex.com/file-browser/file-browser/) +2. Click on **Join the project** and pick your language. We'll accept you as soon as possible. If you're language is not on the list, please request it via the interface. + +Translations are automatically pushed to GitHub via an integration. + +## Authentication Provider + +To build a new authentication provider, you need to implement the [Auther interface](https://github.com/filebrowser/filebrowser/blob/master/auth/auth.go), whose method will be called on the login page after the user has submitted their login data. + +```go +// Auther is the authentication interface. +type Auther interface { + // Auth is called to authenticate a request. + Auth(r *http.Request, s *users.Storage, root string) (*users.User, error) +} +``` + +After implementing the interface you should: + +1. Add it to [`auth` directory](https://github.com/filebrowser/filebrowser/blob/master/auth). +2. Add it to the [configuration parser](https://github.com/filebrowser/filebrowser/blob/master/cmd/config.go) for the CLI. +3. Add it to the [`authBackend.Get`](https://github.com/filebrowser/filebrowser/blob/master/storage/bolt/auth.go). + +If you need to add more flags, please update the function `addConfigFlags`. + diff --git a/Dockerfile b/Dockerfile index 40a91a06..0e64f120 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,32 @@ -FROM alpine:latest -RUN apk --update add ca-certificates \ - mailcap \ - curl \ - jq +FROM alpine:3.22 -COPY healthcheck.sh /healthcheck.sh -RUN chmod +x /healthcheck.sh # Make the script executable +RUN apk update && \ + apk --no-cache add ca-certificates mailcap curl jq tini -HEALTHCHECK --start-period=2s --interval=5s --timeout=3s \ - CMD /healthcheck.sh || exit 1 +# Make user and create necessary directories +ENV UID=1000 +ENV GID=1000 + +RUN addgroup -g $GID user && \ + adduser -D -u $UID -G user user && \ + mkdir -p /config /database /srv && \ + chown -R user:user /config /database /srv + +# Copy files and set permissions +COPY filebrowser /bin/filebrowser +COPY docker/common/ / +COPY docker/alpine/ / + +RUN chown -R user:user /bin/filebrowser /defaults healthcheck.sh init.sh + +# Define healthcheck script +HEALTHCHECK --start-period=2s --interval=5s --timeout=3s CMD /healthcheck.sh + +# Set the user, volumes and exposed ports +USER user + +VOLUME /srv /config /database -VOLUME /srv EXPOSE 80 -COPY docker_config.json /.filebrowser.json -COPY filebrowser /filebrowser - -ENTRYPOINT [ "/filebrowser" ] \ No newline at end of file +ENTRYPOINT [ "tini", "--", "/init.sh", "filebrowser", "--config", "/config/settings.json" ] diff --git a/Dockerfile.s6 b/Dockerfile.s6 index 609a2634..cb34cbd1 100644 --- a/Dockerfile.s6 +++ b/Dockerfile.s6 @@ -1,21 +1,23 @@ -FROM ghcr.io/linuxserver/baseimage-alpine:3.20 +FROM ghcr.io/linuxserver/baseimage-alpine:3.22 -RUN apk --update add ca-certificates \ - mailcap \ - curl \ - jq +RUN apk update && \ + apk --no-cache add ca-certificates mailcap curl jq -COPY healthcheck.sh /healthcheck.sh -RUN chmod +x /healthcheck.sh # Make the script executable +# Make user and create necessary directories +RUN mkdir -p /config /database /srv && \ + chown -R abc:abc /config /database /srv -HEALTHCHECK --start-period=2s --interval=5s --timeout=3s \ - CMD /healthcheck.sh || exit 1 +# Copy files and set permissions +COPY filebrowser /bin/filebrowser +COPY docker/common/ / +COPY docker/s6/ / -# copy local files -COPY docker/root/ / -RUN ln -s /config/settings.json /.filebrowser.json -COPY filebrowser /usr/bin/filebrowser +RUN chown -R abc:abc /bin/filebrowser /defaults healthcheck.sh -# ports and volumes +# Define healthcheck script +HEALTHCHECK --start-period=2s --interval=5s --timeout=3s CMD /healthcheck.sh + +# Set the volumes and exposed ports VOLUME /srv /config /database + EXPOSE 80 diff --git a/Dockerfile.s6.aarch64 b/Dockerfile.s6.aarch64 deleted file mode 100644 index 1e62391e..00000000 --- a/Dockerfile.s6.aarch64 +++ /dev/null @@ -1,21 +0,0 @@ -FROM ghcr.io/linuxserver/baseimage-alpine:arm64v8-3.20 - -RUN apk --update add ca-certificates \ - mailcap \ - curl \ - jq - -COPY healthcheck.sh /healthcheck.sh -RUN chmod +x /healthcheck.sh # Make the script executable - -HEALTHCHECK --start-period=2s --interval=5s --timeout=3s \ - CMD /healthcheck.sh || exit 1 - -# copy local files -COPY docker/root/ / -RUN ln -s /config/settings.json /.filebrowser.json -COPY filebrowser /usr/bin/filebrowser - -# ports and volumes -VOLUME /srv /config /database -EXPOSE 80 diff --git a/LICENSE b/LICENSE index 6011a2a4..c8db89a2 100644 --- a/LICENSE +++ b/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018 File Browser contributors + Copyright 2018 File Browser Contributors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Makefile b/Makefile index 8b2eebfd..10ec3ea9 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,14 @@ include tools.mk LDFLAGS += -X "$(MODULE)/version.Version=$(VERSION)" -X "$(MODULE)/version.CommitSHA=$(VERSION_HASH)" +SITE_DOCKER_FLAGS = \ + -v $(CURDIR)/www:/docs \ + -v $(CURDIR)/LICENSE:/docs/docs/LICENSE \ + -v $(CURDIR)/SECURITY.md:/docs/docs/security.md \ + -v $(CURDIR)/CHANGELOG.md:/docs/docs/changelog.md \ + -v $(CURDIR)/CODE-OF-CONDUCT.md:/docs/docs/code-of-conduct.md \ + -v $(CURDIR)/CONTRIBUTING.md:/docs/docs/contributing.md + ## Build: .PHONY: build @@ -53,6 +61,17 @@ clean: clean-tools ## Clean bump-version: $(standard-version) ## Bump app version $Q ./scripts/bump_version.sh +.PHONY: site +site: ## Build site + @rm -rf www/public + docker build -f www/Dockerfile --progress=plain -t filebrowser.site www + docker run --rm $(SITE_DOCKER_FLAGS) filebrowser.site build -d "public" + +.PHONY: site-serve +site-serve: ## Serve site for development + docker build -f www/Dockerfile --progress=plain -t filebrowser.site www + docker run --rm -it -p 8000:8000 $(SITE_DOCKER_FLAGS) filebrowser.site + ## Help: help: ## Show this help @echo '' diff --git a/README.md b/README.md index 644ea97c..c86eeed4 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ [![Documentation](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](http://godoc.org/github.com/filebrowser/filebrowser) [![Version](https://img.shields.io/gitea/v/release/midou/filebrowser-fork?gitea_url=https%3A%2F%2Fgit.linuxcrack.zip&style=flat-square)](https://git.linuxcrack.zip/midou/filebrowser-fork/releases/latest) -filebrowser provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app. +File Browser provides a file managing interface within a specified directory and it can be used to upload, delete, preview and edit your files. It is a **create-your-own-cloud**-kind of software where you can just install it on your server, direct it to a path and access your files through a nice web interface. # This is a soft fork and is not meant to be used without knowing what it does @@ -23,22 +23,23 @@ filebrowser provides a file managing interface within a specified directory and Below is the rest of the orignal repository links that may be relevant to the fork. --- -## Features +> [!WARNING] +> +> This project is currently on **maintenance-only** mode, and is looking for new maintainers. For more information, please read the [discussion #4906](https://github.com/filebrowser/filebrowser/discussions/4906). Therefore, please note the following: +> +> - It can take a while until someone gets back to you. Please be patient. +> - [Issues][issues] are only being used to track bugs. Any unrelated issues will be converted into a [discussion][discussions]. +> - No new features will be implemented until further notice. The priority is on triaging issues and merge bug fixes. +> +> If you're interested in maintaining this project, please reach out via the discussion above. -Please refer to our docs at [https://filebrowser.org/features](https://filebrowser.org/features) - -## Install - -For installation instructions please refer to our docs at [https://filebrowser.org/installation](https://filebrowser.org/installation). - -## Configuration - -[Authentication Method](https://filebrowser.org/configuration/authentication-method) - You can change the way the user authenticates with the filebrowser server - -[Command Runner](https://filebrowser.org/configuration/command-runner) - The command runner is a feature that enables you to execute any shell command you want before or after a certain event. - -[Custom Branding](https://filebrowser.org/configuration/custom-branding) - You can customize your File Browser installation by change its name to any other you want, by adding a global custom style sheet and by using your own logotype if you want. +[issues]: https://github.com/filebrowser/filebrowser/issues +[discussions]: https://github.com/filebrowser/filebrowser/discussions ## Contributing -If you're interested in contributing to this project, our docs are best places to start [https://filebrowser.org/contributing](https://filebrowser.org/contributing). +Contributions are always welcome. To start contributing to this project, read our [guidelines](CONTRIBUTING.md) first. + +## License + +[Apache License 2.0](LICENSE) © File Browser Contributors diff --git a/SECURITY.md b/SECURITY.md index a36dee40..490a9bea 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -12,7 +12,9 @@ currently being supported with security updates. ## Reporting a Vulnerability -Vulnerabilities should be reported to filebrowser@googlegroups.com - which is a private, maintainer-only group. Maintainers will attempt to respond to/confirm reports within 2-3 days, but if you believe your report to be "critical" to user safety and security, please note as such in the subject. We have tens of thousands of users using our software, and take security vulnerabilities seriously. +Vulnerabilities with critical impact should be reported on the [Security](https://github.com/filebrowser/filebrowser/security) page of this repository, which is a private way of communicating vulnerabilities to maintainers. This project is in maintenance-only mode and it can take a while until someone gets back to you. + +If it is not a critical vulnerability, please open an issue and we will categorize it as a security issue. By giving visibility, we can get more help from the community at fixing such issues. When reporting an issue, where possible, please provide at least: @@ -21,6 +23,4 @@ When reporting an issue, where possible, please provide at least: * Steps to reproduce * Your recommended remediation(s), if any. -The FileBrowser team is a volunteer-only effort, and may reach back out for clarification. - -> Note: Please do not open public issues for security issues, as GitHub does not provide facility for private issues, and deleting the issue makes it hard to triage/respond back to the reporter. +The File Browser team is a volunteer-only effort, and may reach back out for clarification. diff --git a/auth/hook.go b/auth/hook.go index c659e57b..849a923d 100644 --- a/auth/hook.go +++ b/auth/hook.go @@ -150,7 +150,7 @@ func (a *HookAuth) SaveUser() (*users.User, error) { } if u == nil { - pass, err := users.HashPwd(a.Cred.Password) + pass, err := users.ValidateAndHashPwd(a.Cred.Password, a.Settings.MinimumPasswordLength) if err != nil { return nil, err } @@ -186,7 +186,7 @@ func (a *HookAuth) SaveUser() (*users.User, error) { // update the password when it doesn't match the current if p { - pass, err := users.HashPwd(a.Cred.Password) + pass, err := users.ValidateAndHashPwd(a.Cred.Password, a.Settings.MinimumPasswordLength) if err != nil { return nil, err } diff --git a/auth/proxy.go b/auth/proxy.go index 0e954309..301aa292 100644 --- a/auth/proxy.go +++ b/auth/proxy.go @@ -1,7 +1,6 @@ package auth import ( - "crypto/rand" "errors" "net/http" @@ -29,15 +28,14 @@ func (a ProxyAuth) Auth(r *http.Request, usr users.Store, setting *settings.Sett } func (a ProxyAuth) createUser(usr users.Store, setting *settings.Settings, srv *settings.Server, username string) (*users.User, error) { - const passwordSize = 32 - randomPasswordBytes := make([]byte, passwordSize) - _, err := rand.Read(randomPasswordBytes) + const randomPasswordLength = settings.DefaultMinimumPasswordLength + 10 + pwd, err := users.RandomPwd(randomPasswordLength) if err != nil { return nil, err } var hashedRandomPassword string - hashedRandomPassword, err = users.HashPwd(string(randomPasswordBytes)) + hashedRandomPassword, err = users.ValidateAndHashPwd(pwd, setting.MinimumPasswordLength) if err != nil { return nil, err } diff --git a/cmd/config.go b/cmd/config.go index de55c28e..3ee8dab8 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -32,6 +32,7 @@ func addConfigFlags(flags *pflag.FlagSet) { 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.String("shell", "", "shell command to which other commands should be appended") flags.String("auth.method", string(auth.MethodJSONAuth), "authentication type") @@ -144,6 +145,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut fmt.Fprintf(w, "Sign up:\t%t\n", set.Signup) fmt.Fprintf(w, "Create User Dir:\t%t\n", set.CreateUserDir) + fmt.Fprintf(w, "Minimum Password Length:\t%d\n", set.MinimumPasswordLength) fmt.Fprintf(w, "Auth method:\t%s\n", set.AuthMethod) fmt.Fprintf(w, "Shell:\t%s\t\n", strings.Join(set.Shell, " ")) fmt.Fprintln(w, "\nBranding:") diff --git a/cmd/config_import.go b/cmd/config_import.go index ab1ccaf5..6c609481 100644 --- a/cmd/config_import.go +++ b/cmd/config_import.go @@ -56,7 +56,7 @@ The path must be for a json or yaml file.`, checkErr(err) var rawAuther interface{} - if filepath.Ext(args[0]) != ".json" { //nolint:goconst + if filepath.Ext(args[0]) != ".json" { rawAuther = cleanUpInterfaceMap(file.Auther.(map[interface{}]interface{})) } else { rawAuther = file.Auther diff --git a/cmd/config_init.go b/cmd/config_init.go index 60a0f37b..d9710514 100644 --- a/cmd/config_init.go +++ b/cmd/config_init.go @@ -29,12 +29,13 @@ override the options.`, authMethod, auther := getAuthentication(flags) s := &settings.Settings{ - Key: generateKey(), - Signup: mustGetBool(flags, "signup"), - CreateUserDir: mustGetBool(flags, "create-user-dir"), - Shell: convertCmdStrToCmdArray(mustGetString(flags, "shell")), - AuthMethod: authMethod, - Defaults: defaults, + Key: generateKey(), + Signup: mustGetBool(flags, "signup"), + CreateUserDir: mustGetBool(flags, "create-user-dir"), + MinimumPasswordLength: mustGetUint(flags, "minimum-password-length"), + Shell: convertCmdStrToCmdArray(mustGetString(flags, "shell")), + AuthMethod: authMethod, + Defaults: defaults, Branding: settings.Branding{ Name: mustGetString(flags, "branding.name"), DisableExternal: mustGetBool(flags, "branding.disableExternal"), diff --git a/cmd/config_set.go b/cmd/config_set.go index 23ff7e1b..05816795 100644 --- a/cmd/config_set.go +++ b/cmd/config_set.go @@ -51,6 +51,8 @@ you want to change. Other options will remain unchanged.`, set.Shell = convertCmdStrToCmdArray(mustGetString(flags, flag.Name)) case "create-user-dir": set.CreateUserDir = mustGetBool(flags, flag.Name) + case "minimum-password-length": + set.MinimumPasswordLength = mustGetUint(flags, flag.Name) case "branding.name": set.Branding.Name = mustGetString(flags, flag.Name) case "branding.color": diff --git a/cmd/root.go b/cmd/root.go index 59329c5c..4b6819b7 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,6 +1,7 @@ package cmd import ( + "context" "crypto/tls" "errors" "io" @@ -13,6 +14,7 @@ import ( "path/filepath" "strings" "syscall" + "time" homedir "github.com/mitchellh/go-homedir" "github.com/spf13/afero" @@ -48,7 +50,7 @@ func init() { persistent.StringP("database", "d", "./filebrowser.db", "database path") flags.Bool("noauth", false, "use the noauth auther when using quick setup") flags.String("username", "admin", "username for the first user when using quick config") - flags.String("password", "", "hashed password for the first user when using quick config (default \"admin\")") + flags.String("password", "", "hashed password for the first user when using quick config") addServerFlags(flags) } @@ -61,14 +63,14 @@ 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") //nolint:gomnd + 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:gomnd + 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", false, "disables Command Runner feature") + flags.Bool("disable-exec", true, "disables Command Runner feature") flags.Bool("disable-type-detection-by-header", false, "disables type detection by reading file headers") } @@ -129,7 +131,7 @@ user created with the credentials from options "username" and "password".`, cacheDir, err := cmd.Flags().GetString("cache-dir") checkErr(err) if cacheDir != "" { - if err := os.MkdirAll(cacheDir, 0700); err != nil { //nolint:govet,gomnd + if err := os.MkdirAll(cacheDir, 0700); err != nil { //nolint:govet log.Fatalf("can't make directory %s: %s", cacheDir, err) } fileCache = diskcache.New(afero.NewOsFs(), cacheDir) @@ -167,10 +169,6 @@ user created with the credentials from options "username" and "password".`, checkErr(err) } - sigc := make(chan os.Signal, 1) - signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) - go cleanupHandler(listener, sigc) - assetsFs, err := fs.Sub(frontend.Assets(), "dist") if err != nil { panic(err) @@ -182,18 +180,31 @@ user created with the credentials from options "username" and "password".`, defer listener.Close() log.Println("Listening on", listener.Addr().String()) - //nolint: gosec - if err := http.Serve(listener, handler); err != nil { - log.Fatal(err) + srv := &http.Server{ + Handler: handler, + ReadHeaderTimeout: 60 * time.Second, } - }, pythonConfig{allowNoDB: true}), -} -func cleanupHandler(listener net.Listener, c chan os.Signal) { //nolint:interfacer - sig := <-c - log.Printf("Caught signal %s: shutting down.", sig) - listener.Close() - os.Exit(0) + go func() { + if err := srv.Serve(listener); !errors.Is(err, http.ErrServerClosed) { + log.Fatalf("HTTP server error: %v", err) + } + + log.Println("Stopped serving new connections.") + }() + + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) + <-sigc + + shutdownCtx, shutdownRelease := context.WithTimeout(context.Background(), 10*time.Second) //nolint:mnd + defer shutdownRelease() + + if err := srv.Shutdown(shutdownCtx); err != nil { + log.Fatalf("HTTP shutdown error: %v", err) + } + log.Println("Graceful shutdown complete.") + }, pythonConfig{allowNoDB: true}), } //nolint:gocyclo @@ -201,42 +212,42 @@ func getRunParams(flags *pflag.FlagSet, st *storage.Storage) *settings.Server { server, err := st.Settings.GetServer() checkErr(err) - if val, set := getParamB(flags, "root"); set { + if val, set := getStringParamB(flags, "root"); set { server.Root = val } - if val, set := getParamB(flags, "baseurl"); set { + if val, set := getStringParamB(flags, "baseurl"); set { server.BaseURL = val } - if val, set := getParamB(flags, "log"); set { + if val, set := getStringParamB(flags, "log"); set { server.Log = val } isSocketSet := false isAddrSet := false - if val, set := getParamB(flags, "address"); set { + if val, set := getStringParamB(flags, "address"); set { server.Address = val isAddrSet = isAddrSet || set } - if val, set := getParamB(flags, "port"); set { + if val, set := getStringParamB(flags, "port"); set { server.Port = val isAddrSet = isAddrSet || set } - if val, set := getParamB(flags, "key"); set { + if val, set := getStringParamB(flags, "key"); set { server.TLSKey = val isAddrSet = isAddrSet || set } - if val, set := getParamB(flags, "cert"); set { + if val, set := getStringParamB(flags, "cert"); set { server.TLSCert = val isAddrSet = isAddrSet || set } - if val, set := getParamB(flags, "socket"); set { + if val, set := getStringParamB(flags, "socket"); set { server.Socket = val isSocketSet = isSocketSet || set } @@ -250,33 +261,69 @@ func getRunParams(flags *pflag.FlagSet, st *storage.Storage) *settings.Server { server.Socket = "" } - _, disableThumbnails := getParamB(flags, "disable-thumbnails") + disableThumbnails := getBoolParam(flags, "disable-thumbnails") server.EnableThumbnails = !disableThumbnails - _, disablePreviewResize := getParamB(flags, "disable-preview-resize") + disablePreviewResize := getBoolParam(flags, "disable-preview-resize") server.ResizePreview = !disablePreviewResize - _, disableTypeDetectionByHeader := getParamB(flags, "disable-type-detection-by-header") + disableTypeDetectionByHeader := getBoolParam(flags, "disable-type-detection-by-header") server.TypeDetectionByHeader = !disableTypeDetectionByHeader - _, disableExec := getParamB(flags, "disable-exec") + disableExec := getBoolParam(flags, "disable-exec") server.EnableExec = !disableExec - if val, set := getParamB(flags, "token-expiration-time"); set { + if server.EnableExec { + log.Println("WARNING: Command Runner feature enabled!") + log.Println("WARNING: This feature has known security vulnerabilities and should not") + log.Println("WARNING: you fully understand the risks involved. For more information") + 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 } -// getParamB returns a parameter as a string and a boolean to tell if it is different from the default +// 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 getParamB(flags *pflag.FlagSet, key string) (string, bool) { +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. @@ -293,8 +340,8 @@ func getParamB(flags *pflag.FlagSet, key string) (string, bool) { return value, false } -func getParam(flags *pflag.FlagSet, key string) string { - val, _ := getParamB(flags, key) +func getStringParam(flags *pflag.FlagSet, key string) string { + val, _ := getStringParamB(flags, key) return val } @@ -318,10 +365,11 @@ func setupLog(logMethod string) { func quickSetup(flags *pflag.FlagSet, d pythonData) { set := &settings.Settings{ - Key: generateKey(), - Signup: false, - CreateUserDir: false, - UserHomeBasePath: settings.DefaultUsersHomeBasePath, + Key: generateKey(), + Signup: false, + CreateUserDir: false, + MinimumPasswordLength: settings.DefaultMinimumPasswordLength, + UserHomeBasePath: settings.DefaultUsersHomeBasePath, Defaults: settings.UserDefaults{ Scope: ".", Locale: "en", @@ -349,7 +397,7 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) { } var err error - if _, noauth := getParamB(flags, "noauth"); noauth { + if _, noauth := getStringParamB(flags, "noauth"); noauth { set.AuthMethod = auth.MethodNoAuth err = d.store.Auth.Save(&auth.NoAuth{}) } else { @@ -362,23 +410,29 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) { checkErr(err) ser := &settings.Server{ - BaseURL: getParam(flags, "baseurl"), - Port: getParam(flags, "port"), - Log: getParam(flags, "log"), - TLSKey: getParam(flags, "key"), - TLSCert: getParam(flags, "cert"), - Address: getParam(flags, "address"), - Root: getParam(flags, "root"), + 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"), } err = d.store.Settings.SaveServer(ser) checkErr(err) - username := getParam(flags, "username") - password := getParam(flags, "password") + username := getStringParam(flags, "username") + password := getStringParam(flags, "password") if password == "" { - password, err = users.HashPwd("admin") + var pwd string + pwd, err = users.RandomPwd(set.MinimumPasswordLength) + checkErr(err) + + log.Println("Randomly generated password for user 'admin':", pwd) + + password, err = users.ValidateAndHashPwd(pwd, set.MinimumPasswordLength) checkErr(err) } @@ -414,6 +468,7 @@ func initConfig() { v.SetEnvPrefix("FB") v.AutomaticEnv() v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + v.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) if err := v.ReadInConfig(); err != nil { var configParseError v.ConfigParseError diff --git a/cmd/upgrade.go b/cmd/upgrade.go index 83a0729c..f6966e2e 100644 --- a/cmd/upgrade.go +++ b/cmd/upgrade.go @@ -25,7 +25,7 @@ this version.`, flags := cmd.Flags() oldDB := mustGetString(flags, "old.database") oldConf := mustGetString(flags, "old.config") - err := importer.Import(oldDB, oldConf, getParam(flags, "database")) + err := importer.Import(oldDB, oldConf, getStringParam(flags, "database")) checkErr(err) }, } diff --git a/cmd/users_add.go b/cmd/users_add.go index e7f132ed..c3b8af28 100644 --- a/cmd/users_add.go +++ b/cmd/users_add.go @@ -21,7 +21,7 @@ var usersAddCmd = &cobra.Command{ checkErr(err) getUserDefaults(cmd.Flags(), &s.Defaults, false) - password, err := users.HashPwd(args[1]) + password, err := users.ValidateAndHashPwd(args[1], s.MinimumPasswordLength) checkErr(err) user := &users.User{ diff --git a/cmd/users_update.go b/cmd/users_update.go index 822bb6dc..2c58c4af 100644 --- a/cmd/users_update.go +++ b/cmd/users_update.go @@ -27,8 +27,10 @@ options you want to change.`, password := mustGetString(flags, "password") newUsername := mustGetString(flags, "username") + s, err := d.store.Settings.Get() + checkErr(err) + var ( - err error user *users.User ) @@ -64,7 +66,7 @@ options you want to change.`, } if password != "" { - user.Password, err = users.HashPwd(password) + user.Password, err = users.ValidateAndHashPwd(password, s.MinimumPasswordLength) checkErr(err) } diff --git a/cmd/utils.go b/cmd/utils.go index 78f48d13..909a1558 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -14,6 +14,7 @@ import ( "github.com/spf13/pflag" yaml "gopkg.in/yaml.v2" + "github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/storage" "github.com/filebrowser/filebrowser/v2/storage/bolt" @@ -72,7 +73,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,gomnd + if err := os.MkdirAll(d, 0700); err != nil { //nolint:govet return false, err } return false, nil @@ -86,7 +87,7 @@ func python(fn pythonFunc, cfg pythonConfig) cobraFunc { return func(cmd *cobra.Command, args []string) { data := pythonData{hadDB: true} - path := getParam(cmd.Flags(), "database") + path := getStringParam(cmd.Flags(), "database") absPath, err := filepath.Abs(path) if err != nil { panic(err) @@ -105,7 +106,7 @@ func python(fn pythonFunc, cfg pythonConfig) cobraFunc { log.Println("Using database: " + absPath) data.hadDB = exists - db, err := storm.Open(path) + db, err := storm.Open(path, storm.BoltOptions(files.PermFile, nil)) checkErr(err) defer db.Close() data.store, err = bolt.NewStorage(db) @@ -124,7 +125,7 @@ func marshal(filename string, data interface{}) error { encoder := json.NewEncoder(fd) encoder.SetIndent("", " ") return encoder.Encode(data) - case ".yml", ".yaml": //nolint:goconst + case ".yml", ".yaml": encoder := yaml.NewEncoder(fd) return encoder.Encode(data) default: diff --git a/diskcache/file_cache.go b/diskcache/file_cache.go index 5c1fb427..cd5e27c7 100644 --- a/diskcache/file_cache.go +++ b/diskcache/file_cache.go @@ -37,11 +37,11 @@ func (f *FileCache) Store(_ context.Context, key string, value []byte) error { defer mu.Unlock() fileName := f.getFileName(key) - if err := f.fs.MkdirAll(filepath.Dir(fileName), 0700); err != nil { //nolint:gomnd + if err := f.fs.MkdirAll(filepath.Dir(fileName), 0700); err != nil { return err } - if err := afero.WriteFile(f.fs, fileName, value, 0700); err != nil { //nolint:gomnd + if err := afero.WriteFile(f.fs, fileName, value, 0700); err != nil { return err } diff --git a/docker/alpine/init.sh b/docker/alpine/init.sh new file mode 100755 index 00000000..4dc5908a --- /dev/null +++ b/docker/alpine/init.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +set -e + +# Ensure configuration exists +if [ ! -f "/config/settings.json" ]; then + cp -a /defaults/settings.json /config/settings.json +fi + +exec "$@" diff --git a/docker/root/defaults/settings.json b/docker/common/defaults/settings.json similarity index 100% rename from docker/root/defaults/settings.json rename to docker/common/defaults/settings.json diff --git a/docker/common/healthcheck.sh b/docker/common/healthcheck.sh new file mode 100755 index 00000000..e0ab1e65 --- /dev/null +++ b/docker/common/healthcheck.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +set -e + +PORT=${FB_PORT:-$(jq -r .port /config/settings.json)} +ADDRESS=${FB_ADDRESS:-$(jq -r .address /config/settings.json)} +ADDRESS=${ADDRESS:-localhost} + +curl -f http://$ADDRESS:$PORT/health || exit 1 diff --git a/docker/root/etc/services.d/filebrowser/run b/docker/root/etc/services.d/filebrowser/run deleted file mode 100755 index 1d633757..00000000 --- a/docker/root/etc/services.d/filebrowser/run +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/with-contenv bash - -exec s6-setuidgid abc filebrowser -c /config/settings.json -d /database/filebrowser.db; \ No newline at end of file diff --git a/docker/root/custom-cont-init.d/20-config b/docker/s6/custom-cont-init.d/20-config similarity index 80% rename from docker/root/custom-cont-init.d/20-config rename to docker/s6/custom-cont-init.d/20-config index 80bec7cd..a4ba52cf 100755 --- a/docker/root/custom-cont-init.d/20-config +++ b/docker/s6/custom-cont-init.d/20-config @@ -1,9 +1,6 @@ #!/usr/bin/with-contenv bash -# make folders -mkdir -p /database - -# copy config +# Ensure configuration exists if [ ! -f "/config/settings.json" ]; then cp -a /defaults/settings.json /config/settings.json fi diff --git a/docker/s6/etc/services.d/filebrowser/run b/docker/s6/etc/services.d/filebrowser/run new file mode 100755 index 00000000..f4f2fb8e --- /dev/null +++ b/docker/s6/etc/services.d/filebrowser/run @@ -0,0 +1,3 @@ +#!/usr/bin/with-contenv bash + +exec s6-setuidgid abc filebrowser -c /config/settings.json; diff --git a/errors/errors.go b/errors/errors.go index 5ec364c0..5fd760c2 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -1,12 +1,16 @@ package errors -import "errors" +import ( + "errors" + "fmt" +) 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") @@ -19,3 +23,11 @@ var ( ErrSourceIsParent = errors.New("source is parent") ErrRootUserDeletion = errors.New("user with id 1 can't be deleted") ) + +type ErrShortPassword struct { + MinimumLength uint +} + +func (e ErrShortPassword) Error() string { + return fmt.Sprintf("password is too short, minimum length is %d", e.MinimumLength) +} diff --git a/files/file.go b/files/file.go index 03b3a6f9..1ba1cd58 100644 --- a/files/file.go +++ b/files/file.go @@ -27,8 +27,8 @@ import ( "github.com/filebrowser/filebrowser/v2/rules" ) -const PermFile = 0644 -const PermDir = 0755 +const PermFile = 0640 +const PermDir = 0750 var ( reSubDirs = regexp.MustCompile("(?i)^sub(s|titles)$") @@ -86,6 +86,11 @@ func NewFileInfo(opts *FileOptions) (*FileInfo, error) { return nil, err } + // Do not expose the name of root directory. + if file.Path == "/" { + file.Name = "" + } + if opts.Expand { if file.IsDir { if err := file.readListing(opts.Checker, opts.ReadHeader); err != nil { //nolint:govet @@ -217,7 +222,6 @@ func (i *FileInfo) RealPath() string { return i.Path } -//nolint:goconst func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error { if IsNamedPipe(i.Mode) { i.Type = "blob" @@ -314,7 +318,7 @@ func (i *FileInfo) readFirstBytes() []byte { } defer reader.Close() - buffer := make([]byte, 512) //nolint:gomnd + buffer := make([]byte, 512) n, err := reader.Read(buffer) if err != nil && !errors.Is(err, io.EOF) { log.Print(err) diff --git a/files/listing.go b/files/listing.go index bd16afdd..ad60e51e 100644 --- a/files/listing.go +++ b/files/listing.go @@ -16,8 +16,6 @@ type Listing struct { } // ApplySort applies the sort order using .Order and .Sort -// -//nolint:goconst func (l Listing) ApplySort() { // Check '.Order' to know how to sort if !l.Sorting.Asc { diff --git a/frontend/index.html b/frontend/index.html index 02c303ae..3b54dcdf 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -10,18 +10,10 @@ File Browser - - + + + + - - - - - - - - - - diff --git a/frontend/src/components/files/ListingItem.vue b/frontend/src/components/files/ListingItem.vue index 75326f4c..14d897cf 100644 --- a/frontend/src/components/files/ListingItem.vue +++ b/frontend/src/components/files/ListingItem.vue @@ -8,6 +8,13 @@ @dragover="dragOver" @drop="drop" @click="itemClick" + @mousedown="handleMouseDown" + @mouseup="handleMouseUp" + @mouseleave="handleMouseLeave" + @touchstart="handleTouchStart" + @touchend="handleTouchEnd" + @touchcancel="handleTouchCancel" + @touchmove="handleTouchMove" :data-dir="isDir" :data-type="type" :aria-label="name" @@ -50,6 +57,12 @@ import { useRouter } from "vue-router"; const touches = ref(0); +const longPressTimer = ref(null); +const longPressTriggered = ref(false); +const longPressDelay = ref(500); +const startPosition = ref<{ x: number; y: number } | null>(null); +const moveThreshold = ref(10); + const $showError = inject("$showError")!; const router = useRouter(); @@ -209,6 +222,12 @@ const drop = async (event: Event) => { }; const itemClick = (event: Event | KeyboardEvent) => { + // If long press was triggered, prevent normal click behavior + if (longPressTriggered.value) { + longPressTriggered.value = false; + return; + } + if ( singleClick.value && !(event as KeyboardEvent).ctrlKey && @@ -281,4 +300,76 @@ const getExtension = (fileName: string): string => { } return fileName.substring(lastDotIndex); }; + +// Long-press helper functions +const startLongPress = (clientX: number, clientY: number) => { + startPosition.value = { x: clientX, y: clientY }; + longPressTimer.value = window.setTimeout(() => { + handleLongPress(); + }, longPressDelay.value); +}; + +const cancelLongPress = () => { + if (longPressTimer.value !== null) { + window.clearTimeout(longPressTimer.value); + longPressTimer.value = null; + } + startPosition.value = null; +}; + +const handleLongPress = () => { + if (singleClick.value) { + longPressTriggered.value = true; + click(new Event("longpress")); + } + cancelLongPress(); +}; + +const checkMovement = (clientX: number, clientY: number): boolean => { + if (!startPosition.value) return false; + + const deltaX = Math.abs(clientX - startPosition.value.x); + const deltaY = Math.abs(clientY - startPosition.value.y); + + return deltaX > moveThreshold.value || deltaY > moveThreshold.value; +}; + +// Event handlers +const handleMouseDown = (event: MouseEvent) => { + if (event.button === 0) { + startLongPress(event.clientX, event.clientY); + } +}; + +const handleMouseUp = () => { + cancelLongPress(); +}; + +const handleMouseLeave = () => { + cancelLongPress(); +}; + +const handleTouchStart = (event: TouchEvent) => { + if (event.touches.length === 1) { + const touch = event.touches[0]; + startLongPress(touch.clientX, touch.clientY); + } +}; + +const handleTouchEnd = () => { + cancelLongPress(); +}; + +const handleTouchCancel = () => { + cancelLongPress(); +}; + +const handleTouchMove = (event: TouchEvent) => { + if (event.touches.length === 1 && startPosition.value) { + const touch = event.touches[0]; + if (checkMovement(touch.clientX, touch.clientY)) { + cancelLongPress(); + } + } +}; diff --git a/frontend/src/components/files/VideoPlayer.vue b/frontend/src/components/files/VideoPlayer.vue index 40ac4d3e..05191ee4 100644 --- a/frontend/src/components/files/VideoPlayer.vue +++ b/frontend/src/components/files/VideoPlayer.vue @@ -62,7 +62,8 @@ const initVideoPlayer = async () => { const languagePack = await ( languageImports[lang] || languageImports.en )?.(); - videojs.addLanguage("videoPlayerLocal", languagePack.default); + const code = languageImports[lang] ? lang : "en"; + videojs.addLanguage(code, languagePack.default); sourceType.value = ""; // @@ -70,7 +71,7 @@ const initVideoPlayer = async () => { const srcOpt = { sources: { src: props.source, type: sourceType.value } }; //Supporting localized language display. - const langOpt = { language: "videoPlayerLocal" }; + const langOpt = { language: code }; // support for playback at different speeds. const playbackRatesOpt = { playbackRates: [0.5, 1, 1.5, 2, 2.5, 3] }; const options = getOptions( diff --git a/frontend/src/components/prompts/FileList.vue b/frontend/src/components/prompts/FileList.vue index 6a10a127..6d3321b2 100644 --- a/frontend/src/components/prompts/FileList.vue +++ b/frontend/src/components/prompts/FileList.vue @@ -31,9 +31,16 @@ import { useFileStore } from "@/stores/file"; import url from "@/utils/url"; import { files } from "@/api"; +import { StatusError } from "@/api/utils.js"; export default { name: "file-list", + props: { + exclude: { + type: Array, + default: () => [], + }, + }, data: function () { return { items: [], @@ -43,6 +50,7 @@ export default { }, selected: null, current: window.location.pathname, + nextAbortController: new AbortController(), }; }, inject: ["$showError"], @@ -56,7 +64,13 @@ export default { mounted() { this.fillOptions(this.req); }, + unmounted() { + this.abortOngoingNext(); + }, methods: { + abortOngoingNext() { + this.nextAbortController.abort(); + }, fillOptions(req) { // Sets the current path and resets // the current items. @@ -82,6 +96,7 @@ export default { // move options. for (const item of req.items) { if (!item.isDir) continue; + if (this.exclude?.includes(item.url)) continue; this.items.push({ name: item.name, @@ -94,8 +109,17 @@ export default { // just clicked in and fill the options with its // content. const uri = event.currentTarget.dataset.url; - - files.fetch(uri).then(this.fillOptions).catch(this.$showError); + this.abortOngoingNext(); + this.nextAbortController = new AbortController(); + files + .fetch(uri, this.nextAbortController.signal) + .then(this.fillOptions) + .catch((e) => { + if (e instanceof StatusError && e.is_canceled) { + return; + } + this.$showError(e); + }); }, touchstart(event) { const url = event.currentTarget.dataset.url; diff --git a/frontend/src/components/prompts/Help.vue b/frontend/src/components/prompts/Help.vue index a5c5d138..1d99eeed 100644 --- a/frontend/src/components/prompts/Help.vue +++ b/frontend/src/components/prompts/Help.vue @@ -11,7 +11,7 @@
  • DEL - {{ $t("help.del") }}
  • ESC - {{ $t("help.esc") }}
  • CTRL + S - {{ $t("help.ctrl.s") }}
  • -
  • CTRL + F - {{ $t("help.ctrl.f") }}
  • +
  • CTRL + SHIFT + F - {{ $t("help.ctrl.f") }}
  • CTRL + Click - {{ $t("help.ctrl.click") }}
  • Click - {{ $t("help.click") }}
  • Double click - {{ $t("help.doubleClick") }}
  • diff --git a/frontend/src/components/prompts/Move.vue b/frontend/src/components/prompts/Move.vue index 6591d09d..7b2fd615 100644 --- a/frontend/src/components/prompts/Move.vue +++ b/frontend/src/components/prompts/Move.vue @@ -8,6 +8,7 @@ @@ -76,6 +77,11 @@ export default { computed: { ...mapState(useFileStore, ["req", "selected"]), ...mapState(useAuthStore, ["user"]), + excludedFolders() { + return this.selected + .filter((idx) => this.req.items[idx].isDir) + .map((idx) => this.req.items[idx].url); + }, }, methods: { ...mapActions(useLayoutStore, ["showHover", "closeHovers"]), diff --git a/frontend/src/components/prompts/Share.vue b/frontend/src/components/prompts/Share.vue index 99c82f74..3ce2b1fd 100644 --- a/frontend/src/components/prompts/Share.vue +++ b/frontend/src/components/prompts/Share.vue @@ -32,16 +32,6 @@ content_paste - - -