import type { APIRoute } from 'astro'; import { env as workerEnv } from 'cloudflare:workers'; import { getEmailBinding, sendCloudflareEmail } from '../../lib/email'; export const prerender = false; type RuntimeEnv = Record; function getRuntimeEnv(): RuntimeEnv { return workerEnv as unknown as RuntimeEnv; } function readEnv(name: string, runtimeEnv: RuntimeEnv): string { const runtimeValue = runtimeEnv[name]; if (typeof runtimeValue === 'string' && runtimeValue.trim().length > 0) { return runtimeValue.trim(); } const staticValue = import.meta.env[name]; return typeof staticValue === 'string' ? staticValue.trim() : ''; } function sanitizeRedirect(candidate: string, requestUrl: URL): string { if (!candidate) return ''; try { const u = new URL(candidate, requestUrl); return u.toString(); } catch { return ''; } } function required(value: FormDataEntryValue | null): string { return typeof value === 'string' ? value.trim() : ''; } async function verifyTurnstile(token: string, secret: string, ip?: string | null): Promise { const body = new URLSearchParams(); body.set('secret', secret); body.set('response', token); if (ip) body.set('remoteip', ip); const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', { method: 'POST', body, }); if (!response.ok) return false; const payload = (await response.json()) as { success?: boolean }; return payload.success === true; } function resolveDepartmentRecipient(runtimeEnv: RuntimeEnv, defaultRecipient: string, department: string): string { const normalized = department.trim().toLowerCase(); const map: Record = { info: readEnv('CONTACT_FORM_TO_EMAIL_INFO', runtimeEnv) || defaultRecipient, privacy: readEnv('CONTACT_FORM_TO_EMAIL_PRIVACY', runtimeEnv), careers: readEnv('CONTACT_FORM_TO_EMAIL_CAREERS', runtimeEnv), }; return map[normalized] || defaultRecipient; } export const POST: APIRoute = async ({ request, url }) => { const runtimeEnv = getRuntimeEnv(); const mode = readEnv('CONTACT_FORM_MODE', runtimeEnv).toLowerCase() || 'dev'; const fromEmail = readEnv('CONTACT_FORM_FROM_EMAIL', runtimeEnv); const toEmail = readEnv('CONTACT_FORM_TO_EMAIL', runtimeEnv); const turnstileSecret = readEnv('CONTACT_TURNSTILE_SECRET_KEY', runtimeEnv); const form = await request.formData(); const redirectTarget = sanitizeRedirect(required(form.get('_next')), url) || sanitizeRedirect(readEnv('PUBLIC_CONTACT_FORM_SUCCESS_REDIRECT', runtimeEnv), url) || sanitizeRedirect(request.headers.get('referer') ?? '/', url) || '/'; const name = required(form.get('name')); const company = required(form.get('company')); const email = required(form.get('email')); const country = required(form.get('country')); const department = required(form.get('department')) || 'info'; const message = required(form.get('message')); if (!name || !email || !country || !message) { return new Response('Missing required contact fields.', { status: 400 }); } const turnstileToken = required(form.get('cf-turnstile-response')); if (turnstileSecret) { const isHuman = await verifyTurnstile( turnstileToken, turnstileSecret, request.headers.get('CF-Connecting-IP'), ); if (!isHuman) { return new Response('Turnstile verification failed.', { status: 400 }); } } const subject = required(form.get('_subject')) || 'Richiesta contatto - sito NexStudio'; const text = [ `Nome: ${name}`, company ? `Azienda: ${company}` : '', `Email: ${email}`, `Paese: ${country}`, `Reparto: ${department}`, '', message, ] .filter(Boolean) .join('\n'); if (mode === 'live') { const emailBinding = getEmailBinding(runtimeEnv); if (!emailBinding || !fromEmail || !toEmail) { return new Response( 'Live mode requires EMAIL binding (Cloudflare Email Service), CONTACT_FORM_FROM_EMAIL and CONTACT_FORM_TO_EMAIL.', { status: 500, }, ); } const destination = resolveDepartmentRecipient(runtimeEnv, toEmail, department); await sendCloudflareEmail(emailBinding, { fromEmail, toEmail: destination, subject, text, replyTo: email, }); } else { console.info('[contact:dev] Contact payload received', { name, company, email, country, department, subject, }); } return Response.redirect(redirectTarget, 303); };