Compare commits

...
13 changed files with 1585 additions and 1900 deletions
+9
View File
@@ -2,6 +2,15 @@
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
## [2.63.18](https://github.com/filebrowser/filebrowser/compare/v2.63.17...v2.63.18) (2026-07-04)
### Bug Fixes
* avoid recursive conflict checks for copy and move ([#6009](https://github.com/filebrowser/filebrowser/issues/6009)) ([dfc2e88](https://github.com/filebrowser/filebrowser/commit/dfc2e887e1a19d54984a0d7e39a2a63caf73ef19))
* deduplicate PT language ([4470288](https://github.com/filebrowser/filebrowser/commit/4470288ba1828a14453a99348fd22c62aa0b9460))
* **preview:** keep the EPUB table-of-contents button clear of the header ([#6010](https://github.com/filebrowser/filebrowser/issues/6010)) ([aac2516](https://github.com/filebrowser/filebrowser/commit/aac25166378422135e624e305c410c54a39374fb))
## [2.63.17](https://github.com/filebrowser/filebrowser/compare/v2.63.16...v2.63.17) (2026-06-27)
+2 -2
View File
@@ -1,5 +1,5 @@
## Multistage build: First stage fetches dependencies
FROM alpine:3.23 AS fetcher
FROM alpine:3.24 AS fetcher
# install and copy ca-certificates, mailcap, and tini-static; download JSON.sh
RUN apk update && \
@@ -7,7 +7,7 @@ RUN apk update && \
wget -O /JSON.sh https://raw.githubusercontent.com/dominictarr/JSON.sh/0d5e5c77365f63809bf6e77ef44a1f34b0e05840/JSON.sh
## Second stage: Use lightweight BusyBox image for final runtime environment
FROM busybox:1.37.0-musl
FROM busybox:1.38.0-musl
# Define non-root user UID and GID
ENV UID=1000
+1 -1
View File
@@ -74,5 +74,5 @@
"vitest": "^4.1.0",
"vue-tsc": "^3.1.3"
},
"packageManager": "pnpm@10.33.4+sha512.1c67b3b359b2d408119ba1ed289f34b8fc3c6873412bec6fd264fbdc82489e510fcbecb9ce9d22dae7f3b76269d8441046014bdca53b9979cd7a561ad631b800"
"packageManager": "pnpm@10.34.5+sha512.a4ee05f2f73658255bd6a89859c065a45c28a57daefae2c893a168ee2b73168c37b91e83e57ea67654ad03f03031746430e8bce38e362e042605fb8abc80192e"
}
+1419 -1498
View File
File diff suppressed because it is too large Load Diff
@@ -71,7 +71,9 @@ const event = {
describe("copy and move conflict prompts", () => {
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(checkConflict).mockResolvedValue(conflict);
vi.mocked(checkConflict).mockResolvedValue(
conflict as ConflictingResource[]
);
});
it("waits for copy conflict detection before calling the copy API", async () => {
+7
View File
@@ -29,6 +29,13 @@
.epub-reader .tocButton {
color: var(--text);
/*
* vue-reader positions the TOC toggle at top: 10px, which lands under the
* fixed 4em header bar (see .header) — so clicks hit the header's Close
* button instead of opening the table of contents. Push it just below the
* header, tracking the header's height so it stays clear if that changes.
*/
top: calc(4em + 10px);
}
.epub-reader .tocButton.tocButtonExpanded {
+1 -1
View File
@@ -238,7 +238,7 @@
"newPasswordConfirm": "Confirmar a sua palavra-passe",
"newUser": "Novo Utilizador",
"password": "Palavra-passe",
"passwordUpdated": "Palavras-passe não coincidem!",
"passwordUpdated": "Palavra-passe atualizada!",
"path": "Caminho",
"perm": {
"create": "Criar ficheiros e pastas",
-309
View File
@@ -1,309 +0,0 @@
{
"buttons": {
"cancel": "Cancelar",
"clear": "Limpar",
"close": "Fechar",
"continue": "Continuar",
"copy": "Copiar",
"copyFile": "Copiar ficheiro",
"copyToClipboard": "Copiar",
"copyDownloadLinkToClipboard": "Copy download link to clipboard",
"create": "Criar",
"delete": "Eliminar",
"download": "Descarregar",
"file": "File",
"folder": "Folder",
"fullScreen": "Toggle full screen",
"hideDotfiles": "Hide dotfiles",
"info": "Info",
"more": "Mais",
"move": "Mover",
"moveFile": "Mover ficheiro",
"new": "Novo",
"next": "Próximo",
"ok": "OK",
"permalink": "Obter link permanente",
"previous": "Anterior",
"preview": "Preview",
"publish": "Publicar",
"rename": "Alterar nome",
"replace": "Substituir",
"reportIssue": "Reportar erro",
"resumeTransfer": "Resume previous transfer",
"resumeTransferTooltip": "Skip all conflicting files, except the ones that are smaller on the server as we suppose that their transfert has been interrupted.",
"save": "Guardar",
"schedule": "Agendar",
"search": "Pesquisar",
"select": "Selecionar",
"selectMultiple": "Selecionar vários",
"share": "Partilhar",
"shell": "Alternar shell",
"submit": "Submit",
"switchView": "Alterar vista",
"toggleSidebar": "Alternar barra lateral",
"update": "Atualizar",
"upload": "Enviar",
"openFile": "Open file",
"openDirect": "View raw",
"discardChanges": "Discard",
"stopSearch": "Stop searching",
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Descarregar ficheiro",
"downloadFolder": "Descarregar pasta",
"downloadSelected": "Download Selected"
},
"upload": {
"abortUpload": "Are you sure you wish to abort?"
},
"errors": {
"forbidden": "Não tem permissões para aceder a isto",
"internal": "Algo correu bastante mal.",
"notFound": "Esta localização não é alcançável.",
"connection": "The server can't be reached."
},
"files": {
"body": "Corpo",
"closePreview": "Fechar pré-visualização",
"files": "Ficheiros",
"folders": "Pastas",
"home": "Início",
"lastModified": "Última alteração",
"loading": "A carregar...",
"lonely": "Sinto-me sozinho...",
"metadata": "Metadados",
"multipleSelectionEnabled": "Seleção múltipla ativada",
"name": "Nome",
"size": "Tamanho",
"sortByLastModified": "Ordenar pela última alteração",
"sortByName": "Ordenar pelo nome",
"sortBySize": "Ordenar pelo tamanho",
"noPreview": "Preview is not available for this file.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file.",
"showingRows": "Showing {count} row(s)",
"columnSeparator": "Column Separator",
"csvSeparators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
},
"fileEncoding": "File Encoding"
},
"help": {
"click": "selecionar pasta ou ficheiro",
"ctrl": {
"click": "selecionar várias pastas e ficheiros",
"f": "pesquisar",
"s": "guardar um ficheiro ou descarrega a pasta em que está a navegar"
},
"del": "eliminar os ficheiros selecionados",
"doubleClick": "abrir pasta ou ficheiro",
"esc": "limpar seleção e/ou fechar menu",
"f1": "esta informação",
"f2": "alterar nome do ficheiro",
"help": "Ajuda"
},
"login": {
"createAnAccount": "Criar uma conta",
"loginInstead": "Já tenho uma conta",
"password": "Palavra-passe",
"passwordConfirm": "Confirmação da palavra-passe",
"passwordsDontMatch": "As palavras-passe não coincidem",
"signup": "Registar",
"submit": "Entrar na conta",
"username": "Nome de utilizador",
"usernameTaken": "O nome de utilizador já está registado",
"wrongCredentials": "Dados errados",
"passwordTooShort": "Password must be at least {min} characters",
"logout_reasons": {
"inactivity": "You have been logged out due to inactivity."
}
},
"permanent": "Permanente",
"prompts": {
"copy": "Copiar",
"copyMessage": "Escolha um lugar para onde copiar os ficheiros:",
"currentlyNavigating": "A navegar em:",
"deleteMessageMultiple": "Quer eliminar {count} ficheiro(s)?",
"deleteMessageSingle": "Quer eliminar esta pasta/ficheiro?",
"deleteMessageShare": "Are you sure you wish to delete this share({path})?",
"deleteUser": "Are you sure you want to delete this user?",
"deleteTitle": "Eliminar ficheiros",
"displayName": "Nome:",
"download": "Descarregar ficheiros",
"downloadMessage": "Escolha o formato do ficheiro que quer descarregar.",
"error": "Algo correu mal",
"fileInfo": "Informação do ficheiro",
"filesSelected": "{count} ficheiros selecionados.",
"lastModified": "Última alteração",
"move": "Mover",
"moveMessage": "Escolha uma nova casa para os seus ficheiros/pastas:",
"newArchetype": "Criar um novo post baseado num \"archetype\". O seu ficheiro será criado na pasta \"content\".",
"newDir": "Nova pasta",
"newDirMessage": "Escreva o nome da nova pasta.",
"newFile": "Novo ficheiro",
"newFileMessage": "Escreva o nome do novo ficheiro.",
"numberDirs": "Número de pastas",
"numberFiles": "Número de ficheiros",
"rename": "Alterar nome",
"renameMessage": "Insira um novo nome para",
"replace": "Substituir",
"replaceMessage": "Já existe um ficheiro com nome igual a um dos que está a tentar enviar. Quer substituí-lo?\n",
"schedule": "Agendar",
"scheduleMessage": "Escolha uma data para publicar este post.",
"show": "Mostrar",
"size": "Tamanho",
"upload": "Upload",
"uploadFiles": "Uploading {files} files...",
"uploadMessage": "Select an option to upload.",
"optionalPassword": "Optional password",
"resolution": "Resolution",
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error",
"currentPassword": "Your password",
"currentPasswordMessage": "Enter your password to validate this action."
},
"search": {
"images": "Imagens",
"music": "Música",
"pdf": "PDF",
"pressToSearch": "Tecla Enter para pesquisar...",
"search": "Pesquisar...",
"typeToSearch": "Escrever para pesquisar...",
"types": "Tipos",
"video": "Vídeos"
},
"settings": {
"aceEditorTheme": "Ace editor theme",
"admin": "Admin",
"administrator": "Administrador",
"allowCommands": "Executar comandos",
"allowEdit": "Editar, renomear e eliminar ficheiros ou pastas",
"allowNew": "Criar novos ficheiros e pastas",
"allowPublish": "Publicar novas páginas e conteúdos",
"allowSignup": "Permitir que os utilizadores criem contas",
"hideLoginButton": "Hide the login button from public pages",
"avoidChanges": "(deixe em branco para manter)",
"branding": "Marca",
"brandingDirectoryPath": "Caminho da pasta de marca",
"brandingHelp": "Pode personalizar a aparência do seu Navegador de Ficheiros, alterar o nome, substituindo o logótipo, adicionando estilos personalizados e mesmo desativando links externos para o GitHub.\nPara mais informações sobre marca personalizada, por favor veja {0}.",
"changePassword": "Alterar palavra-passe",
"commandRunner": "Execução de comandos",
"commandRunnerHelp": "Aqui pode definir comandos que são executados nos eventos nomeados. Tem de escrever um por linha. As variáveis de ambiente {0} e {1} estarão disponíveis, sendo {0} relativo a {1}. Para mais informações sobre esta funcionalidade e as variáveis de ambiente, veja {2}.",
"commandsUpdated": "Comandos atualizados!",
"createUserDir": "Criar automaticamente a pasta de início ao adicionar um novo utilizador",
"minimumPasswordLength": "Minimum password length",
"tusUploads": "Chunked Uploads",
"tusUploadsHelp": "File Browser supports chunked file uploads, allowing for the creation of efficient, reliable, resumable and chunked file uploads even on unreliable networks.",
"tusUploadsChunkSize": "Indicates to maximum size of a request (direct uploads will be used for smaller uploads). You may input a plain integer denoting byte size input or a string like 10MB, 1GB etc.",
"tusUploadsRetryCount": "Number of retries to perform if a chunk fails to upload.",
"userHomeBasePath": "Base path for user home directories",
"userScopeGenerationPlaceholder": "The scope will be auto generated",
"createUserHomeDirectory": "Create user home directory",
"customStylesheet": "Folha de estilos personalizada",
"defaultUserDescription": "Estas são as configurações padrão para novos utilizadores.",
"disableExternalLinks": "Desativar links externos (exceto documentação)",
"disableUsedDiskPercentage": "Disable used disk percentage graph",
"documentation": "documentação",
"examples": "Exemplos",
"executeOnShell": "Executar na shell",
"executeOnShellDescription": "Por padrão, o Navegador de Ficheiros executa os comandos chamando os seus binários diretamente. Se em vez disso, quiser executá-los numa shell (como Bash ou PowerShell), pode definir isso aqui com os argumentos e bandeiras necessários. Se definido, o comando que executa será anexado como um argumento. Isto aplica-se tanto a comandos do utilizador como a hooks de eventos.",
"globalRules": "Isto é um conjunto global de regras de permissão e negação. Elas aplicam-se a todos os utilizadores. Pode especificar regras específicas para cada configuração do utilizador para sobreporem-se a estas.",
"globalSettings": "Configurações globais",
"hideDotfiles": "Hide dotfiles",
"insertPath": "Inserir o caminho",
"insertRegex": "Inserir expressão regular",
"instanceName": "Nome da instância",
"language": "Linguagem",
"lockPassword": "Não permitir que o utilizador altere a palavra-passe",
"newPassword": "Nova palavra-passe",
"newPasswordConfirm": "Confirme a nova palavra-passe",
"newUser": "Novo utilizador",
"password": "Palavra-passe",
"passwordUpdated": "Palavra-passe atualizada!",
"path": "Path",
"perm": {
"create": "Criar ficheiros e pastas",
"delete": "Eliminar ficheiros e pastas",
"download": "Descarregar",
"execute": "Executar comandos",
"modify": "Editar ficheiros",
"rename": "Alterar o nome ou mover ficheiros e pastas",
"share": "Share files (require download permission)"
},
"permissions": "Permissões",
"permissionsHelp": "Pode definir o utilizador como administrador ou escolher as permissões manualmente. Se selecionar a opção \"Administrador\", todas as outras opções serão automaticamente selecionadas. A gestão dos utilizadores é um privilégio restringido aos administradores.\n",
"profileSettings": "Configurações do utilizador",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "previne o acesso a qualquer \"dotfile\" (como .git, .gitignore) em qualquer pasta\n",
"ruleExample2": "bloqueia o acesso ao ficheiro chamado Caddyfile na raiz.",
"rules": "Regras",
"rulesHelp": "Aqui pode definir um conjunto de regras para permitir ou bloquear o acesso do utilizador a determinados ficheiros ou pastas. Os ficheiros bloqueados não irão aparecer durante a navegação. Suportamos expressões regulares e os caminhos dos ficheiros devem ser relativos à base do utilizador.\n",
"scope": "Base",
"setDateFormat": "Set exact date format",
"settingsUpdated": "Configurações atualizadas!",
"shareDuration": "Share Duration",
"shareManagement": "Share Management",
"shareDeleted": "Share deleted!",
"singleClick": "Use single clicks to open files and directories",
"themes": {
"default": "System default",
"dark": "Dark",
"light": "Light",
"title": "Theme"
},
"user": "Utilizador",
"userCommands": "Comandos",
"userCommandsHelp": "Uma lista, separada com espaços, de comandos disponíveis para este utilizados. Exemplo:",
"userCreated": "Utilizador criado!",
"userDefaults": "Configurações padrão do utilizador",
"userDeleted": "Utilizador eliminado!",
"userManagement": "Gestão de utilizadores",
"userUpdated": "Utilizador atualizado!",
"username": "Nome de utilizador",
"users": "Utilizadores",
"currentPassword": "Your Current Password"
},
"sidebar": {
"diskUsed": "{used} of {total} used",
"help": "Ajuda",
"hugoNew": "Hugo New",
"login": "Entrar",
"logout": "Sair",
"myFiles": "Meus ficheiros",
"newFile": "Novo ficheiro",
"newFolder": "Nova pasta",
"preview": "Pré-visualizar",
"settings": "Configurações",
"signup": "Registar",
"siteSettings": "Configurações do site"
},
"success": {
"linkCopied": "Link copiado!"
},
"time": {
"days": "Dias",
"hours": "Horas",
"minutes": "Minutos",
"seconds": "Segundos",
"unit": "Unidades de tempo"
}
}
@@ -4,6 +4,7 @@ import { files as api } from "@/api";
vi.mock("@/api", () => ({
files: {
fetch: vi.fn(),
fetchAll: vi.fn(),
},
}));
@@ -19,8 +20,8 @@ vi.mock("@/stores/layout", () => ({ useLayoutStore: vi.fn() }));
vi.mock("@/stores/upload", () => ({ useUploadStore: vi.fn() }));
vi.mock("@/utils/url", () => ({ default: {} }));
// A move/copy/drag item carries `name` (raw) and `to` (URL-encoded) but no
// `fullPath` — mirroring what Move.vue / Copy.vue / ListingItem.vue build.
// A move/copy/drag item carries name (raw) and to (URL-encoded) but no
// fullPath - mirroring what Move.vue / Copy.vue / ListingItem.vue build.
function moveItem(name: string, dest: string, size = 12) {
return {
from: `/files/source/${encodeURIComponent(name)}`,
@@ -167,29 +168,46 @@ describe("checkConflict", () => {
expect(conflicts[0].name).toBe("/target/folder/deep/file.txt");
});
// Copy/move stats the whole destination, so a same-named directory is a
// conflict in its own right (regression for the directory case of #5957).
it("reports a directory conflict for copy/move (includeDirectories)", async () => {
vi.mocked(api.fetchAll).mockResolvedValue([
{
path: "/target/folder",
name: "folder",
size: 0,
modified: "2026-06-04T00:00:00Z",
isDir: true,
},
]);
// Copy/move only needs the target directory's direct children. A recursive
// walk can make the UI look frozen on large destinations (regression #6005).
it("checks only the direct destination listing for copy/move", async () => {
vi.mocked(api.fetch).mockResolvedValue({
items: [
{
path: "/target/file.txt",
name: "file.txt",
size: 10,
modified: "2026-06-04T00:00:00Z",
isDir: false,
},
{
path: "/target/folder",
name: "folder",
size: 0,
modified: "2026-06-04T00:00:00Z",
isDir: true,
},
],
} as Resource);
const items = [{ ...moveItem("folder", "/files/target/", 0), isDir: true }];
const items = [
moveItem("file.txt", "/files/target/"),
{ ...moveItem("folder", "/files/target/", 0), isDir: true },
];
const conflicts = await checkConflict(items, "/files/target/", true);
expect(conflicts).toHaveLength(1);
expect(conflicts[0].name).toBe("/target/folder");
expect(api.fetch).toHaveBeenCalledWith("/files/target/");
expect(api.fetchAll).not.toHaveBeenCalled();
expect(conflicts).toHaveLength(2);
expect(conflicts.map((conflict) => conflict.name)).toEqual([
"/target/file.txt",
"/target/folder",
]);
});
// Uploads merge into an existing folder, so the directory itself must not be
// reported — only the files inside it can conflict.
// reported - only the files inside it can conflict.
it("ignores a directory conflict for uploads (default)", async () => {
vi.mocked(api.fetchAll).mockResolvedValue([
{
+58 -25
View File
@@ -21,20 +21,61 @@ function conflictKey(item: UploadEntry): string {
return (item.fullPath || item.name).replace(/^\/+/, "");
}
type ServerConflictEntry = {
path: string;
name: string;
size: number;
modified: string;
};
async function fetchConflictEntries(
basePath: string,
includeDirectories: boolean
): Promise<ServerConflictEntry[]> {
if (!includeDirectories) {
return await api.fetchAll(basePath);
}
const destination = await api.fetch(basePath);
return destination.items ?? [];
}
function conflictPath(entry: ServerConflictEntry): string {
return entry.path.replace(/\\/g, "/");
}
function buildConflictMap(
serverEntries: ServerConflictEntry[],
basePath: string,
includeDirectories: boolean
): Map<string, ServerConflictEntry> {
const serverMap = new Map<string, ServerConflictEntry>();
const normBase = removePrefix(basePath).replace(/\/+$/, "");
for (const entry of serverEntries) {
// A Windows server may return OS-native backslash separators; normalize to
// forward slashes so the prefix strip and key lookup line up.
const path = conflictPath(entry);
const key = includeDirectories
? entry.name
: path.startsWith(normBase)
? path.slice(normBase.length)
: path;
serverMap.set(key.replace(/^\/+/, ""), entry);
}
return serverMap;
}
/**
* Return the entries from `files` that already exist under `basePath` on the
* server, so the caller can prompt the user to overwrite/rename/skip.
*
* The whole destination tree is fetched once and indexed by path relative to
* the destination, then every entry is looked up directly — no need to mirror
* the upload's folder structure.
*
* Directory handling differs by action, hence `includeDirectories`:
* - Upload (false): an existing folder is silently merged, so only the
* individual files inside it can conflict.
* - Copy/move (true): the server stats the destination and rejects it whole if
* a same-named entry exists, so the directory itself is a conflict. The list
* only holds the top-level items being moved, so each is reported once.
* - Upload (false): the destination tree is fetched recursively so nested
* file uploads can be checked; existing folders are silently merged.
* - Copy/move (true): only the destination directory itself is fetched. These
* operations move flat top-level selections, so a same-named direct child is
* the only preflight conflict the backend will reject.
*
* @param files - flat upload list to check
* @param basePath - server destination path (e.g. "/files/uploads/")
@@ -47,27 +88,19 @@ export async function checkConflict(
): Promise<ConflictingResource[]> {
if (files.length === 0) return [];
let serverEntries: RecursiveEntry[];
let serverEntries: ServerConflictEntry[];
try {
// Single API call: fetch the entire server tree under basePath.
serverEntries = await api.fetchAll(basePath);
serverEntries = await fetchConflictEntries(basePath, includeDirectories);
} catch {
// The destination doesn't exist yet, so nothing can conflict.
return [];
}
// The server returns paths absolute within the user's scope
// (e.g. "/uploads/sub/file.txt"). Strip the basePath prefix so the keys line
// up with each entry's conflictKey, which is relative to the destination.
const normBase = removePrefix(basePath).replace(/\/+$/, "");
const serverMap = new Map<string, RecursiveEntry>();
for (const entry of serverEntries) {
// A Windows server may return OS-native backslash separators; normalize to
// forward slashes so the prefix strip and key lookup line up.
const path = entry.path.replace(/\\/g, "/");
const rel = path.startsWith(normBase) ? path.slice(normBase.length) : path;
serverMap.set(rel.replace(/^\/+/, ""), entry);
}
const serverMap = buildConflictMap(
serverEntries,
basePath,
includeDirectories
);
const conflicts: ConflictingResource[] = [];
files.forEach((file, index) => {
@@ -78,7 +111,7 @@ export async function checkConflict(
conflicts.push({
index,
name: server.path,
name: conflictPath(server),
origin: { lastModified: file.file?.lastModified, size: file.size },
dest: { lastModified: server.modified, size: server.size },
checked: ["origin"],
+15 -15
View File
@@ -4,38 +4,38 @@ go 1.25.0
require (
github.com/asdine/storm/v3 v3.2.1
github.com/asticode/go-astisub v0.40.0
github.com/asticode/go-astisub v0.42.0
github.com/disintegration/imaging v1.6.2
github.com/dsoprea/go-exif/v3 v3.0.1
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/gorilla/mux v1.8.1
github.com/gorilla/websocket v1.5.3
github.com/jellydator/ttlcache/v3 v3.4.0
github.com/jellydator/ttlcache/v3 v3.4.1
github.com/maruel/natural v1.3.0
github.com/marusama/semaphore/v2 v2.5.0
github.com/mholt/archives v0.1.5
github.com/mitchellh/go-homedir v1.1.0
github.com/redis/go-redis/v9 v9.20.1
github.com/redis/go-redis/v9 v9.21.0
github.com/samber/lo v1.53.0
github.com/shirou/gopsutil/v4 v4.26.5
github.com/shirou/gopsutil/v4 v4.26.6
github.com/spf13/afero v1.15.0
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
github.com/spf13/viper v1.21.0
github.com/stretchr/testify v1.11.1
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
go.etcd.io/bbolt v1.4.3
golang.org/x/crypto v0.53.0
golang.org/x/image v0.42.0
golang.org/x/text v0.38.0
go.etcd.io/bbolt v1.5.0
golang.org/x/crypto v0.54.0
golang.org/x/image v0.44.0
golang.org/x/text v0.40.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/STARRY-S/zip v0.2.3 // indirect
github.com/andybalholm/brotli v1.2.1 // indirect
github.com/andybalholm/brotli v1.2.2 // indirect
github.com/asticode/go-astikit v0.59.0 // indirect
github.com/asticode/go-astits v1.15.0 // indirect
github.com/bodgit/plumbing v1.3.0 // indirect
@@ -52,16 +52,16 @@ require (
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/golang/geo v0.0.0-20260612074446-f1a45663b0f3 // indirect
github.com/golang/geo v0.0.0-20260625163123-7c0e84413537 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/klauspost/compress v1.18.6 // indirect
github.com/klauspost/compress v1.19.0 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/mikelolasagasti/xz v1.0.1 // indirect
github.com/minio/minlz v1.1.1 // indirect
github.com/nwaples/rardecode/v2 v2.2.3 // indirect
github.com/pelletier/go-toml/v2 v2.3.1 // indirect
github.com/nwaples/rardecode/v2 v2.2.5 // indirect
github.com/pelletier/go-toml/v2 v2.4.2 // indirect
github.com/pierrec/lz4/v4 v4.1.27 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
@@ -77,8 +77,8 @@ require (
go.yaml.in/yaml/v3 v3.0.4 // indirect
go4.org v0.0.0-20260112195520-a5071408f32f // indirect
golang.org/x/net v0.56.0 // indirect
golang.org/x/sync v0.21.0 // indirect
golang.org/x/sys v0.46.0 // indirect
golang.org/x/sync v0.22.0 // indirect
golang.org/x/sys v0.47.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
+30 -30
View File
@@ -4,16 +4,16 @@ github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
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/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro=
github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/andybalholm/brotli v1.2.2 h1:HzTuoo2ErYQqf5qvcJInB8uvqSVxRttzkFexPWtnceM=
github.com/andybalholm/brotli v1.2.2/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
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/asticode/go-astikit v0.20.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
github.com/asticode/go-astikit v0.30.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
github.com/asticode/go-astikit v0.59.0 h1:tjbwDym+MTSxqkAhJoHRZmHMXK6Jv4vGx+97FptKH6k=
github.com/asticode/go-astikit v0.59.0/go.mod h1:fV43j20UZYfXzP9oBn33udkvCvDvCDhzjVqoLFuuYZE=
github.com/asticode/go-astisub v0.40.0 h1:Z8tAXHngjkh/4L9zqt49ru9UdpDTqBIe+80xexKM3/I=
github.com/asticode/go-astisub v0.40.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8=
github.com/asticode/go-astisub v0.42.0 h1:IJuJJTQwCSFeY9bx1kTzdCwkIeG27wj4CAh6nHc+pvY=
github.com/asticode/go-astisub v0.42.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8=
github.com/asticode/go-astits v1.8.0/go.mod h1:DkOWmBNQpnr9mv24KfZjq4JawCFX1FCqjLVGvO0DygQ=
github.com/asticode/go-astits v1.15.0 h1:yRyCiUc8Jj4F7clt2GDxHghMpWuFL5rkaLuGUd2/0J4=
github.com/asticode/go-astits v1.15.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI=
@@ -82,8 +82,8 @@ github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArs
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-20260612074446-f1a45663b0f3 h1:UlucSQUu9SZdDRlMWv5N/T3Rig9gv615vWvHFKrXHWA=
github.com/golang/geo v0.0.0-20260612074446-f1a45663b0f3/go.mod h1:Mymr9kRGDc64JPr03TSZmuIBODZ3KyswLzm1xL0HFA8=
github.com/golang/geo v0.0.0-20260625163123-7c0e84413537 h1:KeIaDS/+VEy/bhDYjG3Z78dOyLAU4HXcVxmd0WYHJTE=
github.com/golang/geo v0.0.0-20260625163123-7c0e84413537/go.mod h1:Mymr9kRGDc64JPr03TSZmuIBODZ3KyswLzm1xL0HFA8=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -101,13 +101,13 @@ github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY=
github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4=
github.com/jellydator/ttlcache/v3 v3.4.1 h1:bOdXmXiycyK6E6Qjyuj5vl+/vU3SCOoDs8a86NbHjAQ=
github.com/jellydator/ttlcache/v3 v3.4.1/go.mod h1:j7LO12PNghFg5+0v9budMAT4rDK4JY969jb9vOdOBBk=
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/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/compress v1.19.0 h1:sXLILfc9jV2QYWkzFOPWStmcUVH2RHEB1JCdY2oVvCQ=
github.com/klauspost/compress v1.19.0/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
@@ -134,10 +134,10 @@ github.com/minio/minlz v1.1.1 h1:OGmft1V6AnI/Wme332U6bhG54nxEan+VFgkD7lat4KM=
github.com/minio/minlz v1.1.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
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/nwaples/rardecode/v2 v2.2.3 h1:qaVuy3ChZDbAQZshPLjHeNJKF3Cru8uo9jmgveKIy2A=
github.com/nwaples/rardecode/v2 v2.2.3/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc=
github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/nwaples/rardecode/v2 v2.2.5 h1:L5doqgGfQwI7qADJMqnkrSB86rpPsqQDrHeO0HWa5JY=
github.com/nwaples/rardecode/v2 v2.2.5/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
github.com/pelletier/go-toml/v2 v2.4.2 h1:M2fKKbmyvI+hGId/D0W64qDBMVhJnNR10O5gIbMc//Q=
github.com/pelletier/go-toml/v2 v2.4.2/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pierrec/lz4/v4 v4.1.27 h1:+PhzhWDrjRj89TH2sw43nE3+4+W8lSxIuQadEHZyjUk=
github.com/pierrec/lz4/v4 v4.1.27/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE=
@@ -146,8 +146,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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/redis/go-redis/v9 v9.20.1 h1:sfCU6A8P3dXbKyWes02uxA2baehGux9dZHfEKtsTB1w=
github.com/redis/go-redis/v9 v9.20.1/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA=
github.com/redis/go-redis/v9 v9.21.0 h1:FPBE4hhbAke+TLmcY3WkpbDffJEomdqPn3HYiqAtL9E=
github.com/redis/go-redis/v9 v9.21.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
@@ -156,8 +156,8 @@ github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88ee
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM=
github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/shirou/gopsutil/v4 v4.26.5 h1:RPcBXkpz7kOj9PqGFQOlBPZHsyaPvPVQc098y9RmCNM=
github.com/shirou/gopsutil/v4 v4.26.5/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
github.com/shirou/gopsutil/v4 v4.26.6 h1:Mzr/npDtQC/xpeEuQKHZt8Zo9CmPvhTj8nkR8w5TLDs=
github.com/shirou/gopsutil/v4 v4.26.6/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
@@ -201,8 +201,8 @@ github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQ
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
go.etcd.io/bbolt v1.5.0 h1:S7GAl7Fxv12yohbwFfIbQCGDWbQbtDGPET4P/bD4lxU=
go.etcd.io/bbolt v1.5.0/go.mod h1:mkltfYE5aUHQxUct9N9V+Kp7aSjFqjgrhcXIS70Lrdk=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@@ -213,11 +213,11 @@ go4.org v0.0.0-20260112195520-a5071408f32f h1:ziUVAjmTPwQMBmYR1tbdRFJPtTcQUI12fH
go4.org v0.0.0-20260112195520-a5071408f32f/go.mod h1:ZRJnO5ZI4zAwMFp+dS1+V6J6MSyAowhRqAE+DPa1Xp0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.42.0 h1:1gSs6ehNWXLbkHBIPcWztk3D/6aIA/8hauiAYtlodVY=
golang.org/x/image v0.42.0/go.mod h1:rrpelvGFt+kLPAjPM4HeWPgrl0FtafueU//e5N0qk/Q=
golang.org/x/image v0.44.0 h1:+tDekMZED9+LrtB3G5xzRggpVh9CARjZqROla3R3R+I=
golang.org/x/image v0.44.0/go.mod h1:V8K3KE9KKKE+pLpQDOeN18w9oacNSvy1tDOirTu4xtY=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20191105084925-a882066a44e0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -229,8 +229,8 @@ golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -242,14 +242,14 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
+4
View File
@@ -46,6 +46,10 @@ The Hook Authentication method in FileBrowser allows developers to delegate user
The hook’s output controls user permissions, scope, locale, and other attributes, making it a powerful and extensible authentication mechanism.
> [!WARNING]
>
> The submitted username and password are attacker-controlled and are handed to your hook command as the `USERNAME` and `PASSWORD` environment variables. File Browser runs the command directly, without a shell, so the values themselves are inert. However, your script must treat them as untrusted: always quote them (`"$USERNAME"`, `"$PASSWORD"`) and never pass them unquoted to a shell, `eval`, `bash -c`, command substitution, or backticks. A hook script that shell-evaluates these values turns any login request into remote code execution.
For example, the following code delegates filebrowser authentication to a PowerShell script on Windows. You can configure any command (for example, a script in Python, Node.js, etc.).
```sh