From d1c84a84123c77dede05c023b3697a432b56122c Mon Sep 17 00:00:00 2001 From: Ryan <43447928+Rmiller5466@users.noreply.github.com> Date: Mon, 20 Jan 2025 18:05:59 -0500 Subject: [PATCH 1/7] fix: prompts disappearing on copy / move / upload (#3537) --------- Co-authored-by: Ryan Miller Co-authored-by: Oleg Lobanov --- frontend/src/api/files.ts | 8 +++++--- frontend/src/components/Sidebar.vue | 2 +- frontend/src/components/prompts/Prompts.vue | 11 ++--------- frontend/src/components/prompts/Upload.vue | 2 -- frontend/src/stores/layout.ts | 12 +++++++++++- frontend/src/types/layout.d.ts | 1 + frontend/src/utils/upload.ts | 4 ++++ frontend/src/views/files/FileListing.vue | 2 -- 8 files changed, 24 insertions(+), 18 deletions(-) diff --git a/frontend/src/api/files.ts b/frontend/src/api/files.ts index a36de03f..928f5282 100644 --- a/frontend/src/api/files.ts +++ b/frontend/src/api/files.ts @@ -1,7 +1,8 @@ -import { createURL, fetchURL, removePrefix } from "./utils"; -import { baseURL } from "@/utils/constants"; import { useAuthStore } from "@/stores/auth"; +import { useLayoutStore } from "@/stores/layout"; +import { baseURL } from "@/utils/constants"; import { upload as postTus, useTus } from "./tus"; +import { createURL, fetchURL, removePrefix } from "./utils"; export async function fetch(url: string) { url = removePrefix(url); @@ -156,6 +157,7 @@ function moveCopy( overwrite = false, rename = false ) { + const layoutStore = useLayoutStore(); const promises = []; for (const item of items) { @@ -166,7 +168,7 @@ function moveCopy( }&destination=${to}&override=${overwrite}&rename=${rename}`; promises.push(resourceAction(url, "PATCH")); } - + layoutStore.closeHovers(); return Promise.all(promises); } diff --git a/frontend/src/components/Sidebar.vue b/frontend/src/components/Sidebar.vue index d88d2d2f..543818ad 100644 --- a/frontend/src/components/Sidebar.vue +++ b/frontend/src/components/Sidebar.vue @@ -101,7 +101,7 @@ href="https://github.com/filebrowser/filebrowser" >File Browser - {{ ' ' }} {{ version }} + {{ " " }} {{ version }} {{ $t("sidebar.help") }} diff --git a/frontend/src/components/prompts/Prompts.vue b/frontend/src/components/prompts/Prompts.vue index 4ecde794..71e4e753 100644 --- a/frontend/src/components/prompts/Prompts.vue +++ b/frontend/src/components/prompts/Prompts.vue @@ -3,7 +3,7 @@ diff --git a/frontend/src/css/fonts.css b/frontend/src/css/fonts.css index c32e1e01..2e65cff6 100644 --- a/frontend/src/css/fonts.css +++ b/frontend/src/css/fonts.css @@ -63,8 +63,8 @@ local("Roboto"), local("Roboto-Regular"), url(../assets/fonts/roboto/normal-latin-ext.woff2) format("woff2"); - unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, - U+A720-A7FF; + unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, + U+2C60-2C7F, U+A720-A7FF; } @font-face { @@ -142,8 +142,8 @@ local("Roboto Medium"), local("Roboto-Medium"), url(../assets/fonts/roboto/medium-latin-ext.woff2) format("woff2"); - unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, - U+A720-A7FF; + unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, + U+2C60-2C7F, U+A720-A7FF; } @font-face { @@ -221,8 +221,8 @@ local("Roboto Bold"), local("Roboto-Bold"), url(../assets/fonts/roboto/bold-latin-ext.woff2) format("woff2"); - unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, - U+A720-A7FF; + unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, + U+2C60-2C7F, U+A720-A7FF; } @font-face { diff --git a/frontend/src/i18n/index.ts b/frontend/src/i18n/index.ts index a16a0a6a..f0b0304b 100644 --- a/frontend/src/i18n/index.ts +++ b/frontend/src/i18n/index.ts @@ -142,7 +142,7 @@ export const i18n = createI18n({ export const isRtl = (locale?: string) => { // see below - // @ts-ignore + // @ts-expect-error incorrect type when legacy return rtlLanguages.includes(locale || i18n.global.locale.value); }; @@ -150,7 +150,7 @@ export function setLocale(locale: string) { dayjs.locale(locale); // according to doc u only need .value if legacy: false but they lied // https://vue-i18n.intlify.dev/guide/essentials/scope.html#local-scope-1 - //@ts-ignore + // @ts-expect-error incorrect type when legacy i18n.global.locale.value = locale; } diff --git a/frontend/src/utils/auth.ts b/frontend/src/utils/auth.ts index a83719ce..b868d90f 100644 --- a/frontend/src/utils/auth.ts +++ b/frontend/src/utils/auth.ts @@ -24,7 +24,7 @@ export async function validateLogin() { await renew(localStorage.getItem("jwt")); } } catch (error) { - console.warn("Invalid JWT token in storage"); // eslint-disable-line + console.warn("Invalid JWT token in storage"); throw error; } } diff --git a/frontend/src/utils/buttons.ts b/frontend/src/utils/buttons.ts index 6a8ac249..30ed74f2 100644 --- a/frontend/src/utils/buttons.ts +++ b/frontend/src/utils/buttons.ts @@ -4,7 +4,7 @@ function loading(button: string) { ); if (el === undefined || el === null) { - console.log("Error getting button " + button); // eslint-disable-line + console.log("Error getting button " + button); return; } @@ -30,7 +30,7 @@ function done(button: string) { ); if (el === undefined || el === null) { - console.log("Error getting button " + button); // eslint-disable-line + console.log("Error getting button " + button); return; } @@ -51,7 +51,7 @@ function success(button: string) { ); if (el === undefined || el === null) { - console.log("Error getting button " + button); // eslint-disable-line + console.log("Error getting button " + button); return; } diff --git a/frontend/src/utils/clipboard.ts b/frontend/src/utils/clipboard.ts index 136f1ad4..23bb7895 100644 --- a/frontend/src/utils/clipboard.ts +++ b/frontend/src/utils/clipboard.ts @@ -1,39 +1,36 @@ // Based on code by the following links: // https://stackoverflow.com/a/74528564 // https://web.dev/articles/async-clipboard -export function copy(text: string) { + +interface ClipboardArgs { + text?: string; + data?: ClipboardItems; +} + +interface ClipboardOpts { + permission?: boolean; +} + +export function copy(data: ClipboardArgs, opts?: ClipboardOpts) { return new Promise((resolve, reject) => { if ( // Clipboard API requires secure context window.isSecureContext && - typeof navigator.clipboard !== "undefined" && - // @ts-ignore - navigator.permissions !== "undefined" + typeof navigator.clipboard !== "undefined" ) { - navigator.permissions - // @ts-ignore - .query({ name: "clipboard-write" }) - .then((permission) => { - if (permission.state === "granted" || permission.state === "prompt") { - // simple writeText should work for all modern browsers - navigator.clipboard.writeText(text).then(resolve).catch(reject); - } else { - reject(new Error("Permission not granted!")); - } - }) - .catch((e) => { - // Firefox doesn't support clipboard-write permission - if (navigator.userAgent.indexOf("Firefox") != -1) { - navigator.clipboard.writeText(text).then(resolve).catch(reject); - } else { - reject(e); - } - }); + if (opts?.permission) { + getPermission("clipboard-write") + .then(() => writeToClipboard(data).then(resolve).catch(reject)) + .catch(reject); + } else { + writeToClipboard(data).then(resolve).catch(reject); + } } else if ( document.queryCommandSupported && - document.queryCommandSupported("copy") + document.queryCommandSupported("copy") && + data.text // old method only supports text ) { - const textarea = createTemporaryTextarea(text); + const textarea = createTemporaryTextarea(data.text); const body = document.activeElement || document.body; try { body.appendChild(textarea); @@ -54,6 +51,35 @@ export function copy(text: string) { }); } +function getPermission(name: string) { + return new Promise((resolve, reject) => { + typeof navigator.permissions !== "undefined" && + navigator.permissions + // @ts-expect-error chrome specific api + .query({ name }) + .then((permission) => { + if (permission.state === "granted" || permission.state === "prompt") { + resolve(); + } else { + reject(new Error("Permission denied!")); + } + }); + }); +} + +function writeToClipboard(data: ClipboardArgs) { + if (data.text) { + return navigator.clipboard.writeText(data.text); + } + if (data.data) { + return navigator.clipboard.write(data.data); + } + + return new Promise((resolve, reject) => { + reject(new Error("No data was supplied!")); + }); +} + const styles = { fontSize: "12pt", position: "fixed", @@ -69,10 +95,10 @@ const styles = { background: "transparent", }; -const createTemporaryTextarea = (text: string) => { +function createTemporaryTextarea(text: string) { const textarea = document.createElement("textarea"); textarea.value = text; textarea.setAttribute("readonly", ""); Object.assign(textarea.style, styles); return textarea; -}; +} diff --git a/frontend/src/utils/css.ts b/frontend/src/utils/css.ts index d727786b..aca6c62a 100644 --- a/frontend/src/utils/css.ts +++ b/frontend/src/utils/css.ts @@ -6,13 +6,16 @@ export default function getRule(rules: string[]) { let result = null; const find = Array.prototype.find; - find.call(document.styleSheets, (styleSheet) => { - result = find.call(styleSheet.cssRules, (cssRule) => { + find.call(document.styleSheets, (styleSheet: CSSStyleSheet) => { + result = find.call(styleSheet.cssRules, (cssRule: CSSRule) => { let found = false; - if (cssRule instanceof window.CSSStyleRule) { + // faster than checking instanceof for every element + if (cssRule.constructor.name === "CSSStyleRule") { for (let i = 0; i < rules.length; i++) { - if (cssRule.selectorText.toLowerCase() === rules[i]) { + if ( + (cssRule as CSSStyleRule).selectorText.toLowerCase() === rules[i] + ) { found = true; } } @@ -24,5 +27,5 @@ export default function getRule(rules: string[]) { return result != null; }); - return result; + return result as CSSStyleRule | null; } diff --git a/frontend/src/views/Share.vue b/frontend/src/views/Share.vue index 53f0cfb1..40b940b5 100644 --- a/frontend/src/views/Share.vue +++ b/frontend/src/views/Share.vue @@ -325,6 +325,7 @@ const token = ref(""); const audio = ref(); const tag = ref(false); +const $showError = inject("$showError")!; const $showSuccess = inject("$showSuccess")!; const { t } = useI18n({}); @@ -463,9 +464,9 @@ const download = () => { if (req.value === null) return false; layoutStore.closeHovers(); - let files: string[] = []; + const files: string[] = []; - for (let i of fileStore.selected) { + for (const i of fileStore.selected) { files.push(req.value.items[i].path); } @@ -488,13 +489,23 @@ const linkSelected = () => { }; const copyToClipboard = (text: string) => { - copy(text).then( + copy({ text }).then( () => { // clipboard successfully set $showSuccess(t("success.linkCopied")); }, () => { // clipboard write failed + copy({ text }, { permission: true }).then( + () => { + // clipboard successfully set + $showSuccess(t("success.linkCopied")); + }, + (e) => { + // clipboard write failed + $showError(e); + } + ); } ); }; diff --git a/frontend/src/views/files/Editor.vue b/frontend/src/views/files/Editor.vue index 7e4d7a9b..9e9b6b68 100644 --- a/frontend/src/views/files/Editor.vue +++ b/frontend/src/views/files/Editor.vue @@ -108,7 +108,7 @@ onMounted(() => { showPrintMargin: false, readOnly: fileStore.req?.type === "textImmutable", theme: "ace/theme/chrome", - mode: modelist.getModeForPath(fileStore.req?.name).mode, + mode: modelist.getModeForPath(fileStore.req!.name).mode, wrap: true, enableBasicAutocompletion: true, enableLiveAutocompletion: true, @@ -173,7 +173,7 @@ const close = () => { fileStore.updateRequest(null); - let uri = url.removeLastDir(route.path) + "/"; + const uri = url.removeLastDir(route.path) + "/"; router.push({ path: uri }); }; diff --git a/frontend/src/views/files/FileListing.vue b/frontend/src/views/files/FileListing.vue index ec75aec5..8fa48f72 100644 --- a/frontend/src/views/files/FileListing.vue +++ b/frontend/src/views/files/FileListing.vue @@ -523,12 +523,12 @@ const keyEvent = (event: KeyboardEvent) => { break; case "a": event.preventDefault(); - for (let file of items.value.files) { + for (const file of items.value.files) { if (fileStore.selected.indexOf(file.index) === -1) { fileStore.selected.push(file.index); } } - for (let dir of items.value.dirs) { + for (const dir of items.value.dirs) { if (fileStore.selected.indexOf(dir.index) === -1) { fileStore.selected.push(dir.index); } @@ -551,9 +551,9 @@ const copyCut = (event: Event | KeyboardEvent): void => { if (fileStore.req === null) return; - let items = []; + const items = []; - for (let i of fileStore.selected) { + for (const i of fileStore.selected) { items.push({ from: fileStore.req.items[i].url, name: fileStore.req.items[i].name, @@ -575,9 +575,9 @@ const paste = (event: Event) => { if ((event.target as HTMLElement).tagName?.toLowerCase() === "input") return; // TODO router location should it be - let items: any[] = []; + const items: any[] = []; - for (let item of clipboardStore.items) { + for (const item of clipboardStore.items) { const from = item.from.endsWith("/") ? item.from.slice(0, -1) : item.from; const to = route.path + encodeURIComponent(item.name); items.push({ from, to, name: item.name }); @@ -614,7 +614,7 @@ const paste = (event: Event) => { return; } - let conflict = upload.checkConflict(items, fileStore.req!.items); + const conflict = upload.checkConflict(items, fileStore.req!.items); let overwrite = false; let rename = false; @@ -640,14 +640,13 @@ const paste = (event: Event) => { const colunmsResize = () => { // Update the columns size based on the window width. - let items_ = css(["#listing.mosaic .item", ".mosaic#listing .item"]); + const items_ = css(["#listing.mosaic .item", ".mosaic#listing .item"]); if (items_ === null) return; let columns = Math.floor( (document.querySelector("main")?.offsetWidth ?? 0) / columnWidth.value ); if (columns === 0) columns = 1; - // @ts-ignore never type error items_.style.width = `calc(${100 / columns}% - 1em)`; }; @@ -677,11 +676,10 @@ const dragEnter = () => { // When the user starts dragging an item, put every // file on the listing with 50% opacity. - let items = document.getElementsByClassName("item"); + const items = document.getElementsByClassName("item"); - // @ts-ignore - Array.from(items).forEach((file: HTMLElement) => { - file.style.opacity = "0.5"; + Array.from(items).forEach((file: Element) => { + (file as HTMLElement).style.opacity = "0.5"; }); }; @@ -698,7 +696,7 @@ const drop = async (event: DragEvent) => { dragCounter.value = 0; resetOpacity(); - let dt = event.dataTransfer; + const dt = event.dataTransfer; let el: HTMLElement | null = event.target as HTMLElement; if (fileStore.req === null || dt === null || dt.files.length <= 0) return; @@ -709,7 +707,7 @@ const drop = async (event: DragEvent) => { } } - let files: UploadList = (await upload.scanFiles(dt)) as UploadList; + const files: UploadList = (await upload.scanFiles(dt)) as UploadList; let items = fileStore.req.items; let path = route.path.endsWith("/") ? route.path : route.path + "/"; @@ -729,7 +727,7 @@ const drop = async (event: DragEvent) => { } } - let conflict = upload.checkConflict(files, items); + const conflict = upload.checkConflict(files, items); if (conflict) { layoutStore.showHover({ @@ -753,10 +751,10 @@ const drop = async (event: DragEvent) => { }; const uploadInput = (event: Event) => { - let files = (event.currentTarget as HTMLInputElement)?.files; + const files = (event.currentTarget as HTMLInputElement)?.files; if (files === null) return; - let folder_upload = !!files[0].webkitRelativePath; + const folder_upload = !!files[0].webkitRelativePath; const uploadFiles: UploadList = []; for (let i = 0; i < files.length; i++) { @@ -771,8 +769,8 @@ const uploadInput = (event: Event) => { }); } - let path = route.path.endsWith("/") ? route.path : route.path + "/"; - let conflict = upload.checkConflict(uploadFiles, fileStore.req!.items); + const path = route.path.endsWith("/") ? route.path : route.path + "/"; + const conflict = upload.checkConflict(uploadFiles, fileStore.req!.items); if (conflict) { layoutStore.showHover({ @@ -796,7 +794,7 @@ const uploadInput = (event: Event) => { }; const resetOpacity = () => { - let items = document.getElementsByClassName("item"); + const items = document.getElementsByClassName("item"); Array.from(items).forEach((file: Element) => { (file as HTMLElement).style.opacity = "1"; @@ -822,7 +820,6 @@ const sort = async (by: string) => { try { if (authStore.user?.id) { - // @ts-ignore await users.update({ id: authStore.user?.id, sorting: { by, asc } }, [ "sorting", ]); @@ -873,10 +870,10 @@ const download = () => { confirm: (format: any) => { layoutStore.closeHovers(); - let files = []; + const files = []; if (fileStore.selectedCount > 0 && fileStore.req !== null) { - for (let i of fileStore.selected) { + for (const i of fileStore.selected) { files.push(fileStore.req.items[i].url); } } else { @@ -899,13 +896,12 @@ const switchView = async () => { const data = { id: authStore.user?.id, - viewMode: modes[authStore.user?.viewMode ?? "list"] || "list", + viewMode: (modes[authStore.user?.viewMode ?? "list"] || + "list") as ViewModeType, }; - // @ts-ignore users.update(data, ["viewMode"]).catch($showError); - // @ts-ignore authStore.updateUser(data); setItemWeight(); diff --git a/frontend/src/views/files/Preview.vue b/frontend/src/views/files/Preview.vue index a4f2cfb7..4fd3d2c8 100644 --- a/frontend/src/views/files/Preview.vue +++ b/frontend/src/views/files/Preview.vue @@ -353,7 +353,7 @@ const updatePreview = async () => { autoPlay.value = false; } - let dirs = route.fullPath.split("/"); + const dirs = route.fullPath.split("/"); name.value = decodeURIComponent(dirs[dirs.length - 1]); if (!listing.value) { @@ -422,7 +422,7 @@ const toggleNavigation = throttle(function () { const close = () => { fileStore.updateRequest(null); - let uri = url.removeLastDir(route.path) + "/"; + const uri = url.removeLastDir(route.path) + "/"; router.push({ path: uri }); }; diff --git a/frontend/src/views/settings/Global.vue b/frontend/src/views/settings/Global.vue index 0b055ee2..5bbaec7f 100644 --- a/frontend/src/views/settings/Global.vue +++ b/frontend/src/views/settings/Global.vue @@ -282,7 +282,7 @@ const formattedChunkSize = computed({ // Define funcs const capitalize = (name: string, where: string | RegExp = "_") => { if (where === "caps") where = /(?=[A-Z])/; - let split = name.split(where); + const split = name.split(where); name = ""; for (let i = 0; i < split.length; i++) { @@ -294,7 +294,7 @@ const capitalize = (name: string, where: string | RegExp = "_") => { const save = async () => { if (settings.value === null) return false; - let newSettings: ISettings = { + const newSettings: ISettings = { ...settings.value, shell: settings.value?.shell @@ -376,7 +376,7 @@ onMounted(async () => { try { layoutStore.loading = true; const original: ISettings = await api.get(); - let newSettings: ISettings = { ...original, commands: {} }; + const newSettings: ISettings = { ...original, commands: {} }; const keys = Object.keys(original.commands) as Array; for (const key of keys) { diff --git a/frontend/src/views/settings/Shares.vue b/frontend/src/views/settings/Shares.vue index 478d595c..e1076ec9 100644 --- a/frontend/src/views/settings/Shares.vue +++ b/frontend/src/views/settings/Shares.vue @@ -87,12 +87,12 @@ onMounted(async () => { layoutStore.loading = true; try { - let newLinks = await api.list(); + const newLinks = await api.list(); if (authStore.user?.perm.admin) { - let userMap = new Map(); - for (let user of await users.getAll()) + const userMap = new Map(); + for (const user of await users.getAll()) userMap.set(user.id, user.username); - for (let link of newLinks) { + for (const link of newLinks) { if (link.userID && userMap.has(link.userID)) link.username = userMap.get(link.userID); } @@ -108,13 +108,23 @@ onMounted(async () => { }); const copyToClipboard = (text: string) => { - copy(text).then( + copy({ text }).then( () => { // clipboard successfully set $showSuccess(t("success.linkCopied")); }, () => { // clipboard write failed + copy({ text }, { permission: true }).then( + () => { + // clipboard successfully set + $showSuccess(t("success.linkCopied")); + }, + (e) => { + // clipboard write failed + $showError(e); + } + ); } ); }; diff --git a/frontend/src/views/settings/User.vue b/frontend/src/views/settings/User.vue index f12d5997..a0da68ce 100644 --- a/frontend/src/views/settings/User.vue +++ b/frontend/src/views/settings/User.vue @@ -90,7 +90,7 @@ const fetchData = async () => { try { if (isNew.value) { - let { defaults, createUserDir: _createUserDir } = await settings.get(); + const { defaults, createUserDir: _createUserDir } = await settings.get(); createUserDir.value = _createUserDir; user.value = { ...defaults, From 1194cfe0097a70399c1f06cf0f514b9d70fa463c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jan 2025 10:22:04 +0100 Subject: [PATCH 4/7] build(deps): bump golang.org/x/net from 0.23.0 to 0.33.0 (#3712) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.23.0 to 0.33.0. - [Commits](https://github.com/golang/net/compare/v0.23.0...v0.33.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b6be3d7b..a58d5c24 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect - golang.org/x/net v0.23.0 // indirect + golang.org/x/net v0.33.0 // indirect golang.org/x/sys v0.28.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 7254e9b5..4e451c05 100644 --- a/go.sum +++ b/go.sum @@ -190,8 +190,8 @@ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= From 252f0a753323f7981fde1da22f2e08cdb69b531b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=84=B1=EB=AF=BC?= Date: Thu, 30 Jan 2025 18:24:44 +0900 Subject: [PATCH 5/7] chore: update ko.json (#3688) --- frontend/src/i18n/ko.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/frontend/src/i18n/ko.json b/frontend/src/i18n/ko.json index 046a712b..722da2d2 100644 --- a/frontend/src/i18n/ko.json +++ b/frontend/src/i18n/ko.json @@ -9,7 +9,7 @@ "create": "생성", "delete": "삭제", "download": "다운로드", - "hideDotfiles": "", + "hideDotfiles": "숨김파일(dotfile)을 표시 안함", "info": "정보", "more": "더보기", "move": "이동", @@ -38,7 +38,7 @@ "download": { "downloadFile": "파일 다운로드", "downloadFolder": "폴더 다운로드", - "downloadSelected": "" + "downloadSelected": "선택 항목 다운로드" }, "errors": { "forbidden": "접근 권한이 없습니다.", @@ -120,8 +120,8 @@ "scheduleMessage": "이 글을 공개할 시간을 알려주세요.", "show": "보기", "size": "크기", - "upload": "", - "uploadMessage": "" + "upload": "업로드", + "uploadMessage": "업로드 옵션을 선택하세요." }, "search": { "images": "이미지", @@ -160,7 +160,7 @@ "executeOnShellDescription": "기본적으로 File Browser 는 바이너리를 명령어로 호출하여 실행합니다. 쉘을 통해 실행하기를 원한다면, Bash 또는 PowerShell 에 필요한 인수와 플래그를 설정하세요. 사용자 명령어와 이벤트 훅에 모두 적용됩니다.", "globalRules": "규칙에 대한 전역설정으로 모든 사용자에게 적용됩니다. 지정된 규칙은 사용자 설정을 덮어쓰기 합니다.", "globalSettings": "전역 설정", - "hideDotfiles": "", + "hideDotfiles": "숨김파일(dotfile)을 표시하지 않습니다.", "insertPath": "경로 입력", "insertRegex": "정규식 입력", "instanceName": "인스턴스 이름", @@ -171,7 +171,7 @@ "newUser": "새로운 사용자", "password": "비밀번호", "passwordUpdated": "비밀번호 수정 완료!", - "path": "", + "path": "경로", "perm": { "create": "파일이나 디렉토리 생성하기", "delete": "화일이나 디렉토리 삭제하기", @@ -190,13 +190,13 @@ "rulesHelp": "사용자별로 규칙을 허용/방지를 지정할 수 있습니다. 방지된 파일은 보이지 않고 사용자들은 접근할 수 없습니다. 사용자의 접근 허용 범위와 관련해 정규표현식(regex)과 경로를 지원합니다.\n", "scope": "범위", "settingsUpdated": "설정 수정됨!", - "shareDuration": "", - "shareManagement": "", - "singleClick": "", + "shareDuration": "공유 기간", + "shareManagement": "공유 내역 관리", + "singleClick": "한번 클릭으로 파일과 폴더를 열도록 합니다.", "themes": { - "dark": "", - "light": "", - "title": "" + "dark": "다크테마", + "light": "라이트테마", + "title": "테마" }, "user": "사용자", "userCommands": "명령어", From 045064f8b8bf9f86058e877448085e38da8b3f2e Mon Sep 17 00:00:00 2001 From: Eden Yemini Date: Thu, 30 Jan 2025 11:29:14 +0200 Subject: [PATCH 6/7] fix: add proper healthcheck for S6 containers (#3691) Co-authored-by: Oleg Lobanov --- Dockerfile.s6 | 11 ++++++++--- Dockerfile.s6.aarch64 | 11 ++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Dockerfile.s6 b/Dockerfile.s6 index 63c43018..609a2634 100644 --- a/Dockerfile.s6 +++ b/Dockerfile.s6 @@ -2,15 +2,20 @@ FROM ghcr.io/linuxserver/baseimage-alpine:3.20 RUN apk --update add ca-certificates \ mailcap \ - curl + curl \ + jq + +COPY healthcheck.sh /healthcheck.sh +RUN chmod +x /healthcheck.sh # Make the script executable HEALTHCHECK --start-period=2s --interval=5s --timeout=3s \ - CMD curl -f http://localhost/health || exit 1 + CMD /healthcheck.sh || exit 1 # copy local files COPY docker/root/ / +RUN ln -s /config/settings.json /.filebrowser.json COPY filebrowser /usr/bin/filebrowser # ports and volumes VOLUME /srv /config /database -EXPOSE 80 \ No newline at end of file +EXPOSE 80 diff --git a/Dockerfile.s6.aarch64 b/Dockerfile.s6.aarch64 index 752e3ed3..1e62391e 100644 --- a/Dockerfile.s6.aarch64 +++ b/Dockerfile.s6.aarch64 @@ -2,15 +2,20 @@ FROM ghcr.io/linuxserver/baseimage-alpine:arm64v8-3.20 RUN apk --update add ca-certificates \ mailcap \ - curl + curl \ + jq + +COPY healthcheck.sh /healthcheck.sh +RUN chmod +x /healthcheck.sh # Make the script executable HEALTHCHECK --start-period=2s --interval=5s --timeout=3s \ - CMD curl -f http://localhost/health || exit 1 + CMD /healthcheck.sh || exit 1 # copy local files COPY docker/root/ / +RUN ln -s /config/settings.json /.filebrowser.json COPY filebrowser /usr/bin/filebrowser # ports and volumes VOLUME /srv /config /database -EXPOSE 80 \ No newline at end of file +EXPOSE 80 From bbdd313705b8d253f0c47ad717a6e47b2f46e719 Mon Sep 17 00:00:00 2001 From: elmodor Date: Thu, 30 Jan 2025 10:32:05 +0100 Subject: [PATCH 7/7] fix: disk usage refreshing (#3692) Co-authored-by: Oleg Lobanov --- frontend/src/components/Sidebar.vue | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Sidebar.vue b/frontend/src/components/Sidebar.vue index 595fd685..4d55cf0f 100644 --- a/frontend/src/components/Sidebar.vue +++ b/frontend/src/components/Sidebar.vue @@ -191,8 +191,13 @@ export default { logout: auth.logout, }, watch: { - isFiles(newValue) { - newValue && this.fetchUsage(); + $route: { + handler(to) { + if (to.path.includes("/files")) { + this.fetchUsage(); + } + }, + immediate: true, }, }, };