Files
2026-06-07 23:57:52 -05:00

65 lines
1.7 KiB
TypeScript

const MEDIA_PROXY_PATH = "/api/media/proxy";
/** 浏览器可直接加载的公开图床,无需经 MinIO 代理缓存 */
const DIRECT_MEDIA_HOSTS = [
"images.unsplash.com",
"unsplash.com",
"i.pravatar.cc",
"api.dicebear.com",
"logo.clearbit.com",
"avatars.githubusercontent.com",
];
function configuredMinioPublicUrl(): string {
return (
process.env.NEXT_PUBLIC_MINIO_PUBLIC_URL ||
process.env.MINIO_PUBLIC_URL ||
""
).replace(/\/$/, "");
}
function isDirectMediaHost(hostname: string): boolean {
const host = hostname.toLowerCase();
return DIRECT_MEDIA_HOSTS.some(
(allowed) => host === allowed || host.endsWith(`.${allowed}`)
);
}
export function isExternalMediaUrl(value: string): boolean {
return /^https?:\/\//i.test(value);
}
export function isMinioUrl(value: string): boolean {
const publicUrl = configuredMinioPublicUrl();
return Boolean(publicUrl && value.startsWith(`${publicUrl}/`));
}
export function mediaUrl(value?: string | null): string {
const url = String(value || "").trim();
if (!url) return "";
if (/^(data:|blob:|#|mailto:|tel:)/i.test(url)) return url;
if (/^file:/i.test(url)) return "";
if (url.startsWith("/") && !url.startsWith("//")) {
const publicUrl = configuredMinioPublicUrl();
return publicUrl ? `${publicUrl}${url}` : url;
}
if (!isExternalMediaUrl(url)) return url;
if (isMinioUrl(url)) return url;
try {
const parsed = new URL(url);
if (isDirectMediaHost(parsed.hostname)) return url;
} catch {
return "";
}
return `${MEDIA_PROXY_PATH}?src=${encodeURIComponent(url)}`;
}
export function mediaUrls(values: string[]): string[] {
return values.map(mediaUrl).filter(Boolean);
}