Compare commits

..
Author SHA1 Message Date
Graham Steffaniak e0a70b9257 updated deps (#2323) 2026-04-20 16:39:05 -05:00
Graham Steffaniak 1cd9050414 updated line count api (#2320) 2026-04-19 14:28:07 -05:00
Graham Steffaniak e2b6f06ee8 fixed single file share (#2319) 2026-04-19 14:13:57 -05:00
Graham Steffaniak ab862f8b74 small bug fixes (#2318) 2026-04-19 13:36:21 -05:00
Graham Steffaniak 5ac617d7b6 Small source size change (#2315) 2026-04-18 00:07:57 -05:00
Graham Steffaniak 838a976868 updated deps (#2313) 2026-04-18 00:04:27 -05:00
Graham Steffaniak 4c3eb73923 Update password change requirements (#2312) 2026-04-17 16:10:34 -05:00
Graham Steffaniak 94852d4fd3 updated with a couple more fixes (#2311) 2026-04-17 15:32:20 -05:00
Kevin B. b00be6717b fix search shortcut and pdf previews (#2307) 2026-04-17 12:08:54 -05:00
Graham Steffaniak 693aaa65ce make tests more reliable 2026-04-16 19:31:09 -05:00
Graham Steffaniak e3b5f08dfe apply all defaults, some were missing (#2306) 2026-04-16 19:25:56 -05:00
Graham Steffaniak b5062ce2f6 fix legacy token (#2305) 2026-04-16 19:25:45 -05:00
Graham Steffaniak 7bf4078371 several bugfixes related to access, otp, password (#2304) 2026-04-16 13:28:03 -05:00
Nebula bb8336dd3f Add .devcontainer and fix 'make dev' startup (#2286) 2026-04-15 14:11:45 -05:00
Stephan Paternotte 5f5e4d7838 Update nl.json (#2293) 2026-04-14 19:23:54 -05:00
Graham Steffaniak 24ca3094ed updated with pwa fixes 2026-04-14 19:21:26 -05:00
Kevin B. 9232a77eaa fix replace-rename prompt that wasn't working in some cases (#2285) 2026-04-14 11:11:13 -05:00
Kevin B. e26ecc10ff Fix copy to clipboard and simplify code (#2281) 2026-04-13 16:43:46 -05:00
Gaurav Kalal d2165ec0c3 Fix: Prevent portrait mobile overflow for large placeholder loader (#2272) 2026-04-13 09:59:10 -05:00
78c554719f Beta/v1.3.4 (#2273)
Co-authored-by: Kevin B. <kurami32_kbm@proton.me>
Co-authored-by: Stephan Paternotte <Stephan-P@users.noreply.github.com>
Co-authored-by: SIDDHU SINGH <siddhu3116@gmail.com>
Co-authored-by: Taufik Hidayat <taufikdev88@gmail.com>
2026-04-11 19:48:26 -05:00
Taufik Hidayat aa8f9e2923 Add resolve nested groupAdmin to oidc auth method (#2260) 2026-04-10 15:39:49 -05:00
Graham SteffaniakandKevin B. 3bfd9d9be0 Beta/v1.3.3 (#2234)
Co-authored-by: Kevin B. <kurami32_kbm@proton.me>
2026-04-03 13:40:25 -05:00
126 changed files with 4327 additions and 2900 deletions

No files matched your search

+39
View File
@@ -0,0 +1,39 @@
{
"name": "filebrowser",
"image": "mcr.microsoft.com/devcontainers/go:1.25-bookworm",
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "20"
},
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
"containerEnv": {
"CGO_ENABLED": "1",
"FILEBROWSER_DEVMODE": "true"
},
"forwardPorts": [80],
"portsAttributes": {
"80": {
"label": "filebrowser app",
"onAutoForward": "notify"
}
},
"postCreateCommand": "make setup",
"customizations": {
"vscode": {
"extensions": [
"golang.go",
"Vue.volar",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"ms-azuretools.vscode-docker"
]
}
}
}
+2
View File
@@ -1,4 +1,6 @@
*.db
*.db.old
*.sqlite
*.bak
*.log
*.mjs
+42
View File
@@ -2,6 +2,48 @@
All notable changes to this project will be documented in this file. For commit guidelines, please refer to [Standard Version](https://github.com/conventional-changelog/standard-version).
## v1.3.6
**BugFixes**:
- file watcher not working for files and not respecting line count (#2314)
- "source" type link shows up multiple times when adding sidebar links if user has multiple sources.
- JWT ES256 results in error (#2188)
- single file shares not loading editor text content (#2122)
## v1.3.5
**Notes**:
- any password change or admin user property change for a password user requires reauthentication (#2112)
**BugFixes**:
- PWA icon fixes (#2292)
- deny-rule'd folders visible in directory listings (regression from v1.2.4-stable) (#2295)
- OTP and password requirement fixes (#2112) (#2263)
- CLI not picking up config properly
- Storage usage numbers on first load adjusted.
- Some items in userDefaults are not setting defaults for new users properly #2278
- Fix copy to clipboard and simplify code (#2281) (#2274)
- Fix search shortcut `/` and pdf previews (#2307)
- Special permissions bit not set when declared in createDirectoryPermission (#2283)
- Execute permission altered after editing sh script with FB Quantum (#2309)
- Source is red but working (#2289)
## v1.3.4
**New Features**:
- Epub placementg url anchors to bookmark a specific location on the doc.
- Add Requirement for Current Password When Changing Account Password (#2112)
- Copy file path to clipborad through right click (#2204)
**Notes**:
- user scope editing has path picker and filesystem validation
**BugFixes**:
- Swiping media files doesn't respect Playback Queue (#2243)
- Audio metadata and album thumbnails doesn't work (#2236)
- Search spinner wouldn't show up sometimes while searching
- The default landing page source should always be the first source in the sidebar, that wasn't always respected.
## v1.3.3
**New Features**:
+3 -2
View File
@@ -18,5 +18,6 @@ COPY [ "./backend/http/embed/", "./http/dist/"]
# Run tests
ENV FILEBROWSER_PLAYWRIGHT_TEST=true
RUN ./filebrowser set -u testuser1,testuser1
RUN ./filebrowser set rule -f /tests/playwright-files -p / -r user -v admin -allow -c config.yaml
RUN ./filebrowser set rule -f /tests/playwright-files -p /excluded -r user -v admin -c config.yaml
RUN ./filebrowser set rule -s access -p / -r user -v admin -allow -c config.yaml
RUN ./filebrowser set rule -s access -p /excluded -r user -v admin -c config.yaml
RUN ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
+107
View File
@@ -1,6 +1,7 @@
package files
import (
"errors"
"fmt"
"os"
"path/filepath"
@@ -11,6 +12,7 @@ import (
"time"
"github.com/gtsteffaniak/filebrowser/backend/adapters/fs/fileutils"
liberrors "github.com/gtsteffaniak/filebrowser/backend/common/errors"
"github.com/gtsteffaniak/filebrowser/backend/common/settings"
dbsql "github.com/gtsteffaniak/filebrowser/backend/database/sql"
"github.com/gtsteffaniak/filebrowser/backend/indexing"
@@ -88,6 +90,111 @@ func Test_GetRealPath(t *testing.T) {
}
}
// Regression: PUT / save must not os.Chmod to PermFile for existing files, or +x (and other mode bits) are lost (#2309).
func TestWriteFilePreservesExecutableBit(t *testing.T) {
cacheDir := t.TempDir()
originalCache := settings.Config.Server.CacheDir
settings.Config.Server.CacheDir = cacheDir
t.Cleanup(func() { settings.Config.Server.CacheDir = originalCache })
fileutils.SetFsPermissions(0o644, 0o755)
if indexing.GetIndexDB() == nil {
db, _, err := dbsql.NewIndexDB("test_writefile_exec", "OFF", 1000, 32, false)
if err != nil {
t.Fatalf("Failed to create test database: %v", err)
}
indexing.SetIndexDBForTesting(db)
}
root := t.TempDir()
scriptName := "backupscript.sh"
scriptAbs := filepath.Join(root, scriptName)
if err := os.WriteFile(scriptAbs, []byte("#!/bin/sh\necho hi\n"), 0o755); err != nil {
t.Fatal(err)
}
sourceName := "test_writefile_exec"
indexing.Initialize(&settings.Source{
Name: sourceName,
Path: root,
}, false, false)
idx := indexing.GetIndex(sourceName)
if idx == nil {
t.Fatal("Failed to get test index")
}
stopPoll := startScannerStatusPoll(t, idx, 1)
defer stopPoll()
waitForScannerReady(t, idx)
indexPath := "/" + scriptName
if err := WriteFile(sourceName, indexPath, strings.NewReader("#!/bin/sh\necho bye\n")); err != nil {
t.Fatalf("WriteFile: %v", err)
}
st, err := os.Stat(scriptAbs)
if err != nil {
t.Fatal(err)
}
if st.Mode()&0o111 == 0 {
t.Fatalf("executable bits cleared after save: mode=%v", st.Mode())
}
indexing.StopAllScanners()
time.Sleep(50 * time.Millisecond)
indexing.ClearTestIndices()
}
func TestWriteFileRejectsExistingDirectory(t *testing.T) {
cacheDir := t.TempDir()
originalCache := settings.Config.Server.CacheDir
settings.Config.Server.CacheDir = cacheDir
t.Cleanup(func() { settings.Config.Server.CacheDir = originalCache })
fileutils.SetFsPermissions(0o644, 0o755)
if indexing.GetIndexDB() == nil {
db, _, err := dbsql.NewIndexDB("test_writefile_isdir", "OFF", 1000, 32, false)
if err != nil {
t.Fatalf("Failed to create test database: %v", err)
}
indexing.SetIndexDBForTesting(db)
}
root := t.TempDir()
dirName := "folder"
if err := os.Mkdir(filepath.Join(root, dirName), 0o755); err != nil {
t.Fatal(err)
}
sourceName := "test_writefile_isdir"
indexing.Initialize(&settings.Source{
Name: sourceName,
Path: root,
}, false, false)
idx := indexing.GetIndex(sourceName)
if idx == nil {
t.Fatal("Failed to get test index")
}
stopPoll := startScannerStatusPoll(t, idx, 1)
defer stopPoll()
waitForScannerReady(t, idx)
err := WriteFile(sourceName, "/"+dirName, strings.NewReader("file content"))
if err == nil {
t.Fatal("expected error when path is an existing directory")
}
if !errors.Is(err, liberrors.ErrIsDirectory) {
t.Fatalf("expected ErrIsDirectory, got: %v", err)
}
indexing.StopAllScanners()
time.Sleep(50 * time.Millisecond)
indexing.ClearTestIndices()
}
func TestSortItems(t *testing.T) {
tests := []struct {
name string
+61 -61
View File
@@ -226,18 +226,17 @@ func fileInfoFasterImpl(opts utils.FileOptions, access *access.Storage, user *us
return response, err // Path excluded by index rules OR doesn't exist
}
// Build response
response.FileInfo = *info
response.RealPath = filepath.Join(idx.Path, indexPath)
response.Source = opts.Source
// Layer 3: FILTER CHILDREN (user access)
// Remove child items THIS user can't access
// otherwise response keeps unfiltered Folders/Files while CheckChildItemAccess only mutates info.
if info.Type == "directory" {
if err := access.CheckChildItemAccess(info, idx, user.Username); err != nil {
return response, err
}
}
// Build response
response.FileInfo = *info
response.RealPath = filepath.Join(idx.Path, indexPath)
response.Source = opts.Source
if share != nil && user.Permissions.Share && opts.ShowSharedAttr {
for i := range response.Files {
file := &response.Files[i]
@@ -272,47 +271,49 @@ func processContent(info *iteminfo.ExtendedFileInfo, idx *indexing.Index, opts u
if isFolder {
return
}
if opts.Metadata {
if isVideo {
extItem := &iteminfo.ExtendedItemInfo{
ItemInfo: info.ItemInfo,
}
err := extractVideoMetadata(context.Background(), extItem, info.RealPath, nil)
if err != nil {
logger.Debugf("failed to extract video metadata for file: "+info.RealPath, info.Name, err)
} else {
info.Metadata = extItem.Metadata
}
parentPath := filepath.Dir(info.Path)
parentInfo, exists := idx.GetMetadataInfo(parentPath, true, false)
if exists {
info.GetSubtitles(parentInfo)
}
if opts.ExtractEmbeddedSubtitles {
subtitles := ffmpeg.DetectEmbeddedSubtitles(info.RealPath, info.ModTime)
info.Subtitles = append(info.Subtitles, subtitles...)
}
return
if isVideo && !opts.Metadata {
return
}
if isAudio && !(opts.Metadata || opts.AlbumArt) {
return
}
if isVideo {
extItem := &iteminfo.ExtendedItemInfo{
ItemInfo: info.ItemInfo,
}
err := extractVideoMetadata(context.Background(), extItem, info.RealPath, nil)
if err != nil {
logger.Debugf("failed to extract video metadata for file: "+info.RealPath, info.Name, err)
} else {
info.Metadata = extItem.Metadata
}
if isAudio {
// Create an ExtendedItemInfo to hold the metadata
extItem := &iteminfo.ExtendedItemInfo{
ItemInfo: info.ItemInfo,
}
err := extractAudioMetadata(context.Background(), extItem, info.RealPath, opts.AlbumArt || opts.Content, opts.Metadata || opts.Content, nil)
if err != nil {
logger.Debugf("failed to extract audio metadata for file: "+info.RealPath, info.Name, err)
} else {
// Copy metadata to ExtendedFileInfo
info.Metadata = extItem.Metadata
info.HasPreview = extItem.Metadata != nil && len(extItem.Metadata.AlbumArt) > 0
}
return
parentPath := filepath.Dir(info.Path)
parentInfo, exists := idx.GetMetadataInfo(parentPath, true, false)
if exists {
info.GetSubtitles(parentInfo)
}
if opts.ExtractEmbeddedSubtitles {
subtitles := ffmpeg.DetectEmbeddedSubtitles(info.RealPath, info.ModTime)
info.Subtitles = append(info.Subtitles, subtitles...)
}
return
}
// Audio: run tag/album-art extraction when full metadata or preview (AlbumArt) is requested.
// Preview uses AlbumArt without Metadata so we do not load arbitrary files as text content.
if isAudio {
extItem := &iteminfo.ExtendedItemInfo{
ItemInfo: info.ItemInfo,
}
err := extractAudioMetadata(context.Background(), extItem, info.RealPath, opts.AlbumArt || opts.Content, opts.Metadata || opts.Content, nil)
if err != nil {
logger.Debugf("failed to extract audio metadata for file: "+info.RealPath, info.Name, err)
} else {
info.Metadata = extItem.Metadata
info.HasPreview = extItem.Metadata != nil && len(extItem.Metadata.AlbumArt) > 0
}
return
}
// Process text content for non-video, non-audio files
@@ -760,22 +761,21 @@ func WriteFile(source, path string, in io.Reader) error {
if err != nil {
return err
}
applyDefaultFilePerm := false
var stat os.FileInfo
// Check if the destination exists and is a directory
if stat, err = os.Stat(realPath); err == nil {
if stat.IsDir() {
// If it's a directory and we're trying to create a file, remove the directory first
err = os.RemoveAll(realPath)
if err != nil {
return fmt.Errorf("could not remove existing directory to create file: %v", err)
}
stat, err = os.Stat(realPath)
if err != nil {
if !os.IsNotExist(err) {
return fmt.Errorf("stat file: %w", err)
}
// If file exists, its permissions will be preserved (O_TRUNC doesn't change permissions)
applyDefaultFilePerm = true
} else if stat.IsDir() {
return fmt.Errorf("%w: %q", errors.ErrIsDirectory, path)
}
// Open the file for writing (create if it doesn't exist, truncate if it does)
// For new files: permissions are set to fileutils.PermFile (subject to umask, which is usually acceptable)
// For existing files: permissions are preserved automatically (O_TRUNC doesn't change them)
// For new files: permissions are set to fileutils.PermFile (subject to umask, then Chmod bypasses umask)
// For existing regular files: do not Chmod so saves preserve mode and special bits
file, err := os.OpenFile(realPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fileutils.PermFile)
if err != nil {
return err
@@ -788,11 +788,11 @@ func WriteFile(source, path string, in io.Reader) error {
return err
}
// Explicitly set file permissions to bypass umask
err = os.Chmod(realPath, fileutils.PermFile)
if err != nil {
// Handle chmod error gracefully
logger.Debugf("Could not set file permissions for %s (this may be expected in restricted environments): %v", realPath, err)
if applyDefaultFilePerm {
err = os.Chmod(realPath, fileutils.PermFile)
if err != nil {
logger.Debugf("Could not set file permissions for %s (this may be expected in restricted environments): %v", realPath, err)
}
}
// Refresh the file itself
+13 -29
View File
@@ -19,7 +19,7 @@ func MakeUserDir(fullPath string) error {
return nil
}
func MakeUserDirs(u *users.User, disableScopeChange bool) error {
func MakeUserDirs(u *users.User, createDir bool) error {
cleanedUserName := users.CleanUsername(u.Username)
if cleanedUserName == "" || cleanedUserName == "-" || cleanedUserName == "." {
return fmt.Errorf("MakeUserDirs: invalid user for home dir creation: [%s]", u.Username)
@@ -27,47 +27,31 @@ func MakeUserDirs(u *users.User, disableScopeChange bool) error {
for i, scope := range u.Scopes {
source, ok := settings.Config.Server.SourceMap[scope.Name]
if !ok {
logger.Errorf("MakeUserDirs: source not found: %s", scope.Name)
continue
return fmt.Errorf("MakeUserDirs: source not found: %s", scope.Name)
}
// create directory and append user name
if filepath.Base(scope.Scope) != cleanedUserName && !disableScopeChange {
fullPath := filepath.Join(source.Path, scope.Scope, cleanedUserName)
parentDir := filepath.Join(source.Path, scope.Scope)
fullPath := filepath.Join(source.Path, scope.Scope)
parentDir := filepath.Dir(fullPath)
if createDir {
if source.Config.CreateUserDir && filepath.Base(scope.Scope) != cleanedUserName {
scope.Scope = utils.JoinPathAsUnix(scope.Scope, cleanedUserName)
fullPath = filepath.Join(fullPath, cleanedUserName)
}
if !Exists(parentDir) {
if err := MakeUserDir(parentDir); err != nil {
logger.Errorf("MakeUserDirs: failed to create parent scope directory: %s - %v", scope.Scope, err)
continue
}
}
// Use JoinPathAsUnix to ensure scope remains in Unix format (forward slashes)
scope.Scope = utils.JoinPathAsUnix(scope.Scope, cleanedUserName)
err := MakeUserDir(fullPath)
if err != nil {
logger.Errorf("MakeUserDirs: failed to create user home dir: %s", err)
// Continue to next scope even if this one failed
}
} else if filepath.Base(scope.Scope) == cleanedUserName {
// create directory exactly as specified
fullPath := filepath.Join(source.Path, scope.Scope)
parentDir := filepath.Dir(fullPath)
if !Exists(parentDir) {
logger.Errorf("MakeUserDirs: scope folder does not exist: %s", parentDir)
continue
}
err := MakeUserDir(fullPath)
if err != nil {
logger.Errorf("create user: failed to create user home dir: %s", err)
// Continue to next scope even if this one failed
return fmt.Errorf("MakeUserDirs: failed to create user home dir: %s", err)
}
} else {
// just assigning scope to path provided, so just check that it exists
path := filepath.Join(source.Path, scope.Scope)
if !Exists(path) {
logger.Errorf("MakeUserDirs: scope folder does not exist: %s", path)
continue
if !Exists(fullPath) {
return fmt.Errorf("MakeUserDirs: scope folder does not exist: %s", fullPath)
}
}
u.Scopes[i] = scope
}
return nil
+21 -3
View File
@@ -12,9 +12,27 @@ import (
var PermFile os.FileMode
var PermDir os.FileMode
func SetFsPermissions(PermFileOctal os.FileMode, PermDirOctal os.FileMode) {
PermFile = PermFileOctal
PermDir = PermDirOctal
// SetFsPermissions sets create modes from Unix chmod(2)-style values (for example
// values produced by strconv.ParseUint(s, 8, 32)). setuid, setgid, and sticky
// bits must be converted for Go's os.FileMode; a plain cast from Unix octal is
// incorrect for those bits.
func SetFsPermissions(unixFileMode, unixDirMode uint32) {
PermFile = unixModeToFileMode(unixFileMode)
PermDir = unixModeToFileMode(unixDirMode)
}
func unixModeToFileMode(u uint32) os.FileMode {
m := os.FileMode(u & 0777)
if u&0o4000 != 0 {
m |= os.ModeSetuid
}
if u&0o2000 != 0 {
m |= os.ModeSetgid
}
if u&0o1000 != 0 {
m |= os.ModeSticky
}
return m
}
// MoveFile moves a file from src to dst.
+17 -1
View File
@@ -1,6 +1,22 @@
package fileutils
import "testing"
import (
"os"
"testing"
)
func TestUnixModeToFileMode_setgid(t *testing.T) {
m := unixModeToFileMode(0o2770)
if got := m.Perm(); got != 0o770 {
t.Fatalf("Perm() = %#o, want 0770", got)
}
if m&os.ModeSetgid == 0 {
t.Fatal("expected ModeSetgid")
}
if m&os.ModeSetuid != 0 || m&os.ModeSticky != 0 {
t.Fatalf("unexpected special bits: %#o", m)
}
}
func TestCommonPrefix(t *testing.T) {
testCases := map[string]struct {
+3 -3
View File
@@ -15,7 +15,8 @@ import (
// JSONAuth is a json implementation of an Auther.
type JSONAuth struct {
ReCaptcha *ReCaptcha `json:"recaptcha" yaml:"recaptcha"`
ReCaptcha *ReCaptcha `json:"recaptcha" yaml:"recaptcha"`
DisableOtp bool `json:"disableOtp" yaml:"disableOtp"`
}
// Auth authenticates the user via a json in content body.
@@ -58,8 +59,7 @@ func (auther JSONAuth) Auth(r *http.Request, userStore *users.Storage) (*users.U
if getErr != nil {
return nil, fmt.Errorf("unable to get user from store: %v", err)
}
// check for OTP for password
if user.TOTPSecret != "" {
if user.TOTPSecret != "" && !auther.DisableOtp {
if totpCode == "" {
return nil, errors.ErrNoTotpProvided
}
+102 -7
View File
@@ -1,7 +1,12 @@
package auth
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"net/http"
@@ -47,14 +52,12 @@ func VerifyExternalJWT(tokenString string, secret string, algorithm string, user
if expectedMethod == nil {
return nil, fmt.Errorf("unsupported signing algorithm: %s", algorithm)
}
if token.Method.Alg() != expectedMethod.Alg() {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
// Return the secret key for HMAC algorithms
// For RSA/ECDSA, this would need to be a public key
return []byte(secret), nil
return jwtVerificationKey(secret, token.Method, algorithm)
})
if err != nil {
@@ -100,10 +103,102 @@ func VerifyExternalJWT(tokenString string, secret string, algorithm string, user
return username, claimsMap, nil
}
func jwtVerificationKey(secret string, method jwt.SigningMethod, algorithm string) (interface{}, error) {
switch method.(type) {
case *jwt.SigningMethodHMAC:
return []byte(secret), nil
case *jwt.SigningMethodRSA:
key, err := parsePublicKeyFromPEM(secret)
if err != nil {
return nil, err
}
rsaKey, ok := key.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("JWT secret must contain an RSA public key in PEM format for algorithm %s", algorithm)
}
return rsaKey, nil
case *jwt.SigningMethodECDSA:
key, err := parsePublicKeyFromPEM(secret)
if err != nil {
return nil, err
}
ecKey, ok := key.(*ecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf("JWT secret must contain an ECDSA public key in PEM format for algorithm %s", algorithm)
}
if err := ecdsaCurveMatchesAlgorithm(algorithm, ecKey); err != nil {
return nil, err
}
return ecKey, nil
default:
return nil, fmt.Errorf("unsupported JWT signing method: %s", method.Alg())
}
}
func ecdsaCurveMatchesAlgorithm(algorithm string, pub *ecdsa.PublicKey) error {
var want elliptic.Curve
switch algorithm {
case "ES256":
want = elliptic.P256()
case "ES384":
want = elliptic.P384()
case "ES512":
want = elliptic.P521()
default:
return nil
}
if pub.Curve != want {
return fmt.Errorf("ECDSA public key curve does not match algorithm %s", algorithm)
}
return nil
}
func parsePublicKeyFromPEM(pemData string) (interface{}, error) {
rest := []byte(pemData)
var lastErr error
for len(rest) > 0 {
block, rem := pem.Decode(rest)
if block == nil {
break
}
rest = rem
var key interface{}
var err error
switch block.Type {
case "CERTIFICATE":
var cert *x509.Certificate
cert, err = x509.ParseCertificate(block.Bytes)
if err == nil {
key = cert.PublicKey
}
case "PUBLIC KEY":
key, err = x509.ParsePKIXPublicKey(block.Bytes)
case "RSA PUBLIC KEY":
key, err = x509.ParsePKCS1PublicKey(block.Bytes)
case "EC PUBLIC KEY":
key, err = x509.ParsePKIXPublicKey(block.Bytes)
default:
continue
}
if err != nil {
lastErr = err
continue
}
if key != nil {
return key, nil
}
}
if lastErr != nil {
return nil, fmt.Errorf("failed to parse JWT public key from PEM: %w", lastErr)
}
return nil, fmt.Errorf("no public key found in JWT secret (expected PEM: CERTIFICATE, PUBLIC KEY, RSA PUBLIC KEY, or EC PUBLIC KEY)")
}
// ExtractGroupsFromClaims extracts groups from JWT claims based on the configured groups claim field
func ExtractGroupsFromClaims(claims map[string]interface{}, groupsClaimField string) []string {
var groups []string
if groupsVal, ok := claims[groupsClaimField]; ok {
switch v := groupsVal.(type) {
case []interface{}:
@@ -129,6 +224,6 @@ func ExtractGroupsFromClaims(claims map[string]interface{}, groupsClaimField str
}
}
}
return groups
}
+142
View File
@@ -1,6 +1,14 @@
package auth_test
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"testing"
"time"
@@ -144,6 +152,140 @@ func TestVerifyExternalJWT_CustomUserIdentifier(t *testing.T) {
}
}
func TestVerifyExternalJWT_ES256_PublicKeyPEM(t *testing.T) {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
pubDER, err := x509.MarshalPKIXPublicKey(&priv.PublicKey)
if err != nil {
t.Fatalf("MarshalPKIXPublicKey: %v", err)
}
pubPEM := string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pubDER}))
username := "ec-user"
claims := jwt.MapClaims{
"sub": username,
"exp": time.Now().Add(time.Hour).Unix(),
"iat": time.Now().Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
tokenString, err := token.SignedString(priv)
if err != nil {
t.Fatalf("SignedString: %v", err)
}
got, _, err := auth.VerifyExternalJWT(tokenString, pubPEM, "ES256", "sub")
if err != nil {
t.Fatalf("VerifyExternalJWT: %v", err)
}
if got != username {
t.Errorf("username: got %q want %q", got, username)
}
}
func TestVerifyExternalJWT_RS256_PublicKeyPEM(t *testing.T) {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
pubDER, err := x509.MarshalPKIXPublicKey(&priv.PublicKey)
if err != nil {
t.Fatalf("MarshalPKIXPublicKey: %v", err)
}
pubPEM := string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pubDER}))
username := "rsa-user"
claims := jwt.MapClaims{
"sub": username,
"exp": time.Now().Add(time.Hour).Unix(),
"iat": time.Now().Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
tokenString, err := token.SignedString(priv)
if err != nil {
t.Fatalf("SignedString: %v", err)
}
got, _, err := auth.VerifyExternalJWT(tokenString, pubPEM, "RS256", "sub")
if err != nil {
t.Fatalf("VerifyExternalJWT: %v", err)
}
if got != username {
t.Errorf("username: got %q want %q", got, username)
}
}
func TestVerifyExternalJWT_ES256_WrongCurveRejected(t *testing.T) {
p256Priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
p384Priv, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
verifyDER, err := x509.MarshalPKIXPublicKey(&p384Priv.PublicKey)
if err != nil {
t.Fatalf("MarshalPKIXPublicKey: %v", err)
}
verifyPEM := string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: verifyDER}))
claims := jwt.MapClaims{
"sub": "u",
"exp": time.Now().Add(time.Hour).Unix(),
"iat": time.Now().Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
tokenString, err := token.SignedString(p256Priv)
if err != nil {
t.Fatalf("SignedString: %v", err)
}
_, _, err = auth.VerifyExternalJWT(tokenString, verifyPEM, "ES256", "sub")
if err == nil {
t.Fatal("expected error when PEM public key curve does not match algorithm")
}
}
func TestVerifyExternalJWT_CertificatePEM(t *testing.T) {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
tpl := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: "jwt-test"},
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().Add(24 * time.Hour),
}
certDER, err := x509.CreateCertificate(rand.Reader, tpl, tpl, &priv.PublicKey, priv)
if err != nil {
t.Fatalf("CreateCertificate: %v", err)
}
certPEM := string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}))
username := "cert-user"
claims := jwt.MapClaims{
"sub": username,
"exp": time.Now().Add(time.Hour).Unix(),
"iat": time.Now().Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
tokenString, err := token.SignedString(priv)
if err != nil {
t.Fatalf("SignedString: %v", err)
}
got, _, err := auth.VerifyExternalJWT(tokenString, certPEM, "ES256", "sub")
if err != nil {
t.Fatalf("VerifyExternalJWT: %v", err)
}
if got != username {
t.Errorf("username: got %q want %q", got, username)
}
}
func TestExtractGroupsFromClaims_ArrayOfStrings(t *testing.T) {
claims := map[string]interface{}{
"groups": []string{"admin", "users"},
+70 -60
View File
@@ -23,7 +23,7 @@ var (
)
// return bool to indicate if the program should continue running
func runCLI() bool {
func runCLI() (bool, bool) {
generateYaml()
@@ -53,85 +53,100 @@ func runCLI() bool {
// Show help if requested
if help {
generalUsage()
return false
return false, false
}
// Create a new FlagSet for the 'set' subcommand
setCmd := flag.NewFlagSet("set", flag.ExitOnError)
var user, dbConfig string
var asAdmin bool
var fsPath, indexPath, ruleCategory, value string
var sourceName, indexPath, ruleCategory, value string
var allow bool
setCmd.StringVar(&user, "u", "", "Comma-separated username and password: \"set -u <username>,<password>\"")
setCmd.BoolVar(&asAdmin, "a", false, "Create a user as admin user, used in combination with -u")
setCmd.StringVar(&dbConfig, "c", "config.yaml", "Path to the config file, default: config.yaml")
setCmd.StringVar(&fsPath, "f", "", "Real filesystem path (e.g. /mnt/storage) for rule command")
setCmd.StringVar(&sourceName, "s", "", "source name to apply the rule to")
setCmd.StringVar(&indexPath, "p", "", "Index path (e.g. /secret) for rule command")
setCmd.StringVar(&ruleCategory, "r", "", "Rule category: 'user', 'group', or 'all' (for deny only)")
setCmd.StringVar(&value, "v", "", "Value: username or groupname (not required if ruleCategory is 'all')")
setCmd.BoolVar(&allow, "allow", false, "Allow access (default: false, which means deny)")
// Parse subcommand flags only if a subcommand is specified
storeCfg := configPath
cliSubcmd := ""
var setKind string
if len(os.Args) > 1 {
switch os.Args[1] {
case "setup":
createConfig(configPath)
return false
case "set":
if len(os.Args) < 3 {
fmt.Printf("error: missing subcommand for 'set'. Use 'set user' or 'set rule'\n")
os.Exit(1)
}
subcommand := os.Args[2]
// Handle old syntax: "set -u user,pass -c config.yaml"
if subcommand == "-u" {
// Parse flags starting from os.Args[2:] to include the -u flag
err := setCmd.Parse(os.Args[2:])
if err != nil {
setCmd.PrintDefaults()
os.Exit(1)
}
logger.Debugf("setUser called with args: %v, config: %v, admin: %v", os.Args, dbConfig, asAdmin)
err = setUser(dbConfig, asAdmin)
if err != nil {
fmt.Printf("error: %v\n", err)
os.Exit(1)
}
return false
}
// New pattern: subcommand-based (e.g., "set user" or "set rule")
// Parse flags starting from os.Args[3:] to skip the subcommand
err := setCmd.Parse(os.Args[3:])
if err != nil {
setCmd.PrintDefaults()
os.Exit(1)
}
switch subcommand {
case "rule":
err := setRule(dbConfig, fsPath, indexPath, ruleCategory, value, allow)
if err != nil {
fmt.Printf("error: %v\n", err)
os.Exit(1)
}
return false
default:
fmt.Printf("error: unknown subcommand '%s' for 'set'. Use 'set user' or 'set rule'\n", subcommand)
os.Exit(1)
}
case "version":
fmt.Printf(`FileBrowser Quantum - A modern web-based file manager
Version : %v
Commit : %v
Release Info : https://github.com/gtsteffaniak/filebrowser/releases/tag/%v
`, version.Version, version.CommitSHA, version.Version)
return false
`, version.Version, version.CommitSHA, version.Version)
return false, false
case "setup":
cliSubcmd = "setup"
case "set":
cliSubcmd = "set"
if len(os.Args) < 3 {
fmt.Printf("error: missing subcommand for 'set'. Use 'set user' or 'set rule'\n")
os.Exit(1)
}
setKind = os.Args[2]
var err error
// Handle old syntax: "set -u user,pass -c config.yaml"
if setKind == "-u" {
err = setCmd.Parse(os.Args[2:])
} else {
// New pattern: subcommand-based (e.g., "set user" or "set rule")
err = setCmd.Parse(os.Args[3:])
}
if err != nil {
setCmd.PrintDefaults()
os.Exit(1)
}
storeCfg = dbConfig
}
}
return true
dbExists := getStore(storeCfg)
switch cliSubcmd {
case "setup":
createConfig(configPath)
return false, dbExists
case "set":
if setKind == "-u" {
logger.Debugf("setUser called with args: %v, config: %v, admin: %v", os.Args, dbConfig, asAdmin)
err := setUser(dbConfig, asAdmin)
if err != nil {
fmt.Printf("error: %v\n", err)
os.Exit(1)
}
return false, dbExists
}
switch setKind {
case "rule":
sourceInfo, ok := settings.Config.Server.NameToSource[sourceName] // backend source is path
if !ok {
fmt.Printf("error: invalid source name: %s\n", sourceName)
os.Exit(1)
}
err := setRule(dbConfig, sourceInfo.Path, indexPath, ruleCategory, value, allow)
if err != nil {
fmt.Printf("error: %v\n", err)
os.Exit(1)
}
return false, dbExists
default:
fmt.Printf("error: unknown subcommand '%s' for 'set'. Use 'set user' or 'set rule'\n", setKind)
os.Exit(1)
}
return false, dbExists
default:
return true, dbExists
}
}
func setRule(dbConfig, fsPath, indexPath, ruleCategory, value string, allow bool) error {
@@ -148,10 +163,6 @@ func setRule(dbConfig, fsPath, indexPath, ruleCategory, value string, allow bool
if ruleCategory != "all" && value == "" {
return fmt.Errorf("value is required when ruleCategory is 'user' or 'group': use -v <username|groupname>")
}
// Initialize store and settings
_ = getStore(dbConfig) // ignore bool check
// Apply the rule based on allow flag and ruleCategory
var err error
if allow {
@@ -202,7 +213,6 @@ func setUser(dbConfig string, asAdmin bool) error {
}
username := userInfo[0]
password := userInfo[1]
_ = getStore(dbConfig) // ignore bool check
user, err := store.Users.Get(username)
if err != nil {
newUser := users.User{
+5 -6
View File
@@ -54,10 +54,14 @@ commands:
}
func StartFilebrowser() {
keepGoing := runCLI()
keepGoing, dbExists := runCLI()
if !keepGoing {
return
}
database := fmt.Sprintf("Using existing database : %v", settings.Config.Server.Database)
if !dbExists {
database = fmt.Sprintf("Creating new database : %v", settings.Config.Server.Database)
}
if !settings.Config.Server.DisableUpdateCheck {
info, _ := utils.CheckForUpdates()
if info.LatestVersion != "" {
@@ -76,11 +80,6 @@ func StartFilebrowser() {
done := make(chan struct{}) // Signals server has stopped
shutdownComplete := make(chan struct{}) // Signals shutdown process is complete
dbExists := getStore(configPath)
database := fmt.Sprintf("Using existing database : %v", settings.Config.Server.Database)
if !dbExists {
database = fmt.Sprintf("Creating new database : %v", settings.Config.Server.Database)
}
// Dev mode enables development features like template hot-reloading
_, err := os.Stat("http/dist")
+1 -1
View File
@@ -19,7 +19,7 @@ var (
ErrInvalidRequestParams = errors.New("invalid request params")
ErrSourceIsParent = errors.New("source is parent")
ErrNoTotpProvided = errors.New("OTP code is required for user")
ErrNoTotpConfigured = errors.New("OTP is enforced, but user is not yet configured")
ErrNoTotpConfigured = errors.New("OTP is enforced, but user is not configured")
ErrUnauthorized = errors.New("user unauthorized")
ErrNotIndexed = errors.New("directory or item excluded from indexing")
ErrNotViewable = errors.New("directory or item is not viewable")
@@ -0,0 +1,192 @@
package settings
import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/gtsteffaniak/filebrowser/backend/database/users"
)
// TestApplyUserDefaults_copiesUserDefaultsOntoUser guards against drift between
// [UserDefaults] and [ApplyUserDefaults] (regression for #2278-style bugs).
func TestApplyUserDefaults_copiesUserDefaultsOntoUser(t *testing.T) {
saved := Config
defer func() { Config = saved }()
dark := false
dlFalse := false
Config = Settings{
Server: Server{},
UserDefaults: UserDefaults{
EditorQuickSave: true,
HideSidebarFileActions: true,
DisableQuickToggles: true,
DisableSearchOptions: true,
StickySidebar: false,
HideFilesInTree: true,
DarkMode: &dark,
Locale: "de",
ViewMode: "list",
SingleClick: true,
ShowHidden: true,
DateFormat: true,
GallerySize: 7,
ThemeColor: "var(--red)",
QuickDownload: true,
DisablePreviewExt: ".exe",
DisableViewingExt: ".bat",
DisableSettings: true,
LockPassword: true,
DisableUpdateNotifications: true,
DeleteWithoutConfirming: true,
DeleteAfterArchive: false,
DisableOfficePreviewExt: "legacy",
DisableOnlyOfficeExt: ".x",
CustomTheme: "Night",
ShowSelectMultiple: true,
DebugOffice: true,
PreferEditorForMarkdown: true,
LoginMethod: "password",
FileLoading: users.FileLoading{
MaxConcurrent: 3,
UploadChunkSize: 5,
DownloadChunkSize: 2,
},
Permissions: UserDefaultsPermissions{
Api: true,
Admin: true,
Modify: true,
Share: true,
Realtime: true,
Delete: true,
Create: true,
Download: &dlFalse,
},
Preview: UserDefaultsPreview{
DisableHideSidebar: true,
Image: boolPtr(false),
Video: boolPtr(false),
Audio: boolPtr(false),
MotionVideoPreview: boolPtr(false),
Office: boolPtr(false),
PopUp: boolPtr(false),
AutoplayMedia: boolPtr(false),
DefaultMediaPlayer: true,
Folder: boolPtr(false),
Models: boolPtr(false),
},
},
}
u := &users.User{Username: "alice"}
ApplyUserDefaults(u)
want := users.User{
DisableSettings: true,
LockPassword: true,
LoginMethod: users.LoginMethodPassword,
Permissions: users.Permissions{
Api: true,
Admin: true,
Modify: true,
Share: true,
Realtime: true,
Delete: true,
Create: true,
Download: false,
},
NonAdminEditable: users.NonAdminEditable{
EditorQuickSave: true,
HideSidebarFileActions: true,
DisableQuickToggles: true,
DisableSearchOptions: true,
StickySidebar: false,
HideFilesInTree: true,
DarkMode: false,
Locale: "de",
ViewMode: "list",
SingleClick: true,
ShowHidden: true,
DateFormat: true,
GallerySize: 7,
ThemeColor: "var(--red)",
QuickDownload: true,
DisablePreviewExt: ".exe",
DisableViewingExt: ".bat",
DisableUpdateNotifications: true,
DisableOfficePreviewExt: "legacy",
DisableOnlyOfficeExt: ".x",
CustomTheme: "Night",
ShowSelectMultiple: true,
DebugOffice: true,
DeleteWithoutConfirming: true,
DeleteAfterArchive: false,
PreferEditorForMarkdown: true,
FileLoading: users.FileLoading{
MaxConcurrent: 3,
UploadChunkSize: 5,
DownloadChunkSize: 2,
},
Preview: users.Preview{
DisableHideSidebar: true,
Image: false,
Video: false,
Audio: false,
MotionVideoPreview: false,
Office: false,
PopUp: false,
AutoplayMedia: false,
DefaultMediaPlayer: true,
Folder: false,
Models: false,
},
},
}
if diff := cmp.Diff(want.Permissions, u.Permissions); diff != "" {
t.Fatalf("Permissions mismatch (-want +got):\n%s", diff)
}
if diff := cmp.Diff(want.NonAdminEditable, u.NonAdminEditable); diff != "" {
t.Fatalf("NonAdminEditable mismatch (-want +got):\n%s", diff)
}
if u.DisableSettings != want.DisableSettings {
t.Fatalf("DisableSettings: got %v want %v", u.DisableSettings, want.DisableSettings)
}
if u.LockPassword != want.LockPassword {
t.Fatalf("LockPassword: got %v want %v", u.LockPassword, want.LockPassword)
}
if u.LoginMethod != want.LoginMethod {
t.Fatalf("LoginMethod: got %q want %q", u.LoginMethod, want.LoginMethod)
}
}
func TestApplyUserDefaults_setsLoginMethodWhenEmpty(t *testing.T) {
saved := Config
defer func() { Config = saved }()
Config = Settings{
Server: Server{},
UserDefaults: UserDefaults{LoginMethod: "jwt"},
}
u := &users.User{Username: "x"}
ApplyUserDefaults(u)
if u.LoginMethod != users.LoginMethodJwt {
t.Fatalf("LoginMethod: got %q want jwt", u.LoginMethod)
}
}
func TestApplyUserDefaults_preservesLoginMethodWhenAlreadySet(t *testing.T) {
saved := Config
defer func() { Config = saved }()
Config = Settings{
Server: Server{},
UserDefaults: UserDefaults{LoginMethod: "password"},
}
u := &users.User{Username: "x", LoginMethod: users.LoginMethodProxy}
ApplyUserDefaults(u)
if u.LoginMethod != users.LoginMethodProxy {
t.Fatalf("LoginMethod: got %q want proxy", u.LoginMethod)
}
}
+2 -2
View File
@@ -106,12 +106,12 @@ type LdapConfig struct {
}
// JwtAuthConfig configures external JWT token authentication
// Similar to Grafana's JWT auth: accepts external JWT tokens signed with a shared secret
// Similar to Grafana's JWT auth: verifies HS* tokens with a shared secret, or RS*/ES* with a PEM public key (or certificate)
// The query parameter is hardcoded to "jwt" (e.g. ?jwt=<token>)
type JwtAuthConfig struct {
AuthCommon `json:",inline"`
Header string `json:"header"` // HTTP header to look for JWT token (e.g. X-JWT-Assertion). Default is "X-JWT-Assertion"
Secret string `json:"secret"` // secret: shared secret key for verifying JWT token signatures (required)
Secret string `json:"secret"` // Shared secret key/bytes for verifying JWT token signatures (required, eg PUBLIC KEY, RSA PUBLIC KEY, EC PUBLIC KEY, or CERTIFICATE)
Algorithm string `json:"algorithm"` // JWT signing algorithm (HS256, HS384, HS512, RS256, ES256). Default is "HS256"
}
+8 -1
View File
@@ -93,7 +93,7 @@ func setupFs() {
Config.Server.Filesystem.CreateDirectoryPermission = "755"
dirPermOctal, _ = strconv.ParseUint("755", 8, 32)
}
fileutils.SetFsPermissions(os.FileMode(filePermOctal), os.FileMode(dirPermOctal))
fileutils.SetFsPermissions(uint32(filePermOctal), uint32(dirPermOctal))
// Perform mandatory cache directory speed test
testCacheDirSpeed()
@@ -817,6 +817,13 @@ func loadCustomFavicon() {
Env.FaviconPath = validatedPath
Env.FaviconIsCustom = true
logger.Infof("Using custom favicon: %s", Env.FaviconPath)
// PWA and platform PNGs are generated at server startup from this path (SVG uses a raster
// sidecar next to the .svg when present; see icons.GeneratePWAIcons).
}
// PWAIconsCacheDir is where startup icon generation writes PNGs (under the server cache dir).
func PWAIconsCacheDir() string {
return filepath.Join(Config.Server.CacheDir, "icons")
}
func loadLoginIcon() {
+55 -41
View File
@@ -65,53 +65,67 @@ func AdminPerms() users.Permissions {
}
// Apply applies the default options to a user.
// Keep this in sync with [UserDefaults]: every user-facing field there should be copied
// here (except DefaultScopes, which is wired through source config rather than this helper).
func ApplyUserDefaults(u *users.User) {
u.StickySidebar = Config.UserDefaults.StickySidebar
u.DisableSettings = Config.UserDefaults.DisableSettings
u.HideFilesInTree = Config.UserDefaults.HideFilesInTree
u.DeleteAfterArchive = Config.UserDefaults.DeleteAfterArchive
d := Config.UserDefaults
// Handle DarkMode with default - dereference pointer from config
u.DarkMode = boolValueOrDefault(Config.UserDefaults.DarkMode, true)
u.DisableSettings = d.DisableSettings
u.LockPassword = d.LockPassword
u.Locale = Config.UserDefaults.Locale
u.ViewMode = Config.UserDefaults.ViewMode
u.SingleClick = Config.UserDefaults.SingleClick
u.EditorQuickSave = d.EditorQuickSave
u.HideSidebarFileActions = d.HideSidebarFileActions
u.DisableQuickToggles = d.DisableQuickToggles
u.DisableSearchOptions = d.DisableSearchOptions
u.StickySidebar = d.StickySidebar
u.HideFilesInTree = d.HideFilesInTree
u.DarkMode = boolValueOrDefault(d.DarkMode, true)
u.Locale = d.Locale
u.ViewMode = d.ViewMode
u.SingleClick = d.SingleClick
u.ShowHidden = d.ShowHidden
u.DateFormat = d.DateFormat
u.GallerySize = d.GallerySize
u.ThemeColor = d.ThemeColor
u.QuickDownload = d.QuickDownload
u.DisablePreviewExt = d.DisablePreviewExt
u.DisableViewingExt = d.DisableViewingExt
u.DisableUpdateNotifications = d.DisableUpdateNotifications
u.DisableOfficePreviewExt = d.DisableOfficePreviewExt
u.DisableOnlyOfficeExt = d.DisableOnlyOfficeExt
u.CustomTheme = d.CustomTheme
u.ShowSelectMultiple = d.ShowSelectMultiple
u.DebugOffice = d.DebugOffice
u.DeleteWithoutConfirming = d.DeleteWithoutConfirming
u.DeleteAfterArchive = d.DeleteAfterArchive
u.PreferEditorForMarkdown = d.PreferEditorForMarkdown
u.FileLoading = d.FileLoading
// Handle Permissions - convert from pointer-based defaults to regular bools
u.Permissions.Api = Config.UserDefaults.Permissions.Api
u.Permissions.Admin = Config.UserDefaults.Permissions.Admin
u.Permissions.Modify = Config.UserDefaults.Permissions.Modify
u.Permissions.Share = Config.UserDefaults.Permissions.Share
u.Permissions.Realtime = Config.UserDefaults.Permissions.Realtime
u.Permissions.Delete = Config.UserDefaults.Permissions.Delete
u.Permissions.Create = Config.UserDefaults.Permissions.Create
u.Permissions.Download = boolValueOrDefault(Config.UserDefaults.Permissions.Download, true)
u.Permissions.Api = d.Permissions.Api
u.Permissions.Admin = d.Permissions.Admin
u.Permissions.Modify = d.Permissions.Modify
u.Permissions.Share = d.Permissions.Share
u.Permissions.Realtime = d.Permissions.Realtime
u.Permissions.Delete = d.Permissions.Delete
u.Permissions.Create = d.Permissions.Create
u.Permissions.Download = boolValueOrDefault(d.Permissions.Download, true)
// Handle Preview - convert from pointer-based defaults to regular bools
u.Preview.DisableHideSidebar = Config.UserDefaults.Preview.DisableHideSidebar
u.Preview.Image = boolValueOrDefault(Config.UserDefaults.Preview.Image, true)
u.Preview.Video = boolValueOrDefault(Config.UserDefaults.Preview.Video, true)
u.Preview.Audio = boolValueOrDefault(Config.UserDefaults.Preview.Audio, true)
u.Preview.MotionVideoPreview = boolValueOrDefault(Config.UserDefaults.Preview.MotionVideoPreview, true)
u.Preview.Office = boolValueOrDefault(Config.UserDefaults.Preview.Office, true)
u.Preview.PopUp = boolValueOrDefault(Config.UserDefaults.Preview.PopUp, true)
u.Preview.AutoplayMedia = boolValueOrDefault(Config.UserDefaults.Preview.AutoplayMedia, true)
u.Preview.DefaultMediaPlayer = Config.UserDefaults.Preview.DefaultMediaPlayer
u.Preview.Folder = boolValueOrDefault(Config.UserDefaults.Preview.Folder, true)
u.Preview.Models = boolValueOrDefault(Config.UserDefaults.Preview.Models, true)
u.Preview.DisableHideSidebar = d.Preview.DisableHideSidebar
u.Preview.Image = boolValueOrDefault(d.Preview.Image, true)
u.Preview.Video = boolValueOrDefault(d.Preview.Video, true)
u.Preview.Audio = boolValueOrDefault(d.Preview.Audio, true)
u.Preview.MotionVideoPreview = boolValueOrDefault(d.Preview.MotionVideoPreview, true)
u.Preview.Office = boolValueOrDefault(d.Preview.Office, true)
u.Preview.PopUp = boolValueOrDefault(d.Preview.PopUp, true)
u.Preview.AutoplayMedia = boolValueOrDefault(d.Preview.AutoplayMedia, true)
u.Preview.DefaultMediaPlayer = d.Preview.DefaultMediaPlayer
u.Preview.Folder = boolValueOrDefault(d.Preview.Folder, true)
u.Preview.Models = boolValueOrDefault(d.Preview.Models, true)
if u.LoginMethod == "" && d.LoginMethod != "" {
u.LoginMethod = users.LoginMethod(d.LoginMethod)
}
u.ShowHidden = Config.UserDefaults.ShowHidden
u.DateFormat = Config.UserDefaults.DateFormat
u.DisableViewingExt = Config.UserDefaults.DisableViewingExt
u.ThemeColor = Config.UserDefaults.ThemeColor
u.GallerySize = Config.UserDefaults.GallerySize
u.QuickDownload = Config.UserDefaults.QuickDownload
u.LockPassword = Config.UserDefaults.LockPassword
u.DisableOnlyOfficeExt = Config.UserDefaults.DisableOnlyOfficeExt
u.FileLoading = Config.UserDefaults.FileLoading
u.DisableOfficePreviewExt = Config.UserDefaults.DisableOfficePreviewExt
u.PreferEditorForMarkdown = Config.UserDefaults.PreferEditorForMarkdown
if len(u.Scopes) == 0 && u.Username != "anonymous" {
for _, source := range Config.Server.Sources {
if source.Config.DefaultEnabled {
-4
View File
@@ -36,10 +36,6 @@ type Environment struct {
FaviconPath string `json:"-"` // resolved favicon path (filesystem or embedded)
FaviconIsCustom bool `json:"-"` // true if favicon is from custom filesystem path
FaviconEmbeddedPath string `json:"-"` // embedded asset path for default favicon
PWAIconsDir string `json:"-"` // directory where generated PWA icons are stored
PWAIcon192 string `json:"-"` // path to 192x192 PWA icon
PWAIcon256 string `json:"-"` // path to 256x256 PWA icon
PWAIcon512 string `json:"-"` // path to 512x512 PWA icon
}
type Server struct {
+21 -10
View File
@@ -74,11 +74,6 @@ func (st usersBackend) Update(user *users.User, actorIsAdmin bool, fields ...str
return err
}
passwordUser := existingUser.LoginMethod == users.LoginMethodPassword
enforcedOtp := settings.Config.Auth.Methods.PasswordAuth.EnforcedOtp
if passwordUser && enforcedOtp && !existingUser.OtpEnabled {
return errors.ErrNoTotpConfigured
}
if user.LoginMethod == "" {
user.LoginMethod = existingUser.LoginMethod
}
@@ -86,11 +81,21 @@ func (st usersBackend) Update(user *users.User, actorIsAdmin bool, fields ...str
if err != nil {
return err
}
enforcedOtp := settings.Config.Auth.Methods.PasswordAuth.EnforcedOtp
if user.LoginMethod == "" {
user.LoginMethod = existingUser.LoginMethod
}
if user.TOTPNonce == "" {
user.TOTPNonce = existingUser.TOTPNonce
}
if user.TOTPSecret == "" {
user.TOTPSecret = existingUser.TOTPSecret
}
if user.LoginMethod == users.LoginMethodPassword && enforcedOtp && !user.OtpEnabled {
if slices.Contains(fields, "OtpEnabled") {
return errors.ErrNoTotpConfigured
}
}
if !slices.Contains(fields, "Password") {
user.Password = existingUser.Password
} else {
@@ -113,10 +118,16 @@ func (st usersBackend) Update(user *users.User, actorIsAdmin bool, fields ...str
if err != nil {
return err
}
for _, scope := range adjustedScopes {
// ensure full path exists
if scope.Scope == "" {
return fmt.Errorf("scope name is required")
}
}
user.Scopes = adjustedScopes
err = files.MakeUserDirs(user, true)
err = files.MakeUserDirs(user, false)
if err != nil {
logger.Error(err.Error())
return err
}
}
// converting scopes to map of paths intead of names (names can change)
@@ -186,7 +197,7 @@ func (st usersBackend) Save(user *users.User, changePass, disableScopeChange boo
return err
}
user.Scopes = adjustedScopes
err = files.MakeUserDirs(user, disableScopeChange)
err = files.MakeUserDirs(user, true)
if err != nil {
logger.Error(err.Error())
}
+2 -1
View File
@@ -83,7 +83,6 @@ type User struct {
LoginMethod LoginMethod `json:"loginMethod"`
OtpEnabled bool `json:"otpEnabled"` // true if TOTP is enabled, false otherwise
Version int `json:"version"`
ShowFirstLogin bool `json:"showFirstLogin"`
// legacy for migration purposes... og filebrowser has perm attribute
Perm Permissions `json:"perm,omitzero"` // deprecated: use Permissions instead
}
@@ -121,12 +120,14 @@ type NonAdminEditable struct {
DisableViewingExt string `json:"disableViewingExt"` // space separated list of file extensions to disable viewing for
CustomTheme string `json:"customTheme"` // Name of theme to use chosen from custom themes config.
ShowSelectMultiple bool `json:"showSelectMultiple"` // show select multiple files on desktop
ShowCopyPath bool `json:"showCopyPath"` // show copy path action in the context menu
DebugOffice bool `json:"debugOffice"` // debug onlyoffice editor
OtpEnabled bool `json:"otpEnabled"` // allow non-admin users to disable their own OTP
SidebarLinks []SidebarLink `json:"sidebarLinks"` // customizable sidebar links
HideFilesInTree bool `json:"hideFilesInTree"` // hide files in the sidebar tree navigation, when true, will show only directories.
DeleteAfterArchive bool `json:"deleteAfterArchive"` // delete source files after successful creation/extraction of archives
PreferEditorForMarkdown bool `json:"preferEditorForMarkdown"` // prefer editor first for markdown files instead of the Markdown Viewer
ShowFirstLogin bool `json:"showFirstLogin"`
}
type FileLoading struct {
+36 -36
View File
@@ -4,35 +4,35 @@ go 1.25.0
require (
github.com/asdine/storm/v3 v3.2.1
github.com/coreos/go-oidc/v3 v3.17.0
github.com/coreos/go-oidc/v3 v3.18.0
github.com/coreos/go-systemd/v22 v22.7.0
github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8
github.com/dsoprea/go-exif/v3 v3.0.1
github.com/gen2brain/go-fitz v1.24.15
github.com/go-ldap/ldap/v3 v3.4.13
github.com/go-playground/validator/v10 v10.30.1
github.com/go-playground/validator/v10 v10.30.2
github.com/goccy/go-yaml v1.19.2
github.com/golang-jwt/jwt/v4 v4.5.2
github.com/google/go-cmp v0.7.0
github.com/gtsteffaniak/go-cache v1.1.0
github.com/gtsteffaniak/go-logger v1.0.2
github.com/kovidgoyal/imaging v1.8.20
github.com/mattn/go-sqlite3 v1.14.37
github.com/gtsteffaniak/go-logger v1.1.0
github.com/kovidgoyal/imaging v1.8.21
github.com/mattn/go-sqlite3 v1.14.42
github.com/pquerna/otp v1.5.0
github.com/spf13/afero v1.15.0
github.com/stretchr/testify v1.11.1
github.com/swaggo/http-swagger v1.3.4
github.com/swaggo/swag v1.16.6
golang.org/x/crypto v0.49.0
golang.org/x/image v0.37.0
golang.org/x/mod v0.34.0
golang.org/x/net v0.52.0
golang.org/x/crypto v0.50.0
golang.org/x/image v0.39.0
golang.org/x/mod v0.35.0
golang.org/x/net v0.53.0
golang.org/x/oauth2 v0.36.0
golang.org/x/sys v0.42.0
golang.org/x/sys v0.43.0
golang.org/x/time v0.15.0
golang.org/x/tools v0.43.0
golang.org/x/tools v0.44.0
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.47.0
modernc.org/sqlite v1.49.1
)
require (
@@ -55,13 +55,13 @@ require (
github.com/Djarvur/go-err113 v0.1.1 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/MirrexOne/unqueryvet v1.5.3 // indirect
github.com/MirrexOne/unqueryvet v1.5.4 // indirect
github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect
github.com/air-verse/air v1.64.5 // indirect
github.com/air-verse/air v1.65.1 // indirect
github.com/alecthomas/chroma/v2 v2.23.1 // indirect
github.com/alecthomas/go-check-sumtype v0.3.1 // indirect
github.com/alexkohler/nakedret/v2 v2.0.6 // indirect
github.com/alexkohler/prealloc v1.0.2 // indirect
github.com/alexkohler/prealloc v1.1.0 // indirect
github.com/alfatraining/structtag v1.0.0 // indirect
github.com/alingse/asasalint v0.0.11 // indirect
github.com/alingse/nilnesserr v0.2.0 // indirect
@@ -103,7 +103,7 @@ require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/ebitengine/purego v0.10.0 // indirect
github.com/ettle/strcase v0.2.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fatih/color v1.19.0 // indirect
github.com/fatih/structtag v1.2.0 // indirect
github.com/firefart/nonamedreturns v1.0.6 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
@@ -113,17 +113,17 @@ require (
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
github.com/go-critic/go-critic v0.14.3 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
github.com/go-openapi/jsonpointer v0.22.5 // indirect
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
github.com/go-openapi/jsonpointer v0.23.1 // indirect
github.com/go-openapi/jsonreference v0.21.5 // indirect
github.com/go-openapi/spec v0.22.4 // indirect
github.com/go-openapi/swag/conv v0.25.5 // indirect
github.com/go-openapi/swag/jsonname v0.25.5 // indirect
github.com/go-openapi/swag/jsonutils v0.25.5 // indirect
github.com/go-openapi/swag/loading v0.25.5 // indirect
github.com/go-openapi/swag/stringutils v0.25.5 // indirect
github.com/go-openapi/swag/typeutils v0.25.5 // indirect
github.com/go-openapi/swag/yamlutils v0.25.5 // indirect
github.com/go-openapi/swag/conv v0.26.0 // indirect
github.com/go-openapi/swag/jsonname v0.26.0 // indirect
github.com/go-openapi/swag/jsonutils v0.26.0 // indirect
github.com/go-openapi/swag/loading v0.26.0 // indirect
github.com/go-openapi/swag/stringutils v0.26.0 // indirect
github.com/go-openapi/swag/typeutils v0.26.0 // indirect
github.com/go-openapi/swag/yamlutils v0.26.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-toolsmith/astcast v1.1.0 // indirect
@@ -139,13 +139,13 @@ require (
github.com/godoc-lint/godoc-lint v0.11.2 // indirect
github.com/gofrs/flock v0.13.0 // indirect
github.com/gohugoio/hugo v0.149.1 // indirect
github.com/golang/geo v0.0.0-20260302211937-87f5a40ea07a // indirect
github.com/golang/geo v0.0.0-20260415063119-550b242b3150 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golangci/asciicheck v0.5.0 // indirect
github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 // indirect
github.com/golangci/go-printf-func-name v0.1.1 // indirect
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d // indirect
github.com/golangci/golangci-lint/v2 v2.10.1 // indirect
github.com/golangci/golangci-lint/v2 v2.11.4 // indirect
github.com/golangci/golines v0.15.0 // indirect
github.com/golangci/misspell v0.8.0 // indirect
github.com/golangci/plugin-module-register v0.1.2 // indirect
@@ -171,7 +171,7 @@ require (
github.com/julz/importas v0.2.0 // indirect
github.com/jupiterrider/ffi v0.6.0 // indirect
github.com/karamaru-alpha/copyloopvar v1.2.2 // indirect
github.com/kisielk/errcheck v1.9.0 // indirect
github.com/kisielk/errcheck v1.10.0 // indirect
github.com/kkHAIKE/contextcheck v1.1.6 // indirect
github.com/kovidgoyal/go-parallel v1.1.1 // indirect
github.com/kovidgoyal/go-shm v1.0.0 // indirect
@@ -196,10 +196,10 @@ require (
github.com/maratori/testpackage v1.1.2 // indirect
github.com/matoous/godox v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-isatty v0.0.21 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mgechev/revive v1.14.0 // indirect
github.com/mgechev/revive v1.15.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect
github.com/moricho/tparallel v0.3.2 // indirect
@@ -228,15 +228,15 @@ require (
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect
github.com/ryancurrah/gomodguard v1.4.1 // indirect
github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect
github.com/ryanrolds/sqlclosecheck v0.6.0 // indirect
github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
github.com/sashamelentyev/usestdlibvars v1.29.0 // indirect
github.com/securego/gosec/v2 v2.23.0 // indirect
github.com/securego/gosec/v2 v2.24.8-0.20260309165252-619ce2117e08 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/sivchari/containedctx v1.0.3 // indirect
github.com/sonatard/noctx v0.4.0 // indirect
github.com/sonatard/noctx v0.5.1 // indirect
github.com/sourcegraph/go-diff v0.7.0 // indirect
github.com/spf13/cast v1.9.2 // indirect
github.com/spf13/cobra v1.10.2 // indirect
@@ -257,7 +257,7 @@ require (
github.com/ultraware/funlen v0.2.0 // indirect
github.com/ultraware/whitespace v0.2.0 // indirect
github.com/urfave/cli/v2 v2.3.0 // indirect
github.com/uudashr/gocognit v1.2.0 // indirect
github.com/uudashr/gocognit v1.2.1 // indirect
github.com/uudashr/iface v1.4.1 // indirect
github.com/xen0n/gosmopolitan v1.3.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
@@ -276,12 +276,12 @@ require (
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect
golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/text v0.35.0 // indirect
golang.org/x/text v0.36.0 // indirect
google.golang.org/protobuf v1.36.8 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
honnef.co/go/tools v0.7.0 // indirect
modernc.org/libc v1.70.0 // indirect
modernc.org/libc v1.72.0 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
mvdan.cc/gofumpt v0.9.2 // indirect
+82 -83
View File
@@ -75,14 +75,14 @@ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/MirrexOne/unqueryvet v1.5.3 h1:LpT3rsH+IY3cQddWF9bg4C7jsbASdGnrOSofY8IPEiw=
github.com/MirrexOne/unqueryvet v1.5.3/go.mod h1:fs9Zq6eh1LRIhsDIsxf9PONVUjYdFHdtkHIgZdJnyPU=
github.com/MirrexOne/unqueryvet v1.5.4 h1:38QOxShO7JmMWT+eCdDMbcUgGCOeJphVkzzRgyLJgsQ=
github.com/MirrexOne/unqueryvet v1.5.4/go.mod h1:fs9Zq6eh1LRIhsDIsxf9PONVUjYdFHdtkHIgZdJnyPU=
github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsuj3piCMx4=
github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo=
github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863 h1:BRrxwOZBolJN4gIwvZMJY1tzqBvQgpaZiQRuIDD40jM=
github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM=
github.com/air-verse/air v1.64.5 h1:+gs/NgTzYYe+gGPyfHy3XxpJReQWC1pIsiKIg0LgNt4=
github.com/air-verse/air v1.64.5/go.mod h1:OaJZSfZqf7wyjS2oP/CcEVyIt0JmZuPh5x1gdtklmmY=
github.com/air-verse/air v1.65.1 h1:G4hy4YemKrIcGAvYMor+86PhxdtBEq0n9ebJ4RoYy8w=
github.com/air-verse/air v1.65.1/go.mod h1:OaJZSfZqf7wyjS2oP/CcEVyIt0JmZuPh5x1gdtklmmY=
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY=
@@ -100,8 +100,8 @@ github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktp
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/alexkohler/nakedret/v2 v2.0.6 h1:ME3Qef1/KIKr3kWX3nti3hhgNxw6aqN5pZmQiFSsuzQ=
github.com/alexkohler/nakedret/v2 v2.0.6/go.mod h1:l3RKju/IzOMQHmsEvXwkqMDzHHvurNQfAgE1eVmT40Q=
github.com/alexkohler/prealloc v1.0.2 h1:MPo8cIkGkZytq7WNH9UHv3DIX1mPz1RatPXnZb0zHWQ=
github.com/alexkohler/prealloc v1.0.2/go.mod h1:fT39Jge3bQrfA7nPMDngUfvUbQGQeJyGQnR+913SCig=
github.com/alexkohler/prealloc v1.1.0 h1:cKGRBqlXw5iyQGLYhrXrDlcHxugXpTq4tQ5c91wkf8M=
github.com/alexkohler/prealloc v1.1.0/go.mod h1:fT39Jge3bQrfA7nPMDngUfvUbQGQeJyGQnR+913SCig=
github.com/alfatraining/structtag v1.0.0 h1:2qmcUqNcCoyVJ0up879K614L9PazjBSFruTB0GOFjCc=
github.com/alfatraining/structtag v1.0.0/go.mod h1:p3Xi5SwzTi+Ryj64DqjLWz7XurHxbGsq6y3ubePJPus=
github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw=
@@ -203,8 +203,8 @@ github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyM
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=
github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
github.com/coreos/go-oidc/v3 v3.18.0 h1:V9orjXynvu5wiC9SemFTWnG4F45v403aIcjWo0d41+A=
github.com/coreos/go-oidc/v3 v3.18.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4=
github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
@@ -258,8 +258,8 @@ github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q=
github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A=
github.com/evanw/esbuild v0.25.9 h1:aU7GVC4lxJGC1AyaPwySWjSIaNLAdVEEuq3chD0Khxs=
github.com/evanw/esbuild v0.25.9/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/firefart/nonamedreturns v1.0.6 h1:vmiBcKV/3EqKY3ZiPxCINmpS431OcE1S47AQUwhrg8E=
@@ -294,8 +294,8 @@ github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3Bop
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
@@ -306,41 +306,41 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA=
github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0=
github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4=
github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY=
github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=
github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw=
github.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ=
github.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g=
github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k=
github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo=
github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU=
github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo=
github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo=
github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU=
github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g=
github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M=
github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII=
github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E=
github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc=
github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ=
github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ=
github.com/go-openapi/testify/enable/yaml/v2 v2.4.0 h1:7SgOMTvJkM8yWrQlU8Jm18VeDPuAvB/xWrdxFJkoFag=
github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM=
github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM=
github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
github.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I=
github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE=
github.com/go-openapi/swag/jsonname v0.26.0 h1:gV1NFX9M8avo0YSpmWogqfQISigCmpaiNci8cGECU5w=
github.com/go-openapi/swag/jsonname v0.26.0/go.mod h1:urBBR8bZNoDYGr653ynhIx+gTeIz0ARZxHkAPktJK2M=
github.com/go-openapi/swag/jsonutils v0.26.0 h1:FawFML2iAXsPqmERscuMPIHmFsoP1tOqWkxBaKNMsnA=
github.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0 h1:apqeINu/ICHouqiRZbyFvuDge5jCmmLTqGQ9V95EaOM=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0/go.mod h1:AyM6QT8uz5IdKxk5akv0y6u4QvcL9GWERt0Jx/F/R8Y=
github.com/go-openapi/swag/loading v0.26.0 h1:Apg6zaKhCJurpJer0DCxq99qwmhFddBhaMX7kilDcko=
github.com/go-openapi/swag/loading v0.26.0/go.mod h1:dBxQ/6V2uBaAQdevN18VELE6xSpJWZxLX4txe12JwDg=
github.com/go-openapi/swag/stringutils v0.26.0 h1:qZQngLxs5s7SLijc3N2ZO+fUq2o8LjuWAASSrJuh+xg=
github.com/go-openapi/swag/stringutils v0.26.0/go.mod h1:sWn5uY+QIIspwPhvgnqJsH8xqFT2ZbYcvbcFanRyhFE=
github.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFuuyQIKYybfq4=
github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE=
github.com/go-openapi/swag/yamlutils v0.26.0 h1:H7O8l/8NJJQ/oiReEN+oMpnGMyt8G0hl460nRZxhLMQ=
github.com/go-openapi/swag/yamlutils v0.26.0/go.mod h1:1evKEGAtP37Pkwcc7EWMF0hedX0/x3Rkvei2wtG/TbU=
github.com/go-openapi/testify/enable/yaml/v2 v2.4.2 h1:5zRca5jw7lzVREKCZVNBpysDNBjj74rBh0N2BGQbSR0=
github.com/go-openapi/testify/enable/yaml/v2 v2.4.2/go.mod h1:XVevPw5hUXuV+5AkI1u1PeAm27EQVrhXTTCPAF85LmE=
github.com/go-openapi/testify/v2 v2.4.2 h1:tiByHpvE9uHrrKjOszax7ZvKB7QOgizBWGBLuq0ePx4=
github.com/go-openapi/testify/v2 v2.4.2/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
github.com/go-playground/validator/v10 v10.30.2 h1:JiFIMtSSHb2/XBUbWM4i/MpeQm9ZK2xqPNk8vgvu5JQ=
github.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@@ -401,8 +401,8 @@ github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/geo v0.0.0-20260302211937-87f5a40ea07a h1:7f/cr+n7Jtd7mzCLrx47sdVIGabpSSxCHrf6fJczDp0=
github.com/golang/geo v0.0.0-20260302211937-87f5a40ea07a/go.mod h1:Mymr9kRGDc64JPr03TSZmuIBODZ3KyswLzm1xL0HFA8=
github.com/golang/geo v0.0.0-20260415063119-550b242b3150 h1:F5CQANtNulclG+xJt370ohUujAEHq2XNBBAjkoSukQI=
github.com/golang/geo v0.0.0-20260415063119-550b242b3150/go.mod h1:Mymr9kRGDc64JPr03TSZmuIBODZ3KyswLzm1xL0HFA8=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -442,8 +442,8 @@ github.com/golangci/go-printf-func-name v0.1.1 h1:hIYTFJqAGp1iwoIfsNTpoq1xZAarog
github.com/golangci/go-printf-func-name v0.1.1/go.mod h1:Es64MpWEZbh0UBtTAICOZiB+miW53w/K9Or/4QogJss=
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d h1:viFft9sS/dxoYY0aiOTsLKO2aZQAPT4nlQCsimGcSGE=
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d/go.mod h1:ivJ9QDg0XucIkmwhzCDsqcnxxlDStoTl89jDMIoNxKY=
github.com/golangci/golangci-lint/v2 v2.10.1 h1:flhw5Px6ojbLyEFzXvJn5B2HEdkkRlkhE1SnmCbQBiE=
github.com/golangci/golangci-lint/v2 v2.10.1/go.mod h1:dBsrOk6zj0vDhlTv+IiJGqkDokR24IVTS7W3EVfPTQY=
github.com/golangci/golangci-lint/v2 v2.11.4 h1:GK+UlZBN5y7rh2PBnHA93XLSX6RaF7uhzJQ3JwU1wuA=
github.com/golangci/golangci-lint/v2 v2.11.4/go.mod h1:ODQDCASMA3VqfZYIbbQLpTRTzV7O/vjmIRF6u8NyFwI=
github.com/golangci/golines v0.15.0 h1:Qnph25g8Y1c5fdo1X7GaRDGgnMHgnxh4Gk4VfPTtRx0=
github.com/golangci/golines v0.15.0/go.mod h1:AZjXd23tbHMpowhtnGlj9KCNsysj72aeZVVHnVcZx10=
github.com/golangci/misspell v0.8.0 h1:qvxQhiE2/5z+BVRo1kwYA8yGz+lOlu5Jfvtx2b04Jbg=
@@ -506,8 +506,8 @@ github.com/gostaticanalysis/testutil v0.5.0 h1:Dq4wT1DdTwTGCQQv3rl3IvD5Ld0E6HiY+
github.com/gostaticanalysis/testutil v0.5.0/go.mod h1:OLQSbuM6zw2EvCcXTz1lVq5unyoNft372msDY0nY5Hs=
github.com/gtsteffaniak/go-cache v1.1.0 h1:ni/2cJpCxpGmaMZd92iTDIGOy+yUcMcxQuW7bSKhkCE=
github.com/gtsteffaniak/go-cache v1.1.0/go.mod h1:rHUZvf4huhMX/bnNK4/oQ3sNAE2fYC2jFpAWZiBPWac=
github.com/gtsteffaniak/go-logger v1.0.2 h1:S4AgR7yDu2geD8y2D9NyJVOBqpZRnl37r2mBi7GDNR0=
github.com/gtsteffaniak/go-logger v1.0.2/go.mod h1:rapapTju4D7bwt6h4JOdZNUXDjnp0WkTBd6TN5yWglI=
github.com/gtsteffaniak/go-logger v1.1.0 h1:baFds+iibuLLNpNLwygz32bDqdsunI6dGWcdxwMIVKI=
github.com/gtsteffaniak/go-logger v1.1.0/go.mod h1:rapapTju4D7bwt6h4JOdZNUXDjnp0WkTBd6TN5yWglI=
github.com/hairyhenderson/go-codeowners v0.7.0 h1:s0W4wF8bdsBEjTWzwzSlsatSthWtTAF2xLgo4a4RwAo=
github.com/hairyhenderson/go-codeowners v0.7.0/go.mod h1:wUlNgQ3QjqC4z8DnM5nnCYVq/icpqXJyJOukKx5U8/Q=
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 h1:CUW5RYIcysz+D3B+l1mDeXrQ7fUvGGCwJfdASSzbrfo=
@@ -569,8 +569,8 @@ github.com/jupiterrider/ffi v0.6.0 h1:UX378KcZvH5c8qgLi9KL/bL82SZTHdRspZ+jj7bvBn
github.com/jupiterrider/ffi v0.6.0/go.mod h1:PqZ5Go6X9by8CIXgfprxfMPYmn8oT5m2O7AA56s64bY=
github.com/karamaru-alpha/copyloopvar v1.2.2 h1:yfNQvP9YaGQR7VaWLYcfZUlRP2eo2vhExWKxD/fP6q0=
github.com/karamaru-alpha/copyloopvar v1.2.2/go.mod h1:oY4rGZqZ879JkJMtX3RRkcXRkmUvH0x35ykgaKgsgJY=
github.com/kisielk/errcheck v1.9.0 h1:9xt1zI9EBfcYBvdU1nVrzMzzUPUtPKs9bVSIM3TAb3M=
github.com/kisielk/errcheck v1.9.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8=
github.com/kisielk/errcheck v1.10.0 h1:Lvs/YAHP24YKg08LA8oDw2z9fJVme090RAXd90S+rrw=
github.com/kisielk/errcheck v1.10.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkHAIKE/contextcheck v1.1.6 h1:7HIyRcnyzxL9Lz06NGhiKvenXq7Zw6Q0UQu/ttjfJCE=
github.com/kkHAIKE/contextcheck v1.1.6/go.mod h1:3dDbMRNBFaq8HFXWC1JyvDSPm43CmE6IuHam8Wr0rkg=
@@ -580,8 +580,8 @@ github.com/kovidgoyal/go-parallel v1.1.1 h1:1OzpNjtrUkBPq3UaqrnvOoB2F9RttSt811ui
github.com/kovidgoyal/go-parallel v1.1.1/go.mod h1:BJNIbe6+hxyFWv7n6oEDPj3PA5qSw5OCtf0hcVxWJiw=
github.com/kovidgoyal/go-shm v1.0.0 h1:HJEel9D1F9YhULvClEHJLawoRSj/1u/EDV7MJbBPgQo=
github.com/kovidgoyal/go-shm v1.0.0/go.mod h1:Yzb80Xf9L3kaoB2RGok9hHwMIt7Oif61kT6t3+VnZds=
github.com/kovidgoyal/imaging v1.8.20 h1:74GZ7C2rIm3rqmGEjK1GvvPOOnJ0SS5iDOa6Flfo0b0=
github.com/kovidgoyal/imaging v1.8.20/go.mod h1:d3phGYkTChGYkY4y++IjpHgUGhWGELDc2NEQAqxwZZg=
github.com/kovidgoyal/imaging v1.8.21 h1:95S2+dowTeKJJHNpf6lnScvIennTr2H0zQotu+ptNQw=
github.com/kovidgoyal/imaging v1.8.21/go.mod h1:976F+zjiQeZ7sd87Pxlm0a64S/w9bImSIWg3sSk1rdQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -640,16 +640,16 @@ github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs=
github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.37 h1:3DOZp4cXis1cUIpCfXLtmlGolNLp2VEqhiB/PARNBIg=
github.com/mattn/go-sqlite3 v1.14.37/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.42 h1:MigqEP4ZmHw3aIdIT7T+9TLa90Z6smwcthx+Azv4Cgo=
github.com/mattn/go-sqlite3 v1.14.42/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgechev/revive v1.14.0 h1:CC2Ulb3kV7JFYt+izwORoS3VT/+Plb8BvslI/l1yZsc=
github.com/mgechev/revive v1.14.0/go.mod h1:MvnujelCZBZCaoDv5B3foPo6WWgULSSFxvfxp7GsPfo=
github.com/mgechev/revive v1.15.0 h1:vJ0HzSBzfNyPbHKolgiFjHxLek9KUijhqh42yGoqZ8Q=
github.com/mgechev/revive v1.15.0/go.mod h1:LlAKO3QQe9OJ0pVZzI2GPa8CbXGZ/9lNpCGvK4T/a8A=
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
@@ -769,8 +769,8 @@ github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/ryancurrah/gomodguard v1.4.1 h1:eWC8eUMNZ/wM/PWuZBv7JxxqT5fiIKSIyTvjb7Elr+g=
github.com/ryancurrah/gomodguard v1.4.1/go.mod h1:qnMJwV1hX9m+YJseXEBhd2s90+1Xn6x9dLz11ualI1I=
github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU=
github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ=
github.com/ryanrolds/sqlclosecheck v0.6.0 h1:pEyL9okISdg1F1SEpJNlrEotkTGerv5BMk7U4AG0eVg=
github.com/ryanrolds/sqlclosecheck v0.6.0/go.mod h1:xyX16hsDaCMXHrMJ3JMzGf5OpDfHTOTTQrT7HOFUmeU=
github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0=
github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
@@ -779,8 +779,8 @@ github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tM
github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ=
github.com/sashamelentyev/usestdlibvars v1.29.0 h1:8J0MoRrw4/NAXtjQqTHrbW9NN+3iMf7Knkq057v4XOQ=
github.com/sashamelentyev/usestdlibvars v1.29.0/go.mod h1:8PpnjHMk5VdeWlVb4wCdrB8PNbLqZ3wBZTZWkrpZZL8=
github.com/securego/gosec/v2 v2.23.0 h1:h4TtF64qFzvnkqvsHC/knT7YC5fqyOCItlVR8+ptEBo=
github.com/securego/gosec/v2 v2.23.0/go.mod h1:qRHEgXLFuYUDkI2T7W7NJAmOkxVhkR0x9xyHOIcMNZ0=
github.com/securego/gosec/v2 v2.24.8-0.20260309165252-619ce2117e08 h1:AoLtJX4WUtZkhhUUMFy3GgecAALp/Mb4S1iyQOA2s0U=
github.com/securego/gosec/v2 v2.24.8-0.20260309165252-619ce2117e08/go.mod h1:+XLCJiRE95ga77XInNELh2M6zQP+PdqiT9Zpm0D9Wpk=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
@@ -793,8 +793,8 @@ github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE=
github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4=
github.com/sonatard/noctx v0.4.0 h1:7MC/5Gg4SQ4lhLYR6mvOP6mQVSxCrdyiExo7atBs27o=
github.com/sonatard/noctx v0.4.0/go.mod h1:64XdbzFb18XL4LporKXp8poqZtPKbCrqQ402CV+kJas=
github.com/sonatard/noctx v0.5.1 h1:wklWg9c9ZYugOAk7qG4yP4PBrlQsmSLPTvW1K4PRQMs=
github.com/sonatard/noctx v0.5.1/go.mod h1:64XdbzFb18XL4LporKXp8poqZtPKbCrqQ402CV+kJas=
github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0=
github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
@@ -860,8 +860,8 @@ github.com/ultraware/whitespace v0.2.0 h1:TYowo2m9Nfj1baEQBjuHzvMRbp19i+RCcRYrSW
github.com/ultraware/whitespace v0.2.0/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYRA=
github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU=
github.com/uudashr/gocognit v1.2.1 h1:CSJynt5txTnORn/DkhiB4mZjwPuifyASC8/6Q0I/QS4=
github.com/uudashr/gocognit v1.2.1/go.mod h1:acaubQc6xYlXFEMb9nWX2dYBzJ/bIjEkc1zzvyIZg5Q=
github.com/uudashr/iface v1.4.1 h1:J16Xl1wyNX9ofhpHmQ9h9gk5rnv2A6lX/2+APLTo0zU=
github.com/uudashr/iface v1.4.1/go.mod h1:pbeBPlbuU2qkNDn0mmfrxP2X+wjPMIQAy+r1MBXSXtg=
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
@@ -928,8 +928,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
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=
@@ -948,8 +948,8 @@ golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 h1:qWFG1Dj7TBjOjO
golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358/go.mod h1:4Mzdyp/6jzw9auFDJ3OMF5qksa7UvPnzKqTVGcb04ms=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.37.0 h1:ZiRjArKI8GwxZOoEtUfhrBtaCN+4b/7709dlT6SSnQA=
golang.org/x/image v0.37.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY=
golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww=
golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -975,8 +975,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1019,8 +1019,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
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=
@@ -1094,12 +1094,11 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -1116,8 +1115,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1173,8 +1172,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=
golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=
@@ -1291,10 +1290,10 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.7.0 h1:w6WUp1VbkqPEgLz4rkBzH/CSU6HkoqNLp6GstyTx3lU=
honnef.co/go/tools v0.7.0/go.mod h1:pm29oPxeP3P82ISxZDgIYeOaf9ta6Pi0EWvCFoLG2vc=
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw=
modernc.org/ccgo/v4 v4.32.0/go.mod h1:6F08EBCx5uQc38kMGl+0Nm0oWczoo1c7cgpzEry7Uc0=
modernc.org/cc/v4 v4.27.3 h1:uNCgn37E5U09mTv1XgskEVUJ8ADKpmFMPxzGJ0TSo+U=
modernc.org/cc/v4 v4.27.3/go.mod h1:3YjcbCqhoTTHPycJDRl2WZKKFj0nwcOIPBfEZK0Hdk8=
modernc.org/ccgo/v4 v4.32.4 h1:L5OB8rpEX4ZsXEQwGozRfJyJSFHbbNVOoQ59DU9/KuU=
modernc.org/ccgo/v4 v4.32.4/go.mod h1:lY7f+fiTDHfcv6YlRgSkxYfhs+UvOEEzj49jAn2TOx0=
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
@@ -1303,8 +1302,8 @@ modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=
modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.70.0 h1:U58NawXqXbgpZ/dcdS9kMshu08aiA6b7gusEusqzNkw=
modernc.org/libc v1.70.0/go.mod h1:OVmxFGP1CI/Z4L3E0Q3Mf1PDE0BucwMkcXjjLntvHJo=
modernc.org/libc v1.72.0 h1:IEu559v9a0XWjw0DPoVKtXpO2qt5NVLAnFaBbjq+n8c=
modernc.org/libc v1.72.0/go.mod h1:tTU8DL8A+XLVkEY3x5E/tO7s2Q/q42EtnNWda/L5QhQ=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
@@ -1313,8 +1312,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.47.0 h1:R1XyaNpoW4Et9yly+I2EeX7pBza/w+pmYee/0HJDyKk=
modernc.org/sqlite v1.47.0/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig=
modernc.org/sqlite v1.49.1 h1:dYGHTKcX1sJ+EQDnUzvz4TJ5GbuvhNJa8Fg6ElGx73U=
modernc.org/sqlite v1.49.1/go.mod h1:m0w8xhwYUVY3H6pSDwc3gkJ/irZT/0YEXwBlhaxQEew=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
+20 -4
View File
@@ -147,6 +147,22 @@ type AuthTokenFrontend struct {
Permissions users.Permissions `json:"Permissions,omitempty"`
}
// authTokenIssuedUnix returns iat from JWT claims when set, otherwise the persisted int64 (e.g. loaded from DB).
func authTokenIssuedUnix(t users.AuthToken) int64 {
if t.RegisteredClaims.IssuedAt != nil {
return t.RegisteredClaims.IssuedAt.Unix()
}
return t.IssuedAt
}
// authTokenExpiresUnix returns exp from JWT claims when set, otherwise the persisted int64.
func authTokenExpiresUnix(t users.AuthToken) int64 {
if t.RegisteredClaims.ExpiresAt != nil {
return t.RegisteredClaims.ExpiresAt.Unix()
}
return t.ExpiresAt
}
// listApiTokensHandler lists all API tokens or retrieves details for a specific token.
// @Summary List API tokens
// @Description List all API tokens or retrieve details for a specific token.
@@ -169,8 +185,8 @@ func listApiTokensHandler(w http.ResponseWriter, r *http.Request, d *requestCont
AuthTokensFrontend = append(AuthTokensFrontend, AuthTokenFrontend{
Token: token.Token,
Name: name,
IssuedAt: token.RegisteredClaims.IssuedAt.Unix(),
ExpiresAt: token.RegisteredClaims.ExpiresAt.Unix(),
IssuedAt: authTokenIssuedUnix(token),
ExpiresAt: authTokenExpiresUnix(token),
Permissions: token.Permissions,
})
}
@@ -205,8 +221,8 @@ func getApiTokenHandler(w http.ResponseWriter, r *http.Request, d *requestContex
AuthTokenFrontendResponse := AuthTokenFrontend{
Token: tokenInfo.Token,
Name: name,
IssuedAt: tokenInfo.IssuedAt,
ExpiresAt: tokenInfo.ExpiresAt,
IssuedAt: authTokenIssuedUnix(tokenInfo),
ExpiresAt: authTokenExpiresUnix(tokenInfo),
Permissions: tokenInfo.Permissions,
}
return renderJSON(w, r, AuthTokenFrontendResponse)
+1
View File
@@ -219,6 +219,7 @@ func fileWatchHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
return http.StatusInternalServerError, fmt.Errorf("error checking file type: %v", err)
}
if isText {
response.IsText = true
// Read the last N lines for text files only
content, err := readLastNLines(realPath, lines)
if err != nil {
+3 -3
View File
@@ -92,11 +92,11 @@ func StartHttp(ctx context.Context, storage *bolt.BoltStore, shutdownComplete ch
// ========================================
// Auth Routes - /api/auth/
// ========================================
api.HandleFunc("POST /auth/login", userWithoutOTP(loginHandler))
api.HandleFunc("POST /auth/login", userWithoutOTP(false, loginHandler))
api.HandleFunc("POST /auth/logout", withOrWithoutUser(logoutHandler))
api.HandleFunc("POST /auth/signup", withoutUser(signupHandler))
api.HandleFunc("POST /auth/otp/generate", userWithoutOTP(generateOTPHandler))
api.HandleFunc("POST /auth/otp/verify", userWithoutOTP(verifyOTPHandler))
api.HandleFunc("POST /auth/otp/generate", userWithoutOTP(true, generateOTPHandler))
api.HandleFunc("POST /auth/otp/verify", userWithoutOTP(false, verifyOTPHandler))
api.HandleFunc("POST /auth/renew", withUser(renewHandler))
api.HandleFunc("POST /auth/token", withUser(createApiTokenHandler))
api.HandleFunc("DELETE /auth/token", withUser(deleteApiTokenHandler))
+7 -1
View File
@@ -109,8 +109,9 @@ func subtitlesHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
// @Tags Resources
// @Accept json
// @Produce json
// @Param path query string true "Path to the directory"
// @Param path query string true "Path to the directory or file"
// @Param source query string true "Source name"
// @Param albumArt query bool false "When true, include embedded album art bytes in audio metadata"
// @Success 200 {object} iteminfo.ExtendedFileInfo
// @Failure 403 {object} map[string]string "Forbidden"
// @Failure 404 {object} map[string]string "Not found"
@@ -118,6 +119,7 @@ func subtitlesHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
func metadataHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
path := r.URL.Query().Get("path")
source := r.URL.Query().Get("source")
albumArt := r.URL.Query().Get("albumArt") == "true"
fileInfo, err := files.FileInfoFaster(utils.FileOptions{
FollowSymlinks: true,
Path: path,
@@ -125,6 +127,7 @@ func metadataHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
Expand: true,
Content: false,
Metadata: true,
AlbumArt: albumArt,
ExtractEmbeddedSubtitles: settings.Config.Integrations.Media.ExtractEmbeddedSubtitles,
ShowHidden: d.user.ShowHidden,
SkipExtendedAttrs: false,
@@ -142,6 +145,7 @@ func metadataHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
// @Produce json
// @Param hash query string true "Share hash"
// @Param path query string false "Path within the share"
// @Param albumArt query bool false "When true, include embedded album art bytes in audio metadata (heavier)"
// @Success 200 {object} iteminfo.ExtendedFileInfo
// @Router /public/api/media/metadata [get]
func publicMetadataHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
@@ -149,6 +153,7 @@ func publicMetadataHandler(w http.ResponseWriter, r *http.Request, d *requestCon
return http.StatusNotImplemented, fmt.Errorf("browsing is disabled for upload shares")
}
path := r.URL.Query().Get("path")
albumArt := r.URL.Query().Get("albumArt") == "true"
sourceCfg, ok := config.Server.SourceMap[d.share.Source]
if !ok {
return http.StatusNotFound, fmt.Errorf("source not found")
@@ -159,6 +164,7 @@ func publicMetadataHandler(w http.ResponseWriter, r *http.Request, d *requestCon
Expand: true,
Content: false,
Metadata: true,
AlbumArt: albumArt,
ExtractEmbeddedSubtitles: settings.Config.Integrations.Media.ExtractEmbeddedSubtitles && d.share.ExtractEmbeddedSubtitles,
ShowHidden: d.share.ShowHidden,
FollowSymlinks: false,
+10 -6
View File
@@ -314,7 +314,7 @@ func withoutUserHelper(fn handleFunc) handleFunc {
}
// allow user without OTP to pass
func userWithoutOTPhelper(fn handleFunc) handleFunc {
func userWithoutOTPhelper(disableOtp bool, fn handleFunc) handleFunc {
return func(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
if config.Auth.Methods.ProxyAuth.Enabled {
@@ -344,15 +344,19 @@ func userWithoutOTPhelper(fn handleFunc) handleFunc {
logger.Debug("ldap auth failed, calling password auth", err)
}
if config.Auth.Methods.PasswordAuth.Enabled {
// Get the authentication method from the settings
auther, err := store.Auth.Get("password")
if err != nil {
return 401, errors.ErrUnauthorized
}
// Authenticate the user based on the request
// OTP routes: password only (do not mutate stored *JSONAuth).
if ja, ok := auther.(*auth.JSONAuth); ok {
x := *ja
x.DisableOtp = disableOtp
auther = &x
}
user, err := auther.Auth(r, store.Users)
if err != nil {
logger.Debug("password auth failed, calling handler", err)
logger.Debug("password auth failed, calling handler:", err)
if err == errors.ErrNoTotpProvided {
return 403, err
}
@@ -655,8 +659,8 @@ func withoutUser(fn handleFunc) http.HandlerFunc {
return wrapHandler(withoutUserHelper(fn))
}
func userWithoutOTP(fn handleFunc) http.HandlerFunc {
return wrapHandler(userWithoutOTPhelper(fn))
func userWithoutOTP(disableOtp bool, fn handleFunc) http.HandlerFunc {
return wrapHandler(userWithoutOTPhelper(disableOtp, fn))
}
func withSelfOrAdmin(fn handleFunc) http.HandlerFunc {
+101 -24
View File
@@ -42,34 +42,112 @@ func (u *userInfoUnmarshaller) UnmarshalJSON(data []byte) error {
return err
}
// Extract groups if configured
// Extract groups if configured (supports nested claims like "resource_access.filebrowser-client.roles")
if u.groupsClaim != "" {
if v, ok := raw[u.groupsClaim]; ok {
switch val := v.(type) {
case map[string]interface{}:
u.userInfo.Groups = slices.Collect(maps.Keys(val))
case []interface{}:
groups := make([]string, len(val))
for i, g := range val {
if s, ok := g.(string); ok {
groups[i] = strings.TrimSpace(s)
}
}
u.userInfo.Groups = groups
case string:
if val != "" {
parts := strings.Split(val, ",")
for i := range parts {
parts[i] = strings.TrimSpace(parts[i])
}
u.userInfo.Groups = parts
u.userInfo.Groups = extractGroupsFromOIDCClaims(raw, u.groupsClaim)
}
u.userInfo.Claims = raw
return nil
}
// extractGroupsFromOIDCClaims extracts groups from OIDC claims supporting nested paths.
// Supports both direct claims (e.g., "groups") and nested claims using '.' separator (e.g., "resource_access.filebrowser-client.roles")
func extractGroupsFromOIDCClaims(claims map[string]interface{}, groupsClaimField string) []string {
var groups []string
if groupsClaimField == "" {
return groups
}
// Try to resolve nested path first (e.g., "custom.groups")
groupsVal := resolveOIDCNestedClaim(claims, groupsClaimField)
if groupsVal == nil {
// If nested path didn't work, try direct lookup
if val, ok := claims[groupsClaimField]; ok {
groupsVal = val
}
}
if groupsVal != nil {
groups = parseOIDCGroupsValue(groupsVal)
}
return groups
}
// resolveOIDCNestedClaim resolves nested OIDC claims using '.' as a separator
func resolveOIDCNestedClaim(claims map[string]interface{}, path string) interface{} {
parts := strings.Split(path, ".")
current := interface{}(claims)
for _, part := range parts {
switch v := current.(type) {
case map[string]interface{}:
if val, ok := v[part]; ok {
current = val
} else {
return nil
}
default:
return nil
}
}
return current
}
// parseOIDCGroupsValue parses OIDC claim values into a string slice of groups
// Returns nil for missing/empty non-array values, []string{} for empty arrays
func parseOIDCGroupsValue(groupsVal interface{}) []string {
var groups []string
switch val := groupsVal.(type) {
case map[string]interface{}:
// Groups as map keys - return empty slice for empty map
groups = slices.Collect(maps.Keys(val))
case []interface{}:
// Groups as array of interfaces - always initialize as empty slice for arrays
groups = []string{}
for _, g := range val {
if groupStr, ok := g.(string); ok {
groups = append(groups, strings.TrimSpace(groupStr))
}
}
case []string:
// Groups as array of strings - always initialize as empty slice for arrays
groups = val
if groups == nil {
groups = []string{}
}
case string:
// Single group as string or comma-separated groups
// Return nil for empty strings, parsed array otherwise
if val != "" {
parts := strings.Split(val, ",")
for i := range parts {
parts[i] = strings.TrimSpace(parts[i])
}
groups = parts
}
// else: groups stays nil for empty string
default:
// Try to unmarshal as JSON array
if jsonBytes, err := json.Marshal(val); err == nil {
var groupsArray []string
if err := json.Unmarshal(jsonBytes, &groupsArray); err == nil {
// Initialize as empty slice if array was parsed
if groupsArray == nil {
groups = []string{}
} else {
groups = groupsArray
}
}
}
}
u.userInfo.Claims = raw
return nil
return groups
}
// oidcLoginHandler initiates OIDC login.
@@ -177,7 +255,7 @@ func oidcCallbackHandler(w http.ResponseWriter, r *http.Request, d *requestConte
// --- Attempt to process ID Token ---
if ok && rawIDToken != "" {
logger.Debug("ID token found in token response, attempting verification.")
logger.Debugf("ID token found in token response, attempting verification. Raw ID Token: %s", rawIDToken)
// Verify the ID token
// This uses the verifier initialized with the provider's JWKS endpoint and client ID
@@ -202,7 +280,6 @@ func oidcCallbackHandler(w http.ResponseWriter, r *http.Request, d *requestConte
claimsFromIDToken = true
}
}
logger.Debugf("Failed to verify ID token: %v", verify_err)
}
} else {
+58
View File
@@ -114,6 +114,64 @@ func TestUserInfoUnmarshaller(t *testing.T) {
Groups: []string{"admins"},
},
},
{
name: "nested single-level claim",
jsonData: `{"username":"john","custom":{"groups":["admin","users"]}}`,
groupsClaim: "custom.groups",
expected: userInfo{
Claims: map[string]interface{}{
"username": "john",
"custom": map[string]interface{}{
"groups": []interface{}{"admin","users"},
},
},
Groups: []string{"admin", "users"},
},
},
{
name: "keycloak resource_access nested structure",
jsonData: `{"sub":"user123","resource_access":{"filebrowser-client":{"roles":["admin","editor","viewer"]}}}`,
groupsClaim: "resource_access.filebrowser-client.roles",
expected: userInfo{
Claims: map[string]interface{}{
"sub": "user123",
"resource_access": map[string]interface{}{
"filebrowser-client": map[string]interface{}{
"roles": []interface{}{"admin","editor","viewer"},
},
},
},
Groups: []string{"admin", "editor", "viewer"},
},
},
{
name: "nested claim with single string value",
jsonData: `{"username":"bob","auth":{"group":"admin"}}`,
groupsClaim: "auth.group",
expected: userInfo{
Claims: map[string]interface{}{
"username": "bob",
"auth": map[string]interface{}{
"group": "admin",
},
},
Groups: []string{"admin"},
},
},
{
name: "invalid nested path returns empty groups",
jsonData: `{"username":"alice","custom":{"roles":["user"]}}`,
groupsClaim: "custom.nonexistent.roles",
expected: userInfo{
Claims: map[string]interface{}{
"username": "alice",
"custom": map[string]interface{}{
"roles": []interface{}{"user"},
},
},
Groups: nil,
},
},
}
for _, tt := range tests {
+4
View File
@@ -521,6 +521,10 @@ func resourcePostHandler(w http.ResponseWriter, r *http.Request, d *requestConte
existingIsDir := stat.IsDir()
requestingDir := isDir
if r.URL.Query().Get("override") != "true" {
return http.StatusConflict, nil
}
// If type mismatch (file vs folder or folder vs file) and not overriding
if existingIsDir != requestingDir && r.URL.Query().Get("override") != "true" {
logger.Debugf("Type conflict detected in chunked: existing is dir=%v, requesting dir=%v at path=%v", existingIsDir, requestingDir, realPath)
+13 -34
View File
@@ -83,7 +83,7 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, d *requestCont
staticURL := config.Server.BaseURL + "public/static"
description := config.Frontend.Description
title := config.Frontend.Name
banner := staticURL + "/pwa-icon-512.png" // Use largest generated icon for best quality
banner := staticURL + "/icons/pwa-icon-512.png" // largest generated PWA icon
disableSidebar := false
// Use custom favicon if configured and validated, otherwise fall back to default
@@ -139,9 +139,9 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, d *requestCont
// Determine OpenGraph image: use banner if set, otherwise use largest available icon (512x512)
ogImage := banner
if banner == staticURL+"/pwa-icon-512.png" {
if banner == staticURL+"/icons/pwa-icon-512.png" {
// Note: 512x512 is square; OpenGraph prefers 1200x630 (1.91:1 ratio) but square works fine
ogImage = staticURL + "/pwa-icon-512.png"
ogImage = staticURL + "/icons/pwa-icon-512.png"
}
// Construct the full URL for the current request
@@ -158,22 +158,9 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, d *requestCont
fullURL = fmt.Sprintf("%s://%s%s", scheme, r.Host, r.URL.Path)
}
// Determine PWA icon URLs based on custom favicon settings
pwaIcon192 := staticURL + "/" + settings.Env.PWAIcon192
pwaIcon256 := staticURL + "/" + settings.Env.PWAIcon256
pwaIcon512 := staticURL + "/" + settings.Env.PWAIcon512
// If custom favicon is set and it's SVG, use the favicon directly
if settings.Env.FaviconIsCustom && strings.ToLower(filepath.Ext(settings.Env.FaviconPath)) == ".svg" {
pwaIcon192 = favicon
pwaIcon256 = favicon
pwaIcon512 = favicon
} else if settings.Env.FaviconIsCustom {
// For custom PNG/ICO favicons, use generated PWA icons
pwaIcon192 = staticURL + "/pwa-icon-192.png"
pwaIcon256 = staticURL + "/pwa-icon-256.png"
pwaIcon512 = staticURL + "/pwa-icon-512.png"
}
pwaIcon192 := staticURL + "/icons/pwa-icon-192.png"
pwaIcon256 := staticURL + "/icons/pwa-icon-256.png"
pwaIcon512 := staticURL + "/icons/pwa-icon-512.png"
data["htmlVars"] = map[string]interface{}{
"title": title,
@@ -186,8 +173,8 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, d *requestCont
"favicon": favicon,
"loginIcon": loginIcon,
"color": defaultThemeColor,
"winIcon": staticURL + "/mstile-256x256.png",
"appIcon": staticURL + "/apple-touch-icon.png",
"winIcon": staticURL + "/icons/mstile-256x256.png",
"appIcon": staticURL + "/icons/apple-touch-icon.png",
"description": description,
"loadingSpinnersCSS": template.CSS(loadingSpinnersCSS),
"banner": banner,
@@ -313,7 +300,7 @@ func staticAssetHandler(w http.ResponseWriter, r *http.Request) {
assetPath = settings.Env.FaviconEmbeddedPath
case "favicon.png":
if settings.Env.FaviconIsCustom && strings.ToLower(filepath.Ext(settings.Env.FaviconPath)) != ".svg" {
iconPath := filepath.Join(settings.Env.PWAIconsDir, "pwa-icon-512.png")
iconPath := filepath.Join(settings.PWAIconsCacheDir(), "pwa-icon-512.png")
if _, err := os.Stat(iconPath); err == nil {
http.ServeFile(w, r, iconPath)
return
@@ -323,18 +310,10 @@ func staticAssetHandler(w http.ResponseWriter, r *http.Request) {
assetPath = "img/icons/favicon.png"
case "icons/favicon-32x32.png",
"icons/pwa-icon-192.png", "icons/pwa-icon-256.png", "icons/pwa-icon-512.png",
"icons/apple-touch-icon.png":
// Serve generated icons from cache directory
iconPath := filepath.Join(settings.Env.PWAIconsDir, path)
if _, err := os.Stat(iconPath); err == nil {
http.ServeFile(w, r, iconPath)
return
}
// Fall back to embedded favicon.png if generation failed
assetPath = "img/icons/favicon.png"
case "icons/mstile-256x256.png":
// Windows tile - redirect to pwa-icon-256.png (they're identical)
iconPath := filepath.Join(settings.Env.PWAIconsDir, "pwa-icon-256.png")
"icons/apple-touch-icon.png", "icons/mstile-256x256.png":
// Files are generated as PWAIconsCacheDir()/basename (URL uses icons/ prefix only for routing)
rel := strings.TrimPrefix(path, "icons/")
iconPath := filepath.Join(settings.PWAIconsCacheDir(), rel)
if _, err := os.Stat(iconPath); err == nil {
http.ServeFile(w, r, iconPath)
return
+73 -1
View File
@@ -5,6 +5,8 @@ import (
"fmt"
"io"
"net/http"
"net/url"
"reflect"
"slices"
"sort"
"strconv"
@@ -12,6 +14,7 @@ import (
"github.com/gtsteffaniak/filebrowser/backend/auth"
"github.com/gtsteffaniak/filebrowser/backend/common/errors"
"github.com/gtsteffaniak/filebrowser/backend/common/utils"
"github.com/gtsteffaniak/filebrowser/backend/database/storage"
"github.com/gtsteffaniak/filebrowser/backend/database/users"
"github.com/gtsteffaniak/go-logger/logger"
@@ -255,16 +258,77 @@ func usersPostHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
return http.StatusCreated, nil
}
// nonAdminEditableFieldNameSet returns User.NonAdminEditable struct field names (e.g. "Locale", "Preview").
func nonAdminEditableFieldNameSet() map[string]struct{} {
m := make(map[string]struct{})
t := reflect.TypeOf(users.NonAdminEditable{})
for i := 0; i < t.NumField(); i++ {
m[t.Field(i).Name] = struct{}{}
}
return m
}
// userPutOnlyNonAdminEditableFields reports whether req.Which lists exclusively NonAdminEditable fields,
// excluding Password. Empty which or which[0] == "all" means a broad update and returns false.
func userPutOnlyNonAdminEditableFields(which []string) bool {
if len(which) == 0 || strings.EqualFold(strings.TrimSpace(which[0]), "all") {
return false
}
allowed := nonAdminEditableFieldNameSet()
for _, w := range which {
f := utils.CapitalizeFirst(strings.TrimSpace(w))
if f == "" {
return false
}
if strings.EqualFold(f, "Password") {
return false
}
if _, ok := allowed[f]; !ok {
return false
}
}
return true
}
// verifyActorPasswordForUserPut requires URL-encoded X-Password when the authenticated actor uses
// password login. Callers should invoke this only when the update requires re-authentication.
func verifyActorPasswordForUserPut(r *http.Request, d *requestContext) (int, error) {
if d.user.LoginMethod != users.LoginMethodPassword {
return 0, nil
}
encoded := r.Header.Get("X-Password")
if encoded == "" {
return http.StatusUnauthorized, fmt.Errorf("X-Password header is required to confirm your password")
}
plain, err := url.QueryUnescape(encoded)
if err != nil {
return http.StatusBadRequest, fmt.Errorf("invalid password encoding")
}
if plain == "" {
return http.StatusUnauthorized, fmt.Errorf("X-Password header is required to confirm your password")
}
actor, err := store.Users.Get(d.user.ID)
if err != nil {
return http.StatusInternalServerError, err
}
if err := utils.CheckPwd(plain, actor.Password); err != nil {
return http.StatusUnauthorized, fmt.Errorf("invalid password")
}
return 0, nil
}
// userPutHandler updates an existing user's details.
// @Summary Update a user's details
// @Description Updates the details of a user identified by ID.
// @Description Updates the details of a user identified by ID. When the authenticated actor uses password login, they must send their current password in the X-Password header unless the update only touches NonAdminEditable profile fields (not password). Full updates (which empty or "all") or any admin-only field require confirmation.
// @Tags Users
// @Accept json
// @Param id query string false "user ID to update"
// @Param id query string false "usename to update"
// @Param X-Password header string false "Actor's current password (URL-encoded); required for password-login actors when updating password, using which=all, or any field outside NonAdminEditable"
// @Param data body users.User true "User data to update"
// @Success 204 "No Content - User updated successfully"
// @Failure 400 {object} map[string]string "Bad Request"
// @Failure 401 {object} map[string]string "Unauthorized - invalid or missing actor password when required"
// @Failure 403 {object} map[string]string "Forbidden"
// @Failure 500 {object} map[string]string "Internal Server Error"
// @Router /api/users [put]
@@ -316,6 +380,14 @@ func userPutHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (
return http.StatusBadRequest, fmt.Errorf("failed to get user: %w", err)
}
if d.user.LoginMethod == users.LoginMethodPassword && !userPutOnlyNonAdminEditableFields(req.Which) {
var status int
status, err = verifyActorPasswordForUserPut(r, d)
if err != nil {
return status, err
}
}
err = store.Users.Update(&req.User, d.user.Permissions.Admin, req.Which...)
if err != nil {
return http.StatusBadRequest, err
+2
View File
@@ -24,6 +24,8 @@ func errToStatus(err error) int {
return http.StatusForbidden
case errors.Is(err, libErrors.ErrInvalidRequestParams):
return http.StatusBadRequest
case errors.Is(err, libErrors.ErrIsDirectory):
return http.StatusMethodNotAllowed
default:
return http.StatusInternalServerError
}
+9 -21
View File
@@ -29,7 +29,6 @@ const (
type iconSize struct {
size int
name string
env *string
}
// determineSourceType determines which type of favicon source we have
@@ -153,13 +152,13 @@ func handleRasterFavicon(faviconPath string) ([]byte, error) {
func getIconSizesToGenerate() []iconSize {
return []iconSize{
// Browser favicon (32x32 PNG for Safari/older browsers)
{32, "favicon-32x32.png", nil},
{32, "favicon-32x32.png"},
// PWA icons (PNG required for Apple PWA compatibility)
{192, "pwa-icon-192.png", &settings.Env.PWAIcon192},
{256, "pwa-icon-256.png", &settings.Env.PWAIcon256}, // Also serves as Windows tile
{512, "pwa-icon-512.png", &settings.Env.PWAIcon512},
{192, "pwa-icon-192.png"},
{256, "pwa-icon-256.png"}, // Also serves as Windows tile
{512, "pwa-icon-512.png"},
// Platform-specific icons
{180, "apple-touch-icon.png", nil}, // iOS home screen (required for Apple)
{180, "apple-touch-icon.png"}, // iOS home screen (required for Apple)
}
}
@@ -181,10 +180,6 @@ func generateIconSizes(sourceData []byte) error {
continue
}
// Update environment variable if specified
if icon.env != nil {
*icon.env = filepath.Join("icons", icon.name)
}
generatedCount++
}
@@ -199,7 +194,7 @@ func generateIconSizes(sourceData []byte) error {
// generateSingleIcon generates a single icon file at the specified size
func generateSingleIcon(previewService *preview.Service, sourceData []byte, icon iconSize) error {
outputPath := filepath.Join(settings.Env.PWAIconsDir, icon.name)
outputPath := filepath.Join(settings.PWAIconsCacheDir(), icon.name)
// Create output file
outFile, err := os.Create(outputPath)
@@ -233,18 +228,11 @@ func generateSingleIcon(previewService *preview.Service, sourceData []byte, icon
// All icons are generated as PNG for maximum compatibility (Apple devices, PWA, older browsers)
func GeneratePWAIcons() error {
logger.Info("Generating PWA icons...")
// Set PWA icons directory in cache
settings.Env.PWAIconsDir = filepath.Join(settings.Config.Server.CacheDir, "icons")
// Initialize with default paths (all icons will be generated)
settings.Env.PWAIcon192 = "icons/pwa-icon-192.png"
settings.Env.PWAIcon256 = "icons/pwa-icon-256.png"
settings.Env.PWAIcon512 = "icons/pwa-icon-512.png"
cacheDir := settings.PWAIconsCacheDir()
// Create icons directory with configured permissions
// Note: Parent cache directory should already exist from testCacheDirSpeed()
if err := os.MkdirAll(settings.Env.PWAIconsDir, fileutils.PermDir); err != nil {
logger.Warningf("Failed to create PWA icons directory %s: %v", settings.Env.PWAIconsDir, err)
if err := os.MkdirAll(cacheDir, fileutils.PermDir); err != nil {
logger.Warningf("Failed to create PWA icons directory %s: %v", cacheDir, err)
}
// Load and prepare source data as PNG
sourceData, err := loadFaviconSource()
+9 -19
View File
@@ -1,9 +1,6 @@
package icons
import (
"path/filepath"
"strings"
"github.com/gtsteffaniak/filebrowser/backend/common/settings"
)
@@ -36,6 +33,9 @@ func generatePWAManifest(name, description, baseURL, themeColor, pwaIcon192, pwa
if len(name) > 12 {
shortName = name[:12]
}
if name == "FileBrowser Quantum" {
shortName = "FBQ"
}
return PWAManifest{
Name: name,
@@ -68,7 +68,9 @@ func generatePWAManifest(name, description, baseURL, themeColor, pwaIcon192, pwa
}
}
// InitializePWAManifest generates and caches the PWA manifest at startup
// InitializePWAManifest caches the PWA manifest at startup. Icon URLs always point at
// PNGs under public/static/icons/, which GeneratePWAIcons produces from the configured
// favicon (custom SVG uses a raster sidecar as the raster source).
func InitializePWAManifest() {
config := &settings.Config
staticURL := config.Server.BaseURL + "public/static"
@@ -76,21 +78,9 @@ func InitializePWAManifest() {
description := config.Frontend.Description
defaultThemeColor := "#455a64"
// Determine PWA icon URLs based on custom favicon settings
pwaIcon192 := staticURL + "/" + settings.Env.PWAIcon192
pwaIcon256 := staticURL + "/" + settings.Env.PWAIcon256
pwaIcon512 := staticURL + "/" + settings.Env.PWAIcon512
if settings.Env.FaviconIsCustom && strings.ToLower(filepath.Ext(settings.Env.FaviconPath)) == ".svg" {
favicon := staticURL + "/favicon"
pwaIcon192 = favicon
pwaIcon256 = favicon
pwaIcon512 = favicon
} else if settings.Env.FaviconIsCustom {
pwaIcon192 = staticURL + "/icons/pwa-icon-192.png"
pwaIcon256 = staticURL + "/icons/pwa-icon-256.png"
pwaIcon512 = staticURL + "/icons/pwa-icon-512.png"
}
pwaIcon192 := staticURL + "/icons/pwa-icon-192.png"
pwaIcon256 := staticURL + "/icons/pwa-icon-256.png"
pwaIcon512 := staticURL + "/icons/pwa-icon-512.png"
CachedManifest = generatePWAManifest(title, description, config.Server.BaseURL, defaultThemeColor, pwaIcon192, pwaIcon256, pwaIcon512)
}
+10 -29
View File
@@ -85,17 +85,17 @@ type Stats struct {
LastIndexedUnix int64 `json:"lastIndexedUnixTime"`
Complexity uint `json:"complexity"`
LastScanned time.Time `json:"lastScanned"`
DiskUsed uint64 `json:"used"`
UsedAlt uint64 `json:"usedAlt"`
UsedAsIndexed uint64 `json:"used"`
UsedDisk uint64 `json:"usedAlt"`
DiskTotal uint64 `json:"total"`
}
// reduced index is json exposed to the client
type ReducedIndex struct {
Stats
IdxName string `json:"name"`
DiskTotal uint64 `json:"total"`
Status IndexStatus `json:"status"`
Scanners []*ScannerInfo `json:"scanners,omitempty"`
IdxName string `json:"name"`
Status IndexStatus `json:"status"`
Scanners []*ScannerInfo `json:"scanners,omitempty"`
}
type Index struct {
@@ -1158,22 +1158,6 @@ func (idx *Index) updateFolderSizeAndParents(path string, newSize uint64, previo
}
}
// SetFolderSize sets the size for a directory in the in-memory map
func (idx *Index) SetFolderSize(path string, newSize uint64) {
idx.folderSizesMu.Lock()
defer idx.folderSizesMu.Unlock()
idx.folderSizes[path] = newSize
idx.folderSizesUnsynced[path] = struct{}{} // Mark as changed for next DB sync
}
// GetFolderSize retrieves the size for a directory from the in-memory map
func (idx *Index) GetFolderSize(path string) (uint64, bool) {
idx.folderSizesMu.RLock()
defer idx.folderSizesMu.RUnlock()
size, exists := idx.folderSizes[path]
return size, exists
}
// GetFolderSizeForDisplay retrieves folder size with appropriate formatting based on config
func (idx *Index) GetFolderSizeForDisplay(path string) int64 {
size, exists := idx.GetFolderSize(path)
@@ -1532,18 +1516,15 @@ func (idx *Index) IsNeverWatchPath(adjustedPath string) bool {
return exists
}
type DiskUsage struct {
Total uint64 `json:"total"`
Used uint64 `json:"used"`
}
func (idx *Index) SetUsage(totalBytes uint64) {
func (idx *Index) SetUsage(totalDiskSize, partitionUsed, UsedAsIndexed uint64) {
if settings.Config.Frontend.DisableUsedPercentage {
return
}
idx.mu.Lock()
defer idx.mu.Unlock()
idx.DiskTotal = totalBytes
idx.DiskTotal = totalDiskSize
idx.UsedAsIndexed = UsedAsIndexed
idx.UsedDisk = partitionUsed
}
func (idx *Index) SetStatus(status IndexStatus) error {
-10
View File
@@ -231,16 +231,6 @@ func (idx *Index) setupMultiScanner(isNewDb bool) {
logger.Errorf("Failed to load persisted index data for [%v]: %v", idx.Name, err)
}
// Load existing folder sizes from database to avoid marking everything as dirty on startup
existingSizes, err := idx.db.LoadFolderSizes(idx.Name)
if err != nil {
logger.Errorf("[INIT] Failed to load existing folder sizes for [%s]: %v", idx.Name, err)
} else {
idx.folderSizesMu.Lock()
idx.folderSizes = existingSizes
idx.folderSizesMu.Unlock()
}
idx.mu.Lock()
idx.scanners = make(map[string]*Scanner)
idx.mu.Unlock()
+32 -26
View File
@@ -296,8 +296,8 @@ func GetIndexInfo(sourceName string, forceCacheRefresh bool) (ReducedIndex, erro
return ReducedIndex{}, fmt.Errorf("index %s not found", sourceName)
}
// Only update disk total if cache is missing or explicitly forced
// The "used" value comes from totalSize and is always current
// Only update disk total if cache is missing or explicitly forced.
// Indexed "used" bytes for the UI come from the SQLite root row (see below), not in-memory scan state.
sourcePath := idx.Path
cacheKey := "usageCache-" + sourceName
if forceCacheRefresh {
@@ -307,26 +307,24 @@ func GetIndexInfo(sourceName string, forceCacheRefresh bool) (ReducedIndex, erro
_, ok = utils.DiskUsageCache.Get(cacheKey)
if !ok {
// Only fetch disk total and used if not cached (this is expensive, so we cache it)
totalBytes, err := fileutils.GetPartitionSize(sourcePath)
totalPartitionSize, err := fileutils.GetPartitionSize(sourcePath)
if err != nil {
idx.mu.Lock()
idx.Status = UNAVAILABLE
idx.mu.Unlock()
return ReducedIndex{}, fmt.Errorf("error getting disk usage for %s: %v", sourcePath, err)
logger.Errorf("Failed to get partition size for index %s: %v", sourceName, err)
}
itemInfo, dbErr := idx.db.GetDirectoryChildren(idx.Name, "/")
if dbErr != nil {
logger.Debugf("GetIndexInfo: could not read root folder size from index DB for %s: %v", sourceName, dbErr)
}
indexedSizeFromDB := uint64(0)
for _, item := range itemInfo {
indexedSizeFromDB += uint64(item.Size)
}
idx.SetUsage(totalBytes)
// Also fetch OS-reported partition used space (total - free)
usedAlt, err := fileutils.GetPartitionUsed(sourcePath)
partitionUsed, err := fileutils.GetPartitionUsed(sourcePath)
if err != nil {
logger.Errorf("Failed to get partition used space for index %s: %v", sourceName, err)
usedAlt = 0
}
idx.mu.Lock()
idx.UsedAlt = usedAlt
idx.mu.Unlock()
idx.SetUsage(totalPartitionSize, partitionUsed, indexedSizeFromDB)
utils.DiskUsageCache.Set(cacheKey, true)
}
@@ -348,16 +346,8 @@ func GetIndexInfo(sourceName string, forceCacheRefresh bool) (ReducedIndex, erro
})
}
reducedIdx := idx.ReducedIndex
var diskUsed uint64
rootTotal, ok := idx.GetFolderSize("/")
if !ok {
logger.Errorf("Failed to get root-level indexed total for index %s", sourceName)
diskUsed = 0
} else {
diskUsed = rootTotal
}
reducedIdx.DiskUsed = diskUsed
reducedIdx.UsedAlt = idx.UsedAlt
reducedIdx.UsedAsIndexed = idx.UsedAsIndexed
reducedIdx.UsedDisk = idx.UsedDisk
reducedIdx.DiskTotal = idx.DiskTotal
reducedIdx.Scanners = scannerInfos
reducedIdx.NumDirs = idx.getNumDirsUnlocked()
@@ -372,3 +362,19 @@ func GetIndexInfo(sourceName string, forceCacheRefresh bool) (ReducedIndex, erro
idx.mu.RUnlock()
return reducedIdx, nil
}
// SetFolderSize sets the size for a directory in the in-memory map
func (idx *Index) SetFolderSize(path string, newSize uint64) {
idx.folderSizesMu.Lock()
defer idx.folderSizesMu.Unlock()
idx.folderSizes[path] = newSize
idx.folderSizesUnsynced[path] = struct{}{} // Mark as changed for next DB sync
}
// GetFolderSize retrieves the size for a directory from the in-memory map
func (idx *Index) GetFolderSize(path string) (uint64, bool) {
idx.folderSizesMu.RLock()
defer idx.folderSizesMu.RUnlock()
size, exists := idx.folderSizes[path]
return size, exists
}
+2 -2
View File
@@ -1,6 +1,6 @@
.card,
#listingView.gallery .item,
#listingView.normal .item,
.listing-item,
.floating-window,
.button,
button,
.input,
+34 -3
View File
@@ -1090,7 +1090,7 @@ const docTemplate = `{
"parameters": [
{
"type": "string",
"description": "Path to the directory",
"description": "Path to the directory or file",
"name": "path",
"in": "query",
"required": true
@@ -1101,6 +1101,12 @@ const docTemplate = `{
"name": "source",
"in": "query",
"required": true
},
{
"type": "boolean",
"description": "When true, include embedded album art bytes in audio metadata",
"name": "albumArt",
"in": "query"
}
],
"responses": {
@@ -3070,7 +3076,7 @@ const docTemplate = `{
}
},
"put": {
"description": "Updates the details of a user identified by ID.",
"description": "Updates the details of a user identified by ID. When the authenticated actor uses password login, they must send their current password in the X-Password header unless the update only touches NonAdminEditable profile fields (not password). Full updates (which empty or \"all\") or any admin-only field require confirmation.",
"consumes": [
"application/json"
],
@@ -3091,6 +3097,12 @@ const docTemplate = `{
"name": "id",
"in": "query"
},
{
"type": "string",
"description": "Actor's current password (URL-encoded); required for password-login actors when updating password, using which=all, or any field outside NonAdminEditable",
"name": "X-Password",
"in": "header"
},
{
"description": "User data to update",
"name": "data",
@@ -3114,6 +3126,15 @@ const docTemplate = `{
}
}
},
"401": {
"description": "Unauthorized - invalid or missing actor password when required",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"403": {
"description": "Forbidden",
"schema": {
@@ -3272,6 +3293,12 @@ const docTemplate = `{
"description": "Path within the share",
"name": "path",
"in": "query"
},
{
"type": "boolean",
"description": "When true, include embedded album art bytes in audio metadata (heavier)",
"name": "albumArt",
"in": "query"
}
],
"responses": {
@@ -5225,7 +5252,7 @@ const docTemplate = `{
"type": "string"
},
"secret": {
"description": "secret: shared secret key for verifying JWT token signatures (required)",
"description": "Shared secret key/bytes for verifying JWT token signatures (required, eg PUBLIC KEY, RSA PUBLIC KEY, EC PUBLIC KEY, or CERTIFICATE)",
"type": "string"
},
"userGroups": {
@@ -6822,6 +6849,10 @@ const docTemplate = `{
"$ref": "#/definitions/users.SourceScope"
}
},
"showCopyPath": {
"description": "show copy path action in the context menu",
"type": "boolean"
},
"showFirstLogin": {
"type": "boolean"
},
+34 -3
View File
@@ -1079,7 +1079,7 @@
"parameters": [
{
"type": "string",
"description": "Path to the directory",
"description": "Path to the directory or file",
"name": "path",
"in": "query",
"required": true
@@ -1090,6 +1090,12 @@
"name": "source",
"in": "query",
"required": true
},
{
"type": "boolean",
"description": "When true, include embedded album art bytes in audio metadata",
"name": "albumArt",
"in": "query"
}
],
"responses": {
@@ -3059,7 +3065,7 @@
}
},
"put": {
"description": "Updates the details of a user identified by ID.",
"description": "Updates the details of a user identified by ID. When the authenticated actor uses password login, they must send their current password in the X-Password header unless the update only touches NonAdminEditable profile fields (not password). Full updates (which empty or \"all\") or any admin-only field require confirmation.",
"consumes": [
"application/json"
],
@@ -3080,6 +3086,12 @@
"name": "id",
"in": "query"
},
{
"type": "string",
"description": "Actor's current password (URL-encoded); required for password-login actors when updating password, using which=all, or any field outside NonAdminEditable",
"name": "X-Password",
"in": "header"
},
{
"description": "User data to update",
"name": "data",
@@ -3103,6 +3115,15 @@
}
}
},
"401": {
"description": "Unauthorized - invalid or missing actor password when required",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"403": {
"description": "Forbidden",
"schema": {
@@ -3261,6 +3282,12 @@
"description": "Path within the share",
"name": "path",
"in": "query"
},
{
"type": "boolean",
"description": "When true, include embedded album art bytes in audio metadata (heavier)",
"name": "albumArt",
"in": "query"
}
],
"responses": {
@@ -5214,7 +5241,7 @@
"type": "string"
},
"secret": {
"description": "secret: shared secret key for verifying JWT token signatures (required)",
"description": "Shared secret key/bytes for verifying JWT token signatures (required, eg PUBLIC KEY, RSA PUBLIC KEY, EC PUBLIC KEY, or CERTIFICATE)",
"type": "string"
},
"userGroups": {
@@ -6811,6 +6838,10 @@
"$ref": "#/definitions/users.SourceScope"
}
},
"showCopyPath": {
"description": "show copy path action in the context menu",
"type": "boolean"
},
"showFirstLogin": {
"type": "boolean"
},
+31 -4
View File
@@ -801,8 +801,8 @@ definitions:
to logout url. Custom logout query params are respected.
type: string
secret:
description: 'secret: shared secret key for verifying JWT token signatures
(required)'
description: Shared secret key/bytes for verifying JWT token signatures (required,
eg PUBLIC KEY, RSA PUBLIC KEY, EC PUBLIC KEY, or CERTIFICATE)
type: string
userGroups:
description: if set, only users in these groups are allowed to log in. Blocks
@@ -1984,6 +1984,9 @@ definitions:
items:
$ref: '#/definitions/users.SourceScope'
type: array
showCopyPath:
description: show copy path action in the context menu
type: boolean
showFirstLogin:
type: boolean
showHidden:
@@ -2772,7 +2775,7 @@ paths:
description: Same ExtendedFileInfo as resources GET with metadata=true (typically
used for directories).
parameters:
- description: Path to the directory
- description: Path to the directory or file
in: query
name: path
required: true
@@ -2782,6 +2785,10 @@ paths:
name: source
required: true
type: string
- description: When true, include embedded album art bytes in audio metadata
in: query
name: albumArt
type: boolean
produces:
- application/json
responses:
@@ -4230,7 +4237,11 @@ paths:
put:
consumes:
- application/json
description: Updates the details of a user identified by ID.
description: Updates the details of a user identified by ID. When the authenticated
actor uses password login, they must send their current password in the X-Password
header unless the update only touches NonAdminEditable profile fields (not
password). Full updates (which empty or "all") or any admin-only field require
confirmation.
parameters:
- description: user ID to update
in: query
@@ -4240,6 +4251,11 @@ paths:
in: query
name: id
type: string
- description: Actor's current password (URL-encoded); required for password-login
actors when updating password, using which=all, or any field outside NonAdminEditable
in: header
name: X-Password
type: string
- description: User data to update
in: body
name: data
@@ -4255,6 +4271,12 @@ paths:
additionalProperties:
type: string
type: object
"401":
description: Unauthorized - invalid or missing actor password when required
schema:
additionalProperties:
type: string
type: object
"403":
description: Forbidden
schema:
@@ -4295,6 +4317,11 @@ paths:
in: query
name: path
type: string
- description: When true, include embedded album art bytes in audio metadata
(heavier)
in: query
name: albumArt
type: boolean
produces:
- application/json
responses:
+237 -283
View File
@@ -11,7 +11,6 @@
"@onlyoffice/document-editor-vue": "^1.4.0",
"ace-builds": "^1.24.2",
"axios": "^1.13.5",
"clipboard": "^2.0.4",
"css-vars-ponyfill": "^2.4.3",
"dompurify": "^3.2.4",
"epubjs": "^0.3.93",
@@ -1127,13 +1126,13 @@
}
},
"node_modules/@playwright/test": {
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz",
"integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==",
"version": "1.59.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz",
"integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.58.2"
"playwright": "1.59.1"
},
"bin": {
"playwright": "cli.js"
@@ -1166,9 +1165,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.1.tgz",
"integrity": "sha512-xB0b51TB7IfDEzAojXahmr+gfA00uYVInJGgNNkeQG6RPnCPGr7udsylFLTubuIUSRE6FkcI1NElyRt83PP5oQ==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz",
"integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==",
"cpu": [
"arm"
],
@@ -1180,9 +1179,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.1.tgz",
"integrity": "sha512-XOjPId0qwSDKHaIsdzHJtKCxX0+nH8MhBwvrNsT7tVyKmdTx1jJ4XzN5RZXCdTzMpufLb+B8llTC0D8uCrLhcw==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz",
"integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==",
"cpu": [
"arm64"
],
@@ -1194,9 +1193,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.1.tgz",
"integrity": "sha512-vQuRd28p0gQpPrS6kppd8IrWmFo42U8Pz1XLRjSZXq5zCqyMDYFABT7/sywL11mO1EL10Qhh7MVPEwkG8GiBeg==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz",
"integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==",
"cpu": [
"arm64"
],
@@ -1208,9 +1207,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.1.tgz",
"integrity": "sha512-x6VG6U29+Ivlnajrg1IHdzXeAwSoEHBFVO+CtC9Brugx6de712CUJobRUxsIA0KYrQvCmzNrMPFTT1A4CCqNTg==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz",
"integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==",
"cpu": [
"x64"
],
@@ -1222,9 +1221,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.1.tgz",
"integrity": "sha512-Sgi0Uo6t1YCHJMNO3Y8+bm+SvOanUGkoZKn/VJPwYUe2kp31X5KnXmzKd/NjW8iA3gFcfNZ64zh14uOGrIllCQ==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz",
"integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==",
"cpu": [
"arm64"
],
@@ -1236,9 +1235,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.1.tgz",
"integrity": "sha512-AM4xnwEZwukdhk7laMWfzWu9JGSVnJd+Fowt6Fd7QW1nrf3h0Hp7Qx5881M4aqrUlKBCybOxz0jofvIIfl7C5g==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz",
"integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==",
"cpu": [
"x64"
],
@@ -1250,9 +1249,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.1.tgz",
"integrity": "sha512-KUizqxpwaR2AZdAUsMWfL/C94pUu7TKpoPd88c8yFVixJ+l9hejkrwoK5Zj3wiNh65UeyryKnJyxL1b7yNqFQA==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz",
"integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==",
"cpu": [
"arm"
],
@@ -1264,9 +1263,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.1.tgz",
"integrity": "sha512-MZoQ/am77ckJtZGFAtPucgUuJWiop3m2R3lw7tC0QCcbfl4DRhQUBUkHWCkcrT3pqy5Mzv5QQgY6Dmlba6iTWg==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz",
"integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==",
"cpu": [
"arm"
],
@@ -1278,9 +1277,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.1.tgz",
"integrity": "sha512-Sez95TP6xGjkWB1608EfhCX1gdGrO5wzyN99VqzRtC17x/1bhw5VU1V0GfKUwbW/Xr1J8mSasoFoJa6Y7aGGSA==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz",
"integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==",
"cpu": [
"arm64"
],
@@ -1292,9 +1291,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.1.tgz",
"integrity": "sha512-9Cs2Seq98LWNOJzR89EGTZoiP8EkZ9UbQhBlDgfAkM6asVna1xJ04W2CLYWDN/RpUgOjtQvcv8wQVi1t5oQazA==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz",
"integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==",
"cpu": [
"arm64"
],
@@ -1306,9 +1305,9 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.1.tgz",
"integrity": "sha512-n9yqttftgFy7IrNEnHy1bOp6B4OSe8mJDiPkT7EqlM9FnKOwUMnCK62ixW0Kd9Clw0/wgvh8+SqaDXMFvw3KqQ==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz",
"integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==",
"cpu": [
"loong64"
],
@@ -1320,9 +1319,9 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-musl": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.1.tgz",
"integrity": "sha512-SfpNXDzVTqs/riak4xXcLpq5gIQWsqGWMhN1AGRQKB4qGSs4r0sEs3ervXPcE1O9RsQ5bm8Muz6zmQpQnPss1g==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz",
"integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==",
"cpu": [
"loong64"
],
@@ -1334,9 +1333,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.1.tgz",
"integrity": "sha512-LjaChED0wQnjKZU+tsmGbN+9nN1XhaWUkAlSbTdhpEseCS4a15f/Q8xC2BN4GDKRzhhLZpYtJBZr2NZhR0jvNw==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz",
"integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==",
"cpu": [
"ppc64"
],
@@ -1348,9 +1347,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-musl": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.1.tgz",
"integrity": "sha512-ojW7iTJSIs4pwB2xV6QXGwNyDctvXOivYllttuPbXguuKDX5vwpqYJsHc6D2LZzjDGHML414Tuj3LvVPe1CT1A==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz",
"integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==",
"cpu": [
"ppc64"
],
@@ -1362,9 +1361,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.1.tgz",
"integrity": "sha512-FP+Q6WTcxxvsr0wQczhSE+tOZvFPV8A/mUE6mhZYFW9/eea/y/XqAgRoLLMuE9Cz0hfX5bi7p116IWoB+P237A==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz",
"integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==",
"cpu": [
"riscv64"
],
@@ -1376,9 +1375,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.1.tgz",
"integrity": "sha512-L1uD9b/Ig8Z+rn1KttCJjwhN1FgjRMBKsPaBsDKkfUl7GfFq71pU4vWCnpOsGljycFEbkHWARZLf4lMYg3WOLw==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz",
"integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==",
"cpu": [
"riscv64"
],
@@ -1390,9 +1389,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.1.tgz",
"integrity": "sha512-EZc9NGTk/oSUzzOD4nYY4gIjteo2M3CiozX6t1IXGCOdgxJTlVu/7EdPeiqeHPSIrxkLhavqpBAUCfvC6vBOug==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz",
"integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==",
"cpu": [
"s390x"
],
@@ -1404,9 +1403,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.1.tgz",
"integrity": "sha512-NQ9KyU1Anuy59L8+HHOKM++CoUxrQWrZWXRik4BJFm+7i5NP6q/SW43xIBr80zzt+PDBJ7LeNmloQGfa0JGk0w==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz",
"integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==",
"cpu": [
"x64"
],
@@ -1418,9 +1417,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.1.tgz",
"integrity": "sha512-GZkLk2t6naywsveSFBsEb0PLU+JC9ggVjbndsbG20VPhar6D1gkMfCx4NfP9owpovBXTN+eRdqGSkDGIxPHhmQ==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz",
"integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==",
"cpu": [
"x64"
],
@@ -1432,9 +1431,9 @@
]
},
"node_modules/@rollup/rollup-openbsd-x64": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.1.tgz",
"integrity": "sha512-1hjG9Jpl2KDOetr64iQd8AZAEjkDUUK5RbDkYWsViYLC1op1oNzdjMJeFiofcGhqbNTaY2kfgqowE7DILifsrA==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz",
"integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==",
"cpu": [
"x64"
],
@@ -1446,9 +1445,9 @@
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.1.tgz",
"integrity": "sha512-ARoKfflk0SiiYm3r1fmF73K/yB+PThmOwfWCk1sr7x/k9dc3uGLWuEE9if+Pw21el8MSpp3TMnG5vLNsJ/MMGQ==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz",
"integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==",
"cpu": [
"arm64"
],
@@ -1460,9 +1459,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.1.tgz",
"integrity": "sha512-oOST61G6VM45Mz2vdzWMr1s2slI7y9LqxEV5fCoWi2MDONmMvgsJVHSXxce/I2xOSZPTZ47nDPOl1tkwKWSHcw==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz",
"integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==",
"cpu": [
"arm64"
],
@@ -1474,9 +1473,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.1.tgz",
"integrity": "sha512-x5WgLi5dWpRz7WclKBGEF15LcWTh0ewrHM6Cq4A+WUbkysUMZNeqt05bwPonOQ3ihPS/WMhAZV5zB1DfnI4Sxg==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz",
"integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==",
"cpu": [
"ia32"
],
@@ -1488,9 +1487,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.1.tgz",
"integrity": "sha512-wS+zHAJRVP5zOL0e+a3V3E/NTEwM2HEvvNKoDy5Xcfs0o8lljxn+EAFPkUsxihBdmDq1JWzXmmB9cbssCPdxxw==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz",
"integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==",
"cpu": [
"x64"
],
@@ -1502,9 +1501,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.1.tgz",
"integrity": "sha512-rhHyrMeLpErT/C7BxcEsU4COHQUzHyrPYW5tOZUeUhziNtRuYxmDWvqQqzpuUt8xpOgmbKa1btGXfnA/ANVO+g==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz",
"integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==",
"cpu": [
"x64"
],
@@ -1551,9 +1550,9 @@
}
},
"node_modules/@types/node": {
"version": "24.12.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz",
"integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==",
"version": "24.12.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz",
"integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1960,39 +1959,39 @@
}
},
"node_modules/@vue/compiler-core": {
"version": "3.5.30",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.30.tgz",
"integrity": "sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==",
"version": "3.5.32",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.32.tgz",
"integrity": "sha512-4x74Tbtqnda8s/NSD6e1Dr5p1c8HdMU5RWSjMSUzb8RTcUQqevDCxVAitcLBKT+ie3o0Dl9crc/S/opJM7qBGQ==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.29.0",
"@vue/shared": "3.5.30",
"@babel/parser": "^7.29.2",
"@vue/shared": "3.5.32",
"entities": "^7.0.1",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.5.30",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.30.tgz",
"integrity": "sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==",
"version": "3.5.32",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.32.tgz",
"integrity": "sha512-ybHAu70NtiEI1fvAUz3oXZqkUYEe5J98GjMDpTGl5iHb0T15wQYLR4wE3h9xfuTNA+Cm2f4czfe8B4s+CCH57Q==",
"license": "MIT",
"dependencies": {
"@vue/compiler-core": "3.5.30",
"@vue/shared": "3.5.30"
"@vue/compiler-core": "3.5.32",
"@vue/shared": "3.5.32"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.5.30",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.30.tgz",
"integrity": "sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==",
"version": "3.5.32",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.32.tgz",
"integrity": "sha512-8UYUYo71cP/0YHMO814TRZlPuUUw3oifHuMR7Wp9SNoRSrxRQnhMLNlCeaODNn6kNTJsjFoQ/kqIj4qGvya4Xg==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.29.0",
"@vue/compiler-core": "3.5.30",
"@vue/compiler-dom": "3.5.30",
"@vue/compiler-ssr": "3.5.30",
"@vue/shared": "3.5.30",
"@babel/parser": "^7.29.2",
"@vue/compiler-core": "3.5.32",
"@vue/compiler-dom": "3.5.32",
"@vue/compiler-ssr": "3.5.32",
"@vue/shared": "3.5.32",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.21",
"postcss": "^8.5.8",
@@ -2000,13 +1999,13 @@
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.5.30",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.30.tgz",
"integrity": "sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==",
"version": "3.5.32",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.32.tgz",
"integrity": "sha512-Gp4gTs22T3DgRotZ8aA/6m2jMR+GMztvBXUBEUOYOcST+giyGWJ4WvFd7QLHBkzTxkfOt8IELKNdpzITLbA2rw==",
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.30",
"@vue/shared": "3.5.30"
"@vue/compiler-dom": "3.5.32",
"@vue/shared": "3.5.32"
}
},
"node_modules/@vue/compiler-vue2": {
@@ -2077,60 +2076,59 @@
}
},
"node_modules/@vue/reactivity": {
"version": "3.5.30",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.30.tgz",
"integrity": "sha512-179YNgKATuwj9gB+66snskRDOitDiuOZqkYia7mHKJaidOMo/WJxHKF8DuGc4V4XbYTJANlfEKb0yxTQotnx4Q==",
"version": "3.5.32",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.32.tgz",
"integrity": "sha512-/ORasxSGvZ6MN5gc+uE364SxFdJ0+WqVG0CENXaGW58TOCdrAW76WWaplDtECeS1qphvtBZtR+3/o1g1zL4xPQ==",
"license": "MIT",
"dependencies": {
"@vue/shared": "3.5.30"
"@vue/shared": "3.5.32"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.5.30",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.30.tgz",
"integrity": "sha512-e0Z+8PQsUTdwV8TtEsLzUM7SzC7lQwYKePydb7K2ZnmS6jjND+WJXkmmfh/swYzRyfP1EY3fpdesyYoymCzYfg==",
"version": "3.5.32",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.32.tgz",
"integrity": "sha512-pDrXCejn4UpFDFmMd27AcJEbHaLemaE5o4pbb7sLk79SRIhc6/t34BQA7SGNgYtbMnvbF/HHOftYBgFJtUoJUQ==",
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.30",
"@vue/shared": "3.5.30"
"@vue/reactivity": "3.5.32",
"@vue/shared": "3.5.32"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.5.30",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.30.tgz",
"integrity": "sha512-2UIGakjU4WSQ0T4iwDEW0W7vQj6n7AFn7taqZ9Cvm0Q/RA2FFOziLESrDL4GmtI1wV3jXg5nMoJSYO66egDUBw==",
"version": "3.5.32",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.32.tgz",
"integrity": "sha512-1CDVv7tv/IV13V8Nip1k/aaObVbWqRlVCVezTwx3K07p7Vxossp5JU1dcPNhJk3w347gonIUT9jQOGutyJrSVQ==",
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.30",
"@vue/runtime-core": "3.5.30",
"@vue/shared": "3.5.30",
"@vue/reactivity": "3.5.32",
"@vue/runtime-core": "3.5.32",
"@vue/shared": "3.5.32",
"csstype": "^3.2.3"
}
},
"node_modules/@vue/server-renderer": {
"version": "3.5.30",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.30.tgz",
"integrity": "sha512-v+R34icapydRwbZRD0sXwtHqrQJv38JuMB4JxbOxd8NEpGLny7cncMp53W9UH/zo4j8eDHjQ1dEJXwzFQknjtQ==",
"version": "3.5.32",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.32.tgz",
"integrity": "sha512-IOjm2+JQwRFS7W28HNuJeXQle9KdZbODFY7hFGVtnnghF51ta20EWAZJHX+zLGtsHhaU6uC9BGPV52KVpYryMQ==",
"license": "MIT",
"dependencies": {
"@vue/compiler-ssr": "3.5.30",
"@vue/shared": "3.5.30"
"@vue/compiler-ssr": "3.5.32",
"@vue/shared": "3.5.32"
},
"peerDependencies": {
"vue": "3.5.30"
"vue": "3.5.32"
}
},
"node_modules/@vue/shared": {
"version": "3.5.30",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.30.tgz",
"integrity": "sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==",
"version": "3.5.32",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.32.tgz",
"integrity": "sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==",
"license": "MIT"
},
"node_modules/@xmldom/xmldom": {
"version": "0.7.13",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.13.tgz",
"integrity": "sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==",
"deprecated": "this version is no longer supported, please update to at least 0.8.*",
"version": "0.8.13",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz",
"integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
@@ -2166,9 +2164,9 @@
}
},
"node_modules/adm-zip": {
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz",
"integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==",
"version": "0.5.17",
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.17.tgz",
"integrity": "sha512-+Ut8d9LLqwEvHHJl1+PIHqoyDxFgVN847JTVM3Izi3xHDWPE4UtzzXysMZQs64DMcrJfBeS/uoEP4AD3HQHnQQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2269,14 +2267,14 @@
"license": "MIT"
},
"node_modules/axios": {
"version": "1.13.6",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
"integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.15.1.tgz",
"integrity": "sha512-WOG+Jj8ZOvR0a3rAn+Tuf1UQJRxw5venr6DgdbJzngJE3qG7X0kL83CZGpdHMxEm+ZK3seAbvFsw4FfOfP9vxg==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
"proxy-from-env": "^1.1.0"
"proxy-from-env": "^2.1.0"
}
},
"node_modules/balanced-match": {
@@ -2319,9 +2317,9 @@
"license": "ISC"
},
"node_modules/brace-expansion": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
"license": "MIT",
"dependencies": {
"balanced-match": "^4.0.2"
@@ -2445,17 +2443,6 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/clipboard": {
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
"integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
"license": "MIT",
"dependencies": {
"good-listener": "^1.2.2",
"select": "^1.1.2",
"tiny-emitter": "^2.0.0"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -2660,16 +2647,16 @@
"license": "MIT"
},
"node_modules/deepl-node": {
"version": "1.24.0",
"resolved": "https://registry.npmjs.org/deepl-node/-/deepl-node-1.24.0.tgz",
"integrity": "sha512-vZ9jUpzJRvFamgVOfm1LDy3YYJ7k8FhxtAX9whR92EFshLIP9JlYS0HFwXL5yYsfqzXdb/wssGRSWvR48t7nSg==",
"version": "1.26.0",
"resolved": "https://registry.npmjs.org/deepl-node/-/deepl-node-1.26.0.tgz",
"integrity": "sha512-KaMZ248O/N2HrevQnsJoCoHxDUNBbsQ7+OZJa2YAOhym4qtVCNTzRst+4rLX9B9T84SVN4xVQSuJyFIdlI/x1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": ">=12.0",
"adm-zip": "^0.5.16",
"axios": "^1.7.4",
"form-data": "^3.0.0",
"form-data": "^3.0.4",
"loglevel": ">=1.6.2",
"uuid": "^8.3.2"
},
@@ -2703,12 +2690,6 @@
"node": ">=0.4.0"
}
},
"node_modules/delegate": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
"license": "MIT"
},
"node_modules/dingbat-to-unicode": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz",
@@ -2742,9 +2723,9 @@
}
},
"node_modules/dompurify": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz",
"integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==",
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.0.tgz",
"integrity": "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
@@ -3494,9 +3475,9 @@
"license": "ISC"
},
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
"funding": [
{
"type": "individual",
@@ -3710,15 +3691,6 @@
"node": ">= 4"
}
},
"node_modules/good-listener": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
"integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
"license": "MIT",
"dependencies": {
"delegate": "^3.1.2"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -3783,9 +3755,9 @@
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
"integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
@@ -4170,9 +4142,9 @@
}
},
"node_modules/jsonfile": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
"integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4268,9 +4240,9 @@
}
},
"node_modules/lodash": {
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"version": "4.18.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"license": "MIT"
},
"node_modules/lodash.merge": {
@@ -4351,15 +4323,6 @@
"node": ">=12.0.0"
}
},
"node_modules/mammoth/node_modules/@xmldom/xmldom": {
"version": "0.8.11",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
"integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/mammoth/node_modules/argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -4421,9 +4384,9 @@
}
},
"node_modules/micromatch/node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4455,12 +4418,12 @@
}
},
"node_modules/minimatch": {
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"version": "10.2.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
"license": "BlueOak-1.0.0",
"dependencies": {
"brace-expansion": "^5.0.2"
"brace-expansion": "^5.0.5"
},
"engines": {
"node": "18 || 20 || >=22"
@@ -4818,9 +4781,9 @@
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4850,13 +4813,13 @@
"license": "MIT"
},
"node_modules/playwright": {
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
"integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",
"version": "1.59.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz",
"integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.58.2"
"playwright-core": "1.59.1"
},
"bin": {
"playwright": "cli.js"
@@ -4869,9 +4832,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz",
"integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",
"version": "1.59.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz",
"integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -4895,9 +4858,9 @@
}
},
"node_modules/postcss": {
"version": "8.5.8",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
"integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
"version": "8.5.10",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz",
"integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==",
"funding": [
{
"type": "opencollective",
@@ -4953,10 +4916,13 @@
"license": "MIT"
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
"integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/punycode": {
"version": "2.3.1",
@@ -4969,9 +4935,9 @@
}
},
"node_modules/qrcode.vue": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/qrcode.vue/-/qrcode.vue-3.8.0.tgz",
"integrity": "sha512-+XKbSKvQu158zlHsJm+HObGUQM3Q+9Oq6yb2op/6lMM2gwOiiG9uIhujvrSV5UoTjrzR0BcNxCdW2kk7KE4NEg==",
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/qrcode.vue/-/qrcode.vue-3.8.1.tgz",
"integrity": "sha512-kJORXcI5Uml5GtzcfFkchpljFgaXKQB/3ZUt+Hwnw7Y1F7FiA01U2yyO5Vscwv3bVxPkm/KLKXUOXsHbTsYrTw==",
"license": "MIT",
"peerDependencies": {
"vue": "^3.0.0"
@@ -5094,9 +5060,9 @@
}
},
"node_modules/rollup": {
"version": "4.59.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.1.tgz",
"integrity": "sha512-iZKH8BeoCwTCBTZBZWQQMreekd4mdomwdjIQ40GC1oZm6o+8PnNMIxFOiCsGMWeS8iDJ7KZcl7KwmKk/0HOQpA==",
"version": "4.60.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz",
"integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5110,31 +5076,31 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.59.1",
"@rollup/rollup-android-arm64": "4.59.1",
"@rollup/rollup-darwin-arm64": "4.59.1",
"@rollup/rollup-darwin-x64": "4.59.1",
"@rollup/rollup-freebsd-arm64": "4.59.1",
"@rollup/rollup-freebsd-x64": "4.59.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.59.1",
"@rollup/rollup-linux-arm-musleabihf": "4.59.1",
"@rollup/rollup-linux-arm64-gnu": "4.59.1",
"@rollup/rollup-linux-arm64-musl": "4.59.1",
"@rollup/rollup-linux-loong64-gnu": "4.59.1",
"@rollup/rollup-linux-loong64-musl": "4.59.1",
"@rollup/rollup-linux-ppc64-gnu": "4.59.1",
"@rollup/rollup-linux-ppc64-musl": "4.59.1",
"@rollup/rollup-linux-riscv64-gnu": "4.59.1",
"@rollup/rollup-linux-riscv64-musl": "4.59.1",
"@rollup/rollup-linux-s390x-gnu": "4.59.1",
"@rollup/rollup-linux-x64-gnu": "4.59.1",
"@rollup/rollup-linux-x64-musl": "4.59.1",
"@rollup/rollup-openbsd-x64": "4.59.1",
"@rollup/rollup-openharmony-arm64": "4.59.1",
"@rollup/rollup-win32-arm64-msvc": "4.59.1",
"@rollup/rollup-win32-ia32-msvc": "4.59.1",
"@rollup/rollup-win32-x64-gnu": "4.59.1",
"@rollup/rollup-win32-x64-msvc": "4.59.1",
"@rollup/rollup-android-arm-eabi": "4.60.2",
"@rollup/rollup-android-arm64": "4.60.2",
"@rollup/rollup-darwin-arm64": "4.60.2",
"@rollup/rollup-darwin-x64": "4.60.2",
"@rollup/rollup-freebsd-arm64": "4.60.2",
"@rollup/rollup-freebsd-x64": "4.60.2",
"@rollup/rollup-linux-arm-gnueabihf": "4.60.2",
"@rollup/rollup-linux-arm-musleabihf": "4.60.2",
"@rollup/rollup-linux-arm64-gnu": "4.60.2",
"@rollup/rollup-linux-arm64-musl": "4.60.2",
"@rollup/rollup-linux-loong64-gnu": "4.60.2",
"@rollup/rollup-linux-loong64-musl": "4.60.2",
"@rollup/rollup-linux-ppc64-gnu": "4.60.2",
"@rollup/rollup-linux-ppc64-musl": "4.60.2",
"@rollup/rollup-linux-riscv64-gnu": "4.60.2",
"@rollup/rollup-linux-riscv64-musl": "4.60.2",
"@rollup/rollup-linux-s390x-gnu": "4.60.2",
"@rollup/rollup-linux-x64-gnu": "4.60.2",
"@rollup/rollup-linux-x64-musl": "4.60.2",
"@rollup/rollup-openbsd-x64": "4.60.2",
"@rollup/rollup-openharmony-arm64": "4.60.2",
"@rollup/rollup-win32-arm64-msvc": "4.60.2",
"@rollup/rollup-win32-ia32-msvc": "4.60.2",
"@rollup/rollup-win32-x64-gnu": "4.60.2",
"@rollup/rollup-win32-x64-msvc": "4.60.2",
"fsevents": "~2.3.2"
}
},
@@ -5195,12 +5161,6 @@
"node": ">=v12.22.7"
}
},
"node_modules/select": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
"integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==",
"license": "MIT"
},
"node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
@@ -5514,12 +5474,6 @@
"integrity": "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==",
"license": "MIT"
},
"node_modules/tiny-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
"license": "MIT"
},
"node_modules/tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
@@ -5542,14 +5496,14 @@
"license": "MIT"
},
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"version": "0.2.16",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
"integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.3"
"picomatch": "^4.0.4"
},
"engines": {
"node": ">=12.0.0"
@@ -5700,9 +5654,9 @@
}
},
"node_modules/typescript": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
"integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"devOptional": true,
"license": "Apache-2.0",
"bin": {
@@ -5803,9 +5757,9 @@
}
},
"node_modules/vite": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz",
"integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6115,16 +6069,16 @@
"license": "MIT"
},
"node_modules/vue": {
"version": "3.5.30",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.30.tgz",
"integrity": "sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==",
"version": "3.5.32",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.32.tgz",
"integrity": "sha512-vM4z4Q9tTafVfMAK7IVzmxg34rSzTFMyIe0UUEijUCkn9+23lj0WRfA83dg7eQZIUlgOSGrkViIaCfqSAUXsMw==",
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.30",
"@vue/compiler-sfc": "3.5.30",
"@vue/runtime-dom": "3.5.30",
"@vue/server-renderer": "3.5.30",
"@vue/shared": "3.5.30"
"@vue/compiler-dom": "3.5.32",
"@vue/compiler-sfc": "3.5.32",
"@vue/runtime-dom": "3.5.32",
"@vue/server-renderer": "3.5.32",
"@vue/shared": "3.5.32"
},
"peerDependencies": {
"typescript": "*"
+2 -2
View File
@@ -25,7 +25,6 @@
"@onlyoffice/document-editor-vue": "^1.4.0",
"ace-builds": "^1.24.2",
"axios": "^1.13.5",
"clipboard": "^2.0.4",
"css-vars-ponyfill": "^2.4.3",
"dompurify": "^3.2.4",
"epubjs": "^0.3.93",
@@ -44,7 +43,8 @@
"vue-router": "^4.3.0"
},
"overrides": {
"minimatch": "^10.2.1"
"minimatch": "^10.2.1",
"@xmldom/xmldom": "^0.8.12"
},
"devDependencies": {
"@intlify/eslint-plugin-vue-i18n": "^3.2.0",
+1 -1
View File
@@ -115,7 +115,7 @@ auth:
disableVerifyTLS: false # disable TLS verification (insecure, for testing only)
logoutRedirectUrl: "" # if provider logout url is provided, filebrowser will also redirect to logout url. Custom logout query params are respected.
header: "" # HTTP header to look for JWT token (e.g. X-JWT-Assertion). Default is "X-JWT-Assertion"
secret: "" # secret: shared secret key for verifying JWT token signatures (required)
secret: "" # Shared secret key/bytes for verifying JWT token signatures (required, eg PUBLIC KEY, RSA PUBLIC KEY, EC PUBLIC KEY, or CERTIFICATE)
algorithm: "" # JWT signing algorithm (HS256, HS384, HS512, RS256, ES256). Default is "HS256"
key: "" # secret: the key used to sign the JWT tokens. If not set, a random key will be generated.
adminUsername: "admin" # secret: the username of the admin user. If not set, the default is "admin".
+11
View File
@@ -347,3 +347,14 @@ body > .loader.done {
min-width: 400px;
min-height: 50px;
}
/* -------------------------------------------------------------------------- */
/* Standard portrait mobile responsive fix */
/* -------------------------------------------------------------------------- */
@media (max-width: 768px) {
.loader--placeholder.loader--large .placeholder-item {
width: 100%;
min-width: auto;
}
}
+20 -4
View File
@@ -83,13 +83,19 @@ export async function generateOTP(username, password) {
let apiPath = getApiPath('auth/otp/generate', params)
const res = await fetch(apiPath, {
method: 'POST',
credentials: 'same-origin',
headers: {
'X-Password': encodeURIComponent(password),
}
})
return await res.json()
const body = await res.json().catch(() => ({}))
if (!res.ok) {
const msg = body.message || 'Failed to generate OTP'
throw new Error(msg)
}
return body
} catch (error) {
notify.showError(error || 'Failed to generate OTP')
notify.showError(error.message || error || 'Failed to generate OTP')
throw error
}
}
@@ -101,16 +107,26 @@ export async function verifyOTP(username, password, otp) {
let apiPath = getApiPath('auth/otp/verify', params)
const res = await fetch(apiPath, {
method: 'POST',
credentials: 'same-origin',
headers: {
'X-Password': encodeURIComponent(password),
'X-Secret': otp,
}
})
if (res.status != 200) {
throw new Error('Failed to verify OTP')
let msg = 'Failed to verify OTP'
try {
const errBody = await res.json()
if (errBody.message) {
msg = errBody.message
}
} catch {
// keep default message
}
throw new Error(msg)
}
} catch (error) {
notify.showError(error || 'Failed to verify OTP')
notify.showError(error.message || error || 'Failed to verify OTP')
throw error
}
}
+13 -6
View File
@@ -21,21 +21,28 @@ export async function getSubtitleContent(source, path, subtitleName, embedded =
}
}
// GET /api/media/metadata — same directory shape as resources with metadata=true (adjusted to items[]).
/** @returns {Promise<object>} directory resource (adjustedData) */
export async function fetchDirectoryMediaMetadata(source, path) {
const apiPath = getApiPath("media/metadata", { source, path });
// GET /api/media/metadata — directory or file with metadata; optional albumArt for embedded cover extraction.
/** @param {boolean} albumArt when true, request embedded album art in audio metadata */
/** @returns {Promise<object>} resource (adjustedData) */
export async function fetchDirectoryMediaMetadata(source, path, albumArt = false) {
const apiPath = getApiPath("media/metadata", {
source,
path,
...(albumArt ? { albumArt: "true" } : {}),
});
const res = await fetchURL(apiPath);
const data = await res.json();
return adjustedData(data);
}
// GET /public/api/media/metadata
/** @returns {Promise<object>} directory resource (adjustedData) */
export async function fetchDirectoryMediaMetadataPublic(path, hash, password = "") {
/** @param {boolean} albumArt when true, request embedded album art in audio metadata */
/** @returns {Promise<object>} resource (adjustedData) */
export async function fetchDirectoryMediaMetadataPublic(path, hash, password = "", albumArt = false) {
const params = {
path,
hash,
...(albumArt ? { albumArt: "true" } : {}),
...(state.shareInfo.token && { token: state.shareInfo.token }),
};
const apiPath = getPublicApiPath("media/metadata", params);
+2 -2
View File
@@ -86,9 +86,9 @@ export async function fileWatcherLatencyCheck() {
}
// GET /api/tools/fileWatcher
export async function fileWatcher(source, path) {
export async function fileWatcher(source, path, lines) {
try {
const params = { source, path };
const params = { source, path, lines: lines };
const apiPath = getApiPath("tools/fileWatcher", params);
const res = await fetchURL(apiPath);
return await res.json();
+60 -8
View File
@@ -1,6 +1,9 @@
import { fetchURL, fetchJSON } from '@/api/utils'
import { getApiPath, getPublicApiPath } from '@/utils/url.js'
import { notify } from '@/notify'
import { state } from '@/store/state.js'
import { mutations } from '@/store/mutations.js'
import i18n from '@/i18n'
// GET /api/users (list all)
export async function getAllUsers() {
@@ -48,13 +51,18 @@ export async function create(user) {
}
// PUT /api/users (update user)
export async function update(user, which = ['all']) {
// Password-login: tries without X-Password first; on 401 requiring X-Password, opens the prompt and retries.
// options.skipActorPasswordConfirm / pre-set X-Password skip that flow.
// options.actorPasswordPromptI18nKey — optional vue-i18n key (default: confirmPasswordToSaveUser).
export async function update(user, which = ['all'], options = {}) {
const excludeKeys = ['id', 'name']
which = which.filter(item => !excludeKeys.includes(item))
if (user.username === 'anonymous') {
return
}
const mergedHeaders = { ...(options.headers || {}) }
let userData = user
if (which.length !== 1 || which[0] !== 'all') {
userData = {}
@@ -66,13 +74,57 @@ export async function update(user, which = ['all']) {
}
const apiPath = getApiPath('users', { id: user.id })
await fetchURL(apiPath, {
method: 'PUT',
body: JSON.stringify({
which: which,
data: userData
})
const body = JSON.stringify({
which: which,
data: userData
})
const needsActorPasswordRetry = (err) =>
state.user?.loginMethod === 'password' &&
options.skipActorPasswordConfirm !== true &&
mergedHeaders['X-Password'] === undefined &&
err &&
err.status === 401 &&
typeof err.message === 'string' &&
err.message.includes('X-Password')
try {
await fetchURL(apiPath, {
method: 'PUT',
body,
headers: mergedHeaders,
})
} catch (e) {
if (!needsActorPasswordRetry(e)) {
throw e
}
const promptKey =
options.actorPasswordPromptI18nKey || 'prompts.confirmPasswordToSaveUser'
return new Promise((resolve, reject) => {
mutations.showPrompt({
name: 'password',
props: {
infoText: i18n.global.t(promptKey),
submitLabel: i18n.global.t('general.confirm'),
submitCallback: async (actorPassword) => {
try {
await update(user, which, {
...options,
headers: {
...mergedHeaders,
'X-Password': encodeURIComponent(actorPassword),
},
skipActorPasswordConfirm: true,
})
resolve(undefined)
} catch (err) {
reject(err)
}
},
},
})
})
}
}
// DELETE /api/users (remove user)
+1 -1
View File
@@ -30,7 +30,7 @@
:title="label"
class="action no-select"
>
<i v-if="icon == 'table_rows_narrow' || icon == 'content_copy'" class="material-symbols-outlined">{{ icon }}</i>
<i v-if="icon == 'table_rows_narrow' || icon == 'content_copy' || icon == 'file_copy' || icon == 'copy_all'" class="material-symbols-outlined">{{ icon }}</i>
<i v-else class="material-symbols">{{ icon }}</i>
<span>{{ label }}</span>
+39 -4
View File
@@ -10,7 +10,7 @@
ref="contextMenu"
v-if="showContext"
:style="centered ? {} : { top: posY + 'px', left: posX + 'px' }"
class="button no-select floating-window"
class="no-select floating-window"
:class="{ 'dark-mode': isDarkMode, 'centered': centered }"
:key="showCreate ? 'create-mode' : 'normal-mode'"
>
@@ -28,6 +28,7 @@
@mouseleave="hideTooltip"
@mouseenter="showTooltip($event, $t('buttons.selectedCount'))"
class="button selected-count-header"
:class="{ 'selected-count-header--circle': selectedCount <= 99 }"
>
<span>{{ selectedCount }}</span>
</div>
@@ -90,10 +91,16 @@
/>
<action
v-if="showCopy"
icon="content_copy"
icon="file_copy"
:label="$t('buttons.copyFile')"
@action="showCopyPrompt"
/>
<action
v-if="showCopyPath"
icon="copy_all"
:label="$t('buttons.copyPath')"
@action="copyPathToClipboard"
/>
<action
v-if="showOpenParentFolder"
icon="folder"
@@ -152,7 +159,7 @@
top: '3em',
right: '1em',
}"
class="button no-select floating-window"
class="no-select floating-window"
:class="{ 'dark-mode': isDarkMode }"
>
<action icon="info" :label="$t('general.info')" @action="showInfoPrompt"/>
@@ -179,6 +186,7 @@ import buttons from "@/utils/buttons";
import { notify } from "@/notify";
import { resourcesApi } from "@/api";
import { url } from "@/utils";
import { copyToClipboard } from "@/utils/clipboard";
function isArchivePath(pathOrName) {
if (!pathOrName || typeof pathOrName !== "string") return false;
@@ -287,6 +295,10 @@ export default {
if (this.showLimitedOptions) return false;
return !this.showCreate && this.selectedCount > 0 && this.permissions.modify;
},
showCopyPath() {
if (this.showLimitedOptions) return false;
return !this.showCreate && this.selectedCount == 1 && !!state.user?.showCopyPath;
},
showOpenParentFolder() {
return !this.showCreate && this.selectedCount == 1 && (this.isSearchActive || this.showLimitedOptions);
},
@@ -647,6 +659,13 @@ export default {
},
});
},
async copyPathToClipboard() {
const item = this.firstSelected;
const path = item?.path || "";
await copyToClipboard(path);
mutations.closeHovers();
},
showNewDirPrompt() {
mutations.closeHovers();
// If the context menu was triggered on a directory, pass its path as base
@@ -792,12 +811,14 @@ export default {
z-index: 1000;
background-color: var(--background);
max-width: 20em;
min-width: 15em;
min-width: 13em;
min-height: 4em;
height: auto;
display: flex;
flex-direction: column;
justify-content: center;
border-radius: 1em;
padding: 0.5em;
}
#context-menu.centered {
@@ -812,6 +833,18 @@ export default {
cursor: unset;
}
/* Circle for up to 3 digits; 1000+ keeps pill shape from .button */
.selected-count-header--circle {
box-sizing: border-box;
flex-shrink: 0;
width: 2em;
height: 2em;
min-width: 2em;
min-height: 2em;
padding: 0;
border-radius: 50%;
}
.context-menu-header > .action i {
padding: 0.25em;
}
@@ -855,5 +888,7 @@ export default {
display: flex;
align-items: center;
justify-content: space-between;
padding-left: 0.5em;
padding-right: 0.5em;
}
</style>
+32 -14
View File
@@ -69,7 +69,7 @@
</div>
</div>
<!-- Loading icon when search is ongoing -->
<LoadingSpinner v-if="ongoing" size="medium" />
<LoadingSpinner v-if="isRunning" size="medium" />
<!-- Message when no results are found -->
<div class="searchPrompt" v-show="isEmpty && !isRunning">
<p>{{ noneMessage }}</p>
@@ -145,14 +145,14 @@ export default {
],
typeSelect: [
{ label: this.$t("general.photos"), value: "type:image" },
{ label: this.$t("general.audios"), value: "type:audio" },
{ label: this.$t("general.audio"), value: "type:audio" },
{ label: this.$t("general.videos"), value: "type:video" },
{ label: this.$t("general.documents"), value: "type:doc" },
{ label: this.$t("general.archives"), value: "type:archive" },
],
toggleOptionButton: [{ label: this.$t("search.showOptions") }],
value: "",
ongoing: false,
ongoing: 0,
results: [],
reload: false,
scrollable: null,
@@ -181,7 +181,7 @@ export default {
},
value() {
if (this.results.length) {
this.ongoing = false;
this.ongoing = 0;
this.results = [];
}
},
@@ -207,7 +207,7 @@ export default {
// Add keyboard event listener for "/" to activate search
this.handleKeydown = (event) => {
if (event.key === '/' && !state.isSearchActive && getters.currentPrompt() === null) {
if (event.key === '/' || event.key === ' ' && !state.isSearchActive && getters.currentPrompt() === null) {
event.preventDefault();
this.open();
}
@@ -267,17 +267,16 @@ export default {
return this.results.length === 0;
},
text() {
if (this.ongoing) {
if (this.ongoing > 0) {
return "";
}
return this.$t("search.typeToSearch", { minSearchLength: globalVars.minSearchLength })
},
isRunning() {
return this.ongoing;
return this.ongoing > 0;
},
activeStates() {
const selectedItems = state.selected ? [].concat(state.selected) : [];
if (selectedItems.length === 0) {
// Return an array of all false if nothing is selected
return new Array(this.results.length).fill(false);
@@ -464,12 +463,11 @@ export default {
},
async submit(event) {
this.results = [];
if (event != undefined) {
event.preventDefault();
}
if (this.value === "" || this.value.length < globalVars.minSearchLength) {
this.ongoing = false; this.noneMessage = this.$t("search.notEnoughCharacters", { minSearchLength: globalVars.minSearchLength });
this.noneMessage = this.$t("search.notEnoughCharacters", { minSearchLength: globalVars.minSearchLength });
return;
}
let searchTypesFull = this.searchTypes;
@@ -488,7 +486,7 @@ export default {
if (newerUnix !== null) {
dateParams.newerThan = newerUnix;
}
this.ongoing = true;
this.ongoing++;
// Determine which sources to search
let sourcesToSearch = [];
@@ -511,13 +509,13 @@ export default {
dateParams
);
this.ongoing = false;
if (this.results.length == 0) {
this.ongoing--;
if (this.results.length == 0 && this.ongoing == 0) {
this.noneMessage = this.$t("search.noResults");
}
},
showHelpTooltip(event) {
const helpText = `${this.$t("search.helpText1")}\n\n${this.$t("search.helpText2")}`;
const helpText = `${this.$t("search.helpText1", { minSearchLength: globalVars.minSearchLength })}\n\n${this.$t("search.helpText2")}`;
mutations.showTooltip({
content: helpText,
x: event.clientX,
@@ -548,6 +546,26 @@ export default {
return "";
}
},
open() {
if (state.isSearchActive) return;
if (getters.currentPromptName()) return;
mutations.closeHovers();
mutations.closeSidebar();
mutations.resetSelected();
mutations.setSearch(true);
this.$nextTick(() => {
const input = this.$refs.input;
if (input) {
input.focus();
}
const resultList = document.getElementById("result-list");
if (resultList) {
resultList.classList.add("active");
}
});
},
addSelected(event, s) {
event.preventDefault();
const pathParts = url.removeTrailingSlash(s.path).split("/");
+1 -1
View File
@@ -202,7 +202,7 @@ export default {
},
// Ctrl + Mouse Wheel to adjust the slider sizes
handleWheel(event) {
if (!event.ctrlKey) return;
if (!(event.ctrlKey || event.metaKey)) return;
const delta = event.deltaY > 0 ? 1 : -1; // Scroll down increases, up decreases
@@ -223,7 +223,8 @@ export default {
fromSource: selectedItem.source,
to: url.joinPath(targetPath, selectedItem.name),
toSource: source,
itemType: selectedItem.type
itemType: selectedItem.type,
name: selectedItem.name
});
}
@@ -547,6 +547,7 @@ export default {
this.applyImgTransform();
},
mousedownStart(event) {
if (event.button !== 0) return;
if (this.scale === 1) {
this.teardownEdgeMouseListeners();
this.edgeMouseActive = true;
@@ -567,12 +568,14 @@ export default {
event.preventDefault();
},
mouseMove(event) {
if (event.button !== 0) return;
if (this.scale > 1 && this.inDrag) {
this.doMove(event.movementX, event.movementY);
event.preventDefault();
}
},
mouseUp(event) {
if (event.button !== 0) return;
if (this.scale > 1) {
this.inDrag = false;
event.preventDefault();
+10 -1
View File
@@ -1,6 +1,6 @@
<template>
<div class="card-content" aria-label="file-list-prompt">
<!-- Source Selection Dropdown -->
<!-- Source Selection Dropdown (hidden when browsing a fixed source, e.g. user scope row) -->
<div v-if="showSourceSelector" class="source-selector" style="margin-bottom: 1rem;">
<label for="destinationSource" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
{{ $t("prompts.destinationSource") }}
@@ -96,6 +96,11 @@ export default {
type: String,
default: null, // Optional initial path to start browsing from
},
/** When true, never show the destination source dropdown (source is fixed via browseSource). */
hideDestinationSource: {
type: Boolean,
default: false,
},
requireFileSelection: {
type: Boolean,
default: false, // If true, only files (not folders) can be selected
@@ -138,6 +143,9 @@ export default {
return state.sources && state.sources.info ? Object.keys(state.sources.info) : [state.req.source];
},
showSourceSelector() {
if (this.hideDestinationSource) {
return false;
}
return this.availableSources.length > 1 && !this.fileList && !getters.isShare() && !this.browseShare;
},
isValidSelection() {
@@ -449,6 +457,7 @@ export default {
let clickedItem = this.items.find(item => item.path === path);
this.selectedSource = clickedItem ? clickedItem.source : this.source;
this.selectedType = clickedItem ? clickedItem.type : null;
const isFile = clickedItem && clickedItem.type !== "directory";
this.$emit("update:selected", {
path: this.selected,
source: this.selectedSource,
@@ -554,13 +554,11 @@ export default {
let items = [];
for (let i of state.selected) {
items.push({
// @ts-ignore
from: state.req.items[i].path,
// @ts-ignore
fromSource: state.req.items[i].source,
// @ts-ignore
to: url.joinPath(this.path, state.req.items[i].name),
toSource: this.source,
name: state.req.items[i].name,
});
}
+6 -120
View File
@@ -96,6 +96,7 @@ import throttle from "@/utils/throttle";
import { previewViews } from "@/utils/constants";
import { url } from "@/utils";
import { resourcesApi } from "@/api";
import { replaceRouteForPlaybackQueueStep } from "@/utils/previewPlaybackQueueNav.js";
export default {
name: "NextPrevious",
@@ -187,13 +188,13 @@ export default {
},
hasPrevious() {
if (this.isMediaQueueMode) {
return this.hasMediaPrevious();
return getters.playbackQueueCanGoPrevious();
}
return state.navigation.previousLink !== "";
},
hasNext() {
if (this.isMediaQueueMode) {
return this.hasMediaNext();
return getters.playbackQueueCanGoNext();
}
return state.navigation.nextLink !== "";
},
@@ -212,14 +213,7 @@ export default {
return previewType === 'audio' || previewType === 'video';
},
isMediaQueueMode() {
const previewType = getters.previewType();
const isMediaView = previewType === 'audio' || previewType === 'video';
const mode = state.playbackQueue?.mode || 'single';
const queueLength = state.playbackQueue?.queue?.length || 0;
const hasQueue = queueLength > 1;
// Use media queue when in media view, NOT in single/loop-single mode, and have a queue
return isMediaView && mode !== 'single' && mode !== 'loop-single' && hasQueue;
return getters.isPreviewPlaybackQueueNavMode();
},
leftZoneStyle() {
const styles = {
@@ -531,7 +525,7 @@ export default {
// Editor and other components check isTransitioning to prevent saves
mutations.setNavigationTransitioning(true);
if (this.isMediaQueueMode) {
this.navigateMediaPrevious();
replaceRouteForPlaybackQueueStep(this.$router, -1);
} else {
this.$router.replace({ path: state.navigation.previousLink });
}
@@ -551,120 +545,12 @@ export default {
mutations.setNavigationTransitioning(true);
if (this.isMediaQueueMode) {
this.navigateMediaNext();
replaceRouteForPlaybackQueueStep(this.$router, 1);
} else {
this.$router.replace({ path: state.navigation.nextLink });
}
}
},
hasMediaPrevious() {
const queue = state.playbackQueue?.queue || [];
const currentIndex = state.playbackQueue?.currentIndex ?? -1;
const mode = state.playbackQueue?.mode || 'single';
if (queue.length <= 1 || currentIndex < 0) return false;
// For sequential mode, no previous if at start
if (mode === 'sequential' && currentIndex === 0) return false;
// For loop-all and shuffle, always have previous (wraps around)
return true;
},
hasMediaNext() {
const queue = state.playbackQueue?.queue || [];
const currentIndex = state.playbackQueue?.currentIndex ?? -1;
const mode = state.playbackQueue?.mode || 'single';
if (queue.length <= 1 || currentIndex < 0) return false;
// For sequential mode, no next if at end
if (mode === 'sequential' && currentIndex >= queue.length - 1) return false;
// For loop-all and shuffle, always have next (wraps around)
return true;
},
navigateMediaPrevious() {
const queue = state.playbackQueue?.queue || [];
const currentIndex = state.playbackQueue?.currentIndex ?? -1;
const mode = state.playbackQueue?.mode || 'single';
if (queue.length === 0 || currentIndex < 0) {
return;
}
let prevIndex = currentIndex - 1;
// Handle wrapping
if (prevIndex < 0) {
if (mode === 'loop-all' || mode === 'shuffle') {
prevIndex = queue.length - 1;
} else {
return;
}
}
const prevItem = queue[prevIndex];
if (!prevItem) {
return;
}
// Update queue index
mutations.setPlaybackQueue({
queue: queue,
currentIndex: prevIndex,
mode: mode
});
// Navigate
const prevItemUrl = url.buildItemUrl(prevItem.source || state.req.source, prevItem.path);
mutations.replaceRequest(prevItem);
this.$router.replace({ path: prevItemUrl }).catch(err => {
if (err.name !== 'NavigationDuplicated') {
// Silently ignore navigation errors
}
});
},
navigateMediaNext() {
const queue = state.playbackQueue?.queue || [];
const currentIndex = state.playbackQueue?.currentIndex ?? -1;
const mode = state.playbackQueue?.mode || 'single';
if (queue.length === 0 || currentIndex < 0) {
return;
}
let nextIndex = currentIndex + 1;
// Handle wrapping
if (nextIndex >= queue.length) {
if (mode === 'loop-all' || mode === 'shuffle') {
nextIndex = 0;
} else {
return;
}
}
const nextItem = queue[nextIndex];
if (!nextItem) {
return;
}
// Update queue index
mutations.setPlaybackQueue({
queue: queue,
currentIndex: nextIndex,
mode: mode
});
// Navigate
const nextItemUrl = url.buildItemUrl(nextItem.source || state.req.source, nextItem.path);
mutations.replaceRequest(nextItem);
this.$router.replace({ path: nextItemUrl }).catch(err => {
if (err.name !== 'NavigationDuplicated') {
// Silently ignore navigation errors
}
});
},
keyEvent(event) {
// Only handle navigation if enabled and no prompt is active
if (!this.enabled || state.prompts.length > 0) {
@@ -3,8 +3,8 @@
<!-- API Token Section -->
<div class="api-section">
<button
class="action copy-clipboard api-key-button"
:data-clipboard-text="name"
class="action api-key-button"
@click.stop="copyToClipboard(name)"
:aria-label="$t('buttons.copyToClipboard')"
:title="$t('buttons.copyToClipboard')"
>
@@ -12,8 +12,8 @@
<i class="material-symbols">content_paste</i>
</button>
<button
class="action copy-clipboard api-key-value-button"
:data-clipboard-text="info.token"
class="action api-key-value-button"
@click.stop="copyToClipboard(info.token)"
:aria-label="$t('api.clickToCopyKey')"
:title="$t('api.clickToCopyKey')"
>
@@ -73,6 +73,7 @@ import { mutations } from "@/store";
import { notify } from "@/notify";
import { authApi } from "@/api";
import { eventBus } from "@/store/eventBus";
import { copyToClipboard } from "@/utils/clipboard";
export default {
name: "ActionApi",
@@ -93,6 +94,9 @@ export default {
},
},
methods: {
async copyToClipboard(text) {
await copyToClipboard(text);
},
formatTime(timestamp) {
return new Date(timestamp * 1000).toLocaleDateString("en-US", {
year: "numeric",
@@ -81,9 +81,12 @@ export default {
closeTopPrompt() {
mutations.closeTopPrompt();
},
confirm() {
this.onConfirm();
mutations.closeTopPrompt();
async confirm() {
try {
await this.onConfirm();
} finally {
mutations.closeTopPrompt();
}
},
},
};
+2 -20
View File
@@ -86,6 +86,7 @@
<script>
import { getHumanReadableFilesize } from "@/utils/filesizes";
import { formatTimestamp } from "@/utils/moment";
import { copyToClipboard } from "@/utils/clipboard";
import { resourcesApi } from "@/api";
import { state } from "@/store";
import { notify } from "@/notify";
@@ -180,26 +181,7 @@ export default {
},
async copyToClipboard() {
if (!this.hashResult) return;
try {
await navigator.clipboard.writeText(this.hashResult);
notify.showSuccessToast(this.$t("prompts.hashCopied"));
} catch (err) {
// Fallback for older browsers
const textArea = document.createElement("textarea");
textArea.value = this.hashResult;
textArea.style.position = "fixed";
textArea.style.opacity = "0";
document.body.appendChild(textArea);
textArea.select();
try {
document.execCommand("copy");
notify.showSuccessToast(this.$t("prompts.hashCopied"));
} catch (e) {
notify.showError(this.$t("prompts.errorCopyingHash"));
}
document.body.removeChild(textArea);
}
await copyToClipboard(this.hashResult);
},
},
};
+14 -2
View File
@@ -78,6 +78,16 @@ export default {
source: state.req?.source || null,
};
},
currentPromptName() {
return getters.currentPromptName();
},
},
watch: {
currentPromptName(newName, oldName) {
if (this.creating && oldName === "replace-rename" && newName !== "new-dir") {
this.creating = false;
}
},
},
methods: {
closeTopPrompt() {
@@ -151,13 +161,15 @@ export default {
if (getters.isShare()) {
await resourcesApi.postPublic(state.shareInfo?.hash, newPath, "", false, undefined, {}, true);
mutations.setReload(true);
mutations.closeTopPrompt();
mutations.closeTopPrompt(); // close conflict prompt
mutations.closeTopPrompt(); // close new dir prompt
success = true;
return;
}
await resourcesApi.post(source, newPath, "", false, undefined, {}, true);
mutations.setReload(true);
mutations.closeTopPrompt();
mutations.closeTopPrompt(); // close conflict prompt
mutations.closeTopPrompt(); // close new dir prompt
success = true;
// Show success notification with "go to item" button
+14 -2
View File
@@ -78,6 +78,16 @@ export default {
source: state.req?.source || null,
};
},
currentPromptName() {
return getters.currentPromptName();
},
},
watch: {
currentPromptName(newName, oldName) {
if (this.creating && oldName === "replace-rename" && newName !== "new-file") {
this.creating = false;
}
},
},
methods: {
async submit(event) {
@@ -148,13 +158,15 @@ export default {
if (getters.isShare()) {
await resourcesApi.postPublic(state.shareInfo?.hash, newPath, "", false);
mutations.setReload(true);
mutations.closeTopPrompt();
mutations.closeTopPrompt(); // close conflict prompt
mutations.closeTopPrompt(); // close new file prompt
success = true;
return;
}
await resourcesApi.post(source, newPath, "", false);
mutations.setReload(true);
mutations.closeTopPrompt();
mutations.closeTopPrompt(); // close conflict prompt
mutations.closeTopPrompt(); // close new file prompt
success = true;
// Show success notification with "go to item" button
+28 -4
View File
@@ -3,6 +3,9 @@
<div v-if="showWrongCredentials" class="form-invalid">
{{ $t("login.wrongCredentials") }}
</div>
<div v-if="infoText">
<p>{{ infoText }}</p>
</div>
<div class="form-flex-group">
<input
class="input share-password"
@@ -10,7 +13,7 @@
type="password"
:placeholder="$t('general.password')"
v-model="password"
@keyup.enter="submit"
@keyup.enter="canSubmit && submit()"
/>
</div>
</div>
@@ -18,11 +21,13 @@
<div class="card-actions">
<button
class="button button--flat"
type="button"
@click="submit"
:aria-label="$t('general.login')"
:title="$t('general.login')"
:disabled="!canSubmit"
:aria-label="submitButtonTitle"
:title="submitButtonTitle"
>
{{ $t("general.login") }}
{{ submitButtonTitle }}
</button>
</div>
</template>
@@ -45,6 +50,22 @@ export default {
type: String,
default: "",
},
infoText: {
type: String,
default: "",
},
submitLabel: {
type: String,
default: "",
},
},
computed: {
submitButtonTitle() {
return this.submitLabel || this.$t("general.login");
},
canSubmit() {
return String(this.password ?? "").trim().length > 0;
},
},
data() {
return {
@@ -53,6 +74,9 @@ export default {
},
methods: {
submit() {
if (!this.canSubmit) {
return;
}
mutations.closeTopPrompt();
this.submitCallback(this.password);
},
+78 -12
View File
@@ -1,15 +1,19 @@
<template>
<div class="card-content">
<file-list ref="fileList" @update:selected="updateSelection" :browseSource="currentSource" :showFiles="showFiles" :showFolders="showFolders">
<file-list ref="fileList" @update:selected="updateSelection" :browseSource="currentSource"
:browse-path="initialBrowsePath" :hide-destination-source="hideDestinationSource" :showFiles="showFiles"
:showFolders="showFolders" :require-file-selection="requireFileSelection"
:allowed-file-types="allowedFileTypes" :title="listTitle">
</file-list>
</div>
<div class="card-actions">
<button class="button button--flat button--grey" @click="closeTopPrompt" :aria-label="$t('general.cancel')"
<button class="button button--flat button--grey" type="button" @click="onCancel" :aria-label="$t('general.cancel')"
:title="$t('general.cancel')">
{{ $t("general.cancel") }}
</button>
<button type="button" class="button button--flat" @click="confirmSelection" :aria-label="$t('general.select')"
<button type="button" class="button button--flat" @click="confirmSelection"
:disabled="requireFileSelection && !selectionIsValid" :aria-label="$t('general.select')"
:title="$t('general.select')">
{{ $t("general.select") }}
</button>
@@ -41,40 +45,102 @@ export default {
type: Boolean,
default: true,
},
hideDestinationSource: {
type: Boolean,
default: false,
},
/** When set, included on pathSelected so listeners can tell this pick apart from other pickers. */
selectionContextId: {
type: String,
default: null,
},
requireFileSelection: {
type: Boolean,
default: false,
},
allowedFileTypes: {
type: Array,
default: null,
},
/** Shown by FileList when set (e.g. share banner/favicon picker). */
listTitle: {
type: String,
default: null,
},
},
data() {
return {
selectedPath: "/",
selectedSource: "",
/** From FileList when requireFileSelection is true (valid file picked). */
selectionIsValid: false,
/** True after confirm or explicit cancel — used to avoid duplicate cancel events. */
selectionFinished: false,
};
},
computed: {
initialBrowsePath() {
const p = this.currentPath;
if (p && typeof p === "string" && p.length > 0) {
return p;
}
return "/";
},
},
mounted() {
// Initialize with current values
this.selectedPath = this.currentPath || "/";
this.selectedSource = this.currentSource || "";
this.selectionIsValid = !this.requireFileSelection;
},
computed: {
closeTopPrompt() {
return mutations.closeTopPrompt();
},
beforeUnmount() {
if (
this.selectionContextId &&
!this.selectionFinished
) {
eventBus.emit("pathPickerCancelled", {
selectionContextId: this.selectionContextId,
});
}
},
methods: {
onCancel() {
this.selectionFinished = true;
if (this.selectionContextId) {
eventBus.emit("pathPickerCancelled", {
selectionContextId: this.selectionContextId,
});
}
mutations.closeTopPrompt();
},
updateSelection(pathOrData) {
// Handle both old format (just path) and new format (object with path and source)
if (typeof pathOrData === 'string') {
this.selectedPath = pathOrData;
this.selectionIsValid = !this.requireFileSelection;
} else if (pathOrData && pathOrData.path) {
this.selectedPath = pathOrData.path;
this.selectedSource = pathOrData.source;
if (Object.prototype.hasOwnProperty.call(pathOrData, 'isValid')) {
this.selectionIsValid = !!pathOrData.isValid;
} else {
this.selectionIsValid = !this.requireFileSelection;
}
}
},
confirmSelection() {
// Emit the selected path and source via eventBus
eventBus.emit('pathSelected', {
if (this.requireFileSelection && !this.selectionIsValid) {
return;
}
this.selectionFinished = true;
const payload = {
path: this.selectedPath,
source: this.selectedSource
});
// Close the prompt
source: this.selectedSource,
};
if (this.selectionContextId) {
payload.selectionContextId = this.selectionContextId;
}
eventBus.emit("pathSelected", payload);
mutations.closeTopPrompt();
},
},
File diff suppressed because it is too large. Load diff
+68 -102
View File
@@ -23,14 +23,7 @@
</div>
</div>
<div v-if="isPickingFile">
<file-list @update:selected="updateTempFilePath" :browse-source="displaySource" :browse-path="displayPath"
:show-files="true" :show-folders="true" :allowed-file-types="filePickerAllowedTypes"
:require-file-selection="true" :title="filePickerTitle">
</file-list>
</div>
<div v-else-if="!isPickingFile">
<div v-else>
<div v-if="isFiles" aria-label="share-path" class="searchContext button"> {{ $t('general.path', { suffix: ':' }) }} {{
displayPath }}</div>
<div v-else>
@@ -64,14 +57,14 @@
</button>
</td>
<td class="small">
<button class="action copy-clipboard" :data-clipboard-text="link.shareURL"
<button class="action" @click.stop="copyToClipboard(link.shareURL)"
:aria-label="$t('buttons.copyToClipboard')" :title="$t('buttons.copyToClipboard')">
<i class="material-symbols">content_paste</i>
</button>
</td>
<td class="small">
<button :disabled="link.shareType == 'upload'" class="action copy-clipboard"
:data-clipboard-text="link.downloadURL" :aria-label="$t('buttons.copyDownloadLinkToClipboard')"
<button :disabled="link.shareType == 'upload'" class="action" @click.stop="copyToClipboard(link.downloadURL)"
:aria-label="$t('buttons.copyDownloadLinkToClipboard')"
:title="$t('buttons.copyDownloadLinkToClipboard')">
<i class="material-symbols">content_paste_go</i>
</button>
@@ -313,7 +306,7 @@
</div>
</div>
<div v-if="!isEditingPath && !isPickingFile" class="card-actions">
<div v-if="!isEditingPath" class="card-actions">
<button v-if="listing" class="button button--flat button--blue" @click="() => switchListing()"
:aria-label="$t('general.new')" :title="$t('general.new')">
{{ $t("general.new") }}
@@ -323,22 +316,12 @@
{{ $t("general.share") }}
</button>
</div>
<div v-if="isPickingFile" class="card-actions">
<button class="button button--flat" @click="cancelFilePicker" :aria-label="$t('general.cancel')"
:title="$t('general.cancel')">
{{ $t("general.cancel") }}
</button>
<button class="button button--flat button--blue" @click="confirmFilePicker" :disabled="!filePickerSelectionValid"
:aria-label="$t('general.ok')" :title="$t('general.ok')">
{{ $t("general.ok") }}
</button>
</div>
</template>
<script>
import { notify } from "@/notify";
import { state, getters, mutations } from "@/store";
import { shareApi } from "@/api";
import Clipboard from "clipboard";
import { copyToClipboard } from "@/utils/clipboard";
import { fromNow } from "@/utils/moment";
import { buildItemUrl } from "@/utils/url";
import ToggleSwitch from "@/components/settings/ToggleSwitch.vue";
@@ -381,8 +364,6 @@ export default {
unit: "hours",
/** @type {Share[]} */
links: [],
/** @type {Clipboard | null} */
clip: null,
password: "",
listing: true,
allowModify: false,
@@ -425,11 +406,9 @@ export default {
tempSource: "",
pathExists: true,
isChangingPassword: false,
isPickingFile: false,
/** Set while a pathPicker for banner/favicon is open; cleared on select/cancel. */
pendingBannerFaviconContextId: null,
filePickerField: null, // 'banner' or 'favicon'
filePickerSelectionValid: false, // Track if a valid file is selected
tempFilePath: "",
tempFileSource: "",
showMoreExpanded: false,
//viewMode: "normal",
};
@@ -490,22 +469,6 @@ export default {
const currentLink = this.isEditMode ? this.link : this.editingLink;
return currentLink && currentLink.hasPassword;
},
filePickerAllowedTypes() {
if (this.filePickerField === 'banner') {
return ['image/']; // Allow all image types
} else if (this.filePickerField === 'favicon') {
return ['image/']; // Allow all image types
}
return [];
},
filePickerTitle() {
if (this.filePickerField === 'banner') {
return this.$t('share.selectBannerImage');
} else if (this.filePickerField === 'favicon') {
return this.$t('share.selectFaviconImage');
}
return this.$t('share.selectImage');
}
},
watch: {
listing(isListing) {
@@ -593,34 +556,21 @@ export default {
}
},
mounted() {
// Initialize clipboard after DOM is ready
this.$nextTick(() => {
this.initClipboard();
});
// Listen for sidebar links updates from the SidebarLinks prompt
eventBus.on('shareSidebarLinksUpdated', this.handleSidebarLinksUpdate);
eventBus.on('pathSelected', this.onBannerFaviconPathSelected);
eventBus.on('pathPickerCancelled', this.onBannerFaviconPathPickerCancelled);
},
beforeUnmount() {
// Clean up event listeners
eventBus.off('apiKeysChanged', this.reloadApiKeys);
eventBus.off('shareSidebarLinksUpdated', this.handleSidebarLinksUpdate);
// Clean up clipboard
if (this.clip) {
this.clip.destroy();
}
eventBus.off('pathSelected', this.onBannerFaviconPathSelected);
eventBus.off('pathPickerCancelled', this.onBannerFaviconPathPickerCancelled);
},
methods: {
initClipboard() {
// Destroy existing clipboard first
if (this.clip) {
this.clip.destroy();
}
// Create new clipboard instance
this.clip = new Clipboard(".copy-clipboard");
this.clip.on("success", () => {
notify.showSuccessToast(this.$t("success.linkCopied"));
});
async copyToClipboard(text) {
await copyToClipboard(text);
},
/**
* @param {MouseEvent} event
@@ -707,10 +657,6 @@ export default {
if (!this.isEditMode && !this.editingLink) {
this.links.push(res);
this.sort();
// reinitialize the clipboard after adding a new link
this.$nextTick(() => {
this.initClipboard();
});
} else if (this.editingLink) {
// Update the link in the local list
const index = this.links.findIndex(l => l.hash === this.editingLink.hash);
@@ -720,10 +666,6 @@ export default {
this.editingLink = null;
// emit event to reload shares in settings view
eventBus.emit('sharesChanged');
// Reinitialize clipboard after edit the share
this.$nextTick(() => {
this.initClipboard();
});
} else {
// emit event to reload shares in settings view
eventBus.emit('sharesChanged');
@@ -798,10 +740,6 @@ export default {
try {
await shareApi.remove(link.hash);
this.links = this.links.filter((item) => item.hash !== link.hash);
// Reinitialize clipboard after deletion too
this.$nextTick(() => {
this.initClipboard();
});
// emit event to reload shares in settings view
eventBus.emit('sharesChanged');
if (this.links.length === 0) {
@@ -933,42 +871,70 @@ export default {
});
},
openBannerPicker() {
const selectionContextId = `share-banner-favicon-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
this.pendingBannerFaviconContextId = selectionContextId;
this.filePickerField = 'banner';
this.tempFilePath = this.displayPath;
this.tempFileSource = this.displaySource;
this.isPickingFile = true;
mutations.showPrompt({
name: 'pathPicker',
pinned: true,
props: {
currentPath: this.displayPath || '/',
currentSource: this.displaySource,
hideDestinationSource: true,
showFiles: true,
showFolders: true,
requireFileSelection: true,
allowedFileTypes: ['image/'],
selectionContextId,
listTitle: this.$t('share.selectBannerImage'),
},
});
},
openFaviconPicker() {
const selectionContextId = `share-banner-favicon-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
this.pendingBannerFaviconContextId = selectionContextId;
this.filePickerField = 'favicon';
this.tempFilePath = this.displayPath;
this.tempFileSource = this.displaySource;
this.isPickingFile = true;
mutations.showPrompt({
name: 'pathPicker',
pinned: true,
props: {
currentPath: this.displayPath || '/',
currentSource: this.displaySource,
hideDestinationSource: true,
showFiles: true,
showFolders: true,
requireFileSelection: true,
allowedFileTypes: ['image/'],
selectionContextId,
listTitle: this.$t('share.selectFaviconImage'),
},
});
},
updateTempFilePath(pathOrData) {
if (pathOrData && pathOrData.path) {
this.tempFilePath = pathOrData.path;
this.tempFileSource = pathOrData.source;
this.filePickerSelectionValid = pathOrData.isValid || false;
/**
* @param {{ path: string, source: string, selectionContextId?: string }} data
*/
onBannerFaviconPathSelected(data) {
if (!this.pendingBannerFaviconContextId || !data || data.selectionContextId !== this.pendingBannerFaviconContextId) {
return;
}
},
confirmFilePicker() {
if (this.tempFilePath && this.tempFileSource) {
// Build query params format: source=sourcename&path=indexpath
const queryParams = `source=${encodeURIComponent(this.tempFileSource)}&path=${encodeURIComponent(this.tempFilePath)}`;
if (this.filePickerField === 'banner') {
this.banner = queryParams;
} else if (this.filePickerField === 'favicon') {
this.favicon = queryParams;
}
const queryParams = `source=${encodeURIComponent(data.source)}&path=${encodeURIComponent(data.path)}`;
if (this.filePickerField === 'banner') {
this.banner = queryParams;
} else if (this.filePickerField === 'favicon') {
this.favicon = queryParams;
}
this.isPickingFile = false;
this.pendingBannerFaviconContextId = null;
this.filePickerField = null;
},
cancelFilePicker() {
this.isPickingFile = false;
/**
* @param {{ selectionContextId?: string }} data
*/
onBannerFaviconPathPickerCancelled(data) {
if (!this.pendingBannerFaviconContextId || !data || data.selectionContextId !== this.pendingBannerFaviconContextId) {
return;
}
this.pendingBannerFaviconContextId = null;
this.filePickerField = null;
this.filePickerSelectionValid = false;
},
},
};
@@ -59,11 +59,9 @@
<!-- Link Type Selection -->
<p>{{ $t('sidebar.linkType') }}</p>
<select aria-label="Link Type" v-model="newLink.category" @change="handleCategoryChange" class="input">
<select aria-label="Link Type" :value="linkTypeSelectValue" @change="onLinkTypeChange" class="input">
<option value="">{{ $t('sidebar.selectLinkType') }}</option>
<option v-if="context === 'user'" value="source">{{ $t('general.source') }}</option>
<option v-if="context === 'user'" value="source-minimal">{{ $t('general.source') }}</option>
<option v-if="context === 'user'" value="source-hybrid">{{ $t('general.source') }}</option>
<option value="share">{{ $t('general.share') }}</option>
<option v-if="context === 'user'" value="tool">{{ $t('general.tool') }}</option>
<option value="custom">{{ $t('sidebar.customLink') }}</option>
@@ -342,6 +340,13 @@ export default {
}
return 'indexed';
},
/** Single "Source" row in the link-type dropdown; actual variant stays in newLink.category (toggles). */
linkTypeSelectValue() {
if (this.isSourceCategory(this.newLink.category)) {
return 'source';
}
return this.newLink.category;
},
},
async mounted() {
// Initialize with existing sidebar links based on context
@@ -496,21 +501,32 @@ export default {
}
return link.name;
},
handleCategoryChange() {
// Reset newLink fields when category changes
resetNewLinkFormForTypeSwitch() {
this.newLink.name = "";
this.newLink.target = "";
this.newLink.icon = "";
this.newLink.sourceName = "";
this.newLink.sourcePath = "";
// Set default name and icon for special share link types
if (this.newLink.category === "shareInfo") {
this.newLink.name = this.$t("share.shareInfo");
this.newLink.icon = "qr_code";
} else if (this.newLink.category === "download") {
this.newLink.name = this.$t("general.download");
this.newLink.icon = "download";
},
onLinkTypeChange(event) {
const value = event.target.value;
if (value === "source") {
if (!this.isSourceCategory(this.newLink.category)) {
this.resetNewLinkFormForTypeSwitch();
this.newLink.category = "source";
}
return;
}
if (value !== this.newLink.category || this.isSourceCategory(this.newLink.category)) {
this.resetNewLinkFormForTypeSwitch();
this.newLink.category = value;
if (value === "shareInfo") {
this.newLink.name = this.$t("share.shareInfo");
this.newLink.icon = "qr_code";
} else if (value === "download") {
this.newLink.name = this.$t("general.download");
this.newLink.icon = "download";
}
}
},
handleSourceChange() {
+27 -6
View File
@@ -7,12 +7,13 @@
<qrcode-vue class="qrcode" :value="url" :size="200" level="M"></qrcode-vue>
</div>
<p>{{ $t("otp.verifyInstructions") }}</p>
<input :class="{'form-invalid': error != '' && code != ''}" v-focus class="input" type="text" v-model="code" @keyup.enter="verifyCode"
<input :class="{'form-invalid': error != '' && code != ''}" v-focus class="input" type="text" v-model="code"
@keydown.enter.prevent="verifyCode"
:placeholder="$t('otp.codeInputPlaceholder')" />
</div>
<div class="card-actions">
<button v-if="!succeeded && code != ''" class="button button--flat button--blue" @click="verifyCode"
<button v-if="!succeeded && code != ''" type="button" class="button button--flat button--blue" @click="verifyCode"
:title="$t('general.verify')">
{{ $t("general.verify") }}
</button>
@@ -37,6 +38,7 @@ export default {
code: "",
url: "",
succeeded: false,
verifyInFlight: false,
};
},
props: {
@@ -59,6 +61,10 @@ export default {
},
async mounted() {
if (this.generate) {
if (String(this.password ?? "").trim() === "") {
this.error = this.$t("otp.passwordRequiredForGeneration");
return;
}
this.generateNewCode();
}
},
@@ -69,6 +75,10 @@ export default {
},
methods: {
async generateNewCode() {
if (String(this.password ?? "").trim() === "") {
this.error = this.$t("otp.passwordRequiredForGeneration");
return;
}
try {
const resp = await authApi.generateOTP(this.username, this.password);
this.url = resp.url;
@@ -78,28 +88,39 @@ export default {
}
},
async verifyCode(event) {
event.preventDefault();
event?.preventDefault?.();
if (this.verifyInFlight || this.succeeded) {
return;
}
if (!this.code) {
this.error = this.$t("otp.invalidCodeType");
notify.showError(this.$t("otp.invalidCodeType"));
return;
}
if (String(this.password ?? "").trim() === "") {
this.error = this.$t("otp.passwordRequiredForGeneration");
notify.showError(this.$t("otp.passwordRequiredForGeneration"));
return;
}
this.verifyInFlight = true;
try {
await authApi.verifyOTP(this.username, this.password, this.code);
if (this.redirect != "") {
await authApi.login(this.username, this.password, this.redirect, this.code);
await initAuth();
this.$router.push(this.redirect);
// Wait for navigation so Layout mounts and registers the toast callback before we toast
await this.$router.push(this.redirect);
}
mutations.updateCurrentUser({ otpEnabled: true });
this.succeeded = true
this.succeeded = true;
this.error = "";
notify.showSuccessToast(this.$t("otp.verificationSucceed"));
mutations.closeTopPrompt();
} catch (error) {
this.error = this.$t("otp.verificationFailed");
console.log("error", error);
return;
} finally {
this.verifyInFlight = false;
}
},
},
+136 -24
View File
@@ -5,7 +5,7 @@
<i class="material-symbols-outlined">sentiment_dissatisfied</i>
<span>{{ $t("files.lonely") }}</span>
</h2>
<div v-if="user.loginMethod == 'password' && globalVars.passwordAvailable && !isNew">
<div v-if="showPasswordChangeSection">
<label for="password">{{ $t("general.password") }}</label>
<div class="form-flex-group">
<input class="input form-form" :class="{ 'form-invalid': invalidPassword }" aria-label="Password1"
@@ -16,7 +16,13 @@
<input class="input form-form" :class="{ 'flat-right': !isNew, 'form-invalid': invalidPassword }"
aria-label="Password2" type="password" autocomplete="new-password"
:placeholder="$t('settings.enterPasswordAgain')" v-model="user.password" id="password" />
<button v-if="!isNew" type="button" class="button form-button flat-left" @click="submitUpdatePassword">
<button
v-if="!isNew"
type="button"
class="button form-button flat-left"
:disabled="!canUpdatePassword"
@click="submitUpdatePassword"
>
{{ $t("general.update") }}
</button>
</div>
@@ -46,7 +52,13 @@
<input class="input form-form" :class="{ 'flat-right': !isNew, 'form-invalid': invalidPassword }"
type="password" :placeholder="$t('settings.enterPasswordAgain')" aria-label="Password2"
v-model="user.password" autocomplete="new-password" id="password" />
<button v-if="!isNew" type="button" class="button form-button flat-left" @click="submitUpdatePassword">
<button
v-if="!isNew"
type="button"
class="button form-button flat-left"
:disabled="!canUpdatePassword"
@click="submitUpdatePassword"
>
{{ $t("general.update") }}
</button>
</div>
@@ -62,16 +74,20 @@
<label for="scopes">{{ $t("settings.scopes") }}</label>
<div class="scope-list" :class="{ 'form-invalid': duplicateSources.includes(source.name) }"
v-for="(source, index) in selectedSources" :key="index">
<select @change="handleSourceChange(source, $event, source.name)" class="input flat-right"
<select @change="handleSourceChange(source, $event, source.name)" class="input flat-right source-dropdown"
v-model="source.name">
<option v-for="s in sourceList" :key="s.name" :value="s.name">
{{ s.name }}
</option>
</select>
<input class="input flat-left scope-input" :placeholder="$t('settings.newUserHintSubFolder')"
@input="updateParent({ source: source, input: $event })" :value="source.scope"
:class="{ 'flat-right': selectedSources.length > 1 }" />
<div
:aria-label="'user-edit-scope-path-' + index"
class="clickable button flat-left scope-path-display"
:class="{ 'flat-right': selectedSources.length > 1 }"
@click="onScopePathRowClick(index, source)"
>{{ scopePathDisplay(source) }}
</div>
<button v-if="selectedSources.length > 1" class="button flat-left no-height" @click="removeScope(index)">
<i class="material-symbols material-size">delete</i>
</button>
@@ -169,12 +185,22 @@ export default {
availableSources: [],
selectedSources: [],
passwordRef: "",
pendingScopeSelectionContextId: null,
pendingScopeIndex: null,
};
},
async created() {
await this.fetchData();
await this.initializeForm();
},
mounted() {
eventBus.on("pathSelected", this.onPathSelectedFromPicker);
eventBus.on("pathPickerCancelled", this.onPathPickerCancelled);
},
beforeUnmount() {
eventBus.off("pathSelected", this.onPathSelectedFromPicker);
eventBus.off("pathPickerCancelled", this.onPathPickerCancelled);
},
computed: {
actor() {
return state.user;
@@ -193,6 +219,15 @@ export default {
this.user.password != this.passwordRef && this.user.password.length > 0;
return matching;
},
/** Update is allowed only when both password fields are non-empty (trimmed) and match. */
canUpdatePassword() {
const a = String(this.passwordRef ?? "").trim();
const b = String(this.user.password ?? "").trim();
if (a.length === 0 || b.length === 0) {
return false;
}
return !this.invalidPassword;
},
passwordAvailable: () => globalVars.passwordAvailable,
globalVars: () => globalVars,
duplicateSources() {
@@ -208,6 +243,15 @@ export default {
displayHomeDirectoryCheckbox() {
return this.isNew && this.createUserDir;
},
/** Password change (existing user): target and signed-in user must both use password login. */
showPasswordChangeSection() {
return (
!this.isNew &&
this.user.loginMethod === "password" &&
this.stateUser.loginMethod === "password" &&
this.globalVars.passwordAvailable
);
},
firstAvailableLoginMethod() {
if (this.globalVars.passwordAvailable) return "password";
if (this.globalVars.oidcAvailable) return "oidc";
@@ -385,30 +429,39 @@ export default {
mutations.closeTopPrompt();
}
} catch (e) {
console.error(e);
notify.showError(e);
}
},
newOTP() {
mutations.showPrompt({
name: "totp",
name: "password",
props: {
generate: true,
username: this.user.username,
password: this.passwordRef || this.user.password || "",
infoText: this.$t("prompts.confirmPasswordToSaveUser"),
submitLabel: this.$t("general.confirm"),
submitCallback: (accountPassword) => {
mutations.showPrompt({
name: "totp",
props: {
generate: true,
username: this.user.username,
password: accountPassword,
},
});
},
},
});
},
async submitUpdatePassword() {
event.preventDefault();
if (this.invalidPassword) {
notify.showError(this.$t("settings.passwordsDoNotMatch"));
if (!this.canUpdatePassword) {
return;
}
try {
await usersApi.update(this.user, ["password"]);
// Only emit usersChanged for admin user management, not profile updates
await usersApi.update(this.user, ["password"], {
actorPasswordPromptI18nKey: "prompts.confirmPasswordToSaveUser",
});
if (state.user.permissions.admin && this.user.id !== state.user.id) {
eventBus.emit('usersChanged');
eventBus.emit("usersChanged");
}
notify.showSuccessToast(this.$t("settings.userUpdated"));
} catch (e) {
@@ -434,13 +487,67 @@ export default {
setUpdatePassword() {
// This method is kept for compatibility but not used in the new structure
},
updateParent(input) {
const updatedScopes = this.selectedSources.map((source) =>
source.name === input.source.name
? { ...source, scope: input.input.target.value }
: source
onScopePathRowClick(index, source) {
if (!source || !source.name) {
return;
}
this.openScopePicker(index);
},
scopePathDisplay(source) {
const s = source && source.scope;
if (s !== undefined && s !== null && String(s).length > 0) {
return s;
}
return "/";
},
openScopePicker(index) {
const row = this.selectedSources[index];
if (!row || !row.name) {
return;
}
const selectionContextId = `user-scope-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
this.pendingScopeSelectionContextId = selectionContextId;
this.pendingScopeIndex = index;
const initialPath =
row.scope && typeof row.scope === "string" && row.scope.length > 0 ? row.scope : "/";
mutations.showPrompt({
name: "pathPicker",
props: {
currentPath: initialPath,
currentSource: row.name,
hideDestinationSource: true,
selectionContextId,
},
});
},
onPathPickerCancelled(data) {
if (!this.pendingScopeSelectionContextId || !data) {
return;
}
if (data.selectionContextId !== this.pendingScopeSelectionContextId) {
return;
}
this.pendingScopeSelectionContextId = null;
this.pendingScopeIndex = null;
},
onPathSelectedFromPicker(data) {
if (!this.pendingScopeSelectionContextId) {
return;
}
if (!data || data.selectionContextId !== this.pendingScopeSelectionContextId) {
return;
}
this.pendingScopeSelectionContextId = null;
const idx = this.pendingScopeIndex;
this.pendingScopeIndex = null;
if (idx === null || idx === undefined || typeof data.path !== "string") {
return;
}
const path = data.path;
const next = this.selectedSources.map((s, i) =>
i === idx ? { ...s, scope: path } : s
);
this.selectedSources = updatedScopes;
this.selectedSources = next;
this.emitUserUpdate();
},
addNewScopeSource(event) {
@@ -498,9 +605,14 @@ export default {
<style scoped>
.scope-list {
display: flex;
align-items: stretch;
}
.scope-input {
.source-dropdown {
width: unset;
}
.scope-path-display {
width: 100%;
}
+2 -3
View File
@@ -39,7 +39,7 @@
</div>
</a>
<!-- Source-type links (source, source-minimal, source-alt, source-hybrid, source-hybrid-2); usage bar hidden for source-minimal -->
<a v-else-if="link.category === 'source' || link.category === 'source-minimal' || link.category === 'source-alt' || link.category === 'source-hybrid' || link.category === 'source-hybrid-2'" :href="getLinkHref(link)"
<a v-else-if="isSourceCategory(link.category)" :href="getLinkHref(link)"
class="action button source-button sidebar-link-button" :class="{
active: isLinkActive(link),
disabled: !isLinkAccessible(link)
@@ -318,8 +318,7 @@ export default {
},
methods: {
isSourceCategory(category) {
return category === 'source' || category === 'source-minimal' || category === 'source-alt' ||
category === 'source-hybrid' || category === 'source-hybrid-2';
return category.startsWith("source")
},
getIconClass,
hasUsageInfo(link) {
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "تم النسخ إلى الحافظة!",
"copyFailed": "فشل النسخ إلى الحافظة",
"copyFile": "نسخ الملف",
"copyToClipboard": "نسخ الى الحافظة",
"moveFile": "نقل الملف",
@@ -33,7 +34,8 @@
"showMore": "إظهار الخيارات المتقدمة",
"showLess": "إخفاء الخيارات المتقدمة",
"goToSource": "الانتقال إلى موقع المصدر",
"goToItem": "انتقل إلى العنصر"
"goToItem": "انتقل إلى العنصر",
"copyPath": "مسار النسخ"
},
"download": {
"downloadFile": "Download File",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "تحديد خوارزمية تجزئة",
"generate": "توليد",
"errorGeneratingHash": "خطأ في توليد التجزئة",
"hashCopied": "التجزئة المنسوخة إلى الحافظة",
"errorCopyingHash": "خطأ في نسخ تجزئة التجزئة",
"token": "الرمز المميز",
"onlyOfficeId": "معرّف المكتب فقط",
"firstLoadTitle": "مرحبًا بك في متصفح الملفات الكمي!",
@@ -226,7 +226,6 @@
"renameSuccess": "تمت إعادة تسمية العنصر بنجاح!",
"newFileSuccess": "تم إنشاء الملف بنجاح!",
"newDirSuccess": "تم إنشاء الدليل بنجاح!",
"copyToClipboardFailed": "تعذر النسخ إلى الحافظة",
"moveFailed": "فشلت عملية النقل",
"operationFailed": "فشلت العملية",
"partialSuccess": "فشلت بعض العمليات: {succeeded} نجحت ، {failed} فشلت ",
@@ -241,14 +240,12 @@
"unarchiveMessage": "اختر مكان استخراج الأرشيف.",
"unarchiveDestination": "مقتطف إلى:",
"unarchiveSuccess": "تم استخراج الأرشيف بنجاح!",
"downloadFailed": "فشل التحميل المقطوع - إذا تكرر ذلك، حاول تقليل حجم القطعة: {message}"
"downloadFailed": "فشل التحميل المقطوع - إذا تكرر ذلك، حاول تقليل حجم القطعة: {message}",
"confirmPasswordToSaveUser": "أدخل كلمة المرور الحالية لحفظ تغييرات المستخدم هذه."
},
"search": {
"smallerThan": "أصغر من:",
"largerThan": "أكبر من:",
"helpText1": "يحدث البحث على كل حرف تكتبه (3 أحرف كحد أدنى لمصطلحات البحث).",
"helpText2": "الفهرس: يستخدم البحث الفهرس الذي يتم تحديثه تلقائيًا على الفاصل الزمني الذي تم تكوينه (افتراضيًا: 5 دقائق). قد يؤدي البحث عندما يكون البرنامج قد بدأ للتو إلى نتائج غير مكتملة.",
"noResults": "لم يتم العثور على نتائج في البحث المفهرس.",
"onlyFolders": "المجلدات فقط",
"onlyFiles": "الملفات فقط",
"showOptions": "عرض الخيارات",
@@ -258,7 +255,10 @@
"showPreviewImages": "عرض صور المعاينة",
"showPreviewImagesDescription": "عرض معاينات الصور المصغرة لنتائج البحث عند توفرها، وإظهار أيقونات أكبر لجميع النتائج.",
"olderThanDate": "أقدم من:",
"newerThanDate": "أحدث من:"
"newerThanDate": "أحدث من:",
"helpText2": "تعتمد نتائج البحث على الملفات المفهرسة، لذلك قد لا تظهر الملفات المضافة حديثاً حتى تتم فهرستها.",
"noResults": "لم يتم العثور على نتائج.",
"helpText1": "يحدث البحث على كل حرف تكتبه ({minSearchLength} حرف كحد أدنى)."
},
"settings": {
"administrator": "Administrator",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "تم تمكين OTP بنجاح",
"verificationFailed": "فشل التحقق",
"verificationSucceed": "نجاح التحقق",
"generationFailed": "فشل التوليد"
"generationFailed": "فشل التوليد",
"passwordRequiredForGeneration": "كلمة المرور الخاصة بك مطلوبة لإنشاء رمز المصادقة."
},
"general": {
"name": "الاسم",
@@ -579,7 +580,6 @@
"results": "النتائج{suffix}",
"users": "المستخدمون{suffix}",
"photos": "الصور",
"audios": "ملفات صوتية{suffix}",
"videos": "مقاطع الفيديو{suffix}",
"models": "نماذج ثلاثية الأبعاد",
"documents": "المستندات{suffix}",
@@ -665,7 +665,8 @@
"format": "التنسيق{suffix}",
"office": "المكتب{suffix}",
"show": "عرض{suffix}",
"divider": "مقسم{suffix}"
"divider": "مقسم{suffix}",
"audio": "الصوت{suffix}"
},
"access": {
"rules": "قواعد الوصول",
@@ -709,14 +710,11 @@
"deleteAfterArchiveDescription": "عند التمكين، سيتم حذف الملفات المصدر بعد إنشاء/استخراج الأرشيفات بنجاح",
"hideFilesInTree": "إخفاء الملفات في شجرة التنقل",
"hideFilesInTreeDescription": "عند التمكين، تظهر المجلدات فقط في التنقل الشجري للشريط الجانبي",
"previewModelsDescription": "عرض صور مصغرة للنماذج ثلاثية الأبعاد، سيتم عرض أيقونة عامة إذا كانت معطلة",
"setDateFormat": "تعيين تنسيق التاريخ الدقيق",
"showHiddenFiles": "إظهار الملفات المخفية",
"showHiddenFilesDescription": "إظهار الملفات المخفية في القائمة (مثل .thumbnails، ProgramData)، ولكن لا يزال يمكن الوصول إليها يدويًا إذا تم تكوينها للفهرسة.",
"showQuickDownload": "إظهار أيقونة التنزيل للوصول السريع",
"showQuickDownloadDescription": "إظهار أيقونة التنزيل للوصول السريع إلى كل عنصر ملف يمكن النقر عليها لتنزيله على الفور.",
"previewImagesDescription": "إظهار معاينات الصور المصغرة للصور، ستظهر أيقونة عامة إذا تم تعطيلها.",
"previewVideosDescription": "إظهار معاينات الصور المصغرة لمقاطع الفيديو، ستظهر أيقونة عامة إذا تم تعطيلها.",
"editOffice": "عرض وتحرير الملفات المكتبية باستخدام OnlyOffice",
"editOfficeDescription": "عند تمكين Office فقط، سيتم تعطيل عارض/محرر الملفات لجميع امتدادات الملفات المتعلقة بملفات Office.",
"filePreviewOptions": "خيارات معاينة الملف",
@@ -761,12 +759,14 @@
"previewFolderDescription": "إظهار معاينات الصور المصغرة للمجلدات التي تحتوي على العنصر الفرعي الأول كغلاف - أو عناصر متعددة إذا تم تمكين معاينة الحركة.",
"previewMotion": "معاينات الحركة",
"previewMotionVideosDescription": "معاينة الحركة هي المعاينة المنبثقة المتحركة التي تتنقل عبر إطارات الفيديو أو الصور المتعددة لمجلد",
"previewAudiosDescription": "عرض صور مصغرة للملفات الصوتية، سيتم عرض أيقونة عامة إذا كانت معطلة",
"popupPreview": "العارض المنبثق",
"popupPreviewDescription": "ستظهر نافذة منبثقة متراكبة فوق واجهة المستخدم عند التمرير فوق العناصر ذات الصور المصغرة",
"thumbnailOptions": "خيارات الصور المصغرة",
"showThumbnails": "إظهار الصور المصغرة للعناصر المدعومة",
"showThumbnailsDescription": "عند التمكين، تظهر الصور المصغرة في القائمة للعناصر التي تدعمها. قم بإيقاف التشغيل لإخفاء جميع الصور المصغرة."
"showThumbnailsDescription": "عند التمكين، تظهر الصور المصغرة في القائمة للعناصر التي تدعمها. قم بإيقاف التشغيل لإخفاء جميع الصور المصغرة.",
"showCopyPath": "إظهار مسار النسخ في قائمة السياق",
"showCopyPathDescription": "إظهار إجراء نسخ المسار في قائمة سياق الملف والمجلد.",
"previewDescription": "إظهار معاينات الصور المصغرة لـ {filetype} ، ستظهر أيقونة عامة إذا تم تعطيلها."
},
"colors": {
"red": "أحمر",
@@ -844,8 +844,6 @@
"showingResults": "عرض {count} النتائج ",
"noResults": "لم يتم العثور على أي أيقونات",
"tryDifferentSearch": "جرّب مصطلح بحث مختلف",
"copiedToClipboard": "نسخ \"{name}\" إلى الحافظة!",
"copyFailed": "فشل النسخ إلى الحافظة",
"custom": "مخصص",
"useCustomIcon": "استخدام أيقونة مخصصة: {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "Zkopírováno do schránky!",
"copyFailed": "Kopírování do schránky se nezdařilo",
"generateNewOtp": "Resetovat a vygenerovat nový kód dvoufázového ověření",
"copyFile": "Kopírovat soubor",
"copyToClipboard": "Kopírovat do schránky",
@@ -33,7 +34,8 @@
"showMore": "Zobrazit rozšířené možnosti",
"showLess": "Skrytí pokročilých možností",
"goToSource": "Přejít na zdrojové umístění",
"goToItem": "Přejít na položku"
"goToItem": "Přejít na položku",
"copyPath": "Kopírovat cestu"
},
"download": {
"downloadFile": "Stáhnout soubor",
@@ -292,8 +294,6 @@
"selectHashAlgorithm": "Výběr algoritmu hash",
"generate": "Generovat",
"errorGeneratingHash": "Chyba při generování hash",
"hashCopied": "Hash zkopírovaný do schránky",
"errorCopyingHash": "Chyba při kopírování hash",
"token": "Token",
"onlyOfficeId": "OnlyOffice ID",
"firstLoadTitle": "Vítejte v aplikaci FileBrowser Quantum!",
@@ -304,7 +304,6 @@
"renameSuccess": "Položka byla úspěšně přejmenována!",
"newFileSuccess": "Soubor byl úspěšně vytvořen!",
"newDirSuccess": "Adresář byl úspěšně vytvořen!",
"copyToClipboardFailed": "Nelze zkopírovat do schránky",
"moveFailed": "Přesun se nezdařil",
"operationFailed": "Operace se nezdařila",
"partialSuccess": "Některé operace se nezdařily: {succeeded} uspěl, {failed} neuspěl.",
@@ -319,14 +318,12 @@
"unarchiveMessage": "Vyberte, kam se má archiv rozbalit.",
"unarchiveDestination": "Výpis na:",
"unarchiveSuccess": "Archiv úspěšně extrahován!",
"downloadFailed": "Stahování po částech se nezdařilo - pokud se tato situace opakuje, zkuste zmenšit velikost části: {message}"
"downloadFailed": "Stahování po částech se nezdařilo - pokud se tato situace opakuje, zkuste zmenšit velikost části: {message}",
"confirmPasswordToSaveUser": "Pro uložení těchto uživatelských změn zadejte své aktuální heslo."
},
"search": {
"smallerThan": "Menší než:",
"largerThan": "Větší než:",
"helpText1": "Vyhledávání probíhá po každém napsaném znaku (minimálně 3 znaky pro hledaný výraz).",
"helpText2": "Index: Vyhledávání využívá index, který se automaticky aktualizuje v nastaveném intervalu (výchozí: 5 minut). Hledání ihned po spuštění programu může vracet neúplné výsledky.",
"noResults": "V indexovaném vyhledávání nebyly nalezeny žádné výsledky.",
"onlyFolders": "Pouze složky",
"onlyFiles": "Pouze soubory",
"showOptions": "Zobrazit možnosti",
@@ -336,7 +333,10 @@
"showPreviewImages": "Zobrazit náhledové obrázky",
"showPreviewImagesDescription": "Zobrazení miniaturních náhledů výsledků vyhledávání, pokud jsou k dispozici, a zobrazení větších ikon u všech výsledků.",
"olderThanDate": "Starší než:",
"newerThanDate": "Novější než:"
"newerThanDate": "Novější než:",
"helpText2": "Výsledky vyhledávání jsou založeny na indexovaných souborech, takže nově přidané soubory se mohou zobrazit až po jejich indexování.",
"noResults": "Nebyly nalezeny žádné výsledky.",
"helpText1": "Vyhledávání probíhá na základě každého zadaného znaku ({minSearchLength} minimálně znaků)."
},
"profileSettings": {
"preferEditorForMarkdown": "Upřednostnit editor pro Markdown soubory",
@@ -345,8 +345,6 @@
"deleteAfterArchiveDescription": "Pokud je povoleno, po úspěšném vytvoření/extrahování archivů dojde ke smazání zdrojových souborů",
"hideFilesInTree": "Skrýt soubory ve stromové navigaci",
"hideFilesInTreeDescription": "Pokud je povoleno, v postranním stromovém navigačním panelu se zobrazují pouze složky",
"previewModelsDescription": "Zobrazit náhledy miniatur pro 3D modely, pokud je zakázáno, zobrazí se obecná ikona",
"previewAudiosDescription": "Zobrazit náhledy miniatur pro zvuky, pokud je zakázáno, zobrazí se obecná ikona",
"setDateFormat": "Nastavit formát data",
"showHiddenFiles": "Zobrazit skryté soubory",
"showQuickDownload": "Zobrazit ikonu rychlého stahování",
@@ -364,8 +362,6 @@
"editorQuickSave": "Zobrazení tlačítka rychlého uložení v editoru",
"showHiddenFilesDescription": "Zobrazení skrytých souborů ve výpisu (např. .thumbnails, ProgramData), ale jsou stále ručně přístupné, pokud jsou nakonfigurovány k indexování.",
"showQuickDownloadDescription": "Zobrazení ikony stahování pro rychlý přístup ke každé položce souboru, na kterou lze kliknout a okamžitě ji stáhnout.",
"previewImagesDescription": "Zobrazení náhledů miniatur obrázků, pokud je zakázáno, zobrazí se obecná ikona.",
"previewVideosDescription": "Zobrazení náhledů miniatur videí, pokud je vypnuto, zobrazí se obecná ikona.",
"editOfficeDescription": "Pokud je povolena pouze funkce office, prohlížeč/editor souborů bude zakázán pro všechny přípony souborů související s kancelářskými soubory.",
"disableUpdateNotificationsDescription": "Zakázat banner s upozorněním na aktualizaci, který se zobrazuje uživatelům správce na postranním panelu.",
"disableHideSidebarDescription": "Je-li tato možnost povolena, postranní panel se při náhledu nebo úpravě souborů automaticky neskryje.",
@@ -402,7 +398,10 @@
"popupPreviewDescription": "Při najetí na položky s miniaturami se nad uživatelským rozhraním zobrazí překryvné vyskakovací okno.",
"thumbnailOptions": "Možnosti miniatur",
"showThumbnails": "Zobrazení miniatur podporovaných položek",
"showThumbnailsDescription": "Pokud je tato možnost povolena, zobrazují se ve výpisu náhledy položek, které je podporují. Vypnutím se všechny miniatury skryjí."
"showThumbnailsDescription": "Pokud je tato možnost povolena, zobrazují se ve výpisu náhledy položek, které je podporují. Vypnutím se všechny miniatury skryjí.",
"showCopyPath": "Zobrazit kopírování cesty v kontextové nabídce",
"showCopyPathDescription": "Zobrazení akce Kopírovat cestu v kontextové nabídce souborů a složek.",
"previewDescription": "Zobrazit náhledy miniatur pro {filetype}, pokud je zakázáno, zobrazí se obecná ikona."
},
"settings": {
"enterPassword": "Zadejte heslo",
@@ -608,7 +607,8 @@
"enabledSuccessfully": "OTP úspěšně aktivováno",
"verificationFailed": "Ověření se nezdařilo",
"verificationSucceed": "Ověření proběhlo úspěšně",
"generationFailed": "Generování selhalo"
"generationFailed": "Generování selhalo",
"passwordRequiredForGeneration": "Pro vygenerování ověřovacího kódu je nutné vaše heslo."
},
"fileLoading": {
"title": "Nahrávání a stahování",
@@ -665,7 +665,6 @@
"results": "Výsledky{suffix}",
"users": "Uživatelé{suffix}",
"photos": "Fotografie",
"audios": "Zvuky{suffix}",
"videos": "Videa{suffix}",
"models": "3D modely",
"documents": "Dokumenty{suffix}",
@@ -751,7 +750,8 @@
"format": "Formát{suffix}",
"office": "Kancelář{suffix}",
"show": "Zobrazit{suffix}",
"divider": "Rozdělovač{suffix}"
"divider": "Rozdělovač{suffix}",
"audio": "Audio{suffix}"
},
"access": {
"rules": "Pravidla přístupu",
@@ -844,8 +844,6 @@
"showingResults": "Zobrazují se výsledky {count} ",
"noResults": "Nebyly nalezeny žádné ikony",
"tryDifferentSearch": "Zkuste použít jiný vyhledávací výraz",
"copiedToClipboard": "Zkopírováno '{name}' do schránky!",
"copyFailed": "Nepodařilo se zkopírovat do schránky",
"custom": "Vlastní",
"useCustomIcon": "Použití vlastní ikony: {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "In die Zwischenablage kopiert!",
"copyFailed": "Kopieren in die Zwischenablage fehlgeschlagen",
"copyFile": "Datei kopieren",
"copyToClipboard": "In Zwischenablage kopieren",
"moveFile": "Datei verschieben",
@@ -33,7 +34,8 @@
"showMore": "Erweiterte Optionen anzeigen",
"showLess": "Erweiterte Optionen ausblenden",
"goToSource": "Zum Quellort gehen",
"goToItem": "Weiter zum Artikel"
"goToItem": "Weiter zum Artikel",
"copyPath": "Pfad kopieren"
},
"download": {
"downloadFile": "Datei herunterladen",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "Wählen Sie einen Hash-Algorithmus",
"generate": "Erzeugen Sie",
"errorGeneratingHash": "Fehler bei der Hash-Erzeugung",
"hashCopied": "Hash in Zwischenablage kopiert",
"errorCopyingHash": "Fehler beim Kopieren des Hashes",
"token": "Token",
"onlyOfficeId": "OnlyOffice ID",
"firstLoadTitle": "Willkommen bei FileBrowser Quantum!",
@@ -226,7 +226,6 @@
"renameSuccess": "Artikel erfolgreich umbenannt!",
"newFileSuccess": "Datei erfolgreich erstellt!",
"newDirSuccess": "Verzeichnis erfolgreich erstellt!",
"copyToClipboardFailed": "Kopieren in die Zwischenablage nicht möglich",
"moveFailed": "Verschieben fehlgeschlagen",
"operationFailed": "Operation fehlgeschlagen",
"partialSuccess": "Einige Operationen sind fehlgeschlagen: {succeeded} erfolgreich, {failed} fehlgeschlagen",
@@ -241,14 +240,12 @@
"unarchiveMessage": "Wählen Sie, wohin das Archiv entpackt werden soll.",
"unarchiveDestination": "Auszug zu:",
"unarchiveSuccess": "Archiv erfolgreich entpackt!",
"downloadFailed": "Chunked Download fehlgeschlagen - falls sich dies wiederholt, versuchen Sie, die Chunk-Größe zu verringern: {message}"
"downloadFailed": "Chunked Download fehlgeschlagen - falls sich dies wiederholt, versuchen Sie, die Chunk-Größe zu verringern: {message}",
"confirmPasswordToSaveUser": "Geben Sie Ihr aktuelles Kennwort ein, um die Benutzeränderungen zu speichern."
},
"search": {
"smallerThan": "Kleiner als:",
"largerThan": "Größer als:",
"helpText1": "Die Suche erfolgt bei jedem Zeichen, das Sie eingeben (mindestens 3 Zeichen für Suchbegriffe).",
"helpText2": "Der Index: Die Suche verwendet den Index, der automatisch in dem konfigurierten Intervall aktualisiert wird (Standard: 5 Minuten). Eine Suche direkt nach dem Start des Programms kann zu unvollständigen Ergebnissen führen.",
"noResults": "Keine Ergebnisse in der indizierten Suche gefunden.",
"onlyFolders": "Nur Ordner",
"onlyFiles": "Nur Dateien",
"showOptions": "Optionen anzeigen",
@@ -258,7 +255,10 @@
"showPreviewImages": "Vorschaubilder anzeigen",
"showPreviewImagesDescription": "Zeigen Sie eine Miniaturvorschau für Suchergebnisse an, wenn verfügbar, und zeigen Sie größere Symbole für alle Ergebnisse an.",
"olderThanDate": "Älter als:",
"newerThanDate": "Neuere als:"
"newerThanDate": "Neuere als:",
"helpText2": "Die Suchergebnisse basieren auf indizierten Dateien, so dass neu hinzugefügte Dateien möglicherweise nicht angezeigt werden, bis sie indiziert sind.",
"noResults": "Keine Ergebnisse gefunden.",
"helpText1": "Die Suche erfolgt bei jedem Zeichen, das Sie eingeben ({minSearchLength} Zeichen Minimum)."
},
"settings": {
"administrator": "Administrator",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "OTP erfolgreich aktiviert",
"verificationFailed": "Verifizierung fehlgeschlagen",
"verificationSucceed": "Überprüfung Erfolgreich",
"generationFailed": "Generation gescheitert"
"generationFailed": "Generation gescheitert",
"passwordRequiredForGeneration": "Ihr Passwort ist erforderlich, um einen Authentifizierungscode zu generieren."
},
"general": {
"name": "Name",
@@ -579,7 +580,6 @@
"results": "Ergebnisse{suffix}",
"users": "Benutzer{suffix}",
"photos": "Fotos",
"audios": "Audios{suffix}",
"videos": "Videos{suffix}",
"models": "3D-Modelle",
"documents": "Dokumente{suffix}",
@@ -665,7 +665,8 @@
"format": "Format{suffix}",
"office": "Büro{suffix}",
"show": "anzeigen{suffix}",
"divider": "Teiler{suffix}"
"divider": "Teiler{suffix}",
"audio": "Audio{suffix}"
},
"access": {
"rules": "Zugangsregeln",
@@ -717,15 +718,11 @@
"deleteAfterArchiveDescription": "Wenn aktiviert, werden die Quelldateien nach erfolgreicher Erstellung/Extraktion von Archiven gelöscht",
"hideFilesInTree": "Dateien in der Baumnavigation ausblenden",
"hideFilesInTreeDescription": "Wenn aktiviert, werden in der Seitenleisten-Baumnavigation nur Ordner angezeigt",
"previewModelsDescription": "Vorschaubilder für 3D-Modelle anzeigen, bei Deaktivierung wird ein generisches Symbol angezeigt",
"previewAudiosDescription": "Vorschaubilder für Audiodateien anzeigen, bei Deaktivierung wird ein generisches Symbol angezeigt",
"setDateFormat": "Genaues Datum statt relativer Zeitangaben verwenden",
"showHiddenFiles": "Versteckte Dateien anzeigen",
"showHiddenFilesDescription": "Versteckte Dateien in der Auflistung anzeigen (z.B. .thumbnails, ProgramData), aber sie sind immer noch manuell zugänglich, wenn sie für die Indizierung konfiguriert sind.",
"showQuickDownload": "Download-Symbol für schnellen Zugriff anzeigen",
"showQuickDownloadDescription": "Zeigen Sie ein Download-Symbol für den schnellen Zugriff auf jedes Dateielement an, das zum sofortigen Herunterladen angeklickt werden kann.",
"previewImagesDescription": "Thumbnail-Vorschau für Bilder anzeigen, bei Deaktivierung wird ein allgemeines Symbol angezeigt.",
"previewVideosDescription": "Thumbnail-Vorschau für Videos anzeigen, bei Deaktivierung wird ein allgemeines Symbol angezeigt.",
"editOffice": "Anzeigen und Bearbeiten von Office-Dateien mit OnlyOffice",
"editOfficeDescription": "Wenn nur Office aktiviert ist, wird der Dateibetrachter/-editor für alle Dateierweiterungen deaktiviert, die mit Office-Dateien zusammenhängen.",
"filePreviewOptions": "Optionen für die Dateivorschau",
@@ -774,7 +771,10 @@
"popupPreviewDescription": "Ein Overlay-Pop-up-Fenster wird über der Benutzeroberfläche angezeigt, wenn der Mauszeiger über Elemente mit Miniaturansichten bewegt wird",
"thumbnailOptions": "Thumbnail-Optionen",
"showThumbnails": "Miniaturansichten für unterstützte Artikel anzeigen",
"showThumbnailsDescription": "Wenn diese Option aktiviert ist, werden Miniaturansichten in der Auflistung für Artikel angezeigt, die sie unterstützen. Schalten Sie die Option aus, um alle Miniaturansichten auszublenden."
"showThumbnailsDescription": "Wenn diese Option aktiviert ist, werden Miniaturansichten in der Auflistung für Artikel angezeigt, die sie unterstützen. Schalten Sie die Option aus, um alle Miniaturansichten auszublenden.",
"showCopyPath": "Pfad kopieren im Kontextmenü anzeigen",
"showCopyPathDescription": "Zeigen Sie eine Aktion \"Pfad kopieren\" im Kontextmenü von Dateien und Ordnern an.",
"previewDescription": "Vorschaubilder für {filetype} anzeigen, bei Deaktivierung wird ein allgemeines Symbol angezeigt."
},
"editor": {
"uninitialized": "Der Editor konnte nicht initialisiert werden. Bitte neu laden.",
@@ -844,8 +844,6 @@
"showingResults": "{count} Ergebnisse anzeigen",
"noResults": "Keine Icons gefunden",
"tryDifferentSearch": "Versuchen Sie einen anderen Suchbegriff",
"copiedToClipboard": "Kopiert '{name}' in die Zwischenablage!",
"copyFailed": "Kopieren in die Zwischenablage fehlgeschlagen",
"custom": "Benutzerdefiniert",
"useCustomIcon": "Benutzerdefiniertes Symbol verwenden: {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "Αντιγράφηκε στο πρόχειρο!",
"copyFailed": "Απέτυχε η αντιγραφή στο πρόχειρο",
"copyFile": "Αντιγραφή αρχείου",
"copyToClipboard": "Αντιγραφή στο πρόχειρο",
"copyDownloadLinkToClipboard": "Αντιγραφή συνδέσμου λήψης στο πρόχειρο",
@@ -33,7 +34,8 @@
"showMore": "Εμφάνιση προηγμένων επιλογών",
"showLess": "Απόκρυψη προηγμένων επιλογών",
"goToSource": "Πηγαίνετε στην τοποθεσία της πηγής",
"goToItem": "Πηγαίνετε στο στοιχείο"
"goToItem": "Πηγαίνετε στο στοιχείο",
"copyPath": "Αντιγραφή διαδρομής"
},
"download": {
"downloadFile": "Λήψη αρχείου",
@@ -217,8 +219,6 @@
"selectHashAlgorithm": "Επιλέξτε έναν αλγόριθμο κατακερματισμού",
"generate": "Δημιουργία",
"errorGeneratingHash": "Σφάλμα δημιουργίας hash",
"hashCopied": "Αντιγραφή κατακερματισμού στο πρόχειρο",
"errorCopyingHash": "Σφάλμα αντιγραφής hash",
"token": "Token",
"onlyOfficeId": "ΜόνοΤαυτότητα γραφείου",
"firstLoadTitle": "Καλώς ήρθατε στο FileBrowser Quantum!",
@@ -229,7 +229,6 @@
"renameSuccess": "Το στοιχείο μετονομάστηκε επιτυχώς!",
"newFileSuccess": "Το αρχείο δημιουργήθηκε με επιτυχία!",
"newDirSuccess": "Ο κατάλογος δημιουργήθηκε με επιτυχία!",
"copyToClipboardFailed": "Αδυναμία αντιγραφής στο πρόχειρο",
"moveFailed": "Η μετακίνηση απέτυχε",
"operationFailed": "Η λειτουργία απέτυχε",
"partialSuccess": "Ορισμένες λειτουργίες απέτυχαν: {succeeded} πέτυχε, {failed} απέτυχε",
@@ -244,14 +243,12 @@
"unarchiveMessage": "Επιλέξτε πού θα εξαγάγετε το αρχείο.",
"unarchiveDestination": "Απόσπασμα προς:",
"unarchiveSuccess": "Το αρχείο εξήχθη με επιτυχία!",
"downloadFailed": "Αποτυχημένη λήψη σε τεμάχια - αν αυτό επαναληφθεί, δοκιμάστε να μειώσετε το μέγεθος των τεμαχίων: {message}"
"downloadFailed": "Αποτυχημένη λήψη σε τεμάχια - αν αυτό επαναληφθεί, δοκιμάστε να μειώσετε το μέγεθος των τεμαχίων: {message}",
"confirmPasswordToSaveUser": "Εισάγετε τον τρέχοντα κωδικό πρόσβασης για να αποθηκεύσετε αυτές τις αλλαγές χρήστη."
},
"search": {
"smallerThan": "Μικρότερο από:",
"largerThan": "Μεγαλύτερο από:",
"helpText1": "Η αναζήτηση πραγματοποιείται σε κάθε χαρακτήρα που πληκτρολογείτε (ελάχιστο όριο 3 χαρακτήρων για τους όρους αναζήτησης).",
"helpText2": "Ο δείκτης: Η αναζήτηση χρησιμοποιεί το ευρετήριο το οποίο ενημερώνεται αυτόματα στο ρυθμισμένο διάστημα (προεπιλογή: 5 λεπτά). Η αναζήτηση όταν το πρόγραμμα έχει μόλις ξεκινήσει μπορεί να οδηγήσει σε ελλιπή αποτελέσματα.",
"noResults": "Δεν βρέθηκαν αποτελέσματα στην ευρετηριασμένη αναζήτηση.",
"onlyFolders": "Μόνο φάκελοι",
"onlyFiles": "Μόνο αρχεία",
"showOptions": "Εμφάνιση επιλογών",
@@ -261,7 +258,10 @@
"showPreviewImages": "Εμφάνιση εικόνων προεπισκόπησης",
"showPreviewImagesDescription": "Εμφάνιση προεπισκοπήσεων μικρογραφιών για τα αποτελέσματα αναζήτησης, όταν είναι διαθέσιμα, και εμφάνιση μεγαλύτερων εικονιδίων για όλα τα αποτελέσματα.",
"olderThanDate": "Μεγαλύτερη από:",
"newerThanDate": "Νεότερο από:"
"newerThanDate": "Νεότερο από:",
"helpText2": "Τα αποτελέσματα της αναζήτησης βασίζονται στα ευρετηριασμένα αρχεία, οπότε τα πρόσφατα προστιθέμενα αρχεία ενδέχεται να μην εμφανίζονται μέχρι να ευρετηριαστούν.",
"noResults": "Δεν βρέθηκαν αποτελέσματα.",
"helpText1": "Η αναζήτηση πραγματοποιείται σε κάθε χαρακτήρα που πληκτρολογείτε ({minSearchLength} ελάχιστος χαρακτήρας)."
},
"settings": {
"administrator": "Διαχειριστής",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "Το OTP ενεργοποιήθηκε με επιτυχία",
"verificationFailed": "Η επαλήθευση απέτυχε",
"verificationSucceed": "Επαλήθευση Succeed",
"generationFailed": "Γενιά που απέτυχε"
"generationFailed": "Γενιά που απέτυχε",
"passwordRequiredForGeneration": "Ο κωδικός πρόσβασής σας απαιτείται για τη δημιουργία ενός κωδικού ελέγχου ταυτότητας."
},
"general": {
"name": "Όνομα",
@@ -579,7 +580,6 @@
"results": "Αποτελέσματα{suffix}",
"users": "Χρήστες{suffix}",
"photos": "Φωτογραφίες",
"audios": "Ήχοι{suffix}",
"videos": "Βίντεο{suffix}",
"models": "Τρισδιάστατα μοντέλα",
"documents": "Έγγραφα{suffix}",
@@ -665,7 +665,8 @@
"format": "Μορφή{suffix}",
"office": "Γραφείο{suffix}",
"show": "Εμφάνιση{suffix}",
"divider": "Διαχωριστικό{suffix}"
"divider": "Διαχωριστικό{suffix}",
"audio": "Ήχος{suffix}"
},
"access": {
"rules": "Κανόνες πρόσβασης",
@@ -717,15 +718,11 @@
"deleteAfterArchiveDescription": "Όταν ενεργοποιηθεί, θα διαγραφούν τα αρχεία προέλευσης μετά την επιτυχή δημιουργία/εξαγωγή αρχείων",
"hideFilesInTree": "Απόκρυψη αρχείων στην πλοήγηση δέντρου",
"hideFilesInTreeDescription": "Όταν ενεργοποιηθεί, μόνο οι φάκελοι εμφανίζονται στην πλευρική δενδρική πλοήγηση",
"previewModelsDescription": "Εμφάνιση μικρογραφιών για τρισδιάστατα μοντέλα, θα εμφανιστεί γενικό εικονίδιο εάν είναι απενεργοποιημένο",
"previewAudiosDescription": "Εμφάνιση μικρογραφιών για ήχους, θα εμφανιστεί γενικό εικονίδιο εάν είναι απενεργοποιημένο",
"setDateFormat": "Ορισμός ακριβούς μορφής ημερομηνίας",
"showHiddenFiles": "Εμφάνιση κρυφών αρχείων",
"showHiddenFilesDescription": "Εμφάνιση κρυμμένων αρχείων στην καταχώριση (π.χ. .thumbnails, ProgramData), αλλά εξακολουθούν να είναι προσβάσιμα χειροκίνητα, εάν έχουν ρυθμιστεί για ευρετηρίαση.",
"showQuickDownload": "Εμφάνιση εικονιδίου λήψης για γρήγορη πρόσβαση",
"showQuickDownloadDescription": "Εμφάνιση εικονιδίου λήψης για γρήγορη πρόσβαση σε κάθε στοιχείο αρχείου που μπορεί να πατηθεί για άμεση λήψη.",
"previewImagesDescription": "Εμφάνιση προεπισκοπήσεων μικρογραφιών για εικόνες, θα εμφανίζει γενικό εικονίδιο αν είναι απενεργοποιημένο.",
"previewVideosDescription": "Εμφάνιση προεπισκοπήσεων μικρογραφιών για βίντεο, θα εμφανίζει γενικό εικονίδιο αν είναι απενεργοποιημένο.",
"editOffice": "Προβολή και επεξεργασία αρχείων γραφείου με το OnlyOffice",
"editOfficeDescription": "Όταν είναι ενεργοποιημένο μόνο το office, η προβολή/επεξεργασία αρχείων θα είναι απενεργοποιημένη για όλες τις επεκτάσεις αρχείων που σχετίζονται με αρχεία office.",
"filePreviewOptions": "Επιλογές προεπισκόπησης αρχείων",
@@ -774,7 +771,10 @@
"popupPreviewDescription": "Ένα αναδυόμενο παράθυρο επικάλυψης θα εμφανίζεται πάνω από το UI όταν περνάτε το ποντίκι πάνω από αντικείμενα με μικρογραφίες.",
"thumbnailOptions": "Επιλογές μικρογραφιών",
"showThumbnails": "Εμφάνιση μικρογραφιών για υποστηριζόμενα στοιχεία",
"showThumbnailsDescription": "Όταν είναι ενεργοποιημένη, οι μικρογραφίες εμφανίζονται στην καταχώριση για τα στοιχεία που τις υποστηρίζουν. Απενεργοποιήστε την επιλογή για να αποκρύψετε όλες τις μικρογραφίες."
"showThumbnailsDescription": "Όταν είναι ενεργοποιημένη, οι μικρογραφίες εμφανίζονται στην καταχώριση για τα στοιχεία που τις υποστηρίζουν. Απενεργοποιήστε την επιλογή για να αποκρύψετε όλες τις μικρογραφίες.",
"showCopyPath": "Εμφάνιση της διαδρομής αντιγραφής στο μενού περιβάλλοντος",
"showCopyPathDescription": "Εμφάνιση της ενέργειας Αντιγραφή διαδρομής στο μενού περιβάλλοντος αρχείων και φακέλων.",
"previewDescription": "Εμφάνιση προεπισκοπήσεων μικρογραφιών για το {filetype}, θα εμφανίζει γενικό εικονίδιο εάν είναι απενεργοποιημένο."
},
"editor": {
"uninitialized": "Απέτυχε η αρχικοποίηση του συντάκτη. Παρακαλούμε επαναφορτώστε.",
@@ -844,8 +844,6 @@
"showingResults": "Εμφάνιση {count} αποτελεσμάτων",
"noResults": "Δεν βρέθηκαν εικονίδια",
"tryDifferentSearch": "Δοκιμάστε έναν διαφορετικό όρο αναζήτησης",
"copiedToClipboard": "Αντιγραφή του '{name}' στο πρόχειρο!",
"copyFailed": "Απέτυχε η αντιγραφή στο πρόχειρο",
"custom": "Προσαρμοσμένο",
"useCustomIcon": "Χρήση προσαρμοσμένου εικονιδίου: {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+21 -23
View File
@@ -57,7 +57,7 @@
"scopes": "Scopes{suffix}",
"commands": "Commands{suffix}",
"photos": "Photos",
"audios": "Audios{suffix}",
"audio": "Audio{suffix}",
"videos": "Videos{suffix}",
"documents": "Documents{suffix}",
"archives": "Archives{suffix}",
@@ -132,8 +132,10 @@
},
"buttons": {
"copySuccess": "Copied to clipboard!",
"copyFailed": "Failed to copy to clipboard",
"generateNewOtp": "Reset and generate new two-factor code",
"copyFile": "Copy file",
"copyPath": "Copy Path",
"copyToClipboard": "Copy to clipboard",
"copyDownloadLinkToClipboard": "Copy download link to clipboard",
"moveFile": "Move file",
@@ -439,9 +441,6 @@
"selectHashAlgorithm": "Select a hash algorithm",
"generate": "Generate",
"errorGeneratingHash": "Error generating hash",
"hashCopied": "Hash copied to clipboard",
"copyToClipboardFailed": "Unable to copy to clipboard",
"errorCopyingHash": "Error copying hash",
"token": "Token",
"onlyOfficeId": "OnlyOffice ID",
"firstLoadTitle": "Welcome to FileBrowser Quantum!",
@@ -450,7 +449,8 @@
"halloweenBody": "The Halloween theme has been applied. You can disable it if you don't like it, or permanently disable it in profile settings by selecting any other theme.",
"moveFailed": "Move failed",
"operationFailed": "Operation failed",
"partialSuccess": "Some operations failed: {succeeded} succeeded, {failed} failed"
"partialSuccess": "Some operations failed: {succeeded} succeeded, {failed} failed",
"confirmPasswordToSaveUser": "Enter your current password to save these user changes."
},
"search": {
"searchContext": "Search Context: {context}",
@@ -459,15 +459,15 @@
"showOptions": "Show Options",
"notEnoughCharacters": "Not enough characters to search (min {minSearchLength})",
"typeToSearch": "Start typing {minSearchLength} or more characters to begin searching.",
"noResults": "No results found in indexed search.",
"noResults": "No results found.",
"smallerThan": "Smaller than:",
"largerThan": "Larger than:",
"olderThanDate": "Older than:",
"newerThanDate": "Newer than:",
"showPreviewImages": "Show preview images",
"showPreviewImagesDescription": "Display thumbnail previews for search results when available, and show larger icons for all results.",
"helpText1": "Search occurs on each character you type (3 character minimum for search terms).",
"helpText2": "The index: Search utilizes the index which automatically gets updated on the configured interval (default: 5 minutes). Searching when the program has just started may result in incomplete results."
"helpText1": "Search occurs on each character you type ({minSearchLength} character minimum).",
"helpText2": "Search results are based on indexed files, so newly added files may not appear until they are indexed."
},
"profileSettings": {
"preferEditorForMarkdown": "Prefer editor first for Markdown files",
@@ -491,10 +491,7 @@
"showHiddenFilesDescription": "Show hidden files in listing (e.g. .thumbnails, ProgramData), but they are still manually accessible if configured to be indexed.",
"showQuickDownload": "Show download icon for quick access",
"showQuickDownloadDescription": "Show download icon for quick access on each file item that can be clicked to download instantly.",
"previewImagesDescription": "Show thumbnail previews for images, will show generic icon if disabled.",
"previewVideosDescription": "Show thumbnail previews for videos, will show generic icon if disabled.",
"previewAudiosDescription": "Show thumbnail previews for audios, will show generic icon if disabled.",
"previewModelsDescription": "Show thumbnail previews for 3D Models, will show generic icon if disabled.",
"previewDescription": "Show thumbnail previews for {filetype}, will show generic icon if disabled.",
"previewMotion": "Motion previews",
"previewMotionVideosDescription": "Motion preview is the animated popup preview that cycles through the frames of the video or multiple images for a folder",
"editOffice": "View and edit office files with OnlyOffice",
@@ -515,6 +512,8 @@
"disableSearchOptions": "Disable search options",
"disableSearchOptionsDescription": "Disable the search options that show up on the sidebar.",
"hideSidebarFileActions": "Hide file actions button in sidebar",
"showCopyPath": "Show Copy Path in context menu",
"showCopyPathDescription": "Show a Copy Path action in the file and folder context menu.",
"deleteWithoutConfirming": "Delete files without confirmation prompt",
"deleteWithoutConfirmingDescription": "Delete files without confirmation prompt. This will only work if the file is a media file.",
"editorViewerOptions": "Editor and viewer options",
@@ -739,7 +738,8 @@
"enabledSuccessfully": "OTP enabled successfully",
"verificationFailed": "Verification Failed",
"verificationSucceed": "Verification Succeed",
"generationFailed": "Generation Failed"
"generationFailed": "Generation Failed",
"passwordRequiredForGeneration": "Your password is required to generate an authenticator code."
},
"access": {
"all": "Deny All",
@@ -785,13 +785,13 @@
"orange": "orange"
},
"editor": {
"uninitialized":"Failed to initialize the editor. Please reload.",
"saveFailed":"Failed to save file. Please try again.",
"saveAborted":"Save aborted. The file name does not match the active file.",
"saveAbortedMessage":"Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
"syncFailed":"failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption.",
"fileSaved":"File saved!",
"errorSavingFile":"Error saving file: {error}",
"uninitialized": "Failed to initialize the editor. Please reload.",
"saveFailed": "Failed to save file. Please try again.",
"saveAborted": "Save aborted. The file name does not match the active file.",
"saveAbortedMessage": "Save operation aborted. The application's active file ({activeFile}) does not match the file you are trying to save ({tryingToSave}).",
"syncFailed": "failed to sync state with the route for \"{filename}\" after 5 attempts. Aborting editor setup to prevent data corruption.",
"fileSaved": "File saved!",
"errorSavingFile": "Error saving file: {error}",
"lines": "{count} lines",
"words": "{count} words",
"chars": "{count} characters"
@@ -854,8 +854,6 @@
"showingResults": "Showing {count} results",
"noResults": "No icons found",
"tryDifferentSearch": "Try a different search term",
"copiedToClipboard": "Copied '{name}' to clipboard!",
"copyFailed": "Failed to copy to clipboard",
"custom": "Custom",
"useCustomIcon": "Use custom icon: {name}"
},
@@ -943,4 +941,4 @@
"editorSuccess": "Editor Initialization - Success! All steps completed.",
"setupError": "Setup Error"
}
}
}
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "¡Copiado al portapapeles!",
"copyFailed": "Error al copiar al portapapeles",
"copyFile": "Copiar archivo",
"copyToClipboard": "Copiar al portapapeles",
"moveFile": "Mover archivo",
@@ -33,7 +34,8 @@
"showMore": "Mostrar opciones avanzadas",
"showLess": "Ocultar opciones avanzadas",
"goToSource": "Ir al origen",
"goToItem": "Abrir"
"goToItem": "Abrir",
"copyPath": "Copiar ruta"
},
"download": {
"downloadFile": "Descargar archivo",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "Selecciona un algoritmo",
"generate": "Generar",
"errorGeneratingHash": "Hubo un error al obtener el hash",
"hashCopied": "¡Se copio el hash al portapapeles!",
"errorCopyingHash": "Hubo un error al copiar el hash",
"token": "Token",
"onlyOfficeId": "OnlyOffice ID",
"firstLoadTitle": "¡Bienvenido a FileBrowser Quantum!",
@@ -226,7 +226,6 @@
"renameSuccess": "El cambio de nombre se ha realizado correctamente",
"newFileSuccess": "¡Archivo creado con éxito!",
"newDirSuccess": "¡Carpeta creada con éxito!",
"copyToClipboardFailed": "Hubo un error al copiar en el portapapeles",
"moveFailed": "Error al mover",
"operationFailed": "Operación fallida",
"partialSuccess": "Algunas acciones fallaron: {succeeded} se completaron, y {failed} fallaron",
@@ -241,24 +240,25 @@
"unarchiveMessage": "Elije dónde quieres extraer el archivo:",
"unarchiveDestination": "Extraer a:",
"unarchiveSuccess": "¡Archivo extraído con éxito!",
"downloadFailed": "Fallo en la descarga por trozos. Si se repite, intente reducir el tamaño de los trozos: {message}"
"downloadFailed": "Fallo en la descarga por trozos. Si se repite, intente reducir el tamaño de los trozos: {message}",
"confirmPasswordToSaveUser": "Introduzca su contraseña actual para guardar estos cambios de usuario."
},
"search": {
"smallerThan": "Menor que:",
"largerThan": "Mayor que:",
"helpText1": "La busqueda se realiza por cada letra que escribes (empezando de {minSearchLength} letras)",
"helpText2": "El índice se actualiza automáticamente cuando es necesario.",
"onlyFolders": "Solo carpetas",
"onlyFiles": "Solo archivos",
"showOptions": "Mostrar opciones",
"noResults": "No pudimos encontrar lo que buscas...",
"searchContext": "Buscar en: {context}",
"notEnoughCharacters": "Escribe al menos {minSearchLength} letras para empezar a buscar",
"typeToSearch": "Escribe {minSearchLength} letras o más para iniciar la búsqueda",
"showPreviewImages": "Mostrar miniaturas",
"showPreviewImagesDescription": "Mostrar miniaturas para los resultados de búsqueda - También hará los iconos más grandes.",
"olderThanDate": "Más viejo que:",
"newerThanDate": "Más nuevo que:"
"newerThanDate": "Más nuevo que:",
"helpText2": "Los resultados de la búsqueda se basan en los archivos indexados, por lo que es posible que los archivos recién añadidos no aparezcan hasta que se indexen.",
"noResults": "No se han encontrado resultados.",
"helpText1": "La búsqueda se produce en cada carácter que escriba ({minSearchLength} carácter mínimo)."
},
"settings": {
"scopes": "Fuentes a las que el usuario tendrá acceso:",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "¡2FA activado!",
"verificationFailed": "No se pudo autenticar",
"verificationSucceed": "¡Verificación exitosa!",
"generationFailed": "Hubo un error al generar el código"
"generationFailed": "Hubo un error al generar el código",
"passwordRequiredForGeneration": "Su contraseña es necesaria para generar un código autenticador."
},
"general": {
"name": "Nombre",
@@ -579,7 +580,6 @@
"results": "Resultados{suffix}",
"users": "Usuarios{suffix}",
"photos": "Fotos",
"audios": "Audios{suffix}",
"videos": "Vídeos{suffix}",
"models": "Modelos 3D",
"documents": "Documentos{suffix}",
@@ -665,7 +665,8 @@
"format": "Formato{suffix}",
"office": "Office{suffix}",
"show": "Mostrar{suffix}",
"divider": "Divisor{suffix}"
"divider": "Divisor{suffix}",
"audio": "Audio{suffix}"
},
"access": {
"rules": "Reglas de acceso",
@@ -717,8 +718,6 @@
"deleteAfterArchiveDescription": "Cuando está activado, eliminará los archivos originales después de la creación o extracción de archivos",
"hideFilesInTree": "Ocultar archivos en la navegación de árbol",
"hideFilesInTreeDescription": "Cuando está activado, solo se muestrarán carpetas en la navegación del panel lateral",
"previewModelsDescription": "Mostrar miniaturas para modelos 3D, se mostrará un icono genérico si se desactiva",
"previewAudiosDescription": "Mostrar miniaturas para audios, se mostrará un icono genérico si se desactiva",
"autoplayMedia": "Reproducción automática de archivos multimedia",
"autoplayMediaDescription": "Reproduce automaticamente los archivos multimedia al abrirlos",
"sidebarOptions": "Opciones del panel lateral",
@@ -742,8 +741,6 @@
"showHiddenFilesDescription": "Muestra los archivos ocultos. (por ejemplo, .thumbnails, ProgramData), pero siguen siendo accesibles manualmente si están configurados para ser indexados.",
"showQuickDownload": "Icono de descarga rápida",
"showQuickDownloadDescription": "Mostrar icono de descarga rápida en cada archivo",
"previewImagesDescription": "Mostrar miniaturas para imágenes, se mostrará un icono genérico si se desactiva",
"previewVideosDescription": "Mostrar miniaturas para vídeos, se mostrará un icono genérico si se desactiva",
"editOffice": "Ver y editar archivos de ofimática con OnlyOffice",
"editOfficeDescription": "Cuando está activado, el visor/editor de archivos estará desactivado para todas las extensiones de archivo relacionadas con archivos office, en vez, las abrirá en OnlyOffice.",
"filePreviewOptions": "Opciones de previsualización de archivos",
@@ -774,7 +771,10 @@
"popupPreviewDescription": "Se mostrará una ventana emergente superpuesta al pasar el mouse por encima de los elementos con miniaturas.",
"thumbnailOptions": "Opciones de miniaturas",
"showThumbnails": "Mostrar miniaturas soportadas",
"showThumbnailsDescription": "Cuando está activada, se mostrarán miniaturas para todos los archivos soportados. Puedes desactivarlo para desactivar todas las miniaturas."
"showThumbnailsDescription": "Cuando está activada, se mostrarán miniaturas para todos los archivos soportados. Puedes desactivarlo para desactivar todas las miniaturas.",
"showCopyPath": "Mostrar ruta de copia en el menú contextual",
"showCopyPathDescription": "Mostrar una acción Copiar ruta en el menú contextual de archivos y carpetas.",
"previewDescription": "Mostrar vistas previas en miniatura para {filetype}, mostrará el icono genérico si está desactivado."
},
"editor": {
"uninitialized": "No se pudo iniciar el editor",
@@ -844,8 +844,6 @@
"showingResults": "Se encontraron {count} iconos",
"noResults": "No se pudieorn encontrar iconos",
"tryDifferentSearch": "Prueba con otro nombre",
"copiedToClipboard": "¡Se copió '{name}' al portapapeles!",
"copyFailed": "Hubo un error al copiar al portapapeles",
"custom": "Personalizar",
"useCustomIcon": "Utilizar icono personalizado: {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "Copié dans le presse-papiers !",
"copyFailed": "Échec de la copie dans le presse-papiers",
"copyFile": "Copier le fichier",
"copyToClipboard": "Copier dans le presse-papier",
"moveFile": "Déplacer le fichier",
@@ -33,7 +34,8 @@
"showMore": "Afficher les options avancées",
"showLess": "Masquer les options avancées",
"goToSource": "Aller à l'emplacement de la source",
"goToItem": "Aller à l'article"
"goToItem": "Aller à l'article",
"copyPath": "Chemin de la copie"
},
"download": {
"downloadFile": "Télécharger le fichier",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "Sélectionner un algorithme de hachage",
"generate": "Générer",
"errorGeneratingHash": "Erreur dans la génération du hachage",
"hashCopied": "Hash copié dans le presse-papiers",
"errorCopyingHash": "Erreur de copie du hachage",
"token": "Jeton",
"onlyOfficeId": "OnlyOffice ID",
"firstLoadTitle": "Bienvenue à FileBrowser Quantum !",
@@ -226,7 +226,6 @@
"renameSuccess": "L'élément a été renommé avec succès !",
"newFileSuccess": "Fichier créé avec succès !",
"newDirSuccess": "Répertoire créé avec succès !",
"copyToClipboardFailed": "Impossible de copier dans le presse-papiers",
"moveFailed": "Échec du déplacement",
"operationFailed": "Échec de l'opération",
"partialSuccess": "Certaines opérations ont échoué : {succeeded} a réussi, {failed} a échoué",
@@ -241,14 +240,12 @@
"unarchiveMessage": "Choisissez l'endroit où extraire l'archive.",
"unarchiveDestination": "Extrait à :",
"unarchiveSuccess": "L'archive a été extraite avec succès !",
"downloadFailed": "Échec du téléchargement groupé - si cela se répète, essayez de réduire la taille des morceaux : {message}"
"downloadFailed": "Échec du téléchargement groupé - si cela se répète, essayez de réduire la taille des morceaux : {message}",
"confirmPasswordToSaveUser": "Saisissez votre mot de passe actuel pour enregistrer ces changements d'utilisateur."
},
"search": {
"smallerThan": "Plus petit que :",
"largerThan": "Plus grand que :",
"helpText1": "La recherche s'effectue sur chaque caractère saisi (3 caractères minimum pour les termes de recherche).",
"helpText2": "Index : La recherche utilise l'index qui est automatiquement mis à jour à l'intervalle configuré (par défaut : 5 minutes). Une recherche effectuée alors que le programme vient de démarrer peut donner des résultats incomplets.",
"noResults": "Aucun résultat n'a été trouvé dans la recherche indexée.",
"onlyFolders": "Dossiers uniquement",
"onlyFiles": "Uniquement les fichiers",
"showOptions": "Afficher les options",
@@ -258,7 +255,10 @@
"showPreviewImages": "Afficher les images de prévisualisation",
"showPreviewImagesDescription": "Afficher les vignettes des résultats de la recherche lorsqu'elles sont disponibles, et afficher des icônes plus grandes pour tous les résultats.",
"olderThanDate": "Plus vieux que :",
"newerThanDate": "Plus récent que :"
"newerThanDate": "Plus récent que :",
"helpText2": "Les résultats de la recherche sont basés sur les fichiers indexés, de sorte que les fichiers nouvellement ajoutés peuvent ne pas apparaître tant qu'ils n'ont pas été indexés.",
"noResults": "Aucun résultat n'a été trouvé.",
"helpText1": "La recherche s'effectue sur chaque caractère que vous tapez ({minSearchLength} caractère minimum)."
},
"settings": {
"scopes": "Définir les sources autorisées et la portée de l'utilisateur au sein de celles-ci",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "OTP activé avec succès",
"verificationFailed": "Échec de la vérification",
"verificationSucceed": "Vérification réussie",
"generationFailed": "Génération échouée"
"generationFailed": "Génération échouée",
"passwordRequiredForGeneration": "Votre mot de passe est nécessaire pour générer un code d'authentification."
},
"general": {
"name": "Nom",
@@ -579,7 +580,6 @@
"results": "Résultats{suffix}",
"users": "Utilisateurs{suffix}",
"photos": "Photos",
"audios": "Audios{suffix}",
"videos": "Vidéos{suffix}",
"models": "Modèles 3D",
"documents": "Documents{suffix}",
@@ -665,7 +665,8 @@
"format": "Format{suffix}",
"office": "Bureau{suffix}",
"show": "Afficher{suffix}",
"divider": "Diviseur{suffix}"
"divider": "Diviseur{suffix}",
"audio": "Audio{suffix}"
},
"access": {
"rules": "Règles d'accès",
@@ -717,15 +718,11 @@
"deleteAfterArchiveDescription": "Lorsque activé, supprimera les fichiers source après la création/extraction réussie d'archives",
"hideFilesInTree": "Masquer les fichiers dans l'arborescence",
"hideFilesInTreeDescription": "Lorsque activé, seuls les dossiers sont affichés dans l'arborescence de navigation de la barre latérale",
"previewModelsDescription": "Afficher les aperçus miniatures pour les modèles 3D, une icône générique s'affichera si désactivé",
"previewAudiosDescription": "Afficher les aperçus miniatures pour les fichiers audio, une icône générique s'affichera si désactivé",
"setDateFormat": "Définir le format exact de la date",
"showHiddenFiles": "Afficher les fichiers cachés",
"showHiddenFilesDescription": "Affiche les fichiers cachés dans la liste (par exemple, .thumbnails, ProgramData), mais ils restent accessibles manuellement s'ils sont configurés pour être indexés.",
"showQuickDownload": "Afficher l'icône de téléchargement pour un accès rapide",
"showQuickDownloadDescription": "Afficher l'icône de téléchargement pour un accès rapide à chaque élément de fichier sur lequel on peut cliquer pour le télécharger instantanément.",
"previewImagesDescription": "Affiche des aperçus de vignettes pour les images, affichera une icône générique si elle est désactivée.",
"previewVideosDescription": "Affiche des aperçus de vignettes pour les vidéos, affichera une icône générique si elle est désactivée.",
"editOffice": "Visualiser et éditer des fichiers bureautiques avec OnlyOffice",
"editOfficeDescription": "Lorsque l'option \"Office\" est activée, le visualiseur/éditeur de fichiers est désactivé pour toutes les extensions de fichiers liées aux fichiers Office.",
"filePreviewOptions": "Options de prévisualisation des fichiers",
@@ -774,7 +771,10 @@
"popupPreviewDescription": "Une fenêtre contextuelle s'affiche au-dessus de l'interface utilisateur lorsque l'on survole des éléments avec des vignettes.",
"thumbnailOptions": "Options de vignettes",
"showThumbnails": "Afficher les vignettes des éléments pris en charge",
"showThumbnailsDescription": "Lorsque cette option est activée, les vignettes sont affichées dans la liste pour les articles qui les prennent en charge. Désactivez cette option pour masquer toutes les vignettes."
"showThumbnailsDescription": "Lorsque cette option est activée, les vignettes sont affichées dans la liste pour les articles qui les prennent en charge. Désactivez cette option pour masquer toutes les vignettes.",
"showCopyPath": "Afficher Copier le chemin dans le menu contextuel",
"showCopyPathDescription": "Afficher une action Copier le chemin dans le menu contextuel des fichiers et des dossiers.",
"previewDescription": "Affiche des aperçus de vignettes pour {filetype}, affichera l'icône générique si elle est désactivée."
},
"editor": {
"uninitialized": "Échec de l'initialisation de l'éditeur. Veuillez recharger.",
@@ -844,8 +844,6 @@
"showingResults": "Afficher les résultats de {count} ",
"noResults": "Aucune icône trouvée",
"tryDifferentSearch": "Essayez un autre terme de recherche",
"copiedToClipboard": "Copie de '{name}' dans le presse-papiers !",
"copyFailed": "Échec de la copie dans le presse-papiers",
"custom": "Sur mesure",
"useCustomIcon": "Utiliser une icône personnalisée : {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "הועתק ללוח!",
"copyFailed": "העתקה ללוח נכשלה",
"copyFile": "העתק קובץ",
"copyToClipboard": "העתק ללוח",
"moveFile": "העבר קובץ",
@@ -33,7 +34,8 @@
"showMore": "הצג אפשרויות מתקדמות",
"showLess": "הסתר אפשרויות מתקדמות",
"goToSource": "עבור למיקום המקור",
"goToItem": "עבור לפריט"
"goToItem": "עבור לפריט",
"copyPath": "העתק נתיב"
},
"download": {
"downloadFile": "הורד קובץ",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "בחר אלגוריתם hash",
"generate": "ליצור",
"errorGeneratingHash": "שגיאה ביצירת hash",
"hashCopied": "ה-hash הועתק ללוח",
"errorCopyingHash": "שגיאה בהעתקת hash",
"token": "אסימון",
"onlyOfficeId": "מזהה OnlyOffice",
"firstLoadTitle": "ברוכים הבאים ל-FileBrowser Quantum!",
@@ -226,7 +226,6 @@
"renameSuccess": "הפריט שונה בהצלחה!",
"newFileSuccess": "הקובץ נוצר בהצלחה!",
"newDirSuccess": "הספרייה נוצרה בהצלחה!",
"copyToClipboardFailed": "לא ניתן להעתיק ללוח",
"moveFailed": "העברה נכשלה",
"operationFailed": "הפעולה נכשלה",
"partialSuccess": "כמה פעולות נכשלו: {succeeded} הצליחו, {failed} נכשלו",
@@ -241,14 +240,12 @@
"unarchiveMessage": "בחר היכן לחלץ את הארכיון.",
"unarchiveDestination": "חלץ אל:",
"unarchiveSuccess": "הארכיון חולץ בהצלחה!",
"downloadFailed": "ההורדה המקוטעת נכשלה - אם הבעיה חוזרת על עצמה, נסה להקטין את גודל החלקים: {message}"
"downloadFailed": "ההורדה המקוטעת נכשלה - אם הבעיה חוזרת על עצמה, נסה להקטין את גודל החלקים: {message}",
"confirmPasswordToSaveUser": "הזן את הסיסמה הנוכחית שלך כדי לשמור את שינויי המשתמש."
},
"search": {
"smallerThan": "קטן מ:",
"largerThan": "גדול מ:",
"helpText1": "החיפוש מתבצע על כל תו שאתה מקליד (מינימום 3 תווים עבור מונחי חיפוש).",
"helpText2": "האינדקס: החיפוש משתמש באינדקס שמתעדכן אוטומטית במרווח הזמן שהוגדר (ברירת מחדל: 5 דקות). חיפוש בעת הפעלת התוכנית עלול להניב תוצאות חלקיות.",
"noResults": "לא נמצאו תוצאות בחיפוש באינדקס.",
"onlyFolders": "רק תיקיות",
"onlyFiles": "רק קבצים",
"showOptions": "הצג אפשרויות",
@@ -258,7 +255,10 @@
"showPreviewImages": "הצג תמונות מקדימות",
"showPreviewImagesDescription": "הצג תצוגה מקדימה של תמונות ממוזערות עבור תוצאות החיפוש, כאשר הן זמינות, והצג סמלים גדולים יותר עבור כל התוצאות.",
"olderThanDate": "גדול מ:",
"newerThanDate": "מאוחר יותר מ:"
"newerThanDate": "מאוחר יותר מ:",
"helpText2": "תוצאות החיפוש מבוססות על קבצים שעברו אינדוקס, ולכן קבצים שנוספו לאחרונה עשויים שלא להופיע עד שיעברו אינדוקס.",
"noResults": "לא נמצאו תוצאות.",
"helpText1": "החיפוש מתבצע על כל תו שאתה מקליד (מינימום{minSearchLength} )."
},
"settings": {
"administrator": "מנהל ראשי",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "OTP הופעל בהצלחה",
"verificationFailed": "האימות נכשל",
"verificationSucceed": "האימות הצליח",
"generationFailed": "דור שנכשל"
"generationFailed": "דור שנכשל",
"passwordRequiredForGeneration": "יש להזין את הסיסמה שלך כדי ליצור קוד אימות."
},
"general": {
"name": "שם",
@@ -579,7 +580,6 @@
"results": "תוצאות{suffix}",
"users": "משתמשים{suffix}",
"photos": "תמונות",
"audios": "שמע{suffix}",
"videos": "סרטונים{suffix}",
"models": "דגמי תלת-ממד",
"documents": "מסמכים{suffix}",
@@ -665,7 +665,8 @@
"format": "פורמט{suffix}",
"office": "משרד{suffix}",
"show": "הצג{suffix}",
"divider": "מפריד{suffix}"
"divider": "מפריד{suffix}",
"audio": "אודיו{suffix}"
},
"access": {
"rules": "כללי גישה",
@@ -717,15 +718,11 @@
"deleteAfterArchiveDescription": "כאשר מופעל, ימחק את קבצי המקור לאחר יצירה/חילוץ מוצלחים של ארכיונים",
"hideFilesInTree": "הסתר קבצים בניווט עץ",
"hideFilesInTreeDescription": "כאשר מופעל, רק תיקיות מוצגות בניווט העץ של סרגל הצד",
"previewModelsDescription": "הצג תצוגות מקדימות ממוזערות לדגמי תלת-ממד, יוצג אייקון כללי אם מושבת",
"previewAudiosDescription": "הצג תצוגות מקדימות ממוזערות לשמע, יוצג אייקון כללי אם מושבת",
"setDateFormat": "הגדר פורמט תאריך מדויק",
"showHiddenFiles": "הצג קבצים מוסתרים",
"showHiddenFilesDescription": "הצג קבצים מוסתרים ברשימה (לדוגמה, .thumbnails, ProgramData), אך הם עדיין נגישים באופן ידני אם הוגדרו לאינדקס.",
"showQuickDownload": "הצג סמל הורדה לגישה מהירה",
"showQuickDownloadDescription": "הצג סמל הורדה לגישה מהירה על כל פריט קובץ שניתן ללחוץ עליו כדי להוריד באופן מיידי.",
"previewImagesDescription": "הצג תצוגה מקדימה של תמונות, יציג סמל כללי אם האפשרות מושבתת.",
"previewVideosDescription": "הצג תצוגה מקדימה של תמונות ממוזערות עבור סרטונים, יציג סמל כללי אם האפשרות מושבתת.",
"editOffice": "הצג וערוך קבצי Office באמצעות OnlyOffice",
"editOfficeDescription": "כאשר רק Office מופעל, תצוגת/עורך הקבצים יושבת עבור כל סיומות הקבצים הקשורות לקבצי Office.",
"filePreviewOptions": "אפשרויות תצוגה מקדימה של קבצים",
@@ -774,7 +771,10 @@
"popupPreviewDescription": "חלון קופץ יופיע מעל ממשק המשתמש כאשר תעבור עם העכבר על פריטים עם תמונות ממוזערות.",
"thumbnailOptions": "אפשרויות תמונות ממוזערות",
"showThumbnails": "הצג תמונות ממוזערות עבור פריטים נתמכים",
"showThumbnailsDescription": "כאשר אפשרות זו מופעלת, תמונות ממוזערות מוצגות ברשימה עבור פריטים התומכים בהן. כבה את האפשרות כדי להסתיר את כל התמונות הממוזערות."
"showThumbnailsDescription": "כאשר אפשרות זו מופעלת, תמונות ממוזערות מוצגות ברשימה עבור פריטים התומכים בהן. כבה את האפשרות כדי להסתיר את כל התמונות הממוזערות.",
"showCopyPath": "הצג את האפשרות \"העתק נתיב\" בתפריט ההקשר",
"showCopyPathDescription": "הצג את הפעולה \"העתק נתיב\" בתפריט ההקשר של קבצים ותיקיות.",
"previewDescription": "הצג תצוגה מקדימה של תמונות ממוזערות עבור {filetype}; אם האפשרות מושבתת, יוצג סמל כללי."
},
"editor": {
"uninitialized": "לא ניתן לאתחל את העורך. אנא טען מחדש.",
@@ -844,8 +844,6 @@
"showingResults": "הצגת תוצאות {count} ",
"noResults": "לא נמצאו סמלים",
"tryDifferentSearch": "נסה מונח חיפוש אחר",
"copiedToClipboard": "הועתק '{name}' ללוח!",
"copyFailed": "לא ניתן להעתיק ללוח",
"custom": "מותאם אישית",
"useCustomIcon": "השתמש בסמל מותאם אישית: {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "Vágólapra másolva!",
"copyFailed": "Nem sikerült a vágólapra másolni",
"copyFile": "Fájl másolása",
"copyToClipboard": "Másolás vágólapra",
"moveFile": "Fájl mozgatása",
@@ -33,7 +34,8 @@
"showMore": "Speciális beállítások megjelenítése",
"showLess": "Speciális beállítások elrejtése",
"goToSource": "Menj a forráshelyre",
"goToItem": "Tovább a tételhez"
"goToItem": "Tovább a tételhez",
"copyPath": "Másolás útvonal"
},
"download": {
"downloadFile": "Fájl letöltése",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "Válasszon ki egy hash algoritmust",
"generate": "Generálja",
"errorGeneratingHash": "Hiba generáló hash",
"hashCopied": "Hash másolva a vágólapra",
"errorCopyingHash": "Hiba a hash másolásában",
"token": "Token",
"onlyOfficeId": "OnlyOffice ID",
"firstLoadTitle": "Üdvözöljük a FileBrowser Quantum-on!",
@@ -226,7 +226,6 @@
"renameSuccess": "Az elem átnevezése sikeres volt!",
"newFileSuccess": "A fájl sikeresen létrehozva!",
"newDirSuccess": "A könyvtárat sikeresen létrehoztuk!",
"copyToClipboardFailed": "Nem lehet a vágólapra másolni",
"moveFailed": "Áthelyezés sikertelen",
"operationFailed": "A művelet sikertelen",
"partialSuccess": "Néhány művelet sikertelen: {succeeded} sikeres volt, {failed} sikertelen.",
@@ -241,14 +240,12 @@
"unarchiveMessage": "Válassza ki, hová szeretné kinyomtatni az archívumot.",
"unarchiveDestination": "Kivonat a következő címre:",
"unarchiveSuccess": "Az archívum sikeresen kinyomtatva!",
"downloadFailed": "A darabos letöltés sikertelen - ha ez ismétlődik, próbálja meg csökkenteni a darabos letöltés méretét: {message}"
"downloadFailed": "A darabos letöltés sikertelen - ha ez ismétlődik, próbálja meg csökkenteni a darabos letöltés méretét: {message}",
"confirmPasswordToSaveUser": "A felhasználói módosítások mentéséhez adja meg az aktuális jelszavát."
},
"search": {
"smallerThan": "Kisebb, mint:",
"largerThan": "Nagyobb, mint:",
"helpText1": "A keresés minden egyes beírt karakterre történik (minimum 3 karakteres keresőkifejezés).",
"helpText2": "Az index: A keresés az indexet használja, amely automatikusan frissül a beállított időközönként (alapértelmezett: 5 perc). A program indulásakor végzett keresés hiányos eredményeket eredményezhet.",
"noResults": "Az indexelt keresésben nem találtak találatot.",
"onlyFolders": "Csak mappák",
"onlyFiles": "Csak fájlok",
"showOptions": "Beállítások megjelenítése",
@@ -258,7 +255,10 @@
"showPreviewImages": "Előnézeti képek megjelenítése",
"showPreviewImagesDescription": "A keresési találatok miniatűr előnézeteinek megjelenítése, ha rendelkezésre állnak, és nagyobb ikonok megjelenítése az összes találathoz.",
"olderThanDate": "Idősebb, mint:",
"newerThanDate": "Újabb, mint:"
"newerThanDate": "Újabb, mint:",
"helpText2": "A keresési eredmények az indexelt fájlokon alapulnak, ezért előfordulhat, hogy az újonnan hozzáadott fájlok nem jelennek meg, amíg indexelésre nem kerülnek.",
"noResults": "Nem találtak eredményt.",
"helpText1": "A keresés minden egyes beírt karakterre történik ({minSearchLength} minimum karakter)."
},
"settings": {
"administrator": "Adminisztrátor",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "OTP sikeresen engedélyezve",
"verificationFailed": "Ellenőrzés sikertelen",
"verificationSucceed": "Ellenőrzés Succeed",
"generationFailed": "Nemzedék sikertelen"
"generationFailed": "Nemzedék sikertelen",
"passwordRequiredForGeneration": "A hitelesítési kód generálásához a jelszóra van szükség."
},
"general": {
"name": "Név",
@@ -579,7 +580,6 @@
"results": "Eredmények{suffix}",
"users": "Felhasználók{suffix}",
"photos": "Fotók",
"audios": "Hangfájlok{suffix}",
"videos": "Videók{suffix}",
"models": "3D modellek",
"documents": "Dokumentumok{suffix}",
@@ -665,7 +665,8 @@
"format": "Formátum{suffix}",
"office": "Iroda{suffix}",
"show": "Mutasd meg a{suffix}",
"divider": "Választó{suffix}"
"divider": "Választó{suffix}",
"audio": "Audio{suffix}"
},
"access": {
"rules": "Hozzáférési szabályok",
@@ -717,15 +718,11 @@
"deleteAfterArchiveDescription": "Ha engedélyezve van, törli a forrásfájlokat az archívumok sikeres létrehozása/kibontása után",
"hideFilesInTree": "Fájlok elrejtése a fa navigációban",
"hideFilesInTreeDescription": "Ha engedélyezve van, csak mappák jelennek meg az oldalsáv fa navigációjában",
"previewModelsDescription": "Miniatűr előnézetek megjelenítése 3D modellekhez, általános ikon jelenik meg ha letiltott",
"previewAudiosDescription": "Miniatűr előnézetek megjelenítése hangfájlokhoz, általános ikon jelenik meg ha letiltott",
"setDateFormat": "Pontos dátumformátum beállítása",
"showHiddenFiles": "Rejtett fájlok megjelenítése",
"showHiddenFilesDescription": "Rejtett fájlok megjelenítése a listában (pl. .thumbnails, ProgramData), de kézzel továbbra is elérhetőek, ha indexelésre vannak beállítva.",
"showQuickDownload": "Letöltés ikon megjelenítése a gyors hozzáférés érdekében",
"showQuickDownloadDescription": "Letöltés ikon megjelenítése a gyors hozzáférés érdekében minden egyes fájlelemen, amelyre kattintva azonnal letölthető.",
"previewImagesDescription": "Miniatűr előnézetek megjelenítése a képekhez, általános ikon megjelenítése, ha le van tiltva.",
"previewVideosDescription": "Videók miniatűr előnézeteinek megjelenítése, általános ikon megjelenítése, ha le van tiltva.",
"editOffice": "Irodai fájlok megtekintése és szerkesztése az OnlyOffice segítségével",
"editOfficeDescription": "Ha csak az office van engedélyezve, a fájlnézegető/szerkesztő az összes office fájlhoz kapcsolódó fájlkiterjesztés esetében le lesz tiltva.",
"filePreviewOptions": "Fájl előnézeti beállítások",
@@ -774,7 +771,10 @@
"popupPreviewDescription": "Egy felugró ablak jelenik meg a felhasználói felület felett, amikor a miniatűrökkel rendelkező elemek fölé mozgatja a gépet.",
"thumbnailOptions": "Miniatűr beállítások",
"showThumbnails": "A támogatott elemek miniatűrjeinek megjelenítése",
"showThumbnailsDescription": "Ha engedélyezve van, a miniatűrök megjelennek a listában az azokat támogató elemeknél. Ha kikapcsolja, az összes miniatűr elrejti."
"showThumbnailsDescription": "Ha engedélyezve van, a miniatűrök megjelennek a listában az azokat támogató elemeknél. Ha kikapcsolja, az összes miniatűr elrejti.",
"showCopyPath": "Az elérési útvonal másolása a kontextusmenüben",
"showCopyPathDescription": "Az elérési útvonal másolása művelet megjelenítése a fájl és mappa kontextusmenüben.",
"previewDescription": "Miniatűr előnézetek megjelenítése a {filetype}, általános ikon megjelenítése, ha le van tiltva."
},
"editor": {
"uninitialized": "Nem sikerült inicializálni a szerkesztőt. Kérjük, töltse újra.",
@@ -844,8 +844,6 @@
"showingResults": "{count} eredmények megjelenítése",
"noResults": "Nem talált ikonok",
"tryDifferentSearch": "Próbáljon ki egy másik kereső kifejezést",
"copiedToClipboard": "Másolt '{name}' a vágólapra!",
"copyFailed": "Nem sikerült a vágólapra másolni",
"custom": "Custom",
"useCustomIcon": "Egyéni ikon használata: {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+1 -3
View File
@@ -305,9 +305,7 @@
"types": "Tegundir",
"video": "Myndband",
"smallerThan": "Minna en:",
"largerThan": "Stærra en:",
"helpText1": "Leit fer fram við hvern staf sem þú slærð inn (3 stafa lágmark fyrir leitarorð).",
"helpText2": "Vísirinn: Leit nýtir vísinn sem uppfærast sjálfkrafa á stilluðu millibili (sjálfgefið: 5 mínútur). Leit þegar forritið er nýrætt getur leitt til ófullnægjandi niðurstaðna."
"largerThan": "Stærra en:"
},
"profileSettings": {
"preferEditorForMarkdown": "Kjósa ritil fyrst fyrir Markdown skrár",
+16 -18
View File
@@ -57,7 +57,6 @@
"scopes": "Ambiti{suffix}",
"commands": "Comandi{suffix}",
"photos": "Foto",
"audios": "Audio{suffix}",
"videos": "Video{suffix}",
"documents": "Documenti{suffix}",
"archives": "Archivi{suffix}",
@@ -128,10 +127,12 @@
"watching": "Monitoraggio{suffix}",
"lines": "Linee{suffix}",
"lastUpdate": "Ultimo aggiornamento{suffix}",
"latency": "Latenza{suffix}"
"latency": "Latenza{suffix}",
"audio": "Audio{suffix}"
},
"buttons": {
"copySuccess": "Copiato negli appunti!",
"copyFailed": "Copia negli appunti fallita",
"generateNewOtp": "Resetta e genera nuovo codice a due fattori",
"copyFile": "Copia file",
"copyToClipboard": "Copia negli appunti",
@@ -164,7 +165,8 @@
"keepEditing": "Continua a modificare",
"closeAnyway": "Chiudi comunque",
"goToItem": "Vai all'elemento",
"goToSource": "Vai alla posizione sorgente"
"goToSource": "Vai alla posizione sorgente",
"copyPath": "Percorso di copia"
},
"download": {
"downloadFile": "Scarica file",
@@ -438,9 +440,6 @@
"selectHashAlgorithm": "Seleziona un algoritmo hash",
"generate": "Genera",
"errorGeneratingHash": "Errore durante la generazione dell'hash",
"hashCopied": "Hash copiato negli appunti",
"copyToClipboardFailed": "Impossibile copiare negli appunti",
"errorCopyingHash": "Errore durante la copia dell'hash",
"token": "Token",
"onlyOfficeId": "ID OnlyOffice",
"firstLoadTitle": "Benvenuto in FileBrowser Quantum!",
@@ -450,7 +449,8 @@
"moveFailed": "Spostamento fallito",
"operationFailed": "Operazione fallita",
"partialSuccess": "Alcune operazioni sono fallite: {succeeded} riuscite, {failed} fallite",
"downloadFailed": "Il download in chunk non è riuscito: se il problema si ripete, provare a ridurre la dimensione del chunk: {message}"
"downloadFailed": "Il download in chunk non è riuscito: se il problema si ripete, provare a ridurre la dimensione del chunk: {message}",
"confirmPasswordToSaveUser": "Immettere la password attuale per salvare le modifiche apportate all'utente."
},
"search": {
"searchContext": "Contesto di ricerca: {context}",
@@ -459,15 +459,15 @@
"showOptions": "Mostra opzioni",
"notEnoughCharacters": "Caratteri insufficienti per la ricerca (minimo {minSearchLength})",
"typeToSearch": "Digita {minSearchLength} o più caratteri per iniziare la ricerca.",
"noResults": "Nessun risultato trovato nella ricerca indicizzata.",
"smallerThan": "Minore di:",
"largerThan": "Maggiore di:",
"olderThanDate": "Più vecchio di:",
"newerThanDate": "Più recente di:",
"showPreviewImages": "Mostra anteprime immagini",
"showPreviewImagesDescription": "Mostra miniature nei risultati di ricerca quando disponibili e icone grandi per tutti i risultati.",
"helpText1": "La ricerca avviene su ogni carattere digitato (minimo 3 caratteri).",
"helpText2": "Indice: la ricerca utilizza l'indice che si aggiorna automaticamente (default: 5 minuti). Cercare all'avvio del programma potrebbe dare risultati incompleti."
"helpText2": "I risultati della ricerca si basano sui file indicizzati, quindi i file aggiunti di recente potrebbero non apparire finché non vengono indicizzati.",
"noResults": "Nessun risultato trovato.",
"helpText1": "La ricerca avviene per ogni carattere digitato ({minSearchLength} minimo)."
},
"profileSettings": {
"preferEditorForMarkdown": "Preferisci editor per file Markdown",
@@ -491,10 +491,6 @@
"showHiddenFilesDescription": "Mostra i file che iniziano con un punto o quelli di sistema.",
"showQuickDownload": "Mostra icona download rapido",
"showQuickDownloadDescription": "Aggiunge un'icona su ogni file per scaricarlo istantaneamente con un click.",
"previewImagesDescription": "Mostra anteprime per le immagini (mostra icona generica se disabilitato).",
"previewVideosDescription": "Mostra anteprime per i video.",
"previewAudiosDescription": "Mostra anteprime per gli audio.",
"previewModelsDescription": "Mostra anteprime per i modelli 3D.",
"previewMotion": "Anteprime animate",
"previewMotionVideosDescription": "L'anteprima animata cicla i fotogrammi del video o le immagini di una cartella al passaggio del mouse.",
"editOffice": "Visualizza e modifica file office con OnlyOffice",
@@ -533,7 +529,10 @@
"themeAndLanguage": "Tema e Lingua",
"thumbnailOptions": "Opzioni Miniature",
"showThumbnails": "Mostra miniature per elementi supportati",
"showThumbnailsDescription": "Spegni per nascondere tutte le miniature nell'elenco."
"showThumbnailsDescription": "Spegni per nascondere tutte le miniature nell'elenco.",
"showCopyPath": "Mostra Copia percorso nel menu contestuale",
"showCopyPathDescription": "Mostra l'azione Copia percorso nel menu contestuale di file e cartelle.",
"previewDescription": "Mostra le anteprime per {filetype}, mostrerà un'icona generica se disabilitata."
},
"settings": {
"systemAdmin": "Sistema e Admin",
@@ -739,7 +738,8 @@
"enabledSuccessfully": "OTP abilitato con successo",
"verificationFailed": "Verifica fallita",
"verificationSucceed": "Verifica riuscita",
"generationFailed": "Generazione fallita"
"generationFailed": "Generazione fallita",
"passwordRequiredForGeneration": "La password è necessaria per generare un codice autenticatore."
},
"access": {
"all": "Nega Tutto",
@@ -854,8 +854,6 @@
"showingResults": "Mostrando {count} risultati",
"noResults": "Nessuna icona trovata",
"tryDifferentSearch": "Prova un termine diverso",
"copiedToClipboard": "Copiato '{name}' negli appunti!",
"copyFailed": "Copia fallita",
"custom": "Personalizzata",
"useCustomIcon": "Usa icona personalizzata: {name}"
},
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "クリップボードにコピーしました!",
"copyFailed": "クリップボードへのコピーに失敗しました",
"copyFile": "ファイルをコピー",
"copyToClipboard": "クリップボードにコピー",
"moveFile": "ファイルを移動",
@@ -33,7 +34,8 @@
"showMore": "詳細オプションを表示",
"showLess": "詳細オプションを隠す",
"goToSource": "ソース・ロケーションへ",
"goToItem": "アイテムへ"
"goToItem": "アイテムへ",
"copyPath": "コピーパス"
},
"download": {
"downloadFile": "ファイルをダウンロード",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "ハッシュアルゴリズムの選択",
"generate": "生成する",
"errorGeneratingHash": "ハッシュ生成エラー",
"hashCopied": "クリップボードにコピーされたハッシュ",
"errorCopyingHash": "ハッシュのコピーエラー",
"token": "トークン",
"onlyOfficeId": "オンリーオフィスID",
"firstLoadTitle": "FileBrowser Quantumへようこそ!",
@@ -226,7 +226,6 @@
"renameSuccess": "アイテム名の変更に成功しました!",
"newFileSuccess": "ファイルは正常に作成されました!",
"newDirSuccess": "ディレクトリは正常に作成されました!",
"copyToClipboardFailed": "クリップボードにコピーできない",
"moveFailed": "移動に失敗しました",
"operationFailed": "操作失敗",
"partialSuccess": "{succeeded} は成功、{failed} は失敗。",
@@ -241,14 +240,12 @@
"unarchiveMessage": "アーカイブを展開する場所を選択します。",
"unarchiveDestination": "に抜粋する:",
"unarchiveSuccess": "アーカイブは正常に抽出されました!",
"downloadFailed": "チャンクダウンロードに失敗しました - これが繰り返される場合は、チャンクサイズを小さくしてみてください:{message}"
"downloadFailed": "チャンクダウンロードに失敗しました - これが繰り返される場合は、チャンクサイズを小さくしてみてください:{message}",
"confirmPasswordToSaveUser": "現在のパスワードを入力して、ユーザーの変更を保存します。"
},
"search": {
"smallerThan": "より小さい:",
"largerThan": "より大きい:",
"helpText1": "検索は入力した文字ごとに行われます(検索語は最低3文字必要)。",
"helpText2": "インデックス:検索は、設定された間隔(デフォルト:5分)で自動的に更新されるインデックスを利用します。プログラムを開始したばかりの時に検索すると、不完全な結果になることがあります。",
"noResults": "インデックス検索で結果が見つかりませんでした。",
"onlyFolders": "フォルダのみ",
"onlyFiles": "ファイルのみ",
"showOptions": "オプションを表示",
@@ -258,7 +255,10 @@
"showPreviewImages": "プレビュー画像を表示する",
"showPreviewImagesDescription": "検索結果のサムネイルプレビューを表示し、すべての検索結果に対して大きなアイコンを表示します。",
"olderThanDate": "より古い:",
"newerThanDate": "より新しい:"
"newerThanDate": "より新しい:",
"helpText2": "検索結果はインデックスされたファイルに基づいているため、新しく追加されたファイルはインデックスされるまで表示されないことがあります。",
"noResults": "結果は見つからなかった。",
"helpText1": "検索は入力した文字ごとに行われます({minSearchLength} 文字以上)。"
},
"settings": {
"administrator": "管理者",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "OTPの有効化に成功しました",
"verificationFailed": "検証に失敗しました",
"verificationSucceed": "検証に成功しました",
"generationFailed": "生成に失敗しました"
"generationFailed": "生成に失敗しました",
"passwordRequiredForGeneration": "認証コードを生成するにはパスワードが必要です。"
},
"general": {
"name": "名前",
@@ -579,7 +580,6 @@
"results": "結果{suffix}",
"users": "ユーザー{suffix}",
"photos": "写真",
"audios": "オーディオ{suffix}",
"videos": "ビデオ{suffix}",
"models": "3Dモデル",
"documents": "資料{suffix}",
@@ -665,7 +665,8 @@
"format": "フォーマット{suffix}",
"office": "オフィス{suffix}",
"show": "ショー{suffix}",
"divider": "ディバイダー{suffix}"
"divider": "ディバイダー{suffix}",
"audio": "オーディオ{suffix}"
},
"access": {
"rules": "アクセスルール",
@@ -717,15 +718,11 @@
"deleteAfterArchiveDescription": "有効にすると、アーカイブの作成/展開が成功した後にソースファイルを削除します",
"hideFilesInTree": "ツリーナビゲーションでファイルを非表示",
"hideFilesInTreeDescription": "有効にすると、サイドバーのツリーナビゲーションにフォルダーのみが表示されます",
"previewModelsDescription": "3Dモデルのサムネイルプレビューを表示します。無効の場合は一般的なアイコンを表示します",
"previewAudiosDescription": "オーディオのサムネイルプレビューを表示します。無効の場合は一般的なアイコンを表示します",
"setDateFormat": "正確な日付書式を設定する",
"showHiddenFiles": "隠しファイルを表示する",
"showHiddenFilesDescription": "隠しファイル(.thumbnails、ProgramDataなど)をリストに表示します。インデックスが設定されている場合は手動でアクセスできます。",
"showQuickDownload": "クイックアクセス用のダウンロードアイコンを表示する",
"showQuickDownloadDescription": "各ファイルアイテムにクイックアクセス用のダウンロードアイコンを表示し、クリックすると即座にダウンロードできます。",
"previewImagesDescription": "画像のサムネイルプレビューを表示します。無効の場合は一般的なアイコンを表示します。",
"previewVideosDescription": "動画のサムネイルプレビューを表示します。無効の場合は一般的なアイコンを表示します。",
"editOffice": "OnlyOfficeでオフィスファイルを閲覧・編集",
"editOfficeDescription": "OnlyOfficeを有効にすると、オフィスファイルに関連するすべてのファイル拡張子でファイルビューア/エディタが無効になります。",
"filePreviewOptions": "ファイルプレビューオプション",
@@ -774,7 +771,10 @@
"popupPreviewDescription": "サムネイル付きのアイテムにカーソルを合わせると、UIの上にオーバーレイ・ポップアップ・ウィンドウが表示されます。",
"thumbnailOptions": "サムネイルオプション",
"showThumbnails": "対応アイテムのサムネイルを表示",
"showThumbnailsDescription": "有効にすると、サムネイルをサポートしているアイテムのリストにサムネイルが表示されます。オフにするとすべてのサムネイルが非表示になります。"
"showThumbnailsDescription": "有効にすると、サムネイルをサポートしているアイテムのリストにサムネイルが表示されます。オフにするとすべてのサムネイルが非表示になります。",
"showCopyPath": "コンテキストメニューにコピーパスを表示する",
"showCopyPathDescription": "ファイルやフォルダのコンテキストメニューに「パスをコピー」アクションを表示します。",
"previewDescription": "{filetype} のサムネイルプレビューを表示する。無効の場合は一般的なアイコンを表示する。"
},
"editor": {
"uninitialized": "エディタの初期化に失敗しました。再読み込みしてください。",
@@ -844,8 +844,6 @@
"showingResults": "{count} の結果を表示",
"noResults": "アイコンが見つかりません",
"tryDifferentSearch": "別の検索語を試す",
"copiedToClipboard": "{name}' をクリップボードにコピー!",
"copyFailed": "クリップボードへのコピーに失敗しました",
"custom": "カスタム",
"useCustomIcon": "カスタムアイコンを使用する:{name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "클립보드에 복사되었습니다!",
"copyFailed": "클립보드 복사 실패",
"copyFile": "파일 복사",
"copyToClipboard": "클립보드 복사",
"moveFile": "파일 이동",
@@ -33,7 +34,8 @@
"showMore": "고급 옵션 표시",
"showLess": "고급 옵션 숨기기",
"goToSource": "소스 위치로 이동",
"goToItem": "항목으로 이동"
"goToItem": "항목으로 이동",
"copyPath": "경로 복사"
},
"download": {
"downloadFile": "파일 다운로드",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "해시 알고리즘 선택",
"generate": "생성",
"errorGeneratingHash": "해시 생성 오류",
"hashCopied": "클립보드에 해시 복사",
"errorCopyingHash": "해시 복사 오류",
"token": "토큰",
"onlyOfficeId": "OnlyOffice ID",
"firstLoadTitle": "파일브라우저 퀀텀에 오신 것을 환영합니다!",
@@ -226,7 +226,6 @@
"renameSuccess": "아이템 이름 변경에 성공했습니다!",
"newFileSuccess": "파일이 성공적으로 생성되었습니다!",
"newDirSuccess": "디렉토리가 성공적으로 생성되었습니다!",
"copyToClipboardFailed": "클립보드에 복사할 수 없습니다.",
"moveFailed": "이동 실패",
"operationFailed": "작업 실패",
"partialSuccess": "일부 작업 실패: {succeeded} 성공, {failed} 실패",
@@ -241,14 +240,12 @@
"unarchiveMessage": "아카이브를 추출할 위치를 선택합니다.",
"unarchiveDestination": "추출 대상:",
"unarchiveSuccess": "아카이브가 성공적으로 추출되었습니다!",
"downloadFailed": "청크 다운로드에 실패했습니다 - 이 문제가 반복되면 청크 크기를 줄여 보세요: {message}"
"downloadFailed": "청크 다운로드에 실패했습니다 - 이 문제가 반복되면 청크 크기를 줄여 보세요: {message}",
"confirmPasswordToSaveUser": "이러한 사용자 변경 사항을 저장하려면 현재 비밀번호를 입력합니다."
},
"search": {
"smallerThan": "보다 작습니다:",
"largerThan": "보다 큽니다:",
"helpText1": "검색은 입력하는 각 문자(검색어의 경우 최소 3자)에 대해 이루어집니다.",
"helpText2": "색인: 검색은 설정된 간격(기본값: 5분)에 따라 자동으로 업데이트되는 색인을 활용합니다. 프로그램이 막 시작되었을 때 검색하면 불완전한 결과가 나올 수 있습니다.",
"noResults": "색인 검색에서 결과를 찾을 수 없습니다.",
"onlyFolders": "폴더만",
"onlyFiles": "파일만",
"showOptions": "옵션 표시",
@@ -258,7 +255,10 @@
"showPreviewImages": "미리 보기 이미지 표시",
"showPreviewImagesDescription": "가능한 경우 검색 결과의 썸네일 미리 보기를 표시하고 모든 결과에 대해 더 큰 아이콘을 표시합니다.",
"olderThanDate": "이전보다 오래되었습니다:",
"newerThanDate": "보다 최신:"
"newerThanDate": "보다 최신:",
"helpText2": "검색 결과는 색인된 파일을 기반으로 하므로 새로 추가된 파일은 색인이 완료될 때까지 표시되지 않을 수 있습니다.",
"noResults": "결과를 찾을 수 없습니다.",
"helpText1": "검색은 입력하는 각 문자({minSearchLength} 최소 글자 수)에 대해 이루어집니다."
},
"settings": {
"administrator": "관리자",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "OTP 활성화 성공",
"verificationFailed": "인증 실패",
"verificationSucceed": "인증 성공",
"generationFailed": "세대 실패"
"generationFailed": "세대 실패",
"passwordRequiredForGeneration": "인증 코드를 생성하려면 비밀번호가 필요합니다."
},
"general": {
"name": "이름",
@@ -579,7 +580,6 @@
"results": "결과{suffix}",
"users": "사용자{suffix}",
"photos": "사진",
"audios": "오디오{suffix}",
"videos": "동영상{suffix}",
"models": "3D 모델",
"documents": "문서{suffix}",
@@ -665,7 +665,8 @@
"format": "형식{suffix}",
"office": "Office{suffix}",
"show": "표시{suffix}",
"divider": "디바이더{suffix}"
"divider": "디바이더{suffix}",
"audio": "오디오{suffix}"
},
"access": {
"rules": "액세스 규칙",
@@ -717,15 +718,11 @@
"deleteAfterArchiveDescription": "활성화되면 아카이브 생성/추출 성공 후 소스 파일을 삭제합니다",
"hideFilesInTree": "트리 탐색에서 파일 숨기기",
"hideFilesInTreeDescription": "활성화되면 사이드바 트리 탐색에 폴더만 표시됩니다",
"previewModelsDescription": "3D 모델의 썸네일 미리보기를 표시합니다. 비활성화된 경우 일반 아이콘이 표시됩니다",
"previewAudiosDescription": "오디오의 썸네일 미리보기를 표시합니다. 비활성화된 경우 일반 아이콘이 표시됩니다",
"setDateFormat": "정확한 날짜 형식 설정",
"showHiddenFiles": "숨겨진 파일 표시",
"showHiddenFilesDescription": "목록에 숨겨진 파일(예: .썸네일, 프로그램 데이터)을 표시하지만 색인화하도록 구성한 경우 여전히 수동으로 액세스할 수 있습니다.",
"showQuickDownload": "빠른 액세스를 위한 다운로드 아이콘 표시",
"showQuickDownloadDescription": "각 파일 항목에 빠르게 액세스할 수 있는 다운로드 아이콘을 표시하여 클릭하여 즉시 다운로드할 수 있습니다.",
"previewImagesDescription": "이미지의 썸네일 미리보기 표시, 비활성화하면 일반 아이콘이 표시됩니다.",
"previewVideosDescription": "동영상 썸네일 미리보기 표시, 비활성화하면 일반 아이콘이 표시됩니다.",
"editOffice": "OnlyOffice로 오피스 파일 보기 및 편집",
"editOfficeDescription": "오피스만 활성화하면 오피스 파일과 관련된 모든 파일 확장자에 대해 파일 뷰어/편집기가 비활성화됩니다.",
"filePreviewOptions": "파일 미리 보기 옵션",
@@ -774,7 +771,10 @@
"popupPreviewDescription": "썸네일이 있는 항목 위로 마우스를 가져가면 UI 위에 오버레이 팝업 창이 표시됩니다.",
"thumbnailOptions": "썸네일 옵션",
"showThumbnails": "지원되는 항목의 썸네일 표시",
"showThumbnailsDescription": "활성화하면 이를 지원하는 항목의 목록에 썸네일이 표시됩니다. 모든 섬네일을 숨기려면 끄면 됩니다."
"showThumbnailsDescription": "활성화하면 이를 지원하는 항목의 목록에 썸네일이 표시됩니다. 모든 섬네일을 숨기려면 끄면 됩니다.",
"showCopyPath": "컨텍스트 메뉴에 경로 복사 표시",
"showCopyPathDescription": "파일 및 폴더 컨텍스트 메뉴에 경로 복사 작업을 표시합니다.",
"previewDescription": "{filetype} 에 대한 썸네일 미리보기 표시 , 비활성화하면 일반 아이콘이 표시됩니다."
},
"editor": {
"uninitialized": "편집기를 초기화하지 못했습니다. 다시 로드하세요.",
@@ -844,8 +844,6 @@
"showingResults": "{count} 결과 표시",
"noResults": "아이콘을 찾을 수 없습니다.",
"tryDifferentSearch": "다른 검색어를 사용해 보세요.",
"copiedToClipboard": "'{name}' 클립보드에 복사했습니다!",
"copyFailed": "클립보드에 복사하지 못했습니다.",
"custom": "사용자 지정",
"useCustomIcon": "사용자 지정 아이콘을 사용합니다: {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "Gekopieerd naar klembord!",
"copyFailed": "Kopiëren naar klembord mislukt",
"copyFile": "Bestand kopiëren",
"copyToClipboard": "Kopiëren naar klembord",
"moveFile": "Verplaats bestand",
@@ -33,7 +34,8 @@
"showMore": "Geavanceerde opties weergeven",
"showLess": "Geavanceerde opties verbergen",
"goToSource": "Ga naar de bronlocatie",
"goToItem": "Ga naar item"
"goToItem": "Ga naar item",
"copyPath": "Kopieer pad"
},
"download": {
"downloadFile": "Bestand downloaden",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "Selecteer een hash-algoritme",
"generate": "genereren",
"errorGeneratingHash": "Fout bij het genereren van hash",
"hashCopied": "Hash gekopieerd naar klembord",
"errorCopyingHash": "Fout bij het kopiëren van hash",
"token": "Penning",
"onlyOfficeId": "Alleen kantoor-ID",
"firstLoadTitle": "Welkom bij FileBrowser Quantum!",
@@ -226,7 +226,6 @@
"renameSuccess": "Item hernoemd succesvol!",
"newFileSuccess": "Bestand succesvol aangemaakt!",
"newDirSuccess": "Directory succesvol aangemaakt!",
"copyToClipboardFailed": "Kan niet naar klembord kopiëren",
"moveFailed": "Verplaatsen mislukt",
"operationFailed": "Operatie mislukt",
"partialSuccess": "Sommige bewerkingen zijn mislukt: {succeeded} gelukt, {failed} mislukt.",
@@ -241,14 +240,12 @@
"unarchiveMessage": "Kies waar het archief moet worden uitgepakt.",
"unarchiveDestination": "Uittreksel naar:",
"unarchiveSuccess": "Archief met succes uitgepakt!",
"downloadFailed": "Chunkedownload mislukt - als dit zich herhaalt, probeer dan de grootte van de chunk te verkleinen: {message}"
"downloadFailed": "Chunkedownload mislukt - als dit zich herhaalt, probeer dan de grootte van de chunk te verkleinen: {message}",
"confirmPasswordToSaveUser": "Voer uw huidige wachtwoord in om deze gebruikerswijzigingen op te slaan."
},
"search": {
"smallerThan": "Kleiner dan:",
"largerThan": "Groter dan:",
"helpText1": "Er wordt gezocht op elk teken dat je typt (minimaal 3 tekens voor zoektermen).",
"helpText2": "De index: Zoeken maakt gebruik van de index die automatisch wordt bijgewerkt op het ingestelde interval (standaard: 5 minuten). Zoeken als het programma net is gestart, kan onvolledige resultaten opleveren.",
"noResults": "Geen resultaten gevonden in geïndexeerde zoekopdrachten.",
"onlyFolders": "Alleen mappen",
"onlyFiles": "Alleen bestanden",
"showOptions": "Opties weergeven",
@@ -258,7 +255,10 @@
"showPreviewImages": "Voorbeeldafbeeldingen tonen",
"showPreviewImagesDescription": "Toon miniatuurvoorbeelden voor zoekresultaten indien beschikbaar, en toon grotere pictogrammen voor alle resultaten.",
"olderThanDate": "Ouder dan:",
"newerThanDate": "Nieuwer dan:"
"newerThanDate": "Nieuwer dan:",
"helpText2": "Zoekresultaten zijn gebaseerd op geïndexeerde bestanden, dus nieuw toegevoegde bestanden verschijnen mogelijk pas als ze geïndexeerd zijn.",
"noResults": "Geen resultaten gevonden.",
"helpText1": "Er wordt gezocht op elk teken dat je typt (minimaal{minSearchLength} tekens)."
},
"settings": {
"administrator": "Administrator",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "OTP succesvol ingeschakeld",
"verificationFailed": "Verificatie mislukt",
"verificationSucceed": "Verificatie slagen",
"generationFailed": "Generatie mislukt"
"generationFailed": "Generatie mislukt",
"passwordRequiredForGeneration": "Uw wachtwoord is nodig om een verificatiecode te genereren."
},
"fileLoading": {
"title": "Uploaden en downloads",
@@ -571,15 +572,11 @@
"deleteAfterArchiveDescription": "Indien ingeschakeld, worden de bronbestanden verwijderd na succesvol aanmaken/uitpakken van archieven",
"hideFilesInTree": "Bestanden verbergen in boomnavigatie",
"hideFilesInTreeDescription": "Indien ingeschakeld, worden alleen mappen weergegeven in de zijbalk Boomnavigatie",
"previewModelsDescription": "Toon miniatuurweergaven voor 3D-modellen, er wordt een algemeen pictogram getoond indien uitgeschakeld",
"previewAudiosDescription": "Toon miniatuurweergaven voor audiobestanden, er wordt een algemeen pictogram getoond indien uitgeschakeld",
"setDateFormat": "Exact datumformaat instellen",
"showHiddenFiles": "Verborgen bestanden weergeven",
"showHiddenFilesDescription": "Toon verborgen bestanden in de lijst (bijv. .thumbnails, ProgramData), maar ze zijn nog steeds handmatig toegankelijk als ze zijn geconfigureerd om te worden geïndexeerd.",
"showQuickDownload": "Downloadpictogram weergeven voor snelle toegang",
"showQuickDownloadDescription": "Toon een downloadpictogram voor snelle toegang tot elk bestandsitem waarop kan worden geklikt om het meteen te downloaden.",
"previewImagesDescription": "Toon miniatuurvoorbeelden voor afbeeldingen, toont generiek pictogram indien uitgeschakeld.",
"previewVideosDescription": "Toon miniatuurvoorbeelden voor video's, toont generiek pictogram indien uitgeschakeld.",
"editOffice": "Office-bestanden bekijken en bewerken met OnlyOffice",
"editOfficeDescription": "Als alleen office is ingeschakeld, is de bestandsviewer/editor uitgeschakeld voor alle bestandsextensies die gerelateerd zijn aan Office-bestanden.",
"filePreviewOptions": "Opties voor bestandsvoorbeeld",
@@ -628,7 +625,10 @@
"popupPreviewDescription": "Er verschijnt een overlay pop-upvenster over de UI wanneer u met de muis over items met miniaturen beweegt",
"thumbnailOptions": "Miniatuuropties",
"showThumbnails": "Toon miniaturen voor ondersteunde items",
"showThumbnailsDescription": "Als deze optie is ingeschakeld, worden miniaturen weergegeven in de lijst met items die deze ondersteunen. Schakel deze optie uit om alle miniaturen te verbergen."
"showThumbnailsDescription": "Als deze optie is ingeschakeld, worden miniaturen weergegeven in de lijst met items die deze ondersteunen. Schakel deze optie uit om alle miniaturen te verbergen.",
"showCopyPath": "Pad kopiëren tonen in contextmenu",
"showCopyPathDescription": "Een actie Pad kopiëren weergeven in het contextmenu van bestanden en mappen.",
"previewDescription": "Toon miniatuurvoorbeelden voor {filetype}, toont generiek pictogram indien uitgeschakeld."
},
"general": {
"name": "Naam",
@@ -665,7 +665,6 @@
"results": "Resultaten{suffix}",
"users": "Gebruikers{suffix}",
"photos": "Foto's",
"audios": "Audio's{suffix}",
"models": "3D-modellen",
"videos": "Video's{suffix}",
"documents": "Documenten{suffix}",
@@ -751,7 +750,8 @@
"format": "Formaat{suffix}",
"office": "Kantoor{suffix}",
"show": "Toon{suffix}",
"divider": "Verdeler{suffix}"
"divider": "Verdeler{suffix}",
"audio": "Audio{suffix}"
},
"access": {
"rules": "Toegangsregels",
@@ -844,8 +844,6 @@
"showingResults": "Resultaten tonen {count} ",
"noResults": "Geen pictogrammen gevonden",
"tryDifferentSearch": "Probeer een andere zoekterm",
"copiedToClipboard": "Gekopieerd '{name}' naar klembord!",
"copyFailed": "Kopiëren naar klembord mislukt",
"custom": "Aangepast",
"useCustomIcon": "Gebruik een aangepast pictogram: {name}",
"iconLibraryUrl": "lettertypen.google.com/iconen",
+13 -15
View File
@@ -57,7 +57,7 @@
"scopes": "Bereiken{suffix}",
"commands": "Opdrachten{suffix}",
"photos": "Foto's",
"audios": "Audio{suffix}",
"audio": "Audio{suffix}",
"videos": "Video{suffix}",
"documents": "Documenten{suffix}",
"archives": "Archieven{suffix}",
@@ -132,8 +132,10 @@
},
"buttons": {
"copySuccess": "Gekopieerd naar klembord!",
"copyFailed": "Kopiëren naar klembord mislukt",
"generateNewOtp": "Herstellen en nieuwe twee-factorcode genereren",
"copyFile": "Bestand kopiëren",
"copyPath": "Pad kopiëren",
"copyToClipboard": "Kopiëren naar klembord",
"copyDownloadLinkToClipboard": "Downloadlink kopiëren naar klembord",
"moveFile": "Bestand verplaatsen",
@@ -377,6 +379,7 @@
"displayName": "Weergavenaam: ",
"download": "Bestanden downloaden",
"downloadMessage": "Kies het formaat dat u wilt downloaden. ",
"downloadFailed": "Download van brok is mislukt - als dit zich herhaalt, probeer dan de brokgrootte te verkleinen: {message}",
"archive": "Archief maken",
"archiveMessage": "Maak een archief op de server van de geselecteerde items.",
"archiveDestination": "Kies een bestemmingsmap (voer hieronder de naam van het archief in):",
@@ -438,9 +441,6 @@
"selectHashAlgorithm": "Selecteer een hash-algoritme",
"generate": "Genereren",
"errorGeneratingHash": "Fout bij het genereren van hash",
"hashCopied": "Hash gekopieerd naar klembord",
"copyToClipboardFailed": "Kan niet naar klembord kopiëren",
"errorCopyingHash": "Fout bij het kopiëren van hash",
"token": "Token",
"onlyOfficeId": "OnlyOffice-ID",
"firstLoadTitle": "Welkom bij FileBrowser Quantum!",
@@ -450,7 +450,7 @@
"moveFailed": "Verplaatsen mislukt",
"operationFailed": "Bewerking is mislukt",
"partialSuccess": "Enkele bewerkingen zijn mislukt: {succeeded} gelukt, {failed} mislukt.",
"downloadFailed": "Chunkedownload mislukt - als dit zich herhaalt, probeer dan de grootte van de chunk te verkleinen: {message}"
"confirmPasswordToSaveUser": "Voer uw huidige wachtwoord in om deze gebruikerswijzigingen op te slaan."
},
"search": {
"searchContext": "Zoekcontext: {context}",
@@ -459,15 +459,15 @@
"showOptions": "Opties weergeven",
"notEnoughCharacters": "Niet genoeg tekens om te zoeken (min {minSearchLength})",
"typeToSearch": "Begin met het typen van {minSearchLength} of meer tekens om te beginnen met zoeken.",
"noResults": "Geen resultaten gevonden in geïndexeerde zoekopdrachten.",
"noResults": "Geen resultaten gevonden.",
"smallerThan": "Kleiner dan:",
"largerThan": "Groter dan:",
"olderThanDate": "Ouder dan:",
"newerThanDate": "Nieuwer dan:",
"showPreviewImages": "Voorbeeldafbeeldingen tonen",
"showPreviewImagesDescription": "Toon, indien beschikbaar, miniatuurvoorbeelden voor zoekresultaten en toon grotere pictogrammen voor alle resultaten.",
"helpText1": "Er wordt gezocht op elk teken dat u typt (minimaal 3 tekens voor zoektermen).",
"helpText2": "De index: Zoeken maakt gebruik van de index die automatisch wordt bijgewerkt op het ingestelde interval (standaard: 5 minuten). Zoeken als het programma net is gestart, kan onvolledige resultaten opleveren."
"helpText1": "Er wordt gezocht op elk teken dat je typt (minimaal{minSearchLength} tekens).",
"helpText2": "Zoekresultaten zijn gebaseerd op geïndexeerde bestanden, dus nieuw toegevoegde bestanden verschijnen mogelijk pas als ze geïndexeerd zijn."
},
"profileSettings": {
"preferEditorForMarkdown": "Geef de voorkeur aan editor voor Markdown-bestanden",
@@ -491,10 +491,7 @@
"showHiddenFilesDescription": "Toon verborgen bestanden in de lijst (bijv. .thumbnails, ProgramData), maar ze zijn nog steeds handmatig toegankelijk als ze zijn geconfigureerd om te worden geïndexeerd.",
"showQuickDownload": "Downloadpictogram weergeven voor snelle toegang",
"showQuickDownloadDescription": "Toon een downloadpictogram voor snelle toegang tot elk bestandsitem waarop kan worden geklikt om het meteen te downloaden.",
"previewImagesDescription": "Toon miniatuurvoorbeelden voor afbeeldingen, toont generiek pictogram indien uitgeschakeld.",
"previewVideosDescription": "Toon miniatuurvoorbeelden voor video, toont generiek pictogram indien uitgeschakeld.",
"previewAudiosDescription": "Toon miniatuurvoorbeelden voor audio, toont generiek pictogram indien uitgeschakeld.",
"previewModelsDescription": "Toon miniatuurvoorbeelden voor 3D-modellen, toont generiek pictogram indien uitgeschakeld.",
"previewDescription": "Toon miniatuurvoorbeelden voor {filetype}, toont generiek pictogram indien uitgeschakeld.",
"previewMotion": "Bewegingsvoorbeelden",
"previewMotionVideosDescription": "Bewegingsvoorbeeld is het geanimeerde popup-voorbeeld dat door de frames van de video of meerdere afbeeldingen voor een map loopt",
"editOffice": "Office-bestanden bekijken en bewerken met OnlyOffice",
@@ -515,6 +512,8 @@
"disableSearchOptions": "Zoekopties uitschakelen",
"disableSearchOptionsDescription": "Schakel de zoekopties in de zijbalk uit.",
"hideSidebarFileActions": "Verberg de knop Bestandsacties in de zijbalk",
"showCopyPath": "Pad kopiëren tonen in contextmenu",
"showCopyPathDescription": "Een actie Pad kopiëren weergeven in het contextmenu van bestanden en mappen.",
"deleteWithoutConfirming": "Bestanden verwijderen zonder bevestigingsvenster",
"deleteWithoutConfirmingDescription": "Bestanden verwijderen zonder bevestigingsvenster. Dit werkt alleen als het bestand een mediabestand is.",
"editorViewerOptions": "Opties voor editor en viewer",
@@ -739,7 +738,8 @@
"enabledSuccessfully": "OTP succesvol ingeschakeld",
"verificationFailed": "Verificatie is mislukt",
"verificationSucceed": "Verificatie is geslaagd",
"generationFailed": "Aanmaken is mislukt"
"generationFailed": "Aanmaken is mislukt",
"passwordRequiredForGeneration": "Uw wachtwoord is nodig om een verificatiecode te genereren."
},
"access": {
"all": "Alles weigeren",
@@ -854,8 +854,6 @@
"showingResults": "{count} resultaten weergeven",
"noResults": "Geen pictogrammen gevonden",
"tryDifferentSearch": "Probeer een andere zoekterm",
"copiedToClipboard": "'{name}' is gekopieerd naar klembord!",
"copyFailed": "Kopiëren naar klembord mislukt",
"custom": "Aangepast",
"useCustomIcon": "Gebruik een aangepast pictogram: {name}"
},
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "Skopiowano do schowka!",
"copyFailed": "Nie udało się skopiować do schowka",
"copyFile": "Kopiuj plik",
"copyToClipboard": "kopiuj do schowka",
"moveFile": "Przenieś plik",
@@ -33,7 +34,8 @@
"showMore": "Pokaż opcje zaawansowane",
"showLess": "Ukryj opcje zaawansowane",
"goToSource": "Przejdź do lokalizacji źródłowej",
"goToItem": "Przejdź do pozycji"
"goToItem": "Przejdź do pozycji",
"copyPath": "Ścieżka kopiowania"
},
"download": {
"downloadFile": "Pobierz plik",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "Wybór algorytmu skrótu",
"generate": "Generowanie",
"errorGeneratingHash": "Błąd generowania skrótu",
"hashCopied": "Hash skopiowany do schowka",
"errorCopyingHash": "Błąd kopiowania skrótu",
"token": "Token",
"onlyOfficeId": "OnlyOffice ID",
"firstLoadTitle": "Witamy w FileBrowser Quantum!",
@@ -226,7 +226,6 @@
"renameSuccess": "Nazwa elementu została zmieniona pomyślnie!",
"newFileSuccess": "Plik został utworzony pomyślnie!",
"newDirSuccess": "Katalog utworzony pomyślnie!",
"copyToClipboardFailed": "Nie można skopiować do schowka",
"moveFailed": "Przenoszenie nie powiodło się",
"operationFailed": "Operacja nie powiodła się",
"partialSuccess": "Niektóre operacje nie powiodły się: {succeeded} powiodło się, {failed} nie powiodło się",
@@ -241,14 +240,12 @@
"unarchiveMessage": "Wybierz miejsce rozpakowania archiwum.",
"unarchiveDestination": "Wyciąg do:",
"unarchiveSuccess": "Archiwum rozpakowane pomyślnie!",
"downloadFailed": "Pobieranie fragmentów nie powiodło się - jeśli sytuacja się powtórzy, spróbuj zmniejszyć rozmiar fragmentu: {message}"
"downloadFailed": "Pobieranie fragmentów nie powiodło się - jeśli sytuacja się powtórzy, spróbuj zmniejszyć rozmiar fragmentu: {message}",
"confirmPasswordToSaveUser": "Wprowadź aktualne hasło, aby zapisać zmiany użytkownika."
},
"search": {
"smallerThan": "Mniejszy niż:",
"largerThan": "Większy niż:",
"helpText1": "Wyszukiwanie odbywa się po każdym wpisanym znaku (minimum 3 znaki dla wyszukiwanych haseł).",
"helpText2": "Indeks: Wyszukiwanie wykorzystuje indeks, który jest automatycznie aktualizowany w skonfigurowanych odstępach czasu (domyślnie: 5 minut). Wyszukiwanie po uruchomieniu programu może skutkować niekompletnymi wynikami.",
"noResults": "Nie znaleziono żadnych wyników w indeksowanym wyszukiwaniu.",
"onlyFolders": "Tylko foldery",
"onlyFiles": "Tylko pliki",
"showOptions": "Pokaż opcje",
@@ -258,7 +255,10 @@
"showPreviewImages": "Pokaż obrazy podglądu",
"showPreviewImagesDescription": "Wyświetlaj podglądy miniatur wyników wyszukiwania, jeśli są dostępne, i wyświetlaj większe ikony dla wszystkich wyników.",
"olderThanDate": "Starszy niż:",
"newerThanDate": "Nowsze niż:"
"newerThanDate": "Nowsze niż:",
"helpText2": "Wyniki wyszukiwania są oparte na zindeksowanych plikach, więc nowo dodane pliki mogą nie być wyświetlane, dopóki nie zostaną zindeksowane.",
"noResults": "Nie znaleziono żadnych wyników.",
"helpText1": "Wyszukiwanie odbywa się po każdym wpisanym znaku (minimum{minSearchLength} znaków)."
},
"settings": {
"administrator": "Administrator",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "OTP został pomyślnie włączony",
"verificationFailed": "Weryfikacja nie powiodła się",
"verificationSucceed": "Sukces weryfikacji",
"generationFailed": "Generacja nieudana"
"generationFailed": "Generacja nieudana",
"passwordRequiredForGeneration": "Hasło jest wymagane do wygenerowania kodu uwierzytelniającego."
},
"general": {
"name": "Nazwa",
@@ -579,7 +580,6 @@
"results": "Wyniki{suffix}",
"users": "Użytkownicy{suffix}",
"photos": "Zdjęcia",
"audios": "Audios{suffix}",
"videos": "Filmy{suffix}",
"models": "Modele 3D",
"documents": "Dokumenty{suffix}",
@@ -665,7 +665,8 @@
"format": "Format{suffix}",
"office": "Biuro{suffix}",
"show": "Pokaż{suffix}",
"divider": "Dzielnik{suffix}"
"divider": "Dzielnik{suffix}",
"audio": "Audio{suffix}"
},
"access": {
"rules": "Zasady dostępu",
@@ -717,15 +718,11 @@
"deleteAfterArchiveDescription": "Po włączeniu, usunie pliki źródłowe po pomyślnym utworzeniu/wypakowaniu archiwów",
"hideFilesInTree": "Ukryj pliki w nawigacji drzewiastej",
"hideFilesInTreeDescription": "Po włączeniu, w drzewiastej nawigacji na pasku bocznym wyświetlane są tylko foldery",
"previewModelsDescription": "Pokaż miniatury podglądów dla modeli 3D, jeśli wyłączone, pojawi się ogólna ikona",
"previewAudiosDescription": "Pokaż miniatury podglądów dla plików audio, jeśli wyłączone, pojawi się ogólna ikona",
"setDateFormat": "Ustaw dokładny format daty",
"showHiddenFiles": "Pokaż ukryte pliki",
"showHiddenFilesDescription": "Pokaż ukryte pliki na liście (np. .thumbnails, ProgramData), ale są one nadal dostępne ręcznie, jeśli są skonfigurowane do indeksowania.",
"showQuickDownload": "Pokaż ikonę pobierania dla szybkiego dostępu",
"showQuickDownloadDescription": "Pokaż ikonę pobierania, aby uzyskać szybki dostęp do każdego elementu pliku, który można kliknąć w celu natychmiastowego pobrania.",
"previewImagesDescription": "Wyświetla podgląd miniatur obrazów, wyświetla ogólną ikonę, jeśli jest wyłączona.",
"previewVideosDescription": "Wyświetlanie podglądu miniatur filmów; jeśli opcja ta jest wyłączona, wyświetlana będzie ogólna ikona.",
"editOffice": "Przeglądaj i edytuj pliki biurowe za pomocą OnlyOffice",
"editOfficeDescription": "Gdy włączona jest tylko opcja Office, przeglądarka/edytor plików będą wyłączone dla wszystkich rozszerzeń plików związanych z plikami biurowymi.",
"filePreviewOptions": "Opcje podglądu plików",
@@ -774,7 +771,10 @@
"popupPreviewDescription": "Po najechaniu kursorem na elementy z miniaturami nad interfejsem użytkownika pojawi się wyskakujące okienko.",
"thumbnailOptions": "Opcje miniatur",
"showThumbnails": "Wyświetlanie miniatur obsługiwanych elementów",
"showThumbnailsDescription": "Po włączeniu tej opcji miniatury są wyświetlane na liście elementów, które je obsługują. Wyłącz, aby ukryć wszystkie miniatury."
"showThumbnailsDescription": "Po włączeniu tej opcji miniatury są wyświetlane na liście elementów, które je obsługują. Wyłącz, aby ukryć wszystkie miniatury.",
"showCopyPath": "Pokaż kopiowanie ścieżki w menu kontekstowym",
"showCopyPathDescription": "Wyświetlanie akcji Kopiuj ścieżkę w menu kontekstowym plików i folderów.",
"previewDescription": "Pokaż podgląd miniatur dla {filetype}, pokaże ogólną ikonę, jeśli jest wyłączona."
},
"editor": {
"uninitialized": "Nie udało się zainicjować edytora. Przeładuj ponownie.",
@@ -844,8 +844,6 @@
"showingResults": "Wyświetlanie wyników {count} ",
"noResults": "Nie znaleziono ikon",
"tryDifferentSearch": "Wypróbuj inne wyszukiwane hasło",
"copiedToClipboard": "Skopiowano '{name}' do schowka!",
"copyFailed": "Nie udało się skopiować do schowka",
"custom": "Niestandardowe",
"useCustomIcon": "Użyj niestandardowej ikony: {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "Copiado para a área de transferência!",
"copyFailed": "Falha ao copiar para a área de transferência",
"copyFile": "Copiar arquivo",
"copyToClipboard": "Copiar",
"moveFile": "Mover arquivo",
@@ -33,7 +34,8 @@
"showMore": "Mostrar opções avançadas",
"showLess": "Ocultar opções avançadas",
"goToSource": "Ir para o local de origem",
"goToItem": "Ir para o item"
"goToItem": "Ir para o item",
"copyPath": "Copiar caminho"
},
"download": {
"downloadFile": "Baixar arquivo",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "Selecione um algoritmo de hash",
"generate": "Gerar",
"errorGeneratingHash": "Erro ao gerar hash",
"hashCopied": "Hash copiado para a área de transferência",
"errorCopyingHash": "Erro ao copiar o hash",
"token": "Token",
"onlyOfficeId": "ID do OnlyOffice",
"firstLoadTitle": "Bem-vindo ao FileBrowser Quantum!",
@@ -226,7 +226,6 @@
"renameSuccess": "Item renomeado com sucesso!",
"newFileSuccess": "Arquivo criado com sucesso!",
"newDirSuccess": "Diretório criado com sucesso!",
"copyToClipboardFailed": "Não é possível copiar para a área de transferência",
"moveFailed": "Falha ao mover",
"operationFailed": "Falha na operação",
"partialSuccess": "Algumas operações falharam: {succeeded} foi bem-sucedido, {failed} falhou",
@@ -241,14 +240,12 @@
"unarchiveMessage": "Escolha onde extrair o arquivo.",
"unarchiveDestination": "Extrair para:",
"unarchiveSuccess": "Arquivo extraído com sucesso!",
"downloadFailed": "Falha no download em pedaços - se isso se repetir, tente reduzir o tamanho do pedaço: {message}"
"downloadFailed": "Falha no download em pedaços - se isso se repetir, tente reduzir o tamanho do pedaço: {message}",
"confirmPasswordToSaveUser": "Digite sua senha atual para salvar essas alterações de usuário."
},
"search": {
"smallerThan": "Menor que:",
"largerThan": "Maior que:",
"helpText1": "A pesquisa ocorre em cada caractere que você digita (mínimo de 3 caracteres para termos de pesquisa).",
"helpText2": "O índice: A pesquisa utiliza o índice que é atualizado automaticamente no intervalo configurado (padrão: 5 minutos). A pesquisa quando o programa acaba de ser iniciado pode resultar em resultados incompletos.",
"noResults": "Não foram encontrados resultados na pesquisa indexada.",
"onlyFolders": "Somente pastas",
"onlyFiles": "Somente arquivos",
"showOptions": "Mostrar opções",
@@ -258,7 +255,10 @@
"showPreviewImages": "Mostrar imagens de visualização",
"showPreviewImagesDescription": "Exibir visualizações em miniatura dos resultados da pesquisa, quando disponíveis, e mostrar ícones maiores para todos os resultados.",
"olderThanDate": "Mais velho que:",
"newerThanDate": "Mais recente que:"
"newerThanDate": "Mais recente que:",
"helpText2": "Os resultados da pesquisa são baseados em arquivos indexados, portanto, os arquivos recém-adicionados podem não aparecer até que sejam indexados.",
"noResults": "Não foram encontrados resultados.",
"helpText1": "A pesquisa ocorre em cada caractere digitado ({minSearchLength} caractere mínimo)."
},
"settings": {
"administrator": "Administrador",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "OTP ativado com sucesso",
"verificationFailed": "Falha na verificação",
"verificationSucceed": "Verificação de sucesso",
"generationFailed": "Falha na geração"
"generationFailed": "Falha na geração",
"passwordRequiredForGeneration": "Sua senha é necessária para gerar um código de autenticação."
},
"general": {
"name": "Nome",
@@ -579,7 +580,6 @@
"results": "Resultados{suffix}",
"users": "Usuários{suffix}",
"photos": "Fotos",
"audios": "Áudios{suffix}",
"videos": "Vídeos{suffix}",
"models": "Modelos 3D",
"documents": "Documentos{suffix}",
@@ -665,7 +665,8 @@
"format": "Formato{suffix}",
"office": "Escritório{suffix}",
"show": "Mostrar{suffix}",
"divider": "Divisor{suffix}"
"divider": "Divisor{suffix}",
"audio": "Áudio{suffix}"
},
"access": {
"rules": "Regras de acesso",
@@ -717,15 +718,11 @@
"deleteAfterArchiveDescription": "Quando ativado, excluirá os arquivos de origem após criação/extração bem-sucedida de arquivos",
"hideFilesInTree": "Ocultar arquivos na navegação em árvore",
"hideFilesInTreeDescription": "Quando ativado, apenas pastas são mostradas na navegação em árvore da barra lateral",
"previewModelsDescription": "Mostrar visualizações em miniatura para modelos 3D, um ícone genérico será mostrado se desativado",
"previewAudiosDescription": "Mostrar pré-visualizações em miniatura para áudios, será mostrado um ícone genérico se desativado",
"setDateFormat": "Definir o formato exato da data",
"showHiddenFiles": "Mostrar arquivos ocultos",
"showHiddenFilesDescription": "Mostrar arquivos ocultos na listagem (por exemplo, .thumbnails, ProgramData), mas eles ainda poderão ser acessados manualmente se estiverem configurados para serem indexados.",
"showQuickDownload": "Mostrar o ícone de download para acesso rápido",
"showQuickDownloadDescription": "Mostrar o ícone de download para acesso rápido em cada item de arquivo que pode ser clicado para fazer o download instantaneamente.",
"previewImagesDescription": "Mostrar visualizações em miniatura de imagens; se desativado, mostrará um ícone genérico.",
"previewVideosDescription": "Mostrar visualizações em miniatura dos vídeos; se estiver desativado, mostrará um ícone genérico.",
"editOffice": "Visualize e edite arquivos de escritório com o OnlyOffice",
"editOfficeDescription": "Quando somente o Office estiver ativado, o visualizador/editor de arquivos será desativado para todas as extensões de arquivos relacionadas a arquivos do Office.",
"filePreviewOptions": "Opções de visualização de arquivos",
@@ -774,7 +771,10 @@
"popupPreviewDescription": "Uma janela pop-up de sobreposição será exibida sobre a interface do usuário ao passar o mouse sobre itens com miniaturas",
"thumbnailOptions": "Opções de miniaturas",
"showThumbnails": "Mostrar miniaturas de itens compatíveis",
"showThumbnailsDescription": "Quando ativada, as miniaturas são mostradas na listagem de itens que as suportam. Desative para ocultar todas as miniaturas."
"showThumbnailsDescription": "Quando ativada, as miniaturas são mostradas na listagem de itens que as suportam. Desative para ocultar todas as miniaturas.",
"showCopyPath": "Mostrar caminho de cópia no menu de contexto",
"showCopyPathDescription": "Mostrar uma ação Copiar caminho no menu de contexto de arquivos e pastas.",
"previewDescription": "Mostrar visualizações em miniatura para {filetype}, mostrará o ícone genérico se estiver desativado."
},
"editor": {
"uninitialized": "Falha ao inicializar o editor. Favor recarregar.",
@@ -844,8 +844,6 @@
"showingResults": "Mostrando {count} resultados",
"noResults": "Nenhum ícone encontrado",
"tryDifferentSearch": "Tente um termo de pesquisa diferente",
"copiedToClipboard": "Copiou '{name}' para a área de transferência!",
"copyFailed": "Falha ao copiar para a área de transferência",
"custom": "Personalizado",
"useCustomIcon": "Usar ícone personalizado: {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "Copiado para a área de transferência!",
"copyFailed": "Falha ao copiar para a área de transferência",
"copyFile": "Copiar ficheiro",
"copyToClipboard": "Copiar",
"moveFile": "Mover ficheiro",
@@ -33,7 +34,8 @@
"showMore": "Mostrar opções avançadas",
"showLess": "Ocultar opções avançadas",
"goToSource": "Ir para o local de origem",
"goToItem": "Ir para o item"
"goToItem": "Ir para o item",
"copyPath": "Caminho de cópia"
},
"download": {
"downloadFile": "Descarregar ficheiro",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "Selecionar um algoritmo de hash",
"generate": "Gerar",
"errorGeneratingHash": "Erro ao gerar hash",
"hashCopied": "Hash copiado para a área de transferência",
"errorCopyingHash": "Erro ao copiar o hash",
"token": "Ficha",
"onlyOfficeId": "ID do OnlyOffice",
"firstLoadTitle": "Bem-vindo ao FileBrowser Quantum!",
@@ -226,7 +226,6 @@
"renameSuccess": "Item renomeado com sucesso!",
"newFileSuccess": "Ficheiro criado com sucesso!",
"newDirSuccess": "Diretório criado com sucesso!",
"copyToClipboardFailed": "Não é possível copiar para a área de transferência",
"moveFailed": "Falha ao mover",
"operationFailed": "A operação falhou",
"partialSuccess": "Algumas operações falharam: {succeeded} foi bem sucedido, {failed} falhou",
@@ -241,14 +240,12 @@
"unarchiveMessage": "Escolha onde extrair o arquivo.",
"unarchiveDestination": "Extrair para:",
"unarchiveSuccess": "Arquivo extraído com sucesso!",
"downloadFailed": "Falha na transferência em pedaços - se isto se repetir, tente reduzir o tamanho dos pedaços: {message}"
"downloadFailed": "Falha na transferência em pedaços - se isto se repetir, tente reduzir o tamanho dos pedaços: {message}",
"confirmPasswordToSaveUser": "Introduza a sua palavra-passe atual para guardar estas alterações de utilizador."
},
"search": {
"smallerThan": "Mais pequeno do que:",
"largerThan": "Maior do que:",
"helpText1": "A pesquisa ocorre em cada caractere digitado (mínimo de 3 caracteres para termos de pesquisa).",
"helpText2": "O índice: A pesquisa utiliza o índice que é atualizado automaticamente no intervalo configurado (predefinição: 5 minutos). A pesquisa quando o programa acabou de ser iniciado pode resultar em resultados incompletos.",
"noResults": "Não foram encontrados resultados na pesquisa indexada.",
"onlyFolders": "Apenas pastas",
"onlyFiles": "Apenas ficheiros",
"showOptions": "Mostrar opções",
@@ -258,7 +255,10 @@
"showPreviewImages": "Mostrar imagens de pré-visualização",
"showPreviewImagesDescription": "Apresentar pré-visualizações em miniatura dos resultados da pesquisa, quando disponíveis, e mostrar ícones maiores para todos os resultados.",
"olderThanDate": "Mais velho do que:",
"newerThanDate": "Mais recente do que:"
"newerThanDate": "Mais recente do que:",
"helpText2": "Os resultados da pesquisa baseiam-se nos ficheiros indexados, pelo que os ficheiros recentemente adicionados podem não aparecer até serem indexados.",
"noResults": "Não foram encontrados resultados.",
"helpText1": "A pesquisa ocorre em cada carácter que escrever ({minSearchLength} carácter mínimo)."
},
"settings": {
"administrator": "Administrador",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "OTP ativado com sucesso",
"verificationFailed": "Falha na verificação",
"verificationSucceed": "Verificação de sucesso",
"generationFailed": "Geração falhada"
"generationFailed": "Geração falhada",
"passwordRequiredForGeneration": "A sua palavra-passe é necessária para gerar um código de autenticação."
},
"fileLoading": {
"title": "Carregamentos e transferências",
@@ -571,15 +572,11 @@
"deleteAfterArchiveDescription": "Quando ativado, eliminará os ficheiros de origem após criação/extração bem-sucedida de arquivos",
"hideFilesInTree": "Ocultar ficheiros na navegação em árvore",
"hideFilesInTreeDescription": "Quando ativado, apenas pastas são mostradas na navegação em árvore da barra lateral",
"previewModelsDescription": "Mostrar pré-visualizações em miniatura para modelos 3D, será mostrado um ícone genérico se desativado",
"previewAudiosDescription": "Mostrar visualizações em miniatura para áudios, um ícone genérico será mostrado se desativado",
"setDateFormat": "Definir o formato exato da data",
"showHiddenFiles": "Mostrar ficheiros ocultos",
"showHiddenFilesDescription": "Mostrar ficheiros ocultos na listagem (por exemplo, .thumbnails, ProgramData), mas estes continuam a ser acessíveis manualmente se estiverem configurados para serem indexados.",
"showQuickDownload": "Mostrar o ícone de transferência para acesso rápido",
"showQuickDownloadDescription": "Mostrar o ícone de transferência para acesso rápido em cada item de ficheiro que pode ser clicado para transferir instantaneamente.",
"previewImagesDescription": "Mostrar pré-visualizações de miniaturas para imagens, mostrará o ícone genérico se estiver desativado.",
"previewVideosDescription": "Mostrar pré-visualizações de miniaturas para vídeos, mostrará um ícone genérico se estiver desativado.",
"editOffice": "Ver e editar ficheiros de escritório com OnlyOffice",
"editOfficeDescription": "Quando apenas o Office está ativado, o visualizador/editor de ficheiros será desativado para todas as extensões de ficheiros relacionadas com ficheiros do Office.",
"filePreviewOptions": "Opções de pré-visualização de ficheiros",
@@ -628,7 +625,10 @@
"popupPreviewDescription": "Será apresentada uma janela pop-up de sobreposição sobre a IU quando se passa o rato sobre itens com miniaturas",
"thumbnailOptions": "Opções de miniaturas",
"showThumbnails": "Mostrar miniaturas para itens suportados",
"showThumbnailsDescription": "Quando activada, as miniaturas são mostradas na listagem de itens que as suportam. Desativar para ocultar todas as miniaturas."
"showThumbnailsDescription": "Quando activada, as miniaturas são mostradas na listagem de itens que as suportam. Desativar para ocultar todas as miniaturas.",
"showCopyPath": "Mostrar caminho de cópia no menu de contexto",
"showCopyPathDescription": "Mostrar uma ação Copiar caminho no menu de contexto de ficheiros e pastas.",
"previewDescription": "Mostrar pré-visualizações em miniatura para {filetype}, mostrará o ícone genérico se estiver desativado."
},
"general": {
"name": "Nome",
@@ -665,7 +665,6 @@
"results": "Resultados{suffix}",
"users": "Utilizadores{suffix}",
"photos": "Fotos",
"audios": "Áudios{suffix}",
"videos": "Vídeos{suffix}",
"models": "Modelos 3D",
"documents": "Documentos{suffix}",
@@ -751,7 +750,8 @@
"format": "Formato{suffix}",
"office": "Escritório{suffix}",
"show": "Mostrar{suffix}",
"divider": "Divisor{suffix}"
"divider": "Divisor{suffix}",
"audio": "Áudio{suffix}"
},
"access": {
"rules": "Regras de acesso",
@@ -844,8 +844,6 @@
"showingResults": "Mostrando {count} resultados",
"noResults": "Não foram encontrados ícones",
"tryDifferentSearch": "Experimente um termo de pesquisa diferente",
"copiedToClipboard": "Copiou '{name}' para a área de transferência!",
"copyFailed": "Falha ao copiar para a área de transferência",
"custom": "Personalizado",
"useCustomIcon": "Utilizar um ícone personalizado: {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "Copiat în clipboard!",
"copyFailed": "Copierea în clipboard a eșuat",
"copyFile": "Copiază fișier",
"copyToClipboard": "Copiază în clipboard",
"moveFile": "Mută fișier",
@@ -33,7 +34,8 @@
"showMore": "Afișare opțiuni avansate",
"showLess": "Ascundeți opțiunile avansate",
"goToSource": "Du-te la locația sursei",
"goToItem": "Du-te la articol"
"goToItem": "Du-te la articol",
"copyPath": "Copiați calea"
},
"download": {
"downloadFile": "Descarcă fișier",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "Selectați un algoritm hash",
"generate": "Generați",
"errorGeneratingHash": "Eroare în generarea hash-ului",
"hashCopied": "Hash copiat în clipboard",
"errorCopyingHash": "Eroare copiere hash",
"token": "Token",
"onlyOfficeId": "NumaiOffice ID",
"firstLoadTitle": "Bine ați venit la FileBrowser Quantum!",
@@ -226,7 +226,6 @@
"renameSuccess": "Element redenumit cu succes!",
"newFileSuccess": "Fișier creat cu succes!",
"newDirSuccess": "Director creat cu succes!",
"copyToClipboardFailed": "Imposibilitatea de a copia în clipboard",
"moveFailed": "Mutarea a eșuat",
"operationFailed": "Operațiunea a eșuat",
"partialSuccess": "Unele operațiuni au eșuat: {succeeded} a reușit, {failed} a eșuat",
@@ -241,14 +240,12 @@
"unarchiveMessage": "Alegeți unde să extrageți arhiva.",
"unarchiveDestination": "Extras la:",
"unarchiveSuccess": "Arhiva extrasă cu succes!",
"downloadFailed": "Descărcarea fragmentată a eșuat - dacă acest lucru se repetă, încercați să reduceți dimensiunea fragmentului: {message}"
"downloadFailed": "Descărcarea fragmentată a eșuat - dacă acest lucru se repetă, încercați să reduceți dimensiunea fragmentului: {message}",
"confirmPasswordToSaveUser": "Introduceți parola curentă pentru a salva aceste modificări de utilizator."
},
"search": {
"smallerThan": "Mai mic decât:",
"largerThan": "Mai mare decât:",
"helpText1": "Căutarea are loc pe fiecare caracter tastat (minim 3 caractere pentru termenii de căutare).",
"helpText2": "Indexul: Căutarea utilizează indexul care se actualizează automat la intervalul configurat (implicit: 5 minute). Căutarea atunci când programul tocmai a început poate duce la rezultate incomplete.",
"noResults": "Nu s-au găsit rezultate în căutarea indexată.",
"onlyFolders": "Numai foldere",
"onlyFiles": "Numai fișiere",
"showOptions": "Afișare opțiuni",
@@ -258,7 +255,10 @@
"showPreviewImages": "Afișați imagini de previzualizare",
"showPreviewImagesDescription": "Afișați previzualizări în miniatură pentru rezultatele căutării, atunci când sunt disponibile, și afișați pictograme mai mari pentru toate rezultatele.",
"olderThanDate": "Mai în vârstă decât:",
"newerThanDate": "Mai nou decât:"
"newerThanDate": "Mai nou decât:",
"helpText2": "Rezultatele căutării se bazează pe fișierele indexate, astfel încât fișierele nou adăugate pot să nu apară până când nu sunt indexate.",
"noResults": "Nu s-au găsit rezultate.",
"helpText1": "Căutarea are loc pe fiecare caracter tastat ({minSearchLength} caracter minim)."
},
"settings": {
"administrator": "Administrator",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "OTP activat cu succes",
"verificationFailed": "Verificarea a eșuat",
"verificationSucceed": "Verificarea reușită",
"generationFailed": "Generație eșuată"
"generationFailed": "Generație eșuată",
"passwordRequiredForGeneration": "Parola dvs. este necesară pentru a genera un cod de autentificare."
},
"general": {
"name": "Nume și prenume",
@@ -579,7 +580,6 @@
"results": "Rezultate{suffix}",
"users": "Utilizatori{suffix}",
"photos": "Fotografii",
"audios": "Audios{suffix}",
"videos": "Video{suffix}",
"models": "Modele 3D",
"documents": "Documente{suffix}",
@@ -665,7 +665,8 @@
"format": "Format{suffix}",
"office": "Birou{suffix}",
"show": "Spectacol{suffix}",
"divider": "Divizor{suffix}"
"divider": "Divizor{suffix}",
"audio": "Audio{suffix}"
},
"access": {
"rules": "Reguli de acces",
@@ -717,15 +718,11 @@
"deleteAfterArchiveDescription": "Când este activat, va șterge fișierele sursă după crearea/extragerea cu succes a arhivelor",
"hideFilesInTree": "Ascunde fișierele în navigarea arborescentă",
"hideFilesInTreeDescription": "Când este activat, numai folderele sunt afișate în navigarea arborescentă a barei laterale",
"previewModelsDescription": "Afișează previzualizări miniatură pentru modele 3D, se va afișa o pictogramă generică dacă este dezactivat",
"previewAudiosDescription": "Afișează previzualizări miniatură pentru fișiere audio, se va afișa o pictogramă generică dacă este dezactivat",
"setDateFormat": "Setați formatul exact al datei",
"showHiddenFiles": "Afișează fișierele ascunse",
"showHiddenFilesDescription": "Afișează fișierele ascunse în listă (de exemplu, .thumbnails, ProgramData), dar acestea sunt încă accesibile manual dacă sunt configurate pentru a fi indexate.",
"showQuickDownload": "Afișați pictograma de descărcare pentru acces rapid",
"showQuickDownloadDescription": "Afișați pictograma de descărcare pentru acces rapid pe fiecare element de fișier pe care puteți face clic pentru a descărca instantaneu.",
"previewImagesDescription": "Afișează previzualizări în miniatură pentru imagini, va afișa pictograma generică dacă este dezactivată.",
"previewVideosDescription": "Afișează previzualizări ale miniaturilor pentru videoclipuri, va afișa pictograma generică dacă este dezactivată.",
"editOffice": "Vizualizați și editați fișiere office cu OnlyOffice",
"editOfficeDescription": "Atunci când este activată doar opțiunea Office, vizualizatorul/editorul de fișiere va fi dezactivat pentru toate extensiile de fișiere legate de fișierele Office.",
"filePreviewOptions": "Opțiuni de previzualizare a fișierelor",
@@ -774,7 +771,10 @@
"popupPreviewDescription": "O fereastră pop-up suprapusă va fi afișată peste interfața de utilizator atunci când treceți peste elemente cu miniaturi",
"thumbnailOptions": "Opțiuni pentru miniaturi",
"showThumbnails": "Afișați miniaturi pentru elementele acceptate",
"showThumbnailsDescription": "Atunci când este activată, miniaturile sunt afișate în listă pentru articolele care le acceptă. Dezactivați pentru a ascunde toate miniaturile."
"showThumbnailsDescription": "Atunci când este activată, miniaturile sunt afișate în listă pentru articolele care le acceptă. Dezactivați pentru a ascunde toate miniaturile.",
"showCopyPath": "Afișați calea de copiere în meniul contextual",
"showCopyPathDescription": "Afișați o acțiune de copiere a căii în meniul contextual al fișierelor și dosarelor.",
"previewDescription": "Afișează previzualizări ale miniaturilor pentru {filetype}, va afișa pictograma generică dacă este dezactivată."
},
"editor": {
"uninitialized": "Nu s-a reușit inițializarea editorului. Vă rugăm să reîncărcați.",
@@ -844,8 +844,6 @@
"showingResults": "Afișarea rezultatelor {count} ",
"noResults": "Nu s-au găsit pictograme",
"tryDifferentSearch": "Încercați un alt termen de căutare",
"copiedToClipboard": "Copiat '{name}' în clipboard!",
"copyFailed": "A eșuat copierea în clipboard",
"custom": "Personalizat",
"useCustomIcon": "Utilizați pictograma personalizată: {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "Скопировано в буфер обмена!",
"copyFailed": "Не удалось скопировать в буфер обмена",
"copyFile": "Скопировать файл",
"copyToClipboard": "Скопировать в буфер",
"moveFile": "Переместить файл",
@@ -33,7 +34,8 @@
"showMore": "Показать дополнительные параметры",
"showLess": "Скрыть дополнительные параметры",
"goToSource": "Перейти к исходному местоположению",
"goToItem": "Перейти к пункту"
"goToItem": "Перейти к пункту",
"copyPath": "Путь копирования"
},
"download": {
"downloadFile": "Скачать файл",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "Выберите алгоритм хэширования",
"generate": "Создать",
"errorGeneratingHash": "Ошибка при генерации хэша",
"hashCopied": "Хэш копируется в буфер обмена",
"errorCopyingHash": "Ошибка при копировании хэша",
"token": "Токен",
"onlyOfficeId": "Идентификатор OnlyOffice",
"firstLoadTitle": "Добро пожаловать в FileBrowser Quantum!",
@@ -226,7 +226,6 @@
"renameSuccess": "Элемент успешно переименован!",
"newFileSuccess": "Файл создан успешно!",
"newDirSuccess": "Каталог создан успешно!",
"copyToClipboardFailed": "Невозможно скопировать в буфер обмена",
"moveFailed": "Перемещение не удалось",
"operationFailed": "Операция не удалась",
"partialSuccess": "Некоторые операции завершились неудачей: {succeeded} - успешно, {failed} - неудачно.",
@@ -241,14 +240,12 @@
"unarchiveMessage": "Выберите, куда распаковать архив.",
"unarchiveDestination": "Отрывок в:",
"unarchiveSuccess": "Архив извлечен успешно!",
"downloadFailed": "Не удалось загрузить чанк - если ситуация повторяется, попробуйте уменьшить размер чанка: {message}"
"downloadFailed": "Не удалось загрузить чанк - если ситуация повторяется, попробуйте уменьшить размер чанка: {message}",
"confirmPasswordToSaveUser": "Введите свой текущий пароль, чтобы сохранить изменения пользователя."
},
"search": {
"smallerThan": "Меньше, чем:",
"largerThan": "Больше, чем:",
"helpText1": "Поиск осуществляется по каждому введенному символу (минимум 3 символа для поисковых запросов).",
"helpText2": "Индекс: Для поиска используется индекс, который автоматически обновляется с заданным интервалом (по умолчанию: 5 минут). Поиск, когда программа только что запущена, может привести к неполным результатам.",
"noResults": "В проиндексированном поиске результатов не найдено.",
"onlyFolders": "Только папки",
"onlyFiles": "Только файлы",
"showOptions": "Показать параметры",
@@ -258,7 +255,10 @@
"showPreviewImages": "Показать изображения для предварительного просмотра",
"showPreviewImagesDescription": "Показывайте миниатюры для результатов поиска, если они доступны, и отображайте более крупные значки для всех результатов.",
"olderThanDate": "Старше, чем:",
"newerThanDate": "Новее, чем:"
"newerThanDate": "Новее, чем:",
"helpText2": "Результаты поиска основаны на проиндексированных файлах, поэтому вновь добавленные файлы могут не отображаться до тех пор, пока они не будут проиндексированы.",
"noResults": "Результаты не найдены.",
"helpText1": "Поиск осуществляется по каждому вводимому символу ({minSearchLength} минимум символов)."
},
"settings": {
"administrator": "Администратор",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "OTP успешно активирован",
"verificationFailed": "Верификация не удалась",
"verificationSucceed": "Проверка удалась",
"generationFailed": "Не удалось создать"
"generationFailed": "Не удалось создать",
"passwordRequiredForGeneration": "Ваш пароль необходим для генерации кода аутентификатора."
},
"general": {
"name": "Имя",
@@ -579,7 +580,6 @@
"results": "Результаты{suffix}",
"users": "Пользователи{suffix}",
"photos": "Фотографии",
"audios": "Аудио{suffix}",
"videos": "Видео{suffix}",
"models": "3D-модели",
"documents": "Документы{suffix}",
@@ -665,7 +665,8 @@
"format": "Формат{suffix}",
"office": "Офис{suffix}",
"show": "Показать{suffix}",
"divider": "Разделитель{suffix}"
"divider": "Разделитель{suffix}",
"audio": "Аудио{suffix}"
},
"access": {
"rules": "Правила доступа",
@@ -717,15 +718,11 @@
"deleteAfterArchiveDescription": "При включении удалит исходные файлы после успешного создания/извлечения архивов",
"hideFilesInTree": "Скрывать файлы в древовидной навигации",
"hideFilesInTreeDescription": "При включении в древовидной навигации боковой панели отображаются только папки",
"previewModelsDescription": "Показывать миниатюры для 3D-моделей, если отключено, будет показана стандартная иконка",
"previewAudiosDescription": "Показывать миниатюры для аудиофайлов, если отключено, будет показана стандартная иконка",
"setDateFormat": "Установить точный формат даты",
"showHiddenFiles": "Показывать скрытые файлы",
"showHiddenFilesDescription": "Показывать в списке скрытые файлы (например, .thumbnails, ProgramData), но они по-прежнему доступны вручную, если настроены на индексирование.",
"showQuickDownload": "Показывать значок загрузки для быстрого доступа",
"showQuickDownloadDescription": "Показывать значок загрузки для быстрого доступа к каждому элементу файла, который можно щелкнуть, чтобы мгновенно загрузить.",
"previewImagesDescription": "Показывать миниатюры изображений, при отключении будет отображаться общий значок.",
"previewVideosDescription": "Показывать миниатюры видео, если отключено, будет отображаться общий значок.",
"editOffice": "Просмотр и редактирование офисных файлов с помощью OnlyOffice",
"editOfficeDescription": "Если включена опция \"Только офис\", просмотрщик/редактор файлов будет отключен для всех расширений файлов, связанных с офисными файлами.",
"filePreviewOptions": "Параметры предварительного просмотра файлов",
@@ -774,7 +771,10 @@
"popupPreviewDescription": "При наведении курсора на элементы с миниатюрами поверх пользовательского интерфейса отображается всплывающее окно",
"thumbnailOptions": "Параметры миниатюр",
"showThumbnails": "Показывайте миниатюры для поддерживаемых элементов",
"showThumbnailsDescription": "Если эта функция включена, миниатюры отображаются в списке товаров, которые их поддерживают. Выключите, чтобы скрыть все эскизы."
"showThumbnailsDescription": "Если эта функция включена, миниатюры отображаются в списке товаров, которые их поддерживают. Выключите, чтобы скрыть все эскизы.",
"showCopyPath": "Показывать путь копирования в контекстном меню",
"showCopyPathDescription": "Показать действие \"Копировать путь\" в контекстном меню файлов и папок.",
"previewDescription": "Показывать миниатюры для {filetype}, если отключено, будет отображаться общий значок."
},
"editor": {
"uninitialized": "Не удалось инициализировать редактор. Пожалуйста, перезагрузитесь.",
@@ -844,8 +844,6 @@
"showingResults": "Показать результаты {count} ",
"noResults": "Значки не найдены",
"tryDifferentSearch": "Попробуйте использовать другой поисковый запрос",
"copiedToClipboard": "Скопировано '{name}' в буфер обмена!",
"copyFailed": "Не удалось скопировать в буфер обмена",
"custom": "Пользовательское",
"useCustomIcon": "Используйте пользовательский значок: {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "Skopírované do schránky!",
"copyFailed": "Kopírovanie do schránky zlyhalo",
"copyFile": "Kopírovať súbor",
"copyToClipboard": "Kopírovať do schránky",
"moveFile": "Presunúť súbory",
@@ -33,7 +34,8 @@
"showMore": "Zobraziť rozšírené možnosti",
"showLess": "Skryť rozšírené možnosti",
"goToSource": "Prejsť na zdrojové miesto",
"goToItem": "Prejsť na položku"
"goToItem": "Prejsť na položku",
"copyPath": "Kopírovať cestu"
},
"download": {
"downloadFile": "Stiahnuť súbor",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "Výber algoritmu hash",
"generate": "Generovať",
"errorGeneratingHash": "Chyba pri generovaní hash",
"hashCopied": "Hash skopírovaný do schránky",
"errorCopyingHash": "Chyba pri kopírovaní hash",
"token": "Token",
"onlyOfficeId": "IbaOffice ID",
"firstLoadTitle": "Vitajte v programe FileBrowser Quantum!",
@@ -226,7 +226,6 @@
"renameSuccess": "Položka bola úspešne premenovaná!",
"newFileSuccess": "Súbor bol úspešne vytvorený!",
"newDirSuccess": "Adresár bol úspešne vytvorený!",
"copyToClipboardFailed": "Nemožnosť kopírovania do schránky",
"moveFailed": "Presun zlyhal",
"operationFailed": "Operácia zlyhala",
"partialSuccess": "Niektoré operácie zlyhali: {succeeded} sa podarilo, {failed} sa nepodarilo",
@@ -241,14 +240,12 @@
"unarchiveMessage": "Vyberte, kam sa má archív extrahovať.",
"unarchiveDestination": "Výpis na:",
"unarchiveSuccess": "Archív úspešne extrahovaný!",
"downloadFailed": "Sťahovanie po častiach zlyhalo - ak sa to opakuje, skúste zmenšiť veľkosť časti: {message}"
"downloadFailed": "Sťahovanie po častiach zlyhalo - ak sa to opakuje, skúste zmenšiť veľkosť časti: {message}",
"confirmPasswordToSaveUser": "Zadajte svoje aktuálne heslo, aby ste tieto zmeny používateľa uložili."
},
"search": {
"smallerThan": "Menšie ako:",
"largerThan": "Väčšie ako:",
"helpText1": "Vyhľadávanie prebieha na základe každého zadaného znaku (minimálne 3 znaky pre hľadané výrazy).",
"helpText2": "Index: Vyhľadávanie využíva index, ktorý sa automaticky aktualizuje v nastavenom intervale (predvolené: 5 minút). Vyhľadávanie, keď sa program práve spustil, môže viesť k neúplným výsledkom.",
"noResults": "V indexovanom vyhľadávaní sa nenašli žiadne výsledky.",
"onlyFolders": "Iba priečinky",
"onlyFiles": "Iba súbory",
"showOptions": "Zobraziť možnosti",
@@ -258,7 +255,10 @@
"showPreviewImages": "Zobraziť náhľad obrázkov",
"showPreviewImagesDescription": "Zobrazenie miniatúrnych náhľadov výsledkov vyhľadávania, ak sú k dispozícii, a zobrazenie väčších ikon pre všetky výsledky.",
"olderThanDate": "Staršie ako:",
"newerThanDate": "Novšie ako:"
"newerThanDate": "Novšie ako:",
"helpText2": "Výsledky vyhľadávania sú založené na indexovaných súboroch, takže novo pridané súbory sa nemusia zobraziť, kým nie sú indexované.",
"noResults": "Nenašli sa žiadne výsledky.",
"helpText1": "Vyhľadávanie prebieha na základe každého zadaného znaku ({minSearchLength} minimálne znak)."
},
"settings": {
"administrator": "Administrátor",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "OTP úspešne povolený",
"verificationFailed": "Overenie sa nepodarilo",
"verificationSucceed": "Overovanie Úspech",
"generationFailed": "Generácia zlyhala"
"generationFailed": "Generácia zlyhala",
"passwordRequiredForGeneration": "Na vygenerovanie autentifikačného kódu je potrebné vaše heslo."
},
"general": {
"name": "Názov",
@@ -579,7 +580,6 @@
"results": "Výsledky{suffix}",
"users": "Používatelia{suffix}",
"photos": "Fotografie",
"audios": "Zvuky{suffix}",
"videos": "Videá{suffix}",
"models": "3D modely",
"documents": "Dokumenty{suffix}",
@@ -665,7 +665,8 @@
"format": "Formát{suffix}",
"office": "Kancelária{suffix}",
"show": "Zobraziť{suffix}",
"divider": "Rozdeľovač{suffix}"
"divider": "Rozdeľovač{suffix}",
"audio": "Audio{suffix}"
},
"access": {
"rules": "Pravidlá prístupu",
@@ -717,15 +718,11 @@
"deleteAfterArchiveDescription": "Ak je povolené, po úspešnom vytvorení/extrahovaní archívov odstráni zdrojové súbory",
"hideFilesInTree": "Skryť súbory v stromovej navigácii",
"hideFilesInTreeDescription": "Ak je povolené, v stromovej navigácii bočného panela sa zobrazujú iba priečinky",
"previewModelsDescription": "Zobraziť miniatúry náhľadov pre 3D modely, ak je zakázané, zobrazí sa všeobecná ikona",
"previewAudiosDescription": "Zobraziť miniatúry náhľadov pre zvuky, ak je zakázané, zobrazí sa všeobecná ikona",
"setDateFormat": "Nastavenie presného formátu dátumu",
"showHiddenFiles": "Zobrazenie skrytých súborov",
"showHiddenFilesDescription": "Zobrazenie skrytých súborov vo výpise (napr. .thumbnails, ProgramData), ale sú stále manuálne prístupné, ak sú nakonfigurované na indexovanie.",
"showQuickDownload": "Zobrazenie ikony na stiahnutie pre rýchly prístup",
"showQuickDownloadDescription": "Zobrazenie ikony na stiahnutie pre rýchly prístup ku každej položke súboru, na ktorú možno kliknúť a okamžite ju stiahnuť.",
"previewImagesDescription": "Zobrazenie náhľadov miniatúr pre obrázky, ak je vypnuté, zobrazí sa generická ikona.",
"previewVideosDescription": "Zobrazenie náhľadov miniatúr pre videá, ak je vypnuté, zobrazí sa generická ikona.",
"editOffice": "Zobrazenie a úprava kancelárskych súborov pomocou OnlyOffice",
"editOfficeDescription": "Ak je povolená iba funkcia office, prehliadač/editor súborov bude zakázaný pre všetky prípony súborov súvisiace s kancelárskymi súbormi.",
"filePreviewOptions": "Možnosti náhľadu súborov",
@@ -774,7 +771,10 @@
"popupPreviewDescription": "Prekryvné vyskakovacie okno sa zobrazí nad používateľským rozhraním pri prejdení nad položky s miniatúrami",
"thumbnailOptions": "Možnosti miniatúr",
"showThumbnails": "Zobrazenie miniatúr pre podporované položky",
"showThumbnailsDescription": "Ak je táto možnosť povolená, vo výpise sa zobrazujú miniatúry položiek, ktoré ich podporujú. Ak chcete skryť všetky miniatúry, možnosť vypnite."
"showThumbnailsDescription": "Ak je táto možnosť povolená, vo výpise sa zobrazujú miniatúry položiek, ktoré ich podporujú. Ak chcete skryť všetky miniatúry, možnosť vypnite.",
"showCopyPath": "Zobraziť kopírovanie cesty v kontextovom menu",
"showCopyPathDescription": "Zobrazenie akcie Kopírovať cestu v kontextovej ponuke súborov a priečinkov.",
"previewDescription": "Zobraziť náhľady miniatúr pre {filetype}, ak je vypnuté, zobrazí sa generická ikona."
},
"editor": {
"uninitialized": "Nepodarilo sa inicializovať editor. Prosím, načítajte ho znova.",
@@ -844,8 +844,6 @@
"showingResults": "Zobrazené výsledky {count} ",
"noResults": "Nenašli sa žiadne ikony",
"tryDifferentSearch": "Skúste použiť iný vyhľadávací výraz",
"copiedToClipboard": "Skopírované '{name}' do schránky!",
"copyFailed": "Nepodarilo sa skopírovať do schránky",
"custom": "Vlastné",
"useCustomIcon": "Použite vlastnú ikonu: {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "Kopierat till urklipp!",
"copyFailed": "Misslyckades med att kopiera till urklipp",
"copyFile": "Kopiera fil",
"copyToClipboard": "Kopiera till urklipp",
"moveFile": "Flytta fil",
@@ -33,7 +34,8 @@
"showMore": "Visa avancerade alternativ",
"showLess": "Dölj avancerade alternativ",
"goToSource": "Gå till källplatsen",
"goToItem": "Gå till objektet"
"goToItem": "Gå till objektet",
"copyPath": "Kopiera sökväg"
},
"download": {
"downloadFile": "Ladda ner fil",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "Välj en hashalgoritm",
"generate": "Generera",
"errorGeneratingHash": "Fel vid generering av hash",
"hashCopied": "Hash kopieras till urklipp",
"errorCopyingHash": "Fel vid kopiering av hash",
"token": "Token",
"onlyOfficeId": "OnlyOffice ID",
"firstLoadTitle": "Välkommen till FileBrowser Quantum!",
@@ -226,7 +226,6 @@
"renameSuccess": "Objektet har bytt namn framgångsrikt!",
"newFileSuccess": "Filen skapades framgångsrikt!",
"newDirSuccess": "Katalog skapad framgångsrikt!",
"copyToClipboardFailed": "Det går inte att kopiera till urklipp",
"moveFailed": "Flyttning misslyckades",
"operationFailed": "Operationen misslyckades",
"partialSuccess": "Vissa operationer misslyckades: {succeeded} lyckades, {failed} misslyckades",
@@ -241,14 +240,12 @@
"unarchiveMessage": "Välj var du vill extrahera arkivet.",
"unarchiveDestination": "Extrahera till:",
"unarchiveSuccess": "Arkivet har extraherats framgångsrikt!",
"downloadFailed": "Nedladdningen av chunked misslyckades - om detta upprepas, försök att minska chunkstorleken: {message}"
"downloadFailed": "Nedladdningen av chunked misslyckades - om detta upprepas, försök att minska chunkstorleken: {message}",
"confirmPasswordToSaveUser": "Ange ditt nuvarande lösenord för att spara dessa användarändringar."
},
"search": {
"smallerThan": "Mindre än:",
"largerThan": "Större än:",
"helpText1": "Sökningen sker på varje tecken du skriver (minst 3 tecken för sökbegrepp).",
"helpText2": "Indexet: Sökningen använder indexet som automatiskt uppdateras med det inställda intervallet (standard: 5 minuter). Om du söker när programmet precis har startat kan det leda till ofullständiga resultat.",
"noResults": "Inga resultat hittades i indexerad sökning.",
"onlyFolders": "Endast mappar",
"onlyFiles": "Endast filer",
"showOptions": "Visa alternativ",
@@ -258,7 +255,10 @@
"showPreviewImages": "Visa förhandsgranskningsbilder",
"showPreviewImagesDescription": "Visa förhandsvisningar av miniatyrbilder för sökresultat när det är tillgängligt och visa större ikoner för alla resultat.",
"olderThanDate": "Äldre än:",
"newerThanDate": "Nyare än:"
"newerThanDate": "Nyare än:",
"helpText2": "Sökresultaten baseras på indexerade filer, så det kan hända att nya filer inte visas förrän de har indexerats.",
"noResults": "Inga resultat hittades.",
"helpText1": "Sökningen sker för varje tecken du skriver ({minSearchLength} tecken minimum)."
},
"settings": {
"administrator": "Administratör",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "OTP aktiverat framgångsrikt",
"verificationFailed": "Verifiering misslyckades",
"verificationSucceed": "Verifiering lyckas",
"generationFailed": "Generation misslyckad"
"generationFailed": "Generation misslyckad",
"passwordRequiredForGeneration": "Ditt lösenord krävs för att generera en autentiseringskod."
},
"fileLoading": {
"title": "Uppladdningar och nedladdningar",
@@ -571,15 +572,11 @@
"deleteAfterArchiveDescription": "När aktiverad kommer att ta bort källfilerna efter framgångsrik skapande/extrahering av arkiv",
"hideFilesInTree": "Dölj filer i trädnavigering",
"hideFilesInTreeDescription": "När aktiverad visas endast mappar i sidofältets trädnavigering",
"previewModelsDescription": "Visa miniatyrförhandsvisningar för 3D-modeller, kommer att visa generisk ikon om inaktiverad",
"previewAudiosDescription": "Visa miniatyrförhandsvisningar för ljudfiler, kommer att visa generisk ikon om inaktiverad",
"setDateFormat": "Ange exakt datumformat",
"showHiddenFiles": "Visa dolda filer",
"showHiddenFilesDescription": "Visa dolda filer i listan (t.ex. .thumbnails, ProgramData), men de är fortfarande manuellt åtkomliga om de är konfigurerade för att indexeras.",
"showQuickDownload": "Visa nedladdningsikon för snabb åtkomst",
"showQuickDownloadDescription": "Visa nedladdningsikon för snabb åtkomst på varje filobjekt som kan klickas för att ladda ner direkt.",
"previewImagesDescription": "Visa förhandsvisningar av miniatyrbilder för bilder, visar generisk ikon om den är inaktiverad.",
"previewVideosDescription": "Visa förhandsvisningar av miniatyrbilder för videor, visar en generisk ikon om den är inaktiverad.",
"editOffice": "Visa och redigera kontorsfiler med OnlyOffice",
"editOfficeDescription": "När endast Office är aktiverat kommer filgranskaren/-redigeraren att inaktiveras för alla filtillägg som är relaterade till Office-filer.",
"filePreviewOptions": "Alternativ för förhandsgranskning av filer",
@@ -628,7 +625,10 @@
"popupPreviewDescription": "Ett överliggande popup-fönster visas över användargränssnittet när du håller muspekaren över objekt med miniatyrbilder",
"thumbnailOptions": "Alternativ för miniatyrbilder",
"showThumbnails": "Visa miniatyrbilder för objekt som stöds",
"showThumbnailsDescription": "När den är aktiverad visas miniatyrbilder i listan för objekt som stöder dem. Stäng av för att dölja alla miniatyrbilder."
"showThumbnailsDescription": "När den är aktiverad visas miniatyrbilder i listan för objekt som stöder dem. Stäng av för att dölja alla miniatyrbilder.",
"showCopyPath": "Visa Kopiera sökväg i snabbmenyn",
"showCopyPathDescription": "Visa åtgärden Kopiera sökväg i snabbmenyn för filer och mappar.",
"previewDescription": "Visa förhandsgranskning av miniatyrbilder för {filetype}, visar generisk ikon om den är inaktiverad."
},
"general": {
"name": "Namn",
@@ -665,7 +665,6 @@
"results": "Resultat{suffix}",
"users": "Användare{suffix}",
"photos": "Bilder",
"audios": "Ljud{suffix}",
"videos": "Videor{suffix}",
"models": "3D-modeller",
"documents": "Dokument{suffix}",
@@ -751,7 +750,8 @@
"format": "Format{suffix}",
"office": "Kontor{suffix}",
"show": "Visa{suffix}",
"divider": "Uppdelare{suffix}"
"divider": "Uppdelare{suffix}",
"audio": "Ljud{suffix}"
},
"access": {
"rules": "Regler för åtkomst",
@@ -844,8 +844,6 @@
"showingResults": "Visar resultat från {count} ",
"noResults": "Inga ikoner hittades",
"tryDifferentSearch": "Försök med en annan sökterm",
"copiedToClipboard": "Kopierade '{name}' till urklipp!",
"copyFailed": "Misslyckades med att kopiera till klippbordet",
"custom": "Anpassad",
"useCustomIcon": "Använd anpassad ikon: {name}",
"iconLibraryUrl": "teckensnitt.google.com/ikoner",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "Panoya kopyalandı!",
"copyFailed": "Panoya kopyalama başarısız",
"copyFile": "Dosyayı kopyala",
"copyToClipboard": "Panoya kopyala",
"moveFile": "Dosyayı taşı",
@@ -33,7 +34,8 @@
"showMore": "Gelişmiş Seçenekleri Göster",
"showLess": "Gelişmiş Seçenekleri Gizle",
"goToSource": "Kaynak Konuma Git",
"goToItem": "Öğeye git"
"goToItem": "Öğeye git",
"copyPath": "Kopyalama Yolu"
},
"download": {
"downloadFile": "Dosyayı indir",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "Bir karma algoritma seçin",
"generate": "Oluşturmak",
"errorGeneratingHash": "Hash oluştururken hata oluştu",
"hashCopied": "Panoya kopyalanan karma",
"errorCopyingHash": "Karma kopyalama hatası",
"token": "Jeton",
"onlyOfficeId": "SadeceOfis Kimliği",
"firstLoadTitle": "FileBrowser Quantum'a hoş geldiniz!",
@@ -226,7 +226,6 @@
"renameSuccess": "Öğe başarıyla yeniden adlandırıldı!",
"newFileSuccess": "Dosya başarıyla oluşturuldu!",
"newDirSuccess": "Dizin başarıyla oluşturuldu!",
"copyToClipboardFailed": "Panoya kopyalanamıyor",
"moveFailed": "Taşıma başarısız",
"operationFailed": "İşlem başarısız oldu",
"partialSuccess": "Bazı işlemler başarısız oldu: {succeeded} başarılı, {failed} başarısız",
@@ -241,14 +240,12 @@
"unarchiveMessage": "Arşivin nereye çıkarılacağını seçin.",
"unarchiveDestination": "Özetle:",
"unarchiveSuccess": "Arşiv başarıyla çıkarıldı!",
"downloadFailed": "Yığın indirme başarısız oldu - bu tekrarlanırsa yığın boyutunu küçültmeyi deneyin: {message}"
"downloadFailed": "Yığın indirme başarısız oldu - bu tekrarlanırsa yığın boyutunu küçültmeyi deneyin: {message}",
"confirmPasswordToSaveUser": "Bu kullanıcı değişikliklerini kaydetmek için mevcut şifrenizi girin."
},
"search": {
"smallerThan": "Daha küçük:",
"largerThan": "Daha büyük:",
"helpText1": "Arama, yazdığınız her karakterde gerçekleşir (arama terimleri için minimum 3 karakter).",
"helpText2": "Dizin: Arama, yapılandırılan aralıkta (varsayılan: 5 dakika) otomatik olarak güncellenen dizini kullanır. Program yeni başladığında arama yapmak eksik sonuçlara neden olabilir.",
"noResults": "Dizinlenmiş aramada sonuç bulunamadı.",
"onlyFolders": "Yalnızca Klasörler",
"onlyFiles": "Sadece Dosyalar",
"showOptions": "Seçenekleri Göster",
@@ -258,7 +255,10 @@
"showPreviewImages": "Önizleme resimlerini göster",
"showPreviewImagesDescription": "Kullanılabilir olduğunda arama sonuçları için küçük resim önizlemelerini görüntüleyin ve tüm sonuçlar için daha büyük simgeler gösterin.",
"olderThanDate": "Daha yaşlı:",
"newerThanDate": "Daha yeni:"
"newerThanDate": "Daha yeni:",
"helpText2": "Arama sonuçları dizine eklenmiş dosyaları temel alır, bu nedenle yeni eklenen dosyalar dizine eklenene kadar görünmeyebilir.",
"noResults": "Sonuç bulunamadı.",
"helpText1": "Arama, yazdığınız her karakterde gerçekleşir ({minSearchLength} minimum karakter)."
},
"settings": {
"administrator": "Yönetici",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "OTP başarıyla etkinleştirildi",
"verificationFailed": "Doğrulama Başarısız",
"verificationSucceed": "Doğrulama Başarılı",
"generationFailed": "Nesil Başarısız"
"generationFailed": "Nesil Başarısız",
"passwordRequiredForGeneration": "Kimlik doğrulayıcı kodu oluşturmak için parolanız gereklidir."
},
"general": {
"name": "İsim",
@@ -579,7 +580,6 @@
"results": "Sonuçlar{suffix}",
"users": "Kullanıcılar{suffix}",
"photos": "Fotoğraflar",
"audios": "Ses Dosyaları{suffix}",
"videos": "Videolar{suffix}",
"models": "3D Modeller",
"documents": "Belgeler{suffix}",
@@ -665,7 +665,8 @@
"format": "Biçim{suffix}",
"office": "Ofis{suffix}",
"show": "Göster{suffix}",
"divider": "Bölücü{suffix}"
"divider": "Bölücü{suffix}",
"audio": "Ses{suffix}"
},
"access": {
"rules": "Erişim kuralları",
@@ -717,15 +718,11 @@
"deleteAfterArchiveDescription": "Etkinleştirildiğinde, arşivlerin başarıyla oluşturulması/çıkarılmasından sonra kaynak dosyaları siler",
"hideFilesInTree": "Ağaç gezintisinde dosyaları gizle",
"hideFilesInTreeDescription": "Etkinleştirildiğinde, kenar çubuğu Ağaç Gezintisinde yalnızca klasörler gösterilir",
"previewModelsDescription": "3D modeller için küçük resim önizlemelerini göster, devre dışı bırakılırsa genel simge gösterilir",
"previewAudiosDescription": "Ses dosyaları için küçük resim önizlemelerini göster, devre dışı bırakılırsa genel simge gösterilir",
"setDateFormat": "Tam tarih biçimini ayarlama",
"showHiddenFiles": "Gizli dosyaları göster",
"showHiddenFilesDescription": "Gizli dosyaları listede gösterin (örn. .thumbnails, ProgramData), ancak dizine eklenecek şekilde yapılandırılmışlarsa yine de manuel olarak erişilebilirler.",
"showQuickDownload": "Hızlı erişim için indirme simgesini göster",
"showQuickDownloadDescription": "Anında indirmek için tıklanabilecek her dosya öğesinde hızlı erişim için indirme simgesini gösterin.",
"previewImagesDescription": "Görüntüler için küçük resim önizlemelerini göster, devre dışı bırakılırsa genel simgeyi gösterecektir.",
"previewVideosDescription": "Videolar için küçük resim önizlemelerini göster, devre dışı bırakılırsa genel simgeyi gösterecektir.",
"editOffice": "OnlyOffice ile ofis dosyalarını görüntüleyin ve düzenleyin",
"editOfficeDescription": "Yalnızca ofis etkinleştirildiğinde, dosya görüntüleyici/düzenleyici ofis dosyalarıyla ilgili tüm dosya uzantıları için devre dışı bırakılır.",
"filePreviewOptions": "Dosya önizleme seçenekleri",
@@ -774,7 +771,10 @@
"popupPreviewDescription": "Küçük resimli öğelerin üzerine gelindiğinde kullanıcı arayüzünün üzerinde bir kaplama açılır penceresi gösterilir",
"thumbnailOptions": "Küçük Resim Seçenekleri",
"showThumbnails": "Desteklenen öğeler için küçük resimleri göster",
"showThumbnailsDescription": "Etkinleştirildiğinde, küçük resimler onları destekleyen öğeler için listede gösterilir. Tüm küçük resimleri gizlemek için kapatın."
"showThumbnailsDescription": "Etkinleştirildiğinde, küçük resimler onları destekleyen öğeler için listede gösterilir. Tüm küçük resimleri gizlemek için kapatın.",
"showCopyPath": "Bağlam menüsünde Yolu Kopyala'yı göster",
"showCopyPathDescription": "Dosya ve klasör bağlam menüsünde Yolu Kopyala eylemini gösterin.",
"previewDescription": "{filetype} için küçük resim önizlemelerini göster, devre dışı bırakılırsa genel simgeyi gösterecektir."
},
"editor": {
"uninitialized": "Düzenleyici başlatılamadı. Lütfen yeniden yükleyin.",
@@ -844,8 +844,6 @@
"showingResults": "{count} sonuçları gösteriliyor",
"noResults": "Simge bulunamadı",
"tryDifferentSearch": "Farklı bir arama terimi deneyin",
"copiedToClipboard": "'{name}' panoya kopyalandı!",
"copyFailed": "Panoya kopyalanamadı",
"custom": "Özel",
"useCustomIcon": "Özel simge kullanın: {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "Скопійовано в буфер обміну!",
"copyFailed": "Не вдалося скопіювати в буфер обміну",
"copyFile": "Копіювати файл",
"copyToClipboard": "Копіювати в буфер обміну",
"moveFile": "Перемістити файл",
@@ -33,7 +34,8 @@
"showMore": "Показати додаткові параметри",
"showLess": "Приховати додаткові параметри",
"goToSource": "Перейдіть до Розташування джерела",
"goToItem": "Перейти до пункту"
"goToItem": "Перейти до пункту",
"copyPath": "Шлях копіювання"
},
"download": {
"downloadFile": "Завантажити файл",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "Виберіть алгоритм хешування",
"generate": "Згенерувати",
"errorGeneratingHash": "Хеш, що генерує помилки",
"hashCopied": "Хеш скопійовано до буфера обміну",
"errorCopyingHash": "Помилка копіювання хешу",
"token": "Токен.",
"onlyOfficeId": "OnlyOffice ID",
"firstLoadTitle": "Ласкаво просимо до FileBrowser Quantum!",
@@ -226,7 +226,6 @@
"renameSuccess": "Елемент перейменовано успішно!",
"newFileSuccess": "Файл успішно створено!",
"newDirSuccess": "Каталог створено успішно!",
"copyToClipboardFailed": "Неможливо скопіювати в буфер обміну",
"moveFailed": "Переміщення не вдалося",
"operationFailed": "Операція не вдалася",
"partialSuccess": "Деякі операції не вдалися: {succeeded} вдалося, {failed} не вдалося",
@@ -241,14 +240,12 @@
"unarchiveMessage": "Виберіть, куди розпакувати архів.",
"unarchiveDestination": "Витягнути до:",
"unarchiveSuccess": "Архів успішно розпаковано!",
"downloadFailed": "Не вдалося завантажити фрагмент - якщо це повториться, спробуйте зменшити розмір фрагмента: {message}"
"downloadFailed": "Не вдалося завантажити фрагмент - якщо це повториться, спробуйте зменшити розмір фрагмента: {message}",
"confirmPasswordToSaveUser": "Введіть ваш поточний пароль, щоб зберегти зміни користувача."
},
"search": {
"smallerThan": "Менше, ніж:",
"largerThan": "Більше, ніж:",
"helpText1": "Пошук відбувається за кожним введеним символом (мінімум 3 символи для пошукових термінів).",
"helpText2": "Індекс: Пошук використовує індекс, який автоматично оновлюється з заданим інтервалом (за замовчуванням: 5 хвилин). Пошук, коли програма щойно запущена, може призвести до неповних результатів.",
"noResults": "В індексованому пошуку результатів не знайдено.",
"onlyFolders": "Тільки папки",
"onlyFiles": "Тільки файли",
"showOptions": "Показати параметри",
@@ -258,7 +255,10 @@
"showPreviewImages": "Показати попередній перегляд зображень",
"showPreviewImagesDescription": "Відображати попередній перегляд мініатюр для результатів пошуку, якщо вони доступні, і показувати більші іконки для всіх результатів.",
"olderThanDate": "Старіше, ніж:",
"newerThanDate": "Новіше, ніж:"
"newerThanDate": "Новіше, ніж:",
"helpText2": "Результати пошуку базуються на проіндексованих файлах, тому нові додані файли можуть не з'являтися, поки вони не будуть проіндексовані.",
"noResults": "Результатів не знайдено.",
"helpText1": "Пошук відбувається за кожним введеним символом ({minSearchLength} мінімум символів)."
},
"settings": {
"administrator": "Адміністратор",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "OTP успішно ввімкнено",
"verificationFailed": "Перевірка не вдалася",
"verificationSucceed": "Перевірка пройшла успішно",
"generationFailed": "Покоління зазнало невдачі"
"generationFailed": "Покоління зазнало невдачі",
"passwordRequiredForGeneration": "Ваш пароль необхідний для генерації коду автентифікатора."
},
"fileLoading": {
"title": "Завантаження та вивантаження",
@@ -571,15 +572,11 @@
"deleteAfterArchiveDescription": "Якщо ввімкнено, видалятиме вихідні файли після успішного створення/вилучення архівів",
"hideFilesInTree": "Приховувати файли в дереві навігації",
"hideFilesInTreeDescription": "Якщо ввімкнено, у деревоподібній навігації бічної панелі відображаються лише папки",
"previewModelsDescription": "Показувати мініатюри для 3D-моделей, якщо вимкнено, буде показано стандартну іконку",
"previewAudiosDescription": "Показувати мініатюри для аудіофайлів, якщо вимкнено, буде показано стандартну іконку",
"setDateFormat": "Встановіть точний формат дати",
"showHiddenFiles": "Показати приховані файли",
"showHiddenFilesDescription": "Показувати приховані файли у лістингу (наприклад, .thumbnails, ProgramData), але вони все ще доступні вручну, якщо їх налаштовано на індексування.",
"showQuickDownload": "Показати іконку завантаження для швидкого доступу",
"showQuickDownloadDescription": "Показувати іконку завантаження для швидкого доступу до кожного елемента файлу, яку можна натиснути, щоб миттєво завантажити.",
"previewImagesDescription": "Показувати попередній перегляд мініатюр для зображень, якщо вимкнено, буде показано загальну іконку.",
"previewVideosDescription": "Показувати попередній перегляд мініатюр для відео, якщо вимкнено, буде показано загальну іконку.",
"editOffice": "Переглядайте та редагуйте офісні файли за допомогою OnlyOffice",
"editOfficeDescription": "Якщо увімкнено лише офіс, переглядач/редактор файлів буде вимкнено для всіх розширень, пов'язаних з офісними файлами.",
"filePreviewOptions": "Параметри попереднього перегляду файлів",
@@ -628,7 +625,10 @@
"popupPreviewDescription": "Спливаюче вікно накладання відображатиметься поверх інтерфейсу при наведенні курсору на елементи з мініатюрами",
"thumbnailOptions": "Параметри мініатюр",
"showThumbnails": "Показати мініатюри для підтримуваних елементів",
"showThumbnailsDescription": "Якщо увімкнено, мініатюри показуються у списку для елементів, які їх підтримують. Вимкніть, щоб приховати всі мініатюри."
"showThumbnailsDescription": "Якщо увімкнено, мініатюри показуються у списку для елементів, які їх підтримують. Вимкніть, щоб приховати всі мініатюри.",
"showCopyPath": "Показати шлях копіювання в контекстному меню",
"showCopyPathDescription": "Показати дію Копіювати шлях у контекстному меню файлів і папок.",
"previewDescription": "Показувати попередній перегляд мініатюр для {filetype}, якщо вимкнено, буде показано загальну піктограму."
},
"general": {
"name": "Ім'я",
@@ -665,7 +665,6 @@
"results": "Результати{suffix}",
"users": "Користувачі{suffix}",
"photos": "Фотографії",
"audios": "Аудіо{suffix}",
"models": "3D-моделі",
"videos": "Відео{suffix}",
"documents": "Документи{suffix}",
@@ -751,7 +750,8 @@
"format": "Формат{suffix}",
"office": "Офіс{suffix}",
"show": "Показати{suffix}",
"divider": "Перегородка{suffix}"
"divider": "Перегородка{suffix}",
"audio": "Аудіо{suffix}"
},
"access": {
"rules": "Правила доступу",
@@ -844,8 +844,6 @@
"showingResults": "Показує результати {count} ",
"noResults": "Не знайдено жодної піктограми",
"tryDifferentSearch": "Спробуйте інший пошуковий запит",
"copiedToClipboard": "Скопіюйте '{name}' в буфер обміну!",
"copyFailed": "Не вдалося скопіювати в буфер обміну",
"custom": "Нестандартний",
"useCustomIcon": "Використовуйте власну іконку: {name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "已复制到剪贴板!",
"copyFailed": "复制到剪贴板失败",
"copyFile": "复制文件",
"copyToClipboard": "复制到剪贴板",
"moveFile": "移动文件",
@@ -33,7 +34,8 @@
"showMore": "显示高级选项",
"showLess": "隐藏高级选项",
"goToSource": "转到源位置",
"goToItem": "转到项目"
"goToItem": "转到项目",
"copyPath": "复制路径"
},
"download": {
"downloadFile": "下载文件",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "选择散列算法",
"generate": "生成",
"errorGeneratingHash": "错误生成哈希值",
"hashCopied": "哈希值复制到剪贴板",
"errorCopyingHash": "复制哈希值出错",
"token": "代币",
"onlyOfficeId": "唯一办公室 ID",
"firstLoadTitle": "欢迎访问 FileBrowser Quantum!",
@@ -226,7 +226,6 @@
"renameSuccess": "项目重命名成功!",
"newFileSuccess": "文件创建成功!",
"newDirSuccess": "目录创建成功!",
"copyToClipboardFailed": "无法复制到剪贴板",
"moveFailed": "移动失败",
"operationFailed": "操作失败",
"partialSuccess": "某些操作失败:{succeeded} 成功,{failed} 失败",
@@ -241,14 +240,12 @@
"unarchiveMessage": "选择提取存档的位置。",
"unarchiveDestination": "摘录至",
"unarchiveSuccess": "档案提取成功!",
"downloadFailed": "分块下载失败 - 如果重复出现这种情况,请尝试减小分块大小:{message}"
"downloadFailed": "分块下载失败 - 如果重复出现这种情况,请尝试减小分块大小:{message}",
"confirmPasswordToSaveUser": "输入您当前的密码以保存这些用户更改。"
},
"search": {
"smallerThan": "小于",
"largerThan": "大于:",
"helpText1": "您键入的每个字符都会进行搜索(搜索词最少 3 个字符)。",
"helpText2": "索引:利用索引进行搜索,索引会根据配置的时间间隔(默认为 5 分钟)自动更新。程序刚启动时进行搜索可能会导致搜索结果不完整。",
"noResults": "索引搜索未找到结果。",
"onlyFolders": "仅文件夹",
"onlyFiles": "仅文件",
"showOptions": "显示选项",
@@ -258,7 +255,10 @@
"showPreviewImages": "显示预览图像",
"showPreviewImagesDescription": "在可用时显示搜索结果的缩略图预览,并为所有结果显示更大的图标。",
"olderThanDate": "比",
"newerThanDate": "新于:"
"newerThanDate": "新于:",
"helpText2": "搜索结果基于已编入索引的文件,因此新添加的文件在编入索引之前可能不会出现。",
"noResults": "未找到结果。",
"helpText1": "您输入的每个字符都会进行搜索({minSearchLength} 字符最小值)。"
},
"settings": {
"scopes": "定义允许的来源及其中的用户范围",
@@ -542,7 +542,8 @@
"enabledSuccessfully": "成功启用 OTP",
"verificationFailed": "验证失败",
"verificationSucceed": "验证成功",
"generationFailed": "一代失败"
"generationFailed": "一代失败",
"passwordRequiredForGeneration": "生成验证码需要您输入密码。"
},
"fileLoading": {
"title": "上传和下载",
@@ -571,15 +572,11 @@
"deleteAfterArchiveDescription": "启用后,将在成功创建/提取存档后删除源文件",
"hideFilesInTree": "在树形导航中隐藏文件",
"hideFilesInTreeDescription": "启用后,侧边栏树形导航中仅显示文件夹",
"previewModelsDescription": "显示3D模型的缩略图预览,如果禁用则显示通用图标",
"previewAudiosDescription": "显示音频的缩略图预览,如果禁用则显示通用图标",
"setDateFormat": "设置准确的日期格式",
"showHiddenFiles": "显示隐藏文件",
"showHiddenFilesDescription": "在列表中显示隐藏文件(如 .缩略图、ProgramData),但如果配置为索引,这些文件仍可手动访问。",
"showQuickDownload": "显示下载图标以便快速访问",
"showQuickDownloadDescription": "在每个文件项目上显示可快速访问的下载图标,点击后可立即下载。",
"previewImagesDescription": "显示图像缩略图预览,如果禁用,将显示通用图标。",
"previewVideosDescription": "显示视频缩略图预览,如果禁用,将显示通用图标。",
"editOffice": "使用 OnlyOffice 查看和编辑办公文件",
"editOfficeDescription": "只启用 Office 时,文件查看器/编辑器将禁用与 Office 文件相关的所有文件扩展名。",
"filePreviewOptions": "文件预览选项",
@@ -628,7 +625,10 @@
"popupPreviewDescription": "将鼠标悬停在带缩略图的项目上时,用户界面上会显示一个叠加弹出窗口",
"thumbnailOptions": "缩略图选项",
"showThumbnails": "显示支持项目的缩略图",
"showThumbnailsDescription": "启用后,支持缩略图的项目将在列表中显示缩略图。关闭则隐藏所有缩略图。"
"showThumbnailsDescription": "启用后,支持缩略图的项目将在列表中显示缩略图。关闭则隐藏所有缩略图。",
"showCopyPath": "在上下文菜单中显示复制路径",
"showCopyPathDescription": "在文件和文件夹上下文菜单中显示复制路径操作。",
"previewDescription": "显示{filetype} 的缩略图预览,如果禁用,将显示通用图标。"
},
"general": {
"name": "名称",
@@ -665,7 +665,6 @@
"results": "成果{suffix}",
"users": "用户{suffix}",
"photos": "照片",
"audios": "音频{suffix}",
"models": "3D模型",
"videos": "视频{suffix}",
"documents": "文件{suffix}",
@@ -751,7 +750,8 @@
"format": "格式{suffix}",
"office": "办公室{suffix}",
"show": "显示{suffix}",
"divider": "分割器{suffix}"
"divider": "分割器{suffix}",
"audio": "音频{suffix}"
},
"access": {
"rules": "访问规则",
@@ -844,8 +844,6 @@
"showingResults": "显示{count} 结果",
"noResults": "未找到图标",
"tryDifferentSearch": "尝试不同的搜索词",
"copiedToClipboard": "将 '{name}' 复制到剪贴板!",
"copyFailed": "复制到剪贴板失败",
"custom": "定制",
"useCustomIcon": "使用自定义图标:{name}",
"iconLibraryUrl": "fonts.google.com/icons",
+17 -19
View File
@@ -1,6 +1,7 @@
{
"buttons": {
"copySuccess": "已複製到剪貼簿!",
"copyFailed": "複製到剪貼簿失敗",
"copyFile": "複製檔案",
"copyToClipboard": "複製到剪貼簿",
"moveFile": "移動檔案",
@@ -33,7 +34,8 @@
"showMore": "顯示進階選項",
"showLess": "隱藏進階選項",
"goToSource": "前往來源位置",
"goToItem": "前往項目"
"goToItem": "前往項目",
"copyPath": "複製路徑"
},
"download": {
"downloadFile": "下載檔案",
@@ -214,8 +216,6 @@
"selectHashAlgorithm": "選擇雜湊演算法",
"generate": "產生",
"errorGeneratingHash": "產生雜湊時出錯",
"hashCopied": "已將雜湊值複製到剪貼簿",
"errorCopyingHash": "複製雜湊時出錯",
"token": "權杖",
"onlyOfficeId": "OnlyOffice ID",
"firstLoadTitle": "歡迎來到 FileBrowser Quantum!",
@@ -226,7 +226,6 @@
"renameSuccess": "項目已成功重新命名!",
"newFileSuccess": "檔案已成功建立!",
"newDirSuccess": "目錄建立成功!",
"copyToClipboardFailed": "無法複製到剪貼簿",
"moveFailed": "移動失敗",
"operationFailed": "操作失敗",
"partialSuccess": "部分作業失敗:成功 {succeeded} 項,失敗 {failed} 項",
@@ -241,14 +240,12 @@
"unarchiveMessage": "選擇解壓縮封存檔的位置。",
"unarchiveDestination": "解壓縮至:",
"unarchiveSuccess": "封存檔解壓縮成功!",
"downloadFailed": "分塊下載失敗 - 如果此情況重複發生,請嘗試減少分塊大小:{message}"
"downloadFailed": "分塊下載失敗 - 如果此情況重複發生,請嘗試減少分塊大小:{message}",
"confirmPasswordToSaveUser": "輸入您目前的密碼以儲存這些使用者變更。"
},
"search": {
"smallerThan": "小於:",
"largerThan": "大於:",
"helpText1": "每輸入一個字元就會觸發搜尋(搜尋字詞至少需 3 個字元)。",
"helpText2": "搜尋會使用索引,索引會依設定的間隔自動更新(預設為 5 分鐘)。如果程式剛啟動就開始搜尋,結果可能不完整。",
"noResults": "在索引搜尋中未找到任何結果。",
"onlyFolders": "僅資料夾",
"onlyFiles": "僅檔案",
"showOptions": "顯示選項",
@@ -258,7 +255,10 @@
"showPreviewImages": "顯示預覽圖片",
"showPreviewImagesDescription": "若可用,會為搜尋結果顯示縮圖預覽,並為所有結果顯示較大的圖示。",
"olderThanDate": "年紀比",
"newerThanDate": "比較新:"
"newerThanDate": "比較新:",
"helpText2": "搜尋結果以已編入索引的檔案為基礎,因此新加入的檔案在編入索引前可能不會出現。",
"noResults": "未找到結果。",
"helpText1": "搜尋會發生在您輸入的每個字元上({minSearchLength} 字元最小值)。"
},
"settings": {
"administrator": "管理員",
@@ -540,15 +540,11 @@
"deleteAfterArchiveDescription": "啟用後,成功建立或解壓縮封存檔後會刪除來源檔案。",
"hideFilesInTree": "在樹狀導覽中隱藏檔案",
"hideFilesInTreeDescription": "啟用後,側邊欄樹狀導覽中僅顯示資料夾。",
"previewModelsDescription": "顯示 3D 模型縮圖;停用時改顯示一般圖示。",
"previewAudiosDescription": "顯示音訊縮圖;停用時改顯示一般圖示。",
"setDateFormat": "設定精確的日期格式",
"showHiddenFiles": "顯示隱藏檔案",
"showHiddenFilesDescription": "在清單中顯示隱藏檔案(例如 .thumbnails、ProgramData),但若已設定索引,仍可手動存取。",
"showQuickDownload": "顯示下載圖示以便快速存取",
"showQuickDownloadDescription": "在每個可下載的檔案項目上顯示下載圖示,按一下即可立即下載。",
"previewImagesDescription": "顯示圖片縮圖;停用時改顯示一般圖示。",
"previewVideosDescription": "顯示影片縮圖;停用時改顯示一般圖示。",
"editOffice": "使用 OnlyOffice 檢視和編輯 Office 檔案",
"editOfficeDescription": "啟用 OnlyOffice 時,所有 Office 類型副檔名都會停用內建檔案檢視器/編輯器。",
"filePreviewOptions": "檔案預覽選項",
@@ -597,7 +593,10 @@
"popupPreviewDescription": "滑鼠停留在有縮圖的項目上時,會以覆蓋式彈出視窗顯示預覽。",
"thumbnailOptions": "縮圖選項",
"showThumbnails": "顯示支援項目的縮圖",
"showThumbnailsDescription": "啟用時,縮圖會顯示在支援縮圖的項目清單中。關閉可隱藏所有縮圖。"
"showThumbnailsDescription": "啟用時,縮圖會顯示在支援縮圖的項目清單中。關閉可隱藏所有縮圖。",
"showCopyPath": "在上下文功能表中顯示複製路徑",
"showCopyPathDescription": "在檔案和資料夾上下文功能表中顯示複製路徑動作。",
"previewDescription": "顯示{filetype} 的縮圖預覽,如果停用會顯示一般圖示。"
},
"otp": {
"name": "雙因素驗證",
@@ -608,7 +607,8 @@
"enabledSuccessfully": "已成功啟用雙因素驗證",
"verificationFailed": "驗證失敗",
"verificationSucceed": "驗證成功",
"generationFailed": "產生驗證碼失敗"
"generationFailed": "產生驗證碼失敗",
"passwordRequiredForGeneration": "生成驗證碼需要您的密碼。"
},
"fileLoading": {
"title": "上傳與下載",
@@ -665,7 +665,6 @@
"results": "結果{suffix}",
"users": "使用者{suffix}",
"photos": "相片",
"audios": "音訊{suffix}",
"videos": "影片{suffix}",
"models": "3D 模型",
"documents": "文件{suffix}",
@@ -751,7 +750,8 @@
"format": "格式{suffix}",
"office": "Office{suffix}",
"show": "顯示{suffix}",
"divider": "分隔線{suffix}"
"divider": "分隔線{suffix}",
"audio": "音訊{suffix}"
},
"access": {
"rules": "存取規則",
@@ -844,8 +844,6 @@
"showingResults": "顯示 {count} 筆結果",
"noResults": "找不到圖示",
"tryDifferentSearch": "嘗試不同的搜尋字詞",
"copiedToClipboard": "已將「{name}」複製到剪貼簿!",
"copyFailed": "複製到剪貼簿失敗",
"custom": "自訂",
"useCustomIcon": "使用自訂圖示:{name}",
"iconLibraryUrl": "fonts.google.com/icons",
Loaded 100 of 126 files, more files were not shown because too many files have changed in this diff. Show more