import fs from 'fs/promises' import path from 'path' import { getUserFromToken, verifyToken } from '../../../utils/auth.js' const getDataPath = (filename) => { const cwd = process.cwd() if (cwd.endsWith('.output')) return path.join(cwd, '../server/data', filename) return path.join(cwd, 'server/data', filename) } const GALERIE_DIR = getDataPath('galerie') const GALERIE_METADATA = getDataPath('galerie-metadata.json') async function readGalerieMetadata() { try { const data = await fs.readFile(GALERIE_METADATA, 'utf-8') return JSON.parse(data) } catch (error) { if (error.code === 'ENOENT') return [] throw error } } async function isLoggedIn(event) { const token = getCookie(event, 'auth_token') || getHeader(event, 'authorization')?.replace('Bearer ', '') if (!token) return false const decoded = verifyToken(token) if (!decoded) return false const user = await getUserFromToken(token) return !!(user && user.active) } export default defineEventHandler(async (event) => { const imageId = getRouterParam(event, 'id') const query = getQuery(event) const preview = query.preview === 'true' if (!imageId) { throw createError({ statusCode: 400, statusMessage: 'Bild-ID erforderlich' }) } const metadata = await readGalerieMetadata() const image = metadata.find(img => img.id === imageId) if (!image) { throw createError({ statusCode: 404, statusMessage: 'Bild nicht gefunden' }) } if (!image.isPublic) { const ok = await isLoggedIn(event) if (!ok) throw createError({ statusCode: 403, statusMessage: 'Keine Berechtigung' }) } const filename = preview ? image.previewFilename : image.filename const sanitized = path.basename(path.normalize(String(filename || ''))) if (!sanitized || sanitized.includes('..')) { throw createError({ statusCode: 400, statusMessage: 'Ungültiger Dateiname' }) } const subdir = preview ? 'previews' : 'originals' const filePath = path.join(GALERIE_DIR, subdir, sanitized) try { await fs.access(filePath) } catch (e) { throw createError({ statusCode: 404, statusMessage: 'Bilddatei nicht gefunden' }) } const ext = path.extname(sanitized).toLowerCase() const mimeTypes = { '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png', '.gif': 'image/gif', '.webp': 'image/webp', '.svg': 'image/svg+xml' } const contentType = mimeTypes[ext] || 'application/octet-stream' const buf = await fs.readFile(filePath) setHeader(event, 'Content-Type', contentType) setHeader(event, 'Cache-Control', image.isPublic ? 'public, max-age=31536000, immutable' : 'private, max-age=0, no-store') return buf })