Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
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/
|
||||
|
||||
66
CHANGELOG.md
66
CHANGELOG.md
@@ -2,6 +2,72 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
|
||||
|
||||
## [2.51.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)
|
||||
|
||||
|
||||
|
||||
@@ -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 && \
|
||||
|
||||
@@ -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")
|
||||
@@ -103,7 +104,7 @@ func getProxyAuth(flags *pflag.FlagSet, defaultAuther map[string]interface{}) (a
|
||||
}
|
||||
|
||||
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, " "))
|
||||
@@ -328,6 +330,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 +361,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
|
||||
}
|
||||
@@ -54,12 +54,12 @@ The path must be for a json or yaml file.`,
|
||||
}
|
||||
|
||||
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 +98,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
|
||||
|
||||
70
cmd/root.go
70
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
|
||||
@@ -146,23 +146,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 +170,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 +192,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 +221,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 +262,7 @@ user created with the credentials from options "username" and "password".`,
|
||||
log.Println("Graceful shutdown complete.")
|
||||
|
||||
return nil
|
||||
}, pythonConfig{allowsNoDatabase: true}),
|
||||
}, storeOptions{allowsNoDatabase: true}),
|
||||
}
|
||||
|
||||
func getServerSettings(v *viper.Viper, st *storage.Storage) (*settings.Server, error) {
|
||||
@@ -309,6 +309,10 @@ func getServerSettings(v *viper.Viper, st *storage.Storage) (*settings.Server, e
|
||||
|
||||
if v.IsSet("baseURL") {
|
||||
server.BaseURL = v.GetString("baseURL")
|
||||
// TODO(remove): remove after July 2026.
|
||||
} else if v := os.Getenv("FB_BASEURL"); v != "" {
|
||||
log.Println("DEPRECATION NOTICE: Environment variable FB_BASEURL has been deprecated, use FB_BASE_URL instead")
|
||||
server.BaseURL = v
|
||||
}
|
||||
|
||||
if v.IsSet("tokenExpirationTime") {
|
||||
@@ -368,7 +372,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{
|
||||
@@ -382,7 +386,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,
|
||||
@@ -406,44 +410,44 @@ func quickSetup(d pythonData) error {
|
||||
}
|
||||
|
||||
var err error
|
||||
if d.viper.GetBool("noauth") {
|
||||
if v.GetBool("noauth") {
|
||||
set.AuthMethod = auth.MethodNoAuth
|
||||
err = d.store.Auth.Save(&auth.NoAuth{})
|
||||
err = s.Auth.Save(&auth.NoAuth{})
|
||||
} else {
|
||||
set.AuthMethod = auth.MethodJSONAuth
|
||||
err = d.store.Auth.Save(&auth.JSONAuth{})
|
||||
err = s.Auth.Save(&auth.JSONAuth{})
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = d.store.Settings.Save(set)
|
||||
err = s.Settings.Save(set)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ser := &settings.Server{
|
||||
BaseURL: d.viper.GetString("baseURL"),
|
||||
Port: d.viper.GetString("port"),
|
||||
Log: d.viper.GetString("log"),
|
||||
TLSKey: d.viper.GetString("key"),
|
||||
TLSCert: d.viper.GetString("cert"),
|
||||
Address: d.viper.GetString("address"),
|
||||
Root: d.viper.GetString("root"),
|
||||
TokenExpirationTime: d.viper.GetString("tokenExpirationTime"),
|
||||
EnableThumbnails: !d.viper.GetBool("disableThumbnails"),
|
||||
ResizePreview: !d.viper.GetBool("disablePreviewResize"),
|
||||
EnableExec: !d.viper.GetBool("disableExec"),
|
||||
TypeDetectionByHeader: !d.viper.GetBool("disableTypeDetectionByHeader"),
|
||||
BaseURL: v.GetString("baseURL"),
|
||||
Port: v.GetString("port"),
|
||||
Log: v.GetString("log"),
|
||||
TLSKey: v.GetString("key"),
|
||||
TLSCert: v.GetString("cert"),
|
||||
Address: v.GetString("address"),
|
||||
Root: v.GetString("root"),
|
||||
TokenExpirationTime: v.GetString("tokenExpirationTime"),
|
||||
EnableThumbnails: !v.GetBool("disableThumbnails"),
|
||||
ResizePreview: !v.GetBool("disablePreviewResize"),
|
||||
EnableExec: !v.GetBool("disableExec"),
|
||||
TypeDetectionByHeader: !v.GetBool("disableTypeDetectionByHeader"),
|
||||
}
|
||||
|
||||
err = d.store.Settings.SaveServer(ser)
|
||||
err = s.Settings.SaveServer(ser)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
username := d.viper.GetString("username")
|
||||
password := d.viper.GetString("password")
|
||||
username := v.GetString("username")
|
||||
password := v.GetString("password")
|
||||
|
||||
if password == "" {
|
||||
var pwd string
|
||||
@@ -474,5 +478,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"
|
||||
)
|
||||
|
||||
@@ -168,7 +168,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 +193,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)
|
||||
|
||||
@@ -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.24.0+sha512.01ff8ae71b4419903b65c60fb2dc9d34cf8bb6e06d03bde112ef38f7a34d6904c424ba66bea5cdcf12890230bf39f9580473140ed9c946fef328b6e5238a345a"
|
||||
}
|
||||
|
||||
918
frontend/pnpm-lock.yaml
generated
918
frontend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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,8 @@
|
||||
"upload": "رفع",
|
||||
"openFile": "فتح الملف",
|
||||
"discardChanges": "إلغاء التغييرات",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "تحميل الملف",
|
||||
@@ -75,7 +76,9 @@
|
||||
"sortByLastModified": "الترتيب بآخر تعديل",
|
||||
"sortByName": "الترتيب باﻹسم",
|
||||
"sortBySize": "الترتيب بالحجم",
|
||||
"noPreview": "لا يوجد عرض مسبق لهذا الملف."
|
||||
"noPreview": "لا يوجد عرض مسبق لهذا الملف.",
|
||||
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
|
||||
"csvLoadFailed": "Failed to load CSV file."
|
||||
},
|
||||
"help": {
|
||||
"click": "حدد الملف أو المجلد",
|
||||
@@ -102,6 +105,7 @@
|
||||
"username": "إسم المستخدم",
|
||||
"usernameTaken": "إسم المستخدم غير متاح",
|
||||
"wrongCredentials": "بيانات دخول خاطئة",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "دقائق",
|
||||
"seconds": "ثواني",
|
||||
"unit": "وحدة الوقت"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
281
frontend/src/i18n/bg.json
Normal file
281
frontend/src/i18n/bg.json
Normal file
@@ -0,0 +1,281 @@
|
||||
{
|
||||
"buttons": {
|
||||
"cancel": "Отмени",
|
||||
"clear": "Изчисти",
|
||||
"close": "Затвори",
|
||||
"continue": "Продължи",
|
||||
"copy": "Копирай",
|
||||
"copyFile": "Копирай файл",
|
||||
"copyToClipboard": "Копирай в клипборда",
|
||||
"copyDownloadLinkToClipboard": "Копирай линк за сваляне в клипборда",
|
||||
"create": "Създай",
|
||||
"delete": "Изтрий",
|
||||
"download": "Свали",
|
||||
"file": "Файл",
|
||||
"folder": "Папка",
|
||||
"fullScreen": "Превключване на цял екран",
|
||||
"hideDotfiles": "Скрий файлове започващи с точка",
|
||||
"info": "Информация",
|
||||
"more": "Повече",
|
||||
"move": "Премести",
|
||||
"moveFile": "Премести файл",
|
||||
"new": "Нов",
|
||||
"next": "Следващ",
|
||||
"ok": "Потвърди",
|
||||
"permalink": "Вземи постоянен линк",
|
||||
"previous": "Предишен",
|
||||
"preview": "Преглед",
|
||||
"publish": "Публикуване",
|
||||
"rename": "Преименуване",
|
||||
"replace": "Замяна",
|
||||
"reportIssue": "Докладвай проблем",
|
||||
"save": "Запис",
|
||||
"schedule": "График",
|
||||
"search": "Търсене",
|
||||
"select": "Избери",
|
||||
"selectMultiple": "Избери няколко",
|
||||
"share": "Сподели",
|
||||
"shell": "Превключване на терминала",
|
||||
"submit": "Изпрати",
|
||||
"switchView": "Смени изгледа",
|
||||
"toggleSidebar": "Превключване на страничен панел",
|
||||
"update": "Обнови",
|
||||
"upload": "Качи",
|
||||
"openFile": "Отвори файл",
|
||||
"discardChanges": "Изчисти",
|
||||
"saveChanges": "Запиши промените",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Свали файл",
|
||||
"downloadFolder": "Свали папка",
|
||||
"downloadSelected": "Свали избраното"
|
||||
},
|
||||
"upload": {
|
||||
"abortUpload": "Сигурни ли сте, че искате да прекратите?"
|
||||
},
|
||||
"errors": {
|
||||
"forbidden": "Нямате право на достъп.",
|
||||
"internal": "Взникна грешка.",
|
||||
"notFound": "Локацията не може да бъде достигната.",
|
||||
"connection": "Сървъра не може да бъде достигнат."
|
||||
},
|
||||
"files": {
|
||||
"body": "Тяло",
|
||||
"closePreview": "Затвори прегледа",
|
||||
"files": "Файлове",
|
||||
"folders": "Папки",
|
||||
"home": "Начало",
|
||||
"lastModified": "Последна промяна",
|
||||
"loading": "Зареждане ...",
|
||||
"lonely": "Тук е самотно ...",
|
||||
"metadata": "Метаданни",
|
||||
"multipleSelectionEnabled": "Множествения избор е разрешен",
|
||||
"name": "Име",
|
||||
"size": "Размер",
|
||||
"sortByLastModified": "Подредба по последна промяна",
|
||||
"sortByName": "Подредба по име",
|
||||
"sortBySize": "Подредба по размер",
|
||||
"noPreview": "За този файл не е наличен преглед.",
|
||||
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
|
||||
"csvLoadFailed": "Failed to load CSV file."
|
||||
},
|
||||
"help": {
|
||||
"click": "избери файл или директория",
|
||||
"ctrl": {
|
||||
"click": "избери файлове или директории",
|
||||
"f": "отваря търсене",
|
||||
"s": "запиши файл или свари директория тук"
|
||||
},
|
||||
"del": "изтрий избраните",
|
||||
"doubleClick": "отвори файл или директория",
|
||||
"esc": "изтрий избраното и/или затвори",
|
||||
"f1": "тази информация",
|
||||
"f2": "преименувай файл",
|
||||
"help": "Помощ"
|
||||
},
|
||||
"login": {
|
||||
"createAnAccount": "Създай акаунт",
|
||||
"loginInstead": "Вече имаш акаунт",
|
||||
"password": "Парола",
|
||||
"passwordConfirm": "Парола Потвърждение",
|
||||
"passwordsDontMatch": "Паролите не съвпадат",
|
||||
"signup": "Абониране",
|
||||
"submit": "Вход",
|
||||
"username": "Потребителско име",
|
||||
"usernameTaken": "Потребителското име вече се използва",
|
||||
"wrongCredentials": "Грешни потребителско име и/или парола",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "Бяхте разлогнати поради неактивност"
|
||||
}
|
||||
},
|
||||
"permanent": "Постоянен",
|
||||
"prompts": {
|
||||
"copy": "Копирай",
|
||||
"copyMessage": "Избери къде да копираш файловете си:",
|
||||
"currentlyNavigating": "В момента навигира към:",
|
||||
"deleteMessageMultiple": "Сигурни ли сте, че искате да изтриете {count} файл(а)?",
|
||||
"deleteMessageSingle": "Сигурни ли сте, че искате да изтриете този файл/папка?",
|
||||
"deleteMessageShare": "Сигурни ли сте, че искате на изтриете това споделяне({path})?",
|
||||
"deleteUser": "Сигурни ли сте, че искате да изтриете този потребител?",
|
||||
"deleteTitle": "Изтрий файлове",
|
||||
"displayName": "Име за показване:",
|
||||
"download": "Свали файлове",
|
||||
"downloadMessage": "Изберете формата, в който искате да свалите.",
|
||||
"error": "Възникна грешка",
|
||||
"fileInfo": "Информация за файла",
|
||||
"filesSelected": "Избрани са {count} файла.",
|
||||
"lastModified": "Последна промяна",
|
||||
"move": "Премести",
|
||||
"moveMessage": "Избери ново място за вашите файл(ове)/папк(а/и):",
|
||||
"newArchetype": "Създай нова публикация базирана на шаблон. Вашия файл ще бъде създаден в папката за съдържание.",
|
||||
"newDir": "Нова директория",
|
||||
"newDirMessage": "Именувайте вашата нова директория.",
|
||||
"newFile": "Нов файл",
|
||||
"newFileMessage": "Именувайте вашия нов файл.",
|
||||
"numberDirs": "Брой на директорийте",
|
||||
"numberFiles": "Брой на файловете",
|
||||
"rename": "Преименувай",
|
||||
"renameMessage": "Вмъкни ново име за",
|
||||
"replace": "Замени",
|
||||
"replaceMessage": "Файл, които се опитвате да качите има конфликтно име. Искате ли да го пропуснете и да продължите качването или да замените съществуващия файл?\n",
|
||||
"schedule": "График",
|
||||
"scheduleMessage": "Изберете дата и час за публикуване на тази публикация.",
|
||||
"show": "Покажи",
|
||||
"size": "Размер",
|
||||
"upload": "Качване",
|
||||
"uploadFiles": "Качване на {files} файла...",
|
||||
"uploadMessage": "Изберете опция за качване.",
|
||||
"optionalPassword": "Опционална парола",
|
||||
"resolution": "Резолюция",
|
||||
"discardEditorChanges": "Сигурни ли сте, че искате да откажете направените промени?"
|
||||
},
|
||||
"search": {
|
||||
"images": "Изображения",
|
||||
"music": "Музика",
|
||||
"pdf": "PDF",
|
||||
"pressToSearch": "За търсене, натиснете Enter ...",
|
||||
"search": "Търсене ...",
|
||||
"typeToSearch": "Пишете за търсене ...",
|
||||
"types": "Типове",
|
||||
"video": "Видео"
|
||||
},
|
||||
"settings": {
|
||||
"aceEditorTheme": "Тема на \"Ace редактор\"",
|
||||
"admin": "Админ",
|
||||
"administrator": "Администратор",
|
||||
"allowCommands": "Изпълни команди",
|
||||
"allowEdit": "Редактира, преименува и изтрива файлове и директории",
|
||||
"allowNew": "Създава нови файлове и директорий",
|
||||
"allowPublish": "Публикува нови публикации и страници",
|
||||
"allowSignup": "Разреши потребителите да се абонират",
|
||||
"hideLoginButton": "Скрий логин бутона от публичните страници",
|
||||
"avoidChanges": "(остави празно за да избегнеш промени)",
|
||||
"branding": "Брандиране",
|
||||
"brandingDirectoryPath": "Брандиране - път до директория",
|
||||
"brandingHelp": "Можете да настроите как изглежда вашия File Browser, като промените името и логото му, да добавите стилове и дори да забраните външни линкове към GitHub.\nЗа повече информация за бандиране се обърнете към {0}",
|
||||
"changePassword": "Промени парола",
|
||||
"commandRunner": "Изпълнение на команди",
|
||||
"commandRunnerHelp": "Тук можете да зададете команди, които да се изпълнят при определени събития. Пишете по една команда на ред. Системните променливите {0} и {1} ще са на разположение, като {0} е релативна на {1}. За повече информация относно тази възможност и наличните системни променливи, моля прочетете {2}.",
|
||||
"commandsUpdated": "Командите са запаметени!",
|
||||
"createUserDir": "Създай автоматично собствена директория на потребителя, когато го добавяш.",
|
||||
"minimumPasswordLength": "Минимална дължина на паролата",
|
||||
"tusUploads": "Качване на части",
|
||||
"tusUploadsHelp": "File Browser поддържа качване на части, което позволява съзадавнето на ефективно, надеждно, и възобновяемо качване на части дори и при ненадеждна мрежа.",
|
||||
"tusUploadsChunkSize": "Максимален размер на заявката (за малки качвания ще бъдат използвано директо качване). Можете да въведете цяло число, което означава размера на данните в байтове, или пък текст от вида на 10MB, 1GB и т.н..",
|
||||
"tusUploadsRetryCount": "Брой повторения, когато част от файл не се качи успешно.",
|
||||
"userHomeBasePath": "Основен път до личните директории на потребителите",
|
||||
"userScopeGenerationPlaceholder": "Обхватът ще бъде автоматично генериран",
|
||||
"createUserHomeDirectory": "Създай лична директория на потребителя",
|
||||
"customStylesheet": "Потребителски Стилове",
|
||||
"defaultUserDescription": "Настройки по подразбиране за нови потребители.",
|
||||
"disableExternalLinks": "Забрани външните връзки (с изключение на документацията)",
|
||||
"disableUsedDiskPercentage": "Забрани графиката за използване на диска",
|
||||
"documentation": "документация",
|
||||
"examples": "Примери",
|
||||
"executeOnShell": "Изпълни в шела",
|
||||
"executeOnShellDescription": "По подразбиране, File Browser изпълнява командите директно. Ако искате да ги изпълните в сесия (като Bash или PowerShell), можете да я дефинирате тук заедно с необходимите аргументи и флагове. Ако това е зададено, командата която изпълните ще бъде добавена като аргумент. Това се отнася както за потребителски команди, така и за обработка на събития.",
|
||||
"globalRules": "Това е общия списък от правила за разрешения или забрани. Те са приложими за всеки потребител. Можете да дефинирате специфични правила в настройките на всеки потребител, които ще имат приоритет над общите.",
|
||||
"globalSettings": "Глобални Настройки",
|
||||
"hideDotfiles": "Скрий фаловете започващи с точка",
|
||||
"insertPath": "Вмъкни пътя",
|
||||
"insertRegex": "Вмъкни регулярен израз",
|
||||
"instanceName": "Име на инстанцията",
|
||||
"language": "Език",
|
||||
"lockPassword": "Забрани на потребителя да променя паролата",
|
||||
"newPassword": "Вашата нова парола",
|
||||
"newPasswordConfirm": "Потвърди вашата нова парола",
|
||||
"newUser": "Нов потребител",
|
||||
"password": "Парола",
|
||||
"passwordUpdated": "Паролата е променена!",
|
||||
"path": "Път",
|
||||
"perm": {
|
||||
"create": "Създаване на файлове и директорий",
|
||||
"delete": "Изтриване на файлове и директорий",
|
||||
"download": "Сваляне",
|
||||
"execute": "Изпълни команди",
|
||||
"modify": "Редактирай файлове",
|
||||
"rename": "Преименувай или премести файлове и директорий",
|
||||
"share": "Сподели файлове"
|
||||
},
|
||||
"permissions": "Разрешения",
|
||||
"permissionsHelp": "Можете да зададете потребител да бъде администратор или да изберете разрешения индивидуално. Ако изберете \"Администратор\" всички други опции ще бъдат автоматично отметнати. Управлението на потребителите е привилегия на администратор.\n",
|
||||
"profileSettings": "Настройки на Профила",
|
||||
"ruleExample1": "предотвратете достъпа до всеки файл започващ с точка (като .git, .gitignore) във всяка папка.\n",
|
||||
"ruleExample2": "блокира достъпа до файл именуван Caddyfile поставен в началото за обхвата.",
|
||||
"rules": "Правила",
|
||||
"rulesHelp": "Тук можете да дефинирате списък от правила за разрешаване и забрана за точно този потребител. Блокираните файлове няма да се покажат в списъците и няма да са достъпни за потребителя. Поддържаме регулярни изрази и пътища релативни на обхвата.\n",
|
||||
"scope": "Обхват",
|
||||
"setDateFormat": "Задайте точен формат на дата",
|
||||
"settingsUpdated": "Настройките са обновени!",
|
||||
"shareDuration": "Продължителност на споделянето",
|
||||
"shareManagement": "Управление на споделянето",
|
||||
"shareDeleted": "Споделянето е премахнато!",
|
||||
"singleClick": "Използвайте единичен клик за да отворите файлове или директорий",
|
||||
"themes": {
|
||||
"default": "Настройки по подразбиране",
|
||||
"dark": "Тъмна",
|
||||
"light": "Светла",
|
||||
"title": "Тема"
|
||||
},
|
||||
"user": "Потребител",
|
||||
"userCommands": "Команди",
|
||||
"userCommandsHelp": "Списък разделен с интервал от наличните команди за този потребител.\n",
|
||||
"userCreated": "Потребителя е създаден!",
|
||||
"userDefaults": "Настройки по подразбиране на потребителя",
|
||||
"userDeleted": "Потребителя е изтрит!",
|
||||
"userManagement": "Управление на потребители",
|
||||
"userUpdated": "Потребителя е обновен!",
|
||||
"username": "Потребителско име",
|
||||
"users": "Потребители"
|
||||
},
|
||||
"sidebar": {
|
||||
"help": "Помощ",
|
||||
"hugoNew": "Hugo New",
|
||||
"login": "Вход",
|
||||
"logout": "Изход",
|
||||
"myFiles": "Моите файлове",
|
||||
"newFile": "Нов файл",
|
||||
"newFolder": "Нова папка",
|
||||
"preview": "Преглед",
|
||||
"settings": "Настройки",
|
||||
"signup": "Абониране",
|
||||
"siteSettings": "Настройки на сървъра"
|
||||
},
|
||||
"success": {
|
||||
"linkCopied": "Връзката е копирана!"
|
||||
},
|
||||
"time": {
|
||||
"days": "Дни",
|
||||
"hours": "Часове",
|
||||
"minutes": "Минути",
|
||||
"seconds": "Секунди",
|
||||
"unit": "Единица за време"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Pujar",
|
||||
"openFile": "Obrir fitxer",
|
||||
"discardChanges": "Descartar",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Descarregar fitxer",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "seleccionar fitxer o carpeta",
|
||||
@@ -102,6 +105,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."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Minuts",
|
||||
"seconds": "Segons",
|
||||
"unit": "Unitat"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Nahrát",
|
||||
"openFile": "Otevřít soubor",
|
||||
"discardChanges": "Zrušit změny",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Stáhnout soubor",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "vyberte soubor nebo adresář",
|
||||
@@ -102,6 +105,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."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Minuty",
|
||||
"seconds": "Sekundy",
|
||||
"unit": "Časová jednotka"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Upload",
|
||||
"openFile": "Datei öffnen",
|
||||
"discardChanges": "Verwerfen",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Änderungen speichern",
|
||||
"editAsText": "Als Text bearbeiten"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Download Datei",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "Wähle Datei oder Ordner",
|
||||
@@ -102,8 +105,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 +162,7 @@
|
||||
"video": "Video"
|
||||
},
|
||||
"settings": {
|
||||
"aceEditorTheme": "Ace editor theme",
|
||||
"aceEditorTheme": "Ace Editor Theme",
|
||||
"admin": "Admin",
|
||||
"administrator": "Administrator",
|
||||
"allowCommands": "Befehle ausführen",
|
||||
@@ -166,7 +170,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 +180,7 @@
|
||||
"commandRunnerHelp": "Hier könne Sie Befehle eintragen, welche bei den benannten Aktionen ausgeführt werden. Sie müssen pro Zeile jeweils einen Befehl eingeben. Die Umgebungsvariable {0} und {1} sind verfügbar, wobei {0} relative zu {1} ist. Für mehr Informationen über diese Funktion und die verfügbaren Umgebungsvariablen lesen Sie bitte die {2}.",
|
||||
"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",
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Minuten",
|
||||
"seconds": "Sekunden",
|
||||
"unit": "Zeiteinheit"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Μεταφόρτωση",
|
||||
"openFile": "Άνοιγμα αρχείου",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Λήψη αρχείου",
|
||||
@@ -75,7 +76,9 @@
|
||||
"sortByLastModified": "Ταξινόμηση κατά πρόσφατη τροποποίηση",
|
||||
"sortByName": "Ταξινόμηση κατά όνομα",
|
||||
"sortBySize": "Ταξινόμηση κατά μέγεθος",
|
||||
"noPreview": "Η προεπισκόπηση δεν είναι διαθέσιμη για αυτό το αρχείο."
|
||||
"noPreview": "Η προεπισκόπηση δεν είναι διαθέσιμη για αυτό το αρχείο.",
|
||||
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
|
||||
"csvLoadFailed": "Failed to load CSV file."
|
||||
},
|
||||
"help": {
|
||||
"click": "επιλέξτε αρχείο ή φάκελο",
|
||||
@@ -102,6 +105,7 @@
|
||||
"username": "Όνομα χρήστη",
|
||||
"usernameTaken": "Το όνομα χρήστη χρησιμοποιείται ήδη",
|
||||
"wrongCredentials": "Λάθος όνομα ή/και κωδικός πρόσβασης",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Λεπτά",
|
||||
"seconds": "Δευτερόλεπτα",
|
||||
"unit": "Μονάδα χρόνου"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Upload",
|
||||
"openFile": "Open file",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Download File",
|
||||
@@ -75,7 +76,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 +112,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,8 @@
|
||||
"upload": "Subir",
|
||||
"openFile": "Abrir archivo",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Guardar cambios"
|
||||
"saveChanges": "Guardar cambios",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Descargar fichero",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "seleccionar archivo o carpeta",
|
||||
@@ -102,6 +105,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."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Minutos",
|
||||
"seconds": "Segundos",
|
||||
"unit": "Unidad"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "آپلود",
|
||||
"openFile": "باز کردن فایل",
|
||||
"discardChanges": "لغو کردن",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "دانلود فایل",
|
||||
@@ -75,7 +76,9 @@
|
||||
"sortByLastModified": "مرتب سازی آخرین ویرایش",
|
||||
"sortByName": "مرتب سازی نام",
|
||||
"sortBySize": "مرتب سازی اندازه",
|
||||
"noPreview": "این فایل قابل نمایش نیست"
|
||||
"noPreview": "این فایل قابل نمایش نیست",
|
||||
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
|
||||
"csvLoadFailed": "Failed to load CSV file."
|
||||
},
|
||||
"help": {
|
||||
"click": "انتخاب فایل یا پوشه",
|
||||
@@ -102,6 +105,7 @@
|
||||
"username": "نام کاربری",
|
||||
"usernameTaken": "نام کاربری تکراری",
|
||||
"wrongCredentials": "خطا در اعتبارسنجی",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "دقیقه",
|
||||
"seconds": "ثانیه",
|
||||
"unit": "واحد زمان"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Importer",
|
||||
"openFile": "Ouvrir le fichier",
|
||||
"discardChanges": "Annuler",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Télécharger le fichier",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "Sélectionner un fichier ou dossier",
|
||||
@@ -102,6 +105,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."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Minutes",
|
||||
"seconds": "Secondes",
|
||||
"unit": "Unité de temps"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "העלאה",
|
||||
"openFile": "פתח קובץ",
|
||||
"discardChanges": "זריקת השינויים",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "הורד קובץ",
|
||||
@@ -75,7 +76,9 @@
|
||||
"sortByLastModified": "מיין לפי השינוי האחרון",
|
||||
"sortByName": "מיין לפי שם",
|
||||
"sortBySize": "מיין לפי גודל",
|
||||
"noPreview": "לא זמינה תצוגה מקדימה לקובץ זה"
|
||||
"noPreview": "לא זמינה תצוגה מקדימה לקובץ זה",
|
||||
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
|
||||
"csvLoadFailed": "Failed to load CSV file."
|
||||
},
|
||||
"help": {
|
||||
"click": "בחר קובץ או תיקייה",
|
||||
@@ -102,6 +105,7 @@
|
||||
"username": "שם משתמש",
|
||||
"usernameTaken": "שם המשתמש כבר קיים",
|
||||
"wrongCredentials": "פרטי התחברות שגויים",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "דקות",
|
||||
"seconds": "שניות",
|
||||
"unit": "יחידת זמן"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Prenesi",
|
||||
"openFile": "Otvori datoteku",
|
||||
"discardChanges": "Odbaci",
|
||||
"saveChanges": "Spremi promjene"
|
||||
"saveChanges": "Spremi promjene",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Preuzmi Datoteku",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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 file is too large for preview (>5MB). Please download to view.",
|
||||
"csvLoadFailed": "Failed to load CSV file."
|
||||
},
|
||||
"help": {
|
||||
"click": "odaberi datoteku ili mapu",
|
||||
@@ -102,6 +105,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 +170,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",
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Minute",
|
||||
"seconds": "Sekunde",
|
||||
"unit": "Jedinica vremena"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Feltöltés",
|
||||
"openFile": "Fájl megnyitása",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Fájl letöltése",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "mappa vagy fájl kijelölése",
|
||||
@@ -102,6 +105,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."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Perc",
|
||||
"seconds": "Másodperc",
|
||||
"unit": "Időegység"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,8 @@
|
||||
"upload": "Hlaða upp",
|
||||
"openFile": "Open file",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Sækja skjal",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "velja skjal eða möppu",
|
||||
@@ -102,6 +105,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."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Mínútur",
|
||||
"seconds": "Sekúndur",
|
||||
"unit": "Tímastilling"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Carica",
|
||||
"openFile": "Apri file",
|
||||
"discardChanges": "Ignora",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Scarica file",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "seleziona un file o una cartella",
|
||||
@@ -102,6 +105,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."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Minuti",
|
||||
"seconds": "Secondi",
|
||||
"unit": "Unità di tempo"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "アップロード",
|
||||
"openFile": "ファイルを開く",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "ファイルのダウンロード",
|
||||
@@ -75,7 +76,9 @@
|
||||
"sortByLastModified": "更新日時で並べ替え",
|
||||
"sortByName": "名前で並べ替え",
|
||||
"sortBySize": "サイズで並べ替え",
|
||||
"noPreview": "プレビューはこのファイルでは利用できません"
|
||||
"noPreview": "プレビューはこのファイルでは利用できません",
|
||||
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
|
||||
"csvLoadFailed": "Failed to load CSV file."
|
||||
},
|
||||
"help": {
|
||||
"click": "ファイルやフォルダーを選択",
|
||||
@@ -102,6 +105,7 @@
|
||||
"username": "ユーザー名",
|
||||
"usernameTaken": "ユーザー名はすでに取得されています",
|
||||
"wrongCredentials": "ユーザー名またはパスワードが間違っています",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "分",
|
||||
"seconds": "秒",
|
||||
"unit": "時間の単位"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "업로드",
|
||||
"openFile": "파일 열기",
|
||||
"discardChanges": "변경 사항 취소",
|
||||
"saveChanges": "변경사항 저장"
|
||||
"saveChanges": "변경사항 저장",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "파일 다운로드",
|
||||
@@ -75,7 +76,9 @@
|
||||
"sortByLastModified": "수정시간순 정렬",
|
||||
"sortByName": "이름순",
|
||||
"sortBySize": "크기순",
|
||||
"noPreview": "미리 보기가 지원되지 않는 파일 유형입니다."
|
||||
"noPreview": "미리 보기가 지원되지 않는 파일 유형입니다.",
|
||||
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
|
||||
"csvLoadFailed": "Failed to load CSV file."
|
||||
},
|
||||
"help": {
|
||||
"click": "파일이나 디렉토리를 선택해주세요.",
|
||||
@@ -102,6 +105,7 @@
|
||||
"username": "사용자 이름",
|
||||
"usernameTaken": "사용자 이름이 존재합니다",
|
||||
"wrongCredentials": "사용자 이름 또는 비밀번호를 확인하십시오",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "분",
|
||||
"seconds": "초",
|
||||
"unit": "Time Unit"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Uploaden",
|
||||
"openFile": "Open file",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Bestand downloaden",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "selecteer bestand of map",
|
||||
@@ -102,6 +105,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."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Minuten",
|
||||
"seconds": "Seconden",
|
||||
"unit": "Tijdseenheid"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Last opp",
|
||||
"openFile": "Open file",
|
||||
"discardChanges": "Slett",
|
||||
"saveChanges": "Lagre Endringane "
|
||||
"saveChanges": "Lagre Endringane ",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Nedlast filen",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "velg fil eller katalog",
|
||||
@@ -102,6 +105,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"
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Minutt",
|
||||
"seconds": "Sekunder",
|
||||
"unit": "Time format"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Wyślij",
|
||||
"openFile": "Otwórz plik",
|
||||
"discardChanges": "Odrzuć",
|
||||
"saveChanges": "Zapisz zmiany"
|
||||
"saveChanges": "Zapisz zmiany",
|
||||
"editAsText": "Edytuj jako tekst"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Pobierz plik",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "zaznacz plik lub folder",
|
||||
@@ -102,6 +105,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."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Minuty",
|
||||
"seconds": "Sekundy",
|
||||
"unit": "Jednostka czasu"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Enviar",
|
||||
"openFile": "Abrir",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Baixar arquivo",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "selecionar pasta ou arquivo",
|
||||
@@ -102,6 +105,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."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Minutos",
|
||||
"seconds": "Segundos",
|
||||
"unit": "Unidades de Tempo"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Enviar",
|
||||
"openFile": "Open file",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Descarregar ficheiro",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "selecionar pasta ou ficheiro",
|
||||
@@ -102,6 +105,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."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Minutos",
|
||||
"seconds": "Segundos",
|
||||
"unit": "Unidades de tempo"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Încarcă",
|
||||
"openFile": "Open file",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Descarcă fișier",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "alege fișier sau director",
|
||||
@@ -102,6 +105,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."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Minute",
|
||||
"seconds": "Secunde",
|
||||
"unit": "Unitate de timp"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Загрузить",
|
||||
"openFile": "Открыть файл",
|
||||
"discardChanges": "Отказаться",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Скачать файл",
|
||||
@@ -75,7 +76,9 @@
|
||||
"sortByLastModified": "Сортировка по дате изменения",
|
||||
"sortByName": "Сортировка по имени",
|
||||
"sortBySize": "Сортировка по размеру",
|
||||
"noPreview": "Предварительный просмотр для этого файла недоступен."
|
||||
"noPreview": "Предварительный просмотр для этого файла недоступен.",
|
||||
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
|
||||
"csvLoadFailed": "Failed to load CSV file."
|
||||
},
|
||||
"help": {
|
||||
"click": "выбрать файл или каталог",
|
||||
@@ -102,6 +105,7 @@
|
||||
"username": "Имя пользователя",
|
||||
"usernameTaken": "Данное имя пользователя уже занято",
|
||||
"wrongCredentials": "Неверные данные",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Минуты",
|
||||
"seconds": "Секунды",
|
||||
"unit": "Единица времени"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Nahrať",
|
||||
"openFile": "Otvoriť súbor",
|
||||
"discardChanges": "Zahodiť",
|
||||
"saveChanges": "Uložiť zmeny"
|
||||
"saveChanges": "Uložiť zmeny",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Stiahnuť súbor",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "vyberie súbor alebo priečinok",
|
||||
@@ -102,6 +105,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."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Minúty",
|
||||
"seconds": "Sekundy",
|
||||
"unit": "Jednotka času"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Ladda upp",
|
||||
"openFile": "Öppna fil",
|
||||
"discardChanges": "Förkasta",
|
||||
"saveChanges": "Spara ändringar"
|
||||
"saveChanges": "Spara ändringar",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Ladda ner fil",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "välj fil eller mapp",
|
||||
@@ -102,6 +105,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."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Minuter",
|
||||
"seconds": "Sekunder",
|
||||
"unit": "Tidsenhet"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Yükle",
|
||||
"openFile": "Dosyayı aç",
|
||||
"discardChanges": "Discard",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Dosyayı indir",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "dosya veya klasör seçin",
|
||||
@@ -102,6 +105,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."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Dakika",
|
||||
"seconds": "Saniye",
|
||||
"unit": "Zaman birimi"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Вивантажити",
|
||||
"openFile": "Відкрити файл",
|
||||
"discardChanges": "Скасувати",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Завантажити файл",
|
||||
@@ -75,7 +76,9 @@
|
||||
"sortByLastModified": "Сортувати за останнім зміненням",
|
||||
"sortByName": "Сортувати за іменем",
|
||||
"sortBySize": "Сортувати за розміром",
|
||||
"noPreview": "Попередній перегляд для цього файлу недоступний."
|
||||
"noPreview": "Попередній перегляд для цього файлу недоступний.",
|
||||
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
|
||||
"csvLoadFailed": "Failed to load CSV file."
|
||||
},
|
||||
"help": {
|
||||
"click": "вибрати файл чи каталог",
|
||||
@@ -102,6 +105,7 @@
|
||||
"username": "Ім'я користувача",
|
||||
"usernameTaken": "Ім'я користувача вже використовується",
|
||||
"wrongCredentials": "Неправильне ім'я користувача або пароль",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Хвилини",
|
||||
"seconds": "Секунди",
|
||||
"unit": "Одиниця часу"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "Tải lên",
|
||||
"openFile": "Mở tệp",
|
||||
"discardChanges": "Hủy bỏ thay đổi",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Tải xuống tệp tin",
|
||||
@@ -75,7 +76,9 @@
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"click": "chọn tập tin hoặc thư mục",
|
||||
@@ -102,6 +105,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."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "Phút",
|
||||
"seconds": "Giây",
|
||||
"unit": "Đơn vị"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "上传",
|
||||
"openFile": "打开文件",
|
||||
"discardChanges": "放弃更改",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "下载文件",
|
||||
@@ -75,7 +76,9 @@
|
||||
"sortByLastModified": "按最后修改时间排序",
|
||||
"sortByName": "按名称排序",
|
||||
"sortBySize": "按大小排序",
|
||||
"noPreview": "此文件无法预览。"
|
||||
"noPreview": "此文件无法预览。",
|
||||
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
|
||||
"csvLoadFailed": "Failed to load CSV file."
|
||||
},
|
||||
"help": {
|
||||
"click": "选择文件或文件夹",
|
||||
@@ -102,6 +105,7 @@
|
||||
"username": "用户名",
|
||||
"usernameTaken": "用户名已经被使用",
|
||||
"wrongCredentials": "用户名或密码错误",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "分钟",
|
||||
"seconds": "秒",
|
||||
"unit": "时间单位"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"upload": "上傳",
|
||||
"openFile": "開啟檔案",
|
||||
"discardChanges": "放棄變更",
|
||||
"saveChanges": "Save changes"
|
||||
"saveChanges": "Save changes",
|
||||
"editAsText": "Edit as Text"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "下載檔案",
|
||||
@@ -75,7 +76,9 @@
|
||||
"sortByLastModified": "按最後修改時間排序",
|
||||
"sortByName": "按名稱排序",
|
||||
"sortBySize": "按大小排序",
|
||||
"noPreview": "此檔案無法預覽。"
|
||||
"noPreview": "此檔案無法預覽。",
|
||||
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
|
||||
"csvLoadFailed": "Failed to load CSV file."
|
||||
},
|
||||
"help": {
|
||||
"click": "選擇檔案或目錄",
|
||||
@@ -102,6 +105,7 @@
|
||||
"username": "帳號",
|
||||
"usernameTaken": "用戶名已存在",
|
||||
"wrongCredentials": "帳號或密碼錯誤",
|
||||
"passwordTooShort": "Password must be at least {min} characters",
|
||||
"logout_reasons": {
|
||||
"inactivity": "You have been logged out due to inactivity."
|
||||
}
|
||||
@@ -268,5 +272,10 @@
|
||||
"minutes": "分鐘",
|
||||
"seconds": "秒",
|
||||
"unit": "時間單位"
|
||||
},
|
||||
"available_csv_separators": {
|
||||
"comma": "Comma (,)",
|
||||
"semicolon": "Semicolon (;)",
|
||||
"both": "Both (,) and (;)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
10
go.mod
10
go.mod
@@ -17,14 +17,14 @@ require (
|
||||
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/crypto v0.45.0
|
||||
golang.org/x/image v0.33.0
|
||||
golang.org/x/text v0.31.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
@@ -44,7 +44,7 @@ require (
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
|
||||
github.com/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,7 +73,7 @@ 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/net v0.47.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
|
||||
20
go.sum
20
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=
|
||||
@@ -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.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -319,8 +319,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-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=
|
||||
|
||||
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"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
fbErrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
fberrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
"github.com/filebrowser/filebrowser/v2/share"
|
||||
)
|
||||
|
||||
@@ -38,7 +38,7 @@ var shareListHandler = withPermShare(func(w http.ResponseWriter, r *http.Request
|
||||
} else {
|
||||
s, err = d.store.Share.FindByUserID(d.user.ID)
|
||||
}
|
||||
if errors.Is(err, fbErrors.ErrNotExist) {
|
||||
if errors.Is(err, fberrors.ErrNotExist) {
|
||||
return renderJSON(w, r, []*share.Link{})
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ var shareListHandler = withPermShare(func(w http.ResponseWriter, r *http.Request
|
||||
|
||||
var shareGetsHandler = withPermShare(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
s, err := d.store.Share.Gets(r.URL.Path, d.user.ID)
|
||||
if errors.Is(err, fbErrors.ErrNotExist) {
|
||||
if errors.Is(err, fberrors.ErrNotExist) {
|
||||
return renderJSON(w, r, []*share.Link{})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -38,6 +38,7 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys
|
||||
"Signup": d.settings.Signup,
|
||||
"NoAuth": d.settings.AuthMethod == auth.MethodNoAuth,
|
||||
"AuthMethod": d.settings.AuthMethod,
|
||||
"LogoutPage": d.settings.LogoutPage,
|
||||
"LoginPage": auther.LoginPage(),
|
||||
"CSS": false,
|
||||
"ReCaptcha": false,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
fbErrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
fberrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
"github.com/filebrowser/filebrowser/v2/users"
|
||||
)
|
||||
|
||||
@@ -36,7 +36,7 @@ func getUserID(r *http.Request) (uint, error) {
|
||||
|
||||
func getUser(_ http.ResponseWriter, r *http.Request) (*modifyUserRequest, error) {
|
||||
if r.Body == nil {
|
||||
return nil, fbErrors.ErrEmptyRequest
|
||||
return nil, fberrors.ErrEmptyRequest
|
||||
}
|
||||
|
||||
req := &modifyUserRequest{}
|
||||
@@ -46,7 +46,7 @@ func getUser(_ http.ResponseWriter, r *http.Request) (*modifyUserRequest, error)
|
||||
}
|
||||
|
||||
if req.What != "user" {
|
||||
return nil, fbErrors.ErrInvalidDataType
|
||||
return nil, fberrors.ErrInvalidDataType
|
||||
}
|
||||
|
||||
return req, nil
|
||||
@@ -87,7 +87,7 @@ var usersGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *
|
||||
|
||||
var userGetHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
u, err := d.store.Users.Get(d.server.Root, d.raw.(uint))
|
||||
if errors.Is(err, fbErrors.ErrNotExist) {
|
||||
if errors.Is(err, fberrors.ErrNotExist) {
|
||||
return http.StatusNotFound, err
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ var userPostHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *
|
||||
}
|
||||
|
||||
if req.Data.Password == "" {
|
||||
return http.StatusBadRequest, fbErrors.ErrEmptyPassword
|
||||
return http.StatusBadRequest, fberrors.ErrEmptyPassword
|
||||
}
|
||||
|
||||
req.Data.Password, err = users.ValidateAndHashPwd(req.Data.Password, d.settings.MinimumPasswordLength)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package http
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user