diff --git a/api/server.ts b/api/server.ts index af6b848..d92100c 100644 --- a/api/server.ts +++ b/api/server.ts @@ -52,6 +52,28 @@ async function serveStatic(pathname: string): Promise { return null } +// In-memory cache for scraped image URLs per SKU +const imageCache: Record = {} + +async function scrapeProductImages(sku: string): Promise { + if (imageCache[sku]) return imageCache[sku] + const images = await readJson(join(PIPELINE_ROOT, 'product-images.json')) as Record + const productPath = images?.[sku]?.url + if (!productPath) return [] + try { + const res = await fetch(`https://www.justvitamins.co.uk${productPath}`, { + headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } + }) + const html = await res.text() + // Extract filenames like {uuid}-{n}.jpg from anywhere in the page + const matches = [...html.matchAll(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}-\d+)\.jpg/g)] + const filenames = [...new Set(matches.map(m => m[1]))] + const urls = filenames.map(f => `https://images.justvitamins.co.uk/product-images/Normal/${f}.jpg`) + imageCache[sku] = urls + return urls + } catch { return [] } +} + const server = Bun.serve({ port: PORT, async fetch(req) { @@ -71,6 +93,13 @@ const server = Bun.serve({ return new Response(JSON.stringify(resolved), { headers: cors }) } + // GET /api/product-images/:sku + const imgMatch = path.match(/^\/api\/product-images\/([^/]+)$/) + if (imgMatch) { + const urls = await scrapeProductImages(imgMatch[1]) + return new Response(JSON.stringify({ urls }), { headers: cors }) + } + // GET /api/intelligence/:sku/:view const intelMatch = path.match(/^\/api\/intelligence\/([^/]+)\/([^/]+)$/) if (intelMatch) { diff --git a/src/components/views/ImageAudit.vue b/src/components/views/ImageAudit.vue index 9ffb31f..f3b6850 100644 --- a/src/components/views/ImageAudit.vue +++ b/src/components/views/ImageAudit.vue @@ -86,6 +86,7 @@ import { useRoute } from 'vue-router' const route = useRoute() const data = ref(null) const loading = ref(false) +const galleryUrls = ref([]) const axisRows = computed(() => { const axes = data.value?.axes || {} @@ -97,16 +98,6 @@ const axisRows = computed(() => { ] }) -// Extract base UUID from imageUrl and generate all variant URLs -const galleryUrls = computed(() => { - const url = data.value?.imageUrl - if (!url) return [] - // URL pattern: .../Normal/{uuid}-1.jpg → strip the -1.jpg suffix - const base = url.replace(/-\d+\.jpg$/, '') - // Generate 7 URLs (confirmed via CDN probing) - return Array.from({ length: 7 }, (_, i) => `${base}-${i + 1}.jpg`) -}) - function scoreClass(v) { if (!v) return 'fair' if (v >= 4) return 'good' @@ -122,8 +113,13 @@ async function load(sku) { if (!sku) return loading.value = true try { - const res = await fetch(`/api/intelligence/${sku}/image-audit`) - data.value = res.ok ? await res.json() : null + const [auditRes, imgRes] = await Promise.all([ + fetch(`/api/intelligence/${sku}/image-audit`), + fetch(`/api/product-images/${sku}`) + ]) + data.value = auditRes.ok ? await auditRes.json() : null + const imgData = imgRes.ok ? await imgRes.json() : null + galleryUrls.value = imgData?.urls || [] } finally { loading.value = false } }