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 <cursoragent@cursor.com>
This commit is contained in:
@ -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<string, string> = {
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user