// server/src/routes/uploads-resized.js import fs from 'node:fs' import path from 'node:path' import { findOriginalFile, getOrCreateResized, SUPPORTED_FORMATS, VALID_WIDTHS } from '../lib/image-resize.js' const CACHE_CONTROL_IMMUTABLE = 'public, max-age=31536000, immutable' const CACHE_CONTROL_SHORT = 'public, max-age=86400' /** * Register GET /uploads-resized/* route for on-demand image resizing. * Must be registered BEFORE fastify-static for /uploads/. */ export function registerUploadsResized(fastify) { fastify.get('/uploads-resized/*', async (request, reply) => { const rawPath = request.params['*'] const url = new URL(request.url, 'http://localhost') const widthParam = url.searchParams.get('w') // Parse: [subdir/]filename.format const parts = rawPath.split('/') let filename, subdir = '' if (parts.length > 1) { subdir = parts.slice(0, -1).join('/') + '/' filename = parts[parts.length - 1] } else { filename = parts[0] } const dotIdx = filename.lastIndexOf('.') if (dotIdx === -1) { return reply.code(400).send({ error: 'Invalid request: no format specified' }) } const uuid = filename.slice(0, dotIdx) const format = filename.slice(dotIdx + 1).toLowerCase() if (!SUPPORTED_FORMATS.has(format)) { return reply.code(400).send({ error: `Unsupported format: ${format}. Use avif or webp.` }) } // Validate width let width = null if (widthParam) { const w = parseInt(widthParam, 10) if (!VALID_WIDTHS.includes(w)) { return reply.code(400).send({ error: `Invalid width: ${widthParam}. Use: ${VALID_WIDTHS.join(', ')}` }) } width = w } // If no width requested, serve original with short cache if (!width) { const originalPath = await findOriginalFile(uuid, subdir || undefined) if (!originalPath) { return reply.code(404).send({ error: 'Image not found' }) } reply.header('Cache-Control', CACHE_CONTROL_SHORT) reply.header('Content-Type', format === 'avif' ? 'image/avif' : 'image/webp') return reply.send(fs.createReadStream(originalPath)) } const result = await getOrCreateResized(uuid, width, format, subdir || undefined) if (!result) { return reply.code(404).send({ error: 'Image not found' }) } reply.header('Cache-Control', CACHE_CONTROL_IMMUTABLE) reply.header('Content-Type', format === 'avif' ? 'image/avif' : 'image/webp') return reply.send(fs.createReadStream(result.path)) }) }