Foto-Upload: 413 beheben und Kamera stabilisieren

- nginx client_max_body_size auf 12m (Standard 1 MB loeste beim Bild-Upload
  einen 413 aus, bevor das Backend den Upload sah).
- Backend-Bildlimit auf 8 MB angehoben, bleibt unter dem nginx-Limit, damit
  Uebergroesse eine klare 400-Meldung statt eines nackten 413 ergibt.
- Web: Foto vor dem Upload clientseitig verkleinern (max 1600px, JPEG) -
  schneller, kleiner in der DB, zuverlaessig unter dem Limit.
- iOS: Kamera ueber fullScreenCover statt sheet und Schliessen deterministisch
  ueber ein Binding - behebt das "Kamera geht direkt wieder zu"-Flackern.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-25 23:11:59 +02:00
parent a664410ac0
commit 5c8b46a0d5
4 changed files with 47 additions and 9 deletions

View File

@@ -31,6 +31,33 @@ const CANONICAL_NAME = { piece: "Stück", gram: "Gramm", milliliter: "Milliliter
const KIND_LABEL = { count: "Anzahl", weight: "Gewicht", volume: "Volumen" };
const KIND_ORDER = ["count", "weight", "volume"];
// Foto vor dem Hochladen verkleinern: kleiner in der DB, schneller im Upload und
// zuverlässig unter dem Server-Limit. Bei Problemen fällt es aufs Original zurück.
function verkleinereBild(datei, maxDim = 1600, quality = 0.85) {
return new Promise((resolve) => {
if (!datei || !datei.type?.startsWith("image/")) return resolve(datei);
const url = URL.createObjectURL(datei);
const img = new Image();
img.onload = () => {
URL.revokeObjectURL(url);
const faktor = Math.min(1, maxDim / Math.max(img.width, img.height));
if (faktor >= 1 && datei.size <= 1_500_000) return resolve(datei);
const w = Math.round(img.width * faktor);
const h = Math.round(img.height * faktor);
const canvas = document.createElement("canvas");
canvas.width = w;
canvas.height = h;
canvas.getContext("2d").drawImage(img, 0, 0, w, h);
canvas.toBlob(
(blob) => resolve(blob ? new File([blob], "foto.jpg", { type: "image/jpeg" }) : datei),
"image/jpeg", quality,
);
};
img.onerror = () => { URL.revokeObjectURL(url); resolve(datei); };
img.src = url;
});
}
export default function ProductForm() {
const confirm = useConfirm();
const toast = useToast();
@@ -142,7 +169,8 @@ export default function ProductForm() {
if (!file || isNew) return;
setError(null);
try {
await api.uploadProductImage(id, file);
const foto = await verkleinereBild(file);
await api.uploadProductImage(id, foto);
setBildVersion((v) => v + 1);
toast("Foto gespeichert.");
} catch (err) {