fix: "Unexpected token '<'" bei Uploads durch die echte Ursache ersetzen
Alle drei Admin-Upload-Felder riefen response.json() ungeprueft auf. Kam statt JSON eine HTML-Seite zurueck, warf der Parser Unexpected token '<', "<!DOCTYPE "... is not valid JSON und genau dieser Text landete im Formular. Damit war die eigentliche Ursache vollstaendig verdeckt: weder der HTTP-Status noch die Schicht, die abgebrochen hat, waren daraus ablesbar. Der Punkt dabei: die Upload-Route-Handler antworten ausnahmslos mit JSON, auch in jedem Fehlerfall — lokal verifiziert bis hinauf zur Limit-Ueberschreitung (105 MB -> 413 mit JSON-Body). Trifft im Browser trotzdem HTML ein, stammt die Antwort gar nicht aus der Anwendung, sondern aus einer Schicht davor: Reverse-Proxy, CDN oder Gateway, das den Request wegen zu grossem Body abgewiesen oder wegen Zeitueberschreitung beim Transcoding abgebrochen hat. readUploadResponse() in lib/upload-client.ts prueft deshalb zuerst den Content-Type und reicht andernfalls den HTTP-Status nach oben, statt zu parsen. Die Felder zeigen daraus eine Meldung, die den Status nennt und die wahrscheinliche Ursache benennt (413 = vom Proxy abgewiesen, 502/503/504/522/524 = Zeitueberschreitung beim Umrechnen, sonst unerwartet). Ein Verbindungsabbruch, bei dem fetch() selbst wirft, wird getrennt gemeldet. Das behebt nicht die Ursache jenseits der Anwendung — es macht sie zum ersten Mal ablesbar, statt sie hinter einem Parser-Fehler zu verbergen.
This commit is contained in:
1 parent
be5fb32a4a
commit
b11edd7751
8 files changed
+219
-15
No files matched your search
@@ -3,6 +3,7 @@
|
||||
import Image from "next/image";
|
||||
import { useRef, useState, type ChangeEvent } from "react";
|
||||
import { useI18n } from "@/i18n/i18n-provider";
|
||||
import { readUploadResponse } from "@/lib/upload-client";
|
||||
|
||||
interface AdminImageFieldProps {
|
||||
/** Formularfeld-Name, unter dem der aktuelle Bildpfad beim Speichern des
|
||||
@@ -36,12 +37,22 @@ export function AdminImageField({ name, label, initialValue }: AdminImageFieldPr
|
||||
try {
|
||||
const body = new FormData();
|
||||
body.append("file", file);
|
||||
const response = await fetch("/api/admin/upload", { method: "POST", body });
|
||||
const data: { path?: string; error?: string } = await response.json();
|
||||
if (!response.ok || !data.path) {
|
||||
throw new Error(data.error ?? t.adminImageField.uploadError);
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetch("/api/admin/upload", { method: "POST", body });
|
||||
} catch {
|
||||
// fetch() wirft nur, wenn der Request den Server gar nicht erreicht
|
||||
// oder die Verbindung abbricht — ein HTTP-Fehlerstatus landet NICHT
|
||||
// hier, sondern wird unten ausgewertet.
|
||||
throw new Error(t.adminImageField.uploadErrorNetwork);
|
||||
}
|
||||
setValue(data.path);
|
||||
const result = await readUploadResponse(response);
|
||||
if (!result.ok) {
|
||||
throw new Error(
|
||||
result.kind === "message" ? result.message : t.adminImageField.uploadErrorStatus(result.status)
|
||||
);
|
||||
}
|
||||
setValue(result.path);
|
||||
setStatus("idle");
|
||||
} catch (error: unknown) {
|
||||
setStatus("error");
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import Image from "next/image";
|
||||
import { useRef, useState, type ChangeEvent } from "react";
|
||||
import { useI18n } from "@/i18n/i18n-provider";
|
||||
import { readUploadResponse } from "@/lib/upload-client";
|
||||
|
||||
interface AdminImageListFieldProps {
|
||||
/** Formularfeld-Name, unter dem JEDES Bild als eigenes verstecktes Feld
|
||||
@@ -37,12 +38,22 @@ export function AdminImageListField({ name, label, initialValues }: AdminImageLi
|
||||
try {
|
||||
const body = new FormData();
|
||||
body.append("file", file);
|
||||
const response = await fetch("/api/admin/upload", { method: "POST", body });
|
||||
const data: { path?: string; error?: string } = await response.json();
|
||||
if (!response.ok || !data.path) {
|
||||
throw new Error(data.error ?? t.adminImageField.uploadError);
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetch("/api/admin/upload", { method: "POST", body });
|
||||
} catch {
|
||||
// fetch() wirft nur, wenn der Request den Server gar nicht erreicht
|
||||
// oder die Verbindung abbricht — ein HTTP-Fehlerstatus landet NICHT
|
||||
// hier, sondern wird unten ausgewertet.
|
||||
throw new Error(t.adminImageField.uploadErrorNetwork);
|
||||
}
|
||||
setImages((prev) => [...prev, data.path!]);
|
||||
const result = await readUploadResponse(response);
|
||||
if (!result.ok) {
|
||||
throw new Error(
|
||||
result.kind === "message" ? result.message : t.adminImageField.uploadErrorStatus(result.status)
|
||||
);
|
||||
}
|
||||
setImages((prev) => [...prev, result.path]);
|
||||
setStatus("idle");
|
||||
} catch (error: unknown) {
|
||||
setStatus("error");
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useRef, useState, type ChangeEvent } from "react";
|
||||
import { useI18n } from "@/i18n/i18n-provider";
|
||||
import { readUploadResponse } from "@/lib/upload-client";
|
||||
|
||||
interface AdminVideoFieldProps {
|
||||
/** Formularfeld-Name, unter dem der aktuelle Videopfad beim Speichern des
|
||||
@@ -34,12 +35,22 @@ export function AdminVideoField({ name, label, initialValue }: AdminVideoFieldPr
|
||||
try {
|
||||
const body = new FormData();
|
||||
body.append("file", file);
|
||||
const response = await fetch("/api/admin/upload-video", { method: "POST", body });
|
||||
const data: { path?: string; error?: string } = await response.json();
|
||||
if (!response.ok || !data.path) {
|
||||
throw new Error(data.error ?? t.adminVideoField.uploadError);
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetch("/api/admin/upload-video", { method: "POST", body });
|
||||
} catch {
|
||||
// fetch() wirft nur, wenn der Request den Server gar nicht erreicht
|
||||
// oder die Verbindung abbricht — ein HTTP-Fehlerstatus landet NICHT
|
||||
// hier, sondern wird unten ausgewertet.
|
||||
throw new Error(t.adminVideoField.uploadErrorNetwork);
|
||||
}
|
||||
setValue(data.path);
|
||||
const result = await readUploadResponse(response);
|
||||
if (!result.ok) {
|
||||
throw new Error(
|
||||
result.kind === "message" ? result.message : t.adminVideoField.uploadErrorStatus(result.status)
|
||||
);
|
||||
}
|
||||
setValue(result.path);
|
||||
setStatus("idle");
|
||||
} catch (error: unknown) {
|
||||
setStatus("error");
|
||||
|
||||
Reference in new issue
Block a user