From f46a24ee4e4b0ac347e3f26afbfdedbf2edc9d95 Mon Sep 17 00:00:00 2001 From: Kenzo Date: Tue, 11 Aug 2026 14:42:40 +0200 Subject: [PATCH] fix(upload): Groessengrenzen realistisch setzen, PDFs eigener Wert Der Upload lief gegen einen einzigen globalen Wert von 5 MB, der fuer alle Dateitypen galt. Eine Getraenkekarte liegt aber schnell bei 15-20 MB, das Hochladen scheiterte deshalb zuverlaessig. - MAX_FILE_SIZE (Bilder) von 5 auf 20 MB. Bilder werden ohnehin auf 1600px heruntergerechnet, die Grenze muss nur ein unbearbeitetes Handyfoto durchlassen - 5 MB reichten dafuer schon nicht. - MAX_PDF_SIZE neu, 40 MB. PDFs werden unveraendert abgelegt. - Beides ueber Umgebungsvariablen uebersteuerbar. Die Registrierung von multipart nimmt den groesseren der beiden Werte als Obergrenze, die Routen setzen ihn per request.file({ limits }) auf ihren eigenen herunter. Die Fehlermeldung im PDF-Zweig nannte bisher die Bildgrenze. Geprueft am laufenden Container, auch mit --memory=512m wie auf Fly: 18-MB- PDF und 18-MB-Foto gehen durch, 45 MB bzw. 25 MB werden mit 413 und passender Meldung abgelehnt, kein OOM. Co-Authored-By: Claude Opus 5 (1M context) --- backend/src/config/env.ts | 7 ++++++- backend/src/index.ts | 4 +++- backend/src/routes/content.ts | 3 ++- backend/src/routes/events.ts | 3 ++- backend/src/routes/gallery.ts | 3 ++- backend/src/routes/pdf.ts | 6 +++--- 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/backend/src/config/env.ts b/backend/src/config/env.ts index 13d4119..c251c4c 100644 --- a/backend/src/config/env.ts +++ b/backend/src/config/env.ts @@ -28,7 +28,12 @@ export const env = { FRONTEND_URL: process.env.FRONTEND_URL || 'http://localhost:5173', // Upload - MAX_FILE_SIZE: parseInt(process.env.MAX_FILE_SIZE || '5242880', 10), + // Bilder werden ohnehin auf 1600px heruntergerechnet, die Grenze muss nur + // gross genug fuer ein unbearbeitetes Handyfoto sein. + MAX_FILE_SIZE: parseInt(process.env.MAX_FILE_SIZE || String(20 * 1024 * 1024), 10), + // PDFs werden unveraendert abgelegt. Eine Getraenkekarte mit Bildern liegt + // schnell bei 15-20 MB, deshalb ein eigener, groesserer Wert. + MAX_PDF_SIZE: parseInt(process.env.MAX_PDF_SIZE || String(40 * 1024 * 1024), 10), }; // Validate required environment variables diff --git a/backend/src/index.ts b/backend/src/index.ts index a251732..3790f95 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -73,9 +73,11 @@ fastify.register(jwt, { }, }); +// Der globale Wert ist die Obergrenze fuer alles. Die Routen setzen ihn per +// request.file({ limits }) auf ihren eigenen, engeren Wert herunter. fastify.register(multipart, { limits: { - fileSize: env.MAX_FILE_SIZE, + fileSize: Math.max(env.MAX_FILE_SIZE, env.MAX_PDF_SIZE), }, }); diff --git a/backend/src/routes/content.ts b/backend/src/routes/content.ts index ba5478a..8781c32 100644 --- a/backend/src/routes/content.ts +++ b/backend/src/routes/content.ts @@ -5,6 +5,7 @@ import { contentSections } from '../db/schema.js'; import { eq } from 'drizzle-orm'; import { saveUploadedImage } from '../services/upload.service.js'; import { dropImageIfUnused, extractImageUrls } from '../services/image-refs.service.js'; +import { env } from '../config/env.js'; // Fastify JSON schema for content section body const contentBodyJsonSchema = { @@ -106,7 +107,7 @@ const contentRoute: FastifyPluginAsync = async (fastify) => { preHandler: [fastify.authenticate], }, async (request, reply) => { try { - const file = await (request as any).file(); + const file = await (request as any).file({ limits: { fileSize: env.MAX_FILE_SIZE } }); if (!file) { return reply.code(400).send({ error: 'No file uploaded' }); } diff --git a/backend/src/routes/events.ts b/backend/src/routes/events.ts index 03b1a8b..2beb7e6 100644 --- a/backend/src/routes/events.ts +++ b/backend/src/routes/events.ts @@ -4,6 +4,7 @@ import { events } from '../db/schema.js'; import { eq } from 'drizzle-orm'; import { dropImageIfUnused } from '../services/image-refs.service.js'; import { saveUploadedImage } from '../services/upload.service.js'; +import { env } from '../config/env.js'; /** Raeumt eine Bilddatei weg, ohne dass ein Fehler die Antwort kippt. */ async function dropUnusedImage(fastify: any, url: string | null | undefined, reason: string) { @@ -102,7 +103,7 @@ const eventsRoute: FastifyPluginAsync = async (fastify) => { }, async (request, reply) => { try { // Expect a single file field named "file" - const file = await (request as any).file(); + const file = await (request as any).file({ limits: { fileSize: env.MAX_FILE_SIZE } }); if (!file) { return reply.code(400).send({ error: 'No file uploaded' }); } diff --git a/backend/src/routes/gallery.ts b/backend/src/routes/gallery.ts index c0a4a02..ef44083 100644 --- a/backend/src/routes/gallery.ts +++ b/backend/src/routes/gallery.ts @@ -5,6 +5,7 @@ import { galleryImages } from '../db/schema.js'; import { eq } from 'drizzle-orm'; import { dropImageIfUnused } from '../services/image-refs.service.js'; import { saveUploadedImage } from '../services/upload.service.js'; +import { env } from '../config/env.js'; /** Raeumt eine Bilddatei weg, ohne dass ein Fehler die Antwort kippt. */ async function dropUnusedImage(fastify: any, url: string | null | undefined, reason: string) { @@ -81,7 +82,7 @@ const galleryRoute: FastifyPluginAsync = async (fastify) => { }, async (request, reply) => { try { // Expect a single file field named "file" - const file = await (request as any).file(); + const file = await (request as any).file({ limits: { fileSize: env.MAX_FILE_SIZE } }); if (!file) { return reply.code(400).send({ error: 'No file uploaded' }); } diff --git a/backend/src/routes/pdf.ts b/backend/src/routes/pdf.ts index f3913a0..0d15d0b 100644 --- a/backend/src/routes/pdf.ts +++ b/backend/src/routes/pdf.ts @@ -45,7 +45,7 @@ const pdfRoute: FastifyPluginAsync = async (fastify) => { } try { - const file = await (request as any).file(); + const file = await (request as any).file({ limits: { fileSize: env.MAX_PDF_SIZE } }); if (!file) { return reply.code(400).send({ error: 'No file uploaded' }); } @@ -63,7 +63,7 @@ const pdfRoute: FastifyPluginAsync = async (fastify) => { const buffer = Buffer.concat(chunks); if ((file.file as any)?.truncated) { - const limit = Math.round(env.MAX_FILE_SIZE / 1024 / 1024); + const limit = Math.round(env.MAX_PDF_SIZE / 1024 / 1024); return reply.code(413).send({ error: `PDF too large. Maximum is ${limit} MB` }); } @@ -113,7 +113,7 @@ const pdfRoute: FastifyPluginAsync = async (fastify) => { } catch (err: any) { if (err?.code === 'FST_REQ_FILE_TOO_LARGE') { - const limit = Math.round(env.MAX_FILE_SIZE / 1024 / 1024); + const limit = Math.round(env.MAX_PDF_SIZE / 1024 / 1024); return reply.code(413).send({ error: `PDF too large. Maximum is ${limit} MB` }); } fastify.log.error({ err }, 'PDF upload failed');