From 7144aa4943c4490009bee0d7ec3e9221ac1bee4d Mon Sep 17 00:00:00 2001 From: DMleadgen Date: Thu, 16 Apr 2026 16:36:14 -0600 Subject: [PATCH] fix: keep tenant thumbnail paths in production manuals render --- app/manuals/page.tsx | 22 ++++++---------------- lib/manuals-render-safety.ts | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 16 deletions(-) create mode 100644 lib/manuals-render-safety.ts diff --git a/app/manuals/page.tsx b/app/manuals/page.tsx index 994ea64e..59688a97 100644 --- a/app/manuals/page.tsx +++ b/app/manuals/page.tsx @@ -1,8 +1,7 @@ export const dynamic = "force-dynamic" import { createHash } from "node:crypto" -import { existsSync } from "fs" -import { join } from "path" +import { existsSync } from "node:fs" import { Metadata } from "next" import { headers } from "next/headers" import { businessConfig } from "@/lib/seo-config" @@ -13,6 +12,7 @@ import { selectManualsForSite } from "@/lib/manuals-site-selection" import { generateSEOMetadata, generateStructuredData } from "@/lib/seo" import { getManualsThumbnailsRoot } from "@/lib/manuals-paths" import { resolveManualsTenantDomain } from "@/lib/manuals-tenant" +import { sanitizeManualThumbnailsForRuntime } from "@/lib/manuals-render-safety" export const metadata: Metadata = generateSEOMetadata({ title: "Vending Machine Manuals | Rocky Mountain Vending", @@ -83,20 +83,10 @@ export default async function ManualsPage() { ) } - // Hide broken local thumbnails so the public manuals page doesn't spam 404s. - const thumbnailsRoot = getManualsThumbnailsRoot() - manuals = manuals.map((manual) => { - if (!manual.thumbnailUrl || /^https?:\/\//i.test(manual.thumbnailUrl)) { - return manual - } - - const relativeThumbnailPath = manual.thumbnailUrl.includes("/thumbnails/") - ? manual.thumbnailUrl.replace(/^.*\/thumbnails\//, "") - : manual.thumbnailUrl - - return existsSync(join(thumbnailsRoot, relativeThumbnailPath)) - ? manual - : { ...manual, thumbnailUrl: undefined } + manuals = sanitizeManualThumbnailsForRuntime(manuals, { + isLocalDevelopment, + thumbnailsRoot: getManualsThumbnailsRoot(), + fileExists: existsSync, }) // Generate structured data for SEO diff --git a/lib/manuals-render-safety.ts b/lib/manuals-render-safety.ts new file mode 100644 index 00000000..4aafc3b6 --- /dev/null +++ b/lib/manuals-render-safety.ts @@ -0,0 +1,33 @@ +import { join } from "node:path" +import type { Manual } from "@/lib/manuals-types" + +type ThumbnailSanitizeOptions = { + isLocalDevelopment: boolean + thumbnailsRoot: string + fileExists?: (path: string) => boolean +} + +export function sanitizeManualThumbnailsForRuntime( + manuals: Manual[], + options: ThumbnailSanitizeOptions +): Manual[] { + if (!options.isLocalDevelopment) { + return manuals + } + + const fileExists = options.fileExists ?? (() => false) + + return manuals.map((manual) => { + if (!manual.thumbnailUrl || /^https?:\/\//i.test(manual.thumbnailUrl)) { + return manual + } + + const relativeThumbnailPath = manual.thumbnailUrl.includes("/thumbnails/") + ? manual.thumbnailUrl.replace(/^.*\/thumbnails\//, "") + : manual.thumbnailUrl + + return fileExists(join(options.thumbnailsRoot, relativeThumbnailPath)) + ? manual + : { ...manual, thumbnailUrl: undefined } + }) +}