Compare commits

...

42 Commits

Author SHA1 Message Date
Henrique Dias
849f5ad443 chore(release): 2.51.2 2025-12-07 08:12:35 +01:00
Henrique Dias
c1715992bd fix(frontend): add missing i18n strings 2025-12-07 07:47:43 +01:00
Henrique Dias
e4f2503298 chore(release): 2.51.1 2025-12-07 07:44:25 +01:00
Ariel Leyva
152f8302f7 fix: prevent the right-click from selecting multiple items when the "single-click" option is active (#5608) 2025-12-07 07:35:47 +01:00
Henrique Dias
4cbb4b73af fix(frontend): csv viewer i18n strings 2025-12-07 07:24:15 +01:00
Henrique Dias
58cc874828 chore(release): 2.51.0 2025-12-06 11:37:24 +01:00
Henrique Dias
124def5cd7 chore: dependency updates 2025-12-06 11:33:43 +01:00
transifex-integration[bot]
2d88c06761 feat: update translations
Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-12-06 11:23:24 +01:00
Ariel Leyva
204a3f0eea fix: added column separator select (comma, semicolon and both) in CSV viewer (#5604)
Co-authored-by: Henrique Dias <mail@hacdias.com>
2025-12-06 11:08:50 +01:00
Henrique Dias
f029c3005e refactor: cleanup package names (#5605) 2025-12-06 10:52:11 +01:00
Henrique Dias
a6934e40ff ci: run renovate on weekends 2025-12-05 15:11:22 +01:00
renovate[bot]
98662ac5ec chore(deps): update all non-major dependencies (#5600)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 08:47:00 +01:00
renovate[bot]
5cf8ce8db5 chore(deps): update dependency vite to v7.2.6 (#5598)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-01 08:38:14 +01:00
renovate[bot]
062dc414f8 chore(deps): update module github.com/shirou/gopsutil/v4 to v4.25.11 (#5597)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-01 07:37:25 +01:00
Henrique Dias
63582b644c chore: run linter 2025-11-30 09:17:35 +01:00
Henrique Dias
4302ece49b chore(release): 2.50.0 2025-11-30 09:08:15 +01:00
Henrique Dias
e1ee14d827 chore(docs): update CLI documentation 2025-11-30 09:07:56 +01:00
Henrique Dias
84ca722261 ci: use docs on GitHub Pages 2025-11-30 08:49:24 +01:00
jake-dog
b9ac45d5da feat: configurable logout page URL for proxy/hook auth (#3884)
Co-authored-by: Henrique Dias <mail@hacdias.com>
2025-11-30 08:44:34 +01:00
Henrique Dias
701522a060 fix: do not close editor if save failed
Closes #5591
2025-11-30 07:58:37 +01:00
transifex-integration[bot]
78e0395960 feat: update translations
Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-11-30 07:32:51 +01:00
renovate[bot]
f0680cf0f5 chore(deps): update dependency prettier to v3.7.3 (#5592)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-30 07:31:40 +01:00
Krishan Bhasin
982405ec94 feat: render CSVs as table (#5569)
Co-authored-by: Henrique Dias <mail@hacdias.com>
2025-11-29 10:45:11 +01:00
renovate[bot]
a78aaed214 chore(deps): update dependency prettier to v3.7.2 (#5589)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-29 10:25:22 +01:00
renovate[bot]
df11a7dd0e chore(deps): update all non-major dependencies (#5583)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-28 17:40:31 +01:00
renovate[bot]
79980bcf52 chore(deps): update all non-major dependencies to v14.1.0 (#5582)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-27 09:05:21 +01:00
renovate[bot]
3be134f23d chore(deps): update all non-major dependencies (#5578)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-26 13:56:59 +01:00
transifex-integration[bot]
279a5ccd1e feat: update frontend/src/i18n/hr.json
Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-11-26 13:56:45 +01:00
renovate[bot]
87f73ac982 chore(deps): update dependency vue to v3.5.25 (#5577)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-24 09:39:33 +01:00
renovate[bot]
85cde140ba chore(deps): update dependency vue-tsc to v3.1.5 (#5575)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-23 08:10:22 +01:00
Henrique Dias
119609c834 chore(release): 2.49.0 2025-11-22 17:15:51 +01:00
Kosmos
d48f5665d6 feat: add "copy download link to clipboard" button to Share prompt (#5173)
Co-authored-by: Henrique Dias <mail@hacdias.com>
2025-11-22 17:07:10 +01:00
transifex-integration[bot]
54306bdc87 feat: Updates for project File Browser (#5566)
Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-11-22 16:36:03 +01:00
renovate[bot]
33deedf559 chore(deps): update dependency vue-i18n to v11.2.1 (#5574)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-22 15:06:10 +01:00
renovate[bot]
88d1eecc4e chore(deps): update dependency eslint-plugin-vue to v10.6.0 (#5573)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-22 07:57:55 +01:00
renovate[bot]
43db19f8c8 chore(deps): update all non-major dependencies (#5571)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-20 18:57:20 +01:00
renovate[bot]
a360f26979 chore(deps): update actions/checkout action to v6 (#5572)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-20 18:54:40 +01:00
renovate[bot]
ab367a2740 chore(deps): update all non-major dependencies (#5567)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-20 09:58:37 +01:00
Henrique Dias
5df5508a85 chore: add govet, gocritic and revive 2025-11-20 07:56:56 +01:00
Brian Fromm
6d5aa355e4 fix: display friendly error message for password validation on signup (#5563)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-19 17:42:50 +01:00
renovate[bot]
a3b5584505 chore(deps): update dependency @vitejs/plugin-vue to v6.0.2 (#5564)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-19 08:14:22 +01:00
transifex-integration[bot]
8db2411cd4 feat: add Bulgarian language
Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
Co-authored-by: Henrique Dias <mail@hacdias.com>
2025-11-19 08:14:10 +01:00
96 changed files with 1752 additions and 668 deletions

View File

@@ -13,7 +13,7 @@ jobs:
name: Lint Frontend name: Lint Frontend
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
with: with:
package_json_file: "frontend/package.json" package_json_file: "frontend/package.json"
@@ -31,7 +31,7 @@ jobs:
name: Lint Backend name: Lint Backend
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
go-version: "1.25.x" go-version: "1.25.x"
@@ -43,7 +43,7 @@ jobs:
name: Test name: Test
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
with: with:
go-version: "1.25.x" go-version: "1.25.x"
@@ -53,7 +53,7 @@ jobs:
name: Build name: Build
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6
@@ -77,7 +77,7 @@ jobs:
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-go@v6 - uses: actions/setup-go@v6

View File

@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: Install Task - name: Install Task
@@ -28,25 +28,25 @@ jobs:
if: github.event_name == 'push' && github.ref == 'refs/heads/master' if: github.event_name == 'push' && github.ref == 'refs/heads/master'
name: Build and Release Docs name: Build and Release Docs
permissions: permissions:
contents: read pages: write
deployments: write id-token: write
pull-requests: write environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 5 timeout-minutes: 5
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: Install Task - name: Install Task
uses: go-task/setup-task@v1 uses: go-task/setup-task@v1
- name: Build site - name: Build site
run: task docs run: task docs
- name: Deploy to Cloudflare Pages - name: Upload static files as artifact
uses: cloudflare/wrangler-action@v3 uses: actions/upload-pages-artifact@v4
with: with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} path: www/public
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - name: Deploy to GitHub Pages
command: pages deploy www/public --project-name=${{ secrets.CLOUDFLARE_PROJECT_NAME }} uses: actions/deploy-pages@v4
gitHubToken: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -2,8 +2,13 @@ version: "2"
linters: linters:
default: standard default: standard
enable:
- gocritic
- govet
- revive
exclusions: exclusions:
presets: presets:
- std-error-handling - std-error-handling
- comments
paths: paths:
- frontend/ - frontend/

View File

@@ -2,6 +2,67 @@
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. 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.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) ## [2.48.2](https://github.com/filebrowser/filebrowser/compare/v2.48.1...v2.48.2) (2025-11-18)

View File

@@ -1,5 +1,5 @@
## Multistage build: First stage fetches dependencies ## 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 # install and copy ca-certificates, mailcap, and tini-static; download JSON.sh
RUN apk update && \ RUN apk update && \

View File

@@ -11,7 +11,7 @@ import (
"slices" "slices"
"strings" "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/files"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
@@ -146,7 +146,7 @@ func (a *HookAuth) GetValues(s string) {
// SaveUser updates the existing user or creates a new one when not found // SaveUser updates the existing user or creates a new one when not found
func (a *HookAuth) SaveUser() (*users.User, error) { func (a *HookAuth) SaveUser() (*users.User, error) {
u, err := a.Users.Get(a.Server.Root, a.Cred.Username) 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 return nil, err
} }

View File

@@ -4,7 +4,7 @@ import (
"errors" "errors"
"net/http" "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/settings"
"github.com/filebrowser/filebrowser/v2/users" "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) { 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) username := r.Header.Get(a.Header)
user, err := usr.Get(srv.Root, username) 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 a.createUser(usr, setting, srv, username)
} }
return user, err return user, err

View File

@@ -2,7 +2,7 @@ package cmd
import ( import (
"encoding/json" "encoding/json"
nerrors "errors" "errors"
"fmt" "fmt"
"os" "os"
"strings" "strings"
@@ -12,7 +12,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/filebrowser/filebrowser/v2/auth" "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" "github.com/filebrowser/filebrowser/v2/settings"
) )
@@ -45,6 +45,7 @@ func addConfigFlags(flags *pflag.FlagSet) {
flags.String("auth.method", string(auth.MethodJSONAuth), "authentication type") flags.String("auth.method", string(auth.MethodJSONAuth), "authentication type")
flags.String("auth.header", "", "HTTP header for auth.method=proxy") flags.String("auth.header", "", "HTTP header for auth.method=proxy")
flags.String("auth.command", "", "command for auth.method=hook") 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.host", "https://www.google.com", "use another host for ReCAPTCHA. recaptcha.net might be useful in China")
flags.String("recaptcha.key", "", "ReCaptcha site key") flags.String("recaptcha.key", "", "ReCaptcha site key")
@@ -103,7 +104,7 @@ func getProxyAuth(flags *pflag.FlagSet, defaultAuther map[string]interface{}) (a
} }
if header == "" { 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 return &auth.ProxyAuth{Header: header}, nil
@@ -162,7 +163,7 @@ func getHookAuth(flags *pflag.FlagSet, defaultAuther map[string]interface{}) (au
} }
if command == "" { 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 return &auth.HookAuth{Command: command}, nil
@@ -185,7 +186,7 @@ func getAuthentication(flags *pflag.FlagSet, defaults ...interface{}) (settings.
case auth.MethodHookAuth: case auth.MethodHookAuth:
auther, err = getHookAuth(flags, defaultAuther) auther, err = getHookAuth(flags, defaultAuther)
default: default:
return "", nil, errors.ErrInvalidAuthMethod return "", nil, fberrors.ErrInvalidAuthMethod
} }
if err != nil { if err != nil {
@@ -201,6 +202,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
fmt.Fprintf(w, "Sign up:\t%t\n", set.Signup) fmt.Fprintf(w, "Sign up:\t%t\n", set.Signup)
fmt.Fprintf(w, "Hide Login Button:\t%t\n", set.HideLoginButton) 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, "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, "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.Fprintf(w, "Shell:\t%s\t\n", strings.Join(set.Shell, " "))
@@ -328,6 +330,8 @@ func getSettings(flags *pflag.FlagSet, set *settings.Settings, ser *settings.Ser
set.DirMode, err = getAndParseFileMode(flags, flag.Name) set.DirMode, err = getAndParseFileMode(flags, flag.Name)
case "auth.method": case "auth.method":
hasAuth = true hasAuth = true
case "auth.logoutPage":
set.LogoutPage, err = flags.GetString(flag.Name)
case "branding.name": case "branding.name":
set.Branding.Name, err = flags.GetString(flag.Name) set.Branding.Name, err = flags.GetString(flag.Name)
case "branding.theme": case "branding.theme":
@@ -357,7 +361,7 @@ func getSettings(flags *pflag.FlagSet, set *settings.Settings, ser *settings.Ser
flags.Visit(visit) flags.Visit(visit)
} }
err := nerrors.Join(errs...) err := errors.Join(errs...)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -35,7 +35,7 @@ var docsCmd = &cobra.Command{
rootCmd.Root().DisableAutoGenTag = true rootCmd.Root().DisableAutoGenTag = true
err = doc.GenMarkdownTreeCustom(cmd.Root(), tempDir, func(f string) string { err = doc.GenMarkdownTreeCustom(cmd.Root(), tempDir, func(_ string) string {
return "" return ""
}, func(s string) string { }, func(s string) string {
return s return s

View File

@@ -54,7 +54,7 @@ var (
) )
// TODO(remove): remove after July 2026. // TODO(remove): remove after July 2026.
func migrateFlagNames(f *pflag.FlagSet, name string) pflag.NormalizedName { func migrateFlagNames(_ *pflag.FlagSet, name string) pflag.NormalizedName {
if newName, ok := flagNamesMigrations[name]; ok { if newName, ok := flagNamesMigrations[name]; ok {
if !warnedFlags[name] { if !warnedFlags[name] {
@@ -146,7 +146,7 @@ The precedence of the configuration values are as follows:
Also, if the database path doesn't exist, File Browser will enter into 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 the quick setup mode and a new database will be bootstrapped and a new
user created with the credentials from options "username" and "password".`, user created with the credentials from options "username" and "password".`,
RunE: withViperAndStore(func(cmd *cobra.Command, _ []string, v *viper.Viper, st *store) error { RunE: withViperAndStore(func(_ *cobra.Command, _ []string, v *viper.Viper, st *store) error {
if !st.databaseExisted { if !st.databaseExisted {
err := quickSetup(v, st.Storage) err := quickSetup(v, st.Storage)
if err != nil { if err != nil {

View File

@@ -160,13 +160,14 @@ func withViperAndStore(fn func(cmd *cobra.Command, args []string, v *viper.Viper
} }
exists, err := dbExists(path) exists, err := dbExists(path)
if err != nil { switch {
case err != nil:
return err return err
} else if exists && options.expectsNoDatabase { case exists && options.expectsNoDatabase:
log.Fatal(path + " already exists") log.Fatal(path + " already exists")
} else if !exists && !options.expectsNoDatabase && !options.allowsNoDatabase { case !exists && !options.expectsNoDatabase && !options.allowsNoDatabase:
log.Fatal(path + " does not exist. Please run 'filebrowser config init' first.") log.Fatal(path + " does not exist. Please run 'filebrowser config init' first.")
} else if !exists && !options.expectsNoDatabase { case !exists && !options.expectsNoDatabase:
log.Println("WARNING: filebrowser.db can't be found. Initialing in " + strings.TrimSuffix(path, "filebrowser.db")) log.Println("WARNING: filebrowser.db can't be found. Initialing in " + strings.TrimSuffix(path, "filebrowser.db"))
} }
@@ -193,7 +194,7 @@ func withViperAndStore(fn func(cmd *cobra.Command, args []string, v *viper.Viper
} }
func withStore(fn func(cmd *cobra.Command, args []string, store *store) error, options storeOptions) cobraFunc { func withStore(fn func(cmd *cobra.Command, args []string, store *store) error, options storeOptions) cobraFunc {
return withViperAndStore(func(cmd *cobra.Command, args []string, v *viper.Viper, store *store) error { return withViperAndStore(func(cmd *cobra.Command, args []string, _ *viper.Viper, store *store) error {
return fn(cmd, args, store) return fn(cmd, args, store)
}, options) }, options)
} }

View File

@@ -25,12 +25,12 @@ func TestFileCache(t *testing.T) {
// store new key // store new key
err := cache.Store(ctx, key, []byte(value)) err := cache.Store(ctx, key, []byte(value))
require.NoError(t, err) 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 // update existing key
err = cache.Store(ctx, key, []byte(newValue)) err = cache.Store(ctx, key, []byte(newValue))
require.NoError(t, err) 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 // delete key
err = cache.Delete(ctx, key) err = cache.Delete(ctx, key)
@@ -40,7 +40,7 @@ func TestFileCache(t *testing.T) {
require.False(t, exists) require.False(t, exists)
} }
func checkValue(t *testing.T, ctx context.Context, fs afero.Fs, fileFullPath string, cache *FileCache, key, wantValue string) { func checkValue(ctx context.Context, t *testing.T, fs afero.Fs, fileFullPath string, cache *FileCache, key, wantValue string) {
t.Helper() t.Helper()
// check actual file content // check actual file content
b, err := afero.ReadFile(fs, fileFullPath) b, err := afero.ReadFile(fs, fileFullPath)

View File

@@ -1,4 +1,4 @@
package errors package fberrors
import ( import (
"errors" "errors"

View File

@@ -23,7 +23,7 @@ import (
"github.com/spf13/afero" "github.com/spf13/afero"
fbErrors "github.com/filebrowser/filebrowser/v2/errors" fberrors "github.com/filebrowser/filebrowser/v2/errors"
"github.com/filebrowser/filebrowser/v2/rules" "github.com/filebrowser/filebrowser/v2/rules"
) )
@@ -168,7 +168,7 @@ func stat(opts *FileOptions) (*FileInfo, error) {
// algorithm. The checksums data is saved on File object. // algorithm. The checksums data is saved on File object.
func (i *FileInfo) Checksum(algo string) error { func (i *FileInfo) Checksum(algo string) error {
if i.IsDir { if i.IsDir {
return fbErrors.ErrIsDirectory return fberrors.ErrIsDirectory
} }
if i.Checksums == nil { if i.Checksums == nil {
@@ -193,7 +193,7 @@ func (i *FileInfo) Checksum(algo string) error {
case "sha512": case "sha512":
h = sha512.New() h = sha512.New()
default: default:
return fbErrors.ErrInvalidOption return fberrors.ErrInvalidOption
} }
_, err = io.Copy(h, reader) _, err = io.Copy(h, reader)

View File

@@ -39,6 +39,7 @@
DisableUsedPercentage: false, DisableUsedPercentage: false,
EnableExec: true, EnableExec: true,
EnableThumbs: true, EnableThumbs: true,
LogoutPage: "",
LoginPage: true, LoginPage: true,
Name: "", Name: "",
NoAuth: false, NoAuth: false,

View File

@@ -71,5 +71,5 @@
"vite-plugin-compression2": "^2.3.1", "vite-plugin-compression2": "^2.3.1",
"vue-tsc": "^3.1.3" "vue-tsc": "^3.1.3"
}, },
"packageManager": "pnpm@10.22.0+sha512.bf049efe995b28f527fd2b41ae0474ce29186f7edcb3bf545087bd61fbbebb2bf75362d1307fda09c2d288e1e499787ac12d4fcb617a974718a6051f2eee741c" "packageManager": "pnpm@10.24.0+sha512.01ff8ae71b4419903b65c60fb2dc9d34cf8bb6e06d03bde112ef38f7a34d6904c424ba66bea5cdcf12890230bf39f9580473140ed9c946fef328b6e5238a345a"
} }

897
frontend/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -129,6 +129,7 @@ import {
disableExternal, disableExternal,
disableUsedPercentage, disableUsedPercentage,
noAuth, noAuth,
logoutPage,
loginPage, loginPage,
} from "@/utils/constants"; } from "@/utils/constants";
import { files as api } from "@/api"; import { files as api } from "@/api";
@@ -159,7 +160,7 @@ export default {
version: () => version, version: () => version,
disableExternal: () => disableExternal, disableExternal: () => disableExternal,
disableUsedPercentage: () => disableUsedPercentage, disableUsedPercentage: () => disableUsedPercentage,
canLogout: () => !noAuth && loginPage, canLogout: () => !noAuth && (loginPage || logoutPage !== "/login"),
}, },
methods: { methods: {
...mapActions(useLayoutStore, ["closeHovers", "showHover"]), ...mapActions(useLayoutStore, ["closeHovers", "showHover"]),

View File

@@ -0,0 +1,252 @@
<template>
<div class="csv-viewer">
<div v-if="displayError" class="csv-error">
<i class="material-icons">error</i>
<p>{{ displayError }}</p>
</div>
<div v-else-if="data.headers.length === 0" class="csv-empty">
<i class="material-icons">description</i>
<p>{{ $t("files.lonely") }}</p>
</div>
<div v-else class="csv-table-container" @wheel.stop @touchmove.stop>
<table class="csv-table">
<thead>
<tr>
<th v-for="(header, index) in data.headers" :key="index">
{{ header || `Column ${index + 1}` }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, rowIndex) in data.rows" :key="rowIndex">
<td v-for="(cell, cellIndex) in row" :key="cellIndex">
{{ cell }}
</td>
</tr>
</tbody>
</table>
<div class="csv-footer">
<div class="csv-info" v-if="data.rows.length > 100">
<i class="material-icons">info</i>
<span>
{{ $t("files.showingRows", { count: data.rows.length }) }}</span
>
</div>
<div class="column-separator">
<label for="columnSeparator">{{ $t("files.columnSeparator") }}</label>
<select
id="columnSeparator"
class="input input--block"
v-model="columnSeparator"
>
<option :value="[',']">
{{ $t("files.csvSeparators.comma") }}
</option>
<option :value="[';']">
{{ $t("files.csvSeparators.semicolon") }}
</option>
<option :value="[',', ';']">
{{ $t("files.csvSeparators.both") }}
</option>
</select>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { parseCSV, type CsvData } from "@/utils/csv";
import { computed, ref } from "vue";
interface Props {
content: string;
error?: string;
}
const props = withDefaults(defineProps<Props>(), {
error: "",
});
const columnSeparator = ref([","]);
const data = computed<CsvData>(() => {
try {
return parseCSV(props.content, columnSeparator.value);
} catch (e) {
console.error("Failed to parse CSV:", e);
return { headers: [], rows: [] };
}
});
const displayError = computed(() => {
// External error takes priority (e.g., file too large)
if (props.error) {
return props.error;
}
// Check for parse errors
if (
props.content &&
props.content.trim().length > 0 &&
data.value.headers.length === 0
) {
return "Failed to parse CSV file";
}
return null;
});
</script>
<style scoped>
.csv-viewer {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background-color: var(--surfacePrimary);
color: var(--textSecondary);
padding: 1rem;
padding-top: 4em;
box-sizing: border-box;
}
.csv-error,
.csv-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
gap: 1rem;
color: var(--textPrimary);
}
.csv-error i,
.csv-empty i {
font-size: 4rem;
opacity: 0.5;
}
.csv-error p,
.csv-empty p {
font-size: 1.1rem;
margin: 0;
}
.csv-table-container {
flex: 1;
overflow: auto;
background-color: var(--surfacePrimary);
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
/* Scrollbar styling for better visibility */
.csv-table-container::-webkit-scrollbar {
width: 12px;
height: 12px;
}
.csv-table-container::-webkit-scrollbar-track {
background: var(--background);
border-radius: 4px;
}
.csv-table-container::-webkit-scrollbar-thumb {
background: var(--borderSecondary);
border-radius: 4px;
}
.csv-table-container::-webkit-scrollbar-thumb:hover {
background: var(--textPrimary);
}
.csv-table {
width: 100%;
border-collapse: collapse;
font-size: 0.875rem;
background-color: var(--surfacePrimary);
}
.csv-table thead {
position: sticky;
top: 0;
z-index: 10;
background-color: var(--surfaceSecondary);
}
.csv-table th {
padding: 0.875rem 1rem;
text-align: left;
font-weight: 600;
border-bottom: 2px solid var(--borderSecondary);
background-color: var(--surfaceSecondary);
white-space: nowrap;
color: var(--textSecondary);
font-size: 0.875rem;
}
.csv-table td {
padding: 0.75rem 1rem;
text-align: left;
border-bottom: 1px solid var(--borderPrimary);
white-space: nowrap;
max-width: 400px;
overflow: hidden;
text-overflow: ellipsis;
color: var(--textSecondary);
}
.csv-table tbody tr:nth-child(even) {
background-color: var(--background);
}
.csv-table tbody tr:hover {
background-color: var(--hover);
transition: background-color 0.15s ease;
}
.csv-footer {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
padding: 0.5rem;
}
.csv-footer > :only-child {
margin-left: auto;
}
.csv-info {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1rem;
margin-top: 0.5rem;
background-color: var(--surfaceSecondary);
border-radius: 4px;
border-left: 3px solid var(--blue);
color: var(--textSecondary);
font-size: 0.875rem;
}
.column-separator {
display: flex;
align-items: center;
gap: 0.5rem;
}
.column-separator > label {
font-size: small;
text-align: end;
}
.column-separator > select {
margin-bottom: 0;
}
.csv-info i {
font-size: 1.2rem;
color: var(--blue);
}
</style>

View File

@@ -265,7 +265,15 @@ const click = (event: Event | KeyboardEvent) => {
} }
if (fileStore.selected.indexOf(props.index) !== -1) { 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; return;
} }
@@ -291,7 +299,6 @@ const click = (event: Event | KeyboardEvent) => {
} }
if ( if (
!singleClick.value &&
!(event as KeyboardEvent).ctrlKey && !(event as KeyboardEvent).ctrlKey &&
!(event as KeyboardEvent).metaKey && !(event as KeyboardEvent).metaKey &&
!fileStore.multiple !fileStore.multiple

View File

@@ -12,6 +12,7 @@
<th>{{ $t("settings.shareDuration") }}</th> <th>{{ $t("settings.shareDuration") }}</th>
<th></th> <th></th>
<th></th> <th></th>
<th></th>
</tr> </tr>
<tr v-for="link in links" :key="link.hash"> <tr v-for="link in links" :key="link.hash">
@@ -24,7 +25,7 @@
</td> </td>
<td class="small"> <td class="small">
<button <button
class="action copy-clipboard" class="action"
:aria-label="$t('buttons.copyToClipboard')" :aria-label="$t('buttons.copyToClipboard')"
:title="$t('buttons.copyToClipboard')" :title="$t('buttons.copyToClipboard')"
@click="copyToClipboard(buildLink(link))" @click="copyToClipboard(buildLink(link))"
@@ -32,6 +33,17 @@
<i class="material-icons">content_paste</i> <i class="material-icons">content_paste</i>
</button> </button>
</td> </td>
<td class="small">
<button
class="action"
:aria-label="$t('buttons.copyDownloadLinkToClipboard')"
:title="$t('buttons.copyDownloadLinkToClipboard')"
:disabled="!!link.password_hash"
@click="copyToClipboard(buildDownloadLink(link))"
>
<i class="material-icons">content_paste_go</i>
</button>
</td>
<td class="small"> <td class="small">
<button <button
class="action" class="action"
@@ -132,7 +144,7 @@
<script> <script>
import { mapActions, mapState } from "pinia"; import { mapActions, mapState } from "pinia";
import { useFileStore } from "@/stores/file"; import { useFileStore } from "@/stores/file";
import { share as api } from "@/api"; import * as api from "@/api/index";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { useLayoutStore } from "@/stores/layout"; import { useLayoutStore } from "@/stores/layout";
import { copy } from "@/utils/clipboard"; import { copy } from "@/utils/clipboard";
@@ -172,7 +184,7 @@ export default {
}, },
async beforeMount() { async beforeMount() {
try { try {
const links = await api.get(this.url); const links = await api.share.get(this.url);
this.links = links; this.links = links;
this.sort(); this.sort();
@@ -211,9 +223,14 @@ export default {
let res = null; let res = null;
if (!this.time) { if (!this.time) {
res = await api.create(this.url, this.password); res = await api.share.create(this.url, this.password);
} else { } else {
res = await api.create(this.url, this.password, this.time, this.unit); res = await api.share.create(
this.url,
this.password,
this.time,
this.unit
);
} }
this.links.push(res); this.links.push(res);
@@ -231,7 +248,7 @@ export default {
deleteLink: async function (event, link) { deleteLink: async function (event, link) {
event.preventDefault(); event.preventDefault();
try { try {
await api.remove(link.hash); await api.share.remove(link.hash);
this.links = this.links.filter((item) => item.hash !== link.hash); this.links = this.links.filter((item) => item.hash !== link.hash);
if (this.links.length == 0) { if (this.links.length == 0) {
@@ -245,7 +262,16 @@ export default {
return dayjs(time * 1000).fromNow(); return dayjs(time * 1000).fromNow();
}, },
buildLink(share) { buildLink(share) {
return api.getShareURL(share); return api.share.getShareURL(share);
},
buildDownloadLink(share) {
return api.pub.getDownloadURL(
{
hash: share.hash,
path: "",
},
true
);
}, },
sort() { sort() {
this.links = this.links.sort((a, b) => { this.links = this.links.sort((a, b) => {

View File

@@ -15,10 +15,8 @@ export default {
data() { data() {
const dataObj = {}; const dataObj = {};
const locales = { const locales = {
he: "עברית",
hr: "Hrvatski",
hu: "Magyar",
ar: "العربية", ar: "العربية",
bg: "български език",
ca: "Català", ca: "Català",
cs: "Čeština", cs: "Čeština",
de: "Deutsch", de: "Deutsch",
@@ -26,15 +24,18 @@ export default {
en: "English", en: "English",
es: "Español", es: "Español",
fr: "Français", fr: "Français",
he: "עברית",
hr: "Hrvatski",
hu: "Magyar",
is: "Icelandic", is: "Icelandic",
it: "Italiano", it: "Italiano",
ja: "日本語", ja: "日本語",
ko: "한국어", ko: "한국어",
"nl-be": "Dutch (Belgium)",
no: "Norsk", no: "Norsk",
"nl-be": "Dutch (Belgium)",
pl: "Polski", pl: "Polski",
"pt-br": "Português", "pt-br": "Português (Brasil)",
pt: "Português (Brasil)", pt: "Português (Portugal)",
ro: "Romanian", ro: "Romanian",
ru: "Русский", ru: "Русский",
sk: "Slovenčina", sk: "Slovenčina",

View File

@@ -98,7 +98,8 @@ main .spinner .bounce2 {
position: relative; position: relative;
} }
.action.disabled { .action.disabled,
.action[disabled] {
opacity: 0.2; opacity: 0.2;
cursor: not-allowed; cursor: not-allowed;
} }
@@ -460,4 +461,4 @@ html[dir="rtl"] .card-content .small + input {
html[dir="rtl"] .card.floating .card-content .file-list { html[dir="rtl"] .card.floating .card-content .file-list {
direction: ltr; direction: ltr;
text-align: left; text-align: left;
} }

View File

@@ -43,7 +43,8 @@
"upload": "رفع", "upload": "رفع",
"openFile": "فتح الملف", "openFile": "فتح الملف",
"discardChanges": "إلغاء التغييرات", "discardChanges": "إلغاء التغييرات",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "تحميل الملف", "downloadFile": "تحميل الملف",
@@ -75,7 +76,9 @@
"sortByLastModified": "الترتيب بآخر تعديل", "sortByLastModified": "الترتيب بآخر تعديل",
"sortByName": "الترتيب باﻹسم", "sortByName": "الترتيب باﻹسم",
"sortBySize": "الترتيب بالحجم", "sortBySize": "الترتيب بالحجم",
"noPreview": "لا يوجد عرض مسبق لهذا الملف." "noPreview": "لا يوجد عرض مسبق لهذا الملف.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "حدد الملف أو المجلد", "click": "حدد الملف أو المجلد",
@@ -102,6 +105,7 @@
"username": "إسم المستخدم", "username": "إسم المستخدم",
"usernameTaken": "إسم المستخدم غير متاح", "usernameTaken": "إسم المستخدم غير متاح",
"wrongCredentials": "بيانات دخول خاطئة", "wrongCredentials": "بيانات دخول خاطئة",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "دقائق", "minutes": "دقائق",
"seconds": "ثواني", "seconds": "ثواني",
"unit": "وحدة الوقت" "unit": "وحدة الوقت"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

281
frontend/src/i18n/bg.json Normal file
View File

@@ -0,0 +1,281 @@
{
"buttons": {
"cancel": "Отмени",
"clear": "Изчисти",
"close": "Затвори",
"continue": "Продължи",
"copy": "Копирай",
"copyFile": "Копирай файл",
"copyToClipboard": "Копирай в клипборда",
"copyDownloadLinkToClipboard": "Копирай линк за сваляне в клипборда",
"create": "Създай",
"delete": "Изтрий",
"download": "Свали",
"file": "Файл",
"folder": "Папка",
"fullScreen": "Превключване на цял екран",
"hideDotfiles": "Скрий файлове започващи с точка",
"info": "Информация",
"more": "Повече",
"move": "Премести",
"moveFile": "Премести файл",
"new": "Нов",
"next": "Следващ",
"ok": "Потвърди",
"permalink": "Вземи постоянен линк",
"previous": "Предишен",
"preview": "Преглед",
"publish": "Публикуване",
"rename": "Преименуване",
"replace": "Замяна",
"reportIssue": "Докладвай проблем",
"save": "Запис",
"schedule": "График",
"search": "Търсене",
"select": "Избери",
"selectMultiple": "Избери няколко",
"share": "Сподели",
"shell": "Превключване на терминала",
"submit": "Изпрати",
"switchView": "Смени изгледа",
"toggleSidebar": "Превключване на страничен панел",
"update": "Обнови",
"upload": "Качи",
"openFile": "Отвори файл",
"discardChanges": "Изчисти",
"saveChanges": "Запиши промените",
"editAsText": "Edit as Text"
},
"download": {
"downloadFile": "Свали файл",
"downloadFolder": "Свали папка",
"downloadSelected": "Свали избраното"
},
"upload": {
"abortUpload": "Сигурни ли сте, че искате да прекратите?"
},
"errors": {
"forbidden": "Нямате право на достъп.",
"internal": "Взникна грешка.",
"notFound": "Локацията не може да бъде достигната.",
"connection": "Сървъра не може да бъде достигнат."
},
"files": {
"body": "Тяло",
"closePreview": "Затвори прегледа",
"files": "Файлове",
"folders": "Папки",
"home": "Начало",
"lastModified": "Последна промяна",
"loading": "Зареждане ...",
"lonely": "Тук е самотно ...",
"metadata": "Метаданни",
"multipleSelectionEnabled": "Множествения избор е разрешен",
"name": "Име",
"size": "Размер",
"sortByLastModified": "Подредба по последна промяна",
"sortByName": "Подредба по име",
"sortBySize": "Подредба по размер",
"noPreview": "За този файл не е наличен преглед.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
},
"help": {
"click": "избери файл или директория",
"ctrl": {
"click": "избери файлове или директории",
"f": "отваря търсене",
"s": "запиши файл или свари директория тук"
},
"del": "изтрий избраните",
"doubleClick": "отвори файл или директория",
"esc": "изтрий избраното и/или затвори",
"f1": "тази информация",
"f2": "преименувай файл",
"help": "Помощ"
},
"login": {
"createAnAccount": "Създай акаунт",
"loginInstead": "Вече имаш акаунт",
"password": "Парола",
"passwordConfirm": "Парола Потвърждение",
"passwordsDontMatch": "Паролите не съвпадат",
"signup": "Абониране",
"submit": "Вход",
"username": "Потребителско име",
"usernameTaken": "Потребителското име вече се използва",
"wrongCredentials": "Грешни потребителско име и/или парола",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": {
"inactivity": "Бяхте разлогнати поради неактивност"
}
},
"permanent": "Постоянен",
"prompts": {
"copy": "Копирай",
"copyMessage": "Избери къде да копираш файловете си:",
"currentlyNavigating": "В момента навигира към:",
"deleteMessageMultiple": "Сигурни ли сте, че искате да изтриете {count} файл(а)?",
"deleteMessageSingle": "Сигурни ли сте, че искате да изтриете този файл/папка?",
"deleteMessageShare": "Сигурни ли сте, че искате на изтриете това споделяне({path})?",
"deleteUser": "Сигурни ли сте, че искате да изтриете този потребител?",
"deleteTitle": "Изтрий файлове",
"displayName": "Име за показване:",
"download": "Свали файлове",
"downloadMessage": "Изберете формата, в който искате да свалите.",
"error": "Възникна грешка",
"fileInfo": "Информация за файла",
"filesSelected": "Избрани са {count} файла.",
"lastModified": "Последна промяна",
"move": "Премести",
"moveMessage": "Избери ново място за вашите файл(ове)/папк(а/и):",
"newArchetype": "Създай нова публикация базирана на шаблон. Вашия файл ще бъде създаден в папката за съдържание.",
"newDir": "Нова директория",
"newDirMessage": "Именувайте вашата нова директория.",
"newFile": "Нов файл",
"newFileMessage": "Именувайте вашия нов файл.",
"numberDirs": "Брой на директорийте",
"numberFiles": "Брой на файловете",
"rename": "Преименувай",
"renameMessage": "Вмъкни ново име за",
"replace": "Замени",
"replaceMessage": "Файл, които се опитвате да качите има конфликтно име. Искате ли да го пропуснете и да продължите качването или да замените съществуващия файл?\n",
"schedule": "График",
"scheduleMessage": "Изберете дата и час за публикуване на тази публикация.",
"show": "Покажи",
"size": "Размер",
"upload": "Качване",
"uploadFiles": "Качване на {files} файла...",
"uploadMessage": "Изберете опция за качване.",
"optionalPassword": "Опционална парола",
"resolution": "Резолюция",
"discardEditorChanges": "Сигурни ли сте, че искате да откажете направените промени?"
},
"search": {
"images": "Изображения",
"music": "Музика",
"pdf": "PDF",
"pressToSearch": "За търсене, натиснете Enter ...",
"search": "Търсене ...",
"typeToSearch": "Пишете за търсене ...",
"types": "Типове",
"video": "Видео"
},
"settings": {
"aceEditorTheme": "Тема на \"Ace редактор\"",
"admin": "Админ",
"administrator": "Администратор",
"allowCommands": "Изпълни команди",
"allowEdit": "Редактира, преименува и изтрива файлове и директории",
"allowNew": "Създава нови файлове и директорий",
"allowPublish": "Публикува нови публикации и страници",
"allowSignup": "Разреши потребителите да се абонират",
"hideLoginButton": "Скрий логин бутона от публичните страници",
"avoidChanges": "(остави празно за да избегнеш промени)",
"branding": "Брандиране",
"brandingDirectoryPath": "Брандиране - път до директория",
"brandingHelp": "Можете да настроите как изглежда вашия File Browser, като промените името и логото му, да добавите стилове и дори да забраните външни линкове към GitHub.\nЗа повече информация за бандиране се обърнете към {0}",
"changePassword": "Промени парола",
"commandRunner": "Изпълнение на команди",
"commandRunnerHelp": "Тук можете да зададете команди, които да се изпълнят при определени събития. Пишете по една команда на ред. Системните променливите {0} и {1} ще са на разположение, като {0} е релативна на {1}. За повече информация относно тази възможност и наличните системни променливи, моля прочетете {2}.",
"commandsUpdated": "Командите са запаметени!",
"createUserDir": "Създай автоматично собствена директория на потребителя, когато го добавяш.",
"minimumPasswordLength": "Минимална дължина на паролата",
"tusUploads": "Качване на части",
"tusUploadsHelp": "File Browser поддържа качване на части, което позволява съзадавнето на ефективно, надеждно, и възобновяемо качване на части дори и при ненадеждна мрежа.",
"tusUploadsChunkSize": "Максимален размер на заявката (за малки качвания ще бъдат използвано директо качване). Можете да въведете цяло число, което означава размера на данните в байтове, или пък текст от вида на 10MB, 1GB и т.н..",
"tusUploadsRetryCount": "Брой повторения, когато част от файл не се качи успешно.",
"userHomeBasePath": "Основен път до личните директории на потребителите",
"userScopeGenerationPlaceholder": "Обхватът ще бъде автоматично генериран",
"createUserHomeDirectory": "Създай лична директория на потребителя",
"customStylesheet": "Потребителски Стилове",
"defaultUserDescription": "Настройки по подразбиране за нови потребители.",
"disableExternalLinks": "Забрани външните връзки (с изключение на документацията)",
"disableUsedDiskPercentage": "Забрани графиката за използване на диска",
"documentation": "документация",
"examples": "Примери",
"executeOnShell": "Изпълни в шела",
"executeOnShellDescription": "По подразбиране, File Browser изпълнява командите директно. Ако искате да ги изпълните в сесия (като Bash или PowerShell), можете да я дефинирате тук заедно с необходимите аргументи и флагове. Ако това е зададено, командата която изпълните ще бъде добавена като аргумент. Това се отнася както за потребителски команди, така и за обработка на събития.",
"globalRules": "Това е общия списък от правила за разрешения или забрани. Те са приложими за всеки потребител. Можете да дефинирате специфични правила в настройките на всеки потребител, които ще имат приоритет над общите.",
"globalSettings": "Глобални Настройки",
"hideDotfiles": "Скрий фаловете започващи с точка",
"insertPath": "Вмъкни пътя",
"insertRegex": "Вмъкни регулярен израз",
"instanceName": "Име на инстанцията",
"language": "Език",
"lockPassword": "Забрани на потребителя да променя паролата",
"newPassword": "Вашата нова парола",
"newPasswordConfirm": "Потвърди вашата нова парола",
"newUser": "Нов потребител",
"password": "Парола",
"passwordUpdated": "Паролата е променена!",
"path": "Път",
"perm": {
"create": "Създаване на файлове и директорий",
"delete": "Изтриване на файлове и директорий",
"download": "Сваляне",
"execute": "Изпълни команди",
"modify": "Редактирай файлове",
"rename": "Преименувай или премести файлове и директорий",
"share": "Сподели файлове"
},
"permissions": "Разрешения",
"permissionsHelp": "Можете да зададете потребител да бъде администратор или да изберете разрешения индивидуално. Ако изберете \"Администратор\" всички други опции ще бъдат автоматично отметнати. Управлението на потребителите е привилегия на администратор.\n",
"profileSettings": "Настройки на Профила",
"ruleExample1": "предотвратете достъпа до всеки файл започващ с точка (като .git, .gitignore) във всяка папка.\n",
"ruleExample2": "блокира достъпа до файл именуван Caddyfile поставен в началото за обхвата.",
"rules": "Правила",
"rulesHelp": "Тук можете да дефинирате списък от правила за разрешаване и забрана за точно този потребител. Блокираните файлове няма да се покажат в списъците и няма да са достъпни за потребителя. Поддържаме регулярни изрази и пътища релативни на обхвата.\n",
"scope": "Обхват",
"setDateFormat": "Задайте точен формат на дата",
"settingsUpdated": "Настройките са обновени!",
"shareDuration": "Продължителност на споделянето",
"shareManagement": "Управление на споделянето",
"shareDeleted": "Споделянето е премахнато!",
"singleClick": "Използвайте единичен клик за да отворите файлове или директорий",
"themes": {
"default": "Настройки по подразбиране",
"dark": "Тъмна",
"light": "Светла",
"title": "Тема"
},
"user": "Потребител",
"userCommands": "Команди",
"userCommandsHelp": "Списък разделен с интервал от наличните команди за този потребител.\n",
"userCreated": "Потребителя е създаден!",
"userDefaults": "Настройки по подразбиране на потребителя",
"userDeleted": "Потребителя е изтрит!",
"userManagement": "Управление на потребители",
"userUpdated": "Потребителя е обновен!",
"username": "Потребителско име",
"users": "Потребители"
},
"sidebar": {
"help": "Помощ",
"hugoNew": "Hugo New",
"login": "Вход",
"logout": "Изход",
"myFiles": "Моите файлове",
"newFile": "Нов файл",
"newFolder": "Нова папка",
"preview": "Преглед",
"settings": "Настройки",
"signup": "Абониране",
"siteSettings": "Настройки на сървъра"
},
"success": {
"linkCopied": "Връзката е копирана!"
},
"time": {
"days": "Дни",
"hours": "Часове",
"minutes": "Минути",
"seconds": "Секунди",
"unit": "Единица за време"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
}
}

View File

@@ -43,7 +43,8 @@
"upload": "Pujar", "upload": "Pujar",
"openFile": "Obrir fitxer", "openFile": "Obrir fitxer",
"discardChanges": "Descartar", "discardChanges": "Descartar",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Descarregar fitxer", "downloadFile": "Descarregar fitxer",
@@ -75,7 +76,9 @@
"sortByLastModified": "Ordenar per última modificació", "sortByLastModified": "Ordenar per última modificació",
"sortByName": "Ordenar per nom", "sortByName": "Ordenar per nom",
"sortBySize": "Ordenar per mida", "sortBySize": "Ordenar per mida",
"noPreview": "La vista prèvia no està disponible per a aquest fitxer." "noPreview": "La vista prèvia no està disponible per a aquest fitxer.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "seleccionar fitxer o carpeta", "click": "seleccionar fitxer o carpeta",
@@ -102,6 +105,7 @@
"username": "Usuari", "username": "Usuari",
"usernameTaken": "Nom d'usuari no disponible", "usernameTaken": "Nom d'usuari no disponible",
"wrongCredentials": "Usuari i/o contrasenya incorrectes", "wrongCredentials": "Usuari i/o contrasenya incorrectes",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "Minuts", "minutes": "Minuts",
"seconds": "Segons", "seconds": "Segons",
"unit": "Unitat" "unit": "Unitat"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Nahrát", "upload": "Nahrát",
"openFile": "Otevřít soubor", "openFile": "Otevřít soubor",
"discardChanges": "Zrušit změny", "discardChanges": "Zrušit změny",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Stáhnout soubor", "downloadFile": "Stáhnout soubor",
@@ -75,7 +76,9 @@
"sortByLastModified": "Seřadit podle poslední změny", "sortByLastModified": "Seřadit podle poslední změny",
"sortByName": "Seřadit podle názvu", "sortByName": "Seřadit podle názvu",
"sortBySize": "Seřadit podle velikosti", "sortBySize": "Seřadit podle velikosti",
"noPreview": "Náhled pro tento soubor není k dispozici." "noPreview": "Náhled pro tento soubor není k dispozici.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "vyberte soubor nebo adresář", "click": "vyberte soubor nebo adresář",
@@ -102,6 +105,7 @@
"username": "Uživatelské jméno", "username": "Uživatelské jméno",
"usernameTaken": "Uživatelské jméno již existuje", "usernameTaken": "Uživatelské jméno již existuje",
"wrongCredentials": "Nesprávné přihlašovací údaje", "wrongCredentials": "Nesprávné přihlašovací údaje",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "Minuty", "minutes": "Minuty",
"seconds": "Sekundy", "seconds": "Sekundy",
"unit": "Časová jednotka" "unit": "Časová jednotka"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Upload", "upload": "Upload",
"openFile": "Datei öffnen", "openFile": "Datei öffnen",
"discardChanges": "Verwerfen", "discardChanges": "Verwerfen",
"saveChanges": "Save changes" "saveChanges": "Änderungen speichern",
"editAsText": "Als Text bearbeiten"
}, },
"download": { "download": {
"downloadFile": "Download Datei", "downloadFile": "Download Datei",
@@ -75,7 +76,9 @@
"sortByLastModified": "Nach Änderungsdatum sortieren", "sortByLastModified": "Nach Änderungsdatum sortieren",
"sortByName": "Nach Namen sortieren", "sortByName": "Nach Namen sortieren",
"sortBySize": "Nach Größe sortieren", "sortBySize": "Nach Größe sortieren",
"noPreview": "Für diese Datei ist keine Vorschau verfügbar." "noPreview": "Für diese Datei ist keine Vorschau verfügbar.",
"csvTooLarge": "Die CSV-Datei ist zu groß für die Vorschau (>5 MB). Bitte herunterladen, um sie anzuzeigen.",
"csvLoadFailed": "Fehler beim Laden der CSV-Datei."
}, },
"help": { "help": {
"click": "Wähle Datei oder Ordner", "click": "Wähle Datei oder Ordner",
@@ -102,8 +105,9 @@
"username": "Benutzername", "username": "Benutzername",
"usernameTaken": "Benutzername ist bereits vergeben", "usernameTaken": "Benutzername ist bereits vergeben",
"wrongCredentials": "Falsche Zugangsdaten", "wrongCredentials": "Falsche Zugangsdaten",
"passwordTooShort": "Passwort muss mindestens {min} Zeichen lang sein",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "Du wurdest aufgrund von Inaktivität abgemeldet."
} }
}, },
"permanent": "Permanent", "permanent": "Permanent",
@@ -158,7 +162,7 @@
"video": "Video" "video": "Video"
}, },
"settings": { "settings": {
"aceEditorTheme": "Ace editor theme", "aceEditorTheme": "Ace Editor Theme",
"admin": "Admin", "admin": "Admin",
"administrator": "Administrator", "administrator": "Administrator",
"allowCommands": "Befehle ausführen", "allowCommands": "Befehle ausführen",
@@ -166,7 +170,7 @@
"allowNew": "Erstellen neuer Dateien und Ordner", "allowNew": "Erstellen neuer Dateien und Ordner",
"allowPublish": "Veröffentlichen von neuen Beiträgen und Seiten", "allowPublish": "Veröffentlichen von neuen Beiträgen und Seiten",
"allowSignup": "Erlaube Benutzern sich zu registrieren", "allowSignup": "Erlaube Benutzern sich zu registrieren",
"hideLoginButton": "Hide the login button from public pages", "hideLoginButton": "Den Login-Button auf öffentlichen Seiten ausblenden",
"avoidChanges": "(leer lassen, um Änderungen zu vermeiden)", "avoidChanges": "(leer lassen, um Änderungen zu vermeiden)",
"branding": "Design", "branding": "Design",
"brandingDirectoryPath": "Designverzeichnispfad", "brandingDirectoryPath": "Designverzeichnispfad",
@@ -176,7 +180,7 @@
"commandRunnerHelp": "Hier könne Sie Befehle eintragen, welche bei den benannten Aktionen ausgeführt werden. Sie müssen pro Zeile jeweils einen Befehl eingeben. Die Umgebungsvariable {0} und {1} sind verfügbar, wobei {0} relative zu {1} ist. Für mehr Informationen über diese Funktion und die verfügbaren Umgebungsvariablen lesen Sie bitte die {2}.", "commandRunnerHelp": "Hier könne Sie Befehle eintragen, welche bei den benannten Aktionen ausgeführt werden. Sie müssen pro Zeile jeweils einen Befehl eingeben. Die Umgebungsvariable {0} und {1} sind verfügbar, wobei {0} relative zu {1} ist. Für mehr Informationen über diese Funktion und die verfügbaren Umgebungsvariablen lesen Sie bitte die {2}.",
"commandsUpdated": "Befehle aktualisiert!", "commandsUpdated": "Befehle aktualisiert!",
"createUserDir": "Automatisches Erstellen des Home-Verzeichnisses beim Anlegen neuer Benutzer", "createUserDir": "Automatisches Erstellen des Home-Verzeichnisses beim Anlegen neuer Benutzer",
"minimumPasswordLength": "Minimum password length", "minimumPasswordLength": "Mindestlänge für Passwörter",
"tusUploads": "Gestückelter Upload", "tusUploads": "Gestückelter Upload",
"tusUploadsHelp": "File Browser unterstützt das Hochladen von gestückelten Dateien und ermöglicht so einen effizienten, zuverlässigen, fortsetzbaren und gestückelten Datei-Upload auch in unzuverlässigen Netzwerken.", "tusUploadsHelp": "File Browser unterstützt das Hochladen von gestückelten Dateien und ermöglicht so einen effizienten, zuverlässigen, fortsetzbaren und gestückelten Datei-Upload auch in unzuverlässigen Netzwerken.",
"tusUploadsChunkSize": "Gibt die maximale Größe pro Anfrage an (direkte Uploads werden für kleinere Uploads verwendet). Bitte geben Sie eine Byte-Angabe oder eine Zeichenfolge wie 10 MB, 1 GB usw. an", "tusUploadsChunkSize": "Gibt die maximale Größe pro Anfrage an (direkte Uploads werden für kleinere Uploads verwendet). Bitte geben Sie eine Byte-Angabe oder eine Zeichenfolge wie 10 MB, 1 GB usw. an",
@@ -268,5 +272,10 @@
"minutes": "Minuten", "minutes": "Minuten",
"seconds": "Sekunden", "seconds": "Sekunden",
"unit": "Zeiteinheit" "unit": "Zeiteinheit"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Μεταφόρτωση", "upload": "Μεταφόρτωση",
"openFile": "Άνοιγμα αρχείου", "openFile": "Άνοιγμα αρχείου",
"discardChanges": "Discard", "discardChanges": "Discard",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Λήψη αρχείου", "downloadFile": "Λήψη αρχείου",
@@ -75,7 +76,9 @@
"sortByLastModified": "Ταξινόμηση κατά πρόσφατη τροποποίηση", "sortByLastModified": "Ταξινόμηση κατά πρόσφατη τροποποίηση",
"sortByName": "Ταξινόμηση κατά όνομα", "sortByName": "Ταξινόμηση κατά όνομα",
"sortBySize": "Ταξινόμηση κατά μέγεθος", "sortBySize": "Ταξινόμηση κατά μέγεθος",
"noPreview": "Η προεπισκόπηση δεν είναι διαθέσιμη για αυτό το αρχείο." "noPreview": "Η προεπισκόπηση δεν είναι διαθέσιμη για αυτό το αρχείο.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "επιλέξτε αρχείο ή φάκελο", "click": "επιλέξτε αρχείο ή φάκελο",
@@ -102,6 +105,7 @@
"username": "Όνομα χρήστη", "username": "Όνομα χρήστη",
"usernameTaken": "Το όνομα χρήστη χρησιμοποιείται ήδη", "usernameTaken": "Το όνομα χρήστη χρησιμοποιείται ήδη",
"wrongCredentials": "Λάθος όνομα ή/και κωδικός πρόσβασης", "wrongCredentials": "Λάθος όνομα ή/και κωδικός πρόσβασης",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "Λεπτά", "minutes": "Λεπτά",
"seconds": "Δευτερόλεπτα", "seconds": "Δευτερόλεπτα",
"unit": "Μονάδα χρόνου" "unit": "Μονάδα χρόνου"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,10 @@
"upload": "Upload", "upload": "Upload",
"openFile": "Open file", "openFile": "Open file",
"discardChanges": "Discard", "discardChanges": "Discard",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
}, },
"download": { "download": {
"downloadFile": "Download File", "downloadFile": "Download File",
@@ -75,7 +78,16 @@
"sortByLastModified": "Sort by last modified", "sortByLastModified": "Sort by last modified",
"sortByName": "Sort by name", "sortByName": "Sort by name",
"sortBySize": "Sort by size", "sortBySize": "Sort by size",
"noPreview": "Preview is not available for this file." "noPreview": "Preview is not available for this file.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file.",
"showingRows": "Showing {count} row(s)",
"columnSeparator": "Column Separator",
"csvSeparators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
}
}, },
"help": { "help": {
"click": "select file or directory", "click": "select file or directory",
@@ -102,6 +114,7 @@
"username": "Username", "username": "Username",
"usernameTaken": "Username already taken", "usernameTaken": "Username already taken",
"wrongCredentials": "Wrong credentials", "wrongCredentials": "Wrong credentials",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }

View File

@@ -43,7 +43,8 @@
"upload": "Subir", "upload": "Subir",
"openFile": "Abrir archivo", "openFile": "Abrir archivo",
"discardChanges": "Discard", "discardChanges": "Discard",
"saveChanges": "Guardar cambios" "saveChanges": "Guardar cambios",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Descargar fichero", "downloadFile": "Descargar fichero",
@@ -75,7 +76,9 @@
"sortByLastModified": "Ordenar por última modificación", "sortByLastModified": "Ordenar por última modificación",
"sortByName": "Ordenar por nombre", "sortByName": "Ordenar por nombre",
"sortBySize": "Ordenar por tamaño", "sortBySize": "Ordenar por tamaño",
"noPreview": "La vista previa no está disponible para este archivo." "noPreview": "La vista previa no está disponible para este archivo.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "seleccionar archivo o carpeta", "click": "seleccionar archivo o carpeta",
@@ -102,6 +105,7 @@
"username": "Usuario", "username": "Usuario",
"usernameTaken": "Nombre usuario no disponible", "usernameTaken": "Nombre usuario no disponible",
"wrongCredentials": "Usuario y/o contraseña incorrectos", "wrongCredentials": "Usuario y/o contraseña incorrectos",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "Minutos", "minutes": "Minutos",
"seconds": "Segundos", "seconds": "Segundos",
"unit": "Unidad" "unit": "Unidad"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "آپلود", "upload": "آپلود",
"openFile": "باز کردن فایل", "openFile": "باز کردن فایل",
"discardChanges": "لغو کردن", "discardChanges": "لغو کردن",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "دانلود فایل", "downloadFile": "دانلود فایل",
@@ -75,7 +76,9 @@
"sortByLastModified": "مرتب سازی آخرین ویرایش", "sortByLastModified": "مرتب سازی آخرین ویرایش",
"sortByName": "مرتب سازی نام", "sortByName": "مرتب سازی نام",
"sortBySize": "مرتب سازی اندازه", "sortBySize": "مرتب سازی اندازه",
"noPreview": "این فایل قابل نمایش نیست" "noPreview": "این فایل قابل نمایش نیست",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "انتخاب فایل یا پوشه", "click": "انتخاب فایل یا پوشه",
@@ -102,6 +105,7 @@
"username": "نام کاربری", "username": "نام کاربری",
"usernameTaken": "نام کاربری تکراری", "usernameTaken": "نام کاربری تکراری",
"wrongCredentials": "خطا در اعتبارسنجی", "wrongCredentials": "خطا در اعتبارسنجی",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "دقیقه", "minutes": "دقیقه",
"seconds": "ثانیه", "seconds": "ثانیه",
"unit": "واحد زمان" "unit": "واحد زمان"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Importer", "upload": "Importer",
"openFile": "Ouvrir le fichier", "openFile": "Ouvrir le fichier",
"discardChanges": "Annuler", "discardChanges": "Annuler",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Télécharger le fichier", "downloadFile": "Télécharger le fichier",
@@ -75,7 +76,9 @@
"sortByLastModified": "Trier par date de modification", "sortByLastModified": "Trier par date de modification",
"sortByName": "Trier par nom", "sortByName": "Trier par nom",
"sortBySize": "Trier par taille", "sortBySize": "Trier par taille",
"noPreview": "L'aperçu n'est pas disponible pour ce fichier." "noPreview": "L'aperçu n'est pas disponible pour ce fichier.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "Sélectionner un fichier ou dossier", "click": "Sélectionner un fichier ou dossier",
@@ -102,6 +105,7 @@
"username": "Utilisateur·ice", "username": "Utilisateur·ice",
"usernameTaken": "Le nom d'utilisateur·ice est déjà pris", "usernameTaken": "Le nom d'utilisateur·ice est déjà pris",
"wrongCredentials": "Identifiants incorrects !", "wrongCredentials": "Identifiants incorrects !",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "Minutes", "minutes": "Minutes",
"seconds": "Secondes", "seconds": "Secondes",
"unit": "Unité de temps" "unit": "Unité de temps"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "העלאה", "upload": "העלאה",
"openFile": "פתח קובץ", "openFile": "פתח קובץ",
"discardChanges": "זריקת השינויים", "discardChanges": "זריקת השינויים",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "הורד קובץ", "downloadFile": "הורד קובץ",
@@ -75,7 +76,9 @@
"sortByLastModified": "מיין לפי השינוי האחרון", "sortByLastModified": "מיין לפי השינוי האחרון",
"sortByName": "מיין לפי שם", "sortByName": "מיין לפי שם",
"sortBySize": "מיין לפי גודל", "sortBySize": "מיין לפי גודל",
"noPreview": "לא זמינה תצוגה מקדימה לקובץ זה" "noPreview": "לא זמינה תצוגה מקדימה לקובץ זה",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "בחר קובץ או תיקייה", "click": "בחר קובץ או תיקייה",
@@ -102,6 +105,7 @@
"username": "שם משתמש", "username": "שם משתמש",
"usernameTaken": "שם המשתמש כבר קיים", "usernameTaken": "שם המשתמש כבר קיים",
"wrongCredentials": "פרטי התחברות שגויים", "wrongCredentials": "פרטי התחברות שגויים",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "דקות", "minutes": "דקות",
"seconds": "שניות", "seconds": "שניות",
"unit": "יחידת זמן" "unit": "יחידת זמן"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Prenesi", "upload": "Prenesi",
"openFile": "Otvori datoteku", "openFile": "Otvori datoteku",
"discardChanges": "Odbaci", "discardChanges": "Odbaci",
"saveChanges": "Spremi promjene" "saveChanges": "Spremi promjene",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Preuzmi Datoteku", "downloadFile": "Preuzmi Datoteku",
@@ -75,7 +76,9 @@
"sortByLastModified": "Sortiraj po zadnjoj izmjeni", "sortByLastModified": "Sortiraj po zadnjoj izmjeni",
"sortByName": "Sortiraj po nazivu", "sortByName": "Sortiraj po nazivu",
"sortBySize": "Sortiraj po veličini", "sortBySize": "Sortiraj po veličini",
"noPreview": "Pregled nije dostupan za ovu datoteku." "noPreview": "Pregled nije dostupan za ovu datoteku.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "odaberi datoteku ili mapu", "click": "odaberi datoteku ili mapu",
@@ -102,6 +105,7 @@
"username": "Korisničko ime", "username": "Korisničko ime",
"usernameTaken": "Korisničko ime zauzeto", "usernameTaken": "Korisničko ime zauzeto",
"wrongCredentials": "Neispravno korisničko ime/lozinka", "wrongCredentials": "Neispravno korisničko ime/lozinka",
"passwordTooShort": "Lozinka mora sadržavati minimalno {min} znakova",
"logout_reasons": { "logout_reasons": {
"inactivity": "Odjavljeni ste zbog neaktivnosti." "inactivity": "Odjavljeni ste zbog neaktivnosti."
} }
@@ -166,7 +170,7 @@
"allowNew": "Stvori nove datoteke i mape", "allowNew": "Stvori nove datoteke i mape",
"allowPublish": "Objavi nove objave i stranice", "allowPublish": "Objavi nove objave i stranice",
"allowSignup": "Dopusti registraciju korisnicima", "allowSignup": "Dopusti registraciju korisnicima",
"hideLoginButton": "Hide the login button from public pages", "hideLoginButton": "Sakrij tipku za prijavu s javnih stranica",
"avoidChanges": "(ostavite prazno kako biste izbjegli promjene)", "avoidChanges": "(ostavite prazno kako biste izbjegli promjene)",
"branding": "Brendiranje", "branding": "Brendiranje",
"brandingDirectoryPath": "Put brendiranja", "brandingDirectoryPath": "Put brendiranja",
@@ -268,5 +272,10 @@
"minutes": "Minute", "minutes": "Minute",
"seconds": "Sekunde", "seconds": "Sekunde",
"unit": "Jedinica vremena" "unit": "Jedinica vremena"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Feltöltés", "upload": "Feltöltés",
"openFile": "Fájl megnyitása", "openFile": "Fájl megnyitása",
"discardChanges": "Discard", "discardChanges": "Discard",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Fájl letöltése", "downloadFile": "Fájl letöltése",
@@ -75,7 +76,9 @@
"sortByLastModified": "Rendezés utolsó módosítás szerint", "sortByLastModified": "Rendezés utolsó módosítás szerint",
"sortByName": "Rendezés név szerint", "sortByName": "Rendezés név szerint",
"sortBySize": "Rendezés méret szerint", "sortBySize": "Rendezés méret szerint",
"noPreview": "Ehhez a fájlhoz nincs előnézet." "noPreview": "Ehhez a fájlhoz nincs előnézet.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "mappa vagy fájl kijelölése", "click": "mappa vagy fájl kijelölése",
@@ -102,6 +105,7 @@
"username": "Felhasználói név", "username": "Felhasználói név",
"usernameTaken": "A felhasználói név már foglalt", "usernameTaken": "A felhasználói név már foglalt",
"wrongCredentials": "Hibás hitelesítő adatok", "wrongCredentials": "Hibás hitelesítő adatok",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "Perc", "minutes": "Perc",
"seconds": "Másodperc", "seconds": "Másodperc",
"unit": "Időegység" "unit": "Időegység"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -2,6 +2,8 @@ import dayjs from "dayjs";
import { createI18n } from "vue-i18n"; import { createI18n } from "vue-i18n";
import("dayjs/locale/ar"); import("dayjs/locale/ar");
import("dayjs/locale/bg");
import("dayjs/locale/cs");
import("dayjs/locale/de"); import("dayjs/locale/de");
import("dayjs/locale/el"); import("dayjs/locale/el");
import("dayjs/locale/en"); import("dayjs/locale/en");
@@ -14,6 +16,7 @@ import("dayjs/locale/is");
import("dayjs/locale/it"); import("dayjs/locale/it");
import("dayjs/locale/ja"); import("dayjs/locale/ja");
import("dayjs/locale/ko"); import("dayjs/locale/ko");
import("dayjs/locale/nb");
import("dayjs/locale/nl-be"); import("dayjs/locale/nl-be");
import("dayjs/locale/pl"); import("dayjs/locale/pl");
import("dayjs/locale/pt-br"); import("dayjs/locale/pt-br");
@@ -27,8 +30,6 @@ import("dayjs/locale/uk");
import("dayjs/locale/vi"); import("dayjs/locale/vi");
import("dayjs/locale/zh-cn"); import("dayjs/locale/zh-cn");
import("dayjs/locale/zh-tw"); import("dayjs/locale/zh-tw");
import("dayjs/locale/cs");
import("dayjs/locale/nb");
// All i18n resources specified in the plugin `include` option can be loaded // All i18n resources specified in the plugin `include` option can be loaded
// at once using the import syntax // at once using the import syntax
@@ -109,6 +110,7 @@ export function detectLocale() {
case /^uk\b/.test(locale): case /^uk\b/.test(locale):
locale = "uk"; locale = "uk";
break; break;
case /^vi\b/.test(locale): case /^vi\b/.test(locale):
locale = "vi"; locale = "vi";
break; break;
@@ -123,6 +125,9 @@ export function detectLocale() {
case /^no\b/.test(locale): case /^no\b/.test(locale):
locale = "no"; locale = "no";
break; break;
case /^bg\b/.test(locale):
locale = "bg";
break;
default: default:
locale = "en"; locale = "en";
} }

View File

@@ -43,7 +43,8 @@
"upload": "Hlaða upp", "upload": "Hlaða upp",
"openFile": "Open file", "openFile": "Open file",
"discardChanges": "Discard", "discardChanges": "Discard",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Sækja skjal", "downloadFile": "Sækja skjal",
@@ -75,7 +76,9 @@
"sortByLastModified": "Flokka eftir Seinast breytt", "sortByLastModified": "Flokka eftir Seinast breytt",
"sortByName": "Flokka eftir nafni", "sortByName": "Flokka eftir nafni",
"sortBySize": "Flokka eftir stærð", "sortBySize": "Flokka eftir stærð",
"noPreview": "Preview is not available for this file." "noPreview": "Preview is not available for this file.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "velja skjal eða möppu", "click": "velja skjal eða möppu",
@@ -102,6 +105,7 @@
"username": "Notendanafn", "username": "Notendanafn",
"usernameTaken": "Þetta norendanafn er þegar í notkun", "usernameTaken": "Þetta norendanafn er þegar í notkun",
"wrongCredentials": "Rangar notendaupplýsingar", "wrongCredentials": "Rangar notendaupplýsingar",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "Mínútur", "minutes": "Mínútur",
"seconds": "Sekúndur", "seconds": "Sekúndur",
"unit": "Tímastilling" "unit": "Tímastilling"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Carica", "upload": "Carica",
"openFile": "Apri file", "openFile": "Apri file",
"discardChanges": "Ignora", "discardChanges": "Ignora",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Scarica file", "downloadFile": "Scarica file",
@@ -75,7 +76,9 @@
"sortByLastModified": "Ordina per ultima modifica", "sortByLastModified": "Ordina per ultima modifica",
"sortByName": "Ordina per nome", "sortByName": "Ordina per nome",
"sortBySize": "Ordina per dimensione", "sortBySize": "Ordina per dimensione",
"noPreview": "L'anteprima non è disponibile per questo file." "noPreview": "L'anteprima non è disponibile per questo file.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "seleziona un file o una cartella", "click": "seleziona un file o una cartella",
@@ -102,6 +105,7 @@
"username": "Nome utente", "username": "Nome utente",
"usernameTaken": "Username già usato", "usernameTaken": "Username già usato",
"wrongCredentials": "Credenziali errate", "wrongCredentials": "Credenziali errate",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "Minuti", "minutes": "Minuti",
"seconds": "Secondi", "seconds": "Secondi",
"unit": "Unità di tempo" "unit": "Unità di tempo"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "アップロード", "upload": "アップロード",
"openFile": "ファイルを開く", "openFile": "ファイルを開く",
"discardChanges": "Discard", "discardChanges": "Discard",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "ファイルのダウンロード", "downloadFile": "ファイルのダウンロード",
@@ -75,7 +76,9 @@
"sortByLastModified": "更新日時で並べ替え", "sortByLastModified": "更新日時で並べ替え",
"sortByName": "名前で並べ替え", "sortByName": "名前で並べ替え",
"sortBySize": "サイズで並べ替え", "sortBySize": "サイズで並べ替え",
"noPreview": "プレビューはこのファイルでは利用できません" "noPreview": "プレビューはこのファイルでは利用できません",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "ファイルやフォルダーを選択", "click": "ファイルやフォルダーを選択",
@@ -102,6 +105,7 @@
"username": "ユーザー名", "username": "ユーザー名",
"usernameTaken": "ユーザー名はすでに取得されています", "usernameTaken": "ユーザー名はすでに取得されています",
"wrongCredentials": "ユーザー名またはパスワードが間違っています", "wrongCredentials": "ユーザー名またはパスワードが間違っています",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "分", "minutes": "分",
"seconds": "秒", "seconds": "秒",
"unit": "時間の単位" "unit": "時間の単位"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "업로드", "upload": "업로드",
"openFile": "파일 열기", "openFile": "파일 열기",
"discardChanges": "변경 사항 취소", "discardChanges": "변경 사항 취소",
"saveChanges": "변경사항 저장" "saveChanges": "변경사항 저장",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "파일 다운로드", "downloadFile": "파일 다운로드",
@@ -75,7 +76,9 @@
"sortByLastModified": "수정시간순 정렬", "sortByLastModified": "수정시간순 정렬",
"sortByName": "이름순", "sortByName": "이름순",
"sortBySize": "크기순", "sortBySize": "크기순",
"noPreview": "미리 보기가 지원되지 않는 파일 유형입니다." "noPreview": "미리 보기가 지원되지 않는 파일 유형입니다.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "파일이나 디렉토리를 선택해주세요.", "click": "파일이나 디렉토리를 선택해주세요.",
@@ -102,6 +105,7 @@
"username": "사용자 이름", "username": "사용자 이름",
"usernameTaken": "사용자 이름이 존재합니다", "usernameTaken": "사용자 이름이 존재합니다",
"wrongCredentials": "사용자 이름 또는 비밀번호를 확인하십시오", "wrongCredentials": "사용자 이름 또는 비밀번호를 확인하십시오",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "분", "minutes": "분",
"seconds": "초", "seconds": "초",
"unit": "Time Unit" "unit": "Time Unit"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Uploaden", "upload": "Uploaden",
"openFile": "Open file", "openFile": "Open file",
"discardChanges": "Discard", "discardChanges": "Discard",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Bestand downloaden", "downloadFile": "Bestand downloaden",
@@ -75,7 +76,9 @@
"sortByLastModified": "Sorteren op laatst bewerkt", "sortByLastModified": "Sorteren op laatst bewerkt",
"sortByName": "Sorteren op naam", "sortByName": "Sorteren op naam",
"sortBySize": "Sorteren op grootte", "sortBySize": "Sorteren op grootte",
"noPreview": "Preview is not available for this file." "noPreview": "Preview is not available for this file.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "selecteer bestand of map", "click": "selecteer bestand of map",
@@ -102,6 +105,7 @@
"username": "Gebruikersnaam", "username": "Gebruikersnaam",
"usernameTaken": "Gebruikersnaam reeds in gebruik", "usernameTaken": "Gebruikersnaam reeds in gebruik",
"wrongCredentials": "Verkeerde inloggegevens", "wrongCredentials": "Verkeerde inloggegevens",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "Minuten", "minutes": "Minuten",
"seconds": "Seconden", "seconds": "Seconden",
"unit": "Tijdseenheid" "unit": "Tijdseenheid"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Last opp", "upload": "Last opp",
"openFile": "Open file", "openFile": "Open file",
"discardChanges": "Slett", "discardChanges": "Slett",
"saveChanges": "Lagre Endringane " "saveChanges": "Lagre Endringane ",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Nedlast filen", "downloadFile": "Nedlast filen",
@@ -75,7 +76,9 @@
"sortByLastModified": "Sorter etter sist endret", "sortByLastModified": "Sorter etter sist endret",
"sortByName": "Sorter etter navn", "sortByName": "Sorter etter navn",
"sortBySize": "Sorter etter størrelse", "sortBySize": "Sorter etter størrelse",
"noPreview": "Forhåndsvisning er ikkje tilgjengeleg for denne filen." "noPreview": "Forhåndsvisning er ikkje tilgjengeleg for denne filen.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "velg fil eller katalog", "click": "velg fil eller katalog",
@@ -102,6 +105,7 @@
"username": "Brukernavn", "username": "Brukernavn",
"usernameTaken": "Brukernavn er allerede i bruk", "usernameTaken": "Brukernavn er allerede i bruk",
"wrongCredentials": "Feil legitimasjon", "wrongCredentials": "Feil legitimasjon",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "Du har blitt logget ut på grunn av inaktivitet" "inactivity": "Du har blitt logget ut på grunn av inaktivitet"
} }
@@ -268,5 +272,10 @@
"minutes": "Minutt", "minutes": "Minutt",
"seconds": "Sekunder", "seconds": "Sekunder",
"unit": "Time format" "unit": "Time format"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Wyślij", "upload": "Wyślij",
"openFile": "Otwórz plik", "openFile": "Otwórz plik",
"discardChanges": "Odrzuć", "discardChanges": "Odrzuć",
"saveChanges": "Zapisz zmiany" "saveChanges": "Zapisz zmiany",
"editAsText": "Edytuj jako tekst"
}, },
"download": { "download": {
"downloadFile": "Pobierz plik", "downloadFile": "Pobierz plik",
@@ -75,7 +76,9 @@
"sortByLastModified": "Sortuj wg ostatniej modyfikacji", "sortByLastModified": "Sortuj wg ostatniej modyfikacji",
"sortByName": "Sortuj wg nazwy", "sortByName": "Sortuj wg nazwy",
"sortBySize": "Sortuj wg rozmiaru", "sortBySize": "Sortuj wg rozmiaru",
"noPreview": "Podgląd tego pliku jest niedostępny." "noPreview": "Podgląd tego pliku jest niedostępny.",
"csvTooLarge": "Plik CSV jest za duży do podglądu (>5 MB). Pobierz, aby wyświetlić.",
"csvLoadFailed": "Nie udało się załadować pliku CSV."
}, },
"help": { "help": {
"click": "zaznacz plik lub folder", "click": "zaznacz plik lub folder",
@@ -102,6 +105,7 @@
"username": "Nazwa użytkownika", "username": "Nazwa użytkownika",
"usernameTaken": "Ta nazwa użytkownika jest zajęta", "usernameTaken": "Ta nazwa użytkownika jest zajęta",
"wrongCredentials": "Błędne dane logowania", "wrongCredentials": "Błędne dane logowania",
"passwordTooShort": "Wymagana minimalna liczba znaków hasła: {min}",
"logout_reasons": { "logout_reasons": {
"inactivity": "Wylogowano z powodu braku aktywności." "inactivity": "Wylogowano z powodu braku aktywności."
} }
@@ -268,5 +272,10 @@
"minutes": "Minuty", "minutes": "Minuty",
"seconds": "Sekundy", "seconds": "Sekundy",
"unit": "Jednostka czasu" "unit": "Jednostka czasu"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Enviar", "upload": "Enviar",
"openFile": "Abrir", "openFile": "Abrir",
"discardChanges": "Discard", "discardChanges": "Discard",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Baixar arquivo", "downloadFile": "Baixar arquivo",
@@ -75,7 +76,9 @@
"sortByLastModified": "Ordenar pela última modificação", "sortByLastModified": "Ordenar pela última modificação",
"sortByName": "Ordenar pelo nome", "sortByName": "Ordenar pelo nome",
"sortBySize": "Ordenar pelo tamanho", "sortBySize": "Ordenar pelo tamanho",
"noPreview": "Pré-visualização não disponível para este arquivo." "noPreview": "Pré-visualização não disponível para este arquivo.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "selecionar pasta ou arquivo", "click": "selecionar pasta ou arquivo",
@@ -102,6 +105,7 @@
"username": "Nome do usuário", "username": "Nome do usuário",
"usernameTaken": "Nome de usuário já existe", "usernameTaken": "Nome de usuário já existe",
"wrongCredentials": "Ops! Dados incorretos.", "wrongCredentials": "Ops! Dados incorretos.",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "Minutos", "minutes": "Minutos",
"seconds": "Segundos", "seconds": "Segundos",
"unit": "Unidades de Tempo" "unit": "Unidades de Tempo"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Enviar", "upload": "Enviar",
"openFile": "Open file", "openFile": "Open file",
"discardChanges": "Discard", "discardChanges": "Discard",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Descarregar ficheiro", "downloadFile": "Descarregar ficheiro",
@@ -75,7 +76,9 @@
"sortByLastModified": "Ordenar pela última alteração", "sortByLastModified": "Ordenar pela última alteração",
"sortByName": "Ordenar pelo nome", "sortByName": "Ordenar pelo nome",
"sortBySize": "Ordenar pelo tamanho", "sortBySize": "Ordenar pelo tamanho",
"noPreview": "Preview is not available for this file." "noPreview": "Preview is not available for this file.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "selecionar pasta ou ficheiro", "click": "selecionar pasta ou ficheiro",
@@ -102,6 +105,7 @@
"username": "Nome de utilizador", "username": "Nome de utilizador",
"usernameTaken": "O nome de utilizador já está registado", "usernameTaken": "O nome de utilizador já está registado",
"wrongCredentials": "Dados errados", "wrongCredentials": "Dados errados",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "Minutos", "minutes": "Minutos",
"seconds": "Segundos", "seconds": "Segundos",
"unit": "Unidades de tempo" "unit": "Unidades de tempo"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Încarcă", "upload": "Încarcă",
"openFile": "Open file", "openFile": "Open file",
"discardChanges": "Discard", "discardChanges": "Discard",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Descarcă fișier", "downloadFile": "Descarcă fișier",
@@ -75,7 +76,9 @@
"sortByLastModified": "Ordonează dup ultima modificare", "sortByLastModified": "Ordonează dup ultima modificare",
"sortByName": "Ordonează după nume", "sortByName": "Ordonează după nume",
"sortBySize": "Ordonează după dimensiune", "sortBySize": "Ordonează după dimensiune",
"noPreview": "Preview is not available for this file." "noPreview": "Preview is not available for this file.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "alege fișier sau director", "click": "alege fișier sau director",
@@ -102,6 +105,7 @@
"username": "Utilizator", "username": "Utilizator",
"usernameTaken": "Utilizatorul există", "usernameTaken": "Utilizatorul există",
"wrongCredentials": "Informații greșite", "wrongCredentials": "Informații greșite",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "Minute", "minutes": "Minute",
"seconds": "Secunde", "seconds": "Secunde",
"unit": "Unitate de timp" "unit": "Unitate de timp"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Загрузить", "upload": "Загрузить",
"openFile": "Открыть файл", "openFile": "Открыть файл",
"discardChanges": "Отказаться", "discardChanges": "Отказаться",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Скачать файл", "downloadFile": "Скачать файл",
@@ -75,7 +76,9 @@
"sortByLastModified": "Сортировка по дате изменения", "sortByLastModified": "Сортировка по дате изменения",
"sortByName": "Сортировка по имени", "sortByName": "Сортировка по имени",
"sortBySize": "Сортировка по размеру", "sortBySize": "Сортировка по размеру",
"noPreview": "Предварительный просмотр для этого файла недоступен." "noPreview": "Предварительный просмотр для этого файла недоступен.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "выбрать файл или каталог", "click": "выбрать файл или каталог",
@@ -102,6 +105,7 @@
"username": "Имя пользователя", "username": "Имя пользователя",
"usernameTaken": "Данное имя пользователя уже занято", "usernameTaken": "Данное имя пользователя уже занято",
"wrongCredentials": "Неверные данные", "wrongCredentials": "Неверные данные",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "Минуты", "minutes": "Минуты",
"seconds": "Секунды", "seconds": "Секунды",
"unit": "Единица времени" "unit": "Единица времени"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Nahrať", "upload": "Nahrať",
"openFile": "Otvoriť súbor", "openFile": "Otvoriť súbor",
"discardChanges": "Zahodiť", "discardChanges": "Zahodiť",
"saveChanges": "Uložiť zmeny" "saveChanges": "Uložiť zmeny",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Stiahnuť súbor", "downloadFile": "Stiahnuť súbor",
@@ -75,7 +76,9 @@
"sortByLastModified": "Zoradiť podľa dátumu", "sortByLastModified": "Zoradiť podľa dátumu",
"sortByName": "Zoradiť podľa názvu", "sortByName": "Zoradiť podľa názvu",
"sortBySize": "Zoradiť podľa veľkosti", "sortBySize": "Zoradiť podľa veľkosti",
"noPreview": "Pre tento súbor nie je dostupný náhľad." "noPreview": "Pre tento súbor nie je dostupný náhľad.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "vyberie súbor alebo priečinok", "click": "vyberie súbor alebo priečinok",
@@ -102,6 +105,7 @@
"username": "Používateľské meno", "username": "Používateľské meno",
"usernameTaken": "Meno je už obsadené", "usernameTaken": "Meno je už obsadené",
"wrongCredentials": "Nesprávne prihlasovacie údaje", "wrongCredentials": "Nesprávne prihlasovacie údaje",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "Boli ste odhlásení z dôvodu nečinnosti." "inactivity": "Boli ste odhlásení z dôvodu nečinnosti."
} }
@@ -268,5 +272,10 @@
"minutes": "Minúty", "minutes": "Minúty",
"seconds": "Sekundy", "seconds": "Sekundy",
"unit": "Jednotka času" "unit": "Jednotka času"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Ladda upp", "upload": "Ladda upp",
"openFile": "Öppna fil", "openFile": "Öppna fil",
"discardChanges": "Förkasta", "discardChanges": "Förkasta",
"saveChanges": "Spara ändringar" "saveChanges": "Spara ändringar",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Ladda ner fil", "downloadFile": "Ladda ner fil",
@@ -75,7 +76,9 @@
"sortByLastModified": "Sortera på senast ändrad", "sortByLastModified": "Sortera på senast ändrad",
"sortByName": "Sortera på namn", "sortByName": "Sortera på namn",
"sortBySize": "Sortera på storlek", "sortBySize": "Sortera på storlek",
"noPreview": "Förhandsvisning är inte tillgänglig för denna fil." "noPreview": "Förhandsvisning är inte tillgänglig för denna fil.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "välj fil eller mapp", "click": "välj fil eller mapp",
@@ -102,6 +105,7 @@
"username": "Användarnamn", "username": "Användarnamn",
"usernameTaken": "Användarnamn upptaget", "usernameTaken": "Användarnamn upptaget",
"wrongCredentials": "Fel inloggning", "wrongCredentials": "Fel inloggning",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "Du har blivit utloggad på grund av inaktivitet." "inactivity": "Du har blivit utloggad på grund av inaktivitet."
} }
@@ -268,5 +272,10 @@
"minutes": "Minuter", "minutes": "Minuter",
"seconds": "Sekunder", "seconds": "Sekunder",
"unit": "Tidsenhet" "unit": "Tidsenhet"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Yükle", "upload": "Yükle",
"openFile": "Dosyayı aç", "openFile": "Dosyayı aç",
"discardChanges": "Discard", "discardChanges": "Discard",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Dosyayı indir", "downloadFile": "Dosyayı indir",
@@ -75,7 +76,9 @@
"sortByLastModified": "Güncelleme tarihine göre sırala", "sortByLastModified": "Güncelleme tarihine göre sırala",
"sortByName": "İsme göre sırala", "sortByName": "İsme göre sırala",
"sortBySize": "Boyuta göre sırala", "sortBySize": "Boyuta göre sırala",
"noPreview": "Bu dosya için önizleme aktif değil" "noPreview": "Bu dosya için önizleme aktif değil",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "dosya veya klasör seçin", "click": "dosya veya klasör seçin",
@@ -102,6 +105,7 @@
"username": "Kullanıcı adı", "username": "Kullanıcı adı",
"usernameTaken": "Kullanıcı adı mevcut", "usernameTaken": "Kullanıcı adı mevcut",
"wrongCredentials": "Yanlış hesap bilgileri", "wrongCredentials": "Yanlış hesap bilgileri",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "Dakika", "minutes": "Dakika",
"seconds": "Saniye", "seconds": "Saniye",
"unit": "Zaman birimi" "unit": "Zaman birimi"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Вивантажити", "upload": "Вивантажити",
"openFile": "Відкрити файл", "openFile": "Відкрити файл",
"discardChanges": "Скасувати", "discardChanges": "Скасувати",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Завантажити файл", "downloadFile": "Завантажити файл",
@@ -75,7 +76,9 @@
"sortByLastModified": "Сортувати за останнім зміненням", "sortByLastModified": "Сортувати за останнім зміненням",
"sortByName": "Сортувати за іменем", "sortByName": "Сортувати за іменем",
"sortBySize": "Сортувати за розміром", "sortBySize": "Сортувати за розміром",
"noPreview": "Попередній перегляд для цього файлу недоступний." "noPreview": "Попередній перегляд для цього файлу недоступний.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "вибрати файл чи каталог", "click": "вибрати файл чи каталог",
@@ -102,6 +105,7 @@
"username": "Ім'я користувача", "username": "Ім'я користувача",
"usernameTaken": "Ім'я користувача вже використовується", "usernameTaken": "Ім'я користувача вже використовується",
"wrongCredentials": "Неправильне ім'я користувача або пароль", "wrongCredentials": "Неправильне ім'я користувача або пароль",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "Хвилини", "minutes": "Хвилини",
"seconds": "Секунди", "seconds": "Секунди",
"unit": "Одиниця часу" "unit": "Одиниця часу"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "Tải lên", "upload": "Tải lên",
"openFile": "Mở tệp", "openFile": "Mở tệp",
"discardChanges": "Hủy bỏ thay đổi", "discardChanges": "Hủy bỏ thay đổi",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "Tải xuống tệp tin", "downloadFile": "Tải xuống tệp tin",
@@ -75,7 +76,9 @@
"sortByLastModified": "Sắp xếp theo ngày sửa đổi", "sortByLastModified": "Sắp xếp theo ngày sửa đổi",
"sortByName": "Sắp xếp theo tên", "sortByName": "Sắp xếp theo tên",
"sortBySize": "Sắp xếp theo kích thước", "sortBySize": "Sắp xếp theo kích thước",
"noPreview": "Không có bản xem trước cho tập tin này." "noPreview": "Không có bản xem trước cho tập tin này.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "chọn tập tin hoặc thư mục", "click": "chọn tập tin hoặc thư mục",
@@ -102,6 +105,7 @@
"username": "Tên người dùng", "username": "Tên người dùng",
"usernameTaken": "Tên người dùng đã tồn tại", "usernameTaken": "Tên người dùng đã tồn tại",
"wrongCredentials": "Thông tin đăng nhập không đúng", "wrongCredentials": "Thông tin đăng nhập không đúng",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "Phút", "minutes": "Phút",
"seconds": "Giây", "seconds": "Giây",
"unit": "Đơn vị" "unit": "Đơn vị"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "上传", "upload": "上传",
"openFile": "打开文件", "openFile": "打开文件",
"discardChanges": "放弃更改", "discardChanges": "放弃更改",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "下载文件", "downloadFile": "下载文件",
@@ -75,7 +76,9 @@
"sortByLastModified": "按最后修改时间排序", "sortByLastModified": "按最后修改时间排序",
"sortByName": "按名称排序", "sortByName": "按名称排序",
"sortBySize": "按大小排序", "sortBySize": "按大小排序",
"noPreview": "此文件无法预览。" "noPreview": "此文件无法预览。",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "选择文件或文件夹", "click": "选择文件或文件夹",
@@ -102,6 +105,7 @@
"username": "用户名", "username": "用户名",
"usernameTaken": "用户名已经被使用", "usernameTaken": "用户名已经被使用",
"wrongCredentials": "用户名或密码错误", "wrongCredentials": "用户名或密码错误",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "分钟", "minutes": "分钟",
"seconds": "秒", "seconds": "秒",
"unit": "时间单位" "unit": "时间单位"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -43,7 +43,8 @@
"upload": "上傳", "upload": "上傳",
"openFile": "開啟檔案", "openFile": "開啟檔案",
"discardChanges": "放棄變更", "discardChanges": "放棄變更",
"saveChanges": "Save changes" "saveChanges": "Save changes",
"editAsText": "Edit as Text"
}, },
"download": { "download": {
"downloadFile": "下載檔案", "downloadFile": "下載檔案",
@@ -75,7 +76,9 @@
"sortByLastModified": "按最後修改時間排序", "sortByLastModified": "按最後修改時間排序",
"sortByName": "按名稱排序", "sortByName": "按名稱排序",
"sortBySize": "按大小排序", "sortBySize": "按大小排序",
"noPreview": "此檔案無法預覽。" "noPreview": "此檔案無法預覽。",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file."
}, },
"help": { "help": {
"click": "選擇檔案或目錄", "click": "選擇檔案或目錄",
@@ -102,6 +105,7 @@
"username": "帳號", "username": "帳號",
"usernameTaken": "用戶名已存在", "usernameTaken": "用戶名已存在",
"wrongCredentials": "帳號或密碼錯誤", "wrongCredentials": "帳號或密碼錯誤",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": { "logout_reasons": {
"inactivity": "You have been logged out due to inactivity." "inactivity": "You have been logged out due to inactivity."
} }
@@ -268,5 +272,10 @@
"minutes": "分鐘", "minutes": "分鐘",
"seconds": "秒", "seconds": "秒",
"unit": "時間單位" "unit": "時間單位"
},
"available_csv_separators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
} }
} }

View File

@@ -2,7 +2,7 @@ import { useAuthStore } from "@/stores/auth";
import router from "@/router"; import router from "@/router";
import type { JwtPayload } from "jwt-decode"; import type { JwtPayload } from "jwt-decode";
import { jwtDecode } from "jwt-decode"; import { jwtDecode } from "jwt-decode";
import { baseURL, noAuth } from "./constants"; import { authMethod, baseURL, noAuth, logoutPage } from "./constants";
import { StatusError } from "@/api/utils"; import { StatusError } from "@/api/utils";
import { setSafeTimeout } from "@/api/utils"; import { setSafeTimeout } from "@/api/utils";
@@ -18,6 +18,12 @@ export function parseToken(token: string) {
authStore.jwt = token; authStore.jwt = token;
authStore.setUser(data.user); authStore.setUser(data.user);
// proxy auth with custom logout subject to unknown external timeout
if (logoutPage !== "/login" && authMethod === "proxy") {
console.warn("idle timeout disabled with proxy auth and custom logout");
return;
}
if (authStore.logoutTimer) { if (authStore.logoutTimer) {
clearTimeout(authStore.logoutTimer); clearTimeout(authStore.logoutTimer);
} }
@@ -101,7 +107,11 @@ export async function signup(username: string, password: string) {
}); });
if (res.status !== 200) { if (res.status !== 200) {
throw new StatusError(`${res.status} ${res.statusText}`, res.status); const body = await res.text();
throw new StatusError(
body || `${res.status} ${res.statusText}`,
res.status
);
} }
} }
@@ -114,6 +124,8 @@ export function logout(reason?: string) {
localStorage.setItem("jwt", ""); localStorage.setItem("jwt", "");
if (noAuth) { if (noAuth) {
window.location.reload(); window.location.reload();
} else if (logoutPage !== "/login") {
document.location.href = `${logoutPage}`;
} else { } else {
if (typeof reason === "string" && reason.trim() !== "") { if (typeof reason === "string" && reason.trim() !== "") {
router.push({ router.push({

View File

@@ -10,6 +10,7 @@ const version: string = window.FileBrowser.Version;
const logoURL = `${staticURL}/img/logo.svg`; const logoURL = `${staticURL}/img/logo.svg`;
const noAuth: boolean = window.FileBrowser.NoAuth; const noAuth: boolean = window.FileBrowser.NoAuth;
const authMethod = window.FileBrowser.AuthMethod; const authMethod = window.FileBrowser.AuthMethod;
const logoutPage: string = window.FileBrowser.LogoutPage;
const loginPage: boolean = window.FileBrowser.LoginPage; const loginPage: boolean = window.FileBrowser.LoginPage;
const theme: UserTheme = window.FileBrowser.Theme; const theme: UserTheme = window.FileBrowser.Theme;
const enableThumbs: boolean = window.FileBrowser.EnableThumbs; const enableThumbs: boolean = window.FileBrowser.EnableThumbs;
@@ -32,6 +33,7 @@ export {
version, version,
noAuth, noAuth,
authMethod, authMethod,
logoutPage,
loginPage, loginPage,
theme, theme,
enableThumbs, enableThumbs,

64
frontend/src/utils/csv.ts Normal file
View File

@@ -0,0 +1,64 @@
export interface CsvData {
headers: string[];
rows: string[][];
}
/**
* Parse CSV content into headers and rows
* Supports quoted fields and handles commas within quotes
*/
export function parseCSV(
content: string,
columnSeparator: Array<string>
): CsvData {
if (!content || content.trim().length === 0) {
return { headers: [], rows: [] };
}
const lines = content.split(/\r?\n/);
const result: string[][] = [];
for (const line of lines) {
if (line.trim().length === 0) continue;
const row: string[] = [];
let currentField = "";
let inQuotes = false;
for (let i = 0; i < line.length; i++) {
const char = line[i];
const nextChar = line[i + 1];
if (char === '"') {
if (inQuotes && nextChar === '"') {
// Escaped quote
currentField += '"';
i++; // Skip next quote
} else {
// Toggle quote state
inQuotes = !inQuotes;
}
} else if (columnSeparator.includes(char) && !inQuotes) {
// Field separator
row.push(currentField);
currentField = "";
} else {
currentField += char;
}
}
// Add the last field
row.push(currentField);
result.push(row);
}
if (result.length === 0) {
return { headers: [], rows: [] };
}
// First row is headers
const headers = result[0];
const rows = result.slice(1);
return { headers, rows };
}

View File

@@ -69,6 +69,12 @@ const currentView = computed(() => {
if (fileStore.req.isDir) { if (fileStore.req.isDir) {
return FileListing; return FileListing;
} else if (fileStore.req.extension.toLowerCase() === ".csv") {
// CSV files use Preview for table view, unless ?edit=true
if (route.query.edit === "true") {
return Editor;
}
return Preview;
} else if ( } else if (
fileStore.req.type === "text" || fileStore.req.type === "text" ||
fileStore.req.type === "textImmutable" fileStore.req.type === "textImmutable"

View File

@@ -112,6 +112,13 @@ const submit = async (event: Event) => {
error.value = t("login.usernameTaken"); error.value = t("login.usernameTaken");
} else if (e.status === 403) { } else if (e.status === 403) {
error.value = t("login.wrongCredentials"); error.value = t("login.wrongCredentials");
} else if (e.status === 400) {
const match = e.message.match(/minimum length is (\d+)/);
if (match) {
error.value = t("login.passwordTooShort", { min: match[1] });
} else {
error.value = e.message;
}
} else { } else {
$showError(e); $showError(e);
} }

View File

@@ -186,7 +186,7 @@ const handlePageChange = (event: BeforeUnloadEvent) => {
} }
}; };
const save = async () => { const save = async (throwError?: boolean) => {
const button = "save"; const button = "save";
buttons.loading("save"); buttons.loading("save");
@@ -197,6 +197,7 @@ const save = async () => {
} catch (e: any) { } catch (e: any) {
buttons.done(button); buttons.done(button);
$showError(e); $showError(e);
if (throwError) throw e;
} }
}; };
@@ -223,8 +224,10 @@ const close = () => {
finishClose(); finishClose();
}, },
saveAction: async () => { saveAction: async () => {
await save(); try {
finishClose(); await save(true);
finishClose();
} catch {}
}, },
}); });
return; return;

View File

@@ -6,7 +6,7 @@
@mousemove="toggleNavigation" @mousemove="toggleNavigation"
@touchstart="toggleNavigation" @touchstart="toggleNavigation"
> >
<header-bar v-if="isPdf || isEpub || showNav"> <header-bar v-if="isPdf || isEpub || isCsv || showNav">
<action icon="close" :label="$t('buttons.close')" @action="close()" /> <action icon="close" :label="$t('buttons.close')" @action="close()" />
<title>{{ name }}</title> <title>{{ name }}</title>
<action <action
@@ -24,6 +24,13 @@
:label="$t('buttons.rename')" :label="$t('buttons.rename')"
show="rename" show="rename"
/> />
<action
:disabled="layoutStore.loading"
v-if="isCsv && authStore.user?.perm.modify"
icon="edit_note"
:label="t('buttons.editAsText')"
@action="editAsText"
/>
<action <action
:disabled="layoutStore.loading" :disabled="layoutStore.loading"
v-if="authStore.user?.perm.delete" v-if="authStore.user?.perm.delete"
@@ -87,6 +94,7 @@
<span>{{ size }}%</span> <span>{{ size }}%</span>
</div> </div>
</div> </div>
<CsvViewer v-else-if="isCsv" :content="csvContent" :error="csvError" />
<ExtendedImage <ExtendedImage
v-else-if="fileStore.req?.type == 'image'" v-else-if="fileStore.req?.type == 'image'"
:src="previewUrl" :src="previewUrl"
@@ -176,11 +184,17 @@ import HeaderBar from "@/components/header/HeaderBar.vue";
import Action from "@/components/header/Action.vue"; import Action from "@/components/header/Action.vue";
import ExtendedImage from "@/components/files/ExtendedImage.vue"; import ExtendedImage from "@/components/files/ExtendedImage.vue";
import VideoPlayer from "@/components/files/VideoPlayer.vue"; import VideoPlayer from "@/components/files/VideoPlayer.vue";
import CsvViewer from "@/components/files/CsvViewer.vue";
import { VueReader } from "vue-reader"; import { VueReader } from "vue-reader";
import { computed, inject, onBeforeUnmount, onMounted, ref, watch } from "vue"; import { computed, inject, onBeforeUnmount, onMounted, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import type { Rendition } from "epubjs"; import type { Rendition } from "epubjs";
import { getTheme } from "@/utils/theme"; import { getTheme } from "@/utils/theme";
import { useI18n } from "vue-i18n";
// CSV file size limit for preview (5MB)
// Prevents browser memory issues with large files
const CSV_MAX_SIZE = 5 * 1024 * 1024;
const location = useStorage("book-progress", 0, undefined, { const location = useStorage("book-progress", 0, undefined, {
serializer: { serializer: {
@@ -239,6 +253,8 @@ const hoverNav = ref<boolean>(false);
const autoPlay = ref<boolean>(false); const autoPlay = ref<boolean>(false);
const previousRaw = ref<string>(""); const previousRaw = ref<string>("");
const nextRaw = ref<string>(""); const nextRaw = ref<string>("");
const csvContent = ref<string>("");
const csvError = ref<string>("");
const player = ref<HTMLVideoElement | HTMLAudioElement | null>(null); const player = ref<HTMLVideoElement | HTMLAudioElement | null>(null);
@@ -248,6 +264,8 @@ const authStore = useAuthStore();
const fileStore = useFileStore(); const fileStore = useFileStore();
const layoutStore = useLayoutStore(); const layoutStore = useLayoutStore();
const { t } = useI18n();
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
@@ -279,6 +297,7 @@ const isPdf = computed(() => fileStore.req?.extension.toLowerCase() == ".pdf");
const isEpub = computed( const isEpub = computed(
() => fileStore.req?.extension.toLowerCase() == ".epub" () => fileStore.req?.extension.toLowerCase() == ".epub"
); );
const isCsv = computed(() => fileStore.req?.extension.toLowerCase() == ".csv");
const isResizeEnabled = computed(() => resizePreview); const isResizeEnabled = computed(() => resizePreview);
@@ -366,6 +385,18 @@ const updatePreview = async () => {
const dirs = route.fullPath.split("/"); const dirs = route.fullPath.split("/");
name.value = decodeURIComponent(dirs[dirs.length - 1]); 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) { if (!listing.value) {
try { try {
const path = url.removeLastDir(route.path); const path = url.removeLastDir(route.path);
@@ -435,4 +466,8 @@ const close = () => {
}; };
const download = () => window.open(downloadUrl.value); const download = () => window.open(downloadUrl.value);
const editAsText = () => {
router.push({ path: route.path, query: { edit: "true" } });
};
</script> </script>

10
go.mod
View File

@@ -17,14 +17,14 @@ require (
github.com/mholt/archives v0.1.5 github.com/mholt/archives v0.1.5
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0
github.com/samber/lo v1.52.0 github.com/samber/lo v1.52.0
github.com/shirou/gopsutil/v4 v4.25.10 github.com/shirou/gopsutil/v4 v4.25.11
github.com/spf13/afero v1.15.0 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/pflag v1.0.10
github.com/spf13/viper v1.21.0 github.com/spf13/viper v1.21.0
github.com/stretchr/testify v1.11.1 github.com/stretchr/testify v1.11.1
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
golang.org/x/crypto v0.44.0 golang.org/x/crypto v0.45.0
golang.org/x/image v0.33.0 golang.org/x/image v0.33.0
golang.org/x/text v0.31.0 golang.org/x/text v0.31.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
@@ -44,7 +44,7 @@ require (
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // 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-logging v0.0.0-20200710184922-b02d349568dd // indirect
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // 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/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect github.com/go-errors/errors v1.5.1 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect
@@ -73,7 +73,7 @@ require (
go.etcd.io/bbolt v1.4.3 // indirect go.etcd.io/bbolt v1.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/net v0.46.0 // indirect golang.org/x/net v0.47.0 // indirect
golang.org/x/sync v0.18.0 // indirect golang.org/x/sync v0.18.0 // indirect
golang.org/x/sys v0.38.0 // indirect golang.org/x/sys v0.38.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect

20
go.sum
View File

@@ -75,8 +75,8 @@ github.com/dsoprea/go-utility/v2 v2.0.0-20221003142440-7a1927d49d9d/go.mod h1:LV
github.com/dsoprea/go-utility/v2 v2.0.0-20221003160719-7bc88537c05e/go.mod h1:VZ7cB0pTjm1ADBWhJUOHESu4ZYy9JN+ZPqjfiW09EPU= github.com/dsoprea/go-utility/v2 v2.0.0-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 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/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.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
github.com/ebitengine/purego v0.9.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= 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/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/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= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
@@ -204,8 +204,8 @@ github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDc
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw= github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/shirou/gopsutil/v4 v4.25.10 h1:at8lk/5T1OgtuCp+AwrDofFRjnvosn0nkN2OLQ6g8tA= github.com/shirou/gopsutil/v4 v4.25.11 h1:X53gB7muL9Gnwwo2evPSE+SfOrltMoR6V3xJAXZILTY=
github.com/shirou/gopsutil/v4 v4.25.10/go.mod h1:+kSwyC8DRUD9XXEHCAFjK+0nuArFJM0lva+StQAcskM= github.com/shirou/gopsutil/v4 v4.25.11/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 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU= 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= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
@@ -214,8 +214,8 @@ github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/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 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= 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.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= 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.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -266,8 +266,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-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-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.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -319,8 +319,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-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.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.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=

View File

@@ -1,4 +1,4 @@
package http package fbhttp
import ( import (
"encoding/json" "encoding/json"
@@ -12,7 +12,9 @@ import (
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
"github.com/golang-jwt/jwt/v5/request" "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" "github.com/filebrowser/filebrowser/v2/users"
) )
@@ -61,6 +63,22 @@ func (e extractor) ExtractToken(r *http.Request) (string, error) {
return "", request.ErrNoTokenInRequest 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 { func withUser(fn handleFunc) handleFunc {
return func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { return func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
keyFunc := func(_ *jwt.Token) (interface{}, error) { keyFunc := func(_ *jwt.Token) (interface{}, error) {
@@ -68,13 +86,9 @@ func withUser(fn handleFunc) handleFunc {
} }
var tk authToken var tk authToken
token, err := request.ParseFromRequest(r, &extractor{}, keyFunc, request.WithClaims(&tk)) p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}), jwt.WithExpirationRequired())
if err != nil || !token.Valid { token, err := request.ParseFromRequest(r, &extractor{}, keyFunc, request.WithClaims(&tk), request.WithParser(p))
return http.StatusUnauthorized, nil if (err != nil || !token.Valid) && !renewableErr(err, d) {
}
err = jwt.NewValidator(jwt.WithExpirationRequired()).Validate(tk)
if err != nil {
return http.StatusUnauthorized, nil return http.StatusUnauthorized, nil
} }
@@ -171,7 +185,7 @@ var signupHandler = func(_ http.ResponseWriter, r *http.Request, d *data) (int,
log.Printf("new user: %s, home dir: [%s].", user.Username, userHome) log.Printf("new user: %s, home dir: [%s].", user.Username, userHome)
err = d.store.Users.Save(user) err = d.store.Users.Save(user)
if errors.Is(err, fbErrors.ErrExist) { if errors.Is(err, fberrors.ErrExist) {
return http.StatusConflict, err return http.StatusConflict, err
} else if err != nil { } else if err != nil {
return http.StatusInternalServerError, err return http.StatusInternalServerError, err

View File

@@ -1,4 +1,4 @@
package http package fbhttp
import ( import (
"bufio" "bufio"

View File

@@ -1,4 +1,4 @@
package http package fbhttp
import ( import (
"log" "log"

View File

@@ -1,6 +1,6 @@
//go:build !dev //go:build !dev
package http package fbhttp
// global headers to append to every response // global headers to append to every response
var globalHeaders = map[string]string{ var globalHeaders = map[string]string{

View File

@@ -1,4 +1,4 @@
package http package fbhttp
import ( import (
"io/fs" "io/fs"

View File

@@ -1,5 +1,5 @@
//go:generate go-enum --sql --marshal --names --file $GOFILE //go:generate go-enum --sql --marshal --names --file $GOFILE
package http package fbhttp
import ( import (
"bytes" "bytes"

View File

@@ -1,7 +1,7 @@
// Code generated by go-enum // Code generated by go-enum
// DO NOT EDIT! // DO NOT EDIT!
package http package fbhttp
import ( import (
"database/sql/driver" "database/sql/driver"

View File

@@ -1,4 +1,4 @@
package http package fbhttp
import ( import (
"errors" "errors"

View File

@@ -1,4 +1,4 @@
package http package fbhttp
import ( import (
"fmt" "fmt"

View File

@@ -1,4 +1,4 @@
package http package fbhttp
import ( import (
"errors" "errors"

View File

@@ -1,4 +1,4 @@
package http package fbhttp
import ( import (
"context" "context"
@@ -17,7 +17,7 @@ import (
"github.com/shirou/gopsutil/v4/disk" "github.com/shirou/gopsutil/v4/disk"
"github.com/spf13/afero" "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/files"
"github.com/filebrowser/filebrowser/v2/fileutils" "github.com/filebrowser/filebrowser/v2/fileutils"
) )
@@ -44,7 +44,7 @@ var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d
if checksum := r.URL.Query().Get("checksum"); checksum != "" { if checksum := r.URL.Query().Get("checksum"); checksum != "" {
err := file.Checksum(checksum) err := file.Checksum(checksum)
if errors.Is(err, fbErrors.ErrInvalidOption) { if errors.Is(err, fberrors.ErrInvalidOption) {
return http.StatusBadRequest, nil return http.StatusBadRequest, nil
} else if err != nil { } else if err != nil {
return http.StatusInternalServerError, err return http.StatusInternalServerError, err
@@ -238,7 +238,7 @@ func checkParent(src, dst string) error {
rel = filepath.ToSlash(rel) rel = filepath.ToSlash(rel)
if !strings.HasPrefix(rel, "../") && rel != ".." && rel != "." { if !strings.HasPrefix(rel, "../") && rel != ".." && rel != "." {
return fbErrors.ErrSourceIsParent return fberrors.ErrSourceIsParent
} }
return nil return nil
@@ -304,13 +304,13 @@ func patchAction(ctx context.Context, action, src, dst string, d *data, fileCach
switch action { switch action {
case "copy": case "copy":
if !d.user.Perm.Create { 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) return fileutils.Copy(d.user.Fs, src, dst, d.settings.FileMode, d.settings.DirMode)
case "rename": case "rename":
if !d.user.Perm.Rename { if !d.user.Perm.Rename {
return fbErrors.ErrPermissionDenied return fberrors.ErrPermissionDenied
} }
src = path.Clean("/" + src) src = path.Clean("/" + src)
dst = path.Clean("/" + dst) dst = path.Clean("/" + dst)
@@ -335,7 +335,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) return fileutils.MoveFile(d.user.Fs, src, dst, d.settings.FileMode, d.settings.DirMode)
default: default:
return fmt.Errorf("unsupported action %s: %w", action, fbErrors.ErrInvalidRequestParams) return fmt.Errorf("unsupported action %s: %w", action, fberrors.ErrInvalidRequestParams)
} }
} }

View File

@@ -1,4 +1,4 @@
package http package fbhttp
import ( import (
"net/http" "net/http"

View File

@@ -1,4 +1,4 @@
package http package fbhttp
import ( import (
"encoding/json" "encoding/json"

View File

@@ -1,4 +1,4 @@
package http package fbhttp
import ( import (
"crypto/rand" "crypto/rand"
@@ -14,7 +14,7 @@ import (
"golang.org/x/crypto/bcrypt" "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" "github.com/filebrowser/filebrowser/v2/share"
) )
@@ -38,7 +38,7 @@ var shareListHandler = withPermShare(func(w http.ResponseWriter, r *http.Request
} else { } else {
s, err = d.store.Share.FindByUserID(d.user.ID) 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{}) 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) { 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) 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{}) return renderJSON(w, r, []*share.Link{})
} }

View File

@@ -1,4 +1,4 @@
package http package fbhttp
import ( import (
"encoding/json" "encoding/json"
@@ -38,6 +38,7 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys
"Signup": d.settings.Signup, "Signup": d.settings.Signup,
"NoAuth": d.settings.AuthMethod == auth.MethodNoAuth, "NoAuth": d.settings.AuthMethod == auth.MethodNoAuth,
"AuthMethod": d.settings.AuthMethod, "AuthMethod": d.settings.AuthMethod,
"LogoutPage": d.settings.LogoutPage,
"LoginPage": auther.LoginPage(), "LoginPage": auther.LoginPage(),
"CSS": false, "CSS": false,
"ReCaptcha": false, "ReCaptcha": false,

View File

@@ -1,4 +1,4 @@
package http package fbhttp
import ( import (
"bytes" "bytes"

View File

@@ -1,4 +1,4 @@
package http package fbhttp
import ( import (
"context" "context"

View File

@@ -1,4 +1,4 @@
package http package fbhttp
import ( import (
"encoding/json" "encoding/json"
@@ -12,7 +12,7 @@ import (
"golang.org/x/text/cases" "golang.org/x/text/cases"
"golang.org/x/text/language" "golang.org/x/text/language"
fbErrors "github.com/filebrowser/filebrowser/v2/errors" fberrors "github.com/filebrowser/filebrowser/v2/errors"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
) )
@@ -36,7 +36,7 @@ func getUserID(r *http.Request) (uint, error) {
func getUser(_ http.ResponseWriter, r *http.Request) (*modifyUserRequest, error) { func getUser(_ http.ResponseWriter, r *http.Request) (*modifyUserRequest, error) {
if r.Body == nil { if r.Body == nil {
return nil, fbErrors.ErrEmptyRequest return nil, fberrors.ErrEmptyRequest
} }
req := &modifyUserRequest{} req := &modifyUserRequest{}
@@ -46,7 +46,7 @@ func getUser(_ http.ResponseWriter, r *http.Request) (*modifyUserRequest, error)
} }
if req.What != "user" { if req.What != "user" {
return nil, fbErrors.ErrInvalidDataType return nil, fberrors.ErrInvalidDataType
} }
return req, nil return req, nil
@@ -87,7 +87,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) { 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)) 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 return http.StatusNotFound, err
} }
@@ -122,7 +122,7 @@ var userPostHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *
} }
if req.Data.Password == "" { 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) req.Data.Password, err = users.ValidateAndHashPwd(req.Data.Password, d.settings.MinimumPasswordLength)

View File

@@ -1,4 +1,4 @@
package http package fbhttp
import ( import (
"encoding/json" "encoding/json"

View File

@@ -10,5 +10,6 @@
"postUpdateOptions": [ "postUpdateOptions": [
"gomodUpdateImportPaths", "gomodUpdateImportPaths",
"gomodTidy" "gomodTidy"
] ],
"schedule": "* * * * 0,6"
} }

View File

@@ -11,6 +11,7 @@ import (
) )
const DefaultUsersHomeBasePath = "/users" const DefaultUsersHomeBasePath = "/users"
const DefaultLogoutPage = "/login"
const DefaultMinimumPasswordLength = 12 const DefaultMinimumPasswordLength = 12
const DefaultFileMode = 0640 const DefaultFileMode = 0640
const DefaultDirMode = 0750 const DefaultDirMode = 0750
@@ -27,6 +28,7 @@ type Settings struct {
UserHomeBasePath string `json:"userHomeBasePath"` UserHomeBasePath string `json:"userHomeBasePath"`
Defaults UserDefaults `json:"defaults"` Defaults UserDefaults `json:"defaults"`
AuthMethod AuthMethod `json:"authMethod"` AuthMethod AuthMethod `json:"authMethod"`
LogoutPage string `json:"logoutPage"`
Branding Branding `json:"branding"` Branding Branding `json:"branding"`
Tus Tus `json:"tus"` Tus Tus `json:"tus"`
Commands map[string][]string `json:"commands"` Commands map[string][]string `json:"commands"`

View File

@@ -1,7 +1,7 @@
package settings package settings
import ( 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/rules"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
) )
@@ -30,24 +30,34 @@ func (s *Storage) Get() (*Settings, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if set.UserHomeBasePath == "" { if set.UserHomeBasePath == "" {
set.UserHomeBasePath = DefaultUsersHomeBasePath set.UserHomeBasePath = DefaultUsersHomeBasePath
} }
if set.LogoutPage == "" {
set.LogoutPage = DefaultLogoutPage
}
if set.MinimumPasswordLength == 0 { if set.MinimumPasswordLength == 0 {
set.MinimumPasswordLength = DefaultMinimumPasswordLength set.MinimumPasswordLength = DefaultMinimumPasswordLength
} }
if set.Tus == (Tus{}) { if set.Tus == (Tus{}) {
set.Tus = Tus{ set.Tus = Tus{
ChunkSize: DefaultTusChunkSize, ChunkSize: DefaultTusChunkSize,
RetryCount: DefaultTusRetryCount, RetryCount: DefaultTusRetryCount,
} }
} }
if set.FileMode == 0 { if set.FileMode == 0 {
set.FileMode = DefaultFileMode set.FileMode = DefaultFileMode
} }
if set.DirMode == 0 { if set.DirMode == 0 {
set.DirMode = DefaultDirMode set.DirMode = DefaultDirMode
} }
return set, nil return set, nil
} }
@@ -62,7 +72,7 @@ var defaultEvents = []string{
// Save saves the settings for the current instance. // Save saves the settings for the current instance.
func (s *Storage) Save(set *Settings) error { func (s *Storage) Save(set *Settings) error {
if len(set.Key) == 0 { if len(set.Key) == 0 {
return errors.ErrEmptyKey return fberrors.ErrEmptyKey
} }
if set.Defaults.Locale == "" { if set.Defaults.Locale == "" {

View File

@@ -3,7 +3,7 @@ package share
import ( import (
"time" "time"
"github.com/filebrowser/filebrowser/v2/errors" fberrors "github.com/filebrowser/filebrowser/v2/errors"
) )
// StorageBackend is the interface to implement for a share storage. // 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 { if err := s.Delete(link.Hash); err != nil {
return nil, err return nil, err
} }
return nil, errors.ErrNotExist return nil, fberrors.ErrNotExist
} }
return link, nil return link, nil

View File

@@ -4,7 +4,7 @@ import (
"github.com/asdine/storm/v3" "github.com/asdine/storm/v3"
"github.com/filebrowser/filebrowser/v2/auth" "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" "github.com/filebrowser/filebrowser/v2/settings"
) )
@@ -25,7 +25,7 @@ func (s authBackend) Get(t settings.AuthMethod) (auth.Auther, error) {
case auth.MethodNoAuth: case auth.MethodNoAuth:
auther = &auth.NoAuth{} auther = &auth.NoAuth{}
default: default:
return nil, errors.ErrInvalidAuthMethod return nil, fberrors.ErrInvalidAuthMethod
} }
return auther, get(s.db, "auther", auther) return auther, get(s.db, "auther", auther)

View File

@@ -6,7 +6,7 @@ import (
"github.com/asdine/storm/v3" "github.com/asdine/storm/v3"
"github.com/asdine/storm/v3/q" "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" "github.com/filebrowser/filebrowser/v2/share"
) )
@@ -18,7 +18,7 @@ func (s shareBackend) All() ([]*share.Link, error) {
var v []*share.Link var v []*share.Link
err := s.db.All(&v) err := s.db.All(&v)
if errors.Is(err, storm.ErrNotFound) { if errors.Is(err, storm.ErrNotFound) {
return v, fbErrors.ErrNotExist return v, fberrors.ErrNotExist
} }
return v, err return v, err
@@ -28,7 +28,7 @@ func (s shareBackend) FindByUserID(id uint) ([]*share.Link, error) {
var v []*share.Link var v []*share.Link
err := s.db.Select(q.Eq("UserID", id)).Find(&v) err := s.db.Select(q.Eq("UserID", id)).Find(&v)
if errors.Is(err, storm.ErrNotFound) { if errors.Is(err, storm.ErrNotFound) {
return v, fbErrors.ErrNotExist return v, fberrors.ErrNotExist
} }
return v, err return v, err
@@ -38,7 +38,7 @@ func (s shareBackend) GetByHash(hash string) (*share.Link, error) {
var v share.Link var v share.Link
err := s.db.One("Hash", hash, &v) err := s.db.One("Hash", hash, &v)
if errors.Is(err, storm.ErrNotFound) { if errors.Is(err, storm.ErrNotFound) {
return nil, fbErrors.ErrNotExist return nil, fberrors.ErrNotExist
} }
return &v, err return &v, err
@@ -48,7 +48,7 @@ func (s shareBackend) GetPermanent(path string, id uint) (*share.Link, error) {
var v share.Link var v share.Link
err := s.db.Select(q.Eq("Path", path), q.Eq("Expire", 0), q.Eq("UserID", id)).First(&v) err := s.db.Select(q.Eq("Path", path), q.Eq("Expire", 0), q.Eq("UserID", id)).First(&v)
if errors.Is(err, storm.ErrNotFound) { if errors.Is(err, storm.ErrNotFound) {
return nil, fbErrors.ErrNotExist return nil, fberrors.ErrNotExist
} }
return &v, err return &v, err
@@ -58,7 +58,7 @@ func (s shareBackend) Gets(path string, id uint) ([]*share.Link, error) {
var v []*share.Link var v []*share.Link
err := s.db.Select(q.Eq("Path", path), q.Eq("UserID", id)).Find(&v) err := s.db.Select(q.Eq("Path", path), q.Eq("UserID", id)).Find(&v)
if errors.Is(err, storm.ErrNotFound) { if errors.Is(err, storm.ErrNotFound) {
return v, fbErrors.ErrNotExist return v, fberrors.ErrNotExist
} }
return v, err return v, err

View File

@@ -7,7 +7,7 @@ import (
"github.com/asdine/storm/v3" "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" "github.com/filebrowser/filebrowser/v2/users"
) )
@@ -25,14 +25,14 @@ func (st usersBackend) GetBy(i interface{}) (user *users.User, err error) {
case string: case string:
arg = "Username" arg = "Username"
default: default:
return nil, fbErrors.ErrInvalidDataType return nil, fberrors.ErrInvalidDataType
} }
err = st.db.One(arg, i, user) err = st.db.One(arg, i, user)
if err != nil { if err != nil {
if errors.Is(err, storm.ErrNotFound) { if errors.Is(err, storm.ErrNotFound) {
return nil, fbErrors.ErrNotExist return nil, fberrors.ErrNotExist
} }
return nil, err return nil, err
} }
@@ -44,7 +44,7 @@ func (st usersBackend) Gets() ([]*users.User, error) {
var allUsers []*users.User var allUsers []*users.User
err := st.db.All(&allUsers) err := st.db.All(&allUsers)
if errors.Is(err, storm.ErrNotFound) { if errors.Is(err, storm.ErrNotFound) {
return nil, fbErrors.ErrNotExist return nil, fberrors.ErrNotExist
} }
if err != nil { 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 { func (st usersBackend) Save(user *users.User) error {
err := st.db.Save(user) err := st.db.Save(user)
if errors.Is(err, storm.ErrAlreadyExists) { if errors.Is(err, storm.ErrAlreadyExists) {
return fbErrors.ErrExist return fberrors.ErrExist
} }
return err return err
} }

View File

@@ -5,13 +5,13 @@ import (
"github.com/asdine/storm/v3" "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 { func get(db *storm.DB, name string, to interface{}) error {
err := db.Get("config", name, to) err := db.Get("config", name, to)
if errors.Is(err, storm.ErrNotFound) { if errors.Is(err, storm.ErrNotFound) {
return fbErrors.ErrNotExist return fberrors.ErrNotExist
} }
return err return err

View File

@@ -6,17 +6,17 @@ import (
"golang.org/x/crypto/bcrypt" "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. // ValidateAndHashPwd validates and hashes a password.
func ValidateAndHashPwd(password string, minimumLength uint) (string, error) { func ValidateAndHashPwd(password string, minimumLength uint) (string, error) {
if uint(len(password)) < minimumLength { if uint(len(password)) < minimumLength {
return "", fbErrors.ErrShortPassword{MinimumLength: minimumLength} return "", fberrors.ErrShortPassword{MinimumLength: minimumLength}
} }
if _, ok := commonPasswords[password]; ok { if _, ok := commonPasswords[password]; ok {
return "", fbErrors.ErrEasyPassword return "", fberrors.ErrEasyPassword
} }
return HashPwd(password) return HashPwd(password)

View File

@@ -4,7 +4,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/filebrowser/filebrowser/v2/errors" fberrors "github.com/filebrowser/filebrowser/v2/errors"
) )
// StorageBackend is the interface to implement for a users storage. // StorageBackend is the interface to implement for a users storage.
@@ -109,16 +109,16 @@ func (s *Storage) Delete(id interface{}) error {
return err return err
} }
if user.ID == 1 { if user.ID == 1 {
return errors.ErrRootUserDeletion return fberrors.ErrRootUserDeletion
} }
return s.back.DeleteByUsername(id) return s.back.DeleteByUsername(id)
case uint: case uint:
if id == 1 { if id == 1 {
return errors.ErrRootUserDeletion return fberrors.ErrRootUserDeletion
} }
return s.back.DeleteByID(id) return s.back.DeleteByID(id)
default: default:
return errors.ErrInvalidDataType return fberrors.ErrInvalidDataType
} }
} }

View File

@@ -5,7 +5,7 @@ import (
"github.com/spf13/afero" "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/files"
"github.com/filebrowser/filebrowser/v2/rules" "github.com/filebrowser/filebrowser/v2/rules"
) )
@@ -64,11 +64,11 @@ func (u *User) Clean(baseScope string, fields ...string) error {
switch field { switch field {
case "Username": case "Username":
if u.Username == "" { if u.Username == "" {
return errors.ErrEmptyUsername return fberrors.ErrEmptyUsername
} }
case "Password": case "Password":
if u.Password == "" { if u.Password == "" {
return errors.ErrEmptyPassword return fberrors.ErrEmptyPassword
} }
case "ViewMode": case "ViewMode":
if u.ViewMode == "" { if u.ViewMode == "" {

View File

@@ -21,6 +21,7 @@ filebrowser config init [flags]
-a, --address string address to listen on (default "127.0.0.1") -a, --address string address to listen on (default "127.0.0.1")
--auth.command string command for auth.method=hook --auth.command string command for auth.method=hook
--auth.header string HTTP header for auth.method=proxy --auth.header string HTTP header for auth.method=proxy
--auth.logoutPage string url of custom logout page
--auth.method string authentication type (default "json") --auth.method string authentication type (default "json")
-b, --baseURL string base url -b, --baseURL string base url
--branding.color string set the theme color --branding.color string set the theme color

View File

@@ -18,6 +18,7 @@ filebrowser config set [flags]
-a, --address string address to listen on (default "127.0.0.1") -a, --address string address to listen on (default "127.0.0.1")
--auth.command string command for auth.method=hook --auth.command string command for auth.method=hook
--auth.header string HTTP header for auth.method=proxy --auth.header string HTTP header for auth.method=proxy
--auth.logoutPage string url of custom logout page
--auth.method string authentication type (default "json") --auth.method string authentication type (default "json")
-b, --baseURL string base url -b, --baseURL string base url
--branding.color string set the theme color --branding.color string set the theme color