Ampel / ampel (push) Failing after 21s
- Der SPA-Rueckfall lieferte fuer unbekannte /api-Pfade die Startseite (HTML, 200). Im
ersten Probelauf stuerzte der Start daran ab (/api/radar gab es noch nicht). Jetzt 404;
api() behandelt Nicht-JSON als Fehler, der Router zeigt eine deutsche Fehleranzeige.
- Waechter: Skriptmeldungen ("! ...", fehlgeschlagen) gehen vor systemd-Rahmenzeilen wie
"Failed to start ..." - die sagen nur, dass es scheiterte, nicht warum.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
53 lines
1.8 KiB
TypeScript
53 lines
1.8 KiB
TypeScript
// Dünner Zugriff auf das MC2-Backend. Bewusst ohne Anmelde- oder Geheimnis-Felder:
|
|
// Nichts, was der Browser speichert, reist hier mit (Lehre aus v3-Umbau P1).
|
|
|
|
export class ApiFehler extends Error {
|
|
constructor(
|
|
public status: number,
|
|
public detail: string,
|
|
) {
|
|
super(detail)
|
|
this.name = "ApiFehler"
|
|
}
|
|
}
|
|
|
|
/** FastAPI legt Fehlertexte in `detail` ab — manchmal als Text, manchmal als Liste. */
|
|
function detailAus(body: unknown, status: number): string {
|
|
if (body && typeof body === "object" && "detail" in body) {
|
|
const d = (body as { detail: unknown }).detail
|
|
if (typeof d === "string") return d
|
|
if (Array.isArray(d)) return d.map((x) => (x as { msg?: string }).msg ?? String(x)).join("; ")
|
|
}
|
|
return `Die Box antwortet mit Fehler ${status}.`
|
|
}
|
|
|
|
export async function api<T>(pfad: string, init: RequestInit = {}): Promise<T> {
|
|
let antwort: Response
|
|
try {
|
|
antwort = await fetch(pfad, {
|
|
...init,
|
|
headers: { "Content-Type": "application/json", Accept: "application/json", ...init.headers },
|
|
})
|
|
} catch {
|
|
throw new ApiFehler(0, "MC2 ist nicht erreichbar. Läuft die Box?")
|
|
}
|
|
const text = await antwort.text()
|
|
const body = text ? safeJson(text) : null
|
|
if (!antwort.ok) throw new ApiFehler(antwort.status, detailAus(body, antwort.status))
|
|
// Kommt statt JSON eine HTML-Seite (alter Server, falscher Pfad), ist das ein Fehler —
|
|
// sonst rechnen die Ansichten mit einem Text, als wären es Daten.
|
|
if (typeof body === "string") throw new ApiFehler(antwort.status, "Die Box antwortet nicht mit Daten.")
|
|
return body as T
|
|
}
|
|
|
|
function safeJson(text: string): unknown {
|
|
try {
|
|
return JSON.parse(text)
|
|
} catch {
|
|
return text
|
|
}
|
|
}
|
|
|
|
export const post = <T>(pfad: string, daten?: unknown) =>
|
|
api<T>(pfad, { method: "POST", body: daten === undefined ? undefined : JSON.stringify(daten) })
|