Fix image gallery: scrape real product image URLs from JV website

This commit is contained in:
Omair Saleh
2026-04-28 18:13:56 +08:00
parent 4abe884e0c
commit fc84c8f263
2 changed files with 37 additions and 12 deletions
+29
View File
@@ -52,6 +52,28 @@ async function serveStatic(pathname: string): Promise<Response | null> {
return null
}
// In-memory cache for scraped image URLs per SKU
const imageCache: Record<string, string[]> = {}
async function scrapeProductImages(sku: string): Promise<string[]> {
if (imageCache[sku]) return imageCache[sku]
const images = await readJson(join(PIPELINE_ROOT, 'product-images.json')) as Record<string, any>
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) {
+8 -12
View File
@@ -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 }
}