diff --git a/app/api/thumbnails/[...path]/route.ts b/app/api/thumbnails/[...path]/route.ts index cfd2e8d2..b1c49851 100644 --- a/app/api/thumbnails/[...path]/route.ts +++ b/app/api/thumbnails/[...path]/route.ts @@ -7,6 +7,33 @@ import { getManualAssetFromStorage } from "@/lib/manuals-object-storage" export const dynamic = "force-dynamic" +function buildPlaceholderSvg(label: string) { + const safeLabel = label + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'") + + return ` + + + + + + + Manual Preview + + + Thumbnail unavailable + + + ${safeLabel} + + + `.trim() +} + function decodeSegments(pathArray: string[]) { return pathArray.map((segment) => { try { @@ -72,7 +99,14 @@ export async function GET( const normalizedThumbnailsDir = thumbnailsDir.replace(/\\/g, "/") if (!existsSync(normalizedFullPath) && !existsSync(fullPath)) { - return new NextResponse("Thumbnail not found", { status: 404 }) + const label = decodedPath.at(-1)?.replace(/\.(jpg|jpeg|png|webp)$/i, "") || "Rocky Mountain Vending" + return new NextResponse(buildPlaceholderSvg(label), { + headers: { + "Content-Type": "image/svg+xml; charset=utf-8", + "Cache-Control": "public, max-age=3600, stale-while-revalidate=86400", + "X-Content-Type-Options": "nosniff", + }, + }) } const fileToRead = existsSync(normalizedFullPath) ? normalizedFullPath : fullPath diff --git a/components/footer.tsx b/components/footer.tsx index 75056405..b6e4d57a 100644 --- a/components/footer.tsx +++ b/components/footer.tsx @@ -150,27 +150,27 @@ export function Footer() {

Service Areas