Fix invio contatti + cursor pointer sul submit #24
@ -230,7 +230,7 @@ const fieldClass =
|
|||||||
<div class="pt-1">
|
<div class="pt-1">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="inline-flex h-10 w-full items-center justify-center rounded-lg bg-nx-fg px-5 text-sm font-semibold text-nx-bg hover:bg-white sm:w-auto sm:min-w-[10rem]"
|
class="inline-flex h-10 w-full cursor-pointer items-center justify-center rounded-lg bg-nx-fg px-5 text-sm font-semibold text-nx-bg hover:bg-white sm:w-auto sm:min-w-[10rem]"
|
||||||
>
|
>
|
||||||
{cf.submitLabel}
|
{cf.submitLabel}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -13,10 +13,16 @@ function getRuntimeEnv(): RuntimeEnv {
|
|||||||
function readEnv(name: string, runtimeEnv: RuntimeEnv): string {
|
function readEnv(name: string, runtimeEnv: RuntimeEnv): string {
|
||||||
const runtimeValue = runtimeEnv[name];
|
const runtimeValue = runtimeEnv[name];
|
||||||
if (typeof runtimeValue === 'string' && runtimeValue.trim().length > 0) {
|
if (typeof runtimeValue === 'string' && runtimeValue.trim().length > 0) {
|
||||||
return runtimeValue.trim();
|
return runtimeValue.trim().replace(/^['"]+|['"]+$/g, '').trim();
|
||||||
}
|
}
|
||||||
const staticValue = import.meta.env[name];
|
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 {
|
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;
|
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 normalized = department.trim().toLowerCase();
|
||||||
const map: Record<string, string> = {
|
if (!normalized || normalized === 'info') {
|
||||||
info: readEnv('CONTACT_FORM_TO_EMAIL_INFO', runtimeEnv) || defaultRecipient,
|
return defaultRecipient;
|
||||||
privacy: readEnv('CONTACT_FORM_TO_EMAIL_PRIVACY', runtimeEnv),
|
}
|
||||||
careers: readEnv('CONTACT_FORM_TO_EMAIL_CAREERS', runtimeEnv),
|
const override =
|
||||||
};
|
normalized === 'privacy'
|
||||||
return map[normalized] || defaultRecipient;
|
? 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 }) => {
|
export const POST: APIRoute = async ({ request, url }) => {
|
||||||
try {
|
try {
|
||||||
const runtimeEnv = getRuntimeEnv();
|
const runtimeEnv = getRuntimeEnv();
|
||||||
const mode = readEnv('CONTACT_FORM_MODE', runtimeEnv).toLowerCase() || 'dev';
|
const mode = readEnv('CONTACT_FORM_MODE', runtimeEnv).toLowerCase() || 'dev';
|
||||||
|
// Preferisci il mittente newsletter (secret già usato con successo) sul Variable del form.
|
||||||
const fromEmail =
|
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 =
|
const toEmail =
|
||||||
readEnv('CONTACT_FORM_TO_EMAIL', runtimeEnv) ||
|
readEnv('CONTACT_FORM_TO_EMAIL', runtimeEnv) ||
|
||||||
readEnv('NEWSLETTER_NOTIFY_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);
|
return fail(request, USER_SEND_ERROR, 500);
|
||||||
}
|
}
|
||||||
const destination = resolveDepartmentRecipient(runtimeEnv, toEmail, department);
|
const destination = resolveDepartmentRecipient(runtimeEnv, toEmail, department);
|
||||||
if (!destination) {
|
if (!looksLikeEmail(fromEmail) || !looksLikeEmail(destination)) {
|
||||||
console.error('[contact] empty department recipient', { department });
|
console.error('[contact] invalid from/to email shape', {
|
||||||
|
fromOk: looksLikeEmail(fromEmail),
|
||||||
|
toOk: looksLikeEmail(destination),
|
||||||
|
department,
|
||||||
|
});
|
||||||
return fail(request, USER_SEND_ERROR, 500);
|
return fail(request, USER_SEND_ERROR, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +213,13 @@ export const POST: APIRoute = async ({ request, url }) => {
|
|||||||
replyTo: email,
|
replyTo: email,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} 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.
|
// Retry without replyTo: some payloads reject Reply-To even when From/To are valid.
|
||||||
try {
|
try {
|
||||||
await sendCloudflareEmail(emailBinding, {
|
await sendCloudflareEmail(emailBinding, {
|
||||||
@ -202,6 +231,8 @@ export const POST: APIRoute = async ({ request, url }) => {
|
|||||||
} catch (retryError) {
|
} catch (retryError) {
|
||||||
console.error('[contact] email retry failed', {
|
console.error('[contact] email retry failed', {
|
||||||
code: emailErrorCode(retryError),
|
code: emailErrorCode(retryError),
|
||||||
|
fromDomain: fromEmail.split('@')[1] || '',
|
||||||
|
toDomain: destination.split('@')[1] || '',
|
||||||
error: retryError,
|
error: retryError,
|
||||||
});
|
});
|
||||||
return fail(request, userEmailErrorMessage(retryError), 502);
|
return fail(request, userEmailErrorMessage(retryError), 502);
|
||||||
|
|||||||
Reference in New Issue
Block a user