65 lines
1.7 KiB
TypeScript
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);
|
|
}
|