import { promises as fs } from 'fs' import path from 'path' const HERO_ROOT_CANDIDATES = [ path.join(process.cwd(), 'public', 'images', 'hero'), path.join(process.cwd(), '.output', 'public', 'images', 'hero'), path.join(process.cwd(), '..', 'public', 'images', 'hero'), path.join(process.cwd(), '..', '.output', 'public', 'images', 'hero') ] const FALLBACK_FILE_CANDIDATES = [ 'hero_fallback.webp', 'hero_fallback.jpg', 'hero_fallback.jpeg', 'hero_fallback.png' ] async function findExistingDir(paths) { for (const candidate of paths) { try { const stats = await fs.stat(candidate) if (stats.isDirectory()) return candidate } catch { // ignore } } return null } async function fileExists(filePath) { try { const stats = await fs.stat(filePath) return stats.isFile() } catch { return false } } function isAllowedVariantKey(key) { return /^[A-Za-z0-9_-]+$/.test(key) } function appendPathSegment(rootDir, segment) { if (!isAllowedVariantKey(segment)) return null return `${rootDir}${path.sep}${segment}` } async function listHeroVariants(heroRoot) { const dirEntries = await fs.readdir(heroRoot, { withFileTypes: true }) const variants = [] for (const entry of dirEntries) { if (!entry.isDirectory()) continue const key = entry.name if (!isAllowedVariantKey(key)) continue const variantDir = appendPathSegment(heroRoot, key) if (!variantDir) continue const mobileFile = 'hero_960.webp' const desktopFile = 'hero_1600.webp' const mobilePath = `${variantDir}${path.sep}${mobileFile}` const desktopPath = `${variantDir}${path.sep}${desktopFile}` if (!mobilePath || !desktopPath) continue if (!(await fileExists(mobilePath)) || !(await fileExists(desktopPath))) { continue } let fallbackFile = desktopFile for (const candidate of FALLBACK_FILE_CANDIDATES) { const fallbackPath = `${variantDir}${path.sep}${candidate}` if (fallbackPath && await fileExists(fallbackPath)) { fallbackFile = candidate break } } variants.push({ key, mobileWebp: `/images/hero/${key}/${mobileFile}`, desktopWebp: `/images/hero/${key}/${desktopFile}`, fallback: `/images/hero/${key}/${fallbackFile}` }) } return variants.sort((a, b) => a.key.localeCompare(b.key, 'de')) } export default defineEventHandler(async (event) => { const heroRoot = await findExistingDir(HERO_ROOT_CANDIDATES) if (!heroRoot) { return { variants: [] } } const variants = await listHeroVariants(heroRoot) setHeader(event, 'Cache-Control', 'public, max-age=300, stale-while-revalidate=600') return { variants } })