/** * Reine Hilfsfunktionen der Flash-Meldungen (siehe lib/flash.ts) ohne * next/headers — damit auch der Proxy (src/proxy.ts) und Route Handler sie * nutzen koennen. */ export const FLASH_COOKIE = "wh_flash"; export const MAX_FLASH_MESSAGE_LENGTH = 300; /** Ohne JavaScript loescht niemand das Cookie — dann verfaellt es von selbst. */ const FLASH_MAX_AGE_SECONDS = 60; const FLASH_KINDS = ["success", "error", "info"] as const; export type FlashKind = (typeof FLASH_KINDS)[number]; export interface Flash { id: string; kind: FlashKind; message: string; } export function serializeFlash(flash: Flash): string { return encodeURIComponent(JSON.stringify({ ...flash, message: flash.message.slice(0, MAX_FLASH_MESSAGE_LENGTH) })); } export function parseFlash(raw: string | undefined): Flash | null { if (!raw) return null; let value: unknown; try { value = JSON.parse(decodeURIComponent(raw)); } catch { return null; } if (typeof value !== "object" || value === null) return null; const { id, kind, message } = value as Record; if (typeof id !== "string" || typeof message !== "string" || message.trim() === "") return null; if (!(FLASH_KINDS as readonly unknown[]).includes(kind)) return null; return { id: id.slice(0, 64), kind: kind as FlashKind, message: message.slice(0, MAX_FLASH_MESSAGE_LENGTH) }; } export function flashCookieOptions() { return { path: "/", maxAge: FLASH_MAX_AGE_SECONDS, sameSite: "lax" as const, secure: process.env.NODE_ENV === "production", httpOnly: false, }; } /** * Fuer Route Handler, die selbst eine NextResponse zurueckgeben (z.B. der * Link aus der Bestaetigungs-E-Mail): haengt die Meldung direkt an die * Weiterleitung. */ export function withFlash unknown } }>( response: T, kind: FlashKind, message: string ): T { response.cookies.set(FLASH_COOKIE, serializeFlash({ id: crypto.randomUUID(), kind, message }), flashCookieOptions()); return response; }