Files
boursecu_user/src/utils/file.ts
T
safavimohadeseh78andCursor 9356a2ab5e Port modular price, product, and PWA fixes into boursecu
Sync live quote handling, SSE product images, store hours, file URL normalization, PWA icon cache, TV monitor, about software, SuperAdmin menu gates, price alerts, and related UX from modular while preserving boursecu branding and config constraints.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 08:26:52 -04:00

91 lines
2.6 KiB
TypeScript

import { resolveProjectBase } from '@/themeConfig'
import type { User } from '@/models/login'
export interface ProductFile {
id?: number
url?: string | null
src?: string
full_src?: string
}
function projectBase(): string {
return resolveProjectBase().replace(/\/$/, '')
}
function storageRelativePath(value: string): string | null {
const match = value.match(/(?:^|\/)(storage\/.+)$/i)
return match ? match[1].replace(/^\/+/, '') : null
}
function joinProjectUrl(relative: string): string {
const path = relative.replace(/^\//, '')
let base = projectBase()
if (!base && typeof window !== 'undefined') {
base = `${window.location.origin}/server`
}
if (!base) {
return `/server/${path}`
}
base = base.replace(/\/$/, '')
if (path.startsWith('storage/') && !/\/(server|api)(\/|$)/i.test(base)) {
base = `${base}/server`
}
let url = `${base}/${path}`
if (typeof window !== 'undefined' && window.location.protocol === 'https:') {
url = url.replace(/^http:\/\//i, 'https://')
}
return url
}
/**
* URL فایل روی API — مسیرهای `storage/` و آدرس‌های localhost/http
* را به `node_server` می‌چسباند تا روی اندروید (PWA/TWA) لود شوند.
*/
export function normalizePublicFileUrl(raw?: string | null): string | null {
if (raw == null) return null
const trimmed = String(raw).trim()
if (!trimmed || trimmed.toLowerCase() === 'null' || trimmed.toLowerCase() === 'undefined') {
return null
}
if (/^(data:|blob:)/i.test(trimmed)) return trimmed
const storagePath = storageRelativePath(trimmed)
if (storagePath) {
return joinProjectUrl(storagePath)
}
if (/^https?:\/\//i.test(trimmed)) {
if (typeof window !== 'undefined' && window.location.protocol === 'https:') {
return trimmed.replace(/^http:\/\//i, 'https://')
}
return trimmed
}
return joinProjectUrl(trimmed)
}
export function getFileUrl(file?: ProductFile | null): string | null {
if (!file) return null
for (const value of [file.full_src, file.url, file.src]) {
if (!value) continue
const normalized = normalizePublicFileUrl(value)
if (normalized) return normalized
}
return null
}
/** Resolve profile avatar URL from GET /customer/me payload */
export function getUserAvatarUrl(user?: User | null): string | null {
if (!user) return null
if (typeof user.avatar_url === 'string' && user.avatar_url) {
return normalizePublicFileUrl(user.avatar_url)
}
if (typeof user.full_src === 'string' && user.full_src) {
return normalizePublicFileUrl(user.full_src)
}
const nested = user.avatar || user.file
return getFileUrl(nested as ProductFile | undefined)
}