feat(photo-upload): Limits hochschrauben fuer Tablet-Fotos
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 2m16s

Tablet- und iPad-Pro-Kameras liefern JPEGs/HEICs bis 15 MB. Mit den
alten 8-/10-MB-Limits scheiterte das Upload beim SvelteKit-Body-Parser
mit "Multipart erwartet" (undurchsichtiger Fehler, weil SvelteKit den
Body frueher abweist als unser Endpoint-Check).

- Endpoint MAX_BYTES: 8 -> 20 MB
- BODY_SIZE_LIMIT: 10 -> 25 MB (mit Multipart-Overhead)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-21 13:31:34 +02:00
parent 173d9d138d
commit 3bc7fa16e2
3 changed files with 11 additions and 6 deletions

View File

@@ -17,9 +17,10 @@ services:
- GEMINI_API_KEY=${GEMINI_API_KEY:-} - GEMINI_API_KEY=${GEMINI_API_KEY:-}
- GEMINI_MODEL=${GEMINI_MODEL:-gemini-2.5-flash} - GEMINI_MODEL=${GEMINI_MODEL:-gemini-2.5-flash}
- GEMINI_TIMEOUT_MS=${GEMINI_TIMEOUT_MS:-20000} - GEMINI_TIMEOUT_MS=${GEMINI_TIMEOUT_MS:-20000}
# adapter-node-Default ist 512 KB; Rezept-Fotos koennen bis 8 MB sein. # adapter-node-Default ist 512 KB. Tablet- und iPad-Pro-Kameras liefern
# Multipart-Overhead einrechnen -> 10 MB gibt etwas Puffer. # JPEGs/HEICs bis 15 MB. Endpoint-Limit ist 20 MB; hier 25 MB fuer den
- BODY_SIZE_LIMIT=10000000 # Multipart-Overhead.
- BODY_SIZE_LIMIT=25000000
depends_on: depends_on:
- searxng - searxng
restart: unless-stopped restart: unless-stopped

View File

@@ -6,7 +6,11 @@ import { pickRandomPhrase } from '$lib/server/ai/description-phrases';
import { createRateLimiter } from '$lib/server/ai/rate-limit'; import { createRateLimiter } from '$lib/server/ai/rate-limit';
import type { Ingredient, Step } from '$lib/types'; import type { Ingredient, Step } from '$lib/types';
const MAX_BYTES = 8 * 1024 * 1024; // 20 MB deckt auch Tablet- und iPad-Pro-Fotos ab (oft 10-15 MB JPEG/HEIC).
// Muss zusammen mit BODY_SIZE_LIMIT (docker-compose.prod.yml) hochgezogen werden --
// SvelteKit rejected groessere Bodies frueher und wirft dann undurchsichtige
// "Multipart erwartet"-Fehler.
const MAX_BYTES = 20 * 1024 * 1024;
const ALLOWED_MIME = new Set([ const ALLOWED_MIME = new Set([
'image/jpeg', 'image/jpeg',
'image/png', 'image/png',

View File

@@ -70,8 +70,8 @@ describe('POST /api/recipes/extract-from-photo', () => {
expect(body.recipe.id).toBeNull(); expect(body.recipe.id).toBeNull();
}); });
it('413 when file exceeds 8 MB', async () => { it('413 when file exceeds 20 MB', async () => {
const big = Buffer.alloc(9 * 1024 * 1024); const big = Buffer.alloc(21 * 1024 * 1024);
const fd = new FormData(); const fd = new FormData();
fd.append('photo', new Blob([new Uint8Array(big)], { type: 'image/jpeg' })); fd.append('photo', new Blob([new Uint8Array(big)], { type: 'image/jpeg' }));
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any