mirror of
https://github.com/gtsteffaniak/filebrowser.git
synced 2026-11-05 01:00:11 -05:00
Compare commits
13
Commits
main
...
v1.1.3-stable
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72d0990703 | ||
|
|
207bc39bf7 | ||
|
|
332888b993 | ||
|
|
0219b891a2 | ||
|
|
eee7b7828f | ||
|
|
7f40776c28 | ||
|
|
9ddafd2df7 | ||
|
|
82da2c31ff | ||
|
|
783b9962bb | ||
|
|
65d2e4a05b | ||
|
|
2ad25a0459 | ||
|
|
eedf63a8de | ||
|
|
58b1d53bbb |
No files matched your search
@@ -2,6 +2,35 @@
|
||||
|
||||
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.1.3-stable
|
||||
|
||||
**Security**:
|
||||
- addresses https://github.com/gtsteffaniak/filebrowser/security/advisories/GHSA-8vrh-3pm2-v4v6
|
||||
|
||||
## v1.1.2-stable
|
||||
|
||||
**BugFixes**:
|
||||
- fix transversal vulnerability
|
||||
- fix image generation concurrency limits issue
|
||||
|
||||
## v1.1.6-beta
|
||||
|
||||
**Notes**:
|
||||
- "showHidden" is now a backend attribute. Shares will NOT show hidden files by default unless configured to.
|
||||
- improved listing view UI performance, especially for folders with many items. (#1773)
|
||||
|
||||
**BugFixes**:
|
||||
- SortBy config is not respected when navigating next/previous (#1786)
|
||||
- unauthenticated users unable to preview files (#1792)
|
||||
- File permission not correctly applied( #1790)
|
||||
- API Key loses "download" permission after creation despite being enabled (#1799)
|
||||
- never exclude root folder (#1749)
|
||||
- sources with "files" name not showing after 1.1.x update (#1772)
|
||||
- Symbolic links can't be deleted from webui. (#1566)
|
||||
- panic: close of closed channel during indexing (#1771)
|
||||
- Public sharing returns 404 errors for album art (#1792)
|
||||
- Directories with some symbols in their name "fail to open" (#1808)
|
||||
|
||||
## v1.1.5-beta
|
||||
|
||||
**Notes**:
|
||||
|
||||
@@ -28,17 +28,30 @@ import (
|
||||
|
||||
func FileInfoFaster(opts utils.FileOptions, access *access.Storage) (*iteminfo.ExtendedFileInfo, error) {
|
||||
response := &iteminfo.ExtendedFileInfo{}
|
||||
index := indexing.GetIndex(opts.Source)
|
||||
if index == nil {
|
||||
|
||||
// Get index
|
||||
idx := indexing.GetIndex(opts.Source)
|
||||
if idx == nil {
|
||||
return response, fmt.Errorf("could not get index: %v ", opts.Source)
|
||||
}
|
||||
|
||||
// Validate path to prevent traversal attacks
|
||||
// Rule 1: Do Not Use User Input in File Paths (without validation)
|
||||
// Clean the path and check for escape attempts
|
||||
cleanPath := filepath.Clean(opts.Path)
|
||||
if strings.Contains(cleanPath, "..") {
|
||||
return response, fmt.Errorf("invalid path: path traversal detected")
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(opts.Path, "/") {
|
||||
opts.Path = "/" + opts.Path
|
||||
}
|
||||
realPath, isDir, err := index.GetRealPath(opts.Path)
|
||||
if err != nil {
|
||||
|
||||
realPath, isDir, err := idx.GetRealPath(opts.Path)
|
||||
if err != nil && opts.FollowSymlinks {
|
||||
return response, fmt.Errorf("could not get real path for requested path: %v, error: %v", opts.Path, err)
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(opts.Path, "/") && isDir {
|
||||
opts.Path = opts.Path + "/"
|
||||
}
|
||||
@@ -46,28 +59,21 @@ func FileInfoFaster(opts utils.FileOptions, access *access.Storage) (*iteminfo.E
|
||||
var info *iteminfo.FileInfo
|
||||
|
||||
// Check if path is viewable (allows filesystem access without indexing)
|
||||
isViewable := index.IsViewable(isDir, opts.Path)
|
||||
isViewable := idx.IsViewable(isDir, opts.Path)
|
||||
|
||||
// For non-viewable paths, verify they are indexed
|
||||
// Skip this check if indexing is disabled for the entire source
|
||||
if !isViewable && !index.Config.DisableIndexing {
|
||||
err = index.RefreshFileInfo(opts)
|
||||
if !isViewable && !idx.Config.DisableIndexing {
|
||||
err = idx.RefreshFileInfo(opts)
|
||||
if err != nil {
|
||||
return response, fmt.Errorf("path not accessible: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if isDir {
|
||||
info, err = index.GetFsDirInfo(opts.Path)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
} else {
|
||||
// For files, get info from parent directory to ensure HasPreview is set correctly
|
||||
info, err = index.GetFsDirInfo(opts.Path)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
info, err = idx.GetFsInfo(opts.Path, opts.FollowSymlinks)
|
||||
if err != nil {
|
||||
fmt.Println("Graham 2: ", err)
|
||||
return response, err
|
||||
}
|
||||
|
||||
response.FileInfo = *info
|
||||
@@ -75,12 +81,31 @@ func FileInfoFaster(opts utils.FileOptions, access *access.Storage) (*iteminfo.E
|
||||
response.Source = opts.Source
|
||||
|
||||
if access != nil {
|
||||
err := access.CheckChildItemAccess(response, index, opts.Username)
|
||||
err := access.CheckChildItemAccess(response, idx, opts.Username)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
}
|
||||
|
||||
// Filter hidden files if ShowHidden is false
|
||||
if !opts.ShowHidden && isDir {
|
||||
filteredFiles := []iteminfo.ExtendedItemInfo{}
|
||||
for _, file := range response.Files {
|
||||
if !file.Hidden {
|
||||
filteredFiles = append(filteredFiles, file)
|
||||
}
|
||||
}
|
||||
response.Files = filteredFiles
|
||||
|
||||
filteredFolders := []iteminfo.ItemInfo{}
|
||||
for _, folder := range response.Folders {
|
||||
if !folder.Hidden {
|
||||
filteredFolders = append(filteredFolders, folder)
|
||||
}
|
||||
}
|
||||
response.Folders = filteredFolders
|
||||
}
|
||||
|
||||
// For directories, populate metadata for audio/video files ONLY if explicitly requested
|
||||
// This avoids expensive ffprobe calls on every directory listing
|
||||
if isDir && opts.Metadata {
|
||||
@@ -101,7 +126,7 @@ func FileInfoFaster(opts utils.FileOptions, access *access.Storage) (*iteminfo.E
|
||||
|
||||
if isItemAudio || isItemVideo {
|
||||
// Get the real path for this file
|
||||
itemRealPath, _, _ := index.GetRealPath(opts.Path, fileItem.Name)
|
||||
itemRealPath, _, _ := idx.GetRealPath(opts.Path, fileItem.Name)
|
||||
|
||||
// Capture loop variables in local copies to avoid closure issues
|
||||
item := fileItem
|
||||
@@ -148,7 +173,7 @@ func FileInfoFaster(opts utils.FileOptions, access *access.Storage) (*iteminfo.E
|
||||
// Extract content/metadata when explicitly requested OR for single file audio/video requests
|
||||
isAudioVideo := strings.HasPrefix(info.Type, "audio") || strings.HasPrefix(info.Type, "video")
|
||||
if opts.Content || opts.Metadata || (!isDir && isAudioVideo) {
|
||||
processContent(response, index, opts)
|
||||
processContent(response, idx, opts)
|
||||
}
|
||||
|
||||
if settings.Config.Integrations.OnlyOffice.Secret != "" && info.Type != "directory" && iteminfo.IsOnlyOffice(info.Name) {
|
||||
@@ -634,8 +659,8 @@ func WriteFile(source, path string, in io.Reader) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Explicitly set directory permissions to bypass umask
|
||||
err = os.Chmod(realPath, fileutils.PermDir)
|
||||
// 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)
|
||||
|
||||
@@ -55,4 +55,6 @@ type FileOptions struct {
|
||||
Metadata bool // whether to get metadata
|
||||
ExtractEmbeddedSubtitles bool // whether to extract embedded subtitles from media files
|
||||
AlbumArt bool // whether to get album art from media files
|
||||
ShowHidden bool // whether to show hidden files (true = show, false = hide)
|
||||
FollowSymlinks bool // whether to follow symlinks
|
||||
}
|
||||
@@ -149,3 +149,17 @@ func CheckPathExists(realPath string) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// SanitizeUserPath prevents path traversal attacks by cleaning and validating user input.
|
||||
// Rule 1: Do Not Use User Input in File Paths (without validation)
|
||||
func SanitizeUserPath(userPath string) (string, error) {
|
||||
// Clean resolves . and .. segments
|
||||
clean := filepath.Clean(userPath)
|
||||
|
||||
// Reject absolute paths or attempts to escape scope
|
||||
if strings.HasPrefix(clean, "..") {
|
||||
return "", fmt.Errorf("invalid path")
|
||||
}
|
||||
|
||||
return clean, nil
|
||||
}
|
||||
@@ -41,6 +41,7 @@ type CommonShare struct {
|
||||
AllowReplacements bool `json:"allowReplacements,omitempty"` // allow replacements of files
|
||||
SidebarLinks []users.SidebarLink `json:"sidebarLinks"` // customizable sidebar links
|
||||
HasPassword bool `json:"hasPassword,omitempty"`
|
||||
ShowHidden bool `json:"showHidden,omitempty"` // show hidden files in share (true = show, false = hide)
|
||||
}
|
||||
type CreateBody struct {
|
||||
CommonShare
|
||||
|
||||
+16
-16
@@ -8,26 +8,26 @@ require (
|
||||
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-playground/validator/v10 v10.29.0
|
||||
github.com/goccy/go-yaml v1.19.0
|
||||
github.com/go-playground/validator/v10 v10.30.1
|
||||
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 v0.2.4
|
||||
github.com/gtsteffaniak/go-logger v1.0.2
|
||||
github.com/kovidgoyal/imaging v1.7.2
|
||||
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.46.0
|
||||
golang.org/x/image v0.34.0
|
||||
golang.org/x/mod v0.31.0
|
||||
golang.org/x/crypto v0.47.0
|
||||
golang.org/x/image v0.35.0
|
||||
golang.org/x/mod v0.32.0
|
||||
golang.org/x/oauth2 v0.34.0
|
||||
golang.org/x/sys v0.39.0
|
||||
golang.org/x/sys v0.40.0
|
||||
golang.org/x/time v0.14.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/sqlite v1.40.1
|
||||
modernc.org/sqlite v1.44.3
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -100,14 +100,14 @@ require (
|
||||
github.com/firefart/nonamedreturns v1.0.6 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/fzipp/gocyclo v0.6.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.11 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
|
||||
github.com/ghostiam/protogetter v0.3.17 // indirect
|
||||
github.com/go-critic/go-critic v0.14.2 // 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.4 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.4 // indirect
|
||||
github.com/go-openapi/spec v0.22.2 // indirect
|
||||
github.com/go-openapi/spec v0.22.3 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
|
||||
@@ -130,7 +130,7 @@ require (
|
||||
github.com/godoc-lint/godoc-lint v0.10.1 // indirect
|
||||
github.com/gofrs/flock v0.13.0 // indirect
|
||||
github.com/gohugoio/hugo v0.149.1 // indirect
|
||||
github.com/golang/geo v0.0.0-20251209161508-25c597310d4b // indirect
|
||||
github.com/golang/geo v0.0.0-20260129164528-943061e2742c // 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
|
||||
@@ -264,17 +264,17 @@ require (
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 // indirect
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/net v0.49.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
golang.org/x/tools v0.40.0 // indirect
|
||||
golang.org/x/text v0.33.0 // indirect
|
||||
golang.org/x/tools v0.41.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.6.1 // indirect
|
||||
modernc.org/libc v1.67.1 // indirect
|
||||
modernc.org/libc v1.67.7 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
mvdan.cc/gofumpt v0.9.2 // indirect
|
||||
|
||||
+39
-94
@@ -195,8 +195,6 @@ 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.16.0 h1:qRQUCFstKpXwmEjDQTIbyY/5jF00+asXzSkmkoa/mow=
|
||||
github.com/coreos/go-oidc/v3 v3.16.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
|
||||
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/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
@@ -263,8 +261,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
|
||||
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
|
||||
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
|
||||
github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gen2brain/go-fitz v1.24.15 h1:sJNB1MOWkqnzzENPHggFpgxTwW0+S5WF/rM5wUBpJWo=
|
||||
github.com/gen2brain/go-fitz v1.24.15/go.mod h1:SftkiVbTHqF141DuiLwBBM65zP7ig6AVDQpf2WlHamo=
|
||||
github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ=
|
||||
@@ -294,48 +292,27 @@ 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.3 h1:dKMwfV4fmt6Ah90zloTbUKWMD+0he+12XYAsPotrkn8=
|
||||
github.com/go-openapi/jsonpointer v0.22.3/go.mod h1:0lBbqeRsQ5lIanv3LHZBrmRGHLHcQoOXQnf88fHlGWo=
|
||||
github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4=
|
||||
github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80=
|
||||
github.com/go-openapi/jsonreference v0.21.3 h1:96Dn+MRPa0nYAR8DR1E03SblB5FJvh7W6krPI0Z7qMc=
|
||||
github.com/go-openapi/jsonreference v0.21.3/go.mod h1:RqkUP0MrLf37HqxZxrIAtTWW4ZJIK1VzduhXYBEeGc4=
|
||||
github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8=
|
||||
github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
|
||||
github.com/go-openapi/spec v0.22.1 h1:beZMa5AVQzRspNjvhe5aG1/XyBSMeX1eEOs7dMoXh/k=
|
||||
github.com/go-openapi/spec v0.22.1/go.mod h1:c7aeIQT175dVowfp7FeCvXXnjN/MrpaONStibD2WtDA=
|
||||
github.com/go-openapi/spec v0.22.2 h1:KEU4Fb+Lp1qg0V4MxrSCPv403ZjBl8Lx1a83gIPU8Qc=
|
||||
github.com/go-openapi/spec v0.22.2/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs=
|
||||
github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc=
|
||||
github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag/conv v0.25.3 h1:PcB18wwfba7MN5BVlBIV+VxvUUeC2kEuCEyJ2/t2X7E=
|
||||
github.com/go-openapi/swag/conv v0.25.3/go.mod h1:n4Ibfwhn8NJnPXNRhBO5Cqb9ez7alBR40JS4rbASUPU=
|
||||
github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
|
||||
github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
|
||||
github.com/go-openapi/swag/jsonname v0.25.3 h1:U20VKDS74HiPaLV7UZkztpyVOw3JNVsit+w+gTXRj0A=
|
||||
github.com/go-openapi/swag/jsonname v0.25.3/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.3 h1:kV7wer79KXUM4Ea4tBdAVTU842Rg6tWstX3QbM4fGdw=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.3/go.mod h1:ILcKqe4HC1VEZmJx51cVuZQ6MF8QvdfXsQfiaCs0z9o=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.3 h1:/i3E9hBujtXfHy91rjtwJ7Fgv5TuDHgnSrYjhFxwxOw=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.3/go.mod h1:8kYfCR2rHyOj25HVvxL5Nm8wkfzggddgjZm6RgjT8Ao=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo=
|
||||
github.com/go-openapi/swag/loading v0.25.3 h1:Nn65Zlzf4854MY6Ft0JdNrtnHh2bdcS/tXckpSnOb2Y=
|
||||
github.com/go-openapi/swag/loading v0.25.3/go.mod h1:xajJ5P4Ang+cwM5gKFrHBgkEDWfLcsAKepIuzTmOb/c=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
|
||||
github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
|
||||
github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
|
||||
github.com/go-openapi/swag/stringutils v0.25.3 h1:nAmWq1fUTWl/XiaEPwALjp/8BPZJun70iDHRNq/sH6w=
|
||||
github.com/go-openapi/swag/stringutils v0.25.3/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
|
||||
github.com/go-openapi/swag/typeutils v0.25.3 h1:2w4mEEo7DQt3V4veWMZw0yTPQibiL3ri2fdDV4t2TQc=
|
||||
github.com/go-openapi/swag/typeutils v0.25.3/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.3 h1:LKTJjCn/W1ZfMec0XDL4Vxh8kyAnv1orH5F2OREDUrg=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.3/go.mod h1:Y7QN6Wc5DOBXK14/xeo1cQlq0EA0wvLoSv13gDQoCao=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
|
||||
@@ -348,10 +325,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
||||
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.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
|
||||
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
|
||||
github.com/go-playground/validator/v10 v10.29.0 h1:lQlF5VNJWNlRbRZNeOIkWElR+1LL/OuHcc0Kp14w1xk=
|
||||
github.com/go-playground/validator/v10 v10.29.0/go.mod h1:D6QxqeMlgIPuT02L66f2ccrZ7AGgHkzKmmTMZhk/Kc4=
|
||||
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-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=
|
||||
@@ -384,10 +359,8 @@ github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4
|
||||
github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/goccy/go-yaml v1.19.0 h1:EmkZ9RIsX+Uq4DYFowegAuJo8+xdX3T/2dwNPXbxEYE=
|
||||
github.com/goccy/go-yaml v1.19.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/godoc-lint/godoc-lint v0.10.1 h1:ZPUVzlDtJfA+P688JfPJPkI/SuzcBr/753yGIk5bOPA=
|
||||
github.com/godoc-lint/godoc-lint v0.10.1/go.mod h1:KleLcHu/CGSvkjUH2RvZyoK1MBC7pDQg4NxMYLcBBsw=
|
||||
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
|
||||
@@ -414,10 +387,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-20251117194806-05dcfdd28b33 h1:1iCpF+lKihFpyN/Ujqvy9cWVlB516bGZItvb3l9HOSo=
|
||||
github.com/golang/geo v0.0.0-20251117194806-05dcfdd28b33/go.mod h1:Mymr9kRGDc64JPr03TSZmuIBODZ3KyswLzm1xL0HFA8=
|
||||
github.com/golang/geo v0.0.0-20251209161508-25c597310d4b h1:6y9D6yfaR5FyqoNoV2S+XJyhzeMUlkdIeUX1Ssj0FJQ=
|
||||
github.com/golang/geo v0.0.0-20251209161508-25c597310d4b/go.mod h1:Mymr9kRGDc64JPr03TSZmuIBODZ3KyswLzm1xL0HFA8=
|
||||
github.com/golang/geo v0.0.0-20260129164528-943061e2742c h1:ysO2h2Odnl1AJM1I2Lm/fa6JvO0pECMSt2CwBaa+ITo=
|
||||
github.com/golang/geo v0.0.0-20260129164528-943061e2742c/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=
|
||||
@@ -521,10 +492,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 v0.2.3 h1:8nSC5xSMnMYQI61tsBS1tARpnkv/rOh5lzEXtAG72Nw=
|
||||
github.com/gtsteffaniak/go-logger v0.2.3/go.mod h1:rapapTju4D7bwt6h4JOdZNUXDjnp0WkTBd6TN5yWglI=
|
||||
github.com/gtsteffaniak/go-logger v0.2.4 h1:CGioQehoo84xufxtS57gqEAYcXdLc5hzA71CPvGQ/3s=
|
||||
github.com/gtsteffaniak/go-logger v0.2.4/go.mod h1:rapapTju4D7bwt6h4JOdZNUXDjnp0WkTBd6TN5yWglI=
|
||||
github.com/gtsteffaniak/go-logger v1.0.2 h1:S4AgR7yDu2geD8y2D9NyJVOBqpZRnl37r2mBi7GDNR0=
|
||||
github.com/gtsteffaniak/go-logger v1.0.2/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=
|
||||
@@ -585,8 +554,8 @@ github.com/kovidgoyal/go-shm v1.0.0 h1:HJEel9D1F9YhULvClEHJLawoRSj/1u/EDV7MJbBPg
|
||||
github.com/kovidgoyal/go-shm v1.0.0/go.mod h1:Yzb80Xf9L3kaoB2RGok9hHwMIt7Oif61kT6t3+VnZds=
|
||||
github.com/kovidgoyal/imaging v1.7.2 h1:mmT6k6Az3mC6dbqdZ6Q9KQCdZFWTAQ+q97NyGZgJ/2c=
|
||||
github.com/kovidgoyal/imaging v1.7.2/go.mod h1:GdkCORjfZMMGFY0Pb7TDmRhj7PDhxF/QShKukSCj0VU=
|
||||
github.com/kovidgoyal/imaging v1.8.17 h1:IDc7lbN2Qrn8s50y7Zt355HhOc+jUpvsScYAaGCW8vs=
|
||||
github.com/kovidgoyal/imaging v1.8.17/go.mod h1:uD4XKN42lLV9du0TsPkwi53yw23vz/qDmfpiDWCSUCE=
|
||||
github.com/kovidgoyal/imaging v1.8.19 h1:zWJdQqF2tfSKjvoB7XpLRhVGbYsze++M0iaqZ4ZkhNk=
|
||||
github.com/kovidgoyal/imaging v1.8.19/go.mod h1:I0q8RdoEuyc4G8GFOF9CaluTUHQSf68d6TmsqpvfRI8=
|
||||
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=
|
||||
@@ -674,8 +643,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U=
|
||||
github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/niklasfasching/go-org v1.9.1 h1:/3s4uTPOF06pImGa2Yvlp24yKXZoTYM+nsIlMzfpg/0=
|
||||
@@ -936,10 +903,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.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||
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=
|
||||
@@ -950,20 +915,16 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
|
||||
golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 h1:MDfG8Cvcqlt9XXrmEiD4epKn7VJHZO84hejP9Jmp0MM=
|
||||
golang.org/x/exp v0.0.0-20251209150349-8475f28825e9/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU=
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU=
|
||||
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20251023183803-a4bb9ffd2546 h1:HDjDiATsGqvuqvkDvgJjD1IgPrVekcSXVVE21JwvzGE=
|
||||
golang.org/x/exp/typeparams v0.0.0-20251023183803-a4bb9ffd2546/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.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ=
|
||||
golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc=
|
||||
golang.org/x/image v0.34.0 h1:33gCkyw9hmwbZJeZkct8XyR11yH889EQt/QH4VmXMn8=
|
||||
golang.org/x/image v0.34.0/go.mod h1:2RNFBZRB+vnwwFil8GkMdRvrJOFd1AzdZI6vOY+eJVU=
|
||||
golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I=
|
||||
golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk=
|
||||
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=
|
||||
@@ -989,10 +950,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.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||
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=
|
||||
@@ -1035,18 +994,14 @@ 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.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||
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=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
|
||||
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -1064,8 +1019,6 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -1121,10 +1074,8 @@ 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.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@@ -1141,10 +1092,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.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
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=
|
||||
@@ -1200,10 +1149,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.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
||||
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
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=
|
||||
@@ -1321,22 +1268,20 @@ 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.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI=
|
||||
honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4=
|
||||
modernc.org/cc/v4 v4.26.5 h1:xM3bX7Mve6G8K8b+T11ReenJOT+BmVqQj0FY5T4+5Y4=
|
||||
modernc.org/cc/v4 v4.26.5/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
|
||||
modernc.org/ccgo/v4 v4.28.1 h1:wPKYn5EC/mYTqBO373jKjvX2n+3+aK7+sICCv4Fjy1A=
|
||||
modernc.org/ccgo/v4 v4.28.1/go.mod h1:uD+4RnfrVgE6ec9NGguUNdhqzNIeeomeXf6CL0GTE5Q=
|
||||
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=
|
||||
modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM=
|
||||
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
|
||||
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=
|
||||
modernc.org/gc/v3 v3.1.1/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.66.10 h1:yZkb3YeLx4oynyR+iUsXsybsX4Ubx7MQlSYEw4yj59A=
|
||||
modernc.org/libc v1.66.10/go.mod h1:8vGSEwvoUoltr4dlywvHqjtAqHBaw0j1jI7iFBTAr2I=
|
||||
modernc.org/libc v1.67.1 h1:bFaqOaa5/zbWYJo8aW0tXPX21hXsngG2M7mckCnFSVk=
|
||||
modernc.org/libc v1.67.1/go.mod h1:QvvnnJ5P7aitu0ReNpVIEyesuhmDLQ8kaEoyMjIFZJA=
|
||||
modernc.org/libc v1.67.7 h1:H+gYQw2PyidyxwxQsGTwQw6+6H+xUk+plvOKW7+d3TI=
|
||||
modernc.org/libc v1.67.7/go.mod h1:UjCSJFl2sYbJbReVQeVpq/MgzlbmDM4cRHIYFelnaDk=
|
||||
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=
|
||||
@@ -1345,8 +1290,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.40.1 h1:VfuXcxcUWWKRBuP8+BR9L7VnmusMgBNNnBYGEe9w/iY=
|
||||
modernc.org/sqlite v1.40.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE=
|
||||
modernc.org/sqlite v1.44.3 h1:+39JvV/HWMcYslAwRxHb8067w+2zowvFOUrOWIy9PjY=
|
||||
modernc.org/sqlite v1.44.3/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
|
||||
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=
|
||||
|
||||
@@ -96,6 +96,7 @@ func createApiKeyHandler(w http.ResponseWriter, r *http.Request, d *requestConte
|
||||
Create: strings.Contains(permissionsStr, "create") && d.user.Permissions.Create,
|
||||
Share: strings.Contains(permissionsStr, "share") && d.user.Permissions.Share,
|
||||
Realtime: strings.Contains(permissionsStr, "realtime") && d.user.Permissions.Realtime,
|
||||
Download: strings.Contains(permissionsStr, "download") && d.user.Permissions.Download,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -211,9 +211,6 @@ func StartHttp(ctx context.Context, storage *bolt.BoltStore, shutdownComplete ch
|
||||
Handler: muxWithMiddleware(router),
|
||||
}
|
||||
listenAddress := config.Server.ListenAddress
|
||||
if listenAddress == "0.0.0.0" {
|
||||
listenAddress = "localhost"
|
||||
}
|
||||
go func() {
|
||||
// Determine whether to use HTTPS (TLS) or HTTP
|
||||
if config.Server.TLSCert != "" && config.Server.TLSKey != "" {
|
||||
|
||||
@@ -111,6 +111,8 @@ func withHashFileHelper(fn handleFunc) handleFunc {
|
||||
Expand: true,
|
||||
Content: getContent,
|
||||
ExtractEmbeddedSubtitles: settings.Config.Integrations.Media.ExtractEmbeddedSubtitles && link.ExtractEmbeddedSubtitles,
|
||||
ShowHidden: link.ShowHidden,
|
||||
FollowSymlinks: true,
|
||||
}, store.Access)
|
||||
if err != nil {
|
||||
logger.Errorf("error fetching file info for share. hash=%v path=%v error=%v", hash, path, err)
|
||||
|
||||
@@ -109,10 +109,11 @@ func onlyofficeClientConfigGetHandler(w http.ResponseWriter, r *http.Request, d
|
||||
path = utils.JoinPathAsUnix(userScope, path)
|
||||
logger.Debugf("OnlyOffice user request: resolved path=%s", path)
|
||||
fileInfo, err := files.FileInfoFaster(utils.FileOptions{
|
||||
Username: d.user.Username,
|
||||
Path: path,
|
||||
Source: source,
|
||||
Expand: false,
|
||||
Username: d.user.Username,
|
||||
Path: path,
|
||||
Source: source,
|
||||
Expand: false,
|
||||
FollowSymlinks: true,
|
||||
}, store.Access)
|
||||
if err != nil {
|
||||
logger.Errorf("OnlyOffice: failed to get file info for source=%s, path=%s: %v", source, path, err)
|
||||
|
||||
+13
-1
@@ -71,13 +71,25 @@ func previewHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (
|
||||
if path == "" {
|
||||
return http.StatusBadRequest, fmt.Errorf("invalid request path")
|
||||
}
|
||||
|
||||
// Sanitize path to prevent path traversal attacks
|
||||
// Rule 1: Do Not Use User Input in File Paths (without validation)
|
||||
safePath, err := utils.SanitizeUserPath(path)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, fmt.Errorf("invalid path: %v", err)
|
||||
}
|
||||
|
||||
userscope, err := settings.GetScopeFromSourceName(d.user.Scopes, source)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, err
|
||||
}
|
||||
|
||||
// Combine scope + sanitized path
|
||||
indexPath := utils.JoinPathAsUnix(userscope, safePath)
|
||||
|
||||
fileInfo, err := files.FileInfoFaster(utils.FileOptions{
|
||||
Username: d.user.Username,
|
||||
Path: utils.JoinPathAsUnix(userscope, path),
|
||||
Path: indexPath,
|
||||
Source: source,
|
||||
AlbumArt: true, // Extract album art for audio previews
|
||||
}, store.Access)
|
||||
|
||||
+42
-10
@@ -94,17 +94,37 @@ func publicRawHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
|
||||
if len(splitFile) == 2 {
|
||||
source := splitFile[0]
|
||||
path := splitFile[1]
|
||||
|
||||
// Sanitize path to prevent path traversal attacks
|
||||
// Rule 1: Do Not Use User Input in File Paths (without validation)
|
||||
safePath, pathErr := utils.SanitizeUserPath(path)
|
||||
if pathErr != nil {
|
||||
return http.StatusForbidden, fmt.Errorf("invalid path")
|
||||
}
|
||||
|
||||
// Join the share path with the requested path
|
||||
filePath := utils.JoinPathAsUnix(d.share.Path, path)
|
||||
filePath := utils.JoinPathAsUnix(d.share.Path, safePath)
|
||||
fileList = append(fileList, source+"::"+filePath)
|
||||
} else {
|
||||
// Sanitize path to prevent path traversal attacks
|
||||
safePath, pathErr := utils.SanitizeUserPath(file)
|
||||
if pathErr != nil {
|
||||
return http.StatusForbidden, fmt.Errorf("invalid path")
|
||||
}
|
||||
|
||||
// Fallback: treat as plain path
|
||||
filePath := utils.JoinPathAsUnix(d.share.Path, file)
|
||||
filePath := utils.JoinPathAsUnix(d.share.Path, safePath)
|
||||
fileList = append(fileList, actualSourceName+"::"+filePath)
|
||||
}
|
||||
} else {
|
||||
// Sanitize path to prevent path traversal attacks
|
||||
safePath, pathErr := utils.SanitizeUserPath(file)
|
||||
if pathErr != nil {
|
||||
return http.StatusForbidden, fmt.Errorf("invalid path")
|
||||
}
|
||||
|
||||
// Plain path without source prefix - use the actual source name from share
|
||||
filePath := utils.JoinPathAsUnix(d.share.Path, file)
|
||||
filePath := utils.JoinPathAsUnix(d.share.Path, safePath)
|
||||
fileList = append(fileList, actualSourceName+"::"+filePath)
|
||||
}
|
||||
}
|
||||
@@ -237,10 +257,20 @@ func publicPreviewHandler(w http.ResponseWriter, r *http.Request, d *requestCont
|
||||
if err != nil {
|
||||
return http.StatusNotFound, fmt.Errorf("source not available")
|
||||
}
|
||||
|
||||
// Sanitize path to prevent path traversal attacks
|
||||
// Rule 1: Do Not Use User Input in File Paths (without validation)
|
||||
safePath, err := utils.SanitizeUserPath(d.fileInfo.Path)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, fmt.Errorf("invalid path")
|
||||
}
|
||||
|
||||
fileInfo, err := FileInfoFasterFunc(utils.FileOptions{
|
||||
Path: utils.JoinPathAsUnix(d.share.Path, d.fileInfo.Path),
|
||||
Source: source,
|
||||
Metadata: true,
|
||||
Path: utils.JoinPathAsUnix(d.share.Path, safePath),
|
||||
Source: source,
|
||||
Metadata: true,
|
||||
AlbumArt: true,
|
||||
ShowHidden: d.share.ShowHidden,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return http.StatusNotFound, fmt.Errorf("resource not available")
|
||||
@@ -314,10 +344,12 @@ func publicDeleteHandler(w http.ResponseWriter, r *http.Request, d *requestConte
|
||||
}
|
||||
|
||||
fileInfo, err := files.FileInfoFaster(utils.FileOptions{
|
||||
Username: d.user.Username,
|
||||
Path: indexPath,
|
||||
Source: source,
|
||||
Expand: false,
|
||||
Username: d.user.Username,
|
||||
FollowSymlinks: true,
|
||||
Path: indexPath,
|
||||
Source: source,
|
||||
Expand: false,
|
||||
ShowHidden: d.share.ShowHidden,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return http.StatusNotFound, fmt.Errorf("resource not available")
|
||||
|
||||
+22
-6
@@ -120,7 +120,14 @@ func addFile(path string, d *requestContext, tarWriter *tar.Writer, zipWriter *z
|
||||
source := splitFile[0]
|
||||
path = splitFile[1]
|
||||
|
||||
var err error
|
||||
// Sanitize path to prevent path traversal attacks
|
||||
// Rule 1: Do Not Use User Input in File Paths (without validation)
|
||||
safePath, err := utils.SanitizeUserPath(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid path: %v", err)
|
||||
}
|
||||
path = safePath
|
||||
|
||||
if d.share == nil {
|
||||
var userScope string
|
||||
userScope, err = settings.GetScopeFromSourceName(d.user.Scopes, source)
|
||||
@@ -145,9 +152,10 @@ func addFile(path string, d *requestContext, tarWriter *tar.Writer, zipWriter *z
|
||||
|
||||
// Verify file exists
|
||||
_, err = files.FileInfoFaster(utils.FileOptions{
|
||||
Path: path,
|
||||
Source: source,
|
||||
Expand: false,
|
||||
Path: path,
|
||||
Source: source,
|
||||
Expand: false,
|
||||
FollowSymlinks: true,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -289,8 +297,16 @@ func rawFilesHandler(w http.ResponseWriter, r *http.Request, d *requestContext,
|
||||
|
||||
firstFileSource := splitFile[0]
|
||||
firstFilePath := splitFile[1]
|
||||
|
||||
// Sanitize path to prevent path traversal attacks
|
||||
// Rule 1: Do Not Use User Input in File Paths (without validation)
|
||||
safePath, err := utils.SanitizeUserPath(firstFilePath)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, fmt.Errorf("invalid path: %v", err)
|
||||
}
|
||||
firstFilePath = safePath
|
||||
|
||||
// decode url encoded source name
|
||||
var err error
|
||||
var userscope string
|
||||
fileName := filepath.Base(firstFilePath)
|
||||
|
||||
@@ -549,7 +565,7 @@ func computeArchiveSize(fileList []string, d *requestContext) (int64, error) {
|
||||
indexPath := idx.MakeIndexPath(realPath)
|
||||
info, ok := idx.GetReducedMetadata(indexPath, isDir)
|
||||
if !ok {
|
||||
info, err = idx.GetFsDirInfo(indexPath)
|
||||
info, err = idx.GetFsInfo(indexPath, false)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to get file info for %s : %v", path, err)
|
||||
}
|
||||
|
||||
+71
-17
@@ -80,21 +80,31 @@ func resourceGetHandler(w http.ResponseWriter, r *http.Request, d *requestContex
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, fmt.Errorf("invalid source encoding: %v", err)
|
||||
}
|
||||
|
||||
// Sanitize path to prevent path traversal attacks
|
||||
// Rule 1: Do Not Use User Input in File Paths (without validation)
|
||||
safePath, err := utils.SanitizeUserPath(path)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, fmt.Errorf("invalid path: %v", err)
|
||||
}
|
||||
|
||||
userscope, err := settings.GetScopeFromSourceName(d.user.Scopes, source)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, err
|
||||
}
|
||||
userscope = strings.TrimRight(userscope, "/")
|
||||
scopePath := utils.JoinPathAsUnix(userscope, path)
|
||||
scopePath := utils.JoinPathAsUnix(userscope, safePath)
|
||||
getContent := r.URL.Query().Get("content") == "true"
|
||||
fileInfo, err := files.FileInfoFaster(utils.FileOptions{
|
||||
Username: d.user.Username,
|
||||
FollowSymlinks: true,
|
||||
Path: scopePath,
|
||||
Source: source,
|
||||
Expand: true,
|
||||
Content: getContent,
|
||||
Metadata: true,
|
||||
ExtractEmbeddedSubtitles: settings.Config.Integrations.Media.ExtractEmbeddedSubtitles,
|
||||
ShowHidden: d.user.ShowHidden,
|
||||
}, store.Access)
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
@@ -166,16 +176,25 @@ func resourceDeleteHandler(w http.ResponseWriter, r *http.Request, d *requestCon
|
||||
if path == "/" {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
// Sanitize path to prevent path traversal attacks
|
||||
// Rule 1: Do Not Use User Input in File Paths (without validation)
|
||||
safePath, err := utils.SanitizeUserPath(path)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, fmt.Errorf("invalid path: %v", err)
|
||||
}
|
||||
|
||||
userscope, err := settings.GetScopeFromSourceName(d.user.Scopes, source)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, err
|
||||
}
|
||||
userscope = strings.TrimRight(userscope, "/")
|
||||
fileInfo, err := files.FileInfoFaster(utils.FileOptions{
|
||||
Username: d.user.Username,
|
||||
Path: utils.JoinPathAsUnix(userscope, path),
|
||||
Source: source,
|
||||
Expand: false,
|
||||
Username: d.user.Username,
|
||||
Path: utils.JoinPathAsUnix(userscope, safePath),
|
||||
Source: source,
|
||||
Expand: false,
|
||||
ShowHidden: d.user.ShowHidden,
|
||||
}, store.Access)
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
@@ -229,6 +248,16 @@ func resourcePostHandler(w http.ResponseWriter, r *http.Request, d *requestConte
|
||||
logger.Debugf("invalid path encoding: %v", err)
|
||||
return http.StatusBadRequest, fmt.Errorf("invalid path encoding: %v", err)
|
||||
}
|
||||
|
||||
// Sanitize path to prevent path traversal attacks
|
||||
// Rule 1: Do Not Use User Input in File Paths (without validation)
|
||||
var safePath string
|
||||
safePath, err = utils.SanitizeUserPath(unescapedPath)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, fmt.Errorf("invalid path: %v", err)
|
||||
}
|
||||
unescapedPath = safePath
|
||||
|
||||
if !d.user.Permissions.Create {
|
||||
return http.StatusForbidden, fmt.Errorf("user is not allowed to create or modify")
|
||||
}
|
||||
@@ -248,10 +277,11 @@ func resourcePostHandler(w http.ResponseWriter, r *http.Request, d *requestConte
|
||||
isDirParam := r.URL.Query().Get("isDir")
|
||||
isDir := isDirParam == "true" || strings.HasSuffix(unescapedPath, "/")
|
||||
fileOpts := utils.FileOptions{
|
||||
Username: d.user.Username,
|
||||
Path: path,
|
||||
Source: source,
|
||||
Expand: false,
|
||||
Username: d.user.Username,
|
||||
Path: path,
|
||||
Source: source,
|
||||
Expand: false,
|
||||
FollowSymlinks: true,
|
||||
}
|
||||
idx := indexing.GetIndex(source)
|
||||
if idx == nil {
|
||||
@@ -432,6 +462,14 @@ func resourcePutHandler(w http.ResponseWriter, r *http.Request, d *requestContex
|
||||
if strings.HasSuffix(path, "/") {
|
||||
return http.StatusMethodNotAllowed, nil
|
||||
}
|
||||
|
||||
// Sanitize path to prevent path traversal attacks
|
||||
// Rule 1: Do Not Use User Input in File Paths (without validation)
|
||||
safePath, err := utils.SanitizeUserPath(path)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, fmt.Errorf("invalid path: %v", err)
|
||||
}
|
||||
|
||||
// Strip trailing slash from userscope to prevent double slashes
|
||||
userscope, err := settings.GetScopeFromSourceName(d.user.Scopes, source)
|
||||
if err != nil {
|
||||
@@ -442,12 +480,12 @@ func resourcePutHandler(w http.ResponseWriter, r *http.Request, d *requestContex
|
||||
if idx == nil {
|
||||
return http.StatusNotFound, fmt.Errorf("source %s not found", source)
|
||||
}
|
||||
if store.Access != nil && !store.Access.Permitted(idx.Path, path, d.user.Username) {
|
||||
logger.Debugf("user %s denied access to path %s", d.user.Username, path)
|
||||
return http.StatusForbidden, fmt.Errorf("access denied to path %s", path)
|
||||
if store.Access != nil && !store.Access.Permitted(idx.Path, safePath, d.user.Username) {
|
||||
logger.Debugf("user %s denied access to path %s", d.user.Username, safePath)
|
||||
return http.StatusForbidden, fmt.Errorf("access denied to path %s", safePath)
|
||||
}
|
||||
|
||||
err = files.WriteFile(source, utils.JoinPathAsUnix(userscope, path), r.Body)
|
||||
err = files.WriteFile(source, utils.JoinPathAsUnix(userscope, safePath), r.Body)
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
@@ -504,6 +542,20 @@ func resourcePatchHandler(w http.ResponseWriter, r *http.Request, d *requestCont
|
||||
return http.StatusForbidden, fmt.Errorf("forbidden: source or destination is attempting to modify root")
|
||||
}
|
||||
|
||||
// Sanitize paths to prevent path traversal attacks
|
||||
// Rule 1: Do Not Use User Input in File Paths (without validation)
|
||||
safeSrc, err := utils.SanitizeUserPath(src)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, fmt.Errorf("invalid source path: %v", err)
|
||||
}
|
||||
src = safeSrc
|
||||
|
||||
safeDst, err := utils.SanitizeUserPath(dst)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, fmt.Errorf("invalid destination path: %v", err)
|
||||
}
|
||||
dst = safeDst
|
||||
|
||||
userscopeDst, err := settings.GetScopeFromSourceName(d.user.Scopes, dstIndex)
|
||||
if err != nil {
|
||||
return http.StatusForbidden, err
|
||||
@@ -614,10 +666,12 @@ func patchAction(ctx context.Context, params patchActionParams) error {
|
||||
srcPath = strings.TrimSuffix(srcPath, "/")
|
||||
}
|
||||
fileInfo, err := files.FileInfoFaster(utils.FileOptions{
|
||||
Username: params.d.user.Username,
|
||||
Path: srcPath,
|
||||
Source: params.srcIndex,
|
||||
IsDir: params.isSrcDir,
|
||||
Username: params.d.user.Username,
|
||||
FollowSymlinks: true,
|
||||
Path: srcPath,
|
||||
Source: params.srcIndex,
|
||||
IsDir: params.isSrcDir,
|
||||
ShowHidden: params.d.user.ShowHidden,
|
||||
}, store.Access)
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -615,8 +615,7 @@ func shareInfoHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
|
||||
return http.StatusNotFound, fmt.Errorf("share hash not found")
|
||||
}
|
||||
commonShare := shareLink.CommonShare
|
||||
commonShare.DownloadURL = getShareURL(r, hash, true, shareLink.Token)
|
||||
commonShare.ShareURL = getShareURL(r, hash, false, shareLink.Token)
|
||||
commonShare.ShareURL = getShareURL(r, hash, false, "")
|
||||
return renderJSON(w, r, commonShare)
|
||||
}
|
||||
|
||||
|
||||
@@ -215,7 +215,32 @@ func (idx *Index) indexDirectory(adjustedPath string, config actionConfig) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (idx *Index) GetFsDirInfo(adjustedPath string) (*iteminfo.FileInfo, error) {
|
||||
func (idx *Index) GetFsInfo(adjustedPath string, followSymlinks bool) (*iteminfo.FileInfo, error) {
|
||||
// If not following symlinks, check if it's a symlink first
|
||||
if !followSymlinks {
|
||||
realPath := filepath.Join(idx.Path, adjustedPath)
|
||||
symlinkInfo, err := os.Lstat(realPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If it's a symlink, return info about the symlink itself (not the target)
|
||||
if symlinkInfo.Mode()&os.ModeSymlink != 0 {
|
||||
realSize, _ := idx.handleFile(symlinkInfo, adjustedPath, realPath)
|
||||
size := int64(realSize)
|
||||
fileInfo := iteminfo.FileInfo{
|
||||
Path: adjustedPath,
|
||||
ItemInfo: iteminfo.ItemInfo{
|
||||
Name: filepath.Base(strings.TrimSuffix(adjustedPath, "/")),
|
||||
Size: size,
|
||||
ModTime: symlinkInfo.ModTime(),
|
||||
Type: "symlink",
|
||||
},
|
||||
}
|
||||
return &fileInfo, nil
|
||||
}
|
||||
}
|
||||
|
||||
realPath, isDir, err := idx.GetRealPath(adjustedPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -396,7 +421,7 @@ func (idx *Index) GetDirInfo(dirInfo *os.File, stat os.FileInfo, realPath, adjus
|
||||
}
|
||||
}
|
||||
}
|
||||
if totalSize == 0 && idx.Config.Conditionals.ZeroSizeFolders {
|
||||
if totalSize == 0 && idx.Config.Conditionals.ZeroSizeFolders && combinedPath != "/" {
|
||||
return nil, errors.ErrNotIndexed
|
||||
}
|
||||
dirFileInfo := &iteminfo.FileInfo{
|
||||
@@ -524,6 +549,9 @@ func setFilePreviewFlags(fileInfo *iteminfo.ItemInfo, realPath string) {
|
||||
|
||||
// IsViewable checks if a path has viewable:true (allows FS access without indexing)
|
||||
func (idx *Index) IsViewable(isDir bool, adjustedPath string) bool {
|
||||
if adjustedPath == "/" {
|
||||
return true
|
||||
}
|
||||
rules := idx.Config.ResolvedConditionals
|
||||
|
||||
baseName := filepath.Base(strings.TrimSuffix(adjustedPath, "/"))
|
||||
|
||||
@@ -3,6 +3,7 @@ package indexing
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gtsteffaniak/go-logger/logger"
|
||||
@@ -36,6 +37,7 @@ type Scanner struct {
|
||||
|
||||
// Control
|
||||
stopChan chan struct{}
|
||||
stopOnce sync.Once // Ensures stop() is only executed once
|
||||
}
|
||||
|
||||
// start begins the scanner's main loop
|
||||
@@ -404,6 +406,9 @@ func (s *Scanner) removeSelf() {
|
||||
}
|
||||
|
||||
// stop gracefully stops the scanner
|
||||
// This method is idempotent and safe to call multiple times
|
||||
func (s *Scanner) stop() {
|
||||
close(s.stopChan)
|
||||
s.stopOnce.Do(func() {
|
||||
close(s.stopChan)
|
||||
})
|
||||
}
|
||||
@@ -37,6 +37,7 @@ type Service struct {
|
||||
officeSem chan struct{} // Semaphore for office document processing
|
||||
videoService *ffmpeg.FFmpegService
|
||||
imageService *ffmpeg.FFmpegService
|
||||
globalLimit chan struct{}
|
||||
}
|
||||
|
||||
func NewPreviewGenerator(concurrencyLimit int, cacheDir string) *Service {
|
||||
@@ -87,6 +88,7 @@ func NewPreviewGenerator(concurrencyLimit int, cacheDir string) *Service {
|
||||
officeSem: make(chan struct{}, concurrencyLimit),
|
||||
videoService: videoService,
|
||||
imageService: imageService,
|
||||
globalLimit: make(chan struct{}, concurrencyLimit),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +120,20 @@ func (s *Service) releaseOffice() {
|
||||
<-s.officeSem
|
||||
}
|
||||
|
||||
// Global limit semaphore methods
|
||||
func (s *Service) acquireGlobal(ctx context.Context) error {
|
||||
select {
|
||||
case s.globalLimit <- struct{}{}:
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) releaseGlobal() {
|
||||
<-s.globalLimit
|
||||
}
|
||||
|
||||
func StartPreviewGenerator(concurrencyLimit int, cacheDir string) error {
|
||||
if service != nil {
|
||||
logger.Errorf("WARNING: StartPreviewGenerator called multiple times! This will create multiple semaphores!")
|
||||
@@ -174,6 +190,12 @@ func GeneratePreviewWithMD5(ctx context.Context, file iteminfo.ExtendedFileInfo,
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
// Acquire global concurrency limit
|
||||
if err := service.acquireGlobal(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer service.releaseGlobal()
|
||||
|
||||
ext := strings.ToLower(filepath.Ext(file.Name))
|
||||
var (
|
||||
err error
|
||||
|
||||
@@ -3127,6 +3127,10 @@ const docTemplate = `{
|
||||
"shareURL": {
|
||||
"type": "string"
|
||||
},
|
||||
"showHidden": {
|
||||
"description": "show hidden files in share (true = show, false = hide)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"sidebarLinks": {
|
||||
"description": "customizable sidebar links",
|
||||
"type": "array",
|
||||
@@ -4415,6 +4419,10 @@ const docTemplate = `{
|
||||
"shareURL": {
|
||||
"type": "string"
|
||||
},
|
||||
"showHidden": {
|
||||
"description": "show hidden files in share (true = show, false = hide)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"sidebarLinks": {
|
||||
"description": "customizable sidebar links",
|
||||
"type": "array",
|
||||
@@ -4548,6 +4556,10 @@ const docTemplate = `{
|
||||
"shareURL": {
|
||||
"type": "string"
|
||||
},
|
||||
"showHidden": {
|
||||
"description": "show hidden files in share (true = show, false = hide)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"sidebarLinks": {
|
||||
"description": "customizable sidebar links",
|
||||
"type": "array",
|
||||
@@ -4687,6 +4699,10 @@ const docTemplate = `{
|
||||
"shareURL": {
|
||||
"type": "string"
|
||||
},
|
||||
"showHidden": {
|
||||
"description": "show hidden files in share (true = show, false = hide)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"sidebarLinks": {
|
||||
"description": "customizable sidebar links",
|
||||
"type": "array",
|
||||
|
||||
@@ -3116,6 +3116,10 @@
|
||||
"shareURL": {
|
||||
"type": "string"
|
||||
},
|
||||
"showHidden": {
|
||||
"description": "show hidden files in share (true = show, false = hide)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"sidebarLinks": {
|
||||
"description": "customizable sidebar links",
|
||||
"type": "array",
|
||||
@@ -4404,6 +4408,10 @@
|
||||
"shareURL": {
|
||||
"type": "string"
|
||||
},
|
||||
"showHidden": {
|
||||
"description": "show hidden files in share (true = show, false = hide)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"sidebarLinks": {
|
||||
"description": "customizable sidebar links",
|
||||
"type": "array",
|
||||
@@ -4537,6 +4545,10 @@
|
||||
"shareURL": {
|
||||
"type": "string"
|
||||
},
|
||||
"showHidden": {
|
||||
"description": "show hidden files in share (true = show, false = hide)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"sidebarLinks": {
|
||||
"description": "customizable sidebar links",
|
||||
"type": "array",
|
||||
@@ -4676,6 +4688,10 @@
|
||||
"shareURL": {
|
||||
"type": "string"
|
||||
},
|
||||
"showHidden": {
|
||||
"description": "show hidden files in share (true = show, false = hide)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"sidebarLinks": {
|
||||
"description": "customizable sidebar links",
|
||||
"type": "array",
|
||||
|
||||
@@ -121,6 +121,9 @@ definitions:
|
||||
type: string
|
||||
shareURL:
|
||||
type: string
|
||||
showHidden:
|
||||
description: show hidden files in share (true = show, false = hide)
|
||||
type: boolean
|
||||
sidebarLinks:
|
||||
description: customizable sidebar links
|
||||
items:
|
||||
@@ -1082,6 +1085,9 @@ definitions:
|
||||
type: string
|
||||
shareURL:
|
||||
type: string
|
||||
showHidden:
|
||||
description: show hidden files in share (true = show, false = hide)
|
||||
type: boolean
|
||||
sidebarLinks:
|
||||
description: customizable sidebar links
|
||||
items:
|
||||
@@ -1175,6 +1181,9 @@ definitions:
|
||||
type: string
|
||||
shareURL:
|
||||
type: string
|
||||
showHidden:
|
||||
description: show hidden files in share (true = show, false = hide)
|
||||
type: boolean
|
||||
sidebarLinks:
|
||||
description: customizable sidebar links
|
||||
items:
|
||||
@@ -1272,6 +1281,9 @@ definitions:
|
||||
type: string
|
||||
shareURL:
|
||||
type: string
|
||||
showHidden:
|
||||
description: show hidden files in share (true = show, false = hide)
|
||||
type: boolean
|
||||
sidebarLinks:
|
||||
description: customizable sidebar links
|
||||
items:
|
||||
|
||||
Generated
+345
-285
File diff suppressed because it is too large.
Load diff
@@ -366,14 +366,8 @@ export default {
|
||||
this.submit();
|
||||
},
|
||||
getRelative(path) {
|
||||
// double encode # to fix issue with # in path
|
||||
// replace all # with %23
|
||||
path = path.replaceAll('#', "%23");
|
||||
if (path.startsWith("/")) {
|
||||
path = path.slice(1); // remove leading slash
|
||||
}
|
||||
const context = url.removeTrailingSlash(this.getContext)
|
||||
const encodedPath = encodeURIComponent(context + "/" + path).replaceAll("%2F", "/");
|
||||
const encodedPath = url.encodePath(context + "/" + path)
|
||||
let fullpath = encodedPath;
|
||||
fullpath = globalVars.baseURL+"files/" + this.selectedSource + encodedPath;
|
||||
return fullpath;
|
||||
|
||||
@@ -7,25 +7,25 @@
|
||||
>
|
||||
<i
|
||||
v-if="hasMotion && isFile"
|
||||
class="material-icons"
|
||||
class="material-icons icon-optimized"
|
||||
:class="{ larger: showLarger, smaller: !showLarger }"
|
||||
>animation</i
|
||||
>
|
||||
<i
|
||||
v-else-if="!isFile"
|
||||
class="material-icons"
|
||||
class="material-icons icon-optimized"
|
||||
:class="{ larger: showLarger, smaller: !showLarger }"
|
||||
>folder</i
|
||||
>
|
||||
<img
|
||||
:key="imageTargetSrc"
|
||||
:src="imageDisplaySrc"
|
||||
class="icon"
|
||||
class="icon icon-optimized"
|
||||
ref="thumbnail"
|
||||
/>
|
||||
</span>
|
||||
<span v-else>
|
||||
<i :class="[classes, { active: active, clickable: clickable }]" class="icon"> {{ materialIcon }} </i>
|
||||
<i :class="[classes, { active: active, clickable: clickable }]" class="icon icon-optimized"> {{ materialIcon }} </i>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@@ -93,17 +93,17 @@ export default {
|
||||
return false;
|
||||
}
|
||||
const simpleType = this.getIconForType().simpleType;
|
||||
if (simpleType === "video" && !state.user.preview?.video) {
|
||||
if (simpleType === "video" && !getters.previewPerms().video) {
|
||||
return false;
|
||||
}
|
||||
if (simpleType === "image" && !state.user.preview?.image) {
|
||||
if (simpleType === "image" && !getters.previewPerms().image) {
|
||||
return false;
|
||||
}
|
||||
// office files
|
||||
if ((simpleType === "document" || simpleType === "text") && !state.user.preview?.office) {
|
||||
if ((simpleType === "document" || simpleType === "text") && !getters.previewPerms().office) {
|
||||
return false;
|
||||
}
|
||||
if (!state.user.preview.folder && this.mimetype == "directory") {
|
||||
if (!getters.previewPerms().folder && this.mimetype == "directory") {
|
||||
return false;
|
||||
}
|
||||
return this.imageState !== 'error' && !this.disablePreviewExt && !this.officeFileDisabled
|
||||
@@ -153,7 +153,7 @@ export default {
|
||||
return this.imageTargetSrc;
|
||||
},
|
||||
showLargeIcon() {
|
||||
return getters.viewMode() === "gallery" && state.user.preview.highQuality;
|
||||
return getters.viewMode() === "gallery" && getters.previewPerms().highQuality;
|
||||
},
|
||||
showLarger() {
|
||||
return getters.viewMode() === "gallery" || getters.viewMode() === "normal";
|
||||
@@ -161,10 +161,9 @@ export default {
|
||||
hasMotion() {
|
||||
return (
|
||||
this.getIconForType().simpleType === "video" &&
|
||||
state.user.preview?.video &&
|
||||
getters.previewPerms().video &&
|
||||
globalVars.mediaAvailable &&
|
||||
// @ts-ignore
|
||||
state.user.preview.motionVideoPreview
|
||||
getters.previewPerms().motionVideoPreview
|
||||
);
|
||||
},
|
||||
isMaterialIcon() {
|
||||
@@ -206,8 +205,7 @@ export default {
|
||||
if (this.imageState == "loaded") {
|
||||
mutations.setPreviewSource(imageUrl);
|
||||
}
|
||||
// @ts-ignore
|
||||
if (!state.user.preview.motionVideoPreview || !this.hasMotion) {
|
||||
if (!getters.previewPerms().motionVideoPreview || !this.hasMotion) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -226,7 +224,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
// Set the thumbnail or popup preview
|
||||
if (state.user.preview.popup) {
|
||||
if (getters.previewPerms().popup) {
|
||||
mutations.setPreviewSource(sequence[index]);
|
||||
} else {
|
||||
this.currentThumbnail = sequence[index];
|
||||
@@ -272,7 +270,6 @@ export default {
|
||||
showLargeIcon() {
|
||||
this.updateImageTargetSrc();
|
||||
},
|
||||
// UPDATED: Load image when URL changes, let hasPreviewImage control display
|
||||
imageTargetSrc: {
|
||||
handler(newSrc) {
|
||||
// Check all conditions EXCEPT imageState to avoid circular dependency
|
||||
@@ -283,17 +280,17 @@ export default {
|
||||
return;
|
||||
}
|
||||
const simpleType = this.getIconForType().simpleType;
|
||||
// Check user preview settings
|
||||
if (simpleType === "video" && !state.user.preview?.video) {
|
||||
// Check preview settings (share or user)
|
||||
if (simpleType === "video" && !getters.previewPerms().video) {
|
||||
return;
|
||||
}
|
||||
if (simpleType === "image" && !state.user.preview?.image) {
|
||||
if (simpleType === "image" && !getters.previewPerms().image) {
|
||||
return;
|
||||
}
|
||||
if ((simpleType === "document" || simpleType === "text") && !state.user.preview?.office) {
|
||||
if ((simpleType === "document" || simpleType === "text") && !getters.previewPerms().office) {
|
||||
return;
|
||||
}
|
||||
if (!state.user.preview.folder && this.mimetype === "directory") {
|
||||
if (!getters.previewPerms().folder && this.mimetype === "directory") {
|
||||
return;
|
||||
}
|
||||
if (this.disablePreviewExt || this.officeFileDisabled) {
|
||||
@@ -319,6 +316,13 @@ export default {
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Performance optimization for icons */
|
||||
.icon-optimized {
|
||||
will-change: auto;
|
||||
transform: translateZ(0);
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
|
||||
.larger {
|
||||
position: absolute;
|
||||
opacity: 0.5;
|
||||
@@ -354,6 +358,9 @@ export default {
|
||||
border-radius: 0.2em;
|
||||
padding: 0.1em;
|
||||
background: var(--iconBackground);
|
||||
/* Performance optimization */
|
||||
will-change: auto;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
.icon.active {
|
||||
background: var(--background);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
hiddenFile: isHiddenNotSelected && this && !this.isDraggedOver,
|
||||
'half-selected': isDraggedOver,
|
||||
'drag-hover': isDraggedOver,
|
||||
'out-of-view': !isInView && !isSelected,
|
||||
}"
|
||||
:id="getID"
|
||||
role="button"
|
||||
@@ -89,6 +90,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
isThumbnailInView: false,
|
||||
isInView: false, // Track if item is in viewport
|
||||
touches: 0,
|
||||
touchStartX: 0,
|
||||
touchStartY: 0,
|
||||
@@ -96,6 +98,7 @@ export default {
|
||||
isSwipe: false,
|
||||
isDraggedOver: false,
|
||||
contextTimeout: null,
|
||||
observer: null, // Store observer reference
|
||||
};
|
||||
},
|
||||
props: [
|
||||
@@ -209,14 +212,21 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// Prevent default navigation for left-clicks
|
||||
const observer = new IntersectionObserver(this.handleIntersect, {
|
||||
// Set up IntersectionObserver with larger margins for smoother rendering
|
||||
this.observer = new IntersectionObserver(this.handleIntersect, {
|
||||
root: null,
|
||||
rootMargin: "0px",
|
||||
threshold: 0.1, // Adjust threshold as needed
|
||||
rootMargin: "1500px", // Start rendering 1500px before entering viewport for smooth scrolling
|
||||
threshold: 0,
|
||||
});
|
||||
|
||||
observer.observe(this.$el);
|
||||
this.observer.observe(this.$el);
|
||||
},
|
||||
beforeUnmount() {
|
||||
// Clean up observer
|
||||
if (this.observer) {
|
||||
this.observer.disconnect();
|
||||
this.observer = null;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** @param {MouseEvent} event */
|
||||
@@ -287,12 +297,12 @@ export default {
|
||||
* @param {IntersectionObserverEntry[]} entries
|
||||
* @param {IntersectionObserver} observer
|
||||
*/
|
||||
handleIntersect(entries, observer) {
|
||||
handleIntersect(entries) {
|
||||
entries.forEach((entry) => {
|
||||
// Update both view state and thumbnail state
|
||||
this.isInView = entry.isIntersecting;
|
||||
if (entry.isIntersecting) {
|
||||
this.isThumbnailInView = true;
|
||||
// Stop observing once thumbnail is in view
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -634,6 +644,22 @@ export default {
|
||||
-webkit-touch-callout: none; /* Disable the default long press preview */
|
||||
}
|
||||
|
||||
/* Disable transitions and hide content for out-of-view items for better performance */
|
||||
.listing-item.out-of-view {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.listing-item.out-of-view * {
|
||||
transition: none !important;
|
||||
opacity: 0 !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
/* Ensure items maintain their height even when content is hidden */
|
||||
.listing-item > div {
|
||||
min-height: 1em; /* Forces layout calculation even with hidden content */
|
||||
}
|
||||
|
||||
.hiddenFile {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
@@ -202,6 +202,8 @@
|
||||
:description="$t('share.keepAfterExpirationDescription')" />
|
||||
<ToggleSwitch v-if="shareType === 'normal'" class="item" v-model="disableThumbnails"
|
||||
:name="$t('share.disableThumbnails')" :description="$t('share.disableThumbnailsDescription')" />
|
||||
<ToggleSwitch v-if="shareType === 'normal'" class="item" v-model="showHidden"
|
||||
:name="$t('profileSettings.showHiddenFiles')" :description="$t('profileSettings.showHiddenFilesDescription')" />
|
||||
<ToggleSwitch class="item" v-model="disableNavButtons" :name="$t('share.hideNavButtons')"
|
||||
:description="$t('share.hideNavButtonsDescription')" />
|
||||
<ToggleSwitch class="item" v-model="disableShareCard" :name="$t('share.disableShareCard')"
|
||||
@@ -367,6 +369,7 @@ export default {
|
||||
maxBandwidth: "",
|
||||
disableFileViewer: false,
|
||||
disableThumbnails: false,
|
||||
showHidden: false,
|
||||
enableAllowedUsernames: false,
|
||||
allowedUsernames: "",
|
||||
keepAfterExpiration: false,
|
||||
@@ -485,6 +488,7 @@ export default {
|
||||
this.disableAnonymous = this.link.disableAnonymous || false;
|
||||
this.disableThumbnails = this.link.disableThumbnails || false;
|
||||
this.disableFileViewer = this.link.disableFileViewer || false;
|
||||
this.showHidden = this.link.showHidden || false;
|
||||
this.enableAllowedUsernames = Array.isArray(this.link.allowedUsernames) && this.link.allowedUsernames.length > 0;
|
||||
this.allowedUsernames = this.enableAllowedUsernames ? this.link.allowedUsernames.join(", ") : "";
|
||||
this.keepAfterExpiration = this.link.keepAfterExpiration || false;
|
||||
@@ -600,6 +604,7 @@ export default {
|
||||
shareTheme: this.shareTheme,
|
||||
disableFileViewer: this.disableFileViewer,
|
||||
disableThumbnails: this.disableThumbnails,
|
||||
showHidden: this.showHidden,
|
||||
allowedUsernames: this.enableAllowedUsernames ? this.allowedUsernames.split(',').map(u => u.trim()) : [],
|
||||
keepAfterExpiration: this.keepAfterExpiration,
|
||||
hash: '',
|
||||
@@ -698,6 +703,7 @@ export default {
|
||||
this.disableAnonymous = link.disableAnonymous || false;
|
||||
this.disableThumbnails = link.disableThumbnails || false;
|
||||
this.disableFileViewer = link.disableFileViewer || false;
|
||||
this.showHidden = link.showHidden || false;
|
||||
this.enableAllowedUsernames = Array.isArray(link.allowedUsernames) && link.allowedUsernames.length > 0;
|
||||
this.allowedUsernames = this.enableAllowedUsernames ? link.allowedUsernames.join(", ") : "";
|
||||
this.keepAfterExpiration = link.keepAfterExpiration || false;
|
||||
|
||||
@@ -44,7 +44,11 @@ body.rtl #listingView {
|
||||
background: var(--item-secondary);
|
||||
color: var(--textPrimary);
|
||||
border-color: var(--divider);
|
||||
background: var(--surfacePrimary)
|
||||
background: var(--surfacePrimary);
|
||||
/* Performance optimizations */
|
||||
contain: layout style paint;
|
||||
content-visibility: auto;
|
||||
will-change: auto;
|
||||
}
|
||||
|
||||
.listing-item div:last-of-type {
|
||||
@@ -66,6 +70,8 @@ body.rtl #listingView {
|
||||
.listing-item i {
|
||||
font-size: 4em;
|
||||
vertical-align: bottom;
|
||||
/* Performance optimization for icon rendering */
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.listing-item img {
|
||||
@@ -166,6 +172,11 @@ body.rtl #listingView {
|
||||
transition: width 0.25s ease-out, height 0.25s ease-out, box-shadow 0.2s ease-in-out, transform 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
/* Disable transitions during resize for better performance */
|
||||
#listingView.resizing .listing-item {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
#listingView.gallery .listing-item .text {
|
||||
color: white;
|
||||
text-shadow: 0 0 2px black;
|
||||
|
||||
@@ -205,7 +205,7 @@ export const getters = {
|
||||
if (getters.currentPromptName() && !getters.isStickySidebar()) {
|
||||
visible = false
|
||||
}
|
||||
if (previewViews.includes(cv) && !state.user.preview?.disableHideSidebar) {
|
||||
if (previewViews.includes(cv) && !getters.previewPerms().disableHideSidebar) {
|
||||
visible = false
|
||||
}
|
||||
if (state.shareInfo?.singleFileShare) {
|
||||
@@ -555,5 +555,37 @@ export const getters = {
|
||||
admin: state.user?.permissions?.admin,
|
||||
api: state.user?.permissions?.api,
|
||||
};
|
||||
},
|
||||
previewPerms: () => {
|
||||
if (getters.isShare()) {
|
||||
// For shares, use defaults for preview settings (shares don't have per-share preview config)
|
||||
return {
|
||||
video: state.user?.preview?.video ?? true,
|
||||
image: state.user?.preview?.image ?? true,
|
||||
office: state.user?.preview?.office ?? true,
|
||||
folder: state.user?.preview?.folder ?? true,
|
||||
popup: state.user?.preview?.popup ?? true,
|
||||
highQuality: state.user?.preview?.highQuality ?? false,
|
||||
motionVideoPreview: state.user?.preview?.motionVideoPreview ?? false,
|
||||
disableHideSidebar: state.user?.preview?.disableHideSidebar ?? false,
|
||||
autoplayMedia: state.user?.preview?.autoplayMedia ?? false,
|
||||
defaultMediaPlayer: false,
|
||||
showHidden: state.shareInfo?.showHidden !== undefined ? state.shareInfo.showHidden : false,
|
||||
};
|
||||
}
|
||||
// For regular users, use their preview settings -- unless is share
|
||||
return {
|
||||
video: state.user?.preview?.video ?? true,
|
||||
image: state.user?.preview?.image ?? true,
|
||||
office: state.user?.preview?.office ?? true,
|
||||
folder: state.user?.preview?.folder ?? true,
|
||||
popup: state.user?.preview?.popup ?? true,
|
||||
highQuality: state.user?.preview?.highQuality ?? false,
|
||||
motionVideoPreview: state.user?.preview?.motionVideoPreview ?? false,
|
||||
disableHideSidebar: state.user?.preview?.disableHideSidebar ?? false,
|
||||
autoplayMedia: state.user?.preview?.autoplayMedia ?? false,
|
||||
defaultMediaPlayer: state.user?.preview?.defaultMediaPlayer ?? false,
|
||||
showHidden: false, // Backend handles this now, but kept for API compatibility
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -499,9 +499,6 @@ export const mutations = {
|
||||
emitStateChanged();
|
||||
return
|
||||
}
|
||||
if (!state.user.showHidden) {
|
||||
value.items = value.items.filter((item) => !item.hidden);
|
||||
}
|
||||
let sortby = "name"
|
||||
let asc = true
|
||||
const sorting = getters.sorting();
|
||||
|
||||
@@ -30,7 +30,6 @@ import MarkdownViewer from "./files/MarkdownViewer.vue";
|
||||
import { state, mutations, getters } from "@/store";
|
||||
import { url } from "@/utils";
|
||||
import router from "@/router";
|
||||
import { globalVars } from "@/utils/constants";
|
||||
import { extractSourceFromPath } from "@/utils/url";
|
||||
|
||||
export default {
|
||||
@@ -391,16 +390,9 @@ export default {
|
||||
|
||||
// Clear share data when accessing files
|
||||
mutations.clearShareData();
|
||||
|
||||
const routePath = url.removeTrailingSlash(getters.routePath(`${globalVars.baseURL}files`));
|
||||
const rootRoute =
|
||||
routePath == "/files" ||
|
||||
routePath == "/files/" ||
|
||||
routePath == "" ||
|
||||
routePath == "/";
|
||||
|
||||
const routePath = url.removeTrailingSlash(getters.routePath());
|
||||
// lets redirect if multiple sources and user went to /files/
|
||||
if (rootRoute) {
|
||||
if (routePath == "/files") {
|
||||
// Check if user has custom sidebar links with sources
|
||||
let targetPath = `/files/${state.sources.current}`;
|
||||
for (const link of state.user?.sidebarLinks || []) {
|
||||
|
||||
@@ -132,7 +132,7 @@ export default {
|
||||
if (!state.user || state.user?.username == "") {
|
||||
return false;
|
||||
}
|
||||
return state.user.preview.popup;
|
||||
return getters.previewPerms().popup;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
||||
@@ -219,6 +219,10 @@ export default {
|
||||
rectangleEnd: { x: 0, y: 0 },
|
||||
rectangleSelection: [],
|
||||
cssVariables: {},
|
||||
rafId: null, // For requestAnimationFrame
|
||||
selectionUpdatePending: false, // Flag to batch updates
|
||||
isResizing: false, // Track resize state
|
||||
resizeTimeout: null, // Timeout for resize end detection
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
@@ -490,10 +494,10 @@ export default {
|
||||
window.addEventListener("click", this.clickClear);
|
||||
window.addEventListener("keyup", this.clearCtrKey);
|
||||
window.addEventListener("dragover", this.preventDefault);
|
||||
document.addEventListener('mousemove', this.updateRectangleSelection);
|
||||
document.addEventListener('mousemove', this.updateRectangleSelection, { passive: true });
|
||||
document.addEventListener('mouseup', this.endRectangleSelection);
|
||||
this.$el.addEventListener('mousedown', this.startRectangleSelection);
|
||||
this.$el.addEventListener("touchmove", this.handleTouchMove);
|
||||
this.$el.addEventListener("touchmove", this.handleTouchMove, { passive: true });
|
||||
|
||||
this.$el.addEventListener("contextmenu", this.openContext);
|
||||
// Adjust contextmenu listener based on browser
|
||||
@@ -517,6 +521,16 @@ export default {
|
||||
}
|
||||
},
|
||||
beforeUnmount() {
|
||||
// Cancel any pending animation frames
|
||||
if (this.rafId) {
|
||||
cancelAnimationFrame(this.rafId);
|
||||
this.rafId = null;
|
||||
}
|
||||
if (this.resizeTimeout) {
|
||||
clearTimeout(this.resizeTimeout);
|
||||
this.resizeTimeout = null;
|
||||
}
|
||||
|
||||
// Remove event listeners before destroying this page.
|
||||
window.removeEventListener("keydown", this.keyEvent);
|
||||
window.removeEventListener("resize", this.windowsResize);
|
||||
@@ -1057,9 +1071,32 @@ export default {
|
||||
showMultipleSelection();
|
||||
},
|
||||
windowsResize: throttle(function () {
|
||||
// Mark as resizing to disable transitions
|
||||
if (!this.isResizing) {
|
||||
this.isResizing = true;
|
||||
if (this.$refs.listingView) {
|
||||
this.$refs.listingView.classList.add('resizing');
|
||||
}
|
||||
}
|
||||
|
||||
// Clear existing timeout
|
||||
if (this.resizeTimeout) {
|
||||
clearTimeout(this.resizeTimeout);
|
||||
}
|
||||
|
||||
// Do the resize work
|
||||
this.colunmsResize();
|
||||
this.width = window.innerWidth;
|
||||
mutations.setMobile();
|
||||
|
||||
// Re-enable transitions after resize is complete
|
||||
this.resizeTimeout = setTimeout(() => {
|
||||
this.isResizing = false;
|
||||
if (this.$refs.listingView) {
|
||||
this.$refs.listingView.classList.remove('resizing');
|
||||
}
|
||||
}, 150); // Wait 150ms after last resize event
|
||||
|
||||
// Listing element is not displayed
|
||||
if (this.$refs.listingView == null) return;
|
||||
}, 100),
|
||||
@@ -1175,13 +1212,27 @@ export default {
|
||||
y: event.clientY - listingRect.top
|
||||
};
|
||||
|
||||
this.updateSelectedItemsInRectangle(event.ctrlKey || event.metaKey);
|
||||
// Use requestAnimationFrame to batch updates
|
||||
if (!this.selectionUpdatePending) {
|
||||
this.selectionUpdatePending = true;
|
||||
this.rafId = requestAnimationFrame(() => {
|
||||
this.updateSelectedItemsInRectangle(event.ctrlKey || event.metaKey);
|
||||
this.selectionUpdatePending = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
endRectangleSelection(event) {
|
||||
if (!this.isRectangleSelecting) return;
|
||||
|
||||
// Cancel any pending animation frame
|
||||
if (this.rafId) {
|
||||
cancelAnimationFrame(this.rafId);
|
||||
this.rafId = null;
|
||||
}
|
||||
|
||||
this.isRectangleSelecting = false;
|
||||
this.selectionUpdatePending = false;
|
||||
this.updateSelectedItemsInRectangle(event.ctrlKey || event.metaKey);
|
||||
|
||||
// Clear rectangle after a short delay
|
||||
@@ -1205,8 +1256,8 @@ export default {
|
||||
|
||||
const rectangleSelectedIndexes = [];
|
||||
|
||||
// Get all item elements
|
||||
const itemElements = this.$el.querySelectorAll('.listing-item');
|
||||
// Get all item elements - use querySelectorAll with specific selector for better performance
|
||||
const itemElements = this.$el.querySelectorAll('.listing-item[data-index]');
|
||||
|
||||
itemElements.forEach((element) => {
|
||||
const elementRect = element.getBoundingClientRect();
|
||||
@@ -1233,7 +1284,7 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
// Update selection based on modifier keys (ctrl/meta)
|
||||
// Batch DOM updates to minimize reflows
|
||||
if (isAdditive) {
|
||||
// only add more items to the current selection without reset selection
|
||||
const newSelection = [...state.selected];
|
||||
|
||||
@@ -103,10 +103,10 @@ export default {
|
||||
return this.previewType == 'image' || this.pdfConvertable;
|
||||
},
|
||||
autoPlay() {
|
||||
return state.user.preview.autoplayMedia;
|
||||
return getters.previewPerms().autoplayMedia;
|
||||
},
|
||||
useDefaultMediaPlayer() {
|
||||
return state.user.preview.defaultMediaPlayer === true;
|
||||
return getters.previewPerms().defaultMediaPlayer === true;
|
||||
},
|
||||
isMobileSafari() {
|
||||
const userAgent = window.navigator.userAgent;
|
||||
|
||||
@@ -24,7 +24,7 @@ test("share file works", async ({ page, checkForErrors, context }) => {
|
||||
});
|
||||
|
||||
test("share download single file", async ({ page, checkForErrors, context }) => {
|
||||
await page.goto("/files/files/");
|
||||
await page.goto("/files/");
|
||||
await expect(page).toHaveTitle("Graham's Filebrowser - Files - playwright-files");
|
||||
|
||||
const shareHash = await page.evaluate(() => localStorage.getItem('shareHash'));
|
||||
|
||||
Reference in new issue
Block a user