interface SoumissionBody { nom: string; courriel: string; telephone: string; type_service: string; adresse?: string; message: string; } interface Env { RESEND_API_KEY: string; CONTACT_EMAIL: string; } const serviceLabels: Record = { interieur: 'Peinture intérieure', exterieur: 'Peinture extérieure', commercial: 'Peinture commerciale', decoration: 'Décoration intérieure', autre: 'Autre', }; function buildEmailHtml(data: SoumissionBody): string { const serviceLabel = serviceLabels[data.type_service] ?? data.type_service; const adresseLine = data.adresse ? `Adresse des travaux${data.adresse}` : ''; return `

Nouvelle demande de soumission

Cachet Peintres Décorateurs

Vous avez reçu une nouvelle demande de soumission de la part de ${data.nom}.

${adresseLine}
Nom ${data.nom}
Courriel ${data.courriel}
Téléphone ${data.telephone}
Type de service ${serviceLabel}

Message

${data.message}
Répondre à ${data.nom}

Cachet Peintres Décorateurs · 9500-5609 Québec Inc · RBQ 5839 8736 01

`; } function validateBody(body: unknown): body is SoumissionBody { if (typeof body !== 'object' || body === null) return false; const b = body as Record; return ( typeof b.nom === 'string' && b.nom.trim().length > 0 && typeof b.courriel === 'string' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(b.courriel) && typeof b.telephone === 'string' && b.telephone.trim().length > 0 && typeof b.type_service === 'string' && b.type_service.length > 0 && typeof b.message === 'string' && b.message.trim().length > 0 ); } export const onRequestPost: PagesFunction = async (context) => { const corsHeaders = { 'Access-Control-Allow-Origin': 'https://cachetdeco.com', 'Content-Type': 'application/json', }; // Rate limiting via CF headers (basic check) const ip = context.request.headers.get('CF-Connecting-IP') ?? 'unknown'; let body: unknown; try { body = await context.request.json(); } catch { return new Response( JSON.stringify({ success: false, error: 'Corps de requête invalide.' }), { status: 400, headers: corsHeaders } ); } if (!validateBody(body)) { return new Response( JSON.stringify({ success: false, error: 'Données manquantes ou invalides.' }), { status: 422, headers: corsHeaders } ); } const apiKey = context.env.RESEND_API_KEY; const contactEmail = context.env.CONTACT_EMAIL; if (!apiKey || !contactEmail) { console.error('Missing env vars: RESEND_API_KEY or CONTACT_EMAIL'); return new Response( JSON.stringify({ success: false, error: 'Configuration serveur manquante.' }), { status: 500, headers: corsHeaders } ); } const emailPayload = { from: 'Cachet Peintres Décorateurs ', to: [contactEmail], reply_to: body.courriel, subject: `Soumission — ${serviceLabels[body.type_service] ?? body.type_service} — ${body.nom}`, html: buildEmailHtml(body), }; const resendRes = await fetch('https://api.resend.com/emails', { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify(emailPayload), }); if (!resendRes.ok) { const resendError = await resendRes.text().catch(() => 'Unknown error'); console.error('Resend error:', resendError, 'IP:', ip); return new Response( JSON.stringify({ success: false, error: 'Erreur lors de l\'envoi du courriel.' }), { status: 502, headers: corsHeaders } ); } return new Response( JSON.stringify({ success: true }), { status: 200, headers: corsHeaders } ); }; // Handle preflight OPTIONS export const onRequestOptions: PagesFunction = async () => { return new Response(null, { status: 204, headers: { 'Access-Control-Allow-Origin': 'https://cachetdeco.com', 'Access-Control-Allow-Methods': 'POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', }, }); };