Files
NexStudio/src/pages/api/contact.ts
Javaxman c4adc116d6 Corregge l'accesso all'env Worker per Astro 6.
Sostituisce locals.runtime.env con import da cloudflare:workers, ripristinando contatti e newsletter.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 14:06:16 +02:00

142 lines
4.5 KiB
TypeScript

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<string, unknown>;
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<boolean> {
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<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;
}
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);
};