/** * CMS-Bild-URLs für Browser und Node (ohne fs/crypto). * Wird von rusty-image und Svelte-Blöcken genutzt — eine Quelle für Loopback→Production und relative Pfade. */ export function getCmsBaseUrl(): string { const url = import.meta.env.PUBLIC_CMS_URL; const base = ( typeof url === "string" && url.trim() ? url : "http://localhost:3000" ).replace(/\/$/, ""); return base; } function isLoopbackAssetHost(hostname: string): boolean { const h = hostname.toLowerCase(); return ( h === "localhost" || h === "127.0.0.1" || h === "::1" || h === "[::1]" || h === "0.0.0.0" ); } /** * Relative CMS-Pfade (`/api/assets/…`) absolut machen; in gespeicherten Einträgen oft * `http://127.0.0.1:3000/api/assets/…` → `PUBLIC_CMS_URL` + Pfad (Production-Build). */ export function normalizeMediaUrl(url: string): string { const u = url.trim(); if (!u) return u; if (u.startsWith("//")) return `https:${u}`; if (u.startsWith("http://") || u.startsWith("https://")) { try { const parsed = new URL(u); if ( isLoopbackAssetHost(parsed.hostname) && parsed.pathname.includes("/api/assets/") ) { const base = getCmsBaseUrl(); return `${base}${parsed.pathname}${parsed.search}`; } } catch { /* ignore */ } return u; } if (u.startsWith("/")) return `${getCmsBaseUrl()}${u}`; return u; } /** * Wie Image-Block / Galerie / Organisation-Logo: `string` oder Objekt mit `src` / `file.url`. * Liefert eine für `ensureTransformedImage` nutzbare URL (nach normalizeMediaUrl). */ export function absoluteUrlFromCmsImageField(field: unknown): string | null { if (typeof field === "string") { const s = field.trim(); if (!s) return null; return normalizeMediaUrl(s.startsWith("//") ? `https:${s}` : s); } if (field && typeof field === "object") { const o = field as { src?: string; file?: { url?: string } }; const u = typeof o.src === "string" && o.src.trim() ? o.src : typeof o.file?.url === "string" && o.file.url.trim() ? o.file.url : undefined; if (!u) return null; return normalizeMediaUrl(u.startsWith("//") ? `https:${u}` : u); } return null; }