export const dynamic = "force-dynamic"; import { cancelAppointmentByToken } from "@/lib/services/appointments"; import { cancelSchema } from "@/lib/validators/public"; import { fail, ok } from "@/lib/api"; import { enforceRateLimit } from "@/lib/rate-limit"; import { readJsonBody, validateMutationRequestOrigin } from "@/lib/security/request"; export async function GET() { return fail("Bitte verwende POST für die Stornierung.", 405); } export async function POST(req: Request) { const originError = validateMutationRequestOrigin(req); if (originError) return originError; const bodyResult = await readJsonBody(req, { maxBytes: 8 * 1024 }); if (!bodyResult.ok) return bodyResult.response; const parsed = cancelSchema.safeParse({ token: (bodyResult.data as { token?: string }).token }); const limit = enforceRateLimit({ req, scope: "public-cancel", limit: 30, windowMs: 60_000, ...(parsed.success ? { keySuffix: parsed.data.token.slice(0, 12) } : {}) }); if (!limit.ok) { return fail("Zu viele Anfragen. Bitte kurz warten.", 429, { retryAfterSeconds: limit.retryAfterSeconds }); } if (!parsed.success) { return fail("Token fehlt oder ist ungültig", 400, parsed.error.flatten()); } const result = await cancelAppointmentByToken(parsed.data.token); if (!result.ok) { return fail(result.message ?? "Stornierung fehlgeschlagen", result.status ?? 400); } return ok({ message: "Termin erfolgreich storniert" }); }