fix(banners): save relative storage paths so admin preview survives refresh
Uploads were stored as host-specific URLs and a force-reload could wipe a just-saved image, leaving a broken dark-theme preview. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+38
-11
@@ -80,36 +80,63 @@ export function preferHttpsFileUrl(url: string): string {
|
||||
|
||||
/**
|
||||
* Turns API paths like "storage/1405/02/27/file.jpg" into a full URL.
|
||||
* Absolute URLs that already contain /storage/ are rebuilt onto the API base
|
||||
* so preview/theme never keep a host without /server.
|
||||
*/
|
||||
export function toAbsoluteFileUrl(path?: string | null): string | undefined {
|
||||
if (!path?.trim()) return undefined
|
||||
|
||||
const trimmed = path.trim()
|
||||
const storageMatch = trimmed.match(/(?:^|\/)storage\/(.+)$/i)
|
||||
const base = getFileBaseUrl()
|
||||
|
||||
if (storageMatch && base) {
|
||||
return preferHttpsFileUrl(`${ensureServerBase(base)}/storage/${storageMatch[1]}`)
|
||||
}
|
||||
|
||||
if (/^https?:\/\//i.test(trimmed)) {
|
||||
return preferHttpsFileUrl(trimmed)
|
||||
}
|
||||
|
||||
const storageMatch = trimmed.match(/(?:^|\/)storage\/(.+)$/i)
|
||||
if (storageMatch) {
|
||||
const base = getFileBaseUrl()
|
||||
if (base) {
|
||||
return preferHttpsFileUrl(`${base}/storage/${storageMatch[1]}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (/^[a-z0-9.-]+(?:\/server)?\/storage\//i.test(trimmed)) {
|
||||
return `https://${trimmed.replace(/^\/+/, '')}`
|
||||
}
|
||||
|
||||
const base = getFileBaseUrl()
|
||||
if (!base) return undefined
|
||||
|
||||
if (trimmed.startsWith('/')) {
|
||||
return preferHttpsFileUrl(`${base}${trimmed}`)
|
||||
return preferHttpsFileUrl(`${ensureServerBase(base)}${trimmed}`)
|
||||
}
|
||||
|
||||
return preferHttpsFileUrl(`${base}/${trimmed.replace(/^\//, '')}`)
|
||||
return preferHttpsFileUrl(`${ensureServerBase(base)}/${trimmed.replace(/^\//, '')}`)
|
||||
}
|
||||
|
||||
function ensureServerBase(base: string): string {
|
||||
const trimmed = base.replace(/\/$/, '')
|
||||
if (/\/(server|api)$/i.test(trimmed) || /\/(server|api)\//i.test(trimmed)) {
|
||||
return trimmed
|
||||
}
|
||||
return `${trimmed}/server`
|
||||
}
|
||||
|
||||
/** مسیر نسبی storage برای ذخیره در const — مستقل از دامنهٔ پنل */
|
||||
export function toStoredStoragePath(path?: string | null): string | null {
|
||||
if (!path?.trim()) return null
|
||||
const match = path.trim().match(/(?:^|\/)(storage\/.+)$/i)
|
||||
return match ? match[1].replace(/^\/+/, '') : path.trim()
|
||||
}
|
||||
|
||||
/** اگر فایل WebP باشد و URL با .png ذخیره شده باشد، پسوندهای جایگزین را هم امتحان کن. */
|
||||
export function fileUrlCandidates(url?: string | null): string[] {
|
||||
const absolute = toAbsoluteFileUrl(url) ?? url?.trim() ?? ''
|
||||
if (!absolute) return []
|
||||
const match = absolute.match(/^(.*)\.(png|jpe?g|webp|gif)(\?.*)?$/i)
|
||||
if (!match) return [absolute]
|
||||
const stem = match[1]
|
||||
const current = match[2].toLowerCase()
|
||||
const query = match[3] ?? ''
|
||||
const alts = ['webp', 'png', 'jpg', 'jpeg', 'gif']
|
||||
return [absolute, ...alts.filter((ext) => ext !== current).map((ext) => `${stem}.${ext}${query}`)]
|
||||
}
|
||||
|
||||
function isBareFileName(value: string): boolean {
|
||||
|
||||
Reference in New Issue
Block a user