Compare commits

...
9 Commits
54 changed files with 979 additions and 742 deletions

No files matched your search

+12 -12
View File
@@ -15,18 +15,18 @@ jobs:
go-version: 'stable'
- working-directory: backend
run: go test -race -v ./...
# golanci-lint is currently disabled due to slowness
# lint-backend:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# - uses: actions/setup-go@v5
# with:
# go-version: 'stable'
# - uses: golangci/golangci-lint-action@v5
# with:
# version: 'v1.64'
# working-directory: backend
lint-backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 'stable'
- uses: golangci/golangci-lint-action@v7
with:
version: 'v2.1.6'
working-directory: backend
install-mode: goinstall
format-backend:
runs-on: ubuntu-latest
steps:
+26
View File
@@ -2,6 +2,32 @@
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).
## v0.7.9-beta
**New Features**:
- Admin users will get a small notification banner for available update in sidebar with link to new release.
**Notes**:
- docker now defaults to ./data/databse.db as the database path allowing a simplified initial docker-compose.yaml. Existing configurations do not need updating.
- oidc groups header updates admin permission of existing user (either add/remove if role exists)'
- builds amd64 binary with musl for compatibility (glic error) https://github.com/gtsteffaniak/filebrowser/issues/755
- renamed `server.sources.config.disabled` to `server.sources.config.disableIndexing`
- better support for running with disabled index.
- small indexing behavior tweaks.
- markdown viewer hides sidebar https://github.com/gtsteffaniak/filebrowser/issues/744
- quick download only applies to files
**BugFixes**:
- subtitles filename issue https://github.com/gtsteffaniak/filebrowser/issues/678
- search result links not working with custom baseUrl https://github.com/gtsteffaniak/filebrowser/issues/746
- preview error for office native preview https://github.com/gtsteffaniak/filebrowser/issues/744
- more source name safety for special characters.
- shares with special character errors https://github.com/gtsteffaniak/filebrowser/issues/753
- backspace navigates back a page when typing https://github.com/gtsteffaniak/filebrowser/issues/663
- markdown viewer scrolling https://github.com/gtsteffaniak/filebrowser/issues/767
- fix user permissions updated when modifying api key permissions
- fix language change issue https://github.com/gtsteffaniak/filebrowser/issues/768 https://github.com/gtsteffaniak/filebrowser/issues/487
## v0.7.8-beta
Note: if using oidc, please update from 0.7.7 to resolve invalid_grant issue. Also - oidc no longer creates users automatically by default -- must be enabled.
+18 -9
View File
@@ -18,6 +18,18 @@
> [!WARNING]
> There is no stable version -- planned for 2025. ([Read more](https://github.com/gtsteffaniak/filebrowser/discussions/628))
## About
FileBrowser Quantum provides an easy way to access and manage your files from the web. It has has a web page interface that allows you to create secure shared links, users with their own specific permissions and settings, and offers a great viewing experience for many file types.
This version is called "Quantum" because it packs tons of advanced features into a tiny easy to run file. Unlike the majority of alternative options, FileBrowser Quantum is simple to install and easy to configure.
The goal for this repo is to become the best open-source self-hosted file browsing application that exists -- **all for free**. This repo will always be free and open-source.
Ready to try it out? See [Getting Started Wiki](https://github.com/gtsteffaniak/filebrowser/wiki/Getting-Started).
## How its different
FileBrowser Quantum is a massive fork of the file browser open-source project with the following changes:
1. ✅ Multiple sources support
@@ -43,18 +55,11 @@ Notable features that this fork *does not* have (removed):
- ❌ rules are not supported yet (planned).
- ❌ shell commands are completely removed and will not be returned.
## About
> [!WARNING]
> Every file and directory in the source gets indexed (by default). This enables powerful features such as instant search, but large source filesystems can increase your system requirements. [See indexing wiki](https://github.com/gtsteffaniak/filebrowser/wiki/Indexing) for more info.
FileBrowser Quantum differs significantly from the original version. Many of these changes required a significant overhaul. Creating a fork was a necessary process to make the program better. There have been many growing pains, but a stable release is planned and coming soon.
This version is called "Quantum" because it packs tons of advanced features in a tiny executable file. Unlike the majority of alternative options, FileBrowser Quantum is simple to install and easy to configure.
The goal for this repo is to become the best open-source self-hosted file browsing application that exists -- **all for free**.
This repo will always be free and open-source.
For more, see the [Q&A Wiki](https://github.com/gtsteffaniak/filebrowser/wiki/Q&A)
## The UI
The UI has a simple three-component navigation system:
@@ -94,6 +99,10 @@ See [Office Support Wiki](https://github.com/gtsteffaniak/filebrowser/wiki/Offic
See the [Migration Wiki](https://github.com/gtsteffaniak/filebrowser/wiki/Migration)
## Other Questions
For more, see the [Q&A Wiki](https://github.com/gtsteffaniak/filebrowser/wiki/Q&A)
## Comparison Chart
Application Name | <img width="48" src="https://github.com/user-attachments/assets/59986a2a-f960-4536-aa35-4a9a7c98ad48" > Quantum | <img width="48" src="https://github.com/filebrowser/filebrowser/blob/master/frontend/public/img/logo.svg" > Filebrowser | <img width="48" src="https://github.com/mickael-kerjean/filestash/blob/master/public/assets/logo/app_icon.png?raw=true" > Filestash | <img width="48" src="https://avatars.githubusercontent.com/u/19211038?s=200&v=4" > Nextcloud | <img width="48" src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/da/Google_Drive_logo.png/480px-Google_Drive_logo.png" > Google_Drive | <img width="48" src="https://avatars.githubusercontent.com/u/6422152?v=4" > FileRun
+2 -2
View File
@@ -21,8 +21,8 @@ RUN npm run build-docker
FROM alpine:latest
COPY --from=ffmpeg [ "/ffmpeg", "/ffprobe", "/usr/local/bin/" ]
ENV FILEBROWSER_FFMPEG_PATH="/usr/local/bin/"
ENV FILEBROWSER_DATABASE="/home/filebrowser/data/database.db"
ENV FILEBROWSER_NO_EMBEDED="true"
ENV PATH="$PATH:/home/filebrowser"
RUN apk --no-cache add ca-certificates mailcap tzdata
@@ -30,7 +30,7 @@ RUN adduser -D -s /bin/true -u 1000 filebrowser
USER filebrowser
WORKDIR /home/filebrowser
COPY --from=base --chown=filebrowser:1000 /app/filebrowser ./
COPY --from=base --chown=filebrowser:1000 /app/config.media.yaml ./config.yaml
COPY --from=base --chown=filebrowser:1000 /app/config.yaml ./data/config.yaml
COPY --from=nbuild --chown=filebrowser:1000 /app/dist/ ./http/dist/
## sanity checks
RUN [ "filebrowser", "version" ]
+2 -1
View File
@@ -19,13 +19,14 @@ RUN npm run build-docker
FROM alpine:latest
ENV FILEBROWSER_NO_EMBEDED="true"
ENV FILEBROWSER_DATABASE="/home/filebrowser/data/database.db"
ENV PATH="$PATH:/home/filebrowser"
RUN apk --no-cache add ca-certificates mailcap tzdata
RUN adduser -D -s /bin/true -u 1000 filebrowser
USER filebrowser
WORKDIR /home/filebrowser
COPY --from=base --chown=filebrowser:1000 /app/filebrowser ./
COPY --from=base --chown=filebrowser:1000 /app/config.yaml ./config.yaml
COPY --from=base --chown=filebrowser:1000 /app/config.yaml ./data/config.yaml
COPY --from=nbuild --chown=filebrowser:1000 /app/dist/ ./http/dist/
## sanity checks
RUN [ "filebrowser", "version" ]
+145 -231
View File
@@ -1,232 +1,146 @@
# This code is licensed under the terms of the MIT license https://opensource.org/license/mit
# Copyright (c) 2021 Marat Reymers
## Golden config for golangci-lint v1.59.1
#
# This is the best config for golangci-lint based on my experience and opinion.
# It is very strict, but not extremely strict.
# Feel free to adapt and change it for your needs.
run:
# Timeout for analysis, e.g. 30s, 5m.
# Default: 1m
timeout: 3m
# This file contains only configs which differ from defaults.
# All possible options can be found here https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml
linters-settings:
cyclop:
# The maximal code complexity to report.
# Default: 10
max-complexity: 30
# The maximal average package complexity.
# If it's higher than 0.0 (float) the check is enabled
# Default: 0.0
package-average: 10.0
errcheck:
# Report about not checking of errors in type assertions: `a := b.(MyStruct)`.
# Such cases aren't reported by default.
# Default: false
check-type-assertions: true
exhaustive:
# Program elements to check for exhaustiveness.
# Default: [ switch ]
check:
- switch
- map
exhaustruct:
# List of regular expressions to exclude struct packages and their names from checks.
# Regular expressions must match complete canonical struct package/name/structname.
# Default: []
exclude:
# std libs
- "^net/http.Client$"
- "^net/http.Cookie$"
- "^net/http.Request$"
- "^net/http.Response$"
- "^net/http.Server$"
- "^net/http.Transport$"
- "^net/url.URL$"
- "^os/exec.Cmd$"
- "^reflect.StructField$"
# public libs
- "^github.com/Shopify/sarama.Config$"
- "^github.com/Shopify/sarama.ProducerMessage$"
- "^github.com/mitchellh/mapstructure.DecoderConfig$"
- "^github.com/prometheus/client_golang/.+Opts$"
- "^github.com/spf13/cobra.Command$"
- "^github.com/spf13/cobra.CompletionOptions$"
- "^github.com/stretchr/testify/mock.Mock$"
- "^github.com/testcontainers/testcontainers-go.+Request$"
- "^github.com/testcontainers/testcontainers-go.FromDockerfile$"
- "^golang.org/x/tools/go/analysis.Analyzer$"
- "^google.golang.org/protobuf/.+Options$"
- "^gopkg.in/yaml.v3.Node$"
funlen:
# Checks the number of lines in a function.
# If lower than 0, disable the check.
# Default: 60
lines: 100
# Checks the number of statements in a function.
# If lower than 0, disable the check.
# Default: 40
statements: 50
# Ignore comments when counting lines.
# Default false
ignore-comments: true
gocognit:
# Minimal code complexity to report.
# Default: 30 (but we recommend 10-20)
min-complexity: 20
gocritic:
# Settings passed to gocritic.
# The settings key is the name of a supported gocritic checker.
# The list of supported checkers can be find in https://go-critic.github.io/overview.
settings:
captLocal:
# Whether to restrict checker to params only.
# Default: true
paramsOnly: false
underef:
# Whether to skip (*x).method() calls where x is a pointer receiver.
# Default: true
skipRecvDeref: false
gomodguard:
blocked:
# List of blocked modules.
# Default: []
modules:
- github.com/golang/protobuf:
recommendations:
- google.golang.org/protobuf
reason: "see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules"
- github.com/satori/go.uuid:
recommendations:
- github.com/google/uuid
reason: "satori's package is not maintained"
- github.com/gofrs/uuid:
recommendations:
- github.com/gofrs/uuid/v5
reason: "gofrs' package was not go module before v5"
govet:
# Enable all analyzers.
# Default: false
enable-all: true
# Disable analyzers by name.
# Run `go tool vet help` to see all analyzers.
# Default: []
disable:
- fieldalignment # too strict
# Settings per analyzer.
settings:
shadow:
# Whether to be strict about shadowing; can be noisy.
# Default: false
strict: false
inamedparam:
# Skips check for interface methods with only a single parameter.
# Default: false
skip-single-param: true
mnd:
# List of function patterns to exclude from analysis.
# Values always ignored: `time.Date`,
# `strconv.FormatInt`, `strconv.FormatUint`, `strconv.FormatFloat`,
# `strconv.ParseInt`, `strconv.ParseUint`, `strconv.ParseFloat`.
# Default: []
ignored-functions:
- args.Error
- flag.Arg
- flag.Duration.*
- flag.Float.*
- flag.Int.*
- flag.Uint.*
- os.Chmod
- os.Mkdir.*
- os.OpenFile
- os.WriteFile
- prometheus.ExponentialBuckets.*
- prometheus.LinearBuckets
nakedret:
# Make an issue if func has more lines of code than this setting, and it has naked returns.
# Default: 30
max-func-lines: 0
nolintlint:
# Exclude following linters from requiring an explanation.
# Default: []
allow-no-explanation: [ funlen, gocognit, lll ]
# Enable to require an explanation of nonzero length after each nolint directive.
# Default: false
require-explanation: true
# Enable to require nolint directives to mention the specific linter being suppressed.
# Default: false
require-specific: true
perfsprint:
# Optimizes into strings concatenation.
# Default: true
strconcat: false
rowserrcheck:
# database/sql is always checked
# Default: []
packages:
- github.com/jmoiron/sqlx
sloglint:
# Enforce not using global loggers.
# Values:
# - "": disabled
# - "all": report all global loggers
# - "default": report only the default slog logger
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-global
# Default: ""
no-global: "all"
# Enforce using methods that accept a context.
# Values:
# - "": disabled
# - "all": report all contextless calls
# - "scope": report only if a context exists in the scope of the outermost function
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#context-only
# Default: ""
context: "scope"
tenv:
# The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.
# Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked.
# Default: false
all: true
issues:
# Maximum count of issues with the same text.
# Set to 0 to disable.
# Default: 3
max-same-issues: 50
exclude-rules:
- source: "(noinspection|TODO)"
linters: [ godot ]
- source: "//noinspection"
linters: [ gocritic ]
- path: "_test\\.go"
linters:
- bodyclose
- dupl
version: "2"
linters:
settings:
staticcheck:
checks:
- all
- '-QF1001'
- '-ST1003'
- '-ST1016'
- '-QF1004'
- '-QF1003'
cyclop:
max-complexity: 30
package-average: 10
errcheck:
check-type-assertions: true
exhaustive:
check:
- switch
- map
exhaustruct:
exclude:
- ^net/http.Client$
- ^net/http.Cookie$
- ^net/http.Request$
- ^net/http.Response$
- ^net/http.Server$
- ^net/http.Transport$
- ^net/url.URL$
- ^os/exec.Cmd$
- ^reflect.StructField$
- ^github.com/Shopify/sarama.Config$
- ^github.com/Shopify/sarama.ProducerMessage$
- ^github.com/mitchellh/mapstructure.DecoderConfig$
- ^github.com/prometheus/client_golang/.+Opts$
- ^github.com/spf13/cobra.Command$
- ^github.com/spf13/cobra.CompletionOptions$
- ^github.com/stretchr/testify/mock.Mock$
- ^github.com/testcontainers/testcontainers-go.+Request$
- ^github.com/testcontainers/testcontainers-go.FromDockerfile$
- ^golang.org/x/tools/go/analysis.Analyzer$
- ^google.golang.org/protobuf/.+Options$
- ^gopkg.in/yaml.v3.Node$
funlen:
lines: 100
statements: 50
ignore-comments: true
gocognit:
min-complexity: 20
gocritic:
settings:
captLocal:
paramsOnly: false
underef:
skipRecvDeref: false
gomodguard:
blocked:
modules:
- github.com/golang/protobuf:
recommendations:
- google.golang.org/protobuf
reason: see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules
- github.com/satori/go.uuid:
recommendations:
- github.com/google/uuid
reason: satori's package is not maintained
- github.com/gofrs/uuid:
recommendations:
- github.com/gofrs/uuid/v5
reason: gofrs' package was not go module before v5
govet:
disable:
- fieldalignment
enable-all: true
settings:
shadow:
strict: false
inamedparam:
skip-single-param: true
mnd:
ignored-functions:
- args.Error
- flag.Arg
- flag.Duration.*
- flag.Float.*
- flag.Int.*
- flag.Uint.*
- os.Chmod
- os.Mkdir.*
- os.OpenFile
- os.WriteFile
- prometheus.ExponentialBuckets.*
- prometheus.LinearBuckets
nakedret:
max-func-lines: 0
nolintlint:
require-explanation: true
require-specific: true
allow-no-explanation:
- funlen
- goconst
- gosec
- noctx
- wrapcheck
- gocognit
- lll
perfsprint:
strconcat: false
rowserrcheck:
packages:
- github.com/jmoiron/sqlx
sloglint:
no-global: all
context: scope
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- godot
source: (noinspection|TODO)
- linters:
- gocritic
source: //noinspection
- linters:
- bodyclose
- dupl
- funlen
- goconst
- gosec
- noctx
- wrapcheck
path: _test\.go
paths:
- third_party$
- builtin$
- examples$
issues:
max-same-issues: 50
formatters:
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
+2
View File
@@ -7,8 +7,10 @@ builds:
- id: linux-amd64
env:
- CGO_ENABLED=1
- CC=zig cc -target x86_64-linux-musl
tags:
- mupdf
- musl
ldflags: &ldflags
- -s -w -X github.com/gtsteffaniak/filebrowser/backend/common/version.Version={{ .Version }} -X github.com/gtsteffaniak/filebrowser/backend/common/version.CommitSHA={{ .ShortCommit }}
main: main.go
+15 -24
View File
@@ -41,32 +41,23 @@ func FileInfoFaster(opts iteminfo.FileOptions) (iteminfo.ExtendedFileInfo, error
return response, err
}
opts.IsDir = isDir
// TODO: whats the best way to save trips to disk here?
// disabled using cache because its not clear if this is helping or hurting
// check if the file exists in the index
//info, exists := index.GetReducedMetadata(opts.Path, opts.IsDir)
//if exists {
// err := RefreshFileInfo(opts)
// if err != nil {
// return info, err
// }
// if opts.Content {
// content := ""
// content, err = getContent(opts.Path)
// if err != nil {
// return info, err
// }
// info.Content = content
// }
// return info, nil
//}
var info *iteminfo.FileInfo
var exists bool
err = index.RefreshFileInfo(opts)
if err != nil {
return response, err
}
info, exists := index.GetReducedMetadata(opts.Path, opts.IsDir)
if !exists {
return response, fmt.Errorf("could not get metadata for path: %v", opts.Path)
if err == errors.ErrNotIndexed && index.Config.DisableIndexing {
info, err = index.GetFsDirInfo(opts.Path)
if err != nil {
return response, err
}
} else {
return response, fmt.Errorf("could not refresh file info: %v", err)
}
} else {
info, exists = index.GetReducedMetadata(opts.Path, opts.IsDir)
if !exists {
return response, fmt.Errorf("could not get metadata for path: %v", opts.Path)
}
}
if opts.Content {
if info.Size < 20*1024*1024 { // 20 megabytes in bytes
+9 -4
View File
@@ -36,10 +36,15 @@ func runCLI() bool {
if configPath == "" {
configPath = os.Getenv("FILEBROWSER_CONFIG")
}
if configPath == "" {
configPath = "config.yaml"
// backwards compatibility in docker, prefer config.yaml if it exists
if configPath != "" {
_, err := os.Stat(configPath)
if err != nil {
logger.Fatalf("config file %v does not exist, please create it or set the FILEBROWSER_CONFIG environment variable to a valid config file path", configPath)
}
} else {
configPath = "config.yaml"
}
}
// Parse global flags (before subcommands)
+11 -2
View File
@@ -7,8 +7,12 @@ import (
"os/signal"
"syscall"
_ "net/http/pprof"
"github.com/gtsteffaniak/filebrowser/backend/adapters/fs/fileutils"
"github.com/gtsteffaniak/filebrowser/backend/common/settings"
"github.com/gtsteffaniak/filebrowser/backend/common/utils"
"github.com/gtsteffaniak/filebrowser/backend/common/version"
"github.com/gtsteffaniak/filebrowser/backend/database/storage"
fbhttp "github.com/gtsteffaniak/filebrowser/backend/http"
"github.com/gtsteffaniak/filebrowser/backend/indexing"
@@ -16,8 +20,6 @@ import (
"github.com/gtsteffaniak/filebrowser/backend/swagger/docs"
"github.com/gtsteffaniak/go-logger/logger"
"github.com/swaggo/swag"
"github.com/gtsteffaniak/filebrowser/backend/common/version"
)
var store *storage.Storage
@@ -51,6 +53,13 @@ func StartFilebrowser() {
if !keepGoing {
return
}
info, _ := utils.CheckForUpdates()
if info.LatestVersion != "" {
logger.Infof("A new version is available: %s (current: %s)", info.LatestVersion, info.CurrentVersion)
logger.Infof("Release notes: %s", info.ReleaseNotes)
}
go utils.StartCheckForUpdates()
// Create context and channels for graceful shutdown
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
+1
View File
@@ -21,4 +21,5 @@ var (
ErrNoTotpProvided = errors.New("OTP code is required for user")
ErrNoTotpConfigured = errors.New("OTP is enforced, but user is not yet configured")
ErrUnauthorized = errors.New("user unauthorized")
ErrNotIndexed = errors.New("directory or item excluded from indexing")
)
+6 -2
View File
@@ -226,7 +226,7 @@ func loadConfigWithDefaults(configFile string) error {
// Open and read the YAML file
yamlFile, err := os.Open(configFile)
if err != nil {
logger.Warningf("could not open config file '%v', using default settings", configFile)
logger.Errorf("could not open config file '%v', using default settings.", configFile)
Config.Server.Sources = []Source{
{
Path: ".",
@@ -292,12 +292,16 @@ func setDefaults() Settings {
if cpus := runtime.NumCPU(); cpus > 0 {
numCpus = cpus
}
database := os.Getenv("FILEBROWSER_DATABASE")
if database == "" {
database = "database.db"
}
return Settings{
Server: Server{
Port: 80,
NumImageProcessors: numCpus,
BaseURL: "",
Database: "database.db",
Database: database,
SourceMap: map[string]Source{},
NameToSource: map[string]Source{},
MaxArchiveSizeGB: 50,
+30 -29
View File
@@ -78,17 +78,17 @@ type Source struct {
}
type SourceConfig struct {
IndexingInterval uint32 `json:"indexingInterval"` // optional manual overide interval in seconds to re-index the source
Disabled bool `json:"disabled"` // disable the indexing of this source
MaxWatchers int `json:"maxWatchers"` // number of concurrent watchers to use for this source, currently not supported
NeverWatch []string `json:"neverWatchPaths"` // paths to never watch, relative to the source path (eg. "/folder/file.txt")
IgnoreHidden bool `json:"ignoreHidden"` // ignore hidden files and folders.
IgnoreZeroSizeFolders bool `json:"ignoreZeroSizeFolders"` // ignore folders with 0 size
Exclude IndexFilter `json:"exclude"` // exclude files and folders from indexing, if include is not set
Include IndexFilter `json:"include"` // include files and folders from indexing, if exclude is not set
DefaultUserScope string `json:"defaultUserScope"` // default "/" should match folders under path
DefaultEnabled bool `json:"defaultEnabled"` // should be added as a default source for new users?
CreateUserDir bool `json:"createUserDir"` // create a user directory for each user
IndexingInterval uint32 `json:"indexingIntervalMinutes"` // optional manual overide interval in seconds to re-index the source
DisableIndexing bool `json:"disableIndexing"` // disable the indexing of this source
MaxWatchers int `json:"maxWatchers"` // number of concurrent watchers to use for this source, currently not supported
NeverWatch []string `json:"neverWatchPaths"` // paths to never watch, relative to the source path (eg. "/folder/file.txt")
IgnoreHidden bool `json:"ignoreHidden"` // ignore hidden files and folders.
IgnoreZeroSizeFolders bool `json:"ignoreZeroSizeFolders"` // ignore folders with 0 size
Exclude IndexFilter `json:"exclude"` // exclude files and folders from indexing, if include is not set
Include IndexFilter `json:"include"` // include files and folders from indexing, if exclude is not set
DefaultUserScope string `json:"defaultUserScope"` // default "/" should match folders under path
DefaultEnabled bool `json:"defaultEnabled"` // should be added as a default source for new users?
CreateUserDir bool `json:"createUserDir"` // create a user directory for each user
}
type IndexFilter struct {
@@ -113,22 +113,23 @@ type ExternalLink struct {
// UserDefaults is a type that holds the default values
// for some fields on User.
type UserDefaults struct {
StickySidebar bool `json:"stickySidebar"` // keep sidebar open when navigating
DarkMode bool `json:"darkMode"` // should dark mode be enabled
Locale string `json:"locale"` // language to use: eg. de, en, or fr
ViewMode string `json:"viewMode"` // view mode to use: eg. normal, list, grid, or compact
SingleClick bool `json:"singleClick"` // open directory on single click, also enables middle click to open in new tab
ShowHidden bool `json:"showHidden"` // show hidden files in the UI. On windows this includes files starting with a dot and windows hidden files
DateFormat bool `json:"dateFormat"` // when false, the date is relative, when true, the date is an exact timestamp
GallerySize int `json:"gallerySize"` // 0-9 - the size of the gallery thumbnails
ThemeColor string `json:"themeColor"` // theme color to use: eg. #ff0000, or var(--red), var(--purple), etc
QuickDownload bool `json:"quickDownload"` // show icon to download in one click
DisableOnlyOfficeExt string `json:"disableOnlyOfficeExt"` // comma separated list of file extensions to disable onlyoffice preview for
DisableOfficePreviewExt string `json:"disableOfficePreviewExt"` // comma separated list of file extensions to disable office preview for
LockPassword bool `json:"lockPassword"` // disable the user from changing their password
DisableSettings bool `json:"disableSettings,omitempty"` // disable the user from viewing the settings page
Preview users.Preview `json:"preview"`
DefaultScopes []users.SourceScope `json:"-"`
Permissions users.Permissions `json:"permissions"`
LoginMethod string `json:"loginMethod,omitempty"` // login method to use: eg. password, proxy, oidc
StickySidebar bool `json:"stickySidebar"` // keep sidebar open when navigating
DarkMode bool `json:"darkMode"` // should dark mode be enabled
Locale string `json:"locale"` // language to use: eg. de, en, or fr
ViewMode string `json:"viewMode"` // view mode to use: eg. normal, list, grid, or compact
SingleClick bool `json:"singleClick"` // open directory on single click, also enables middle click to open in new tab
ShowHidden bool `json:"showHidden"` // show hidden files in the UI. On windows this includes files starting with a dot and windows hidden files
DateFormat bool `json:"dateFormat"` // when false, the date is relative, when true, the date is an exact timestamp
GallerySize int `json:"gallerySize"` // 0-9 - the size of the gallery thumbnails
ThemeColor string `json:"themeColor"` // theme color to use: eg. #ff0000, or var(--red), var(--purple), etc
QuickDownload bool `json:"quickDownload"` // show icon to download in one click
DisableOnlyOfficeExt string `json:"disableOnlyOfficeExt"` // comma separated list of file extensions to disable onlyoffice preview for
DisableOfficePreviewExt string `json:"disableOfficePreviewExt"` // comma separated list of file extensions to disable office preview for
LockPassword bool `json:"lockPassword"` // disable the user from changing their password
DisableSettings bool `json:"disableSettings,omitempty"` // disable the user from viewing the settings page
Preview users.Preview `json:"preview"`
DefaultScopes []users.SourceScope `json:"-"`
Permissions users.Permissions `json:"permissions"`
LoginMethod string `json:"loginMethod,omitempty"` // login method to use: eg. password, proxy, oidc
DisableUpdateNotifications bool `json:"disableUpdateNotifications"` // disable update notifications banner for admin users
}
+102
View File
@@ -0,0 +1,102 @@
package utils
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"github.com/gtsteffaniak/filebrowser/backend/common/version"
"github.com/gtsteffaniak/go-logger/logger"
"golang.org/x/mod/semver"
)
var updateAvailableUrl = ""
type Tag struct {
Name string `json:"name"`
}
type updateInfo struct {
LatestVersion string
CurrentVersion string
ReleaseNotes string
ReleaseUrl string
}
func CheckForUpdates() (updateInfo, error) {
// --- Configuration ---
repoOwner := "gtsteffaniak"
repoName := "filebrowser"
currentVersion := version.Version
splitVersion := strings.Split(currentVersion, "-")
versionCategory := "stable"
if len(splitVersion) > 1 {
versionCategory = splitVersion[1]
}
githubApiUrl := fmt.Sprintf("https://api.github.com/repos/%s/%s/tags", repoOwner, repoName)
// Make the GET request
resp, err := http.Get(githubApiUrl)
if err != nil {
return updateInfo{}, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return updateInfo{}, fmt.Errorf("failed to fetch tags from GitHub API")
}
// Decode the JSON response
var tags []Tag
if err := json.NewDecoder(resp.Body).Decode(&tags); err != nil {
return updateInfo{}, err
}
// Find the latest beta version greater than the current one
var NewVersion string
for _, tag := range tags {
// Check if the version is valid, is a pre-release, and contains "beta"
if semver.IsValid(tag.Name) && strings.Contains(semver.Prerelease(tag.Name), versionCategory) {
// Check if this tag is greater than the current version
// and also greater than any other beta version we've found so far
if semver.Compare(tag.Name, currentVersion) > 0 {
if NewVersion == "" || semver.Compare(tag.Name, NewVersion) > 0 {
NewVersion = tag.Name
}
}
}
}
if NewVersion == "" {
// No newer version found, return empty updateInfo
return updateInfo{}, fmt.Errorf("no newer version found for %s", currentVersion)
}
updateAvailableUrl = fmt.Sprintf("https://github.com/%s/%s/releases/tag/%s", repoOwner, repoName, NewVersion)
return updateInfo{
LatestVersion: NewVersion,
CurrentVersion: currentVersion,
ReleaseNotes: updateAvailableUrl,
ReleaseUrl: updateAvailableUrl,
}, nil
}
func GetUpdateAvailableUrl() string {
return updateAvailableUrl
}
// starts a background process to check for updates periodically.
func StartCheckForUpdates() {
// Create a ticker that fires every 24 hours.
ticker := time.NewTicker(24 * time.Hour)
defer ticker.Stop()
// Start a loop that waits for the ticker to fire.
for range ticker.C {
_, err := CheckForUpdates()
if err != nil {
// In a real application, you might want more sophisticated logging.
logger.Debug("update check failed:", err)
continue // Don't stop the loop, just wait for the next tick.
}
// Update the global variable with the result of the check.
// This will be an empty string if no newer version was found.
}
}
+16 -15
View File
@@ -75,21 +75,22 @@ type SourceScope struct {
// json tags must match variable name with smaller case first letter
type NonAdminEditable struct {
Preview Preview `json:"preview"`
StickySidebar bool `json:"stickySidebar"` // keep sidebar open when navigating
DarkMode bool `json:"darkMode"` // should dark mode be enabled
Password string `json:"password,omitempty"`
Locale string `json:"locale"` // language to use: eg. de, en, or fr
ViewMode string `json:"viewMode"` // view mode to use: eg. normal, list, grid, or compact
SingleClick bool `json:"singleClick"` // open directory on single click, also enables middle click to open in new tab
Sorting Sorting `json:"sorting"`
ShowHidden bool `json:"showHidden"` // show hidden files in the UI. On windows this includes files starting with a dot and windows hidden files
DateFormat bool `json:"dateFormat"` // when false, the date is relative, when true, the date is an exact timestamp
GallerySize int `json:"gallerySize"` // 0-9 - the size of the gallery thumbnails
ThemeColor string `json:"themeColor"` // theme color to use: eg. #ff0000, or var(--red), var(--purple), etc
QuickDownload bool `json:"quickDownload"` // show icon to download in one click
DisableOnlyOfficeExt string `json:"disableOnlyOfficeExt"` // comma separated list of file extensions to disable onlyoffice preview for
DisableOfficePreviewExt string `json:"disableOfficePreviewExt"` // comma separated list of file extensions to disable office preview for
Preview Preview `json:"preview"`
StickySidebar bool `json:"stickySidebar"` // keep sidebar open when navigating
DarkMode bool `json:"darkMode"` // should dark mode be enabled
Password string `json:"password,omitempty"`
Locale string `json:"locale"` // language to use: eg. de, en, or fr
ViewMode string `json:"viewMode"` // view mode to use: eg. normal, list, grid, or compact
SingleClick bool `json:"singleClick"` // open directory on single click, also enables middle click to open in new tab
Sorting Sorting `json:"sorting"`
ShowHidden bool `json:"showHidden"` // show hidden files in the UI. On windows this includes files starting with a dot and windows hidden files
DateFormat bool `json:"dateFormat"` // when false, the date is relative, when true, the date is an exact timestamp
GallerySize int `json:"gallerySize"` // 0-9 - the size of the gallery thumbnails
ThemeColor string `json:"themeColor"` // theme color to use: eg. #ff0000, or var(--red), var(--purple), etc
QuickDownload bool `json:"quickDownload"` // show icon to download in one click
DisableOnlyOfficeExt string `json:"disableOnlyOfficeExt"` // comma separated list of file extensions to disable onlyoffice preview for
DisableOfficePreviewExt string `json:"disableOfficePreviewExt"` // comma separated list of file extensions to disable office preview for
DisableUpdateNotifications bool `json:"disableUpdateNotifications"` // disable update notifications
}
var PublicUser = User{
+66 -53
View File
@@ -20,8 +20,8 @@ require (
github.com/stretchr/testify v1.10.0
github.com/swaggo/http-swagger v1.3.4
github.com/swaggo/swag v1.16.4
golang.org/x/crypto v0.38.0
golang.org/x/image v0.27.0
golang.org/x/crypto v0.39.0
golang.org/x/image v0.28.0
golang.org/x/oauth2 v0.30.0
golang.org/x/sys v0.33.0
gopkg.in/yaml.v3 v3.0.1
@@ -32,55 +32,63 @@ require (
4d63.com/gochecknoglobals v0.2.2 // indirect
github.com/4meepo/tagalign v1.4.2 // indirect
github.com/Abirdcfly/dupword v0.1.3 // indirect
github.com/Antonboom/errname v1.0.0 // indirect
github.com/Antonboom/nilnil v1.0.1 // indirect
github.com/Antonboom/testifylint v1.5.2 // indirect
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
github.com/Crocmagnon/fatcontext v0.7.1 // indirect
github.com/Antonboom/errname v1.1.0 // indirect
github.com/Antonboom/nilnil v1.1.0 // indirect
github.com/Antonboom/testifylint v1.6.1 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/Masterminds/semver/v3 v3.3.1 // indirect
github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect
github.com/alecthomas/chroma/v2 v2.17.2 // indirect
github.com/alecthomas/go-check-sumtype v0.3.1 // indirect
github.com/alexkohler/nakedret/v2 v2.0.5 // indirect
github.com/alexkohler/nakedret/v2 v2.0.6 // indirect
github.com/alexkohler/prealloc v1.0.0 // indirect
github.com/alingse/asasalint v0.0.11 // indirect
github.com/alingse/nilnesserr v0.1.2 // indirect
github.com/alingse/nilnesserr v0.2.0 // indirect
github.com/ashanbrown/forbidigo v1.6.0 // indirect
github.com/ashanbrown/makezero v1.2.0 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bkielbasa/cyclop v1.2.3 // indirect
github.com/blizzy78/varnamelen v0.8.0 // indirect
github.com/bombsimon/wsl/v4 v4.5.0 // indirect
github.com/bombsimon/wsl/v4 v4.7.0 // indirect
github.com/boombuler/barcode v1.0.2 // indirect
github.com/breml/bidichk v0.3.2 // indirect
github.com/breml/errchkjson v0.4.0 // indirect
github.com/butuzov/ireturn v0.3.1 // indirect
github.com/breml/bidichk v0.3.3 // indirect
github.com/breml/errchkjson v0.4.1 // indirect
github.com/butuzov/ireturn v0.4.0 // indirect
github.com/butuzov/mirror v1.3.0 // indirect
github.com/catenacyber/perfsprint v0.8.2 // indirect
github.com/catenacyber/perfsprint v0.9.1 // indirect
github.com/ccojocar/zxcvbn-go v1.0.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charithe/durationcheck v0.0.10 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/lipgloss v1.1.0 // indirect
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/chavacava/garif v0.1.0 // indirect
github.com/ckaznocha/intrange v0.3.0 // indirect
github.com/ckaznocha/intrange v0.3.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/curioswitch/go-reassign v0.3.0 // indirect
github.com/daixiang0/gci v0.13.5 // indirect
github.com/daixiang0/gci v0.13.6 // indirect
github.com/dave/dst v0.27.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/denis-tingaikin/go-header v0.5.0 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // indirect
github.com/ebitengine/purego v0.8.4 // indirect
github.com/ettle/strcase v0.2.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fatih/structtag v1.2.0 // indirect
github.com/firefart/nonamedreturns v1.0.5 // indirect
github.com/firefart/nonamedreturns v1.0.6 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/fzipp/gocyclo v0.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/ghostiam/protogetter v0.3.9 // indirect
github.com/go-critic/go-critic v0.12.0 // indirect
github.com/ghostiam/protogetter v0.3.15 // indirect
github.com/go-critic/go-critic v0.13.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-jose/go-jose/v4 v4.1.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
@@ -101,16 +109,17 @@ require (
github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/golang/geo v0.0.0-20250602202553-92e1c6cfbc31 // indirect
github.com/golang/geo v0.0.0-20250613135800-9e8e59d779cc // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 // indirect
github.com/golangci/go-printf-func-name v0.1.0 // indirect
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d // indirect
github.com/golangci/golangci-lint v1.64.8 // indirect
github.com/golangci/golangci-lint/v2 v2.1.6 // indirect
github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95 // indirect
github.com/golangci/misspell v0.6.0 // indirect
github.com/golangci/plugin-module-register v0.1.1 // indirect
github.com/golangci/revgrep v0.8.0 // indirect
github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e // indirect
github.com/gordonklaus/ineffassign v0.1.0 // indirect
github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
github.com/gostaticanalysis/comment v1.5.0 // indirect
@@ -122,28 +131,30 @@ require (
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jgautheron/goconst v1.7.1 // indirect
github.com/jgautheron/goconst v1.8.1 // indirect
github.com/jingyugao/rowserrcheck v1.1.1 // indirect
github.com/jjti/go-spancheck v0.6.4 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/julz/importas v0.2.0 // indirect
github.com/jupiterrider/ffi v0.4.1 // indirect
github.com/jupiterrider/ffi v0.5.0 // indirect
github.com/karamaru-alpha/copyloopvar v1.2.1 // indirect
github.com/kisielk/errcheck v1.9.0 // indirect
github.com/kkHAIKE/contextcheck v1.1.6 // indirect
github.com/kulti/thelper v0.6.3 // indirect
github.com/kunwardeep/paralleltest v1.0.10 // indirect
github.com/kunwardeep/paralleltest v1.0.14 // indirect
github.com/lasiar/canonicalheader v1.1.2 // indirect
github.com/ldez/exptostd v0.4.2 // indirect
github.com/ldez/exptostd v0.4.3 // indirect
github.com/ldez/gomoddirectives v0.6.1 // indirect
github.com/ldez/grignotin v0.9.0 // indirect
github.com/ldez/tagliatelle v0.7.1 // indirect
github.com/ldez/usetesting v0.4.2 // indirect
github.com/ldez/usetesting v0.4.3 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/leonklingele/grouper v1.1.2 // indirect
github.com/macabu/inamedparam v0.1.3 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/macabu/inamedparam v0.2.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/manuelarte/funcorder v0.2.1 // indirect
github.com/maratori/testableexamples v1.0.0 // indirect
github.com/maratori/testpackage v1.1.1 // indirect
github.com/matoous/godox v1.1.0 // indirect
@@ -151,25 +162,26 @@ require (
github.com/mattn/go-isatty v0.0.20 // 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.7.0 // indirect
github.com/mgechev/revive v1.9.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moricho/tparallel v0.3.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/nakabonne/nestif v0.3.1 // indirect
github.com/nishanths/exhaustive v0.12.0 // indirect
github.com/nishanths/predeclared v0.2.2 // indirect
github.com/nunnatsa/ginkgolinter v0.19.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/polyfloyd/go-errorlint v1.7.1 // indirect
github.com/polyfloyd/go-errorlint v1.8.0 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/client_golang v1.12.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 // indirect
github.com/quasilyte/go-ruleguard v0.4.4 // indirect
github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect
github.com/quasilyte/gogrep v0.5.0 // indirect
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect
@@ -178,16 +190,15 @@ require (
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/ryancurrah/gomodguard v1.3.5 // indirect
github.com/ryancurrah/gomodguard v1.4.1 // indirect
github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect
github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
github.com/sashamelentyev/usestdlibvars v1.28.0 // indirect
github.com/securego/gosec/v2 v2.22.2 // indirect
github.com/securego/gosec/v2 v2.22.3 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sivchari/containedctx v1.0.3 // indirect
github.com/sivchari/tenv v1.12.1 // indirect
github.com/sonatard/noctx v0.1.0 // indirect
github.com/sourcegraph/go-diff v0.7.0 // indirect
github.com/spf13/cast v1.5.0 // indirect
@@ -201,45 +212,47 @@ require (
github.com/subosito/gotenv v1.4.1 // indirect
github.com/swaggo/files v1.0.1 // indirect
github.com/tdakkota/asciicheck v0.4.1 // indirect
github.com/tetafro/godot v1.5.0 // indirect
github.com/timakin/bodyclose v0.0.0-20241017074812-ed6a65f985e3 // indirect
github.com/timonwong/loggercheck v0.10.1 // indirect
github.com/tomarrell/wrapcheck/v2 v2.10.0 // indirect
github.com/tetafro/godot v1.5.1 // indirect
github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 // indirect
github.com/timonwong/loggercheck v0.11.0 // indirect
github.com/tomarrell/wrapcheck/v2 v2.11.0 // indirect
github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
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/iface v1.3.1 // indirect
github.com/xen0n/gosmopolitan v1.2.2 // indirect
github.com/xen0n/gosmopolitan v1.3.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yagipy/maintidx v1.0.0 // indirect
github.com/yeya24/promlinter v0.3.0 // indirect
github.com/ykadowak/zerologlint v0.1.5 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
gitlab.com/bosi/decorder v0.4.2 // indirect
go-simpler.org/musttag v0.13.0 // indirect
go-simpler.org/sloglint v0.9.0 // indirect
go.etcd.io/bbolt v1.4.0 // indirect
go-simpler.org/musttag v0.13.1 // indirect
go-simpler.org/sloglint v0.11.0 // indirect
go.augendre.info/fatcontext v0.8.0 // indirect
go.etcd.io/bbolt v1.4.1 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/net v0.40.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/text v0.25.0 // indirect
golang.org/x/tools v0.33.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
golang.org/x/mod v0.25.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/text v0.26.0 // indirect
golang.org/x/tools v0.34.0 // indirect
google.golang.org/protobuf v1.36.6 // 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
mvdan.cc/gofumpt v0.7.0 // indirect
mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f // indirect
mvdan.cc/gofumpt v0.8.0 // indirect
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
tool (
github.com/golangci/golangci-lint/cmd/golangci-lint
github.com/golangci/golangci-lint/v2/cmd/golangci-lint
github.com/swaggo/swag/cmd/swag
)
+138 -112
View File
@@ -39,18 +39,16 @@ github.com/4meepo/tagalign v1.4.2 h1:0hcLHPGMjDyM1gHG58cS73aQF8J4TdVR96TZViorO9E
github.com/4meepo/tagalign v1.4.2/go.mod h1:+p4aMyFM+ra7nb41CnFG6aSDXqRxU/w1VQqScKqDARI=
github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE=
github.com/Abirdcfly/dupword v0.1.3/go.mod h1:8VbB2t7e10KRNdwTVoxdBaxla6avbhGzb8sCTygUMhw=
github.com/Antonboom/errname v1.0.0 h1:oJOOWR07vS1kRusl6YRSlat7HFnb3mSfMl6sDMRoTBA=
github.com/Antonboom/errname v1.0.0/go.mod h1:gMOBFzK/vrTiXN9Oh+HFs+e6Ndl0eTFbtsRTSRdXyGI=
github.com/Antonboom/nilnil v1.0.1 h1:C3Tkm0KUxgfO4Duk3PM+ztPncTFlOf0b2qadmS0s4xs=
github.com/Antonboom/nilnil v1.0.1/go.mod h1:CH7pW2JsRNFgEh8B2UaPZTEPhCMuFowP/e8Udp9Nnb0=
github.com/Antonboom/testifylint v1.5.2 h1:4s3Xhuv5AvdIgbd8wOOEeo0uZG7PbDKQyKY5lGoQazk=
github.com/Antonboom/testifylint v1.5.2/go.mod h1:vxy8VJ0bc6NavlYqjZfmp6EfqXMtBgQ4+mhCojwC1P8=
github.com/Antonboom/errname v1.1.0 h1:A+ucvdpMwlo/myWrkHEUEBWc/xuXdud23S8tmTb/oAE=
github.com/Antonboom/errname v1.1.0/go.mod h1:O1NMrzgUcVBGIfi3xlVuvX8Q/VP/73sseCaAppfjqZw=
github.com/Antonboom/nilnil v1.1.0 h1:jGxJxjgYS3VUUtOTNk8Z1icwT5ESpLH/426fjmQG+ng=
github.com/Antonboom/nilnil v1.1.0/go.mod h1:b7sAlogQjFa1wV8jUW3o4PMzDVFLbTux+xnQdvzdcIE=
github.com/Antonboom/testifylint v1.6.1 h1:6ZSytkFWatT8mwZlmRCHkWz1gPi+q6UBSbieji2Gj/o=
github.com/Antonboom/testifylint v1.6.1/go.mod h1:k+nEkathI2NFjKO6HvwmSrbzUcQ6FAnbZV+ZRrnXPLI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Crocmagnon/fatcontext v0.7.1 h1:SC/VIbRRZQeQWj/TcQBS6JmrXcfA+BU4OGSVUt54PjM=
github.com/Crocmagnon/fatcontext v0.7.1/go.mod h1:1wMvv3NXEBJucFGfwOJBxSVWcoIO6emV215SMkW9MFU=
github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM=
@@ -59,14 +57,16 @@ github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 h1:Sz1JIXEcSfhz7fUi7xHnh
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1/go.mod h1:n/LSCXNuIYqVfBlVXyHfMQkZDdp1/mmxfSjADd3z1Zg=
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.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
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/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.17.2 h1:Rm81SCZ2mPoH+Q8ZCc/9YvzPUN/E7HgPiPJD8SLV6GI=
github.com/alecthomas/chroma/v2 v2.17.2/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
github.com/alecthomas/go-check-sumtype v0.3.1 h1:u9aUvbGINJxLVXiFvHUlPEaD7VDULsrxJb4Aq31NLkU=
github.com/alecthomas/go-check-sumtype v0.3.1/go.mod h1:A8TSiN3UPRw3laIgWEUOHHLPa6/r9MtoigdlP5h3K/E=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
@@ -76,20 +76,22 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alexkohler/nakedret/v2 v2.0.5 h1:fP5qLgtwbx9EJE8dGEERT02YwS8En4r9nnZ71RK+EVU=
github.com/alexkohler/nakedret/v2 v2.0.5/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU=
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.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw=
github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE=
github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw=
github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I=
github.com/alingse/nilnesserr v0.1.2 h1:Yf8Iwm3z2hUUrP4muWfW83DF4nE3r1xZ26fGWUKCZlo=
github.com/alingse/nilnesserr v0.1.2/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg=
github.com/alingse/nilnesserr v0.2.0 h1:raLem5KG7EFVb4UIDAXgrv3N2JIaffeKNtcEXkEWd/w=
github.com/alingse/nilnesserr v0.2.0/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg=
github.com/asdine/storm/v3 v3.2.1 h1:I5AqhkPK6nBZ/qJXySdI7ot5BlXSZ7qvDY1zAn5ZJac=
github.com/asdine/storm/v3 v3.2.1/go.mod h1:LEpXwGt4pIqrE/XcTvCnZHT5MgZCV6Ub9q7yQzOFWr0=
github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY=
github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU=
github.com/ashanbrown/makezero v1.2.0 h1:/2Lp1bypdmK9wDIq7uWBlDF1iMUpIIS4A+pF6C9IEUU=
github.com/ashanbrown/makezero v1.2.0/go.mod h1:dxlPhHbDMC6N6xICzFBSK+4njQDdK8euNO0qjQMtGY4=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@@ -100,21 +102,21 @@ github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5
github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo=
github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M=
github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k=
github.com/bombsimon/wsl/v4 v4.5.0 h1:iZRsEvDdyhd2La0FVi5k6tYehpOR/R7qIUjmKk7N74A=
github.com/bombsimon/wsl/v4 v4.5.0/go.mod h1:NOQ3aLF4nD7N5YPXMruR6ZXDOAqLoM0GEpLwTdvmOSc=
github.com/bombsimon/wsl/v4 v4.7.0 h1:1Ilm9JBPRczjyUs6hvOPKvd7VL1Q++PL8M0SXBDf+jQ=
github.com/bombsimon/wsl/v4 v4.7.0/go.mod h1:uV/+6BkffuzSAVYD+yGyld1AChO7/EuLrCF/8xTiapg=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.2 h1:79yrbttoZrLGkL/oOI8hBrUKucwOL0oOjUgEguGMcJ4=
github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/breml/bidichk v0.3.2 h1:xV4flJ9V5xWTqxL+/PMFF6dtJPvZLPsyixAoPe8BGJs=
github.com/breml/bidichk v0.3.2/go.mod h1:VzFLBxuYtT23z5+iVkamXO386OB+/sVwZOpIj6zXGos=
github.com/breml/errchkjson v0.4.0 h1:gftf6uWZMtIa/Is3XJgibewBm2ksAQSY/kABDNFTAdk=
github.com/breml/errchkjson v0.4.0/go.mod h1:AuBOSTHyLSaaAFlWsRSuRBIroCh3eh7ZHh5YeelDIk8=
github.com/butuzov/ireturn v0.3.1 h1:mFgbEI6m+9W8oP/oDdfA34dLisRFCj2G6o/yiI1yZrY=
github.com/butuzov/ireturn v0.3.1/go.mod h1:ZfRp+E7eJLC0NQmk1Nrm1LOrn/gQlOykv+cVPdiXH5M=
github.com/breml/bidichk v0.3.3 h1:WSM67ztRusf1sMoqH6/c4OBCUlRVTKq+CbSeo0R17sE=
github.com/breml/bidichk v0.3.3/go.mod h1:ISbsut8OnjB367j5NseXEGGgO/th206dVa427kR8YTE=
github.com/breml/errchkjson v0.4.1 h1:keFSS8D7A2T0haP9kzZTi7o26r7kE3vymjZNeNDRDwg=
github.com/breml/errchkjson v0.4.1/go.mod h1:a23OvR6Qvcl7DG/Z4o0el6BRAjKnaReoPQFciAl9U3s=
github.com/butuzov/ireturn v0.4.0 h1:+s76bF/PfeKEdbG8b54aCocxXmi0wvYdOVsWxVO7n8E=
github.com/butuzov/ireturn v0.4.0/go.mod h1:ghI0FrCmap8pDWZwfPisFD1vEc56VKH4NpQUxDHta70=
github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc=
github.com/butuzov/mirror v1.3.0/go.mod h1:AEij0Z8YMALaq4yQj9CPPVYOyJQyiexpQEQgihajRfI=
github.com/catenacyber/perfsprint v0.8.2 h1:+o9zVmCSVa7M4MvabsWvESEhpsMkhfE7k0sHNGL95yw=
github.com/catenacyber/perfsprint v0.8.2/go.mod h1:q//VWC2fWbcdSLEY1R3l8n0zQCDPdE4IjZwyY1HMunM=
github.com/catenacyber/perfsprint v0.9.1 h1:5LlTp4RwTooQjJCvGEFV6XksZvWE7wCOUvjD2z0vls0=
github.com/catenacyber/perfsprint v0.9.1/go.mod h1:q//VWC2fWbcdSLEY1R3l8n0zQCDPdE4IjZwyY1HMunM=
github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg=
github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -124,13 +126,23 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4=
github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc=
github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/ckaznocha/intrange v0.3.0 h1:VqnxtK32pxgkhJgYQEeOArVidIPg+ahLP7WBOXZd5ZY=
github.com/ckaznocha/intrange v0.3.0/go.mod h1:+I/o2d2A1FBHgGELbGxzIcyd3/9l9DuwjM8FsbSS3Lo=
github.com/ckaznocha/intrange v0.3.1 h1:j1onQyXvHUsPWujDH6WIjhyH26gkRt/txNlV7LspvJs=
github.com/ckaznocha/intrange v0.3.1/go.mod h1:QVepyz1AkUoFQkpEqksSYpNpUo3c5W7nWh/s6SHIJJk=
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.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk=
@@ -140,15 +152,19 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs=
github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88=
github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c=
github.com/daixiang0/gci v0.13.5/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk=
github.com/daixiang0/gci v0.13.6 h1:RKuEOSkGpSadkGbvZ6hJ4ddItT3cVZ9Vn9Rybk6xjl8=
github.com/daixiang0/gci v0.13.6/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk=
github.com/dave/dst v0.27.3 h1:P1HPoMza3cMEquVf9kKy8yXsFirry4zEnWOdYPOoIzY=
github.com/dave/dst v0.27.3/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc=
github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo=
github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8=
github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
github.com/dsoprea/go-exif/v3 v3.0.0-20200717053412-08f1b6708903/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8=
github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk=
@@ -178,8 +194,8 @@ 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/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.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA=
github.com/firefart/nonamedreturns v1.0.5/go.mod h1:gHJjDqhGM4WyPt639SOZs+G89Ko7QKH5R5BhnO6xJhw=
github.com/firefart/nonamedreturns v1.0.6 h1:vmiBcKV/3EqKY3ZiPxCINmpS431OcE1S47AQUwhrg8E=
github.com/firefart/nonamedreturns v1.0.6/go.mod h1:R8NisJnSIpvPWheCq0mNRXJok6D8h7fagJTF8EMEwCo=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
@@ -190,10 +206,10 @@ github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBv
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
github.com/gen2brain/go-fitz v1.24.14 h1:09weRkjVtLYNGo7l0J7DyOwBExbwi8SJ9h8YPhw9WEo=
github.com/gen2brain/go-fitz v1.24.14/go.mod h1:0KaZeQgASc20Yp5R/pFzyy7SmP01XcoHKNF842U2/S4=
github.com/ghostiam/protogetter v0.3.9 h1:j+zlLLWzqLay22Cz/aYwTHKQ88GE2DQ6GkWSYFOI4lQ=
github.com/ghostiam/protogetter v0.3.9/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA=
github.com/go-critic/go-critic v0.12.0 h1:iLosHZuye812wnkEz1Xu3aBwn5ocCPfc9yqmFG9pa6w=
github.com/go-critic/go-critic v0.12.0/go.mod h1:DpE0P6OVc6JzVYzmM5gq5jMU31zLr4am5mB/VfFK64w=
github.com/ghostiam/protogetter v0.3.15 h1:1KF5sXel0HE48zh1/vn0Loiw25A9ApyseLzQuif1mLY=
github.com/ghostiam/protogetter v0.3.15/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA=
github.com/go-critic/go-critic v0.13.0 h1:kJzM7wzltQasSUXtYyTl6UaPVySO6GkaR1thFnJ6afY=
github.com/go-critic/go-critic v0.13.0/go.mod h1:M/YeuJ3vOCQDnP2SU+ZhjgRzwzcBW87JqLpMJLrZDLI=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
@@ -272,10 +288,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-20250516193853-92f93c4cb289 h1:HeOFbnyPys/vx/t+d4fwZM782mnjRVtbjxVkDittTUs=
github.com/golang/geo v0.0.0-20250516193853-92f93c4cb289/go.mod h1:Vaw7L5b+xa3Rj4/pRtrQkymn3lSBRB/NAEdbF9YEVLA=
github.com/golang/geo v0.0.0-20250602202553-92e1c6cfbc31 h1:E1wdqK72/SVuxkxQ2DSE7YZEmcUZvExysw58zmkLKik=
github.com/golang/geo v0.0.0-20250602202553-92e1c6cfbc31/go.mod h1:Vaw7L5b+xa3Rj4/pRtrQkymn3lSBRB/NAEdbF9YEVLA=
github.com/golang/geo v0.0.0-20250613135800-9e8e59d779cc h1:HeTandKJybEWXv6wW9o3ybXTNqGGV4Q6kW3S+8RsXbk=
github.com/golang/geo v0.0.0-20250613135800-9e8e59d779cc/go.mod h1:Vaw7L5b+xa3Rj4/pRtrQkymn3lSBRB/NAEdbF9YEVLA=
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=
@@ -314,15 +328,18 @@ github.com/golangci/go-printf-func-name v0.1.0/go.mod h1:wqhWFH5mUdJQhweRnldEywn
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 v1.64.8 h1:y5TdeVidMtBGG32zgSC7ZXTFNHrsJkDnpO4ItB3Am+I=
github.com/golangci/golangci-lint v1.64.8/go.mod h1:5cEsUQBSr6zi8XI8OjmcY2Xmliqc4iYL7YoPrL+zLJ4=
github.com/golangci/golangci-lint/v2 v2.1.6 h1:LXqShFfAGM5BDzEOWD2SL1IzJAgUOqES/HRBsfKjI+w=
github.com/golangci/golangci-lint/v2 v2.1.6/go.mod h1:EPj+fgv4TeeBq3TcqaKZb3vkiV5dP4hHHKhXhEhzci8=
github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95 h1:AkK+w9FZBXlU/xUmBtSJN1+tAI4FIvy5WtnUnY8e4p8=
github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95/go.mod h1:k9mmcyWKSTMcPPvQUCfRWWQ9VHJ1U9Dc0R7kaXAgtnQ=
github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs=
github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c=
github.com/golangci/plugin-module-register v0.1.1/go.mod h1:TTpqoB6KkwOJMV8u7+NyXMrkwwESJLOkfl9TxR1DGFc=
github.com/golangci/revgrep v0.8.0 h1:EZBctwbVd0aMeRnNUsFogoyayvKHyxlV3CdUA46FX2s=
github.com/golangci/revgrep v0.8.0/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k=
github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed h1:IURFTjxeTfNFP0hTEi1YKjB/ub8zkpaOqFFMApi2EAs=
github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed/go.mod h1:XLXN8bNw4CGRPaqgl3bv/lhz7bsGPh4/xSaMTbo2vkQ=
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e h1:gD6P7NEo7Eqtt0ssnqSJNNndxe69DOQ24A5h7+i3KpM=
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e/go.mod h1:h+wZwLjUTJnm/P2rwlbJdRPZXOzaT36/FwnPnY2inzc=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -393,8 +410,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk=
github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
github.com/jgautheron/goconst v1.8.1 h1:PPqCYp3K/xlOj5JmIe6O1Mj6r1DbkdbLtR3AJuZo414=
github.com/jgautheron/goconst v1.8.1/go.mod h1:A0oxgBCHy55NQn6sYpO7UdnA9p+h7cPtoOZUmvNIako=
github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs=
github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c=
github.com/jjti/go-spancheck v0.6.4 h1:Tl7gQpYf4/TMU7AT84MN83/6PutY21Nb9fuQjFTpRRc=
@@ -412,8 +429,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ=
github.com/julz/importas v0.2.0/go.mod h1:pThlt589EnCYtMnmhmRYY/qn9lCf/frPOK+WMx3xiJY=
github.com/jupiterrider/ffi v0.4.1 h1:Obg+xpuA+rRkRTEN7xu3IagjNDMO8fRUhOPpLEztPYI=
github.com/jupiterrider/ffi v0.4.1/go.mod h1:Ba3hjU7Sz2+TO8HupjZwGssVnzo7RG3xF77byUBueB4=
github.com/jupiterrider/ffi v0.5.0 h1:j2nSgpabbV1JOwgP4Kn449sJUHq3cVLAZVBoOYn44V8=
github.com/jupiterrider/ffi v0.5.0/go.mod h1:x7xdNKo8h0AmLuXfswDUBxUsd2OqUP4ekC8sCnsmbvo=
github.com/karamaru-alpha/copyloopvar v1.2.1 h1:wmZaZYIjnJ0b5UoKDjUHrikcV0zuPyyxI4SVplLd2CI=
github.com/karamaru-alpha/copyloopvar v1.2.1/go.mod h1:nFmMlFNlClC2BPvNaHMdkirmTJxVCY0lhxBtlfOypMM=
github.com/kisielk/errcheck v1.9.0 h1:9xt1zI9EBfcYBvdU1nVrzMzzUPUtPKs9bVSIM3TAb3M=
@@ -435,30 +452,34 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs=
github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I=
github.com/kunwardeep/paralleltest v1.0.10 h1:wrodoaKYzS2mdNVnc4/w31YaXFtsc21PCTdvWJ/lDDs=
github.com/kunwardeep/paralleltest v1.0.10/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY=
github.com/kunwardeep/paralleltest v1.0.14 h1:wAkMoMeGX/kGfhQBPODT/BL8XhK23ol/nuQ3SwFaUw8=
github.com/kunwardeep/paralleltest v1.0.14/go.mod h1:di4moFqtfz3ToSKxhNjhOZL+696QtJGCFe132CbBLGk=
github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4=
github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI=
github.com/ldez/exptostd v0.4.2 h1:l5pOzHBz8mFOlbcifTxzfyYbgEmoUqjxLFHZkjlbHXs=
github.com/ldez/exptostd v0.4.2/go.mod h1:iZBRYaUmcW5jwCR3KROEZ1KivQQp6PHXbDPk9hqJKCQ=
github.com/ldez/exptostd v0.4.3 h1:Ag1aGiq2epGePuRJhez2mzOpZ8sI9Gimcb4Sb3+pk9Y=
github.com/ldez/exptostd v0.4.3/go.mod h1:iZBRYaUmcW5jwCR3KROEZ1KivQQp6PHXbDPk9hqJKCQ=
github.com/ldez/gomoddirectives v0.6.1 h1:Z+PxGAY+217f/bSGjNZr/b2KTXcyYLgiWI6geMBN2Qc=
github.com/ldez/gomoddirectives v0.6.1/go.mod h1:cVBiu3AHR9V31em9u2kwfMKD43ayN5/XDgr+cdaFaKs=
github.com/ldez/grignotin v0.9.0 h1:MgOEmjZIVNn6p5wPaGp/0OKWyvq42KnzAt/DAb8O4Ow=
github.com/ldez/grignotin v0.9.0/go.mod h1:uaVTr0SoZ1KBii33c47O1M8Jp3OP3YDwhZCmzT9GHEk=
github.com/ldez/tagliatelle v0.7.1 h1:bTgKjjc2sQcsgPiT902+aadvMjCeMHrY7ly2XKFORIk=
github.com/ldez/tagliatelle v0.7.1/go.mod h1:3zjxUpsNB2aEZScWiZTHrAXOl1x25t3cRmzfK1mlo2I=
github.com/ldez/usetesting v0.4.2 h1:J2WwbrFGk3wx4cZwSMiCQQ00kjGR0+tuuyW0Lqm4lwA=
github.com/ldez/usetesting v0.4.2/go.mod h1:eEs46T3PpQ+9RgN9VjpY6qWdiw2/QmfiDeWmdZdrjIQ=
github.com/ldez/usetesting v0.4.3 h1:pJpN0x3fMupdTf/IapYjnkhiY1nSTN+pox1/GyBRw3k=
github.com/ldez/usetesting v0.4.3/go.mod h1:eEs46T3PpQ+9RgN9VjpY6qWdiw2/QmfiDeWmdZdrjIQ=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY=
github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA=
github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk=
github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/macabu/inamedparam v0.2.0 h1:VyPYpOc10nkhI2qeNUdh3Zket4fcZjEWe35poddBCpE=
github.com/macabu/inamedparam v0.2.0/go.mod h1:+Pee9/YfGe5LJ62pYXqB89lJ+0k5bsR8Wgz/C0Zlq3U=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/manuelarte/funcorder v0.2.1 h1:7QJsw3qhljoZ5rH0xapIvjw31EcQeFbF31/7kQ/xS34=
github.com/manuelarte/funcorder v0.2.1/go.mod h1:BQQ0yW57+PF9ZpjpeJDKOffEsQbxDFKW8F8zSMe/Zd0=
github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI=
github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE=
github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04=
@@ -476,8 +497,8 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
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.7.0 h1:JyeQ4yO5K8aZhIKf5rec56u0376h8AlKNQEmjfkjKlY=
github.com/mgechev/revive v1.7.0/go.mod h1:qZnwcNhoguE58dfi96IJeSTPeZQejNeoMQLUZGi4SW4=
github.com/mgechev/revive v1.9.0 h1:8LaA62XIKrb8lM6VsBSQ92slt/o92z5+hTw3CmrvSrM=
github.com/mgechev/revive v1.9.0/go.mod h1:LAPq3+MgOf7GcL5PlWIkHb0PT7XH4NuC2LdWymhb9Mo=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@@ -489,6 +510,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI=
github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
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=
@@ -501,10 +524,10 @@ github.com/nunnatsa/ginkgolinter v0.19.1 h1:mjwbOlDQxZi9Cal+KfbEJTCz327OLNfwNvoZ
github.com/nunnatsa/ginkgolinter v0.19.1/go.mod h1:jkQ3naZDmxaZMXPWaS9rblH+i+GWXQCaS/JFIWcOH2s=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0=
github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
@@ -514,16 +537,16 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/polyfloyd/go-errorlint v1.7.1 h1:RyLVXIbosq1gBdk/pChWA8zWYLsq9UEw7a1L5TVMCnA=
github.com/polyfloyd/go-errorlint v1.7.1/go.mod h1:aXjNb1x2TNhoLsk26iv1yl7a+zTnXPhwEMtEXukiLR8=
github.com/polyfloyd/go-errorlint v1.8.0 h1:DL4RestQqRLr8U4LygLw8g2DX6RN1eBJOpa2mzsrl1Q=
github.com/polyfloyd/go-errorlint v1.8.0/go.mod h1:G2W0Q5roxbLCt0ZQbdoxQxXktTjwNyDbEaj3n7jvl4s=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
@@ -552,8 +575,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 h1:+Wl/0aFp0hpuHM3H//KMft64WQ1yX9LdJY64Qm/gFCo=
github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI=
github.com/quasilyte/go-ruleguard v0.4.4 h1:53DncefIeLX3qEpjzlS1lyUmQoUEeOWPFWqaTJq9eAQ=
github.com/quasilyte/go-ruleguard v0.4.4/go.mod h1:Vl05zJ538vcEEwu16V/Hdu7IYZWyKSwIy4c88Ro1kRE=
github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE=
github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo=
@@ -573,8 +596,8 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryancurrah/gomodguard v1.3.5 h1:cShyguSwUEeC0jS7ylOiG/idnd1TpJ1LfHGpV3oJmPU=
github.com/ryancurrah/gomodguard v1.3.5/go.mod h1:MXlEPQRxgfPQa62O8wzK3Ozbkv9Rkqr+wKjSxTdsNJE=
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/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0=
@@ -585,8 +608,10 @@ 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.28.0 h1:jZnudE2zKCtYlGzLVreNp5pmCdOxXUzwsMDBkR21cyQ=
github.com/sashamelentyev/usestdlibvars v1.28.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8=
github.com/securego/gosec/v2 v2.22.2 h1:IXbuI7cJninj0nRpZSLCUlotsj8jGusohfONMrHoF6g=
github.com/securego/gosec/v2 v2.22.2/go.mod h1:UEBGA+dSKb+VqM6TdehR7lnQtIIMorYJ4/9CW1KVQBE=
github.com/securego/gosec/v2 v2.22.3 h1:mRrCNmRF2NgZp4RJ8oJ6yPJ7G4x6OCiAXHd8x4trLRc=
github.com/securego/gosec/v2 v2.22.3/go.mod h1:42M9Xs0v1WseinaB/BmNGO8AVqG8vRfhC2686ACY48k=
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/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
@@ -599,8 +624,6 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
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/sivchari/tenv v1.12.1 h1:+E0QzjktdnExv/wwsnnyk4oqZBUfuh89YMQT1cyuvSY=
github.com/sivchari/tenv v1.12.1/go.mod h1:1LjSOUCc25snIr5n3DtGGrENhX3LuWefcplwVGC24mw=
github.com/sonatard/noctx v0.1.0 h1:JjqOc2WN16ISWAjAk8M5ej0RfExEXtkEyExl2hLW+OM=
github.com/sonatard/noctx v0.1.0/go.mod h1:0RvBxqY8D4j9cTTTWE8ylt2vqj2EPI8fHmrxHdsaZ2c=
github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0=
@@ -652,14 +675,14 @@ github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA
github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0=
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag=
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY=
github.com/tetafro/godot v1.5.0 h1:aNwfVI4I3+gdxjMgYPus9eHmoBeJIbnajOyqZYStzuw=
github.com/tetafro/godot v1.5.0/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio=
github.com/timakin/bodyclose v0.0.0-20241017074812-ed6a65f985e3 h1:y4mJRFlM6fUyPhoXuFg/Yu02fg/nIPFMOY8tOqppoFg=
github.com/timakin/bodyclose v0.0.0-20241017074812-ed6a65f985e3/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460=
github.com/timonwong/loggercheck v0.10.1 h1:uVZYClxQFpw55eh+PIoqM7uAOHMrhVcDoWDery9R8Lg=
github.com/timonwong/loggercheck v0.10.1/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8=
github.com/tomarrell/wrapcheck/v2 v2.10.0 h1:SzRCryzy4IrAH7bVGG4cK40tNUhmVmMDuJujy4XwYDg=
github.com/tomarrell/wrapcheck/v2 v2.10.0/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo=
github.com/tetafro/godot v1.5.1 h1:PZnjCol4+FqaEzvZg5+O8IY2P3hfY9JzRBNPv1pEDS4=
github.com/tetafro/godot v1.5.1/go.mod h1:cCdPtEndkmqqrhiCfkmxDodMQJ/f3L1BCNskCUZdTwk=
github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 h1:9LPGD+jzxMlnk5r6+hJnar67cgpDIz/iyD+rfl5r2Vk=
github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460=
github.com/timonwong/loggercheck v0.11.0 h1:jdaMpYBl+Uq9mWPXv1r8jc5fC3gyXx4/WGwTnnNKn4M=
github.com/timonwong/loggercheck v0.11.0/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8=
github.com/tomarrell/wrapcheck/v2 v2.11.0 h1:BJSt36snX9+4WTIXeJ7nvHBQBcm1h2SjQMSlmQ6aFSU=
github.com/tomarrell/wrapcheck/v2 v2.11.0/go.mod h1:wFL9pDWDAbXhhPZZt+nG8Fu+h29TtnZ2MW6Lx4BRXIU=
github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw=
github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
github.com/ultraware/funlen v0.2.0 h1:gCHmCn+d2/1SemTdYMiKLAHFYxTYz7z9VIDRaTGyLkI=
@@ -674,8 +697,10 @@ github.com/uudashr/iface v1.3.1 h1:bA51vmVx1UIhiIsQFSNq6GZ6VPTk3WNMZgRiCe9R29U=
github.com/uudashr/iface v1.3.1/go.mod h1:4QvspiRd3JLPAEXBQ9AiZpLbJlrWWgRChOKDJEuQTdg=
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU=
github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg=
github.com/xen0n/gosmopolitan v1.3.0 h1:zAZI1zefvo7gcpbCOrPSHJZJYA9ZgLfJqtKzZ5pHqQM=
github.com/xen0n/gosmopolitan v1.3.0/go.mod h1:rckfr5T6o4lBtM1ga7mLGKZmLxswUoH1zxHgNXOsEt4=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM=
github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk=
github.com/yeya24/promlinter v0.3.0 h1:JVDbMp08lVCP7Y6NP3qHroGAO6z2yGKQtS5JsjqtoFs=
@@ -695,13 +720,15 @@ gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo=
gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8=
go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ=
go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28=
go-simpler.org/musttag v0.13.0 h1:Q/YAW0AHvaoaIbsPj3bvEI5/QFP7w696IMUpnKXQfCE=
go-simpler.org/musttag v0.13.0/go.mod h1:FTzIGeK6OkKlUDVpj0iQUXZLUO1Js9+mvykDQy9C5yM=
go-simpler.org/sloglint v0.9.0 h1:/40NQtjRx9txvsB/RN022KsUJU+zaaSb/9q9BSefSrE=
go-simpler.org/sloglint v0.9.0/go.mod h1:G/OrAF6uxj48sHahCzrbarVMptL2kjWTaUeC8+fOGww=
go-simpler.org/musttag v0.13.1 h1:lw2sJyu7S1X8lc8zWUAdH42y+afdcCnHhWpnkWvd6vU=
go-simpler.org/musttag v0.13.1/go.mod h1:8r450ehpMLQgvpb6sg+hV5Ur47eH6olp/3yEanfG97k=
go-simpler.org/sloglint v0.11.0 h1:JlR1X4jkbeaffiyjLtymeqmGDKBDO1ikC6rjiuFAOco=
go-simpler.org/sloglint v0.11.0/go.mod h1:CFDO8R1i77dlciGfPEPvYke2ZMx4eyGiEIWkyeW2Pvw=
go.augendre.info/fatcontext v0.8.0 h1:2dfk6CQbDGeu1YocF59Za5Pia7ULeAM6friJ3LP7lmk=
go.augendre.info/fatcontext v0.8.0/go.mod h1:oVJfMgwngMsHO+KB2MdgzcO+RvtNdiCEOlWvSFtax/s=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
go.etcd.io/bbolt v1.4.1 h1:5mOV+HWjIPLEAlUGMsveaUvK2+byZMFOzojoi7bh7uI=
go.etcd.io/bbolt v1.4.1/go.mod h1:c8zu2BnXWTu2XM4XcICtbGSl9cFwsXtcf9zLt2OncM8=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -726,8 +753,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.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
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=
@@ -746,9 +773,8 @@ golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac h1:TSSpLIG4v+p0rP
golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g=
golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE=
golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY=
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=
@@ -776,8 +802,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.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.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
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=
@@ -822,8 +848,8 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
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.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
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=
@@ -847,8 +873,8 @@ 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.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -929,8 +955,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
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.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
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=
@@ -990,8 +1016,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
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.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1073,8 +1099,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -1105,10 +1131,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.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI=
honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4=
mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU=
mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo=
mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f h1:lMpcwN6GxNbWtbpI1+xzFLSW8XzX0u72NttUGVFjO3U=
mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f/go.mod h1:RSLa7mKKCNeTTMHBw5Hsy2rfJmd6O2ivt9Dw9ZqCQpQ=
mvdan.cc/gofumpt v0.8.0 h1:nZUCeC2ViFaerTcYKstMmfysj6uhQrA2vJe+2vwGU6k=
mvdan.cc/gofumpt v0.8.0/go.mod h1:vEYnSzyGPmjvFkqJWtXkh79UwPWP9/HMxQdGEXZHjpg=
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 h1:WjUu4yQoT5BHT1w8Zu56SP8367OuBV5jvo+4Ulppyf8=
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4/go.mod h1:rthT7OuvRbaGcd5ginj6dA2oLE7YNlta9qhBNNdCaLE=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+11 -19
View File
@@ -125,32 +125,15 @@ func oidcCallbackHandler(w http.ResponseWriter, r *http.Request, d *requestConte
logger.Debugf("failed to verify ID token: %v. This might be expected, falling back to UserInfo endpoint.", err)
// Verification failed, claimsFromIDToken remains false
} else {
var claims map[string]interface{}
// Decode the ID token claims into a map to handle arbitrary structure
// This is where the JWE unmarshalling error occurs if the token is encrypted
if err := idToken.Claims(&claims); err != nil {
if err := idToken.Claims(&userdata); err != nil {
logger.Warningf("failed to decode ID token claims: %v. Falling back to UserInfo endpoint.", err)
// Claims decoding failed, claimsFromIDToken remains false
} else {
// Successfully verified and decoded ID token claims
logger.Debugf("ID Token verified and claims decoded: %+v", claims)
logger.Debugf("ID Token verified and claims decoded: %+v", userdata)
// Populate userdata from ID token claims
if name, ok := claims["name"].(string); ok {
userdata.Name = name
}
if preferredUsername, ok := claims["preferred_username"].(string); ok {
userdata.PreferredUsername = preferredUsername
}
if email, ok := claims["email"].(string); ok {
userdata.Email = email
}
if sub, ok := claims["sub"].(string); ok {
userdata.Sub = sub
}
if phone, ok := claims["phone_number"].(string); ok {
userdata.Phone = phone
}
// Decide if we rely on ID token claims or still need UserInfo
// Even if parsing succeeded, if essential claims are missing, use UserInfo
switch oidcCfg.UserIdentifier {
@@ -245,6 +228,15 @@ func loginWithOidcUser(w http.ResponseWriter, r *http.Request, username string,
} else {
return http.StatusForbidden, fmt.Errorf("user %s does not exist and createUser is disabled. Your admin needs to create your user before you can access this application", username)
}
} else {
// update user admin perms
if isAdmin != user.Permissions.Admin {
user.Permissions.Admin = isAdmin
err = store.Users.Update(user, true, "Permissions")
if err != nil {
logger.Warningf("failed to update oidc user %s admin permissions: %v", username, err)
}
}
}
if user.LoginMethod != users.LoginMethodOidc {
logger.Warningf("user %s is not allowed to login with oidc authentication, bypassing and updating login method", user.Username)
+8 -9
View File
@@ -44,12 +44,7 @@ func setContentDisposition(w http.ResponseWriter, r *http.Request, fileName stri
// @Failure 500 {object} map[string]string "Internal server error"
// @Router /api/raw [get]
func rawHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
encodedFiles := r.URL.Query().Get("files")
// Decode the URL-encoded path
files, err := url.QueryUnescape(encodedFiles)
if err != nil {
return http.StatusBadRequest, fmt.Errorf("invalid path encoding: %v", err)
}
files := r.URL.Query().Get("files")
fileList := strings.Split(files, "||")
return rawFilesHandler(w, r, d, fileList)
}
@@ -188,8 +183,9 @@ func rawFilesHandler(w http.ResponseWriter, r *http.Request, d *requestContext,
firstFileSource := splitFile[0]
firstFilePath := splitFile[1]
fileName := filepath.Base(firstFilePath)
// decode url encoded source name
var err error
fileName := filepath.Base(firstFilePath)
userscope := "/"
if d.user.Username != "publicUser" {
userscope, err = settings.GetScopeFromSourceName(d.user.Scopes, firstFileSource)
@@ -323,11 +319,11 @@ func computeArchiveSize(fileList []string, d *requestContext) (int64, error) {
}
source := splitFile[0]
path := splitFile[1]
var err error
idx := indexing.GetIndex(source)
if idx == nil {
return 0, fmt.Errorf("source %s is not available", source)
}
var err error
userScope := "/"
if d.user.Username != "publicUser" {
userScope, err = settings.GetScopeFromSourceName(d.user.Scopes, source)
@@ -342,7 +338,10 @@ func computeArchiveSize(fileList []string, d *requestContext) (int64, error) {
indexPath := idx.MakeIndexPath(realPath)
info, ok := idx.GetReducedMetadata(indexPath, isDir)
if !ok {
return 0, fmt.Errorf("failed to get metadata info for %s", path)
info, err = idx.GetFsDirInfo(indexPath)
if err != nil {
return 0, fmt.Errorf("failed to get file info for %s : %v", path, err)
}
}
estimatedSize += info.Size
}
-7
View File
@@ -41,13 +41,6 @@ func resourceGetHandler(w http.ResponseWriter, r *http.Request, d *requestContex
source := r.URL.Query().Get("source")
if source == "" {
source = config.Server.DefaultSource.Name
} else {
var err error
// decode url encoded source name
source, err = url.QueryUnescape(source)
if err != nil {
return http.StatusBadRequest, fmt.Errorf("invalid source encoding: %v", err)
}
}
// Decode the URL-encoded path
path, err := url.QueryUnescape(encodedPath)
+2 -8
View File
@@ -60,13 +60,6 @@ func searchHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (i
source := r.URL.Query().Get("source")
if source == "" {
source = config.Server.DefaultSource.Name
} else {
var err error
// decode url encoded source name
source, err = url.QueryUnescape(source)
if err != nil {
return http.StatusBadRequest, fmt.Errorf("invalid source encoding: %v", err)
}
}
scope := r.URL.Query().Get("scope")
unencodedScope, err := url.QueryUnescape(scope)
@@ -85,11 +78,12 @@ func searchHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (i
return http.StatusForbidden, err
}
combinedPath := index.MakeIndexPath(filepath.Join(userscope, searchScope))
combinedPath = strings.TrimSuffix(combinedPath, "/") + "/" // Ensure trailing slash
// Perform the search using the provided query and user scope
response := index.Search(query, combinedPath, sessionId)
for i := range response {
// Remove the user scope from the path
response[i].Path = strings.TrimPrefix(response[i].Path, userscope)
response[i].Path = strings.TrimPrefix(response[i].Path, combinedPath)
if response[i].Path == "" {
response[i].Path = "/"
}
-14
View File
@@ -69,13 +69,6 @@ func shareGetHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
source := r.URL.Query().Get("source")
if source == "" {
source = settings.Config.Server.DefaultSource.Name
} else {
var err error
// decode url encoded source name
source, err = url.QueryUnescape(source)
if err != nil {
return http.StatusBadRequest, fmt.Errorf("invalid source encoding: %v", err)
}
}
// Decode the URL-encoded path
path, err := url.QueryUnescape(encodedPath)
@@ -199,13 +192,6 @@ func sharePostHandler(w http.ResponseWriter, r *http.Request, d *requestContext)
sourceName := r.URL.Query().Get("source")
if sourceName == "" {
sourceName = config.Server.DefaultSource.Name
} else {
var err error
// decode url encoded source name
sourceName, err = url.QueryUnescape(sourceName)
if err != nil {
return http.StatusBadRequest, fmt.Errorf("invalid source encoding: %v", err)
}
}
source := config.Server.NameToSource[sourceName]
+2
View File
@@ -9,6 +9,7 @@ import (
"text/template"
"github.com/gtsteffaniak/filebrowser/backend/common/settings"
"github.com/gtsteffaniak/filebrowser/backend/common/utils"
"github.com/gtsteffaniak/filebrowser/backend/common/version"
)
@@ -59,6 +60,7 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, file, contentT
"PasswordAvailable": config.Auth.Methods.PasswordAuth.Enabled,
"MediaAvailable": config.Integrations.Media.FfmpegPath != "",
"MuPdfAvailable": config.Server.MuPdfAvailable,
"UpdateAvailable": utils.GetUpdateAvailableUrl(),
}
b, err := json.Marshal(data)
+137 -55
View File
@@ -10,6 +10,7 @@ import (
"sync"
"time"
"github.com/gtsteffaniak/filebrowser/backend/common/errors"
"github.com/gtsteffaniak/filebrowser/backend/common/settings"
"github.com/gtsteffaniak/filebrowser/backend/common/utils"
"github.com/gtsteffaniak/filebrowser/backend/indexing/iteminfo"
@@ -39,6 +40,7 @@ type Index struct {
CurrentSchedule int `json:"-"`
settings.Source `json:"-"`
Directories map[string]*iteminfo.FileInfo `json:"-"`
DirectoriesLedger map[string]bool `json:"-"`
runningScannerCount int `json:"-"`
SmartModifier time.Duration `json:"-"`
FilesChangedDuringIndexing bool `json:"-"`
@@ -66,34 +68,36 @@ func init() {
func Initialize(source settings.Source, mock bool) {
indexesMutex.Lock()
newIndex := Index{
mock: mock,
Source: source,
Directories: make(map[string]*iteminfo.FileInfo),
mock: mock,
Source: source,
Directories: make(map[string]*iteminfo.FileInfo),
DirectoriesLedger: make(map[string]bool),
}
newIndex.ReducedIndex = ReducedIndex{
Status: "indexing",
IdxName: source.Name,
Assessment: "unknown",
}
indexes[newIndex.Source.Name] = &newIndex
indexes[newIndex.Name] = &newIndex
indexesMutex.Unlock()
if !newIndex.Source.Config.Disabled {
if !newIndex.Config.DisableIndexing {
time.Sleep(time.Second)
logger.Infof("initializing index: [%v]", newIndex.Source.Name)
logger.Infof("initializing index: [%v]", newIndex.Name)
newIndex.RunIndexing("/", false)
go newIndex.setupIndexingScanners()
} else {
logger.Debug("indexing disabled for source: " + newIndex.Source.Name)
newIndex.Status = "ready"
logger.Debug("indexing disabled for source: " + newIndex.Name)
}
}
// Define a function to recursively index files and directories
func (idx *Index) indexDirectory(adjustedPath string, quick, recursive bool) error {
realPath := strings.TrimRight(idx.Source.Path, "/") + adjustedPath
realPath := strings.TrimRight(idx.Path, "/") + adjustedPath
// Open the directory
dir, err := os.Open(realPath)
if err != nil {
idx.RemoveDirectory(adjustedPath) // Remove, must have been deleted
// must have been deleted
return err
}
defer dir.Close()
@@ -102,6 +106,21 @@ func (idx *Index) indexDirectory(adjustedPath string, quick, recursive bool) err
if err != nil {
return err
}
// check if excluded from indexing
hidden := isHidden(dirInfo, idx.Path+adjustedPath)
if idx.shouldSkip(dirInfo.IsDir(), hidden, adjustedPath) {
logger.Debugf("skipping directory %s due to exclusion rules", adjustedPath)
return errors.ErrNotIndexed
}
// if indexing, mark the directory as valid and indexed.
if recursive {
// Prevent race conditions if scanning becomes concurrent in the future.
idx.mu.Lock()
idx.DirectoriesLedger[adjustedPath] = true
idx.mu.Unlock()
}
combinedPath := adjustedPath + "/"
if adjustedPath == "/" {
combinedPath = "/"
@@ -109,9 +128,9 @@ func (idx *Index) indexDirectory(adjustedPath string, quick, recursive bool) err
// get whats currently in cache
idx.mu.RLock()
cacheDirItems := []iteminfo.ItemInfo{}
modChange := true // default to true
modChange := false
cachedDir, exists := idx.Directories[adjustedPath]
if exists && quick {
if exists {
modChange = dirInfo.ModTime() != cachedDir.ModTime
cacheDirItems = cachedDir.Folders
}
@@ -119,48 +138,107 @@ func (idx *Index) indexDirectory(adjustedPath string, quick, recursive bool) err
// If the directory has not been modified since the last index, skip expensive readdir
// recursively check cached dirs for mod time changes as well
if !modChange && recursive {
for _, item := range cacheDirItems {
err = idx.indexDirectory(combinedPath+item.Name, quick, true)
if err != nil {
logger.Errorf("error indexing directory %v : %v", combinedPath+item.Name, err)
if recursive {
if modChange {
idx.mu.Lock()
idx.FilesChangedDuringIndexing = true
idx.mu.Unlock()
} else if quick {
for _, item := range cacheDirItems {
err = idx.indexDirectory(combinedPath+item.Name, quick, true)
if err != nil && err != errors.ErrNotIndexed {
logger.Errorf("error indexing directory %v : %v", combinedPath+item.Name, err)
}
}
return nil
}
}
dirFileInfo, err2 := idx.GetDirInfo(dir, dirInfo, realPath, adjustedPath, combinedPath, quick, recursive)
if err2 != nil {
return err2
}
// Update the current directory metadata in the index
idx.UpdateMetadata(dirFileInfo)
return nil
}
func (idx *Index) GetFsDirInfo(adjustedPath string) (*iteminfo.FileInfo, error) {
realPath, isDir, err := idx.GetRealPath(adjustedPath)
if err != nil {
return nil, err
}
originalPath := realPath
if !isDir {
// get parent directory info
realPath = filepath.Dir(realPath)
}
dir, err := os.Open(realPath)
if err != nil {
return nil, err
}
defer dir.Close()
dirInfo, err := dir.Stat()
if err != nil {
return nil, err
}
combinedPath := adjustedPath + "/"
if adjustedPath == "/" {
combinedPath = "/"
}
var response *iteminfo.FileInfo
response, err = idx.GetDirInfo(dir, dirInfo, realPath, adjustedPath, combinedPath, false, false)
if err != nil {
return nil, err
}
if !isDir {
baseName := filepath.Base(originalPath)
idx.MakeIndexPath(realPath)
found := false
for _, item := range response.Files {
if item.Name == baseName {
response = &iteminfo.FileInfo{
Path: adjustedPath + "/" + item.Name,
ItemInfo: item,
}
found = true
continue
}
}
return nil
}
if !found {
return nil, fmt.Errorf("file not found in directory: %s", adjustedPath)
}
if quick {
idx.mu.Lock()
idx.FilesChangedDuringIndexing = true
idx.mu.Unlock()
}
return response, nil
}
func (idx *Index) GetDirInfo(dirInfo *os.File, stat os.FileInfo, realPath, adjustedPath, combinedPath string, quick, recursive bool) (*iteminfo.FileInfo, error) {
// Read directory contents
files, err := dir.Readdir(-1)
files, err := dirInfo.Readdir(-1)
if err != nil {
return err
return nil, err
}
var totalSize int64
fileInfos := []iteminfo.ItemInfo{}
dirInfos := []iteminfo.ItemInfo{}
// Process each file and directory in the current directory
for _, file := range files {
isHidden := isHidden(file, idx.Source.Path+combinedPath)
hidden := isHidden(file, idx.Path+combinedPath)
isDir := iteminfo.IsDirectory(file)
fullCombined := combinedPath + file.Name()
if idx.shouldSkip(isDir, isHidden, fullCombined) {
if idx.shouldSkip(isDir, hidden, fullCombined) && recursive {
continue
}
itemInfo := &iteminfo.ItemInfo{
Name: file.Name(),
ModTime: file.ModTime(),
Hidden: isHidden,
Hidden: hidden,
}
if isDir {
// skip non-indexable dirs.
if file.Name() == "$RECYCLE.BIN" || file.Name() == "System Volume Information" {
continue
@@ -168,6 +246,8 @@ func (idx *Index) indexDirectory(adjustedPath string, quick, recursive bool) err
dirPath := combinedPath + file.Name()
if recursive {
// clear for garbage collection
file = nil
// Recursively index the subdirectory
err = idx.indexDirectory(dirPath, quick, recursive)
if err != nil {
@@ -191,9 +271,11 @@ func (idx *Index) indexDirectory(adjustedPath string, quick, recursive bool) err
idx.NumFiles++
}
}
if totalSize == 0 && idx.Source.Config.IgnoreZeroSizeFolders {
return nil
if totalSize == 0 && idx.Config.IgnoreZeroSizeFolders {
return nil, errors.ErrNotIndexed
}
// Create FileInfo for the current directory
dirFileInfo := &iteminfo.FileInfo{
Path: adjustedPath,
@@ -201,16 +283,13 @@ func (idx *Index) indexDirectory(adjustedPath string, quick, recursive bool) err
Folders: dirInfos,
}
dirFileInfo.ItemInfo = iteminfo.ItemInfo{
Name: dirInfo.Name(),
Name: filepath.Base(dirInfo.Name()),
Type: "directory",
Size: totalSize,
ModTime: dirInfo.ModTime(),
ModTime: stat.ModTime(),
}
dirFileInfo.SortItems()
// Update the current directory metadata in the index
idx.UpdateMetadata(dirFileInfo)
return nil
return dirFileInfo, nil
}
// input should be non-index path.
@@ -218,12 +297,12 @@ func (idx *Index) MakeIndexPath(subPath string) string {
if strings.HasPrefix(subPath, "./") {
subPath = strings.TrimPrefix(subPath, ".")
}
if idx.Source.Path == subPath || subPath == "." {
if idx.Path == subPath || subPath == "." {
return "/"
}
// clean path
subPath = strings.TrimSuffix(subPath, "/")
adjustedPath := strings.TrimPrefix(subPath, idx.Source.Path)
adjustedPath := strings.TrimPrefix(subPath, idx.Path)
// remove index prefix
adjustedPath = strings.ReplaceAll(adjustedPath, "\\", "/")
// remove trailing slash
@@ -247,7 +326,7 @@ func (idx *Index) recursiveUpdateDirSizes(childInfo *iteminfo.FileInfo, previous
}
func (idx *Index) GetRealPath(relativePath ...string) (string, bool, error) {
combined := append([]string{idx.Source.Path}, relativePath...)
combined := append([]string{idx.Path}, relativePath...)
joinedPath := filepath.Join(combined...)
isDir, _ := RealPathCache.Get(joinedPath + ":isdir").(bool)
cached, ok := RealPathCache.Get(joinedPath).(string)
@@ -279,7 +358,7 @@ func (idx *Index) RefreshFileInfo(opts iteminfo.FileOptions) error {
}
err := idx.indexDirectory(refreshOptions.Path, false, false)
if err != nil {
return fmt.Errorf("file/folder does not exist to refresh data: %s", refreshOptions.Path)
return err
}
file, exists := idx.GetMetadataInfo(refreshOptions.Path, true)
if !exists {
@@ -317,21 +396,24 @@ func isHidden(file os.FileInfo, srcPath string) bool {
}
func (idx *Index) shouldSkip(isDir bool, isHidden bool, fullCombined string) bool {
if idx.Config.DisableIndexing {
return true
}
// check inclusions first
if isDir && len(idx.Source.Config.Include.Folders) > 0 {
if !slices.Contains(idx.Source.Config.Include.Folders, fullCombined) {
if isDir && len(idx.Config.Include.Folders) > 0 {
if !slices.Contains(idx.Config.Include.Folders, fullCombined) {
return true
}
}
if !isDir && len(idx.Source.Config.Include.Files) > 0 {
if !slices.Contains(idx.Source.Config.Include.Files, fullCombined) {
if !isDir && len(idx.Config.Include.Files) > 0 {
if !slices.Contains(idx.Config.Include.Files, fullCombined) {
return true
}
}
if !isDir && len(idx.Source.Config.Include.FileEndsWith) > 0 {
if !isDir && len(idx.Config.Include.FileEndsWith) > 0 {
shouldSkip := true
for _, end := range idx.Source.Config.Include.FileEndsWith {
for _, end := range idx.Config.Include.FileEndsWith {
if strings.HasSuffix(fullCombined, end) {
shouldSkip = false
break
@@ -343,19 +425,19 @@ func (idx *Index) shouldSkip(isDir bool, isHidden bool, fullCombined string) boo
}
// check exclusions
if isDir && slices.Contains(idx.Source.Config.Exclude.Folders, fullCombined) {
if isDir && slices.Contains(idx.Config.Exclude.Folders, fullCombined) {
return true
}
if !isDir && slices.Contains(idx.Source.Config.Exclude.Files, fullCombined) {
if !isDir && slices.Contains(idx.Config.Exclude.Files, fullCombined) {
return true
}
if idx.Source.Config.IgnoreHidden && isHidden {
if idx.Config.IgnoreHidden && isHidden {
return true
}
if !isDir && len(idx.Source.Config.Exclude.FileEndsWith) > 0 {
if !isDir && len(idx.Config.Exclude.FileEndsWith) > 0 {
shouldSkip := false
for _, end := range idx.Source.Config.Exclude.FileEndsWith {
for _, end := range idx.Config.Exclude.FileEndsWith {
if strings.HasSuffix(fullCombined, end) {
shouldSkip = true
break
@@ -378,13 +460,13 @@ func (idx *Index) SetUsage(usage DiskUsage) {
}
idx.mu.Lock()
defer idx.mu.Unlock()
idx.ReducedIndex.DiskUsed = int64(usage.Used)
idx.ReducedIndex.DiskTotal = int64(usage.Total)
idx.DiskUsed = int64(usage.Used)
idx.DiskTotal = int64(usage.Total)
}
func (idx *Index) SetStatus(status IndexStatus) {
idx.mu.Lock()
idx.ReducedIndex.Status = status
idx.Status = status
switch status {
case INDEXING:
idx.runningScannerCount++
+38 -18
View File
@@ -9,15 +9,17 @@ import (
)
// schedule in minutes
var scanSchedule = []time.Duration{
5 * time.Minute, // 5 minute quick scan & 25 minutes for a full scan
10 * time.Minute,
20 * time.Minute,
40 * time.Minute, // [3] element is 40 minutes, reset anchor for full scan
1 * time.Hour,
2 * time.Hour,
3 * time.Hour,
4 * time.Hour, // 4 hours for quick scan & 20 hours for a full scan
var scanSchedule = map[int]time.Duration{
0: 5 * time.Minute, // 5 minute quick scan & 25 minutes for a full scan
1: 10 * time.Minute,
2: 20 * time.Minute,
3: 40 * time.Minute, // reset anchor for full scan
4: 1 * time.Hour,
5: 2 * time.Hour,
6: 3 * time.Hour, // [6]
7: 4 * time.Hour, // [7] 4 hours for quick scan & 20 hours for a full scan
8: 8 * time.Hour,
9: 12 * time.Hour,
}
var fullScanAnchor = 3 // index of the schedule for a full scan
@@ -31,8 +33,8 @@ func (idx *Index) newScanner(origin string) {
if idx.Assessment == "simple" {
sleepTime = scanSchedule[idx.CurrentSchedule] - idx.SmartModifier
}
if idx.Source.Config.IndexingInterval > 0 {
sleepTime = time.Duration(idx.Source.Config.IndexingInterval) * time.Minute
if idx.Config.IndexingInterval > 0 {
sleepTime = time.Duration(idx.Config.IndexingInterval) * time.Minute
}
// Log and sleep before indexing
logger.Debugf("Next scan in %v", sleepTime)
@@ -53,6 +55,7 @@ func (idx *Index) PreScan() {
func (idx *Index) PostScan() {
idx.mu.Lock()
idx.garbageCollection()
idx.runningScannerCount--
idx.mu.Unlock()
if idx.runningScannerCount == 0 {
@@ -60,6 +63,19 @@ func (idx *Index) PostScan() {
}
}
func (idx *Index) garbageCollection() {
for path := range idx.Directories {
_, ok := idx.DirectoriesLedger[path]
if !ok {
idx.Directories[path] = nil
delete(idx.Directories, path)
idx.NumDeleted++
}
}
// Reset the ledger for the next scan.
idx.DirectoriesLedger = make(map[string]bool)
}
func (idx *Index) UpdateSchedule() {
// Adjust schedule based on file changes
if idx.FilesChangedDuringIndexing {
@@ -109,13 +125,17 @@ func (idx *Index) SendSourceUpdateEvent() {
}
func (idx *Index) RunIndexing(origin string, quick bool) {
if idx.runningScannerCount > 0 {
logger.Debugf("Indexing already in progress for [%v]", idx.Name)
return
}
idx.PreScan()
prevNumDirs := idx.NumDirs
prevNumFiles := idx.NumFiles
if quick {
logger.Debugf("Starting quick scan for [%v]", idx.Source.Name)
logger.Debugf("Starting quick scan for [%v]", idx.Name)
} else {
logger.Debugf("Starting full scan for [%v]", idx.Source.Name)
logger.Debugf("Starting full scan for [%v]", idx.Name)
idx.NumDirs = 0
idx.NumFiles = 0
}
@@ -126,13 +146,13 @@ func (idx *Index) RunIndexing(origin string, quick bool) {
if err != nil {
logger.Errorf("Error during indexing: %v", err)
}
firstRun := idx.LastIndexed == time.Time{}
firstRun := time.Time.Equal(idx.LastIndexed, time.Time{})
// Update the LastIndexed time
idx.LastIndexed = time.Now()
idx.LastIndexedUnix = idx.LastIndexed.Unix()
if quick {
idx.QuickScanTime = int(time.Since(startTime).Seconds())
logger.Debugf("Time spent indexing [%v]: %v seconds", idx.Source.Name, idx.QuickScanTime)
logger.Debugf("Time spent indexing [%v]: %v seconds", idx.Name, idx.QuickScanTime)
} else {
idx.FullScanTime = int(time.Since(startTime).Seconds())
// update smart indexing
@@ -147,14 +167,14 @@ func (idx *Index) RunIndexing(origin string, quick bool) {
idx.Assessment = "normal"
}
if firstRun {
logger.Infof("Index assessment : [%v] complexity=%v directories=%v files=%v", idx.Source.Name, idx.Assessment, idx.NumDirs, idx.NumFiles)
logger.Infof("Index assessment : [%v] complexity=%v directories=%v files=%v", idx.Name, idx.Assessment, idx.NumDirs, idx.NumFiles)
} else {
logger.Debugf("Index assessment : [%v] complexity=%v directories=%v files=%v", idx.Source.Name, idx.Assessment, idx.NumDirs, idx.NumFiles)
logger.Debugf("Index assessment : [%v] complexity=%v directories=%v files=%v", idx.Name, idx.Assessment, idx.NumDirs, idx.NumFiles)
}
if idx.NumDirs != prevNumDirs || idx.NumFiles != prevNumFiles {
idx.FilesChangedDuringIndexing = true
}
logger.Debugf("Time spent indexing [%v]: %v seconds", idx.Source.Name, idx.FullScanTime)
logger.Debugf("Time spent indexing [%v]: %v seconds", idx.Name, idx.FullScanTime)
}
idx.PostScan()
+11
View File
@@ -20,6 +20,17 @@ var AllFiletypeOptions = []string{
"text",
}
var SubtitleExts = []string{
".vtt",
".srt",
".lrc",
".sbv",
".ass",
".ssa",
".sub",
".smi",
}
var MuPdfConvertable = []string{
".pdf", // PDF
".xps", // XPS
+7 -7
View File
@@ -1,6 +1,7 @@
package iteminfo
import (
"path/filepath"
"sort"
"strconv"
"strings"
@@ -14,18 +15,17 @@ func (i *ExtendedFileInfo) DetectSubtitles(parentInfo *FileInfo) {
logger.Debug("subtitles are not supported for this file : " + i.Name)
return
}
base := strings.Split(i.Name, ".")[0]
ext := filepath.Ext(i.Name)
baseWithoutExt := strings.TrimSuffix(i.Name, ext)
for _, f := range parentInfo.Files {
baseName := strings.Split(f.Name, ".")[0]
if baseName != base {
baseName := strings.TrimSuffix(i.Name, ext)
if baseName != baseWithoutExt {
continue
}
for _, subtitleExt := range []string{".vtt", ".srt", ".lrc", ".sbv", ".ass", ".ssa", ".sub", ".smi"} {
for _, subtitleExt := range SubtitleExts {
if strings.HasSuffix(f.Name, subtitleExt) {
fullPathBase := strings.Split(i.Path, ".")[0]
i.Subtitles = append(i.Subtitles, fullPathBase+subtitleExt)
i.Subtitles = append(i.Subtitles, parentInfo.Path+f.Name)
}
}
}
+2 -9
View File
@@ -22,8 +22,8 @@ func (idx *Index) UpdateMetadata(info *iteminfo.FileInfo) bool {
// GetMetadataInfo retrieves the FileInfo from the specified file or directory in the index.
func (idx *Index) GetReducedMetadata(target string, isDir bool) (*iteminfo.FileInfo, bool) {
idx.mu.Lock()
defer idx.mu.Unlock()
idx.mu.RLock()
defer idx.mu.RUnlock()
checkDir := idx.MakeIndexPath(target)
if !isDir {
checkDir = idx.MakeIndexPath(filepath.Dir(target))
@@ -71,13 +71,6 @@ func (idx *Index) GetMetadataInfo(target string, isDir bool) (*iteminfo.FileInfo
return dir, exists
}
func (idx *Index) RemoveDirectory(path string) {
idx.mu.Lock()
defer idx.mu.Unlock()
idx.NumDeleted++
delete(idx.Directories, path)
}
func GetIndex(name string) *Index {
indexesMutex.Lock()
defer indexesMutex.Unlock()
-14
View File
@@ -193,20 +193,6 @@ func TestSetDirectoryInfo(t *testing.T) {
}
}
// Test for RemoveDirectory
func TestRemoveDirectory(t *testing.T) {
index := &Index{
Directories: map[string]*iteminfo.FileInfo{
"/testpath": {},
},
}
index.RemoveDirectory("/testpath")
_, exists := index.Directories["/testpath"]
if exists {
t.Fatalf("expected directory to be removed")
}
}
func init() {
testIndex = Index{
ReducedIndex: ReducedIndex{
+2 -2
View File
@@ -37,12 +37,12 @@ func (idx *Index) Search(search string, scope string, sourceSession string) []Se
results := make(map[string]SearchResult, 0)
count := 0
var directories []string
cachedDirs, ok := SearchResultsCache.Get(idx.Source.Path + scope).([]string)
cachedDirs, ok := SearchResultsCache.Get(idx.Path + scope).([]string)
if ok {
directories = cachedDirs
} else {
directories = idx.getDirsInScope(scope)
SearchResultsCache.Set(idx.Source.Path+scope, directories)
SearchResultsCache.Set(idx.Path+scope, directories)
}
for _, searchTerm := range searchOptions.Terms {
if searchTerm == "" {
+6 -6
View File
@@ -92,7 +92,7 @@ func GetPreviewForFile(file iteminfo.ExtendedFileInfo, previewSize, url string,
if !AvailablePreview(file) {
return nil, ErrUnsupportedMedia
}
cacheKey := CacheKey(file.RealPath, previewSize, file.ItemInfo.ModTime, seekPercentage)
cacheKey := CacheKey(file.RealPath, previewSize, file.ModTime, seekPercentage)
if data, found, err := service.fileCache.Load(context.Background(), cacheKey); err != nil {
return nil, fmt.Errorf("failed to load from cache: %w", err)
} else if found {
@@ -110,7 +110,7 @@ func GeneratePreview(file iteminfo.ExtendedFileInfo, previewSize, officeUrl stri
)
// Generate thumbnail image from video
hasher := md5.New() //nolint:gosec
_, _ = hasher.Write([]byte(CacheKey(file.RealPath, previewSize, file.ItemInfo.ModTime, seekPercentage)))
_, _ = hasher.Write([]byte(CacheKey(file.RealPath, previewSize, file.ModTime, seekPercentage)))
hash := hex.EncodeToString(hasher.Sum(nil))
// Generate an image from office document
if iteminfo.HasDocConvertableExtension(file.Name, file.Type) {
@@ -154,13 +154,13 @@ func GeneratePreview(file iteminfo.ExtendedFileInfo, previewSize, officeUrl stri
return nil, fmt.Errorf("failed to resize preview image: %w", err)
}
// Cache and return
cacheKey := CacheKey(file.RealPath, previewSize, file.ItemInfo.ModTime, seekPercentage)
cacheKey := CacheKey(file.RealPath, previewSize, file.ModTime, seekPercentage)
if err := service.fileCache.Store(context.Background(), cacheKey, resizedBytes); err != nil {
logger.Errorf("failed to cache resized image: %v", err)
}
return resizedBytes, nil
} else {
cacheKey := CacheKey(file.RealPath, previewSize, file.ItemInfo.ModTime, seekPercentage)
cacheKey := CacheKey(file.RealPath, previewSize, file.ModTime, seekPercentage)
if err := service.fileCache.Store(context.Background(), cacheKey, imageBytes); err != nil {
logger.Errorf("failed to cache resized image: %v", err)
}
@@ -202,9 +202,9 @@ func CacheKey(realPath, previewSize string, modTime time.Time, percentage int) s
}
func DelThumbs(ctx context.Context, file iteminfo.ExtendedFileInfo) {
errSmall := service.fileCache.Delete(ctx, CacheKey(file.RealPath, "small", file.ItemInfo.ModTime, 0))
errSmall := service.fileCache.Delete(ctx, CacheKey(file.RealPath, "small", file.ModTime, 0))
if errSmall != nil {
errLarge := service.fileCache.Delete(ctx, CacheKey(file.RealPath, "large", file.ItemInfo.ModTime, 0))
errLarge := service.fileCache.Delete(ctx, CacheKey(file.RealPath, "large", file.ModTime, 0))
if errLarge != nil {
logger.Debugf("Could not delete thumbnail: %v", file.Name)
}
+10 -2
View File
@@ -2174,7 +2174,7 @@ const docTemplate = `{
"description": "default \"/\" should match folders under path",
"type": "string"
},
"disabled": {
"disableIndexing": {
"description": "disable the indexing of this source",
"type": "boolean"
},
@@ -2202,7 +2202,7 @@ const docTemplate = `{
}
]
},
"indexingInterval": {
"indexingIntervalMinutes": {
"description": "optional manual overide interval in seconds to re-index the source",
"type": "integer"
},
@@ -2242,6 +2242,10 @@ const docTemplate = `{
"description": "disable the user from viewing the settings page",
"type": "boolean"
},
"disableUpdateNotifications": {
"description": "disable update notifications banner for admin users",
"type": "boolean"
},
"gallerySize": {
"description": "0-9 - the size of the gallery thumbnails",
"type": "integer"
@@ -2466,6 +2470,10 @@ const docTemplate = `{
"disableSettings": {
"type": "boolean"
},
"disableUpdateNotifications": {
"description": "disable update notifications",
"type": "boolean"
},
"gallerySize": {
"description": "0-9 - the size of the gallery thumbnails",
"type": "integer"
+10 -2
View File
@@ -2163,7 +2163,7 @@
"description": "default \"/\" should match folders under path",
"type": "string"
},
"disabled": {
"disableIndexing": {
"description": "disable the indexing of this source",
"type": "boolean"
},
@@ -2191,7 +2191,7 @@
}
]
},
"indexingInterval": {
"indexingIntervalMinutes": {
"description": "optional manual overide interval in seconds to re-index the source",
"type": "integer"
},
@@ -2231,6 +2231,10 @@
"description": "disable the user from viewing the settings page",
"type": "boolean"
},
"disableUpdateNotifications": {
"description": "disable update notifications banner for admin users",
"type": "boolean"
},
"gallerySize": {
"description": "0-9 - the size of the gallery thumbnails",
"type": "integer"
@@ -2455,6 +2459,10 @@
"disableSettings": {
"type": "boolean"
},
"disableUpdateNotifications": {
"description": "disable update notifications",
"type": "boolean"
},
"gallerySize": {
"description": "0-9 - the size of the gallery thumbnails",
"type": "integer"
+8 -2
View File
@@ -410,7 +410,7 @@ definitions:
defaultUserScope:
description: default "/" should match folders under path
type: string
disabled:
disableIndexing:
description: disable the indexing of this source
type: boolean
exclude:
@@ -427,7 +427,7 @@ definitions:
allOf:
- $ref: '#/definitions/settings.IndexFilter'
description: include files and folders from indexing, if exclude is not set
indexingInterval:
indexingIntervalMinutes:
description: optional manual overide interval in seconds to re-index the source
type: integer
maxWatchers:
@@ -460,6 +460,9 @@ definitions:
disableSettings:
description: disable the user from viewing the settings page
type: boolean
disableUpdateNotifications:
description: disable update notifications banner for admin users
type: boolean
gallerySize:
description: 0-9 - the size of the gallery thumbnails
type: integer
@@ -624,6 +627,9 @@ definitions:
type: string
disableSettings:
type: boolean
disableUpdateNotifications:
description: disable update notifications
type: boolean
gallerySize:
description: 0-9 - the size of the gallery thumbnails
type: integer
+4 -3
View File
@@ -1,5 +1,5 @@
server:
numImageProcessors: 14 # number of concurrent image processing jobs used to create previews, default is number of cpu cores available.
numImageProcessors: 8 # number of concurrent image processing jobs used to create previews, default is number of cpu cores available.
socket: "" # socket to listen on
tlsKey: "" # path to TLS key
tlsCert: "" # path to TLS cert
@@ -21,8 +21,8 @@ server:
- path: /relative/or/absolute/path # file system path. (Can be relative) validate:required
name: backend # display name
config:
indexingInterval: 0 # optional manual overide interval in seconds to re-index the source
disabled: false # disable the indexing of this source
indexingIntervalMinutes: 0 # optional manual overide interval in seconds to re-index the source
disableIndexing: false # disable the indexing of this source
maxWatchers: 0 # number of concurrent watchers to use for this source, currently not supported
neverWatchPaths: []
ignoreHidden: false # ignore hidden files and folders.
@@ -116,6 +116,7 @@ userDefaults:
share: false
realtime: false
loginMethod: password # login method to use: eg. password, proxy, oidc
disableUpdateNotifications: false # disable update notifications banner for admin users
integrations:
office: # validate:omitempty
url: "" # The URL to the OnlyOffice Document Server, needs to be accessible to the user. validate:required
+10 -12
View File
@@ -26,7 +26,7 @@ export async function fetchFiles(url, content = false) {
async function resourceAction(url, method, content) {
try {
const result = extractSourceFromPath(url)
let source = result.source
let source = encodeURIComponent(result.source)
let path = result.path
let opts = { method }
if (content) {
@@ -69,14 +69,13 @@ export function download(format, files) {
if (format !== 'zip') {
format = 'tar.gz'
}
let fileargs = ''
if (files.length === 1) {
const result = extractSourceFromPath(decodeURI(files[0]))
const result = extractSourceFromPath(decodeURIComponent(files[0]))
fileargs = result.source + '::' + result.path + '||'
} else {
for (let file of files) {
const result = extractSourceFromPath(decodeURI(file))
const result = extractSourceFromPath(decodeURIComponent(file))
fileargs += result.source + '::' + result.path + '||'
}
}
@@ -110,7 +109,7 @@ export async function post(url, content = '', overwrite = false, onupload) {
const apiPath = getApiPath('api/resources', {
path: result.path,
source: result.source,
source: encodeURIComponent(result.source),
override: overwrite
})
return new Promise((resolve, reject) => {
@@ -164,7 +163,7 @@ export async function moveCopy(
const toResult = extractSourceFromPath(item.to)
let localParams = {
...params,
destination: toResult.source + '::' + toResult.path,
destination: encodeURIComponent(toResult.source) + '::' + toResult.path,
from: fromResult.source + '::' + fromResult.path
}
const apiPath = getApiPath('api/resources', localParams)
@@ -189,12 +188,11 @@ export async function moveCopy(
}
}
export async function checksum(url, algo) {
export async function checksum(source, path, algo) {
try {
const result = extractSourceFromPath(url)
const params = {
path: encodeURIComponent(result.path),
source: result.source,
path: encodeURIComponent(path),
source: encodeURIComponent(source),
checksum: algo
}
const apiPath = getApiPath('api/resources', params)
@@ -210,7 +208,7 @@ export async function checksum(url, algo) {
export function getDownloadURL(source, path, inline, useExternal) {
try {
const params = {
files: source + '::' + encodeURIComponent(path),
files: encodeURIComponent(source) + '::' + encodeURIComponent(path),
...(inline && { inline: 'true' })
}
const apiPath = getApiPath('api/raw', params)
@@ -230,7 +228,7 @@ export function getPreviewURL(source, path, modified) {
const params = {
path: encodeURIComponent(path),
key: Date.parse(modified), // Use modified date as cache key
source: source,
source: encodeURIComponent(source),
inline: 'true'
}
const apiPath = getApiPath('api/preview', params)
+1 -1
View File
@@ -8,7 +8,7 @@ export default async function search(base, source, query) {
if (!base.endsWith("/")) {
base += "/";
}
const apiPath = getApiPath("api/search", { scope: encodeURIComponent(base), query: query, source: source });
const apiPath = getApiPath("api/search", { scope: encodeURIComponent(base), query: query, source: encodeURIComponent(source) });
const res = await fetchURL(apiPath);
let data = await res.json();
+2 -2
View File
@@ -10,7 +10,7 @@ export async function list() {
export async function get(path, source) {
try {
const params = { path, source };
const params = { path: encodeURIComponent(path), source: encodeURIComponent(source) };
const apiPath = getApiPath("api/share",params);
let data = fetchJSON(apiPath);
return adjustedData(data, path);
@@ -29,7 +29,7 @@ export async function remove(hash) {
}
export async function create(path, source, password = "", expires = "", unit = "hours") {
const params = { path: encodeURIComponent(path), source: source };
const params = { path: encodeURIComponent(path), source: encodeURIComponent(source) };
const apiPath = getApiPath("api/share",params);
let body = "{}";
if (password != "" || expires !== "" || unit !== "hours") {
+11 -7
View File
@@ -148,7 +148,7 @@ import { getHumanReadableFilesize } from "@/utils/filesizes";
import { url } from "@/utils/";
import Icon from "@/components/files/Icon.vue";
import { serverHasMultipleSources } from "@/utils/constants";
import { serverHasMultipleSources, baseURL } from "@/utils/constants";
var boxes = {
folder: { label: "folders", icon: "folder" },
@@ -395,9 +395,9 @@ export default {
const encodedPath = encodeURIComponent(path).replace("/%20/g", "/");
let fullpath = path;
if (serverHasMultipleSources) {
fullpath = "/files/" + this.selectedSource + "/" + encodedPath;
fullpath = baseURL+"files/" + this.selectedSource + "/" + encodedPath;
} else {
fullpath = "/files/" + encodedPath;
fullpath = baseURL+"files/" + encodedPath;
}
return fullpath;
},
@@ -498,9 +498,9 @@ export default {
this.ongoing = true;
let source = this.selectedSource;
if (source == "") {
source = state.sources.current;
this.selectedSource = state.sources.current;
}
this.results = await search(this.getContext, source, searchTypesFull + this.value);
this.results = await search(this.getContext, this.selectedSource, searchTypesFull + this.value);
this.ongoing = false;
if (this.results.length == 0) {
@@ -519,13 +519,17 @@ export default {
if (this.getContext === "/") {
path = s.path;
}
let urlPath = "/files/" + state.sources.current + path;
if (!state.serverHasMultipleSources) {
urlPath = "/files" + path;
}
const modifiedItem = {
name: pathParts.pop(),
path: path,
size: s.size,
type: s.type,
source: "",
url: path,
source: this.selectedSource,
url: urlPath,
fullPath: path,
};
mutations.resetSelected();
@@ -101,7 +101,7 @@ export default {
return state.user.viewMode === "gallery";
},
quickDownloadEnabled() {
return state.user?.quickDownload;
return state.user?.quickDownload && !this.galleryView && !this.isDir;
},
isHiddenNotSelected() {
return !this.isSelected && this.reducedOpacity;
+2 -2
View File
@@ -165,7 +165,7 @@ export default {
event.preventDefault();
let link;
if (state.isSearchActive) {
const hash = await filesApi.checksum(state.selected[0].path, algo);
const hash = await filesApi.checksum(state.selected[0].source, state.selected[0].path, algo);
event.target.innerHTML = hash;
return;
}
@@ -175,7 +175,7 @@ export default {
link = state.route.path;
}
const hash = await filesApi.checksum(link, algo);
const hash = await filesApi.checksum(state.sources.current, link, algo);
event.target.innerHTML = hash;
},
},
@@ -1,7 +1,7 @@
<template>
<select @change="change" :value="locale">
<option v-for="(value, label) in locales" :key="label" :value="label">
{{ $t("languages." + value) }}
{{ $t("languages." + label) }}
</option>
</select>
</template>
+1 -1
View File
@@ -10,7 +10,7 @@
'active-settings': active(setting.id + '-main'),
}"
>
<div v-if="shouldShow(setting)" class="settings-card">{{ setting.label }}</div>
<div v-if="shouldShow(setting)" class="settings-card">{{ $t(setting.label) }}</div>
</div>
</template>
+29 -1
View File
@@ -3,6 +3,10 @@
id="sidebar"
:class="{ active: active, 'dark-mode': isDarkMode, 'behind-overlay': behindOverlay }"
>
<div v-if="shouldShow" class="button release-banner">
<a :href="releaseUrl">{{ $t("sidebar.updateIsAvailable") }}</a>
<i @click="setSeenUpdate" aria-label="close-banner" class="material-icons">close</i>
</div>
<SidebarSettings v-if="isSettings"></SidebarSettings>
<SidebarGeneral v-else-if="isLoggedIn"></SidebarGeneral>
@@ -19,7 +23,7 @@
</template>
<script>
import { externalLinks, name } from "@/utils/constants";
import { externalLinks, name, updateAvailable } from "@/utils/constants";
import { getters, mutations, state } from "@/store"; // Import your custom store
import SidebarGeneral from "./General.vue";
import SidebarSettings from "./Settings.vue";
@@ -36,18 +40,34 @@ export default {
name,
};
},
mounted() {
// Ensure the sidebar is initialized correctly
mutations.setSeenUpdate(localStorage.getItem("seenUpdate"));
},
computed: {
releaseUrl: () => updateAvailable,
isDarkMode: () => getters.isDarkMode(),
isLoggedIn: () => getters.isLoggedIn(),
isSettings: () => getters.isSettings(),
active: () => getters.isSidebarVisible(),
behindOverlay: () => state.isSearchActive,
shouldShow() {
return (
updateAvailable != "" &&
state.user.permissions.admin &&
state.seenUpdate != updateAvailable &&
!state.user.disableUpdateNotifications
);
},
},
methods: {
// Show the help overlay
help() {
mutations.showHover("help");
},
setSeenUpdate() {
mutations.setSeenUpdate(updateAvailable);
},
},
};
</script>
@@ -155,4 +175,12 @@ body.rtl .action {
.clickable:hover {
box-shadow: 0 2px 2px #00000024, 0 1px 5px #0000001f, 0 3px 1px -2px #0003;
}
.release-banner {
background-color: var(--primarColor);
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1em;
}
</style>
+4 -2
View File
@@ -197,7 +197,8 @@
"highQualityPreview": "High quality preview for gallery images",
"editOffice": "Edit/view office files",
"popupPreview": "Enable pop-up preview",
"filePreviewOptions": "File preview options"
"filePreviewOptions": "File preview options",
"disableUpdateNotifications": "Disable showing notification update banner in sidebar."
},
"settings": {
"enterPassword": "Enter password",
@@ -311,7 +312,8 @@
"settings": "Settings",
"sources": "Sources",
"signup": "Signup",
"siteSettings": "Site Settings"
"siteSettings": "Site Settings",
"updateIsAvailable": "An update is available!"
},
"success": {
"linkCopied": "Link copied!"
+2 -11
View File
@@ -24,7 +24,6 @@ export const getters = {
if (
cv == 'preview' ||
cv == 'onlyOfficeEditor' ||
cv == 'markdownViewer' ||
cv == 'epubViewer' ||
cv == 'docViewer' ||
cv == 'editor'
@@ -153,16 +152,8 @@ export const getters = {
) {
visible = false
}
if (currentView == 'preview') {
const contentType = getters.previewType()
if (
contentType == 'audio' ||
contentType == 'video' ||
contentType == 'image' ||
contentType == 'pdf'
) {
visible = false
}
if (currentView == 'preview' || currentView == 'markdownViewer') {
visible = false
}
if (currentView == 'editor' || currentView == 'onlyOfficeEditor') {
visible = false
+9 -2
View File
@@ -8,6 +8,11 @@ import { sortedItems } from "@/utils/sort.js";
import { serverHasMultipleSources } from "@/utils/constants.js";
export const mutations = {
setSeenUpdate: (value) => {
state.seenUpdate = value
localStorage.setItem("seenUpdate", value);
emitStateChanged();
},
setMultiButtonState: (value) => {
if (state.multiButtonLastState != value) {
state.multiButtonLastState = state.multiButtonState;
@@ -88,7 +93,7 @@ export const mutations = {
let sources = {info: {}, current: currentSource, count: user.scopes.length};
for (const source of user.scopes) {
sources.info[source.name] = {
pathPrefix: sources.count == 1 ? "" : source.name,
pathPrefix: sources.count == 1 ? "" : encodeURIComponent(source.name),
used: 0,
total: 0,
usedPercentage: 0,
@@ -212,9 +217,12 @@ export const mutations = {
if (value.username != "publicUser") {
mutations.setSources(value);
}
console.log("Setting current user:", value);
// Ensure locale exists and is valid
if (!value.locale) {
value.locale = i18n.detectLocale(); // Default to detected locale if missing
} else {
i18n.setLocale(value.locale);
}
state.user = value;
} catch (error) {
@@ -270,7 +278,6 @@ export const mutations = {
// Merge the new values into the current user state
state.user = { ...state.user, ...value };
// Handle locale change
if (state.user.locale !== previousUser.locale) {
//state.user.locale = i18n.detectLocale();
+6 -6
View File
@@ -1,5 +1,3 @@
import i18n from '@/i18n'; // Import the default export (your i18n instance)
const name = window.FileBrowser.Name;
const disableExternal = window.FileBrowser.DisableExternal;
const externalLinks = window.FileBrowser.ExternalLinks;
@@ -22,17 +20,19 @@ const oidcAvailable = window.FileBrowser.OidcAvailable;
const passwordAvailable = window.FileBrowser.PasswordAvailable;
const mediaAvailable = window.FileBrowser.MediaAvailable;
const muPdfAvailable = window.FileBrowser.MuPdfAvailable;
const updateAvailable = window.FileBrowser.UpdateAvailable;
const origin = window.location.origin;
const settings = [
{ id: 'profile', label: i18n.global.t('settings.profileSettings'), component: 'ProfileSettings' },
{ id: 'shares', label: i18n.global.t('settings.shareSettings'), component: 'SharesSettings', permissions: { share: true } },
{ id: 'api', label: i18n.global.t('api.title'), component: 'ApiKeys', permissions: { api: true } },
{ id: 'profile', label: 'settings.profileSettings', component: 'ProfileSettings' },
{ id: 'shares', label: 'settings.shareSettings', component: 'SharesSettings', permissions: { share: true } },
{ id: 'api', label: 'api.title', component: 'ApiKeys', permissions: { api: true } },
//{ id: 'global', label: 'Global', component: 'GlobalSettings', permissions: { admin: true } },
{ id: 'users', label: i18n.global.t('settings.userManagement'), component: 'UserManagement' },
{ id: 'users', label: 'settings.userManagement', component: 'UserManagement' },
];
export {
updateAvailable,
muPdfAvailable,
mediaAvailable,
oidcAvailable,
+1 -2
View File
@@ -121,9 +121,8 @@ export function extractSourceFromPath(url) {
if (state.serverHasMultipleSources) {
source = path.split('/')[2];
path = removePrefix(path, `/files/${source}`);
source = encodeURIComponent(source)
} else {
source = encodeURIComponent(state.sources.current);
source = state.sources.current;
path = removePrefix(path, '/files');
}
+2 -1
View File
@@ -123,7 +123,8 @@ export default {
routePath == "/";
// lets redirect if multiple sources and user went to /files/
if (state.serverHasMultipleSources && rootRoute) {
router.push(`${routePath}/${state.sources.current}`);
const urlEncodedSource = encodeURIComponent(state.sources.current)
router.push(`${routePath}/${urlEncodedSource}`);
return;
}
this.lastHash = "";
+3
View File
@@ -608,6 +608,9 @@ export default {
break;
case "Backspace":
if (getters.CurrentPromptName !== null) {
return;
}
// go back
router.push({ path: newPath });
break;
+4 -1
View File
@@ -127,7 +127,10 @@ export default {
".hwp": true,
".hwpx": true,
};
return !!pdfConvertCompatibleFileExtensions[ext];
if (state.user.disableOfficePreviewExt.includes(ext)) {
return false;
}
return !!pdfConvertCompatibleFileExtensions[ext] ;
},
sidebarShowing() {
return getters.isSidebarVisible();
+3 -3
View File
@@ -86,6 +86,9 @@ export default {
error: null,
links: {},
clip: null,
user: {
permissions: { ...state.user.permissions}
},
};
},
async created() {
@@ -115,9 +118,6 @@ export default {
active() {
return state.activeSettingsView === "shares-main";
},
user() {
return state.user;
},
loading() {
return getters.isLoading();
},
+28 -13
View File
@@ -6,7 +6,7 @@
<div class="card-content">
<form>
<div class="card-content">
<h3>{{ $t('settings.listingOptions') }}</h3>
<h3>{{ $t("settings.listingOptions") }}</h3>
<div class="settings-items">
<ToggleSwitch
class="item"
@@ -24,7 +24,7 @@
:name="$t('profileSettings.showQuickDownload')"
/>
</div>
<h3> {{ $t('profileSettings.filePreviewOptions') }}</h3>
<h3>{{ $t("profileSettings.filePreviewOptions") }}</h3>
<div class="settings-items">
<ToggleSwitch
class="item"
@@ -61,9 +61,9 @@
/>
</div>
<div v-if="hasOnlyOfficeEnabled">
<h3> {{ $t('settings.disableOfficePreview')}} </h3>
<h3>{{ $t("settings.disableOfficePreview") }}</h3>
<p>
{{ $t('settings.disableOfficePreviewDescription') }}
{{ $t("settings.disableOfficePreviewDescription") }}
</p>
<div class="form-group">
<input
@@ -74,14 +74,20 @@
id="onlyofficeExt"
v-model="formOnlyOfficeExt"
/>
<button type="button" class="button form-button" @click="submitOnlyOfficeChange"> {{ $t('buttons.save') }} </button>
<button
type="button"
class="button form-button"
@click="submitOnlyOfficeChange"
>
{{ $t("buttons.save") }}
</button>
</div>
</div>
<div v-if="muPdfAvailable">
<h3> {{ $t('settings.disableOfficePreviews')}} </h3>
<h3>{{ $t("settings.disableOfficePreviews") }}</h3>
<p>
{{ $t('settings.disableOfficePreviewsDescription') }}
{{ $t("settings.disableOfficePreviewsDescription") }}
</p>
<div class="form-group">
<input
@@ -92,11 +98,22 @@
id="officePreviewExt"
v-model="formOfficePreviewExt"
/>
<button type="button" class="button form-button" @click="submitOfficePreviewsChange"> {{ $t('buttons.save') }} </button>
<button
type="button"
class="button form-button"
@click="submitOfficePreviewsChange"
>
{{ $t("buttons.save") }}
</button>
</div>
</div>
<h3> {{ $t('settings.themeColor') }} </h3>
<ToggleSwitch
v-if="localuser.permissions?.admin"
class="item"
v-model="localuser.disableUpdateNotifications"
:name="$t('profileSettings.disableUpdateNotifications')"
/>
<h3>{{ $t("settings.themeColor") }}</h3>
<ButtonGroup
:buttons="colorChoices"
@button-clicked="setColor"
@@ -132,7 +149,7 @@ export default {
},
data() {
return {
localuser: { preview: {} },
localuser: { preview: {}, permissions: {} }, // Initialize localuser with empty objects to avoid undefined errors
initialized: false,
formOnlyOfficeExt: "", // holds temporary input before saving
formOfficePreviewExt: "", // holds temporary input before saving
@@ -250,6 +267,4 @@ export default {
.card-content h3 {
text-align: center;
}
</style>