import { NextResponse } from "next/server"; import { randomUUID } from "node:crypto"; import { getPayload } from "payload"; import config from "@payload-config"; import { getClientIp, checkRateLimit } from "@/lib/auth/rateLimit"; import { newConversationAdminEmail } from "@/lib/email/conversationTemplates"; import { sendEmail } from "@/lib/email/sendBookingEmails"; import { verifyTurnstileToken } from "@/lib/turnstile/server"; export const dynamic = "force-dynamic"; type Body = { name?: unknown; email?: unknown; message?: unknown; subject?: unknown; type?: unknown; token?: unknown; }; function clean(value: unknown, max: number): string { return typeof value === "string" ? value.trim().slice(0, max) : ""; } export async function POST(request: Request) { let body: Body; try { body = await request.json(); } catch { return NextResponse.json({ error: "Ungültige Anfrage." }, { status: 400 }); } const name = clean(body.name, 160); const email = clean(body.email, 254).toLowerCase(); const message = clean(body.message, 5000); const subject = clean(body.subject, 180) || "Allgemeine Anfrage"; const type = body.type === "appointment" ? "appointment" : "contact"; if (!name || !email || !message || !/^\S+@\S+\.\S+$/.test(email)) { return NextResponse.json({ error: "Bitte fülle alle Pflichtfelder korrekt aus." }, { status: 400 }); } if (!(await verifyTurnstileToken(body.token, "contact", request))) { return NextResponse.json({ error: "Sicherheitsprüfung fehlgeschlagen. Bitte versuche es erneut." }, { status: 403 }); } if (!checkRateLimit(`contact:ip:${getClientIp(request)}`, { max: 8, windowMs: 60 * 60 * 1000 })) { return NextResponse.json({ error: "Zu viele Anfragen. Bitte versuche es später erneut." }, { status: 429 }); } try { const payload = await getPayload({ config }); const { user } = await payload.auth({ headers: request.headers }); const customerId = user?.collection === "customers" ? user.id : undefined; const conversation = await payload.create({ collection: "conversations", overrideAccess: true, data: { name, email, subject, type, status: "open", threadKey: randomUUID(), ...(customerId ? { customer: customerId } : {}), lastMessageAt: new Date().toISOString(), }, }); await payload.create({ collection: "conversation-messages", overrideAccess: true, data: { conversation: conversation.id, senderType: "visitor", senderName: name, senderEmail: email, body: message, }, }); const contact = await payload.findGlobal({ slug: "contact", overrideAccess: true }); const serverUrl = process.env.NEXT_PUBLIC_SERVER_URL || new URL(request.url).origin; const adminUrl = `${serverUrl}/admin/collections/conversations/${conversation.id}`; if (contact.email) { await sendEmail(contact.email, newConversationAdminEmail({ name, email, subject, body: message, adminUrl, threadKey: conversation.threadKey }), { replyTo: process.env.INBOUND_REPLY_TO || "reply-to@anouma.org", }); } return NextResponse.json({ ok: true }); } catch (error) { console.error("contact inquiry failed", error); return NextResponse.json({ error: "Die Nachricht konnte nicht gesendet werden. Bitte versuche es später erneut." }, { status: 500 }); } }