Fix image gallery: scrape real product image URLs from JV website
This commit is contained in:
@@ -52,6 +52,28 @@ async function serveStatic(pathname: string): Promise<Response | null> {
|
|||||||
return 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({
|
const server = Bun.serve({
|
||||||
port: PORT,
|
port: PORT,
|
||||||
async fetch(req) {
|
async fetch(req) {
|
||||||
@@ -71,6 +93,13 @@ const server = Bun.serve({
|
|||||||
return new Response(JSON.stringify(resolved), { headers: cors })
|
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
|
// GET /api/intelligence/:sku/:view
|
||||||
const intelMatch = path.match(/^\/api\/intelligence\/([^/]+)\/([^/]+)$/)
|
const intelMatch = path.match(/^\/api\/intelligence\/([^/]+)\/([^/]+)$/)
|
||||||
if (intelMatch) {
|
if (intelMatch) {
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ import { useRoute } from 'vue-router'
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const data = ref(null)
|
const data = ref(null)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
const galleryUrls = ref([])
|
||||||
|
|
||||||
const axisRows = computed(() => {
|
const axisRows = computed(() => {
|
||||||
const axes = data.value?.axes || {}
|
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) {
|
function scoreClass(v) {
|
||||||
if (!v) return 'fair'
|
if (!v) return 'fair'
|
||||||
if (v >= 4) return 'good'
|
if (v >= 4) return 'good'
|
||||||
@@ -122,8 +113,13 @@ async function load(sku) {
|
|||||||
if (!sku) return
|
if (!sku) return
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/intelligence/${sku}/image-audit`)
|
const [auditRes, imgRes] = await Promise.all([
|
||||||
data.value = res.ok ? await res.json() : null
|
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 }
|
} finally { loading.value = false }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user