mirror of
https://github.com/filebrowser/filebrowser.git
synced 2026-01-01 00:00:45 +00:00
Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
943e5340d0 | ||
|
|
88aa56f53b | ||
|
|
a12a612970 | ||
|
|
a2d80c62c1 | ||
|
|
1faa21844f | ||
|
|
7a93b2a062 | ||
|
|
032d6c7520 | ||
|
|
9595f3939c | ||
|
|
60b1ee8bb9 | ||
|
|
79d1aa9229 | ||
|
|
20bfd131c6 | ||
|
|
f89975603e | ||
|
|
2d3b64ecf6 | ||
|
|
579756dbb5 | ||
|
|
c11c986b73 | ||
|
|
3fdca6dfd9 | ||
|
|
cf966578d8 | ||
|
|
6d4c867672 | ||
|
|
169e97e6f9 | ||
|
|
7fa3432f25 | ||
|
|
e9ce7fa5aa | ||
|
|
849f5ad443 | ||
|
|
c1715992bd | ||
|
|
e4f2503298 | ||
|
|
152f8302f7 | ||
|
|
4cbb4b73af | ||
|
|
58cc874828 | ||
|
|
124def5cd7 | ||
|
|
2d88c06761 | ||
|
|
204a3f0eea | ||
|
|
f029c3005e | ||
|
|
a6934e40ff | ||
|
|
98662ac5ec | ||
|
|
5cf8ce8db5 | ||
|
|
062dc414f8 | ||
|
|
63582b644c | ||
|
|
4302ece49b | ||
|
|
e1ee14d827 | ||
|
|
84ca722261 | ||
|
|
b9ac45d5da | ||
|
|
701522a060 | ||
|
|
78e0395960 | ||
|
|
f0680cf0f5 | ||
|
|
982405ec94 | ||
|
|
a78aaed214 | ||
|
|
df11a7dd0e | ||
|
|
79980bcf52 | ||
|
|
3be134f23d | ||
|
|
279a5ccd1e | ||
|
|
87f73ac982 | ||
|
|
85cde140ba | ||
|
|
119609c834 | ||
|
|
d48f5665d6 | ||
|
|
54306bdc87 | ||
|
|
33deedf559 | ||
|
|
88d1eecc4e | ||
|
|
43db19f8c8 | ||
|
|
a360f26979 | ||
|
|
ab367a2740 | ||
|
|
5df5508a85 | ||
|
|
6d5aa355e4 | ||
|
|
a3b5584505 | ||
|
|
8db2411cd4 | ||
|
|
c284de9d2c | ||
|
|
984ea7b569 | ||
|
|
fd7b70cf38 | ||
|
|
13e3b46718 | ||
|
|
d759ab0bd8 | ||
|
|
00323a8f37 | ||
|
|
420adea7e6 |
10
.github/workflows/ci.yaml
vendored
10
.github/workflows/ci.yaml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
name: Lint Frontend
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
package_json_file: "frontend/package.json"
|
||||
@ -31,7 +31,7 @@ jobs:
|
||||
name: Lint Backend
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.25.x"
|
||||
@ -43,7 +43,7 @@ jobs:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.25.x"
|
||||
@ -53,7 +53,7 @@ jobs:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@v6
|
||||
@ -77,7 +77,7 @@ jobs:
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@v6
|
||||
|
||||
24
.github/workflows/docs.yml
vendored
24
.github/workflows/docs.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Install Task
|
||||
@ -28,25 +28,25 @@ jobs:
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||
name: Build and Release Docs
|
||||
permissions:
|
||||
contents: read
|
||||
deployments: write
|
||||
pull-requests: write
|
||||
pages: write
|
||||
id-token: write
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Install Task
|
||||
uses: go-task/setup-task@v1
|
||||
- name: Build site
|
||||
run: task docs
|
||||
- name: Deploy to Cloudflare Pages
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
- name: Upload static files as artifact
|
||||
uses: actions/upload-pages-artifact@v4
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: pages deploy www/public --project-name=${{ secrets.CLOUDFLARE_PROJECT_NAME }}
|
||||
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
path: www/public
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
@ -2,8 +2,13 @@ version: "2"
|
||||
|
||||
linters:
|
||||
default: standard
|
||||
enable:
|
||||
- gocritic
|
||||
- govet
|
||||
- revive
|
||||
exclusions:
|
||||
presets:
|
||||
- std-error-handling
|
||||
- comments
|
||||
paths:
|
||||
- frontend/
|
||||
|
||||
112
CHANGELOG.md
112
CHANGELOG.md
@ -2,6 +2,118 @@
|
||||
|
||||
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.53.0](https://github.com/filebrowser/filebrowser/compare/v2.52.0...v2.53.0) (2025-12-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add "disable image resolution calculation" flag ([#5638](https://github.com/filebrowser/filebrowser/issues/5638)) ([a2d80c6](https://github.com/filebrowser/filebrowser/commit/a2d80c62c1c17962e566f68fb7cac6960ed3e4cb))
|
||||
* support streaming response for search results ([#5630](https://github.com/filebrowser/filebrowser/issues/5630)) ([20bfd13](https://github.com/filebrowser/filebrowser/commit/20bfd131c6a4fca48a645b52171c2d1cc3ce92b7))
|
||||
* update translations ([a12a612](https://github.com/filebrowser/filebrowser/commit/a12a612970d6cc3dfbca1b35ef3a60a887a4effb))
|
||||
* update translations ([#5626](https://github.com/filebrowser/filebrowser/issues/5626)) ([f899756](https://github.com/filebrowser/filebrowser/commit/f89975603e29b9f1fc05aec58afb42bbd56ed696))
|
||||
* update translations ([#5631](https://github.com/filebrowser/filebrowser/issues/5631)) ([032d6c7](https://github.com/filebrowser/filebrowser/commit/032d6c7520a64686c9d9b1218562256f629b4703))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* conversion of backslashes in file paths for archive creation ([#5637](https://github.com/filebrowser/filebrowser/issues/5637)) ([9595f39](https://github.com/filebrowser/filebrowser/commit/9595f3939c1c129ed875a47adcc4fbcfad9a0e65))
|
||||
* Don't crash on invalid config import ([#5640](https://github.com/filebrowser/filebrowser/issues/5640)) ([79d1aa9](https://github.com/filebrowser/filebrowser/commit/79d1aa9229b076ee8e3b71d6cf061fc90738f4da))
|
||||
* fix nil deref in config set command ([#5641](https://github.com/filebrowser/filebrowser/issues/5641)) ([60b1ee8](https://github.com/filebrowser/filebrowser/commit/60b1ee8bb9e18b21d7f2c04cb1cc90046cecd3e1))
|
||||
|
||||
## [2.52.0](https://github.com/filebrowser/filebrowser/compare/v2.51.2...v2.52.0) (2025-12-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* sync translations with Transifex ([7fa3432](https://github.com/filebrowser/filebrowser/commit/7fa3432f25610bbb55a718bc709b9a7bf41d92f0))
|
||||
* update translations ([#5615](https://github.com/filebrowser/filebrowser/issues/5615)) ([3fdca6d](https://github.com/filebrowser/filebrowser/commit/3fdca6dfd9a18c3f4895b4ef3cbd216824dbb57a))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* display the directory name in the shared folder view ([#5617](https://github.com/filebrowser/filebrowser/issues/5617)) ([6d4c867](https://github.com/filebrowser/filebrowser/commit/6d4c86767239dad4f09f30f48678f2f3a716eb12))
|
||||
* hide the context menu when changing the route ([#5613](https://github.com/filebrowser/filebrowser/issues/5613)) ([cf96657](https://github.com/filebrowser/filebrowser/commit/cf966578d8c6beab111b74f495bac6bdec173f41))
|
||||
|
||||
## [2.51.2](https://github.com/filebrowser/filebrowser/compare/v2.51.1...v2.51.2) (2025-12-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **frontend:** add missing i18n strings ([c171599](https://github.com/filebrowser/filebrowser/commit/c1715992bda46517f801c1aa496df8a3b42a4e4d))
|
||||
|
||||
## [2.51.1](https://github.com/filebrowser/filebrowser/compare/v2.51.0...v2.51.1) (2025-12-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **frontend:** csv viewer i18n strings ([4cbb4b7](https://github.com/filebrowser/filebrowser/commit/4cbb4b73af816104475f15c1d996640b56203602))
|
||||
* prevent the right-click from selecting multiple items when the "single-click" option is active ([#5608](https://github.com/filebrowser/filebrowser/issues/5608)) ([152f830](https://github.com/filebrowser/filebrowser/commit/152f8302f7cda21bde37692b175c22c124233f45))
|
||||
|
||||
## [2.51.0](https://github.com/filebrowser/filebrowser/compare/v2.50.0...v2.51.0) (2025-12-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* update translations ([2d88c06](https://github.com/filebrowser/filebrowser/commit/2d88c067611e936056dbbf04247f1c1c709b2a09))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* added column separator select (comma, semicolon and both) in CSV viewer ([#5604](https://github.com/filebrowser/filebrowser/issues/5604)) ([204a3f0](https://github.com/filebrowser/filebrowser/commit/204a3f0eeaa0c68781b60651bf27c4b27eac44e6))
|
||||
|
||||
|
||||
### Refactorings
|
||||
|
||||
* cleanup package names ([#5605](https://github.com/filebrowser/filebrowser/issues/5605)) ([f029c30](https://github.com/filebrowser/filebrowser/commit/f029c3005e450cfbebb074c42dbdf65db9c8d56a))
|
||||
|
||||
## [2.50.0](https://github.com/filebrowser/filebrowser/compare/v2.49.0...v2.50.0) (2025-11-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* configurable logout page URL for proxy/hook auth ([#3884](https://github.com/filebrowser/filebrowser/issues/3884)) ([b9ac45d](https://github.com/filebrowser/filebrowser/commit/b9ac45d5dac4b4eb2ba364629090fbf306cffd2b))
|
||||
* render CSVs as table ([#5569](https://github.com/filebrowser/filebrowser/issues/5569)) ([982405e](https://github.com/filebrowser/filebrowser/commit/982405ec944f94baf43594b0ed2f06329ff4e9ed))
|
||||
* update frontend/src/i18n/hr.json ([279a5cc](https://github.com/filebrowser/filebrowser/commit/279a5ccd1e8d7bde4568b63cb3c506af48b6c618))
|
||||
* update translations ([78e0395](https://github.com/filebrowser/filebrowser/commit/78e039596070a3a9e643a693cc99960c69dcfe92))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* do not close editor if save failed ([701522a](https://github.com/filebrowser/filebrowser/commit/701522a0600cfa542469540ed764630c0ba1a732)), closes [#5591](https://github.com/filebrowser/filebrowser/issues/5591)
|
||||
|
||||
## [2.49.0](https://github.com/filebrowser/filebrowser/compare/v2.48.2...v2.49.0) (2025-11-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add "copy download link to clipboard" button to Share prompt ([#5173](https://github.com/filebrowser/filebrowser/issues/5173)) ([d48f566](https://github.com/filebrowser/filebrowser/commit/d48f5665d6975c4cbbdf9be20dc2e0106db02f01))
|
||||
* add Bulgarian language ([8db2411](https://github.com/filebrowser/filebrowser/commit/8db2411cd43a23ae3292a817e3524cfdb5ae9b86))
|
||||
* Updates for project File Browser ([#5566](https://github.com/filebrowser/filebrowser/issues/5566)) ([54306bd](https://github.com/filebrowser/filebrowser/commit/54306bdc8700fac489326ae81e28ac5db0580d13))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* display friendly error message for password validation on signup ([#5563](https://github.com/filebrowser/filebrowser/issues/5563)) ([6d5aa35](https://github.com/filebrowser/filebrowser/commit/6d5aa355e433d613e5a3ae137f410c63baeddf0f))
|
||||
|
||||
## [2.48.2](https://github.com/filebrowser/filebrowser/compare/v2.48.1...v2.48.2) (2025-11-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add transitionary support for FB_BASEURL ([984ea7b](https://github.com/filebrowser/filebrowser/commit/984ea7b569e3bd33b6f91ebdf63684a618d51e94))
|
||||
|
||||
|
||||
### Refactorings
|
||||
|
||||
* rename python for clarification ([fd7b70c](https://github.com/filebrowser/filebrowser/commit/fd7b70cf38ac67c8c9ff79f2e7fde5e2ec45a1de))
|
||||
|
||||
## [2.48.1](https://github.com/filebrowser/filebrowser/compare/v2.48.0...v2.48.1) (2025-11-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* options should only override if set ([420adea](https://github.com/filebrowser/filebrowser/commit/420adea7e61a1c182cddd6fb2544a0752e5709f7))
|
||||
|
||||
## [2.48.0](https://github.com/filebrowser/filebrowser/compare/v2.47.0...v2.48.0) (2025-11-17)
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
## Multistage build: First stage fetches dependencies
|
||||
FROM alpine:3.22 AS fetcher
|
||||
FROM alpine:3.23 AS fetcher
|
||||
|
||||
# install and copy ca-certificates, mailcap, and tini-static; download JSON.sh
|
||||
RUN apk update && \
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
FROM ghcr.io/linuxserver/baseimage-alpine:3.22
|
||||
FROM ghcr.io/linuxserver/baseimage-alpine:3.23
|
||||
|
||||
RUN apk update && \
|
||||
apk --no-cache add ca-certificates mailcap jq libcap
|
||||
|
||||
@ -49,7 +49,10 @@ tasks:
|
||||
cmds:
|
||||
- task: docs:cli:generate
|
||||
- git add www/docs/cli
|
||||
- "git commit -m 'chore(docs): update CLI documentation'"
|
||||
- |
|
||||
if [[ `git status www/docs/cli --porcelain` ]]; then
|
||||
git commit -m 'chore(docs): update CLI documentation'
|
||||
fi
|
||||
- task: release:dry-run
|
||||
- task: release:make
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
fbErrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
fberrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
"github.com/filebrowser/filebrowser/v2/files"
|
||||
"github.com/filebrowser/filebrowser/v2/settings"
|
||||
"github.com/filebrowser/filebrowser/v2/users"
|
||||
@ -146,7 +146,7 @@ func (a *HookAuth) GetValues(s string) {
|
||||
// SaveUser updates the existing user or creates a new one when not found
|
||||
func (a *HookAuth) SaveUser() (*users.User, error) {
|
||||
u, err := a.Users.Get(a.Server.Root, a.Cred.Username)
|
||||
if err != nil && !errors.Is(err, fbErrors.ErrNotExist) {
|
||||
if err != nil && !errors.Is(err, fberrors.ErrNotExist) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
fbErrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
fberrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
"github.com/filebrowser/filebrowser/v2/settings"
|
||||
"github.com/filebrowser/filebrowser/v2/users"
|
||||
)
|
||||
@ -21,7 +21,7 @@ type ProxyAuth struct {
|
||||
func (a ProxyAuth) Auth(r *http.Request, usr users.Store, setting *settings.Settings, srv *settings.Server) (*users.User, error) {
|
||||
username := r.Header.Get(a.Header)
|
||||
user, err := usr.Get(srv.Root, username)
|
||||
if errors.Is(err, fbErrors.ErrNotExist) {
|
||||
if errors.Is(err, fberrors.ErrNotExist) {
|
||||
return a.createUser(usr, setting, srv, username)
|
||||
}
|
||||
return user, err
|
||||
|
||||
@ -15,18 +15,18 @@ var cmdsAddCmd = &cobra.Command{
|
||||
Short: "Add a command to run on a specific event",
|
||||
Long: `Add a command to run on a specific event.`,
|
||||
Args: cobra.MinimumNArgs(2),
|
||||
RunE: python(func(_ *cobra.Command, args []string, d *pythonData) error {
|
||||
s, err := d.store.Settings.Get()
|
||||
RunE: withStore(func(_ *cobra.Command, args []string, st *store) error {
|
||||
s, err := st.Settings.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
command := strings.Join(args[1:], " ")
|
||||
s.Commands[args[0]] = append(s.Commands[args[0]], command)
|
||||
err = d.store.Settings.Save(s)
|
||||
err = st.Settings.Save(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printEvents(s.Commands)
|
||||
return nil
|
||||
}, pythonConfig{}),
|
||||
}, storeOptions{}),
|
||||
}
|
||||
|
||||
@ -14,8 +14,8 @@ var cmdsLsCmd = &cobra.Command{
|
||||
Short: "List all commands for each event",
|
||||
Long: `List all commands for each event.`,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: python(func(cmd *cobra.Command, _ []string, d *pythonData) error {
|
||||
s, err := d.store.Settings.Get()
|
||||
RunE: withStore(func(cmd *cobra.Command, _ []string, st *store) error {
|
||||
s, err := st.Settings.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -35,5 +35,5 @@ var cmdsLsCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
return nil
|
||||
}, pythonConfig{}),
|
||||
}, storeOptions{}),
|
||||
}
|
||||
|
||||
@ -35,8 +35,8 @@ including 'index_end'.`,
|
||||
|
||||
return nil
|
||||
},
|
||||
RunE: python(func(_ *cobra.Command, args []string, d *pythonData) error {
|
||||
s, err := d.store.Settings.Get()
|
||||
RunE: withStore(func(_ *cobra.Command, args []string, st *store) error {
|
||||
s, err := st.Settings.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -55,11 +55,11 @@ including 'index_end'.`,
|
||||
}
|
||||
|
||||
s.Commands[evt] = append(s.Commands[evt][:i], s.Commands[evt][f+1:]...)
|
||||
err = d.store.Settings.Save(s)
|
||||
err = st.Settings.Save(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printEvents(s.Commands)
|
||||
return nil
|
||||
}, pythonConfig{}),
|
||||
}, storeOptions{}),
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
nerrors "errors"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
@ -12,7 +12,7 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/filebrowser/filebrowser/v2/auth"
|
||||
"github.com/filebrowser/filebrowser/v2/errors"
|
||||
fberrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
"github.com/filebrowser/filebrowser/v2/settings"
|
||||
)
|
||||
|
||||
@ -45,6 +45,7 @@ func addConfigFlags(flags *pflag.FlagSet) {
|
||||
flags.String("auth.method", string(auth.MethodJSONAuth), "authentication type")
|
||||
flags.String("auth.header", "", "HTTP header for auth.method=proxy")
|
||||
flags.String("auth.command", "", "command for auth.method=hook")
|
||||
flags.String("auth.logoutPage", "", "url of custom logout page")
|
||||
|
||||
flags.String("recaptcha.host", "https://www.google.com", "use another host for ReCAPTCHA. recaptcha.net might be useful in China")
|
||||
flags.String("recaptcha.key", "", "ReCaptcha site key")
|
||||
@ -98,12 +99,12 @@ func getProxyAuth(flags *pflag.FlagSet, defaultAuther map[string]interface{}) (a
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if header == "" {
|
||||
if header == "" && defaultAuther != nil {
|
||||
header = defaultAuther["header"].(string)
|
||||
}
|
||||
|
||||
if header == "" {
|
||||
return nil, nerrors.New("you must set the flag 'auth.header' for method 'proxy'")
|
||||
return nil, errors.New("you must set the flag 'auth.header' for method 'proxy'")
|
||||
}
|
||||
|
||||
return &auth.ProxyAuth{Header: header}, nil
|
||||
@ -162,7 +163,7 @@ func getHookAuth(flags *pflag.FlagSet, defaultAuther map[string]interface{}) (au
|
||||
}
|
||||
|
||||
if command == "" {
|
||||
return nil, nerrors.New("you must set the flag 'auth.command' for method 'hook'")
|
||||
return nil, errors.New("you must set the flag 'auth.command' for method 'hook'")
|
||||
}
|
||||
|
||||
return &auth.HookAuth{Command: command}, nil
|
||||
@ -185,7 +186,7 @@ func getAuthentication(flags *pflag.FlagSet, defaults ...interface{}) (settings.
|
||||
case auth.MethodHookAuth:
|
||||
auther, err = getHookAuth(flags, defaultAuther)
|
||||
default:
|
||||
return "", nil, errors.ErrInvalidAuthMethod
|
||||
return "", nil, fberrors.ErrInvalidAuthMethod
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -201,6 +202,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
|
||||
fmt.Fprintf(w, "Sign up:\t%t\n", set.Signup)
|
||||
fmt.Fprintf(w, "Hide Login Button:\t%t\n", set.HideLoginButton)
|
||||
fmt.Fprintf(w, "Create User Dir:\t%t\n", set.CreateUserDir)
|
||||
fmt.Fprintf(w, "Logout Page:\t%s\n", set.LogoutPage)
|
||||
fmt.Fprintf(w, "Minimum Password Length:\t%d\n", set.MinimumPasswordLength)
|
||||
fmt.Fprintf(w, "Auth Method:\t%s\n", set.AuthMethod)
|
||||
fmt.Fprintf(w, "Shell:\t%s\t\n", strings.Join(set.Shell, " "))
|
||||
@ -306,6 +308,9 @@ func getSettings(flags *pflag.FlagSet, set *settings.Settings, ser *settings.Ser
|
||||
case "disableTypeDetectionByHeader":
|
||||
ser.TypeDetectionByHeader, err = flags.GetBool(flag.Name)
|
||||
ser.TypeDetectionByHeader = !ser.TypeDetectionByHeader
|
||||
case "disableImageResolutionCalc":
|
||||
ser.ImageResolutionCal, err = flags.GetBool(flag.Name)
|
||||
ser.ImageResolutionCal = !ser.ImageResolutionCal
|
||||
|
||||
// Settings flags from [addConfigFlags]
|
||||
case "signup":
|
||||
@ -328,6 +333,8 @@ func getSettings(flags *pflag.FlagSet, set *settings.Settings, ser *settings.Ser
|
||||
set.DirMode, err = getAndParseFileMode(flags, flag.Name)
|
||||
case "auth.method":
|
||||
hasAuth = true
|
||||
case "auth.logoutPage":
|
||||
set.LogoutPage, err = flags.GetString(flag.Name)
|
||||
case "branding.name":
|
||||
set.Branding.Name, err = flags.GetString(flag.Name)
|
||||
case "branding.theme":
|
||||
@ -357,7 +364,7 @@ func getSettings(flags *pflag.FlagSet, set *settings.Settings, ser *settings.Ser
|
||||
flags.Visit(visit)
|
||||
}
|
||||
|
||||
err := nerrors.Join(errs...)
|
||||
err := errors.Join(errs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -13,19 +13,19 @@ var configCatCmd = &cobra.Command{
|
||||
Short: "Prints the configuration",
|
||||
Long: `Prints the configuration.`,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: python(func(_ *cobra.Command, _ []string, d *pythonData) error {
|
||||
set, err := d.store.Settings.Get()
|
||||
RunE: withStore(func(_ *cobra.Command, _ []string, st *store) error {
|
||||
set, err := st.Settings.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ser, err := d.store.Settings.GetServer()
|
||||
ser, err := st.Settings.GetServer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
auther, err := d.store.Auth.Get(set.AuthMethod)
|
||||
auther, err := st.Auth.Get(set.AuthMethod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printSettings(ser, set, auther)
|
||||
}, pythonConfig{}),
|
||||
}, storeOptions{}),
|
||||
}
|
||||
|
||||
@ -15,18 +15,18 @@ var configExportCmd = &cobra.Command{
|
||||
json or yaml file. This exported configuration can be changed,
|
||||
and imported again with 'config import' command.`,
|
||||
Args: jsonYamlArg,
|
||||
RunE: python(func(_ *cobra.Command, args []string, d *pythonData) error {
|
||||
settings, err := d.store.Settings.Get()
|
||||
RunE: withStore(func(_ *cobra.Command, args []string, st *store) error {
|
||||
settings, err := st.Settings.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
server, err := d.store.Settings.GetServer()
|
||||
server, err := st.Settings.GetServer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
auther, err := d.store.Auth.Get(settings.AuthMethod)
|
||||
auther, err := st.Auth.Get(settings.AuthMethod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -42,5 +42,5 @@ and imported again with 'config import' command.`,
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, pythonConfig{}),
|
||||
}, storeOptions{}),
|
||||
}
|
||||
|
||||
@ -34,11 +34,11 @@ database.
|
||||
|
||||
The path must be for a json or yaml file.`,
|
||||
Args: jsonYamlArg,
|
||||
RunE: python(func(_ *cobra.Command, args []string, d *pythonData) error {
|
||||
RunE: withStore(func(_ *cobra.Command, args []string, st *store) error {
|
||||
var key []byte
|
||||
var err error
|
||||
if d.databaseExisted {
|
||||
settings, settingErr := d.store.Settings.Get()
|
||||
if st.databaseExisted {
|
||||
settings, settingErr := st.Settings.Get()
|
||||
if settingErr != nil {
|
||||
return settingErr
|
||||
}
|
||||
@ -53,13 +53,17 @@ The path must be for a json or yaml file.`,
|
||||
return err
|
||||
}
|
||||
|
||||
if file.Settings == nil || file.Server == nil {
|
||||
return errors.New("invalid configuration file: 'settings' or 'server' fields are missing. Please ensure you are importing a file generated by the 'config export' command")
|
||||
}
|
||||
|
||||
file.Settings.Key = key
|
||||
err = d.store.Settings.Save(file.Settings)
|
||||
err = st.Settings.Save(file.Settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = d.store.Settings.SaveServer(file.Server)
|
||||
err = st.Settings.SaveServer(file.Server)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -98,13 +102,13 @@ The path must be for a json or yaml file.`,
|
||||
return autherErr
|
||||
}
|
||||
|
||||
err = d.store.Auth.Save(auther)
|
||||
err = st.Auth.Save(auther)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return printSettings(file.Server, file.Settings, auther)
|
||||
}, pythonConfig{allowsNoDatabase: true}),
|
||||
}, storeOptions{allowsNoDatabase: true}),
|
||||
}
|
||||
|
||||
func getAuther(sample auth.Auther, data interface{}) (interface{}, error) {
|
||||
|
||||
@ -22,7 +22,7 @@ this options can be changed in the future with the command
|
||||
to the defaults when creating new users and you don't
|
||||
override the options.`,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: python(func(cmd *cobra.Command, _ []string, d *pythonData) error {
|
||||
RunE: withStore(func(cmd *cobra.Command, _ []string, st *store) error {
|
||||
flags := cmd.Flags()
|
||||
|
||||
// Initialize config
|
||||
@ -36,17 +36,17 @@ override the options.`,
|
||||
}
|
||||
|
||||
// Save updated config
|
||||
err = d.store.Settings.Save(s)
|
||||
err = st.Settings.Save(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = d.store.Settings.SaveServer(ser)
|
||||
err = st.Settings.SaveServer(ser)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = d.store.Auth.Save(auther)
|
||||
err = st.Auth.Save(auther)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -57,5 +57,5 @@ Now add your first user via 'filebrowser users add' and then you just
|
||||
need to call the main command to boot up the server.
|
||||
`)
|
||||
return printSettings(ser, s, auther)
|
||||
}, pythonConfig{expectsNoDatabase: true}),
|
||||
}, storeOptions{expectsNoDatabase: true}),
|
||||
}
|
||||
|
||||
@ -15,21 +15,21 @@ var configSetCmd = &cobra.Command{
|
||||
Long: `Updates the configuration. Set the flags for the options
|
||||
you want to change. Other options will remain unchanged.`,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: python(func(cmd *cobra.Command, _ []string, d *pythonData) error {
|
||||
RunE: withStore(func(cmd *cobra.Command, _ []string, st *store) error {
|
||||
flags := cmd.Flags()
|
||||
|
||||
// Read existing config
|
||||
set, err := d.store.Settings.Get()
|
||||
set, err := st.Settings.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ser, err := d.store.Settings.GetServer()
|
||||
ser, err := st.Settings.GetServer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
auther, err := d.store.Auth.Get(set.AuthMethod)
|
||||
auther, err := st.Auth.Get(set.AuthMethod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -41,21 +41,21 @@ you want to change. Other options will remain unchanged.`,
|
||||
}
|
||||
|
||||
// Save updated config
|
||||
err = d.store.Auth.Save(auther)
|
||||
err = st.Auth.Save(auther)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = d.store.Settings.Save(set)
|
||||
err = st.Settings.Save(set)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = d.store.Settings.SaveServer(ser)
|
||||
err = st.Settings.SaveServer(ser)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return printSettings(ser, set, auther)
|
||||
}, pythonConfig{}),
|
||||
}, storeOptions{}),
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ var docsCmd = &cobra.Command{
|
||||
|
||||
rootCmd.Root().DisableAutoGenTag = true
|
||||
|
||||
err = doc.GenMarkdownTreeCustom(cmd.Root(), tempDir, func(f string) string {
|
||||
err = doc.GenMarkdownTreeCustom(cmd.Root(), tempDir, func(_ string) string {
|
||||
return ""
|
||||
}, func(s string) string {
|
||||
return s
|
||||
|
||||
164
cmd/root.go
164
cmd/root.go
@ -54,12 +54,12 @@ var (
|
||||
)
|
||||
|
||||
// TODO(remove): remove after July 2026.
|
||||
func migrateFlagNames(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||
func migrateFlagNames(_ *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||
if newName, ok := flagNamesMigrations[name]; ok {
|
||||
|
||||
if !warnedFlags[name] {
|
||||
warnedFlags[name] = true
|
||||
fmt.Printf("WARNING: Flag --%s has been deprecated, use --%s instead\n", name, newName)
|
||||
log.Printf("DEPRECATION NOTICE: Flag --%s has been deprecated, use --%s instead\n", name, newName)
|
||||
}
|
||||
|
||||
name = newName
|
||||
@ -108,6 +108,7 @@ func addServerFlags(flags *pflag.FlagSet) {
|
||||
flags.Bool("disablePreviewResize", false, "disable resize of image previews")
|
||||
flags.Bool("disableExec", true, "disables Command Runner feature")
|
||||
flags.Bool("disableTypeDetectionByHeader", false, "disables type detection by reading file headers")
|
||||
flags.Bool("disableImageResolutionCalc", false, "disables image resolution calculation by reading image files")
|
||||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
@ -135,6 +136,11 @@ file named .filebrowser.{json, toml, yaml, yml} in the following directories:
|
||||
- $HOME/
|
||||
- /etc/filebrowser/
|
||||
|
||||
**Note:** Only the options listed below can be set via the config file or
|
||||
environment variables. Other configuration options live exclusively in the
|
||||
database and so they must be set by the "config set" or "config
|
||||
import" commands.
|
||||
|
||||
The precedence of the configuration values are as follows:
|
||||
|
||||
- Flags
|
||||
@ -146,23 +152,23 @@ The precedence of the configuration values are as follows:
|
||||
Also, if the database path doesn't exist, File Browser will enter into
|
||||
the quick setup mode and a new database will be bootstrapped and a new
|
||||
user created with the credentials from options "username" and "password".`,
|
||||
RunE: python(func(cmd *cobra.Command, _ []string, d *pythonData) error {
|
||||
if !d.databaseExisted {
|
||||
err := quickSetup(*d)
|
||||
RunE: withViperAndStore(func(_ *cobra.Command, _ []string, v *viper.Viper, st *store) error {
|
||||
if !st.databaseExisted {
|
||||
err := quickSetup(v, st.Storage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// build img service
|
||||
imgWorkersCount := d.viper.GetInt("imageProcessors")
|
||||
imgWorkersCount := v.GetInt("imageProcessors")
|
||||
if imgWorkersCount < 1 {
|
||||
return errors.New("image resize workers count could not be < 1")
|
||||
}
|
||||
imageService := img.New(imgWorkersCount)
|
||||
|
||||
var fileCache diskcache.Interface = diskcache.NewNoOp()
|
||||
cacheDir := d.viper.GetString("cacheDir")
|
||||
cacheDir := v.GetString("cacheDir")
|
||||
if cacheDir != "" {
|
||||
if err := os.MkdirAll(cacheDir, 0700); err != nil {
|
||||
return fmt.Errorf("can't make directory %s: %w", cacheDir, err)
|
||||
@ -170,7 +176,7 @@ user created with the credentials from options "username" and "password".`,
|
||||
fileCache = diskcache.New(afero.NewOsFs(), cacheDir)
|
||||
}
|
||||
|
||||
server, err := getServerSettings(d.viper, d.store)
|
||||
server, err := getServerSettings(v, st.Storage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -192,7 +198,7 @@ user created with the credentials from options "username" and "password".`,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
socketPerm := d.viper.GetUint32("socketPerm")
|
||||
socketPerm := v.GetUint32("socketPerm")
|
||||
err = os.Chmod(server.Socket, os.FileMode(socketPerm))
|
||||
if err != nil {
|
||||
return err
|
||||
@ -221,7 +227,7 @@ user created with the credentials from options "username" and "password".`,
|
||||
panic(err)
|
||||
}
|
||||
|
||||
handler, err := fbhttp.NewHandler(imageService, fileCache, d.store, server, assetsFs)
|
||||
handler, err := fbhttp.NewHandler(imageService, fileCache, st.Storage, server, assetsFs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -262,7 +268,7 @@ user created with the credentials from options "username" and "password".`,
|
||||
log.Println("Graceful shutdown complete.")
|
||||
|
||||
return nil
|
||||
}, pythonConfig{allowsNoDatabase: true}),
|
||||
}, storeOptions{allowsNoDatabase: true}),
|
||||
}
|
||||
|
||||
func getServerSettings(v *viper.Viper, st *storage.Storage) (*settings.Server, error) {
|
||||
@ -271,48 +277,72 @@ func getServerSettings(v *viper.Viper, st *storage.Storage) (*settings.Server, e
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if val, set := vGetStringIsSet(v, "root"); set {
|
||||
server.Root = val
|
||||
}
|
||||
|
||||
if val, set := vGetStringIsSet(v, "baseURL"); set {
|
||||
server.BaseURL = val
|
||||
}
|
||||
|
||||
if val, set := vGetStringIsSet(v, "log"); set {
|
||||
server.Log = val
|
||||
}
|
||||
|
||||
isSocketSet := false
|
||||
isAddrSet := false
|
||||
|
||||
if val, set := vGetStringIsSet(v, "address"); set {
|
||||
server.Address = val
|
||||
isAddrSet = isAddrSet || set
|
||||
if v.IsSet("address") {
|
||||
server.Address = v.GetString("address")
|
||||
isAddrSet = true
|
||||
}
|
||||
|
||||
if val, set := vGetStringIsSet(v, "port"); set {
|
||||
server.Port = val
|
||||
isAddrSet = isAddrSet || set
|
||||
if v.IsSet("log") {
|
||||
server.Log = v.GetString("log")
|
||||
}
|
||||
|
||||
if val, set := vGetStringIsSet(v, "key"); set {
|
||||
server.TLSKey = val
|
||||
isAddrSet = isAddrSet || set
|
||||
if v.IsSet("port") {
|
||||
server.Port = v.GetString("port")
|
||||
isAddrSet = true
|
||||
}
|
||||
|
||||
if val, set := vGetStringIsSet(v, "cert"); set {
|
||||
server.TLSCert = val
|
||||
isAddrSet = isAddrSet || set
|
||||
if v.IsSet("cert") {
|
||||
server.TLSCert = v.GetString("cert")
|
||||
isAddrSet = true
|
||||
}
|
||||
|
||||
if val, set := vGetStringIsSet(v, "socket"); set {
|
||||
server.Socket = val
|
||||
isSocketSet = isSocketSet || set
|
||||
if v.IsSet("key") {
|
||||
server.TLSKey = v.GetString("key")
|
||||
isAddrSet = true
|
||||
}
|
||||
|
||||
if val, set := vGetStringIsSet(v, "tokenExpirationTime"); set {
|
||||
server.TokenExpirationTime = val
|
||||
if v.IsSet("root") {
|
||||
server.Root = v.GetString("root")
|
||||
}
|
||||
|
||||
if v.IsSet("socket") {
|
||||
server.Socket = v.GetString("socket")
|
||||
isSocketSet = true
|
||||
}
|
||||
|
||||
if v.IsSet("baseURL") {
|
||||
server.BaseURL = v.GetString("baseURL")
|
||||
// TODO(remove): remove after July 2026.
|
||||
} else if v := os.Getenv("FB_BASEURL"); v != "" {
|
||||
log.Println("DEPRECATION NOTICE: Environment variable FB_BASEURL has been deprecated, use FB_BASE_URL instead")
|
||||
server.BaseURL = v
|
||||
}
|
||||
|
||||
if v.IsSet("tokenExpirationTime") {
|
||||
server.TokenExpirationTime = v.GetString("tokenExpirationTime")
|
||||
}
|
||||
|
||||
if v.IsSet("disableThumbnails") {
|
||||
server.EnableThumbnails = !v.GetBool("disableThumbnails")
|
||||
}
|
||||
|
||||
if v.IsSet("disablePreviewResize") {
|
||||
server.ResizePreview = !v.GetBool("disablePreviewResize")
|
||||
}
|
||||
|
||||
if v.IsSet("disableTypeDetectionByHeader") {
|
||||
server.TypeDetectionByHeader = !v.GetBool("disableTypeDetectionByHeader")
|
||||
}
|
||||
|
||||
if v.IsSet("disableImageResolutionCalc") {
|
||||
server.ImageResolutionCal = !v.GetBool("disableImageResolutionCalc")
|
||||
}
|
||||
|
||||
if v.IsSet("disableExec") {
|
||||
server.EnableExec = !v.GetBool("disableExec")
|
||||
}
|
||||
|
||||
if isAddrSet && isSocketSet {
|
||||
@ -324,11 +354,6 @@ func getServerSettings(v *viper.Viper, st *storage.Storage) (*settings.Server, e
|
||||
server.Socket = ""
|
||||
}
|
||||
|
||||
server.EnableThumbnails = !v.GetBool("disableThumbnails")
|
||||
server.ResizePreview = !v.GetBool("disablePreviewResize")
|
||||
server.TypeDetectionByHeader = !v.GetBool("disableTypeDetectionByHeader")
|
||||
server.EnableExec = !v.GetBool("disableExec")
|
||||
|
||||
if server.EnableExec {
|
||||
log.Println("WARNING: Command Runner feature enabled!")
|
||||
log.Println("WARNING: This feature has known security vulnerabilities and should not")
|
||||
@ -339,10 +364,6 @@ func getServerSettings(v *viper.Viper, st *storage.Storage) (*settings.Server, e
|
||||
return server, nil
|
||||
}
|
||||
|
||||
func vGetStringIsSet(v *viper.Viper, key string) (string, bool) {
|
||||
return v.GetString(key), v.IsSet(key)
|
||||
}
|
||||
|
||||
func setupLog(logMethod string) {
|
||||
switch logMethod {
|
||||
case "stdout":
|
||||
@ -361,7 +382,7 @@ func setupLog(logMethod string) {
|
||||
}
|
||||
}
|
||||
|
||||
func quickSetup(d pythonData) error {
|
||||
func quickSetup(v *viper.Viper, s *storage.Storage) error {
|
||||
log.Println("Performing quick setup")
|
||||
|
||||
set := &settings.Settings{
|
||||
@ -375,7 +396,7 @@ func quickSetup(d pythonData) error {
|
||||
Scope: ".",
|
||||
Locale: "en",
|
||||
SingleClick: false,
|
||||
AceEditorTheme: d.viper.GetString("defaults.aceEditorTheme"),
|
||||
AceEditorTheme: v.GetString("defaults.aceEditorTheme"),
|
||||
Perm: users.Permissions{
|
||||
Admin: false,
|
||||
Execute: true,
|
||||
@ -399,44 +420,45 @@ func quickSetup(d pythonData) error {
|
||||
}
|
||||
|
||||
var err error
|
||||
if _, noauth := vGetStringIsSet(d.viper, "noauth"); noauth {
|
||||
if v.GetBool("noauth") {
|
||||
set.AuthMethod = auth.MethodNoAuth
|
||||
err = d.store.Auth.Save(&auth.NoAuth{})
|
||||
err = s.Auth.Save(&auth.NoAuth{})
|
||||
} else {
|
||||
set.AuthMethod = auth.MethodJSONAuth
|
||||
err = d.store.Auth.Save(&auth.JSONAuth{})
|
||||
err = s.Auth.Save(&auth.JSONAuth{})
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = d.store.Settings.Save(set)
|
||||
err = s.Settings.Save(set)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ser := &settings.Server{
|
||||
BaseURL: d.viper.GetString("baseURL"),
|
||||
Port: d.viper.GetString("port"),
|
||||
Log: d.viper.GetString("log"),
|
||||
TLSKey: d.viper.GetString("key"),
|
||||
TLSCert: d.viper.GetString("cert"),
|
||||
Address: d.viper.GetString("address"),
|
||||
Root: d.viper.GetString("root"),
|
||||
TokenExpirationTime: d.viper.GetString("tokenExpirationTime"),
|
||||
EnableThumbnails: !d.viper.GetBool("disableThumbnails"),
|
||||
ResizePreview: !d.viper.GetBool("disablePreviewResize"),
|
||||
EnableExec: !d.viper.GetBool("disableExec"),
|
||||
TypeDetectionByHeader: !d.viper.GetBool("disableTypeDetectionByHeader"),
|
||||
BaseURL: v.GetString("baseURL"),
|
||||
Port: v.GetString("port"),
|
||||
Log: v.GetString("log"),
|
||||
TLSKey: v.GetString("key"),
|
||||
TLSCert: v.GetString("cert"),
|
||||
Address: v.GetString("address"),
|
||||
Root: v.GetString("root"),
|
||||
TokenExpirationTime: v.GetString("tokenExpirationTime"),
|
||||
EnableThumbnails: !v.GetBool("disableThumbnails"),
|
||||
ResizePreview: !v.GetBool("disablePreviewResize"),
|
||||
EnableExec: !v.GetBool("disableExec"),
|
||||
TypeDetectionByHeader: !v.GetBool("disableTypeDetectionByHeader"),
|
||||
ImageResolutionCal: !v.GetBool("disableImageResolutionCalc"),
|
||||
}
|
||||
|
||||
err = d.store.Settings.SaveServer(ser)
|
||||
err = s.Settings.SaveServer(ser)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
username := d.viper.GetString("username")
|
||||
password := d.viper.GetString("password")
|
||||
username := v.GetString("username")
|
||||
password := v.GetString("password")
|
||||
|
||||
if password == "" {
|
||||
var pwd string
|
||||
@ -467,5 +489,5 @@ func quickSetup(d pythonData) error {
|
||||
set.Defaults.Apply(user)
|
||||
user.Perm.Admin = true
|
||||
|
||||
return d.store.Users.Save(user)
|
||||
return s.Users.Save(user)
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ including 'index_end'.`,
|
||||
|
||||
return nil
|
||||
},
|
||||
RunE: python(func(cmd *cobra.Command, args []string, d *pythonData) error {
|
||||
RunE: withStore(func(cmd *cobra.Command, args []string, st *store) error {
|
||||
i, err := strconv.Atoi(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
@ -55,14 +55,14 @@ including 'index_end'.`,
|
||||
|
||||
user := func(u *users.User) error {
|
||||
u.Rules = append(u.Rules[:i], u.Rules[f+1:]...)
|
||||
return d.store.Users.Save(u)
|
||||
return st.Users.Save(u)
|
||||
}
|
||||
|
||||
global := func(s *settings.Settings) error {
|
||||
s.Rules = append(s.Rules[:i], s.Rules[f+1:]...)
|
||||
return d.store.Settings.Save(s)
|
||||
return st.Settings.Save(s)
|
||||
}
|
||||
|
||||
return runRules(d.store, cmd, user, global)
|
||||
}, pythonConfig{}),
|
||||
return runRules(st.Storage, cmd, user, global)
|
||||
}, storeOptions{}),
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ var rulesAddCmd = &cobra.Command{
|
||||
Short: "Add a global rule or user rule",
|
||||
Long: `Add a global rule or user rule.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: python(func(cmd *cobra.Command, args []string, d *pythonData) error {
|
||||
RunE: withStore(func(cmd *cobra.Command, args []string, st *store) error {
|
||||
flags := cmd.Flags()
|
||||
|
||||
allow, err := flags.GetBool("allow")
|
||||
@ -53,14 +53,14 @@ var rulesAddCmd = &cobra.Command{
|
||||
|
||||
user := func(u *users.User) error {
|
||||
u.Rules = append(u.Rules, rule)
|
||||
return d.store.Users.Save(u)
|
||||
return st.Users.Save(u)
|
||||
}
|
||||
|
||||
global := func(s *settings.Settings) error {
|
||||
s.Rules = append(s.Rules, rule)
|
||||
return d.store.Settings.Save(s)
|
||||
return st.Settings.Save(s)
|
||||
}
|
||||
|
||||
return runRules(d.store, cmd, user, global)
|
||||
}, pythonConfig{}),
|
||||
return runRules(st.Storage, cmd, user, global)
|
||||
}, storeOptions{}),
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ var rulesLsCommand = &cobra.Command{
|
||||
Short: "List global rules or user specific rules",
|
||||
Long: `List global rules or user specific rules.`,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: python(func(cmd *cobra.Command, _ []string, d *pythonData) error {
|
||||
return runRules(d.store, cmd, nil, nil)
|
||||
}, pythonConfig{}),
|
||||
RunE: withStore(func(cmd *cobra.Command, _ []string, st *store) error {
|
||||
return runRules(st.Storage, cmd, nil, nil)
|
||||
}, storeOptions{}),
|
||||
}
|
||||
|
||||
@ -16,9 +16,9 @@ var usersAddCmd = &cobra.Command{
|
||||
Short: "Create a new user",
|
||||
Long: `Create a new user and add it to the database.`,
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: python(func(cmd *cobra.Command, args []string, d *pythonData) error {
|
||||
RunE: withStore(func(cmd *cobra.Command, args []string, st *store) error {
|
||||
flags := cmd.Flags()
|
||||
s, err := d.store.Settings.Get()
|
||||
s, err := st.Settings.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -54,14 +54,14 @@ var usersAddCmd = &cobra.Command{
|
||||
|
||||
s.Defaults.Apply(user)
|
||||
|
||||
servSettings, err := d.store.Settings.GetServer()
|
||||
servSettings, err := st.Settings.GetServer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// since getUserDefaults() polluted s.Defaults.Scope
|
||||
// which makes the Scope not the one saved in the db
|
||||
// we need the right s.Defaults.Scope here
|
||||
s2, err := d.store.Settings.Get()
|
||||
s2, err := st.Settings.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -72,11 +72,11 @@ var usersAddCmd = &cobra.Command{
|
||||
}
|
||||
user.Scope = userHome
|
||||
|
||||
err = d.store.Users.Save(user)
|
||||
err = st.Users.Save(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printUsers([]*users.User{user})
|
||||
return nil
|
||||
}, pythonConfig{}),
|
||||
}, storeOptions{}),
|
||||
}
|
||||
|
||||
@ -14,8 +14,8 @@ var usersExportCmd = &cobra.Command{
|
||||
Long: `Export all users to a json or yaml file. Please indicate the
|
||||
path to the file where you want to write the users.`,
|
||||
Args: jsonYamlArg,
|
||||
RunE: python(func(_ *cobra.Command, args []string, d *pythonData) error {
|
||||
list, err := d.store.Users.Gets("")
|
||||
RunE: withStore(func(_ *cobra.Command, args []string, st *store) error {
|
||||
list, err := st.Users.Gets("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -25,5 +25,5 @@ path to the file where you want to write the users.`,
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, pythonConfig{}),
|
||||
}, storeOptions{}),
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ var usersLsCmd = &cobra.Command{
|
||||
RunE: findUsers,
|
||||
}
|
||||
|
||||
var findUsers = python(func(_ *cobra.Command, args []string, d *pythonData) error {
|
||||
var findUsers = withStore(func(_ *cobra.Command, args []string, st *store) error {
|
||||
var (
|
||||
list []*users.User
|
||||
user *users.User
|
||||
@ -36,14 +36,14 @@ var findUsers = python(func(_ *cobra.Command, args []string, d *pythonData) erro
|
||||
if len(args) == 1 {
|
||||
username, id := parseUsernameOrID(args[0])
|
||||
if username != "" {
|
||||
user, err = d.store.Users.Get("", username)
|
||||
user, err = st.Users.Get("", username)
|
||||
} else {
|
||||
user, err = d.store.Users.Get("", id)
|
||||
user, err = st.Users.Get("", id)
|
||||
}
|
||||
|
||||
list = []*users.User{user}
|
||||
} else {
|
||||
list, err = d.store.Users.Gets("")
|
||||
list, err = st.Users.Gets("")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -51,4 +51,4 @@ var findUsers = python(func(_ *cobra.Command, args []string, d *pythonData) erro
|
||||
}
|
||||
printUsers(list)
|
||||
return nil
|
||||
}, pythonConfig{})
|
||||
}, storeOptions{})
|
||||
|
||||
@ -25,7 +25,7 @@ file. You can use this command to import new users to your
|
||||
installation. For that, just don't place their ID on the files
|
||||
list or set it to 0.`,
|
||||
Args: jsonYamlArg,
|
||||
RunE: python(func(cmd *cobra.Command, args []string, d *pythonData) error {
|
||||
RunE: withStore(func(cmd *cobra.Command, args []string, st *store) error {
|
||||
flags := cmd.Flags()
|
||||
fd, err := os.Open(args[0])
|
||||
if err != nil {
|
||||
@ -52,7 +52,7 @@ list or set it to 0.`,
|
||||
}
|
||||
|
||||
if replace {
|
||||
oldUsers, userImportErr := d.store.Users.Gets("")
|
||||
oldUsers, userImportErr := st.Users.Gets("")
|
||||
if userImportErr != nil {
|
||||
return userImportErr
|
||||
}
|
||||
@ -63,7 +63,7 @@ list or set it to 0.`,
|
||||
}
|
||||
|
||||
for _, user := range oldUsers {
|
||||
err = d.store.Users.Delete(user.ID)
|
||||
err = st.Users.Delete(user.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -76,7 +76,7 @@ list or set it to 0.`,
|
||||
}
|
||||
|
||||
for _, user := range list {
|
||||
onDB, err := d.store.Users.Get("", user.ID)
|
||||
onDB, err := st.Users.Get("", user.ID)
|
||||
|
||||
// User exists in DB.
|
||||
if err == nil {
|
||||
@ -88,7 +88,7 @@ list or set it to 0.`,
|
||||
// with the new username. If there is, print an error and cancel the
|
||||
// operation
|
||||
if user.Username != onDB.Username {
|
||||
if conflictuous, err := d.store.Users.Get("", user.Username); err == nil {
|
||||
if conflictuous, err := st.Users.Get("", user.Username); err == nil {
|
||||
return usernameConflictError(user.Username, conflictuous.ID, user.ID)
|
||||
}
|
||||
}
|
||||
@ -98,13 +98,13 @@ list or set it to 0.`,
|
||||
user.ID = 0
|
||||
}
|
||||
|
||||
err = d.store.Users.Save(user)
|
||||
err = st.Users.Save(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}, pythonConfig{}),
|
||||
}, storeOptions{}),
|
||||
}
|
||||
|
||||
func usernameConflictError(username string, originalID, newID uint) error {
|
||||
|
||||
@ -15,14 +15,14 @@ var usersRmCmd = &cobra.Command{
|
||||
Short: "Delete a user by username or id",
|
||||
Long: `Delete a user by username or id`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: python(func(_ *cobra.Command, args []string, d *pythonData) error {
|
||||
RunE: withStore(func(_ *cobra.Command, args []string, st *store) error {
|
||||
username, id := parseUsernameOrID(args[0])
|
||||
var err error
|
||||
|
||||
if username != "" {
|
||||
err = d.store.Users.Delete(username)
|
||||
err = st.Users.Delete(username)
|
||||
} else {
|
||||
err = d.store.Users.Delete(id)
|
||||
err = st.Users.Delete(id)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -30,5 +30,5 @@ var usersRmCmd = &cobra.Command{
|
||||
}
|
||||
fmt.Println("user deleted successfully")
|
||||
return nil
|
||||
}, pythonConfig{}),
|
||||
}, storeOptions{}),
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ var usersUpdateCmd = &cobra.Command{
|
||||
Long: `Updates an existing user. Set the flags for the
|
||||
options you want to change.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: python(func(cmd *cobra.Command, args []string, d *pythonData) error {
|
||||
RunE: withStore(func(cmd *cobra.Command, args []string, st *store) error {
|
||||
flags := cmd.Flags()
|
||||
username, id := parseUsernameOrID(args[0])
|
||||
password, err := flags.GetString("password")
|
||||
@ -34,7 +34,7 @@ options you want to change.`,
|
||||
return err
|
||||
}
|
||||
|
||||
s, err := d.store.Settings.Get()
|
||||
s, err := st.Settings.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -43,9 +43,9 @@ options you want to change.`,
|
||||
user *users.User
|
||||
)
|
||||
if id != 0 {
|
||||
user, err = d.store.Users.Get("", id)
|
||||
user, err = st.Users.Get("", id)
|
||||
} else {
|
||||
user, err = d.store.Users.Get("", username)
|
||||
user, err = st.Users.Get("", username)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
@ -99,11 +99,11 @@ options you want to change.`,
|
||||
}
|
||||
}
|
||||
|
||||
err = d.store.Users.Update(user)
|
||||
err = st.Users.Update(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printUsers([]*users.User{user})
|
||||
return nil
|
||||
}, pythonConfig{}),
|
||||
}, storeOptions{}),
|
||||
}
|
||||
|
||||
65
cmd/utils.go
65
cmd/utils.go
@ -36,6 +36,7 @@ func getAndParseFileMode(flags *pflag.FlagSet, name string) (fs.FileMode, error)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return fs.FileMode(b), nil
|
||||
}
|
||||
|
||||
@ -131,55 +132,46 @@ func initViper(cmd *cobra.Command) (*viper.Viper, error) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
type cobraFunc func(cmd *cobra.Command, args []string) error
|
||||
type pythonFunc func(cmd *cobra.Command, args []string, data *pythonData) error
|
||||
type store struct {
|
||||
*storage.Storage
|
||||
databaseExisted bool
|
||||
}
|
||||
|
||||
type pythonConfig struct {
|
||||
type storeOptions struct {
|
||||
expectsNoDatabase bool
|
||||
allowsNoDatabase bool
|
||||
}
|
||||
|
||||
type pythonData struct {
|
||||
databaseExisted bool
|
||||
viper *viper.Viper
|
||||
store *storage.Storage
|
||||
}
|
||||
type cobraFunc func(cmd *cobra.Command, args []string) error
|
||||
|
||||
func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
|
||||
// withViperAndStore initializes Viper and the storage.Store and passes them to the callback function.
|
||||
// This function should only be used by [withStore] and the root command. No other command should call
|
||||
// this function directly.
|
||||
func withViperAndStore(fn func(cmd *cobra.Command, args []string, v *viper.Viper, store *store) error, options storeOptions) cobraFunc {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
v, err := initViper(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := &pythonData{databaseExisted: true}
|
||||
path := v.GetString("database")
|
||||
|
||||
// Only make the viper instance available to the root command (filebrowser).
|
||||
// This is to make sure that we don't make the mistake of using it somewhere
|
||||
// else.
|
||||
if cmd.Name() == "filebrowser" {
|
||||
data.viper = v
|
||||
}
|
||||
|
||||
absPath, err := filepath.Abs(path)
|
||||
path, err := filepath.Abs(v.GetString("database"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exists, err := dbExists(path)
|
||||
if err != nil {
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
} else if exists && cfg.expectsNoDatabase {
|
||||
log.Fatal(absPath + " already exists")
|
||||
} else if !exists && !cfg.expectsNoDatabase && !cfg.allowsNoDatabase {
|
||||
log.Fatal(absPath + " does not exist. Please run 'filebrowser config init' first.")
|
||||
} else if !exists && !cfg.expectsNoDatabase {
|
||||
log.Println("Warning: filebrowser.db can't be found. Initialing in " + strings.TrimSuffix(absPath, "filebrowser.db"))
|
||||
case exists && options.expectsNoDatabase:
|
||||
log.Fatal(path + " already exists")
|
||||
case !exists && !options.expectsNoDatabase && !options.allowsNoDatabase:
|
||||
log.Fatal(path + " does not exist. Please run 'filebrowser config init' first.")
|
||||
case !exists && !options.expectsNoDatabase:
|
||||
log.Println("WARNING: filebrowser.db can't be found. Initialing in " + strings.TrimSuffix(path, "filebrowser.db"))
|
||||
}
|
||||
|
||||
log.Println("Using database: " + absPath)
|
||||
data.databaseExisted = exists
|
||||
log.Println("Using database: " + path)
|
||||
|
||||
db, err := storm.Open(path, storm.BoltOptions(databasePermissions, nil))
|
||||
if err != nil {
|
||||
@ -187,15 +179,26 @@ func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
data.store, err = bolt.NewStorage(db)
|
||||
storage, err := bolt.NewStorage(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fn(cmd, args, data)
|
||||
store := &store{
|
||||
Storage: storage,
|
||||
databaseExisted: exists,
|
||||
}
|
||||
|
||||
return fn(cmd, args, v, store)
|
||||
}
|
||||
}
|
||||
|
||||
func withStore(fn func(cmd *cobra.Command, args []string, store *store) error, options storeOptions) cobraFunc {
|
||||
return withViperAndStore(func(cmd *cobra.Command, args []string, _ *viper.Viper, store *store) error {
|
||||
return fn(cmd, args, store)
|
||||
}, options)
|
||||
}
|
||||
|
||||
func marshal(filename string, data interface{}) error {
|
||||
fd, err := os.Create(filename)
|
||||
if err != nil {
|
||||
|
||||
@ -25,12 +25,12 @@ func TestFileCache(t *testing.T) {
|
||||
// store new key
|
||||
err := cache.Store(ctx, key, []byte(value))
|
||||
require.NoError(t, err)
|
||||
checkValue(t, ctx, fs, filepath.Join(cacheRoot, cachedFilePath), cache, key, value)
|
||||
checkValue(ctx, t, fs, filepath.Join(cacheRoot, cachedFilePath), cache, key, value)
|
||||
|
||||
// update existing key
|
||||
err = cache.Store(ctx, key, []byte(newValue))
|
||||
require.NoError(t, err)
|
||||
checkValue(t, ctx, fs, filepath.Join(cacheRoot, cachedFilePath), cache, key, newValue)
|
||||
checkValue(ctx, t, fs, filepath.Join(cacheRoot, cachedFilePath), cache, key, newValue)
|
||||
|
||||
// delete key
|
||||
err = cache.Delete(ctx, key)
|
||||
@ -40,7 +40,7 @@ func TestFileCache(t *testing.T) {
|
||||
require.False(t, exists)
|
||||
}
|
||||
|
||||
func checkValue(t *testing.T, ctx context.Context, fs afero.Fs, fileFullPath string, cache *FileCache, key, wantValue string) {
|
||||
func checkValue(ctx context.Context, t *testing.T, fs afero.Fs, fileFullPath string, cache *FileCache, key, wantValue string) {
|
||||
t.Helper()
|
||||
// check actual file content
|
||||
b, err := afero.ReadFile(fs, fileFullPath)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package errors
|
||||
package fberrors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
@ -23,7 +23,7 @@ import (
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
fbErrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
fberrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
"github.com/filebrowser/filebrowser/v2/rules"
|
||||
)
|
||||
|
||||
@ -60,6 +60,7 @@ type FileOptions struct {
|
||||
Modify bool
|
||||
Expand bool
|
||||
ReadHeader bool
|
||||
CalcImgRes bool
|
||||
Token string
|
||||
Checker rules.Checker
|
||||
Content bool
|
||||
@ -90,13 +91,13 @@ func NewFileInfo(opts *FileOptions) (*FileInfo, error) {
|
||||
|
||||
if opts.Expand {
|
||||
if file.IsDir {
|
||||
if err := file.readListing(opts.Checker, opts.ReadHeader); err != nil {
|
||||
if err := file.readListing(opts.Checker, opts.ReadHeader, opts.CalcImgRes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
|
||||
err = file.detectType(opts.Modify, opts.Content, true)
|
||||
err = file.detectType(opts.Modify, opts.Content, true, opts.CalcImgRes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -168,7 +169,7 @@ func stat(opts *FileOptions) (*FileInfo, error) {
|
||||
// algorithm. The checksums data is saved on File object.
|
||||
func (i *FileInfo) Checksum(algo string) error {
|
||||
if i.IsDir {
|
||||
return fbErrors.ErrIsDirectory
|
||||
return fberrors.ErrIsDirectory
|
||||
}
|
||||
|
||||
if i.Checksums == nil {
|
||||
@ -193,7 +194,7 @@ func (i *FileInfo) Checksum(algo string) error {
|
||||
case "sha512":
|
||||
h = sha512.New()
|
||||
default:
|
||||
return fbErrors.ErrInvalidOption
|
||||
return fberrors.ErrInvalidOption
|
||||
}
|
||||
|
||||
_, err = io.Copy(h, reader)
|
||||
@ -218,7 +219,7 @@ func (i *FileInfo) RealPath() string {
|
||||
return i.Path
|
||||
}
|
||||
|
||||
func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error {
|
||||
func (i *FileInfo) detectType(modify, saveContent, readHeader bool, calcImgRes bool) error {
|
||||
if IsNamedPipe(i.Mode) {
|
||||
i.Type = "blob"
|
||||
return nil
|
||||
@ -249,11 +250,13 @@ func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error {
|
||||
return nil
|
||||
case strings.HasPrefix(mimetype, "image"):
|
||||
i.Type = "image"
|
||||
resolution, err := calculateImageResolution(i.Fs, i.Path)
|
||||
if err != nil {
|
||||
log.Printf("Error calculating image resolution: %v", err)
|
||||
} else {
|
||||
i.Resolution = resolution
|
||||
if calcImgRes {
|
||||
resolution, err := calculateImageResolution(i.Fs, i.Path)
|
||||
if err != nil {
|
||||
log.Printf("Error calculating image resolution: %v", err)
|
||||
} else {
|
||||
i.Resolution = resolution
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case strings.HasSuffix(mimetype, "pdf"):
|
||||
@ -387,7 +390,7 @@ func (i *FileInfo) addSubtitle(fPath string) {
|
||||
i.Subtitles = append(i.Subtitles, fPath)
|
||||
}
|
||||
|
||||
func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error {
|
||||
func (i *FileInfo) readListing(checker rules.Checker, readHeader bool, calcImgRes bool) error {
|
||||
afs := &afero.Afero{Fs: i.Fs}
|
||||
dir, err := afs.ReadDir(i.Path)
|
||||
if err != nil {
|
||||
@ -434,7 +437,7 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error {
|
||||
currentDir: dir,
|
||||
}
|
||||
|
||||
if !file.IsDir && strings.HasPrefix(mime.TypeByExtension(file.Extension), "image/") {
|
||||
if !file.IsDir && strings.HasPrefix(mime.TypeByExtension(file.Extension), "image/") && calcImgRes {
|
||||
resolution, err := calculateImageResolution(file.Fs, file.Path)
|
||||
if err != nil {
|
||||
log.Printf("Error calculating resolution for image %s: %v", file.Path, err)
|
||||
@ -451,7 +454,7 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error {
|
||||
if isInvalidLink {
|
||||
file.Type = "invalid_link"
|
||||
} else {
|
||||
err := file.detectType(true, false, readHeader)
|
||||
err := file.detectType(true, false, readHeader, calcImgRes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
DisableUsedPercentage: false,
|
||||
EnableExec: true,
|
||||
EnableThumbs: true,
|
||||
LogoutPage: "",
|
||||
LoginPage: true,
|
||||
Name: "",
|
||||
NoAuth: false,
|
||||
|
||||
@ -71,5 +71,5 @@
|
||||
"vite-plugin-compression2": "^2.3.1",
|
||||
"vue-tsc": "^3.1.3"
|
||||
},
|
||||
"packageManager": "pnpm@10.22.0+sha512.bf049efe995b28f527fd2b41ae0474ce29186f7edcb3bf545087bd61fbbebb2bf75362d1307fda09c2d288e1e499787ac12d4fcb617a974718a6051f2eee741c"
|
||||
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6"
|
||||
}
|
||||
|
||||
1648
frontend/pnpm-lock.yaml
generated
1648
frontend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,12 @@
|
||||
import { fetchURL, removePrefix } from "./utils";
|
||||
import { fetchURL, removePrefix, StatusError } from "./utils";
|
||||
import url from "../utils/url";
|
||||
|
||||
export default async function search(base: string, query: string) {
|
||||
export default async function search(
|
||||
base: string,
|
||||
query: string,
|
||||
signal: AbortSignal,
|
||||
callback: (item: ResourceItem) => void
|
||||
) {
|
||||
base = removePrefix(base);
|
||||
query = encodeURIComponent(query);
|
||||
|
||||
@ -9,19 +14,60 @@ export default async function search(base: string, query: string) {
|
||||
base += "/";
|
||||
}
|
||||
|
||||
const res = await fetchURL(`/api/search${base}?query=${query}`, {});
|
||||
const res = await fetchURL(`/api/search${base}?query=${query}`, { signal });
|
||||
if (!res.body) {
|
||||
throw new StatusError("000 No connection", 0);
|
||||
}
|
||||
try {
|
||||
// Try streaming approach first (modern browsers)
|
||||
if (res.body && typeof res.body.pipeThrough === "function") {
|
||||
const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
|
||||
let buffer = "";
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (value) {
|
||||
buffer += value;
|
||||
}
|
||||
const lines = buffer.split(/\n/);
|
||||
let lastLine = lines.pop();
|
||||
// Save incomplete last line
|
||||
if (!lastLine) {
|
||||
lastLine = "";
|
||||
}
|
||||
buffer = lastLine;
|
||||
|
||||
let data = await res.json();
|
||||
|
||||
data = data.map((item: ResourceItem & { dir: boolean }) => {
|
||||
item.url = `/files${base}` + url.encodePath(item.path);
|
||||
|
||||
if (item.dir) {
|
||||
item.url += "/";
|
||||
for (const line of lines) {
|
||||
if (line) {
|
||||
const item = JSON.parse(line) as ResourceItem;
|
||||
item.url = `/files${base}` + url.encodePath(item.path);
|
||||
if (item.isDir) {
|
||||
item.url += "/";
|
||||
}
|
||||
callback(item);
|
||||
}
|
||||
}
|
||||
if (done) break;
|
||||
}
|
||||
} else {
|
||||
// Fallback for browsers without streaming support (e.g., Safari)
|
||||
const text = await res.text();
|
||||
const lines = text.split(/\n/);
|
||||
for (const line of lines) {
|
||||
if (line) {
|
||||
const item = JSON.parse(line) as ResourceItem;
|
||||
item.url = `/files${base}` + url.encodePath(item.path);
|
||||
if (item.isDir) {
|
||||
item.url += "/";
|
||||
}
|
||||
callback(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
|
||||
return data;
|
||||
} catch (e) {
|
||||
// Check if the error is an intentional cancellation
|
||||
if (e instanceof Error && e.name === "AbortError") {
|
||||
throw new StatusError("000 No connection", 0, true);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,10 +5,11 @@
|
||||
v-if="active"
|
||||
class="action"
|
||||
@click="close"
|
||||
:aria-label="$t('buttons.close')"
|
||||
:title="$t('buttons.close')"
|
||||
:aria-label="closeButtonTitle"
|
||||
:title="closeButtonTitle"
|
||||
>
|
||||
<i class="material-icons">arrow_back</i>
|
||||
<i v-if="ongoing" class="material-icons">stop_circle</i>
|
||||
<i v-else class="material-icons">arrow_back</i>
|
||||
</button>
|
||||
<i v-else class="material-icons">search</i>
|
||||
<input
|
||||
@ -21,6 +22,15 @@
|
||||
:aria-label="$t('search.search')"
|
||||
:placeholder="$t('search.search')"
|
||||
/>
|
||||
<i
|
||||
v-show="ongoing"
|
||||
class="material-icons spin"
|
||||
style="display: inline-block"
|
||||
>autorenew
|
||||
</i>
|
||||
<span style="margin-top: 5px" v-show="results.length > 0">
|
||||
{{ results.length }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div id="result" ref="result">
|
||||
@ -57,9 +67,6 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p id="renew">
|
||||
<i class="material-icons spin">autorenew</i>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -70,10 +77,11 @@ import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
import url from "@/utils/url";
|
||||
import { search } from "@/api";
|
||||
import { computed, inject, onMounted, ref, watch } from "vue";
|
||||
import { computed, inject, onMounted, ref, watch, onUnmounted } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useRoute } from "vue-router";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { StatusError } from "@/api/utils";
|
||||
|
||||
const boxes = {
|
||||
image: { label: "images", icon: "insert_photo" },
|
||||
@ -84,6 +92,7 @@ const boxes = {
|
||||
|
||||
const layoutStore = useLayoutStore();
|
||||
const fileStore = useFileStore();
|
||||
let searchAbortController = new AbortController();
|
||||
|
||||
const { currentPromptName } = storeToRefs(layoutStore);
|
||||
|
||||
@ -124,9 +133,7 @@ watch(currentPromptName, (newVal, oldVal) => {
|
||||
});
|
||||
|
||||
watch(prompt, () => {
|
||||
if (results.value.length) {
|
||||
reset();
|
||||
}
|
||||
reset();
|
||||
});
|
||||
|
||||
// ...mapState(useFileStore, ["isListing"]),
|
||||
@ -149,6 +156,10 @@ const filteredResults = computed(() => {
|
||||
return results.value.slice(0, resultsCount.value);
|
||||
});
|
||||
|
||||
const closeButtonTitle = computed(() => {
|
||||
return ongoing.value ? t("buttons.stopSearch") : t("buttons.close");
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (result.value === null) {
|
||||
return;
|
||||
@ -164,14 +175,23 @@ onMounted(() => {
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
abortLastSearch();
|
||||
});
|
||||
|
||||
const open = () => {
|
||||
!active.value && layoutStore.showHover("search");
|
||||
};
|
||||
|
||||
const close = (event: Event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
layoutStore.closeHovers();
|
||||
if (ongoing.value) {
|
||||
abortLastSearch();
|
||||
ongoing.value = false;
|
||||
} else {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
layoutStore.closeHovers();
|
||||
}
|
||||
};
|
||||
|
||||
const keyup = (event: KeyboardEvent) => {
|
||||
@ -188,11 +208,16 @@ const init = (string: string) => {
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
abortLastSearch();
|
||||
ongoing.value = false;
|
||||
resultsCount.value = 50;
|
||||
results.value = [];
|
||||
};
|
||||
|
||||
const abortLastSearch = () => {
|
||||
searchAbortController.abort();
|
||||
};
|
||||
|
||||
const submit = async (event: Event) => {
|
||||
event.preventDefault();
|
||||
|
||||
@ -208,8 +233,16 @@ const submit = async (event: Event) => {
|
||||
ongoing.value = true;
|
||||
|
||||
try {
|
||||
results.value = await search(path, prompt.value);
|
||||
abortLastSearch();
|
||||
searchAbortController = new AbortController();
|
||||
results.value = [];
|
||||
await search(path, prompt.value, searchAbortController.signal, (item) =>
|
||||
results.value.push(item)
|
||||
);
|
||||
} catch (error: any) {
|
||||
if (error instanceof StatusError && error.is_canceled) {
|
||||
return;
|
||||
}
|
||||
$showError(error);
|
||||
}
|
||||
|
||||
|
||||
@ -129,6 +129,7 @@ import {
|
||||
disableExternal,
|
||||
disableUsedPercentage,
|
||||
noAuth,
|
||||
logoutPage,
|
||||
loginPage,
|
||||
} from "@/utils/constants";
|
||||
import { files as api } from "@/api";
|
||||
@ -159,7 +160,7 @@ export default {
|
||||
version: () => version,
|
||||
disableExternal: () => disableExternal,
|
||||
disableUsedPercentage: () => disableUsedPercentage,
|
||||
canLogout: () => !noAuth && loginPage,
|
||||
canLogout: () => !noAuth && (loginPage || logoutPage !== "/login"),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["closeHovers", "showHover"]),
|
||||
|
||||
252
frontend/src/components/files/CsvViewer.vue
Normal file
252
frontend/src/components/files/CsvViewer.vue
Normal 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>
|
||||
@ -265,7 +265,15 @@ const click = (event: Event | KeyboardEvent) => {
|
||||
}
|
||||
|
||||
if (fileStore.selected.indexOf(props.index) !== -1) {
|
||||
fileStore.removeSelected(props.index);
|
||||
if (
|
||||
(event as KeyboardEvent).ctrlKey ||
|
||||
(event as KeyboardEvent).metaKey ||
|
||||
fileStore.multiple
|
||||
) {
|
||||
fileStore.removeSelected(props.index);
|
||||
} else {
|
||||
fileStore.selected = [props.index];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -291,7 +299,6 @@ const click = (event: Event | KeyboardEvent) => {
|
||||
}
|
||||
|
||||
if (
|
||||
!singleClick.value &&
|
||||
!(event as KeyboardEvent).ctrlKey &&
|
||||
!(event as KeyboardEvent).metaKey &&
|
||||
!fileStore.multiple
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, nextTick, defineProps } from "vue";
|
||||
import { ref, computed, watch, nextTick } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import url from "@/utils/url";
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
<th>{{ $t("settings.shareDuration") }}</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
||||
<tr v-for="link in links" :key="link.hash">
|
||||
@ -24,7 +25,7 @@
|
||||
</td>
|
||||
<td class="small">
|
||||
<button
|
||||
class="action copy-clipboard"
|
||||
class="action"
|
||||
:aria-label="$t('buttons.copyToClipboard')"
|
||||
:title="$t('buttons.copyToClipboard')"
|
||||
@click="copyToClipboard(buildLink(link))"
|
||||
@ -32,6 +33,17 @@
|
||||
<i class="material-icons">content_paste</i>
|
||||
</button>
|
||||
</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">
|
||||
<button
|
||||
class="action"
|
||||
@ -132,7 +144,7 @@
|
||||
<script>
|
||||
import { mapActions, mapState } from "pinia";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { share as api } from "@/api";
|
||||
import * as api from "@/api/index";
|
||||
import dayjs from "dayjs";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
import { copy } from "@/utils/clipboard";
|
||||
@ -172,7 +184,7 @@ export default {
|
||||
},
|
||||
async beforeMount() {
|
||||
try {
|
||||
const links = await api.get(this.url);
|
||||
const links = await api.share.get(this.url);
|
||||
this.links = links;
|
||||
this.sort();
|
||||
|
||||
@ -211,9 +223,14 @@ export default {
|
||||
let res = null;
|
||||
|
||||
if (!this.time) {
|
||||
res = await api.create(this.url, this.password);
|
||||
res = await api.share.create(this.url, this.password);
|
||||
} 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);
|
||||
@ -231,7 +248,7 @@ export default {
|
||||
deleteLink: async function (event, link) {
|
||||
event.preventDefault();
|
||||
try {
|
||||
await api.remove(link.hash);
|
||||
await api.share.remove(link.hash);
|
||||
this.links = this.links.filter((item) => item.hash !== link.hash);
|
||||
|
||||
if (this.links.length == 0) {
|
||||
@ -245,7 +262,16 @@ export default {
|
||||
return dayjs(time * 1000).fromNow();
|
||||
},
|
||||
buildLink(share) {
|
||||
return api.getShareURL(share);
|
||||
return api.share.getShareURL(share);
|
||||
},
|
||||
buildDownloadLink(share) {
|
||||
return api.pub.getDownloadURL(
|
||||
{
|
||||
hash: share.hash,
|
||||
path: "",
|
||||
},
|
||||
true
|
||||
);
|
||||
},
|
||||
sort() {
|
||||
this.links = this.links.sort((a, b) => {
|
||||
|
||||
@ -15,10 +15,8 @@ export default {
|
||||
data() {
|
||||
const dataObj = {};
|
||||
const locales = {
|
||||
he: "עברית",
|
||||
hr: "Hrvatski",
|
||||
hu: "Magyar",
|
||||
ar: "العربية",
|
||||
bg: "български език",
|
||||
ca: "Català",
|
||||
cs: "Čeština",
|
||||
de: "Deutsch",
|
||||
@ -26,15 +24,18 @@ export default {
|
||||
en: "English",
|
||||
es: "Español",
|
||||
fr: "Français",
|
||||
he: "עברית",
|
||||
hr: "Hrvatski",
|
||||
hu: "Magyar",
|
||||
is: "Icelandic",
|
||||
it: "Italiano",
|
||||
ja: "日本語",
|
||||
ko: "한국어",
|
||||
"nl-be": "Dutch (Belgium)",
|
||||
no: "Norsk",
|
||||
"nl-be": "Dutch (Belgium)",
|
||||
pl: "Polski",
|
||||
"pt-br": "Português",
|
||||
pt: "Português (Brasil)",
|
||||
"pt-br": "Português (Brasil)",
|
||||
pt: "Português (Portugal)",
|
||||
ro: "Romanian",
|
||||
ru: "Русский",
|
||||
sk: "Slovenčina",
|
||||
|
||||
@ -98,7 +98,8 @@ main .spinner .bounce2 {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.action.disabled {
|
||||
.action.disabled,
|
||||
.action[disabled] {
|
||||
opacity: 0.2;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
@ -460,4 +461,4 @@ html[dir="rtl"] .card-content .small + input {
|
||||
html[dir="rtl"] .card.floating .card-content .file-list {
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "رفع",
|
||||
"openFile": "فتح الملف",
|
||||
"discardChanges": "إلغاء التغييرات",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "تحميل الملف",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "الترتيب بآخر تعديل",
|
||||
"sortByName": "الترتيب باﻹسم",
|
||||
"sortBySize": "الترتيب بالحجم",
|
||||
"noPreview": "لا يوجد عرض مسبق لهذا الملف."
|
||||
"noPreview": "لا يوجد عرض مسبق لهذا الملف.",
|
||||
"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": {
|
||||
"click": "حدد الملف أو المجلد",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "إسم المستخدم",
|
||||
"usernameTaken": "إسم المستخدم غير متاح",
|
||||
"wrongCredentials": "بيانات دخول خاطئة",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
286
frontend/src/i18n/bg.json
Normal file
286
frontend/src/i18n/bg.json
Normal file
@ -0,0 +1,286 @@
|
||||
{
|
||||
"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": "Изчисти",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Запиши промените",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"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": "Единица за време"
|
||||
}
|
||||
}
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Pujar",
|
||||
"openFile": "Obrir fitxer",
|
||||
"discardChanges": "Descartar",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Descarregar fitxer",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Ordenar per última modificació",
|
||||
"sortByName": "Ordenar per nom",
|
||||
"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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "seleccionar fitxer o carpeta",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Usuari",
|
||||
"usernameTaken": "Nom d'usuari no disponible",
|
||||
"wrongCredentials": "Usuari i/o contrasenya incorrectes",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Nahrát",
|
||||
"openFile": "Otevřít soubor",
|
||||
"discardChanges": "Zrušit změny",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Stáhnout soubor",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Seřadit podle poslední změny",
|
||||
"sortByName": "Seřadit podle názvu",
|
||||
"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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "vyberte soubor nebo adresář",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Uživatelské jméno",
|
||||
"usernameTaken": "Uživatelské jméno již existuje",
|
||||
"wrongCredentials": "Nesprávné přihlašovací údaje",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Upload",
|
||||
"openFile": "Datei öffnen",
|
||||
"discardChanges": "Verwerfen",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Änderungen speichern",
|
||||
"editAsText": "Als Text bearbeiten",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Download Datei",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Nach Änderungsdatum sortieren",
|
||||
"sortByName": "Nach Namen 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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "Wähle Datei oder Ordner",
|
||||
@ -102,8 +115,9 @@
|
||||
"username": "Benutzername",
|
||||
"usernameTaken": "Benutzername ist bereits vergeben",
|
||||
"wrongCredentials": "Falsche Zugangsdaten",
|
||||
"passwordTooShort": "Passwort muss mindestens {min} Zeichen lang sein",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
"inactivity": "Du wurdest aufgrund von Inaktivität abgemeldet."
|
||||
}
|
||||
},
|
||||
"permanent": "Permanent",
|
||||
@ -158,7 +172,7 @@
|
||||
"video": "Video"
|
||||
},
|
||||
"settings": {
|
||||
"aceEditorTheme": "Ace editor theme",
|
||||
"aceEditorTheme": "Ace Editor Theme",
|
||||
"admin": "Admin",
|
||||
"administrator": "Administrator",
|
||||
"allowCommands": "Befehle ausführen",
|
||||
@ -166,7 +180,7 @@
|
||||
"allowNew": "Erstellen neuer Dateien und Ordner",
|
||||
"allowPublish": "Veröffentlichen von neuen Beiträgen und Seiten",
|
||||
"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)",
|
||||
"branding": "Design",
|
||||
"brandingDirectoryPath": "Designverzeichnispfad",
|
||||
@ -176,7 +190,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}.",
|
||||
"commandsUpdated": "Befehle aktualisiert!",
|
||||
"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",
|
||||
"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",
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Μεταφόρτωση",
|
||||
"openFile": "Άνοιγμα αρχείου",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Λήψη αρχείου",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Ταξινόμηση κατά πρόσφατη τροποποίηση",
|
||||
"sortByName": "Ταξινόμηση κατά όνομα",
|
||||
"sortBySize": "Ταξινόμηση κατά μέγεθος",
|
||||
"noPreview": "Η προεπισκόπηση δεν είναι διαθέσιμη για αυτό το αρχείο."
|
||||
"noPreview": "Η προεπισκόπηση δεν είναι διαθέσιμη για αυτό το αρχείο.",
|
||||
"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": {
|
||||
"click": "επιλέξτε αρχείο ή φάκελο",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Όνομα χρήστη",
|
||||
"usernameTaken": "Το όνομα χρήστη χρησιμοποιείται ήδη",
|
||||
"wrongCredentials": "Λάθος όνομα ή/και κωδικός πρόσβασης",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Upload",
|
||||
"openFile": "Open file",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Download File",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Sort by last modified",
|
||||
"sortByName": "Sort by name",
|
||||
"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": {
|
||||
"click": "select file or directory",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Username",
|
||||
"usernameTaken": "Username already taken",
|
||||
"wrongCredentials": "Wrong credentials",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Subir",
|
||||
"openFile": "Abrir archivo",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Guardar cambios"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Guardar cambios",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Descargar fichero",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Ordenar por última modificación",
|
||||
"sortByName": "Ordenar por nombre",
|
||||
"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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "seleccionar archivo o carpeta",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Usuario",
|
||||
"usernameTaken": "Nombre usuario no disponible",
|
||||
"wrongCredentials": "Usuario y/o contraseña incorrectos",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "آپلود",
|
||||
"openFile": "باز کردن فایل",
|
||||
"discardChanges": "لغو کردن",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "دانلود فایل",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "مرتب سازی آخرین ویرایش",
|
||||
"sortByName": "مرتب سازی نام",
|
||||
"sortBySize": "مرتب سازی اندازه",
|
||||
"noPreview": "این فایل قابل نمایش نیست"
|
||||
"noPreview": "این فایل قابل نمایش نیست",
|
||||
"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": {
|
||||
"click": "انتخاب فایل یا پوشه",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "نام کاربری",
|
||||
"usernameTaken": "نام کاربری تکراری",
|
||||
"wrongCredentials": "خطا در اعتبارسنجی",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Importer",
|
||||
"openFile": "Ouvrir le fichier",
|
||||
"discardChanges": "Annuler",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Télécharger le fichier",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Trier par date de modification",
|
||||
"sortByName": "Trier par nom",
|
||||
"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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "Sélectionner un fichier ou dossier",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Utilisateur·ice",
|
||||
"usernameTaken": "Le nom d'utilisateur·ice est déjà pris",
|
||||
"wrongCredentials": "Identifiants incorrects !",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "העלאה",
|
||||
"openFile": "פתח קובץ",
|
||||
"discardChanges": "זריקת השינויים",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "הורד קובץ",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "מיין לפי השינוי האחרון",
|
||||
"sortByName": "מיין לפי שם",
|
||||
"sortBySize": "מיין לפי גודל",
|
||||
"noPreview": "לא זמינה תצוגה מקדימה לקובץ זה"
|
||||
"noPreview": "לא זמינה תצוגה מקדימה לקובץ זה",
|
||||
"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": {
|
||||
"click": "בחר קובץ או תיקייה",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "שם משתמש",
|
||||
"usernameTaken": "שם המשתמש כבר קיים",
|
||||
"wrongCredentials": "פרטי התחברות שגויים",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Prenesi",
|
||||
"openFile": "Otvori datoteku",
|
||||
"discardChanges": "Odbaci",
|
||||
"saveChanges": "Spremi promjene"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Spremi promjene",
|
||||
"editAsText": "Uredi kao tekst",
|
||||
"increaseFontSize": "Povećaj veličinu fonta",
|
||||
"decreaseFontSize": "Smanji veličinu fonta"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Preuzmi Datoteku",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Sortiraj po zadnjoj izmjeni",
|
||||
"sortByName": "Sortiraj po nazivu",
|
||||
"sortBySize": "Sortiraj po veličini",
|
||||
"noPreview": "Pregled nije dostupan za ovu datoteku."
|
||||
"noPreview": "Pregled nije dostupan za ovu datoteku.",
|
||||
"csvTooLarge": "CSV datoteka je prevelika za pregled (>5MB). Molimo preuzmite da bi ste ju pregledali.",
|
||||
"csvLoadFailed": "Neuspješno učitavanje CSV datoteke.",
|
||||
"showingRows": "Prikazuje se {count} red(ova)",
|
||||
"columnSeparator": "Separator stupaca",
|
||||
"csvSeparators": {
|
||||
"comma": "Zarez (,)",
|
||||
"semicolon": "Točka zarez (;)",
|
||||
"both": "I (,) i (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "odaberi datoteku ili mapu",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Korisničko ime",
|
||||
"usernameTaken": "Korisničko ime zauzeto",
|
||||
"wrongCredentials": "Neispravno korisničko ime/lozinka",
|
||||
"passwordTooShort": "Lozinka mora sadržavati minimalno {min} znakova",
|
||||
"logout_reasons": {
|
||||
"inactivity": "Odjavljeni ste zbog neaktivnosti."
|
||||
}
|
||||
@ -166,7 +180,7 @@
|
||||
"allowNew": "Stvori nove datoteke i mape",
|
||||
"allowPublish": "Objavi nove objave i stranice",
|
||||
"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)",
|
||||
"branding": "Brendiranje",
|
||||
"brandingDirectoryPath": "Put brendiranja",
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Feltöltés",
|
||||
"openFile": "Fájl megnyitása",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Fájl letöltése",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Rendezés utolsó módosítás szerint",
|
||||
"sortByName": "Rendezés név 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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "mappa vagy fájl kijelölése",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Felhasználói név",
|
||||
"usernameTaken": "A felhasználói név már foglalt",
|
||||
"wrongCredentials": "Hibás hitelesítő adatok",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -2,6 +2,8 @@ import dayjs from "dayjs";
|
||||
import { createI18n } from "vue-i18n";
|
||||
|
||||
import("dayjs/locale/ar");
|
||||
import("dayjs/locale/bg");
|
||||
import("dayjs/locale/cs");
|
||||
import("dayjs/locale/de");
|
||||
import("dayjs/locale/el");
|
||||
import("dayjs/locale/en");
|
||||
@ -14,6 +16,7 @@ import("dayjs/locale/is");
|
||||
import("dayjs/locale/it");
|
||||
import("dayjs/locale/ja");
|
||||
import("dayjs/locale/ko");
|
||||
import("dayjs/locale/nb");
|
||||
import("dayjs/locale/nl-be");
|
||||
import("dayjs/locale/pl");
|
||||
import("dayjs/locale/pt-br");
|
||||
@ -27,8 +30,6 @@ import("dayjs/locale/uk");
|
||||
import("dayjs/locale/vi");
|
||||
import("dayjs/locale/zh-cn");
|
||||
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
|
||||
// at once using the import syntax
|
||||
@ -109,6 +110,7 @@ export function detectLocale() {
|
||||
case /^uk\b/.test(locale):
|
||||
locale = "uk";
|
||||
break;
|
||||
|
||||
case /^vi\b/.test(locale):
|
||||
locale = "vi";
|
||||
break;
|
||||
@ -123,6 +125,9 @@ export function detectLocale() {
|
||||
case /^no\b/.test(locale):
|
||||
locale = "no";
|
||||
break;
|
||||
case /^bg\b/.test(locale):
|
||||
locale = "bg";
|
||||
break;
|
||||
default:
|
||||
locale = "en";
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Hlaða upp",
|
||||
"openFile": "Open file",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Sækja skjal",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Flokka eftir Seinast breytt",
|
||||
"sortByName": "Flokka eftir nafni",
|
||||
"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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "velja skjal eða möppu",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Notendanafn",
|
||||
"usernameTaken": "Þetta norendanafn er þegar í notkun",
|
||||
"wrongCredentials": "Rangar notendaupplýsingar",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Carica",
|
||||
"openFile": "Apri file",
|
||||
"discardChanges": "Ignora",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Scarica file",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Ordina per ultima modifica",
|
||||
"sortByName": "Ordina per nome",
|
||||
"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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "seleziona un file o una cartella",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Nome utente",
|
||||
"usernameTaken": "Username già usato",
|
||||
"wrongCredentials": "Credenziali errate",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -42,8 +42,13 @@
|
||||
"update": "更新",
|
||||
"upload": "アップロード",
|
||||
"openFile": "ファイルを開く",
|
||||
"stopSearch": "検索を停止",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "ファイルのダウンロード",
|
||||
@ -75,7 +80,16 @@
|
||||
"sortByLastModified": "更新日時で並べ替え",
|
||||
"sortByName": "名前で並べ替え",
|
||||
"sortBySize": "サイズで並べ替え",
|
||||
"noPreview": "プレビューはこのファイルでは利用できません"
|
||||
"noPreview": "プレビューはこのファイルでは利用できません",
|
||||
"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": {
|
||||
"click": "ファイルやフォルダーを選択",
|
||||
@ -102,6 +116,7 @@
|
||||
"username": "ユーザー名",
|
||||
"usernameTaken": "ユーザー名はすでに取得されています",
|
||||
"wrongCredentials": "ユーザー名またはパスワードが間違っています",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -41,9 +41,14 @@
|
||||
"toggleSidebar": "사이드바 전환",
|
||||
"update": "업데이트",
|
||||
"upload": "업로드",
|
||||
"stopSearch": "검색 중지",
|
||||
"openFile": "파일 열기",
|
||||
"discardChanges": "변경 사항 취소",
|
||||
"saveChanges": "변경사항 저장"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "변경사항 저장",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "파일 다운로드",
|
||||
@ -75,7 +80,16 @@
|
||||
"sortByLastModified": "수정시간순 정렬",
|
||||
"sortByName": "이름순",
|
||||
"sortBySize": "크기순",
|
||||
"noPreview": "미리 보기가 지원되지 않는 파일 유형입니다."
|
||||
"noPreview": "미리 보기가 지원되지 않는 파일 유형입니다.",
|
||||
"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": {
|
||||
"click": "파일이나 디렉토리를 선택해주세요.",
|
||||
@ -102,6 +116,7 @@
|
||||
"username": "사용자 이름",
|
||||
"usernameTaken": "사용자 이름이 존재합니다",
|
||||
"wrongCredentials": "사용자 이름 또는 비밀번호를 확인하십시오",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
286
frontend/src/i18n/lv.json
Normal file
286
frontend/src/i18n/lv.json
Normal file
@ -0,0 +1,286 @@
|
||||
{
|
||||
"buttons": {
|
||||
"cancel": "Atcelt",
|
||||
"clear": "Tīrs",
|
||||
"close": "Aizvērt",
|
||||
"continue": "Turpināt",
|
||||
"copy": "Kopēt",
|
||||
"copyFile": "Kopēt failu",
|
||||
"copyToClipboard": "Kopēt starpliktuvē",
|
||||
"copyDownloadLinkToClipboard": "Kopēt lejupielādes saiti starpliktuvē",
|
||||
"create": "Izveidot",
|
||||
"delete": "Dzēst",
|
||||
"download": "Lejupielādēt",
|
||||
"file": "Fails",
|
||||
"folder": "Mape",
|
||||
"fullScreen": "Pārslēgt pilnekrāna režīmu",
|
||||
"hideDotfiles": "Slēpt punktfailus",
|
||||
"info": "Informācija",
|
||||
"more": "Vairāk",
|
||||
"move": "Pārvietot",
|
||||
"moveFile": "Pārvietot failu",
|
||||
"new": "Jauns",
|
||||
"next": "Nākamais",
|
||||
"ok": "Labi",
|
||||
"permalink": "Iegūt pastāvīgo saiti",
|
||||
"previous": "Iepriekšējais",
|
||||
"preview": "Priekšskatījums",
|
||||
"publish": "Publicēt",
|
||||
"rename": "Pārdēvēt",
|
||||
"replace": "Aizstāt",
|
||||
"reportIssue": "Ziņot par problēmu",
|
||||
"save": "Saglabāt",
|
||||
"schedule": "Grafiks",
|
||||
"search": "Meklēt",
|
||||
"select": "Izvēlieties",
|
||||
"selectMultiple": "Izvēlieties vairākus",
|
||||
"share": "Dalīties",
|
||||
"shell": "Pārslēgt apvalku",
|
||||
"submit": "Iesniegt",
|
||||
"switchView": "Pārslēgt skatu",
|
||||
"toggleSidebar": "Pārslēgt sānjoslu",
|
||||
"update": "Atjaunināt",
|
||||
"upload": "Augšupielādēt",
|
||||
"openFile": "Atvērt failu",
|
||||
"discardChanges": "Izmest",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Saglabāt izmaiņas",
|
||||
"editAsText": "Rediģēt kā tekstu",
|
||||
"increaseFontSize": "Palieliniet fonta lielumu",
|
||||
"decreaseFontSize": "Samaziniet fonta lielumu"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Lejupielādēt failu",
|
||||
"downloadFolder": "Lejupielādēt mapi",
|
||||
"downloadSelected": "Lejupielādēt atlasīto"
|
||||
},
|
||||
"upload": {
|
||||
"abortUpload": "Vai tiešām vēlaties pārtraukt?"
|
||||
},
|
||||
"errors": {
|
||||
"forbidden": "Jums nav atļaujas piekļūt šim.",
|
||||
"internal": "Kaut kas tiešām nogāja greizi.",
|
||||
"notFound": "Šo atrašanās vietu nevar sasniegt.",
|
||||
"connection": "Ar serveri nevar sazināties."
|
||||
},
|
||||
"files": {
|
||||
"body": "Ķermenis",
|
||||
"closePreview": "Aizvērt priekšskatījumu",
|
||||
"files": "Faili",
|
||||
"folders": "Mapes",
|
||||
"home": "Sākums",
|
||||
"lastModified": "Pēdējoreiz modificēts",
|
||||
"loading": "Notiek ielāde...",
|
||||
"lonely": "Šeit ir tukša vieta...",
|
||||
"metadata": "Metadati",
|
||||
"multipleSelectionEnabled": "Vairākas atlases ir iespējotas",
|
||||
"name": "Vārds",
|
||||
"size": "Izmērs",
|
||||
"sortByLastModified": "Kārtot pēc pēdējās modifikācijas",
|
||||
"sortByName": "Kārtot pēc nosaukuma",
|
||||
"sortBySize": "Kārtot pēc izmēra",
|
||||
"noPreview": "Šim failam nav pieejams priekšskatījums.",
|
||||
"csvTooLarge": "CSV fails ir pārāk liels priekšskatīšanai (>5 MB). Lūdzu, lejupielādējiet to, lai skatītu",
|
||||
"csvLoadFailed": "Neizdevās ielādēt CSV failu.",
|
||||
"showingRows": "Rāda {count} rindu(as)",
|
||||
"columnSeparator": "Kolonnu atdalītājs",
|
||||
"csvSeparators": {
|
||||
"comma": "Komats (,)",
|
||||
"semicolon": "Semikols (;)",
|
||||
"both": "Gan (,) gan (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "atlasiet failu vai direktoriju",
|
||||
"ctrl": {
|
||||
"click": "atlasīt vairākus failus vai direktorijus",
|
||||
"f": "atver meklēšanu",
|
||||
"s": "saglabājiet failu vai lejupielādējiet direktoriju, kurā atrodaties"
|
||||
},
|
||||
"del": "dzēst atlasītos vienumus",
|
||||
"doubleClick": "atvērt failu vai direktoriju",
|
||||
"esc": "notīrīt atlasi un/vai aizvērt uzvedni",
|
||||
"f1": "šo informāciju",
|
||||
"f2": "pārdēvēt failu",
|
||||
"help": "Palīdzība"
|
||||
},
|
||||
"login": {
|
||||
"createAnAccount": "Izveidot kontu",
|
||||
"loginInstead": "Jau ir konts",
|
||||
"password": "Parole",
|
||||
"passwordConfirm": "Paroles apstiprināšana",
|
||||
"passwordsDontMatch": "Paroles nesakrīt",
|
||||
"signup": "Reģistrēšanās",
|
||||
"submit": "Pieteikties",
|
||||
"username": "Lietotājvārds",
|
||||
"usernameTaken": "Lietotājvārds jau aizņemts",
|
||||
"wrongCredentials": "Nepareizi akreditācijas dati",
|
||||
"passwordTooShort": "Parolei jābūt vismaz {min} rakstzīmju garai",
|
||||
"logout_reasons": {
|
||||
"inactivity": "Jūs esat atteicies no sistēmas neaktivitātes dēļ."
|
||||
}
|
||||
},
|
||||
"permanent": "Pastāvīgs",
|
||||
"prompts": {
|
||||
"copy": "Kopēt",
|
||||
"copyMessage": "Izvēlieties atrašanās vietu, uz kuru kopēt failus:",
|
||||
"currentlyNavigating": "Pašlaik navigācija:",
|
||||
"deleteMessageMultiple": "Vai tiešām vēlaties dzēst {count} failu(s)?",
|
||||
"deleteMessageSingle": "Vai tiešām vēlaties dzēst šo failu/mapi?",
|
||||
"deleteMessageShare": "Vai tiešām vēlaties dzēst šo koplietojumu({path})?",
|
||||
"deleteUser": "Vai tiešām vēlaties dzēst šo lietotāju?",
|
||||
"deleteTitle": "Dzēst failus",
|
||||
"displayName": "Displeja nosaukums:",
|
||||
"download": "Lejupielādēt failus",
|
||||
"downloadMessage": "Izvēlieties formātu, kuru vēlaties lejupielādēt.",
|
||||
"error": "Kaut kas nogāja greizi",
|
||||
"fileInfo": "Informācija par failu",
|
||||
"filesSelected": "{count} atlasīti faili",
|
||||
"lastModified": "Pēdējās izmaiņas",
|
||||
"move": "Pārvietot",
|
||||
"moveMessage": "Izvēlieties jaunu mājvietu failam(iem)/mapei(ēm):",
|
||||
"newArchetype": "Izveidojiet jaunu ierakstu, pamatojoties uz arhtipu. Jūsu fails tiks izveidots satura mapē.",
|
||||
"newDir": "Jauna direktorija",
|
||||
"newDirMessage": "Nosaukums savai jaunajai direktorijai.",
|
||||
"newFile": "Jauns fails",
|
||||
"newFileMessage": "Nosauciet savu jauno failu.",
|
||||
"numberDirs": "Katalogu skaits",
|
||||
"numberFiles": "Failu skaits",
|
||||
"rename": "Pārdēvēt",
|
||||
"renameMessage": "Ievietojiet jaunu nosaukumu",
|
||||
"replace": "Aizstāt",
|
||||
"replaceMessage": "Vienam no failiem, kurus mēģināt augšupielādēt, ir konfliktējošs nosaukums. Vai vēlaties izlaist šo failu un turpināt augšupielādi vai aizstāt esošo?\n",
|
||||
"schedule": "Grafiks",
|
||||
"scheduleMessage": "Izvēlieties datumu un laiku, lai ieplānotu šī ieraksta publicēšanu.",
|
||||
"show": "Rādīt",
|
||||
"size": "Izmērs",
|
||||
"upload": "Augšupielādēt",
|
||||
"uploadFiles": "Notiek {files} failu augšupielāde...",
|
||||
"uploadMessage": "Atlasiet augšupielādes opciju.",
|
||||
"optionalPassword": "Izvēles parole",
|
||||
"resolution": "Izšķirtspēja",
|
||||
"discardEditorChanges": "Vai tiešām vēlaties atmest veiktās izmaiņas?"
|
||||
},
|
||||
"search": {
|
||||
"images": "Attēli",
|
||||
"music": "Mūzika",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Nospiediet taustiņu Enter, lai meklētu...",
|
||||
"search": "Meklē...",
|
||||
"typeToSearch": "Ierakstiet, lai meklētu...",
|
||||
"types": "Veidi",
|
||||
"video": "Video"
|
||||
},
|
||||
"settings": {
|
||||
"aceEditorTheme": "Ace redaktora tēma",
|
||||
"admin": "Admin",
|
||||
"administrator": "Administrator",
|
||||
"allowCommands": "Izpildīt komandas",
|
||||
"allowEdit": "Rediģēt, pārdēvēt un dzēst failus vai direktorijus",
|
||||
"allowNew": "Izveidojiet jaunus failus un direktorijus",
|
||||
"allowPublish": "Publicēt jaunus ierakstus un lapas",
|
||||
"allowSignup": "Atļaut lietotājiem reģistrēties",
|
||||
"hideLoginButton": "Paslēpt pieteikšanās pogu publiskajās lapās",
|
||||
"avoidChanges": "(atstājiet tukšu, lai izvairītos no izmaiņām)",
|
||||
"branding": "Zīmols",
|
||||
"brandingDirectoryPath": "Zīmola direktorijas ceļš",
|
||||
"brandingHelp": "Jūs varat pielāgot sava failu pārlūka instances izskatu un darbību, mainot tās nosaukumu, aizstājot logotipu, pievienojot pielāgotus stilus un pat atspējojot ārējās saites uz GitHub.\nLai iegūtu plašāku informāciju par pielāgotu zīmola veidošanu, lūdzu, skatiet {0}.",
|
||||
"changePassword": "Mainīt paroli",
|
||||
"commandRunner": "Komandu skrējējs",
|
||||
"commandRunnerHelp": "Šeit varat iestatīt komandas, kas tiek izpildītas nosauktajos notikumos. Katrā rindā jāraksta pa vienai. Būs pieejami vides mainīgie {0} un {1}, kas ir {0} relatīvi pret {1}. Lai iegūtu plašāku informāciju par šo funkciju un pieejamajiem vides mainīgajiem, lūdzu, izlasiet {2}.",
|
||||
"commandsUpdated": "Komandas atjauninātas!",
|
||||
"createUserDir": "Automātiski izveidot lietotāja mājas direktoriju, pievienojot jaunu lietotāju",
|
||||
"minimumPasswordLength": "Minimālais paroles garums",
|
||||
"tusUploads": "Sadalītas augšupielādes",
|
||||
"tusUploadsHelp": "Failu pārlūks atbalsta failu augšupielādi fragmentos, ļaujot izveidot efektīvu, uzticamu, atsākamu un fragmentos sadalītu failu augšupielādi pat neuzticamos tīklos.",
|
||||
"tusUploadsChunkSize": "Norāda pieprasījuma maksimālo izmēru (mazākiem augšupielādes apjomiem tiks izmantota tieša augšupielāde). Varat ievadīt vienkāršu veselu skaitli, kas apzīmē baitu izmēru, vai virkni, piemēram, 10 MB, 1 GB utt.",
|
||||
"tusUploadsRetryCount": "Atkārtotu mēģinājumu skaits, kas jāveic, ja fragmenta augšupielāde neizdodas.",
|
||||
"userHomeBasePath": "Lietotāja mājas direktoriju bāzes ceļš",
|
||||
"userScopeGenerationPlaceholder": "Darbības joma tiks ģenerēta automātiski",
|
||||
"createUserHomeDirectory": "Izveidojiet lietotāja mājas direktoriju",
|
||||
"customStylesheet": "Pielāgota stila lapa",
|
||||
"defaultUserDescription": "Šie ir noklusējuma iestatījumi jaunajiem lietotājiem.",
|
||||
"disableExternalLinks": "Atspējot ārējās saites (izņemot dokumentāciju)",
|
||||
"disableUsedDiskPercentage": "Atspējot izmantotā diska procentuālās daļas grafiku",
|
||||
"documentation": "dokumentācija",
|
||||
"examples": "Piemēri",
|
||||
"executeOnShell": "Izpildīt uz čaulas",
|
||||
"executeOnShellDescription": "Pēc noklusējuma failu pārlūks izpilda komandas, tieši izsaucot to bināros failus. Ja vēlaties tās palaist čaulā (piemēram, Bash vai PowerShell), varat to definēt šeit ar nepieciešamajiem argumentiem un karodziņiem. Ja tas ir iestatīts, izpildītā komanda tiks pievienota kā arguments. Tas attiecas gan uz lietotāja komandām, gan notikumu piesaistes rīkiem.",
|
||||
"globalRules": "Šis ir globāls atļaušanas un aizliegšanas noteikumu kopums. Tie attiecas uz katru lietotāju. Katra lietotāja iestatījumos varat definēt konkrētus noteikumus, lai tos ignorētu.",
|
||||
"globalSettings": "Globālie iestatījumi",
|
||||
"hideDotfiles": "Slēpt punktfailus",
|
||||
"insertPath": "Ievietojiet ceļu",
|
||||
"insertRegex": "Ievietojiet regex izteiksmi",
|
||||
"instanceName": "Gadījuma nosaukums",
|
||||
"language": "Valoda",
|
||||
"lockPassword": "Neļaut lietotājam mainīt paroli",
|
||||
"newPassword": "Jūsu jaunā parole",
|
||||
"newPasswordConfirm": "Apstipriniet savu jauno paroli",
|
||||
"newUser": "Jauns lietotājs",
|
||||
"password": "Parole",
|
||||
"passwordUpdated": "Parole atjaunināta!",
|
||||
"path": "Ceļš",
|
||||
"perm": {
|
||||
"create": "Izveidojiet failus un direktorijus",
|
||||
"delete": "Dzēst failus un direktorijus",
|
||||
"download": "Lejupielādēt",
|
||||
"execute": "Izpildīt komandas",
|
||||
"modify": "Rediģēt failus",
|
||||
"rename": "Pārdēvēt vai pārvietot failus un direktorijus",
|
||||
"share": "Kopīgojiet failus"
|
||||
},
|
||||
"permissions": "Atļaujas",
|
||||
"permissionsHelp": "Varat iestatīt lietotāju kā administratoru vai izvēlēties atļaujas individuāli. Ja atlasīsiet “Administrators”, visas pārējās opcijas tiks automātiski atzīmētas. Lietotāju pārvaldība joprojām ir administratora privilēģija.\n",
|
||||
"profileSettings": "Profila iestatījumi",
|
||||
"ruleExample1": "neļauj piekļūt jebkuram dotfile failam (piemēram, .git, .gitignore) katrā mapē.\n",
|
||||
"ruleExample2": "bloķē piekļuvi failam ar nosaukumu Caddyfile darbības jomas saknē.",
|
||||
"rules": "Noteikumi",
|
||||
"rulesHelp": "Šeit varat definēt atļaušanas un aizliegšanas noteikumu kopu šim konkrētajam lietotājam. Bloķētie faili netiks rādīti sarakstos, un lietotājs tiem nevarēs piekļūt. Mēs atbalstām regulārās izteiksmes un ceļus attiecībā pret lietotāja darbības jomu.\n",
|
||||
"scope": "Darbības joma",
|
||||
"setDateFormat": "Iestatiet precīzu datuma formātu",
|
||||
"settingsUpdated": "Iestatījumi atjaunināti!",
|
||||
"shareDuration": "Kopīgošanas ilgums",
|
||||
"shareManagement": "Kopīgošanas pārvaldība",
|
||||
"shareDeleted": "Kopīgošana ir izdzēsta!",
|
||||
"singleClick": "Failu un direktoriju atvēršanai izmantojiet vienus klikšķi",
|
||||
"themes": {
|
||||
"default": "Sistēmas noklusējums",
|
||||
"dark": "Tumša",
|
||||
"light": "Gaiša",
|
||||
"title": "Tēma"
|
||||
},
|
||||
"user": "Lietotājs",
|
||||
"userCommands": "Komandas",
|
||||
"userCommandsHelp": "Ar atstarpi atdalīts saraksts ar šim lietotājam pieejamajām komandām. Piemērs:\n",
|
||||
"userCreated": "Lietotājs izveidots!",
|
||||
"userDefaults": "Lietotāja noklusējuma iestatījumi",
|
||||
"userDeleted": "Lietotājs izdzēsts!",
|
||||
"userManagement": "Lietotāju pārvaldība",
|
||||
"userUpdated": "Lietotājs atjaunināts!",
|
||||
"username": "Lietotājvārds",
|
||||
"users": "Lietotāji"
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Palīdzība",
|
||||
"hugoNew": "Hugo Jauns",
|
||||
"login": "Pieteikties",
|
||||
"logout": "Atteikties",
|
||||
"myFiles": "Mani faili",
|
||||
"newFile": "Jauns fails",
|
||||
"newFolder": "Jauna mape",
|
||||
"preview": "Priekšskatījums",
|
||||
"settings": "Iestatījumi",
|
||||
"signup": "Reģistrēties",
|
||||
"siteSettings": "Vietnes iestatījumi"
|
||||
},
|
||||
"success": {
|
||||
"linkCopied": "Saite nokopēta!"
|
||||
},
|
||||
"time": {
|
||||
"days": "Dienas",
|
||||
"hours": "Stundas",
|
||||
"minutes": "Minūtes",
|
||||
"seconds": "Sekundes",
|
||||
"unit": "Laika vienība"
|
||||
}
|
||||
}
|
||||
286
frontend/src/i18n/lv_LV.json
Normal file
286
frontend/src/i18n/lv_LV.json
Normal file
@ -0,0 +1,286 @@
|
||||
{
|
||||
"buttons": {
|
||||
"cancel": "Atcelt",
|
||||
"clear": "Tīrs",
|
||||
"close": "Aizvērt",
|
||||
"continue": "Turpināt",
|
||||
"copy": "Kopēt",
|
||||
"copyFile": "Kopēt failu",
|
||||
"copyToClipboard": "Kopēt starpliktuvē",
|
||||
"copyDownloadLinkToClipboard": "Kopēt lejupielādes saiti starpliktuvē",
|
||||
"create": "Izveidot",
|
||||
"delete": "Dzēst",
|
||||
"download": "Lejupielādēt",
|
||||
"file": "Fails",
|
||||
"folder": "Mape",
|
||||
"fullScreen": "Pārslēgt pilnekrāna režīmu",
|
||||
"hideDotfiles": "Slēpt punktfailus",
|
||||
"info": "Informācija",
|
||||
"more": "Vairāk",
|
||||
"move": "Pārvietot",
|
||||
"moveFile": "Pārvietot failu",
|
||||
"new": "Jauns",
|
||||
"next": "Nākamais",
|
||||
"ok": "Labi",
|
||||
"permalink": "Iegūt pastāvīgo saiti",
|
||||
"previous": "Iepriekšējais",
|
||||
"preview": "Priekšskatījums",
|
||||
"publish": "Publicēt",
|
||||
"rename": "Pārdēvēt",
|
||||
"replace": "Aizstāt",
|
||||
"reportIssue": "Ziņot par problēmu",
|
||||
"save": "Saglabāt",
|
||||
"schedule": "Grafiks",
|
||||
"search": "Meklēt",
|
||||
"select": "Izvēlieties",
|
||||
"selectMultiple": "Izvēlieties vairākus",
|
||||
"share": "Dalīties",
|
||||
"shell": "Pārslēgt apvalku",
|
||||
"submit": "Iesniegt",
|
||||
"switchView": "Pārslēgt skatu",
|
||||
"toggleSidebar": "Pārslēgt sānjoslu",
|
||||
"update": "Atjaunināt",
|
||||
"upload": "Augšupielādēt",
|
||||
"openFile": "Atvērt failu",
|
||||
"discardChanges": "Izmest",
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Saglabāt izmaiņas",
|
||||
"editAsText": "Rediģēt kā tekstu",
|
||||
"increaseFontSize": "Palieliniet fonta lielumu",
|
||||
"decreaseFontSize": "Samaziniet fonta lielumu"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Lejupielādēt failu",
|
||||
"downloadFolder": "Lejupielādēt mapi",
|
||||
"downloadSelected": "Lejupielādēt atlasīto"
|
||||
},
|
||||
"upload": {
|
||||
"abortUpload": "Vai tiešām vēlaties pārtraukt?"
|
||||
},
|
||||
"errors": {
|
||||
"forbidden": "Jums nav atļaujas piekļūt šim.",
|
||||
"internal": "Kaut kas tiešām nogāja greizi.",
|
||||
"notFound": "Šo atrašanās vietu nevar sasniegt.",
|
||||
"connection": "Ar serveri nevar sazināties."
|
||||
},
|
||||
"files": {
|
||||
"body": "Ķermenis",
|
||||
"closePreview": "Aizvērt priekšskatījumu",
|
||||
"files": "Faili",
|
||||
"folders": "Mapes",
|
||||
"home": "Sākums",
|
||||
"lastModified": "Pēdējoreiz modificēts",
|
||||
"loading": "Notiek ielāde...",
|
||||
"lonely": "Šeit ir tukša vieta...",
|
||||
"metadata": "Metadati",
|
||||
"multipleSelectionEnabled": "Vairākas atlases ir iespējotas",
|
||||
"name": "Vārds",
|
||||
"size": "Izmērs",
|
||||
"sortByLastModified": "Kārtot pēc pēdējās modifikācijas",
|
||||
"sortByName": "Kārtot pēc nosaukuma",
|
||||
"sortBySize": "Kārtot pēc izmēra",
|
||||
"noPreview": "Šim failam nav pieejams priekšskatījums.",
|
||||
"csvTooLarge": "CSV fails ir pārāk liels priekšskatīšanai (>5 MB). Lūdzu, lejupielādējiet to, lai skatītu",
|
||||
"csvLoadFailed": "Neizdevās ielādēt CSV failu.",
|
||||
"showingRows": "Rāda {count} rindu(as)",
|
||||
"columnSeparator": "Kolonnu atdalītājs",
|
||||
"csvSeparators": {
|
||||
"comma": "Komats (,)",
|
||||
"semicolon": "Semikols (;)",
|
||||
"both": "Gan (,) gan (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "atlasiet failu vai direktoriju",
|
||||
"ctrl": {
|
||||
"click": "atlasīt vairākus failus vai direktorijus",
|
||||
"f": "atver meklēšanu",
|
||||
"s": "saglabājiet failu vai lejupielādējiet direktoriju, kurā atrodaties"
|
||||
},
|
||||
"del": "dzēst atlasītos vienumus",
|
||||
"doubleClick": "atvērt failu vai direktoriju",
|
||||
"esc": "notīrīt atlasi un/vai aizvērt uzvedni",
|
||||
"f1": "šo informāciju",
|
||||
"f2": "pārdēvēt failu",
|
||||
"help": "Palīdzība"
|
||||
},
|
||||
"login": {
|
||||
"createAnAccount": "Izveidot kontu",
|
||||
"loginInstead": "Jau ir konts",
|
||||
"password": "Parole",
|
||||
"passwordConfirm": "Paroles apstiprināšana",
|
||||
"passwordsDontMatch": "Paroles nesakrīt",
|
||||
"signup": "Reģistrēšanās",
|
||||
"submit": "Pieteikties",
|
||||
"username": "Lietotājvārds",
|
||||
"usernameTaken": "Lietotājvārds jau aizņemts",
|
||||
"wrongCredentials": "Nepareizi akreditācijas dati",
|
||||
"passwordTooShort": "Parolei jābūt vismaz {min} rakstzīmju garai",
|
||||
"logout_reasons": {
|
||||
"inactivity": "Jūs esat atteicies no sistēmas neaktivitātes dēļ."
|
||||
}
|
||||
},
|
||||
"permanent": "Pastāvīgs",
|
||||
"prompts": {
|
||||
"copy": "Kopēt",
|
||||
"copyMessage": "Izvēlieties atrašanās vietu, uz kuru kopēt failus:",
|
||||
"currentlyNavigating": "Pašlaik navigācija:",
|
||||
"deleteMessageMultiple": "Vai tiešām vēlaties dzēst {count} failu(s)?",
|
||||
"deleteMessageSingle": "Vai tiešām vēlaties dzēst šo failu/mapi?",
|
||||
"deleteMessageShare": "Vai tiešām vēlaties dzēst šo koplietojumu({path})?",
|
||||
"deleteUser": "Vai tiešām vēlaties dzēst šo lietotāju?",
|
||||
"deleteTitle": "Dzēst failus",
|
||||
"displayName": "Displeja nosaukums:",
|
||||
"download": "Lejupielādēt failus",
|
||||
"downloadMessage": "Izvēlieties formātu, kuru vēlaties lejupielādēt.",
|
||||
"error": "Kaut kas nogāja greizi",
|
||||
"fileInfo": "Informācija par failu",
|
||||
"filesSelected": "{count} atlasīti faili",
|
||||
"lastModified": "Pēdējās izmaiņas",
|
||||
"move": "Pārvietot",
|
||||
"moveMessage": "Izvēlieties jaunu mājvietu failam(iem)/mapei(ēm):",
|
||||
"newArchetype": "Izveidojiet jaunu ierakstu, pamatojoties uz arhtipu. Jūsu fails tiks izveidots satura mapē.",
|
||||
"newDir": "Jauna direktorija",
|
||||
"newDirMessage": "Nosaukums savai jaunajai direktorijai.",
|
||||
"newFile": "Jauns fails",
|
||||
"newFileMessage": "Nosauciet savu jauno failu.",
|
||||
"numberDirs": "Katalogu skaits",
|
||||
"numberFiles": "Failu skaits",
|
||||
"rename": "Pārdēvēt",
|
||||
"renameMessage": "Ievietojiet jaunu nosaukumu",
|
||||
"replace": "Aizstāt",
|
||||
"replaceMessage": "Vienam no failiem, kurus mēģināt augšupielādēt, ir konfliktējošs nosaukums. Vai vēlaties izlaist šo failu un turpināt augšupielādi vai aizstāt esošo?\n",
|
||||
"schedule": "Grafiks",
|
||||
"scheduleMessage": "Izvēlieties datumu un laiku, lai ieplānotu šī ieraksta publicēšanu.",
|
||||
"show": "Rādīt",
|
||||
"size": "Izmērs",
|
||||
"upload": "Augšupielādēt",
|
||||
"uploadFiles": "Notiek {files} failu augšupielāde...",
|
||||
"uploadMessage": "Atlasiet augšupielādes opciju.",
|
||||
"optionalPassword": "Izvēles parole",
|
||||
"resolution": "Izšķirtspēja",
|
||||
"discardEditorChanges": "Vai tiešām vēlaties atmest veiktās izmaiņas?"
|
||||
},
|
||||
"search": {
|
||||
"images": "Attēli",
|
||||
"music": "Mūzika",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "Nospiediet taustiņu Enter, lai meklētu...",
|
||||
"search": "Meklē...",
|
||||
"typeToSearch": "Ierakstiet, lai meklētu...",
|
||||
"types": "Veidi",
|
||||
"video": "Video"
|
||||
},
|
||||
"settings": {
|
||||
"aceEditorTheme": "Ace redaktora tēma",
|
||||
"admin": "Admin",
|
||||
"administrator": "Administrator",
|
||||
"allowCommands": "Izpildīt komandas",
|
||||
"allowEdit": "Rediģēt, pārdēvēt un dzēst failus vai direktorijus",
|
||||
"allowNew": "Izveidojiet jaunus failus un direktorijus",
|
||||
"allowPublish": "Publicēt jaunus ierakstus un lapas",
|
||||
"allowSignup": "Atļaut lietotājiem reģistrēties",
|
||||
"hideLoginButton": "Paslēpt pieteikšanās pogu publiskajās lapās",
|
||||
"avoidChanges": "(atstājiet tukšu, lai izvairītos no izmaiņām)",
|
||||
"branding": "Zīmols",
|
||||
"brandingDirectoryPath": "Zīmola direktorijas ceļš",
|
||||
"brandingHelp": "Jūs varat pielāgot sava failu pārlūka instances izskatu un darbību, mainot tās nosaukumu, aizstājot logotipu, pievienojot pielāgotus stilus un pat atspējojot ārējās saites uz GitHub.\nLai iegūtu plašāku informāciju par pielāgotu zīmola veidošanu, lūdzu, skatiet {0}.",
|
||||
"changePassword": "Mainīt paroli",
|
||||
"commandRunner": "Komandu skrējējs",
|
||||
"commandRunnerHelp": "Šeit varat iestatīt komandas, kas tiek izpildītas nosauktajos notikumos. Katrā rindā jāraksta pa vienai. Būs pieejami vides mainīgie {0} un {1}, kas ir {0} relatīvi pret {1}. Lai iegūtu plašāku informāciju par šo funkciju un pieejamajiem vides mainīgajiem, lūdzu, izlasiet {2}.",
|
||||
"commandsUpdated": "Komandas atjauninātas!",
|
||||
"createUserDir": "Automātiski izveidot lietotāja mājas direktoriju, pievienojot jaunu lietotāju",
|
||||
"minimumPasswordLength": "Minimālais paroles garums",
|
||||
"tusUploads": "Sadalītas augšupielādes",
|
||||
"tusUploadsHelp": "Failu pārlūks atbalsta failu augšupielādi fragmentos, ļaujot izveidot efektīvu, uzticamu, atsākamu un fragmentos sadalītu failu augšupielādi pat neuzticamos tīklos.",
|
||||
"tusUploadsChunkSize": "Norāda pieprasījuma maksimālo izmēru (mazākiem augšupielādes apjomiem tiks izmantota tieša augšupielāde). Varat ievadīt vienkāršu veselu skaitli, kas apzīmē baitu izmēru, vai virkni, piemēram, 10 MB, 1 GB utt.",
|
||||
"tusUploadsRetryCount": "Atkārtotu mēģinājumu skaits, kas jāveic, ja fragmenta augšupielāde neizdodas.",
|
||||
"userHomeBasePath": "Lietotāja mājas direktoriju bāzes ceļš",
|
||||
"userScopeGenerationPlaceholder": "Darbības joma tiks ģenerēta automātiski",
|
||||
"createUserHomeDirectory": "Izveidojiet lietotāja mājas direktoriju",
|
||||
"customStylesheet": "Pielāgota stila lapa",
|
||||
"defaultUserDescription": "Šie ir noklusējuma iestatījumi jaunajiem lietotājiem.",
|
||||
"disableExternalLinks": "Atspējot ārējās saites (izņemot dokumentāciju)",
|
||||
"disableUsedDiskPercentage": "Atspējot izmantotā diska procentuālās daļas grafiku",
|
||||
"documentation": "dokumentācija",
|
||||
"examples": "Piemēri",
|
||||
"executeOnShell": "Izpildīt uz čaulas",
|
||||
"executeOnShellDescription": "Pēc noklusējuma failu pārlūks izpilda komandas, tieši izsaucot to bināros failus. Ja vēlaties tās palaist čaulā (piemēram, Bash vai PowerShell), varat to definēt šeit ar nepieciešamajiem argumentiem un karodziņiem. Ja tas ir iestatīts, izpildītā komanda tiks pievienota kā arguments. Tas attiecas gan uz lietotāja komandām, gan notikumu piesaistes rīkiem.",
|
||||
"globalRules": "Šis ir globāls atļaušanas un aizliegšanas noteikumu kopums. Tie attiecas uz katru lietotāju. Katra lietotāja iestatījumos varat definēt konkrētus noteikumus, lai tos ignorētu.",
|
||||
"globalSettings": "Globālie iestatījumi",
|
||||
"hideDotfiles": "Slēpt punktfailus",
|
||||
"insertPath": "Ievietojiet ceļu",
|
||||
"insertRegex": "Ievietojiet regex izteiksmi",
|
||||
"instanceName": "Gadījuma nosaukums",
|
||||
"language": "Valoda",
|
||||
"lockPassword": "Neļaut lietotājam mainīt paroli",
|
||||
"newPassword": "Jūsu jaunā parole",
|
||||
"newPasswordConfirm": "Apstipriniet savu jauno paroli",
|
||||
"newUser": "Jauns lietotājs",
|
||||
"password": "Parole",
|
||||
"passwordUpdated": "Parole atjaunināta!",
|
||||
"path": "Ceļš",
|
||||
"perm": {
|
||||
"create": "Izveidojiet failus un direktorijus",
|
||||
"delete": "Dzēst failus un direktorijus",
|
||||
"download": "Lejupielādēt",
|
||||
"execute": "Izpildīt komandas",
|
||||
"modify": "Rediģēt failus",
|
||||
"rename": "Pārdēvēt vai pārvietot failus un direktorijus",
|
||||
"share": "Kopīgojiet failus"
|
||||
},
|
||||
"permissions": "Atļaujas",
|
||||
"permissionsHelp": "Varat iestatīt lietotāju kā administratoru vai izvēlēties atļaujas individuāli. Ja atlasīsiet “Administrators”, visas pārējās opcijas tiks automātiski atzīmētas. Lietotāju pārvaldība joprojām ir administratora privilēģija.\n",
|
||||
"profileSettings": "Profila iestatījumi",
|
||||
"ruleExample1": "neļauj piekļūt jebkuram dotfile failam (piemēram, .git, .gitignore) katrā mapē.\n",
|
||||
"ruleExample2": "bloķē piekļuvi failam ar nosaukumu Caddyfile darbības jomas saknē.",
|
||||
"rules": "Noteikumi",
|
||||
"rulesHelp": "Šeit varat definēt atļaušanas un aizliegšanas noteikumu kopu šim konkrētajam lietotājam. Bloķētie faili netiks rādīti sarakstos, un lietotājs tiem nevarēs piekļūt. Mēs atbalstām regulārās izteiksmes un ceļus attiecībā pret lietotāja darbības jomu.\n",
|
||||
"scope": "Darbības joma",
|
||||
"setDateFormat": "Iestatiet precīzu datuma formātu",
|
||||
"settingsUpdated": "Iestatījumi atjaunināti!",
|
||||
"shareDuration": "Kopīgošanas ilgums",
|
||||
"shareManagement": "Kopīgošanas pārvaldība",
|
||||
"shareDeleted": "Kopīgošana ir izdzēsta!",
|
||||
"singleClick": "Failu un direktoriju atvēršanai izmantojiet vienus klikšķi",
|
||||
"themes": {
|
||||
"default": "Sistēmas noklusējums",
|
||||
"dark": "Tumša",
|
||||
"light": "Gaiša",
|
||||
"title": "Tēma"
|
||||
},
|
||||
"user": "Lietotājs",
|
||||
"userCommands": "Komandas",
|
||||
"userCommandsHelp": "Ar atstarpi atdalīts saraksts ar šim lietotājam pieejamajām komandām. Piemērs:\n",
|
||||
"userCreated": "Lietotājs izveidots!",
|
||||
"userDefaults": "Lietotāja noklusējuma iestatījumi",
|
||||
"userDeleted": "Lietotājs izdzēsts!",
|
||||
"userManagement": "Lietotāju pārvaldība",
|
||||
"userUpdated": "Lietotājs atjaunināts!",
|
||||
"username": "Lietotājvārds",
|
||||
"users": "Lietotāji"
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Palīdzība",
|
||||
"hugoNew": "Hugo Jauns",
|
||||
"login": "Pieteikties",
|
||||
"logout": "Atteikties",
|
||||
"myFiles": "Mani faili",
|
||||
"newFile": "Jauns fails",
|
||||
"newFolder": "Jauna mape",
|
||||
"preview": "Priekšskatījums",
|
||||
"settings": "Iestatījumi",
|
||||
"signup": "Reģistrēties",
|
||||
"siteSettings": "Vietnes iestatījumi"
|
||||
},
|
||||
"success": {
|
||||
"linkCopied": "Saite nokopēta!"
|
||||
},
|
||||
"time": {
|
||||
"days": "Dienas",
|
||||
"hours": "Stundas",
|
||||
"minutes": "Minūtes",
|
||||
"seconds": "Sekundes",
|
||||
"unit": "Laika vienība"
|
||||
}
|
||||
}
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Uploaden",
|
||||
"openFile": "Open file",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Bestand downloaden",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Sorteren op laatst bewerkt",
|
||||
"sortByName": "Sorteren op naam",
|
||||
"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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "selecteer bestand of map",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Gebruikersnaam",
|
||||
"usernameTaken": "Gebruikersnaam reeds in gebruik",
|
||||
"wrongCredentials": "Verkeerde inloggegevens",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Last opp",
|
||||
"openFile": "Open file",
|
||||
"discardChanges": "Slett",
|
||||
"saveChanges": "Lagre Endringane "
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Lagre Endringane ",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Nedlast filen",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Sorter etter sist endret",
|
||||
"sortByName": "Sorter etter navn",
|
||||
"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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "velg fil eller katalog",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Brukernavn",
|
||||
"usernameTaken": "Brukernavn er allerede i bruk",
|
||||
"wrongCredentials": "Feil legitimasjon",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "Du har blitt logget ut på grunn av inaktivitet"
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Wyślij",
|
||||
"openFile": "Otwórz plik",
|
||||
"discardChanges": "Odrzuć",
|
||||
"saveChanges": "Zapisz zmiany"
|
||||
"stopSearch": "Zatrzymaj wyszukiwanie",
|
||||
"saveChanges": "Zapisz zmiany",
|
||||
"editAsText": "Edytuj jako tekst",
|
||||
"increaseFontSize": "Zwiększ rozmiar czcionki",
|
||||
"decreaseFontSize": "Zmniejsz rozmiar czcionki"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Pobierz plik",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Sortuj wg ostatniej modyfikacji",
|
||||
"sortByName": "Sortuj wg nazwy",
|
||||
"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.",
|
||||
"showingRows": "Wyświetlanie wierszy: {count}",
|
||||
"columnSeparator": "Separator kolumn",
|
||||
"csvSeparators": {
|
||||
"comma": "Przecinek (,)",
|
||||
"semicolon": "Średnik (;)",
|
||||
"both": "Zarówno (,), jak i (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "zaznacz plik lub folder",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Nazwa użytkownika",
|
||||
"usernameTaken": "Ta nazwa użytkownika jest zajęta",
|
||||
"wrongCredentials": "Błędne dane logowania",
|
||||
"passwordTooShort": "Wymagana minimalna liczba znaków hasła: {min}",
|
||||
"logout_reasons": {
|
||||
"inactivity": "Wylogowano z powodu braku aktywności."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Enviar",
|
||||
"openFile": "Abrir",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Baixar arquivo",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Ordenar pela última modificação",
|
||||
"sortByName": "Ordenar pelo nome",
|
||||
"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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "selecionar pasta ou arquivo",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Nome do usuário",
|
||||
"usernameTaken": "Nome de usuário já existe",
|
||||
"wrongCredentials": "Ops! Dados incorretos.",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Enviar",
|
||||
"openFile": "Open file",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Descarregar ficheiro",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Ordenar pela última alteração",
|
||||
"sortByName": "Ordenar pelo nome",
|
||||
"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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "selecionar pasta ou ficheiro",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Nome de utilizador",
|
||||
"usernameTaken": "O nome de utilizador já está registado",
|
||||
"wrongCredentials": "Dados errados",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Încarcă",
|
||||
"openFile": "Open file",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Descarcă fișier",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Ordonează dup ultima modificare",
|
||||
"sortByName": "Ordonează după nume",
|
||||
"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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "alege fișier sau director",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Utilizator",
|
||||
"usernameTaken": "Utilizatorul există",
|
||||
"wrongCredentials": "Informații greșite",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -42,8 +42,13 @@
|
||||
"update": "Обновить",
|
||||
"upload": "Загрузить",
|
||||
"openFile": "Открыть файл",
|
||||
"stopSearch": "Прекратить поиск",
|
||||
"discardChanges": "Отказаться",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Сохранить",
|
||||
"editAsText": "Редактировать как текст",
|
||||
"increaseFontSize": "Увеличить размер шрифта",
|
||||
"decreaseFontSize": "Уменьшить размер шрифта"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Скачать файл",
|
||||
@ -75,7 +80,16 @@
|
||||
"sortByLastModified": "Сортировка по дате изменения",
|
||||
"sortByName": "Сортировка по имени",
|
||||
"sortBySize": "Сортировка по размеру",
|
||||
"noPreview": "Предварительный просмотр для этого файла недоступен."
|
||||
"noPreview": "Предварительный просмотр для этого файла недоступен.",
|
||||
"csvTooLarge": "Этот CSV файл слишком большой для предпросмотра (>5 МБ). Скачайте и откройте его локально.",
|
||||
"csvLoadFailed": "Не удалось загрузить этот CSV.",
|
||||
"showingRows": "Отображается {count} строк(а)",
|
||||
"columnSeparator": "Разделитель столбцов",
|
||||
"csvSeparators": {
|
||||
"comma": "Запятая (,)",
|
||||
"semicolon": "Точка с запятой (;)",
|
||||
"both": "Оба варианта — (,) и (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "выбрать файл или каталог",
|
||||
@ -102,8 +116,9 @@
|
||||
"username": "Имя пользователя",
|
||||
"usernameTaken": "Данное имя пользователя уже занято",
|
||||
"wrongCredentials": "Неверные данные",
|
||||
"passwordTooShort": "Пароль должен состоять как минимум из {min} символов",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
"inactivity": "Сессия завершена после долгого отсутствия."
|
||||
}
|
||||
},
|
||||
"permanent": "Постоянный",
|
||||
@ -158,7 +173,7 @@
|
||||
"video": "Видео"
|
||||
},
|
||||
"settings": {
|
||||
"aceEditorTheme": "Ace editor theme",
|
||||
"aceEditorTheme": "Тема редактора Ace",
|
||||
"admin": "Админ",
|
||||
"administrator": "Администратор",
|
||||
"allowCommands": "Запуск команд",
|
||||
@ -166,7 +181,7 @@
|
||||
"allowNew": "Создание новых файлов или каталогов",
|
||||
"allowPublish": "Публикация новых записей и страниц",
|
||||
"allowSignup": "Разрешить пользователям регистрироваться",
|
||||
"hideLoginButton": "Hide the login button from public pages",
|
||||
"hideLoginButton": "Спрятать кнопку входа с публичных страниц",
|
||||
"avoidChanges": "(оставьте поле пустым, чтобы избежать изменений)",
|
||||
"branding": "Брендинг",
|
||||
"brandingDirectoryPath": "Путь к каталогу брендов",
|
||||
@ -176,7 +191,7 @@
|
||||
"commandRunnerHelp": "Здесь вы можете установить команды, которые будут выполняться в указанных событиях. Вы должны указать по одной команде в каждой строке. Переменные среды {0} и {1} будут доступны, будучи {0} относительно {1}. Дополнительные сведения об этой функции и доступных переменных среды см. В {2}.",
|
||||
"commandsUpdated": "Команды обновлены!",
|
||||
"createUserDir": "Автоматическое создание домашнего каталога пользователя при добавлении нового пользователя",
|
||||
"minimumPasswordLength": "Minimum password length",
|
||||
"minimumPasswordLength": "Минимальная длина пароля",
|
||||
"tusUploads": "Загруженные файлы",
|
||||
"tusUploadsHelp": " File Browser поддерживает загрузку файлов по частям, что позволяет работать в сетях низкого качества.",
|
||||
"tusUploadsChunkSize": "Указывает максимальный размер запроса (мелкие загрузки пойдут напрямую). Вы можете ввести простое целое число, обозначающее размер ввода в байтах, или строку, например 10MB, 1GB и т. д.",
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Nahrať",
|
||||
"openFile": "Otvoriť súbor",
|
||||
"discardChanges": "Zahodiť",
|
||||
"saveChanges": "Uložiť zmeny"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Uložiť zmeny",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Stiahnuť súbor",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Zoradiť podľa dátumu",
|
||||
"sortByName": "Zoradiť podľa názvu",
|
||||
"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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "vyberie súbor alebo priečinok",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Používateľské meno",
|
||||
"usernameTaken": "Meno je už obsadené",
|
||||
"wrongCredentials": "Nesprávne prihlasovacie údaje",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "Boli ste odhlásení z dôvodu nečinnosti."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Ladda upp",
|
||||
"openFile": "Öppna fil",
|
||||
"discardChanges": "Förkasta",
|
||||
"saveChanges": "Spara ändringar"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Spara ändringar",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Ladda ner fil",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Sortera på senast ändrad",
|
||||
"sortByName": "Sortera på namn",
|
||||
"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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "välj fil eller mapp",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Användarnamn",
|
||||
"usernameTaken": "Användarnamn upptaget",
|
||||
"wrongCredentials": "Fel inloggning",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "Du har blivit utloggad på grund av inaktivitet."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Yükle",
|
||||
"openFile": "Dosyayı aç",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Dosyayı indir",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Güncelleme tarihine göre sırala",
|
||||
"sortByName": "İsme 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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "dosya veya klasör seçin",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Kullanıcı adı",
|
||||
"usernameTaken": "Kullanıcı adı mevcut",
|
||||
"wrongCredentials": "Yanlış hesap bilgileri",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Вивантажити",
|
||||
"openFile": "Відкрити файл",
|
||||
"discardChanges": "Скасувати",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Завантажити файл",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Сортувати за останнім зміненням",
|
||||
"sortByName": "Сортувати за іменем",
|
||||
"sortBySize": "Сортувати за розміром",
|
||||
"noPreview": "Попередній перегляд для цього файлу недоступний."
|
||||
"noPreview": "Попередній перегляд для цього файлу недоступний.",
|
||||
"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": {
|
||||
"click": "вибрати файл чи каталог",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Ім'я користувача",
|
||||
"usernameTaken": "Ім'я користувача вже використовується",
|
||||
"wrongCredentials": "Неправильне ім'я користувача або пароль",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -43,7 +43,11 @@
|
||||
"upload": "Tải lên",
|
||||
"openFile": "Mở tệp",
|
||||
"discardChanges": "Hủy bỏ thay đổi",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Tải xuống tệp tin",
|
||||
@ -75,7 +79,16 @@
|
||||
"sortByLastModified": "Sắp xếp theo ngày sửa đổi",
|
||||
"sortByName": "Sắp xếp theo tên",
|
||||
"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.",
|
||||
"showingRows": "Showing {count} row(s)",
|
||||
"columnSeparator": "Column Separator",
|
||||
"csvSeparators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "chọn tập tin hoặc thư mục",
|
||||
@ -102,6 +115,7 @@
|
||||
"username": "Tên người dùng",
|
||||
"usernameTaken": "Tên người dùng đã tồn tại",
|
||||
"wrongCredentials": "Thông tin đăng nhập không đúng",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -42,8 +42,13 @@
|
||||
"update": "更新",
|
||||
"upload": "上传",
|
||||
"openFile": "打开文件",
|
||||
"stopSearch": "停止搜索",
|
||||
"discardChanges": "放弃更改",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "保存更改",
|
||||
"editAsText": "以文本形式编辑",
|
||||
"increaseFontSize": "增大字体大小",
|
||||
"decreaseFontSize": "减小字体大小"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "下载文件",
|
||||
@ -75,7 +80,16 @@
|
||||
"sortByLastModified": "按最后修改时间排序",
|
||||
"sortByName": "按名称排序",
|
||||
"sortBySize": "按大小排序",
|
||||
"noPreview": "此文件无法预览。"
|
||||
"noPreview": "此文件无法预览。",
|
||||
"csvTooLarge": "CSV文件大到无法预览(>5MB)。请下载查看。",
|
||||
"csvLoadFailed": "加载 CSV 文件失败。",
|
||||
"showingRows": "正在显示 {count} 行",
|
||||
"columnSeparator": "列分隔符",
|
||||
"csvSeparators": {
|
||||
"comma": "逗号 (,)",
|
||||
"semicolon": "分号 (;)",
|
||||
"both": "逗号 (,) 和分号 (;)"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
"click": "选择文件或文件夹",
|
||||
@ -102,8 +116,9 @@
|
||||
"username": "用户名",
|
||||
"usernameTaken": "用户名已经被使用",
|
||||
"wrongCredentials": "用户名或密码错误",
|
||||
"passwordTooShort": "密码必须至少包含 {min} 个字符",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
"inactivity": "由于未活动,您已登出。"
|
||||
}
|
||||
},
|
||||
"permanent": "永久",
|
||||
@ -158,7 +173,7 @@
|
||||
"video": "视频"
|
||||
},
|
||||
"settings": {
|
||||
"aceEditorTheme": "Ace editor theme",
|
||||
"aceEditorTheme": "Ace编辑器主题",
|
||||
"admin": "管理员",
|
||||
"administrator": "管理员",
|
||||
"allowCommands": "执行命令(Shell 命令)",
|
||||
@ -166,7 +181,7 @@
|
||||
"allowNew": "创建新文件和文件夹",
|
||||
"allowPublish": "发布新的帖子与页面",
|
||||
"allowSignup": "允许用户注册",
|
||||
"hideLoginButton": "Hide the login button from public pages",
|
||||
"hideLoginButton": "从公开页面隐藏登录按钮",
|
||||
"avoidChanges": "(留空以避免更改)",
|
||||
"branding": "品牌",
|
||||
"brandingDirectoryPath": "品牌信息文件夹路径",
|
||||
|
||||
@ -42,8 +42,13 @@
|
||||
"update": "更新",
|
||||
"upload": "上傳",
|
||||
"openFile": "開啟檔案",
|
||||
"stopSearch": "停止搜尋",
|
||||
"discardChanges": "放棄變更",
|
||||
"saveChanges": "Save changes"
|
||||
"stopSearch": "Stop searching",
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text",
|
||||
"increaseFontSize": "Increase font size",
|
||||
"decreaseFontSize": "Decrease font size"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "下載檔案",
|
||||
@ -75,7 +80,16 @@
|
||||
"sortByLastModified": "按最後修改時間排序",
|
||||
"sortByName": "按名稱排序",
|
||||
"sortBySize": "按大小排序",
|
||||
"noPreview": "此檔案無法預覽。"
|
||||
"noPreview": "此檔案無法預覽。",
|
||||
"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": {
|
||||
"click": "選擇檔案或目錄",
|
||||
@ -102,6 +116,7 @@
|
||||
"username": "帳號",
|
||||
"usernameTaken": "用戶名已存在",
|
||||
"wrongCredentials": "帳號或密碼錯誤",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import { useAuthStore } from "@/stores/auth";
|
||||
import router from "@/router";
|
||||
import type { JwtPayload } 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 { setSafeTimeout } from "@/api/utils";
|
||||
|
||||
@ -18,6 +18,12 @@ export function parseToken(token: string) {
|
||||
authStore.jwt = token;
|
||||
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) {
|
||||
clearTimeout(authStore.logoutTimer);
|
||||
}
|
||||
@ -101,7 +107,11 @@ export async function signup(username: string, password: string) {
|
||||
});
|
||||
|
||||
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", "");
|
||||
if (noAuth) {
|
||||
window.location.reload();
|
||||
} else if (logoutPage !== "/login") {
|
||||
document.location.href = `${logoutPage}`;
|
||||
} else {
|
||||
if (typeof reason === "string" && reason.trim() !== "") {
|
||||
router.push({
|
||||
|
||||
@ -10,6 +10,7 @@ const version: string = window.FileBrowser.Version;
|
||||
const logoURL = `${staticURL}/img/logo.svg`;
|
||||
const noAuth: boolean = window.FileBrowser.NoAuth;
|
||||
const authMethod = window.FileBrowser.AuthMethod;
|
||||
const logoutPage: string = window.FileBrowser.LogoutPage;
|
||||
const loginPage: boolean = window.FileBrowser.LoginPage;
|
||||
const theme: UserTheme = window.FileBrowser.Theme;
|
||||
const enableThumbs: boolean = window.FileBrowser.EnableThumbs;
|
||||
@ -32,6 +33,7 @@ export {
|
||||
version,
|
||||
noAuth,
|
||||
authMethod,
|
||||
logoutPage,
|
||||
loginPage,
|
||||
theme,
|
||||
enableThumbs,
|
||||
|
||||
64
frontend/src/utils/csv.ts
Normal file
64
frontend/src/utils/csv.ts
Normal 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 };
|
||||
}
|
||||
@ -69,6 +69,12 @@ const currentView = computed(() => {
|
||||
|
||||
if (fileStore.req.isDir) {
|
||||
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 (
|
||||
fileStore.req.type === "text" ||
|
||||
fileStore.req.type === "textImmutable"
|
||||
|
||||
@ -112,6 +112,13 @@ const submit = async (event: Event) => {
|
||||
error.value = t("login.usernameTaken");
|
||||
} else if (e.status === 403) {
|
||||
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 {
|
||||
$showError(e);
|
||||
}
|
||||
|
||||
@ -186,7 +186,7 @@ const handlePageChange = (event: BeforeUnloadEvent) => {
|
||||
}
|
||||
};
|
||||
|
||||
const save = async () => {
|
||||
const save = async (throwError?: boolean) => {
|
||||
const button = "save";
|
||||
buttons.loading("save");
|
||||
|
||||
@ -197,6 +197,7 @@ const save = async () => {
|
||||
} catch (e: any) {
|
||||
buttons.done(button);
|
||||
$showError(e);
|
||||
if (throwError) throw e;
|
||||
}
|
||||
};
|
||||
|
||||
@ -223,8 +224,10 @@ const close = () => {
|
||||
finishClose();
|
||||
},
|
||||
saveAction: async () => {
|
||||
await save();
|
||||
finishClose();
|
||||
try {
|
||||
await save(true);
|
||||
finishClose();
|
||||
} catch {}
|
||||
},
|
||||
});
|
||||
return;
|
||||
|
||||
@ -356,7 +356,7 @@ import {
|
||||
ref,
|
||||
watch,
|
||||
} from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useRoute, onBeforeRouteUpdate } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { removePrefix } from "@/api/utils";
|
||||
@ -379,6 +379,9 @@ const layoutStore = useLayoutStore();
|
||||
const { req } = storeToRefs(fileStore);
|
||||
|
||||
const route = useRoute();
|
||||
onBeforeRouteUpdate(() => {
|
||||
hideContextMenu();
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
@mousemove="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()" />
|
||||
<title>{{ name }}</title>
|
||||
<action
|
||||
@ -24,6 +24,13 @@
|
||||
:label="$t('buttons.rename')"
|
||||
show="rename"
|
||||
/>
|
||||
<action
|
||||
:disabled="layoutStore.loading"
|
||||
v-if="isCsv && authStore.user?.perm.modify"
|
||||
icon="edit_note"
|
||||
:label="t('buttons.editAsText')"
|
||||
@action="editAsText"
|
||||
/>
|
||||
<action
|
||||
:disabled="layoutStore.loading"
|
||||
v-if="authStore.user?.perm.delete"
|
||||
@ -87,6 +94,7 @@
|
||||
<span>{{ size }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<CsvViewer v-else-if="isCsv" :content="csvContent" :error="csvError" />
|
||||
<ExtendedImage
|
||||
v-else-if="fileStore.req?.type == 'image'"
|
||||
:src="previewUrl"
|
||||
@ -176,11 +184,17 @@ import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||
import Action from "@/components/header/Action.vue";
|
||||
import ExtendedImage from "@/components/files/ExtendedImage.vue";
|
||||
import VideoPlayer from "@/components/files/VideoPlayer.vue";
|
||||
import CsvViewer from "@/components/files/CsvViewer.vue";
|
||||
import { VueReader } from "vue-reader";
|
||||
import { computed, inject, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import type { Rendition } from "epubjs";
|
||||
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, {
|
||||
serializer: {
|
||||
@ -239,6 +253,8 @@ const hoverNav = ref<boolean>(false);
|
||||
const autoPlay = ref<boolean>(false);
|
||||
const previousRaw = ref<string>("");
|
||||
const nextRaw = ref<string>("");
|
||||
const csvContent = ref<string>("");
|
||||
const csvError = ref<string>("");
|
||||
|
||||
const player = ref<HTMLVideoElement | HTMLAudioElement | null>(null);
|
||||
|
||||
@ -248,6 +264,8 @@ const authStore = useAuthStore();
|
||||
const fileStore = useFileStore();
|
||||
const layoutStore = useLayoutStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
@ -279,6 +297,7 @@ const isPdf = computed(() => fileStore.req?.extension.toLowerCase() == ".pdf");
|
||||
const isEpub = computed(
|
||||
() => fileStore.req?.extension.toLowerCase() == ".epub"
|
||||
);
|
||||
const isCsv = computed(() => fileStore.req?.extension.toLowerCase() == ".csv");
|
||||
|
||||
const isResizeEnabled = computed(() => resizePreview);
|
||||
|
||||
@ -366,6 +385,18 @@ const updatePreview = async () => {
|
||||
const dirs = route.fullPath.split("/");
|
||||
name.value = decodeURIComponent(dirs[dirs.length - 1]);
|
||||
|
||||
// Load CSV content if it's a CSV file
|
||||
if (isCsv.value && fileStore.req) {
|
||||
csvContent.value = "";
|
||||
csvError.value = "";
|
||||
|
||||
if (fileStore.req.size > CSV_MAX_SIZE) {
|
||||
csvError.value = t("files.csvTooLarge");
|
||||
} else {
|
||||
csvContent.value = fileStore.req.content ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
if (!listing.value) {
|
||||
try {
|
||||
const path = url.removeLastDir(route.path);
|
||||
@ -435,4 +466,8 @@ const close = () => {
|
||||
};
|
||||
|
||||
const download = () => window.open(downloadUrl.value);
|
||||
|
||||
const editAsText = () => {
|
||||
router.push({ path: route.path, query: { edit: "true" } });
|
||||
};
|
||||
</script>
|
||||
|
||||
20
go.mod
20
go.mod
@ -12,21 +12,21 @@ require (
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/jellydator/ttlcache/v3 v3.4.0
|
||||
github.com/maruel/natural v1.2.1
|
||||
github.com/maruel/natural v1.3.0
|
||||
github.com/marusama/semaphore/v2 v2.5.0
|
||||
github.com/mholt/archives v0.1.5
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/samber/lo v1.52.0
|
||||
github.com/shirou/gopsutil/v4 v4.25.10
|
||||
github.com/shirou/gopsutil/v4 v4.25.11
|
||||
github.com/spf13/afero v1.15.0
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/spf13/viper v1.21.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
|
||||
golang.org/x/crypto v0.44.0
|
||||
golang.org/x/image v0.33.0
|
||||
golang.org/x/text v0.31.0
|
||||
golang.org/x/crypto v0.46.0
|
||||
golang.org/x/image v0.34.0
|
||||
golang.org/x/text v0.32.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
@ -44,7 +44,7 @@ require (
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
|
||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
|
||||
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // indirect
|
||||
github.com/ebitengine/purego v0.9.0 // indirect
|
||||
github.com/ebitengine/purego v0.9.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-errors/errors v1.5.1 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
@ -73,9 +73,9 @@ require (
|
||||
go.etcd.io/bbolt v1.4.3 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/net v0.46.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
40
go.sum
40
go.sum
@ -75,8 +75,8 @@ github.com/dsoprea/go-utility/v2 v2.0.0-20221003142440-7a1927d49d9d/go.mod h1:LV
|
||||
github.com/dsoprea/go-utility/v2 v2.0.0-20221003160719-7bc88537c05e/go.mod h1:VZ7cB0pTjm1ADBWhJUOHESu4ZYy9JN+ZPqjfiW09EPU=
|
||||
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 h1:DilThiXje0z+3UQ5YjYiSRRzVdtamFpvBQXKwMglWqw=
|
||||
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349/go.mod h1:4GC5sXji84i/p+irqghpPFZBF8tRN/Q7+700G0/DLe8=
|
||||
github.com/ebitengine/purego v0.9.0 h1:mh0zpKBIXDceC63hpvPuGLiJ8ZAa3DfrFTudmfi8A4k=
|
||||
github.com/ebitengine/purego v0.9.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||
@ -169,8 +169,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/maruel/natural v1.2.1 h1:G/y4pwtTA07lbQsMefvsmEO0VN0NfqpxprxXDM4R/4o=
|
||||
github.com/maruel/natural v1.2.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
|
||||
github.com/maruel/natural v1.3.0 h1:VsmCsBmEyrR46RomtgHs5hbKADGRVtliHTyCOLFBpsg=
|
||||
github.com/maruel/natural v1.3.0/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
|
||||
github.com/marusama/semaphore/v2 v2.5.0 h1:o/1QJD9DBYOWRnDhPwDVAXQn6mQYD0gZaS1Tpx6DJGM=
|
||||
github.com/marusama/semaphore/v2 v2.5.0/go.mod h1:z9nMiNUekt/LTpTUQdpp+4sJeYqUGpwMHfW0Z8V8fnQ=
|
||||
github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ=
|
||||
@ -204,8 +204,8 @@ github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDc
|
||||
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
|
||||
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/shirou/gopsutil/v4 v4.25.10 h1:at8lk/5T1OgtuCp+AwrDofFRjnvosn0nkN2OLQ6g8tA=
|
||||
github.com/shirou/gopsutil/v4 v4.25.10/go.mod h1:+kSwyC8DRUD9XXEHCAFjK+0nuArFJM0lva+StQAcskM=
|
||||
github.com/shirou/gopsutil/v4 v4.25.11 h1:X53gB7muL9Gnwwo2evPSE+SfOrltMoR6V3xJAXZILTY=
|
||||
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/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||
@ -214,8 +214,8 @@ github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
@ -266,8 +266,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
|
||||
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@ -279,8 +279,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ=
|
||||
golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc=
|
||||
golang.org/x/image v0.34.0 h1:33gCkyw9hmwbZJeZkct8XyR11yH889EQt/QH4VmXMn8=
|
||||
golang.org/x/image v0.34.0/go.mod h1:2RNFBZRB+vnwwFil8GkMdRvrJOFd1AzdZI6vOY+eJVU=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@ -319,8 +319,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -333,8 +333,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -360,8 +360,8 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@ -372,8 +372,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
||||
34
http/auth.go
34
http/auth.go
@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@ -12,7 +12,9 @@ import (
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/golang-jwt/jwt/v5/request"
|
||||
|
||||
fbErrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
fbAuth "github.com/filebrowser/filebrowser/v2/auth"
|
||||
fberrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
"github.com/filebrowser/filebrowser/v2/settings"
|
||||
"github.com/filebrowser/filebrowser/v2/users"
|
||||
)
|
||||
|
||||
@ -61,6 +63,22 @@ func (e extractor) ExtractToken(r *http.Request) (string, error) {
|
||||
return "", request.ErrNoTokenInRequest
|
||||
}
|
||||
|
||||
func renewableErr(err error, d *data) bool {
|
||||
if d.settings.AuthMethod != fbAuth.MethodProxyAuth || err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if d.settings.LogoutPage == settings.DefaultLogoutPage {
|
||||
return false
|
||||
}
|
||||
|
||||
if !errors.Is(err, jwt.ErrTokenExpired) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func withUser(fn handleFunc) handleFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
keyFunc := func(_ *jwt.Token) (interface{}, error) {
|
||||
@ -68,13 +86,9 @@ func withUser(fn handleFunc) handleFunc {
|
||||
}
|
||||
|
||||
var tk authToken
|
||||
token, err := request.ParseFromRequest(r, &extractor{}, keyFunc, request.WithClaims(&tk))
|
||||
if err != nil || !token.Valid {
|
||||
return http.StatusUnauthorized, nil
|
||||
}
|
||||
|
||||
err = jwt.NewValidator(jwt.WithExpirationRequired()).Validate(tk)
|
||||
if err != nil {
|
||||
p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}), jwt.WithExpirationRequired())
|
||||
token, err := request.ParseFromRequest(r, &extractor{}, keyFunc, request.WithClaims(&tk), request.WithParser(p))
|
||||
if (err != nil || !token.Valid) && !renewableErr(err, d) {
|
||||
return http.StatusUnauthorized, nil
|
||||
}
|
||||
|
||||
@ -171,7 +185,7 @@ var signupHandler = func(_ http.ResponseWriter, r *http.Request, d *data) (int,
|
||||
log.Printf("new user: %s, home dir: [%s].", user.Username, userHome)
|
||||
|
||||
err = d.store.Users.Save(user)
|
||||
if errors.Is(err, fbErrors.ErrExist) {
|
||||
if errors.Is(err, fberrors.ErrExist) {
|
||||
return http.StatusConflict, err
|
||||
} else if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
//go:build !dev
|
||||
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
// global headers to append to every response
|
||||
var globalHeaders = map[string]string{
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//go:generate go-enum --sql --marshal --names --file $GOFILE
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Code generated by go-enum
|
||||
// DO NOT EDIT!
|
||||
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@ -41,6 +41,7 @@ var withHashFile = func(fn handleFunc) handleFunc {
|
||||
Modify: d.user.Perm.Modify,
|
||||
Expand: false,
|
||||
ReadHeader: d.server.TypeDetectionByHeader,
|
||||
CalcImgRes: d.server.TypeDetectionByHeader,
|
||||
Checker: d,
|
||||
Token: link.Token,
|
||||
})
|
||||
@ -74,6 +75,12 @@ var withHashFile = func(fn handleFunc) handleFunc {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
if file.IsDir {
|
||||
// extract name from the last directory in the path
|
||||
name := filepath.Base(strings.TrimRight(link.Path, string(filepath.Separator)))
|
||||
file.Name = name
|
||||
}
|
||||
|
||||
d.raw = file
|
||||
return fn(w, r, d)
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@ -123,6 +123,7 @@ func getFiles(d *data, path, commonPath string) ([]archives.FileInfo, error) {
|
||||
if path != commonPath {
|
||||
nameInArchive := strings.TrimPrefix(path, commonPath)
|
||||
nameInArchive = strings.TrimPrefix(nameInArchive, string(filepath.Separator))
|
||||
nameInArchive = filepath.ToSlash(nameInArchive)
|
||||
|
||||
archiveFiles = append(archiveFiles, archives.FileInfo{
|
||||
FileInfo: info,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -17,7 +17,7 @@ import (
|
||||
"github.com/shirou/gopsutil/v4/disk"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
fbErrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
fberrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
"github.com/filebrowser/filebrowser/v2/files"
|
||||
"github.com/filebrowser/filebrowser/v2/fileutils"
|
||||
)
|
||||
@ -44,7 +44,7 @@ var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d
|
||||
|
||||
if checksum := r.URL.Query().Get("checksum"); checksum != "" {
|
||||
err := file.Checksum(checksum)
|
||||
if errors.Is(err, fbErrors.ErrInvalidOption) {
|
||||
if errors.Is(err, fberrors.ErrInvalidOption) {
|
||||
return http.StatusBadRequest, nil
|
||||
} else if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
@ -238,7 +238,7 @@ func checkParent(src, dst string) error {
|
||||
|
||||
rel = filepath.ToSlash(rel)
|
||||
if !strings.HasPrefix(rel, "../") && rel != ".." && rel != "." {
|
||||
return fbErrors.ErrSourceIsParent
|
||||
return fberrors.ErrSourceIsParent
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -304,13 +304,13 @@ func patchAction(ctx context.Context, action, src, dst string, d *data, fileCach
|
||||
switch action {
|
||||
case "copy":
|
||||
if !d.user.Perm.Create {
|
||||
return fbErrors.ErrPermissionDenied
|
||||
return fberrors.ErrPermissionDenied
|
||||
}
|
||||
|
||||
return fileutils.Copy(d.user.Fs, src, dst, d.settings.FileMode, d.settings.DirMode)
|
||||
case "rename":
|
||||
if !d.user.Perm.Rename {
|
||||
return fbErrors.ErrPermissionDenied
|
||||
return fberrors.ErrPermissionDenied
|
||||
}
|
||||
src = path.Clean("/" + src)
|
||||
dst = path.Clean("/" + dst)
|
||||
@ -335,7 +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)
|
||||
default:
|
||||
return fmt.Errorf("unsupported action %s: %w", action, fbErrors.ErrInvalidRequestParams)
|
||||
return fmt.Errorf("unsupported action %s: %w", action, fberrors.ErrInvalidRequestParams)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user