"use server"; import { redirect } from "next/navigation"; import { isRedirectError } from "next/dist/client/components/redirect"; import { z } from "zod"; import { enforceContactRateLimit, verifyTurnstileToken } from "@/lib/contact-guard"; import { getContactProtectionSettings } from "@/lib/app-config"; import { getLocalizedPath, resolveLocale } from "@/lib/locale"; import { sendContactMessage } from "@/lib/mail"; const contactFormSchema = z.object({ locale: z.string().trim().min(1), name: z.string().trim().min(2).max(120), email: z.string().trim().email(), phone: z.string().trim().max(40).optional(), company: z.string().trim().max(120).optional(), message: z.string().trim().min(10).max(5000), turnstileToken: z.string().trim().optional(), }); const contactErrorMessages = { ar: { invalid: "يرجى تعبئة كل الحقول بشكل صحيح.", failed: "تعذر إرسال الرسالة حالياً.", blocked: "تم إرسال عدد كبير من الطلبات. حاول لاحقاً.", verification: "يرجى إكمال التحقق قبل الإرسال.", }, en: { invalid: "Please fill all fields correctly.", failed: "Message could not be sent right now.", blocked: "Too many requests. Please try again later.", verification: "Please complete the verification before sending.", }, de: { invalid: "Bitte alle Felder korrekt ausfuellen.", failed: "Nachricht konnte gerade nicht gesendet werden.", blocked: "Zu viele Anfragen. Bitte spaeter erneut versuchen.", verification: "Bitte die Verifizierung vor dem Senden abschliessen.", }, } as const; function withError(pathname: string, message: string) { const params = new URLSearchParams(); params.set("error", message); return `${pathname}?${params.toString()}`; } function getStringValue(formData: FormData, key: string) { const value = formData.get(key); return typeof value === "string" ? value : ""; } export async function submitContactFormAction(formData: FormData) { const locale = resolveLocale(String(formData.get("locale") ?? "")); const contactPath = getLocalizedPath(locale, "/contact"); try { const protectionSettings = await getContactProtectionSettings(); const values = contactFormSchema.parse({ locale, name: getStringValue(formData, "name"), email: getStringValue(formData, "email"), phone: getStringValue(formData, "phone"), company: getStringValue(formData, "company"), message: getStringValue(formData, "message"), turnstileToken: getStringValue(formData, "turnstileToken"), }); await verifyTurnstileToken(protectionSettings, values.turnstileToken ?? ""); await enforceContactRateLimit(protectionSettings); await sendContactMessage({ locale, name: values.name, email: values.email, phone: values.phone, company: values.company, message: values.message, }); redirect(getLocalizedPath(locale, "/success")); } catch (error) { if (isRedirectError(error)) { throw error; } if (error instanceof z.ZodError) { redirect(withError(contactPath, contactErrorMessages[locale].invalid)); } if (error instanceof Error && error.message === "Too many contact requests. Please try again later.") { redirect(withError(contactPath, contactErrorMessages[locale].blocked)); } if ( error instanceof Error && (error.message === "Turnstile verification is required." || error.message === "Turnstile verification failed." || error.message === "Turnstile verification request failed.") ) { redirect(withError(contactPath, contactErrorMessages[locale].verification)); } console.error("Contact form delivery failed.", error); redirect(withError(contactPath, contactErrorMessages[locale].failed)); } }