From 1dcc69482ef4b573a3d96cb96c48e03bb187bf72 Mon Sep 17 00:00:00 2001 From: grabowski Date: Tue, 7 Apr 2026 17:18:21 +0700 Subject: [PATCH] Add server route to serve uploaded files in production adapter-node only serves pre-built static assets, not files uploaded at runtime. Added /uploads/[...path] catch-all route that reads from static/uploads/ with proper MIME types and cache headers. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/routes/uploads/[...path]/+server.ts | 40 +++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/routes/uploads/[...path]/+server.ts diff --git a/src/routes/uploads/[...path]/+server.ts b/src/routes/uploads/[...path]/+server.ts new file mode 100644 index 0000000..01a4062 --- /dev/null +++ b/src/routes/uploads/[...path]/+server.ts @@ -0,0 +1,40 @@ +import type { RequestHandler } from './$types'; +import { readFile } from 'fs/promises'; +import { join, extname } from 'path'; +import { error } from '@sveltejs/kit'; + +const MIME_TYPES: Record = { + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.png': 'image/png', + '.webp': 'image/webp', + '.gif': 'image/gif', + '.svg': 'image/svg+xml', + '.pdf': 'application/pdf', + '.txt': 'text/plain', + '.zip': 'application/zip' +}; + +export const GET: RequestHandler = async ({ params }) => { + const filePath = params.path; + + // Prevent directory traversal + if (filePath.includes('..')) error(400, 'Invalid path'); + + const fullPath = join('static', 'uploads', filePath); + + try { + const data = await readFile(fullPath); + const ext = extname(filePath).toLowerCase(); + const contentType = MIME_TYPES[ext] ?? 'application/octet-stream'; + + return new Response(data, { + headers: { + 'Content-Type': contentType, + 'Cache-Control': 'public, max-age=86400' + } + }); + } catch { + error(404, 'File not found'); + } +};