From 1adc0ee021d5102f1f6deed0c00e8002e7cf56e8 Mon Sep 17 00:00:00 2001 From: Hendrik Date: Fri, 17 Apr 2026 15:28:21 +0200 Subject: [PATCH] feat(api): serve local images with cache headers Co-Authored-By: Claude Opus 4.7 (1M context) --- src/routes/images/[filename]/+server.ts | 36 +++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/routes/images/[filename]/+server.ts diff --git a/src/routes/images/[filename]/+server.ts b/src/routes/images/[filename]/+server.ts new file mode 100644 index 0000000..45bb136 --- /dev/null +++ b/src/routes/images/[filename]/+server.ts @@ -0,0 +1,36 @@ +import type { RequestHandler } from './$types'; +import { error } from '@sveltejs/kit'; +import { createReadStream, existsSync, statSync } from 'node:fs'; +import { join, basename, extname } from 'node:path'; + +const IMAGE_DIR = process.env.IMAGE_DIR ?? './data/images'; + +const MIME: Record = { + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.png': 'image/png', + '.webp': 'image/webp', + '.gif': 'image/gif', + '.avif': 'image/avif' +}; + +export const GET: RequestHandler = ({ params }) => { + const filename = basename(params.filename ?? ''); + if (!filename || filename.includes('..')) error(400, { message: 'Invalid filename' }); + const full = join(IMAGE_DIR, filename); + if (!existsSync(full)) error(404, { message: 'Not found' }); + + const st = statSync(full); + const ext = extname(filename).toLowerCase(); + const mime = MIME[ext] ?? 'application/octet-stream'; + + const stream = createReadStream(full); + return new Response(stream as unknown as ReadableStream, { + status: 200, + headers: { + 'content-type': mime, + 'content-length': String(st.size), + 'cache-control': 'public, max-age=86400, immutable' + } + }); +};