import { POST_MEDIA_PUBLIC_PREFIX } from "#server/constants/media"; const MD_IMG_RE = /!\[[^\]]*]\(([^)]+)\)/g; function normalizeUrl(raw: string): string { const t = raw.trim().replace(/^<|>$/g, "").split(/\s+/)[0] ?? ""; if (t.startsWith(POST_MEDIA_PUBLIC_PREFIX)) { return t; } return ""; } /** 从 markdown 中提取本站图片 URL(去重,顺序稳定) */ export function extractMediaUrlsFromMarkdown(markdown: string): string[] { const out: string[] = []; const seen = new Set(); let m: RegExpExecArray | null; const re = new RegExp(MD_IMG_RE.source, MD_IMG_RE.flags); while ((m = re.exec(markdown)) !== null) { const u = normalizeUrl(m[1] ?? ""); if (u && !seen.has(u)) { seen.add(u); out.push(u); } } return out; } export function extractMediaUrlsFromCover(coverUrl: string | null | undefined): string[] { if (!coverUrl) { return []; } const u = normalizeUrl(coverUrl); return u ? [u] : []; } export function mergePostMediaUrls(bodyMarkdown: string, coverUrl: string | null | undefined): string[] { const a = extractMediaUrlsFromMarkdown(bodyMarkdown); const b = extractMediaUrlsFromCover(coverUrl); const seen = new Set(a); for (const u of b) { if (!seen.has(u)) { seen.add(u); a.push(u); } } return a; } export function mergeProfileMediaUrls(bioMarkdown: string | null | undefined, avatar: string | null | undefined): string[] { return mergePostMediaUrls(bioMarkdown ?? "", avatar ?? null); } /** `/public/assets/foo.webp` → `foo.webp` */ export function publicAssetUrlToStorageKey(url: string): string | null { const t = url.trim(); if (!t.startsWith(POST_MEDIA_PUBLIC_PREFIX)) { return null; } const key = t.slice(POST_MEDIA_PUBLIC_PREFIX.length).replace(/^\/+/, ""); if (!key || key.includes("..") || key.includes("/") || key.includes("\\")) { return null; } return key; }