Merge remote-tracking branch 'upstream/master' into fixes-13

pull/5270/head
Ramires Viana 2025-07-10 17:41:55 -03:00
commit c2c696c212
16 changed files with 168 additions and 120 deletions

View File

@ -131,7 +131,7 @@ dockers:
- "filebrowser/filebrowser:v{{ .Major }}-amd64-s6" - "filebrowser/filebrowser:v{{ .Major }}-amd64-s6"
extra_files: extra_files:
- docker - docker
- dockerfile: Dockerfile.s6.aarch64 - dockerfile: Dockerfile.s6
use: buildx use: buildx
build_flag_templates: build_flag_templates:
- "--pull" - "--pull"

View File

@ -2,6 +2,43 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [2.37.0](https://github.com/filebrowser/filebrowser/compare/v2.36.3...v2.37.0) (2025-07-08)
### Features
* Translate frontend/src/i18n/en.json in zh_CN ([65bbf44](https://github.com/filebrowser/filebrowser/commit/65bbf44e3c0bff83e64193d46e9d6ad302952276))
* Translate frontend/src/i18n/en.json in zh_TW ([b28952c](https://github.com/filebrowser/filebrowser/commit/b28952cb2582bd4eb44e91d0676e2803c458cf31))
* Translate frontend/src/i18n/en.json in zh_TW ([1e96fd9](https://github.com/filebrowser/filebrowser/commit/1e96fd9035d5185dc80970a2826ccb573b5f000e))
### Bug Fixes
* long file name overlap ([fcb248a](https://github.com/filebrowser/filebrowser/commit/fcb248a5feb7b7404ca5923aae17f6d3f8d3cc96))
* preview PDF is correctly displayed ([bf73e4d](https://github.com/filebrowser/filebrowser/commit/bf73e4dea3b27c01c8f6e60fb2048e1a2122a70e))
* Upload progress size calculation ([e423395](https://github.com/filebrowser/filebrowser/commit/e423395ef0bcd106ddc7d460c055b95b5208415e))
### [2.36.3](https://github.com/filebrowser/filebrowser/compare/v2.36.2...v2.36.3) (2025-07-06)
### Bug Fixes
* log error if branding file exists but cannot be loaded ([3645b57](https://github.com/filebrowser/filebrowser/commit/3645b578cddb9fc8f25a00e0153fb600ad1b9266))
### [2.36.2](https://github.com/filebrowser/filebrowser/compare/v2.36.1...v2.36.2) (2025-07-06)
### Bug Fixes
* lookup directory name if blank when downloading shared directory ([046d619](https://github.com/filebrowser/filebrowser/commit/046d6193c57b4df0e3dc583b6518b43d29d302c9))
### [2.36.1](https://github.com/filebrowser/filebrowser/compare/v2.36.0...v2.36.1) (2025-07-03)
### Bug Fixes
* remove associated shares when deleting file/folder ([e99e0b3](https://github.com/filebrowser/filebrowser/commit/e99e0b3028e1c8a50e1744bb07ecc8e809bdb8e6))
## [2.36.0](https://github.com/filebrowser/filebrowser/compare/v2.35.0...v2.36.0) (2025-07-02) ## [2.36.0](https://github.com/filebrowser/filebrowser/compare/v2.35.0...v2.36.0) (2025-07-02)

View File

@ -1,23 +0,0 @@
FROM ghcr.io/linuxserver/baseimage-alpine:arm64v8-3.22
RUN apk update && \
apk --no-cache add ca-certificates mailcap curl jq
# Make user and create necessary directories
RUN mkdir -p /config /database /srv && \
chown -R abc:abc /config /database /srv
# Copy files and set permissions
COPY filebrowser /bin/filebrowser
COPY docker/common/ /
COPY docker/s6/ /
RUN chown -R abc:abc /bin/filebrowser /defaults healthcheck.sh
# Define healthcheck script
HEALTHCHECK --start-period=2s --interval=5s --timeout=3s CMD /healthcheck.sh
# Set the volumes and exposed ports
VOLUME /srv /config /database
EXPOSE 80

View File

@ -195,10 +195,6 @@ html[dir="rtl"] #listing {
align-items: center; align-items: center;
} }
#listing.list .item p.name:not(#listing.list .item.header .name) {
margin-right: -3em;
}
#listing.list .item .name { #listing.list .item .name {
width: 50%; width: 50%;
} }
@ -227,18 +223,18 @@ html[dir="rtl"] #listing {
border-bottom: 1px solid var(--borderPrimary); border-bottom: 1px solid var(--borderPrimary);
} }
#listing.list .item.header > div:first-child { #listing.list .item.header > div {
width: 0; width: 100%;
}
#listing.list .item.header .name {
margin-right: 3em;
} }
#listing.list .header a { #listing.list .header a {
color: inherit; color: inherit;
} }
#listing.list .item.header > div:first-child {
width: 0;
}
#listing.list .name { #listing.list .name {
font-weight: normal; font-weight: normal;
word-wrap: break-word; word-wrap: break-word;

View File

@ -3,17 +3,17 @@
"cancel": "Відмінити", "cancel": "Відмінити",
"clear": "Очистити", "clear": "Очистити",
"close": "Закрити", "close": "Закрити",
"continue": "Continue", "continue": "Продовжити",
"copy": "Копіювати", "copy": "Копіювати",
"copyFile": "Копіювати файл", "copyFile": "Копіювати файл",
"copyToClipboard": "Копіювати в буфер обміну", "copyToClipboard": "Копіювати в буфер обміну",
"copyDownloadLinkToClipboard": "Copy download link to clipboard", "copyDownloadLinkToClipboard": "Скопіювати завантажувальне посилання в буфер обміну",
"create": "Створити", "create": "Створити",
"delete": "Видалити", "delete": "Видалити",
"download": "Завантажити", "download": "Завантажити",
"file": "Файл", "file": "Файл",
"folder": "Папка", "folder": "Папка",
"fullScreen": "Toggle full screen", "fullScreen": "Перемкнути повноекранний режим",
"hideDotfiles": "Приховати точкові файли", "hideDotfiles": "Приховати точкові файли",
"info": "Інфо", "info": "Інфо",
"more": "Більше", "more": "Більше",
@ -24,7 +24,7 @@
"ok": "ОК", "ok": "ОК",
"permalink": "Отримати постійне посилання", "permalink": "Отримати постійне посилання",
"previous": "Назад", "previous": "Назад",
"preview": "Preview", "preview": "Попередній перегляд",
"publish": "Опублікувати", "publish": "Опублікувати",
"rename": "Перейменувати", "rename": "Перейменувати",
"replace": "Замінити", "replace": "Замінити",
@ -42,7 +42,7 @@
"update": "Оновити", "update": "Оновити",
"upload": "Вивантажити", "upload": "Вивантажити",
"openFile": "Відкрити файл", "openFile": "Відкрити файл",
"discardChanges": "Discard" "discardChanges": "Скасувати"
}, },
"download": { "download": {
"downloadFile": "Завантажити файл", "downloadFile": "Завантажити файл",
@ -50,7 +50,7 @@
"downloadSelected": "Завантажити вибране" "downloadSelected": "Завантажити вибране"
}, },
"upload": { "upload": {
"abortUpload": "Are you sure you wish to abort?" "abortUpload": "Ви впевнені, що хочете перервати?"
}, },
"errors": { "errors": {
"forbidden": "У вас немає прав доступу до цього.", "forbidden": "У вас немає прав доступу до цього.",
@ -66,7 +66,7 @@
"home": "Домівка", "home": "Домівка",
"lastModified": "Останній раз змінено", "lastModified": "Останній раз змінено",
"loading": "Завантаження...", "loading": "Завантаження...",
"lonely": "Тут пусто...", "lonely": "Тут порожньо...",
"metadata": "Метадані", "metadata": "Метадані",
"multipleSelectionEnabled": "Мультивибір включений", "multipleSelectionEnabled": "Мультивибір включений",
"name": "Ім'я", "name": "Ім'я",
@ -81,7 +81,7 @@
"ctrl": { "ctrl": {
"click": "вибрати кілька файлів чи каталогів", "click": "вибрати кілька файлів чи каталогів",
"f": "відкрити пошук", "f": "відкрити пошук",
"s": "скачати файл або поточний каталог" "s": "завантажити файл або поточний каталог"
}, },
"del": "видалити вибрані елементи", "del": "видалити вибрані елементи",
"doubleClick": "відкрити файл чи каталог", "doubleClick": "відкрити файл чи каталог",
@ -100,7 +100,7 @@
"submit": "Увійти", "submit": "Увійти",
"username": "Ім'я користувача", "username": "Ім'я користувача",
"usernameTaken": "Ім'я користувача вже використовується", "usernameTaken": "Ім'я користувача вже використовується",
"wrongCredentials": "Невірне ім'я користувача або пароль" "wrongCredentials": "Неправильне ім'я користувача або пароль"
}, },
"permanent": "Постійний", "permanent": "Постійний",
"prompts": { "prompts": {
@ -110,7 +110,7 @@
"deleteMessageMultiple": "Видалити ці файли ({count})?", "deleteMessageMultiple": "Видалити ці файли ({count})?",
"deleteMessageSingle": "Видалити цей файл/каталог?", "deleteMessageSingle": "Видалити цей файл/каталог?",
"deleteMessageShare": "Видалити цей спільний файл/каталог ({path})?", "deleteMessageShare": "Видалити цей спільний файл/каталог ({path})?",
"deleteUser": "Are you sure you want to delete this user?", "deleteUser": "Видалити цього користувача?",
"deleteTitle": "Видалити файли", "deleteTitle": "Видалити файли",
"displayName": "Відображене ім'я:", "displayName": "Відображене ім'я:",
"download": "Завантажити файли", "download": "Завантажити файли",
@ -137,11 +137,11 @@
"show": "Показати", "show": "Показати",
"size": "Розмір", "size": "Розмір",
"upload": "Вивантажити", "upload": "Вивантажити",
"uploadFiles": "Uploading {files} files...", "uploadFiles": "Вивантаження {files} файлів...",
"uploadMessage": "Виберіть варіант для вивантаження.", "uploadMessage": "Виберіть варіант для вивантаження.",
"optionalPassword": "Необов'язковий пароль", "optionalPassword": "Необов'язковий пароль",
"resolution": "Resolution", "resolution": "Розширення",
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?" "discardEditorChanges": "Чи дійсно ви хочете скасувати поточні зміни?"
}, },
"search": { "search": {
"images": "Зображення", "images": "Зображення",
@ -170,14 +170,14 @@
"commandRunnerHelp": "Тут ви можете встановити команди, які будуть виконуватися у зазначених подіях. Ви повинні вказати по одній команді в кожному рядку. Змінні середовища {0} та {1} будуть доступні, будучи {0} щодо {1}. Додаткові відомості про цю функцію та доступні змінні середовища див. у {2}.", "commandRunnerHelp": "Тут ви можете встановити команди, які будуть виконуватися у зазначених подіях. Ви повинні вказати по одній команді в кожному рядку. Змінні середовища {0} та {1} будуть доступні, будучи {0} щодо {1}. Додаткові відомості про цю функцію та доступні змінні середовища див. у {2}.",
"commandsUpdated": "Команди оновлені!", "commandsUpdated": "Команди оновлені!",
"createUserDir": "Автоматичне створення домашнього каталогу користувача при додаванні нового користувача", "createUserDir": "Автоматичне створення домашнього каталогу користувача при додаванні нового користувача",
"minimumPasswordLength": "Minimum password length", "minimumPasswordLength": "Мінімальна довжина паролю",
"tusUploads": "Chunked Uploads", "tusUploads": "Фрагментовані завантаження",
"tusUploadsHelp": "File Browser supports chunked file uploads, allowing for the creation of efficient, reliable, resumable and chunked file uploads even on unreliable networks.", "tusUploadsHelp": "File Browser підтримує завантаження частинами, дозволяючи створення ефективних, надійних, відновлюваних та фрагментованих завантажень навіть при ненадійному з'єднанні.",
"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.", "tusUploadsChunkSize": "Вказує на максимальний розмір запиту (для менших завантажень використовуватиметься пряме завантаження). Ви можете ввести цілочисельне значення у байтах або ж рядок на кшталт 10MB, 1GB тощо.",
"tusUploadsRetryCount": "Number of retries to perform if a chunk fails to upload.", "tusUploadsRetryCount": "Кількість повторних спроб які потрібно виконати, якщо фрагмент не вдалося завантажити.",
"userHomeBasePath": "Base path for user home directories", "userHomeBasePath": "Основний шлях для домашніх каталогів користувачів",
"userScopeGenerationPlaceholder": "The scope will be auto generated", "userScopeGenerationPlaceholder": "Кореневий каталог буде згенеровано автоматично",
"createUserHomeDirectory": "Create user home directory", "createUserHomeDirectory": "Створити домашній каталог користувача",
"customStylesheet": "Свій стиль", "customStylesheet": "Свій стиль",
"defaultUserDescription": "Це налаштування за замовчуванням для нових користувачів.", "defaultUserDescription": "Це налаштування за замовчуванням для нових користувачів.",
"disableExternalLinks": "Вимкнути зовнішні посилання (крім документації)", "disableExternalLinks": "Вимкнути зовнішні посилання (крім документації)",
@ -210,12 +210,12 @@
"share": "Ділітися файлами" "share": "Ділітися файлами"
}, },
"permissions": "Дозволи", "permissions": "Дозволи",
"permissionsHelp": "Можна настроїти користувача як адміністратора або вибрати індивідуальні дозволи. При виборі \"Адміністратор\" всі інші параметри будуть автоматично вибрані. Керування користувачами - привілей адміністратора.\n", "permissionsHelp": "Можна налаштувати користувача як адміністратора чи вибрати індивідуальні дозволи. При виборі \"Адміністратор\" всі інші параметри будуть автоматично вибрані. Керування користувачами - привілей адміністратора.\n",
"profileSettings": "Налаштування профілю", "profileSettings": "Налаштування профілю",
"ruleExample1": "запобігти доступу до будь-якого прихованого файлу (наприклад: .git, .gitignore) у кожній папці.\n", "ruleExample1": "запобігти доступу до будь-якого прихованого файлу (наприклад: .git, .gitignore) у кожній папці.\n",
"ruleExample2": "блокує доступ до файлу з ім'ям Caddyfile у кореневій області.", "ruleExample2": "блокує доступ до файлу з ім'ям Caddyfile у кореневій області.",
"rules": "Права", "rules": "Права",
"rulesHelp": "Тут ви можете визначити набір дозволяючих та забороняючих правил для цього конкретного користувача. Блоковані файли не відображатимуться у списках, і не будуть доступні для користувача. Є підтримка регулярних виразів та відносних шляхів.\n", "rulesHelp": "Тут ви можете визначити набір дозволів та заборон для цього конкретного користувача. Блоковані файли не відображатимуться у списках і не будуть доступними для користувача. Є підтримка регулярних виразів та відносних шляхів.\n",
"scope": "Корінь", "scope": "Корінь",
"setDateFormat": "Встановити точний формат дати", "setDateFormat": "Встановити точний формат дати",
"settingsUpdated": "Налаштування застосовані!", "settingsUpdated": "Налаштування застосовані!",
@ -224,7 +224,7 @@
"shareDeleted": "Спільне посилання видалено!", "shareDeleted": "Спільне посилання видалено!",
"singleClick": "Відкриття файлів та каталогів одним кліком", "singleClick": "Відкриття файлів та каталогів одним кліком",
"themes": { "themes": {
"default": "System default", "default": "За замовчуванням (системна)",
"dark": "Темна", "dark": "Темна",
"light": "Світла", "light": "Світла",
"title": "Тема" "title": "Тема"
@ -232,11 +232,11 @@
"user": "Користувач", "user": "Користувач",
"userCommands": "Команди", "userCommands": "Команди",
"userCommandsHelp": "Список команд, доступних користувачу, розділений пробілами. Приклад:\n", "userCommandsHelp": "Список команд, доступних користувачу, розділений пробілами. Приклад:\n",
"userCreated": "Користувач створений!", "userCreated": "Користувача створено!",
"userDefaults": "Налаштування користувача за замовчуванням", "userDefaults": "Налаштування користувача за замовчуванням",
"userDeleted": "Користувач видалений!", "userDeleted": "Користувача видалено!",
"userManagement": "Керування користувачами", "userManagement": "Керування користувачами",
"userUpdated": "Користувач змінений!", "userUpdated": "Користувача змінено!",
"username": "Ім'я користувача", "username": "Ім'я користувача",
"users": "Користувачі" "users": "Користувачі"
}, },

View File

@ -170,7 +170,7 @@
"commandRunnerHelp": "你可以在此设置在下列事件中执行的命令。每行必须写一条命令。可以在命令中使用环境变量 {0} 和 {1},使 {0} 与 {1} 相关联。关于此功能和可用环境变量的更多信息,请阅读 {2}。", "commandRunnerHelp": "你可以在此设置在下列事件中执行的命令。每行必须写一条命令。可以在命令中使用环境变量 {0} 和 {1},使 {0} 与 {1} 相关联。关于此功能和可用环境变量的更多信息,请阅读 {2}。",
"commandsUpdated": "命令已更新!", "commandsUpdated": "命令已更新!",
"createUserDir": "在添加新用户的同时自动创建用户的主目录", "createUserDir": "在添加新用户的同时自动创建用户的主目录",
"minimumPasswordLength": "Minimum password length", "minimumPasswordLength": "最小密码长度",
"tusUploads": "分块上传", "tusUploads": "分块上传",
"tusUploadsHelp": "File Browser 支持分块上传,在不佳的网络下也可进行高效、可靠、可续的文件上传", "tusUploadsHelp": "File Browser 支持分块上传,在不佳的网络下也可进行高效、可靠、可续的文件上传",
"tusUploadsChunkSize": "分块上传大小,例如 10MB 或 1GB", "tusUploadsChunkSize": "分块上传大小,例如 10MB 或 1GB",

View File

@ -24,7 +24,7 @@
"ok": "確認", "ok": "確認",
"permalink": "獲取永久連結", "permalink": "獲取永久連結",
"previous": "上一個", "previous": "上一個",
"preview": "Preview", "preview": "預覽",
"publish": "發佈", "publish": "發佈",
"rename": "重新命名", "rename": "重新命名",
"replace": "更換", "replace": "更換",
@ -170,7 +170,7 @@
"commandRunnerHelp": "在這裡你可以設定在下面的事件中執行的命令。每行必須寫一條命令。可以在命令中使用環境變數 {0} 和 {1}。關於此功能和可用環境變數的更多資訊,請閱讀{2}.", "commandRunnerHelp": "在這裡你可以設定在下面的事件中執行的命令。每行必須寫一條命令。可以在命令中使用環境變數 {0} 和 {1}。關於此功能和可用環境變數的更多資訊,請閱讀{2}.",
"commandsUpdated": "命令已更新!", "commandsUpdated": "命令已更新!",
"createUserDir": "在新增新使用者的同時自動建立使用者的個人目錄", "createUserDir": "在新增新使用者的同時自動建立使用者的個人目錄",
"minimumPasswordLength": "Minimum password length", "minimumPasswordLength": "密碼最短長度",
"tusUploads": "分塊上傳", "tusUploads": "分塊上傳",
"tusUploadsHelp": "File Browser 支援分塊上傳,在不佳的網絡環境下也可進行高效、可靠、可續的檔案上傳", "tusUploadsHelp": "File Browser 支援分塊上傳,在不佳的網絡環境下也可進行高效、可靠、可續的檔案上傳",
"tusUploadsChunkSize": "分塊上傳大小,例如 10MB 或 1GB", "tusUploadsChunkSize": "分塊上傳大小,例如 10MB 或 1GB",

View File

@ -74,7 +74,12 @@ export const useUploadStore = defineStore("upload", {
if (state.progress.length === 0 || state.sizes.length === 0) { if (state.progress.length === 0 || state.sizes.length === 0) {
return "0 Bytes"; return "0 Bytes";
} }
const sum = state.progress.reduce((acc, val) => +acc + +val, 0) as number; const sum = state.progress.reduce(
(sum, p, i) =>
(sum as number) +
(typeof p === "number" ? p : p ? state.sizes[i] : 0),
0
) as number;
return formatSize(sum); return formatSize(sum);
}, },
getTotalSize: (state) => { getTotalSize: (state) => {

View File

@ -162,7 +162,6 @@
> >
<div> <div>
<div class="item header"> <div class="item header">
<div></div>
<div> <div>
<p <p
:class="{ active: nameSorted }" :class="{ active: nameSorted }"

View File

@ -60,7 +60,7 @@
<div v-if="isEpub" class="epub-reader"> <div v-if="isEpub" class="epub-reader">
<vue-reader <vue-reader
:location="location" :location="location"
:url="raw" :url="previewUrl"
:get-rendition="getRendition" :get-rendition="getRendition"
:epubInitOptions="{ :epubInitOptions="{
requestCredentials: true, requestCredentials: true,
@ -87,11 +87,14 @@
<span>{{ size }}%</span> <span>{{ size }}%</span>
</div> </div>
</div> </div>
<ExtendedImage v-else-if="fileStore.req?.type == 'image'" :src="raw" /> <ExtendedImage
v-else-if="fileStore.req?.type == 'image'"
:src="previewUrl"
/>
<audio <audio
v-else-if="fileStore.req?.type == 'audio'" v-else-if="fileStore.req?.type == 'audio'"
ref="player" ref="player"
:src="raw" :src="previewUrl"
controls controls
:autoplay="autoPlay" :autoplay="autoPlay"
@play="autoPlay = true" @play="autoPlay = true"
@ -99,12 +102,12 @@
<VideoPlayer <VideoPlayer
v-else-if="fileStore.req?.type == 'video'" v-else-if="fileStore.req?.type == 'video'"
ref="player" ref="player"
:source="raw" :source="previewUrl"
:subtitles="subtitles" :subtitles="subtitles"
:options="videoOptions" :options="videoOptions"
> >
</VideoPlayer> </VideoPlayer>
<object v-else-if="isPdf" class="pdf" :data="raw"></object> <object v-else-if="isPdf" class="pdf" :data="previewUrl"></object>
<div v-else-if="fileStore.req?.type == 'blob'" class="info"> <div v-else-if="fileStore.req?.type == 'blob'" class="info">
<div class="title"> <div class="title">
<i class="material-icons">feedback</i> <i class="material-icons">feedback</i>
@ -119,7 +122,7 @@
</a> </a>
<a <a
target="_blank" target="_blank"
:href="raw" :href="previewUrl"
class="button button--flat" class="button button--flat"
v-if="!fileStore.req?.isDir" v-if="!fileStore.req?.isDir"
> >
@ -256,16 +259,20 @@ const downloadUrl = computed(() =>
fileStore.req ? api.getDownloadURL(fileStore.req, false) : "" fileStore.req ? api.getDownloadURL(fileStore.req, false) : ""
); );
const raw = computed(() => { const previewUrl = computed(() => {
if (fileStore.req?.type === "image" && !fullSize.value) { if (!fileStore.req) {
return "";
}
if (fileStore.req.type === "image" && !fullSize.value) {
return api.getPreviewURL(fileStore.req, "big"); return api.getPreviewURL(fileStore.req, "big");
} }
if (isEpub.value) { if (isEpub.value) {
return createURL("api/raw" + fileStore.req?.path, {}); return createURL("api/raw" + fileStore.req.path, {});
} }
return downloadUrl.value; return api.getDownloadURL(fileStore.req, true);
}); });
const isPdf = computed(() => fileStore.req?.extension.toLowerCase() == ".pdf"); const isPdf = computed(() => fileStore.req?.extension.toLowerCase() == ".pdf");

View File

@ -177,7 +177,18 @@ func rawDirHandler(w http.ResponseWriter, r *http.Request, d *data, file *files.
name := filepath.Base(commonDir) name := filepath.Base(commonDir)
if name == "." || name == "" || name == string(filepath.Separator) { if name == "." || name == "" || name == string(filepath.Separator) {
// Not sure when/if this will ever be true, though kept incase there is an edge-case where it is
if file.Name != "" {
name = file.Name name = file.Name
} else {
// This should indicate that the fs root is the directory being downloaded, lookup its name
actual, statErr := file.Fs.Stat(".")
if statErr != nil {
return http.StatusInternalServerError, statErr
}
name = actual.Name()
}
} }
// Prefix used to distinguish a filelist generated // Prefix used to distinguish a filelist generated
// archive from the full directory archive // archive from the full directory archive

View File

@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"log"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
@ -73,6 +74,11 @@ func resourceDeleteHandler(fileCache FileCache) handleFunc {
return errToStatus(err), err return errToStatus(err), err
} }
err = d.store.Share.DeleteWithPathPrefix(file.Path)
if err != nil {
log.Printf("WARNING: Error(s) occurred while deleting associated shares with file: %s", err)
}
// delete thumbnails // delete thumbnails
err = delThumbs(r.Context(), fileCache, file) err = delThumbs(r.Context(), fileCache, file)
if err != nil { if err != nil {

View File

@ -124,7 +124,10 @@ func getStaticHandlers(store *storage.Storage, server *settings.Server, assetsFs
if d.settings.Branding.Files != "" { if d.settings.Branding.Files != "" {
if strings.HasPrefix(r.URL.Path, "img/") { if strings.HasPrefix(r.URL.Path, "img/") {
fPath := filepath.Join(d.settings.Branding.Files, r.URL.Path) fPath := filepath.Join(d.settings.Branding.Files, r.URL.Path)
if _, err := os.Stat(fPath); err == nil { _, err := os.Stat(fPath)
if err != nil && !os.IsNotExist(err) {
log.Printf("could not load branding file override: %v", err)
} else if err == nil {
http.ServeFile(w, r, fPath) http.ServeFile(w, r, fPath)
return 0, nil return 0, nil
} }

View File

@ -15,6 +15,7 @@ type StorageBackend interface {
Gets(path string, id uint) ([]*Link, error) Gets(path string, id uint) ([]*Link, error)
Save(s *Link) error Save(s *Link) error
Delete(hash string) error Delete(hash string) error
DeleteWithPathPrefix(path string) error
} }
// Storage is a storage. // Storage is a storage.
@ -118,3 +119,7 @@ func (s *Storage) Save(l *Link) error {
func (s *Storage) Delete(hash string) error { func (s *Storage) Delete(hash string) error {
return s.back.Delete(hash) return s.back.Delete(hash)
} }
func (s *Storage) DeleteWithPathPrefix(path string) error {
return s.back.DeleteWithPathPrefix(path)
}

View File

@ -75,3 +75,16 @@ func (s shareBackend) Delete(hash string) error {
} }
return err return err
} }
func (s shareBackend) DeleteWithPathPrefix(pathPrefix string) error {
var links []share.Link
if err := s.db.Prefix("Path", pathPrefix, &links); err != nil {
return err
}
var err error
for _, link := range links {
err = errors.Join(err, s.db.DeleteStruct(&share.Link{Hash: link.Hash}))
}
return err
}

View File

@ -32,24 +32,30 @@ File Browser is now up and running. Read some [first boot](#first-boot) for more
## Docker ## Docker
File Browser is available as two different Docker images, which can be found on [Docker Hub](https://hub.docker.com/r/filebrowser/filebrowser). File Browser is available as two different Docker images, which can be found on [Docker Hub](https://hub.docker.com/r/filebrowser/filebrowser): a [bare Alpine image](#bare-alpine-image) and an [S6 Overlay image](#s6-overlay-image).
=== "Alpine" ### Bare Alpine Image
The
```sh ```sh
docker run \ docker run \
-v /path/to/srv:/srv \ -v filebrowser_data:/srv \
-v /path/to/database:/database \ -v filebrowser_database:/database \
-v /path/to/config:/config \ -v filebrowser_config:/config \
-p 8080:80 \ -p 8080:80 \
filebrowser/filebrowser filebrowser/filebrowser
``` ```
The default user has PID 1000 and GID 1000. Please make sure that this user has access to the different mounted volumes. To change the user running inside the Docker image, you need to use the [`--user` flag](https://docs.docker.com/engine/containers/run/#user). Where `filebrowser_data`, `filebrowser_database` and `filebrowser_config` are Docker [volumes](https://docs.docker.com/engine/storage/volumes/), where the data, database and configuration will be stored, respectively. The default configuration and database will be automatically initialized.
=== "s6 overlay" The default user that runs File Browser inside the container has PID 1000 and GID 1000. If, for one reason or another, you want to run the Docker container with a different user, please consult Docker's [user documentation](https://docs.docker.com/engine/containers/run/#user).
> [!NOTE]
>
> When using [bind mounts](https://docs.docker.com/engine/storage/bind-mounts/), that is, when you mount a path on the host in the container, you must manually ensure that they have the correct **permissions**. Docker does not do this automatically for you. The host directories must be readable and writable by the user running inside the container. You can use the [`chown`](https://linux.die.net/man/1/chown) command to change the owner of those paths.
File Browser is now up and running. Read the ["First Boot"](#first-boot) section for more information.
### S6 Overlay Image
The `s6` image is based on LinuxServer and leverages the [s6-overlay](https://github.com/just-containers/s6-overlay) system for a standard, highly customizable image. It should be used as follows: The `s6` image is based on LinuxServer and leverages the [s6-overlay](https://github.com/just-containers/s6-overlay) system for a standard, highly customizable image. It should be used as follows:
@ -72,24 +78,7 @@ Where:
Both `settings.json` and `filebrowser.db` will automatically be initialized if they don't exist. Both `settings.json` and `filebrowser.db` will automatically be initialized if they don't exist.
File Browser is now up and running. Read some [first boot](#first-boot) for more information. File Browser is now up and running. Read the ["First Boot"](#first-boot) section for more information.
> [!NOTE]
>
> The Alpine Docker image has breaking changes from v2.33.0, in order to address multiple issues that have continuously affected multiple users. The changes are as follows:
>
> - **User**: File Browser no longer runs as `root`, but as user with PID 1000 and GID 1000. You can still change this by using Docker's [`--user` flag](https://www.docker.com/blog/understanding-the-docker-user-instruction/).
> - **Volumes**: the volumes with the database and configuration are now aligned with the s6-overlay images. Instead of mounting the files themselves, which leads to frequent issues, you now mount the surrounding directory.
>
> Assuming you have a `database.db`, a `.filebrowser.json` and the data in `/data`, do the following:
>
> 1. Change the path of `database` in `.filebrowser.json` to `/database/filebrowser.db`
> 2. Rename `database.db` to `filebrowser.db`
> 3. Rename `.filebrowser.json` to `settings.json`
> 4. Put them in the same directory locally, let's say `/app/filebrowser/`
> 5. Change the permissions of both directories: `sudo chown -R 1000:1000 /app/filebrowser /data`
> 6. Mount with the flags `-v /app/filebrowser:/database -v /app/filebrowser:/config` - you can also choose to put them in separate directories, but it is not needed.
## First Boot ## First Boot