"use server"; import { redirect } from "next/navigation"; import { isRedirectError } from "next/dist/client/components/redirect-error"; import { z } from "zod"; import { enforceContactRateLimit, verifyTurnstileToken } from "@/lib/contact-guard"; import { getContactProtectionSettings, getSiteSettings } 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; const contactSuccessMessages = { ar: "شكراً على رسالتك.", en: "Thank you for your message.", de: "Vielen Dank fuer deine Nachricht.", } as const; function withMessage(pathname: string, type: "success" | "error", message: string) { const params = new URLSearchParams(); params.set(type, 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 siteSettings = await getSiteSettings(); const contactPath = getLocalizedPath(locale, "/contact", siteSettings.defaultLocale); 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( withMessage( getLocalizedPath(locale, "/success", siteSettings.defaultLocale), "success", contactSuccessMessages[locale], ), ); } catch (error) { if (isRedirectError(error)) { throw error; } if (error instanceof z.ZodError) { redirect(withMessage(contactPath, "error", contactErrorMessages[locale].invalid)); } if (error instanceof Error && error.message === "Too many contact requests. Please try again later.") { redirect(withMessage(contactPath, "error", 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(withMessage(contactPath, "error", contactErrorMessages[locale].verification)); } console.error("Contact form delivery failed.", error); redirect(withMessage(contactPath, "error", contactErrorMessages[locale].failed)); } }