@samitouri / QOSami-HFS / commits / eab409e7

fix: admin/shared: link field not working for roots with special chars; also fall back to other roots that are not baseUrl

Massimo Melina committed May 28, 2026 at 10:17 UTC eab409e7cef4ecae0d128ae3d292817853a6bbc5
2 files changed +29 -7
admin/src/FileForm.ts
+28 -6
@@ -8,7 +8,7 @@ import {
8 } from '@hfs/mui-grid-form'
9 import { apiCall, UseApi } from './api'
10 import {
11 - basename, defaultPerms, formatBytes, formatTimestamp, isWhoObject, newDialog, objSameKeys,
11 + basename, defaultPerms, formatBytes, formatTimestamp, isWhoObject, newDialog, objSameKeys, pathEncode,
12 onlyTruthy, prefix, VfsPerms, wantArray, Who, WhoObject, matches, xlate, md, Callback, MASK_IN_TESTS,
13 useRequestRender, splitAt, IMAGE_FILEMASK, copyTextToClipboard, normalizeHost, CFG, try_, WHO_ANY_ACCOUNT,
14 } from './misc'
@@ -349,12 +349,28 @@ function LinkField({ value, statusApi }: LinkFieldProps) {
349 const data = statusApi.getData()
350
351 const urls: string[] = data && (data.urls.https || data.urls.http || [data.base_url])
352 - const baseHost = try_(() => normalizeHost(new URL(data?.baseUrl).host)) // URL can throw on malformed data
353 - const root = useMemo(() => baseHost && _.find(data.roots, (_root, host) => matches(baseHost, host)),
354 - [data])
352 + const baseHost = try_(() => new URL(data?.baseUrl).host) // URL can throw on malformed data
353 + const roots = data?.roots || {}
354 + const root = baseHost && _.find(roots, (_root, host) => matches(baseHost, host))
355 + const originalValue = value
356 if (root)
356 - value &&= value.indexOf(root) === 1 ? value.slice(root.length) : undefined
357 - const link = prefix(data?.baseUrl || '', value)
357 + value = pathInRoot(value, root)
358 + let linkBase = data?.baseUrl || ''
359 + if (value === undefined) { // baseUrl didn't match, but other hosts in roots may
360 + const base = try_(() => new URL(linkBase))
361 + if (base) {
362 + const sorted = _.sortBy(Object.entries(roots), ([, root]) => -String(root).length) // prioritize longer roots because are more specific
363 + for (const [hostMask, root] of sorted) {
364 + if (typeof root !== 'string') continue
365 + value = pathInRoot(originalValue, root)
366 + const host = value && hostMask.split('|').find(x => x && !/[*?]/.test(x) && x !== baseHost)
367 + if (!host) continue
368 + linkBase = base.protocol + '//' + host
369 + break
370 + }
371 + }
372 + }
373 + const link = prefix(linkBase, value)
374 const RenderLink = useMemo(() => forwardRef((props: any, ref) =>
375 h(Link, {
376 ref,
@@ -414,6 +430,12 @@ function LinkField({ value, statusApi }: LinkFieldProps) {
430 console.error('Error generating QR code:', error);
431 }
432 }
433 +
434 + function pathInRoot(uri: string | undefined, root: string | undefined) {
435 + if (!root || root === '/') return uri
436 + root = pathEncode(root)
437 + return uri?.startsWith(root, 1) ? uri.slice(root.length) : undefined
438 + }
439 }
440
441 export async function changeBaseUrl() {
shared/index.ts
+1 -1
@@ -129,7 +129,7 @@ export function getHFS() {
129 }
130
131 export function getPrefixUrl() {
132 - return getHFS().prefixUrl
132 + return getHFS().prefixUrl || ''
133 }
134
135 export function makeSessionRefresher(state: any) {