feat: Grundgerüst mit Anmeldung, Layout und Design-Tokens
Next.js 16, Prisma 7, Tailwind 4 und Vitest/Playwright, Konfiguration aus Laufzettel übernommen, weil dort derselbe Stack erprobt ist. Die Farben der Branding-Vorgabe stehen als Tokens in globals.css, das helle Theme setzt dieselben Namen neu. Amber und Rot sind dort abgedunkelt, sonst reicht der Kontrast nicht – ein Test prüft beide Themes gegen 4,5:1. Anmeldung mit lokalem Benutzer, Rolle je Anfrage aus der Datenbank, Audit-Log per Trigger nur anhängbar. Geprüft im gebauten Stand auf 1440 px und 390 px. Closes #001 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { ZodError } from "@/lib/validierung";
|
||||
import { logger } from "@/lib/logger";
|
||||
import { AnwendungsFehler } from "@/server/fehler";
|
||||
|
||||
/**
|
||||
* Einheitliche Fehlerantwort der REST-API – Web-GUI und CLI lesen dasselbe Format:
|
||||
* `{ fehler: { code, nachricht, hinweis?, felder? } }`.
|
||||
*/
|
||||
export type FehlerAntwort = {
|
||||
fehler: { code: string; nachricht: string; hinweis?: string; felder?: Record<string, string> };
|
||||
};
|
||||
|
||||
export function fehlerAntwort(fehler: unknown): NextResponse<FehlerAntwort> {
|
||||
if (fehler instanceof AnwendungsFehler) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
fehler: {
|
||||
code: fehler.code,
|
||||
nachricht: fehler.message,
|
||||
...(fehler.hinweis ? { hinweis: fehler.hinweis } : {}),
|
||||
},
|
||||
},
|
||||
{ status: fehler.status },
|
||||
);
|
||||
}
|
||||
if (fehler instanceof ZodError) {
|
||||
const felder: Record<string, string> = {};
|
||||
for (const problem of fehler.issues) {
|
||||
const pfad = problem.path.join(".") || "_";
|
||||
felder[pfad] ??= problem.message;
|
||||
}
|
||||
return NextResponse.json(
|
||||
{
|
||||
fehler: {
|
||||
code: "ungueltig",
|
||||
nachricht: "Die Eingaben sind nicht vollständig oder ungültig.",
|
||||
felder,
|
||||
},
|
||||
},
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
logger.error({ err: fehler }, "Unerwarteter Fehler in der API");
|
||||
return NextResponse.json({ fehler: { code: "intern", nachricht: "Interner Fehler." } }, { status: 500 });
|
||||
}
|
||||
|
||||
/** Umhüllt einen Route Handler: wandelt geworfene Fehler in das einheitliche Format. */
|
||||
export function api<A extends unknown[]>(
|
||||
handler: (...argumente: A) => Promise<Response>,
|
||||
): (...argumente: A) => Promise<Response> {
|
||||
return async (...argumente: A) => {
|
||||
try {
|
||||
return await handler(...argumente);
|
||||
} catch (fehler) {
|
||||
return fehlerAntwort(fehler);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Liest einen JSON-Rumpf; ein kaputter Rumpf ist ein 400, kein 500. */
|
||||
export async function jsonLesen(anfrage: Request): Promise<unknown> {
|
||||
try {
|
||||
return await anfrage.json();
|
||||
} catch {
|
||||
throw new AnwendungsFehler(400, "kein_json", "Der Anfragerumpf ist kein gültiges JSON.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user