From 03aabd29cc0da09a6299ff81af60dd5082c77882 Mon Sep 17 00:00:00 2001 From: Javaxman Date: Wed, 29 Jul 2026 17:20:08 +0200 Subject: [PATCH] Corregge destinatario contatti e cursore sul pulsante Invia. (#24) Ignora override INFO malformati, preferisce il mittente newsletter e usa cursor-pointer. Co-authored-by: Cursor --- src/components/ContactFormInline.astro | 2 +- src/pages/api/contact.ts | 59 ++++++++++++++++++++------ 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/components/ContactFormInline.astro b/src/components/ContactFormInline.astro index 54ad4f9..76a9f53 100644 --- a/src/components/ContactFormInline.astro +++ b/src/components/ContactFormInline.astro @@ -230,7 +230,7 @@ const fieldClass =
diff --git a/src/pages/api/contact.ts b/src/pages/api/contact.ts index 6bd9d57..80745e3 100644 --- a/src/pages/api/contact.ts +++ b/src/pages/api/contact.ts @@ -13,10 +13,16 @@ function getRuntimeEnv(): RuntimeEnv { function readEnv(name: string, runtimeEnv: RuntimeEnv): string { const runtimeValue = runtimeEnv[name]; if (typeof runtimeValue === 'string' && runtimeValue.trim().length > 0) { - return runtimeValue.trim(); + return runtimeValue.trim().replace(/^['"]+|['"]+$/g, '').trim(); } const staticValue = import.meta.env[name]; - return typeof staticValue === 'string' ? staticValue.trim() : ''; + return typeof staticValue === 'string' + ? staticValue.trim().replace(/^['"]+|['"]+$/g, '').trim() + : ''; +} + +function looksLikeEmail(value: string): boolean { + return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value); } function sanitizeRedirect(candidate: string, requestUrl: URL): string { @@ -87,23 +93,36 @@ async function verifyTurnstile(token: string, secret: string, ip?: string | null return payload.success === true; } -function resolveDepartmentRecipient(runtimeEnv: RuntimeEnv, defaultRecipient: string, department: string): string { +/** + * Destinatario per area. Override solo se l'indirizzo è valido; + * altrimenti usa sempre CONTACT_FORM_TO_EMAIL (evita secret INFO/PRIVACY malformati). + */ +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; + if (!normalized || normalized === 'info') { + return defaultRecipient; + } + const override = + normalized === 'privacy' + ? readEnv('CONTACT_FORM_TO_EMAIL_PRIVACY', runtimeEnv) + : normalized === 'careers' + ? readEnv('CONTACT_FORM_TO_EMAIL_CAREERS', runtimeEnv) + : ''; + return looksLikeEmail(override) ? override : defaultRecipient; } export const POST: APIRoute = async ({ request, url }) => { try { const runtimeEnv = getRuntimeEnv(); const mode = readEnv('CONTACT_FORM_MODE', runtimeEnv).toLowerCase() || 'dev'; + // Preferisci il mittente newsletter (secret già usato con successo) sul Variable del form. const fromEmail = - readEnv('CONTACT_FORM_FROM_EMAIL', runtimeEnv) || - readEnv('NEWSLETTER_FROM_EMAIL', runtimeEnv); + readEnv('NEWSLETTER_FROM_EMAIL', runtimeEnv) || + readEnv('CONTACT_FORM_FROM_EMAIL', runtimeEnv); const toEmail = readEnv('CONTACT_FORM_TO_EMAIL', runtimeEnv) || readEnv('NEWSLETTER_NOTIFY_TO_EMAIL', runtimeEnv); @@ -176,8 +195,12 @@ export const POST: APIRoute = async ({ request, url }) => { return fail(request, USER_SEND_ERROR, 500); } const destination = resolveDepartmentRecipient(runtimeEnv, toEmail, department); - if (!destination) { - console.error('[contact] empty department recipient', { department }); + if (!looksLikeEmail(fromEmail) || !looksLikeEmail(destination)) { + console.error('[contact] invalid from/to email shape', { + fromOk: looksLikeEmail(fromEmail), + toOk: looksLikeEmail(destination), + department, + }); return fail(request, USER_SEND_ERROR, 500); } @@ -190,7 +213,13 @@ export const POST: APIRoute = async ({ request, url }) => { replyTo: email, }); } catch (error) { - console.error('[contact] email failed', error); + console.error('[contact] email failed', { + code: emailErrorCode(error), + fromDomain: fromEmail.split('@')[1] || '', + toDomain: destination.split('@')[1] || '', + department, + error, + }); // Retry without replyTo: some payloads reject Reply-To even when From/To are valid. try { await sendCloudflareEmail(emailBinding, { @@ -202,6 +231,8 @@ export const POST: APIRoute = async ({ request, url }) => { } catch (retryError) { console.error('[contact] email retry failed', { code: emailErrorCode(retryError), + fromDomain: fromEmail.split('@')[1] || '', + toDomain: destination.split('@')[1] || '', error: retryError, }); return fail(request, userEmailErrorMessage(retryError), 502);