feat(worker): re-enable welcome-back email via Worker + Resend (footer 2026) #8
@ -238,6 +238,39 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── action: sendWelcomeBack ─────────────────────────────
|
||||||
|
// Envoie un email "Vous êtes déjà inscrit" au client qui tente
|
||||||
|
// une ré-inscription. Idempotent côté HubSpot (= aucune création
|
||||||
|
// ni update de contact). Anti-bot via Turnstile + sanity check
|
||||||
|
// que l'email existe vraiment dans HubSpot avant d'envoyer.
|
||||||
|
if (action === 'sendWelcomeBack') {
|
||||||
|
if (!body.email) return jsonResponse({ error: 'email requis' }, 400);
|
||||||
|
|
||||||
|
const turnstileOk = await verifyTurnstile(env, body.turnstile_token, request);
|
||||||
|
if (!turnstileOk) {
|
||||||
|
return jsonResponse({ ok: false, error: 'Turnstile validation failed' }, 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Vérification : le contact existe bien (= prevent spam vers
|
||||||
|
// emails inconnus en passant un faux turnstile)
|
||||||
|
const search = await searchContactByEmail(token, body.email);
|
||||||
|
const existing = (search.results || [])[0];
|
||||||
|
if (!existing) {
|
||||||
|
return jsonResponse({ ok: false, error: 'Contact not found' }, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
await sendWelcomeBackViaResend(env, {
|
||||||
|
firstname : body.firstname || existing.properties?.firstname || '',
|
||||||
|
email : body.email,
|
||||||
|
reference_client : existing.properties?.reference_client || '',
|
||||||
|
});
|
||||||
|
return jsonResponse({ ok: true });
|
||||||
|
} catch (err) {
|
||||||
|
return jsonResponse({ ok: false, error: err.message }, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── action: listSubscriptions (debug : trouver les IDs) ──
|
// ── action: listSubscriptions (debug : trouver les IDs) ──
|
||||||
if (action === 'listSubscriptions') {
|
if (action === 'listSubscriptions') {
|
||||||
// Endpoint legacy email/public/v1 nécessite scope content au lieu de
|
// Endpoint legacy email/public/v1 nécessite scope content au lieu de
|
||||||
@ -530,6 +563,79 @@ async function sendWelcomeViaResend(env, contact) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Email "Ravis de vous revoir" pour les clients déjà inscrits qui retentent
|
||||||
|
// le formulaire de contact. Rappelle le numéro de référence existant et
|
||||||
|
// précise que le contact n'est pas re-créé (= anti-doublon HubSpot).
|
||||||
|
// Idempotent côté HubSpot (= zéro write). Footer 2026 cohérent avec les
|
||||||
|
// autres emails Resend.
|
||||||
|
async function sendWelcomeBackViaResend(env, contact) {
|
||||||
|
const siteUrl = env.SITE_URL || 'https://mva-globalfret.com';
|
||||||
|
const logoUrl = `${siteUrl}/PNG%20MVA%20GLOBAL%20FRET.png`;
|
||||||
|
const firstname = escapeHtml(contact.firstname || '');
|
||||||
|
const ref = escapeHtml(contact.reference_client || '');
|
||||||
|
const refBlock = ref
|
||||||
|
? `<div style="margin:24px 0;padding:18px 24px;background:#1a1a3e;border:2px solid #c5a55a;border-radius:10px;text-align:center;">
|
||||||
|
<div style="color:#c5a55a;font-size:11px;font-weight:700;letter-spacing:1.5px;margin-bottom:6px;">VOTRE NUMÉRO DE RÉFÉRENCE EXISTANT</div>
|
||||||
|
<div style="color:#fff;font-size:28px;font-weight:700;letter-spacing:3px;">${ref}</div>
|
||||||
|
</div>`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
const html = `<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<body style="margin:0;padding:0;font-family:Arial,sans-serif;background:#f5f5f5;">
|
||||||
|
<div style="max-width:600px;margin:0 auto;background:#fff;">
|
||||||
|
<div style="background:#1a1a3e;padding:24px 30px;">
|
||||||
|
<table role="presentation" cellspacing="0" cellpadding="0" border="0" style="width:100%;border-collapse:collapse;">
|
||||||
|
<tr>
|
||||||
|
<td style="width:80px;vertical-align:middle;">
|
||||||
|
<img src="${logoUrl}" alt="MVA Global Fret" style="display:block;width:70px;height:auto;border:0;">
|
||||||
|
</td>
|
||||||
|
<td style="vertical-align:middle;text-align:center;padding-right:80px;">
|
||||||
|
<div style="color:#c5a55a;font-size:24px;font-weight:700;letter-spacing:2px;">MVA GLOBAL FRET</div>
|
||||||
|
<div style="color:#fff;font-size:13px;margin-top:6px;">Fret Aérien Paris — Antananarivo</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div style="padding:40px;">
|
||||||
|
<p style="font-size:18px;color:#1a1a3e;font-weight:bold;">Bonjour ${firstname || 'cher client'},</p>
|
||||||
|
<p style="color:#333;line-height:1.6;">
|
||||||
|
Votre adresse email est <strong>déjà enregistrée</strong> chez MVA Global Fret.
|
||||||
|
Pas besoin de vous ré-inscrire — votre compte est actif.
|
||||||
|
</p>
|
||||||
|
${refBlock}
|
||||||
|
<p style="color:#333;line-height:1.6;">
|
||||||
|
Utilisez ce numéro pour étiqueter vos colis et les rendre traçables tout au long du transport Paris → Antananarivo.
|
||||||
|
</p>
|
||||||
|
<p style="color:#333;line-height:1.6;">
|
||||||
|
Pour toute question, n'hésitez pas à nous contacter :
|
||||||
|
</p>
|
||||||
|
<ul style="color:#333;line-height:1.8;padding-left:18px;">
|
||||||
|
<li><a href="mailto:mvaglobalfret@gmail.com" style="color:#c5a55a;text-decoration:none;">mvaglobalfret@gmail.com</a></li>
|
||||||
|
<li><a href="tel:+33780970825" style="color:#c5a55a;text-decoration:none;">+33 7 80 97 08 25</a> (France)</li>
|
||||||
|
<li><a href="tel:+261384973751" style="color:#c5a55a;text-decoration:none;">+261 38 49 737 51</a> (Madagascar)</li>
|
||||||
|
</ul>
|
||||||
|
<p style="color:#666;font-size:13px;line-height:1.6;border-top:1px solid #eee;padding-top:18px;margin-top:30px;">
|
||||||
|
À très bientôt pour votre prochain envoi,<br>
|
||||||
|
<strong>L'équipe MVA Global Fret</strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div style="background:#1a1a3e;padding:18px;text-align:center;color:#c5a55a;font-size:12px;">
|
||||||
|
© 2026 MVA Global Fret — Antananarivo 101, Madagascar
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
|
||||||
|
return resendSend(env, {
|
||||||
|
to: contact.email,
|
||||||
|
subject: ref
|
||||||
|
? `Vous êtes déjà inscrit chez MVA Global Fret — Référence ${contact.reference_client}`
|
||||||
|
: 'Vous êtes déjà inscrit chez MVA Global Fret',
|
||||||
|
html,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function escapeHtml(s) {
|
function escapeHtml(s) {
|
||||||
return String(s)
|
return String(s)
|
||||||
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||||
|
|||||||
@ -310,18 +310,27 @@ function showSuccess(_refNumber, _clientData) {
|
|||||||
|
|
||||||
// ── EMAIL "RAVIS DE TE REVOIR" (client déjà inscrit) ──────────────────────────
|
// ── EMAIL "RAVIS DE TE REVOIR" (client déjà inscrit) ──────────────────────────
|
||||||
// Rappelle au client son numéro de référence existant — n'écrit RIEN dans HubSpot.
|
// Rappelle au client son numéro de référence existant — n'écrit RIEN dans HubSpot.
|
||||||
|
// Passe par le Cloudflare Worker (action: sendWelcomeBack) qui délègue à Resend
|
||||||
|
// — footer 2026 cohérent avec les autres emails transactionnels. Anti-bot via
|
||||||
|
// Turnstile : on transmet le token déjà validé au moment du submit du formulaire.
|
||||||
async function sendWelcomeBackEmail(contact) {
|
async function sendWelcomeBackEmail(contact) {
|
||||||
if (typeof emailjs === 'undefined') return;
|
if (!WORKER_PROXY_URL) return;
|
||||||
if (!contact || !contact.email) return;
|
if (!contact || !contact.email) return;
|
||||||
|
if (!window.turnstileToken) return;
|
||||||
try {
|
try {
|
||||||
await emailjs.send(EMAILJS_SERVICE_ID, EMAILJS_TEMPLATE_WELCOME_BACK, {
|
await fetch(WORKER_PROXY_URL, {
|
||||||
firstname: contact.firstname || '',
|
method: 'POST',
|
||||||
email: contact.email,
|
headers: { 'Content-Type': 'application/json' },
|
||||||
reference_client: contact.reference_client || '',
|
body: JSON.stringify({
|
||||||
|
action : 'sendWelcomeBack',
|
||||||
|
email : contact.email,
|
||||||
|
firstname : contact.firstname || '',
|
||||||
|
turnstile_token : window.turnstileToken,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Si le template n'existe pas encore ou erreur réseau : on n'interrompt rien
|
// Erreur réseau : on n'interrompt pas l'UX (le client voit déjà sa ref dans le UI)
|
||||||
console.warn('EmailJS welcome-back email failed:', err);
|
console.warn('Worker sendWelcomeBack failed:', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,11 +360,8 @@ function showAlreadyRegistered(contact) {
|
|||||||
// Envoi d'une notification interne à MVA (sans modifier les données du client)
|
// Envoi d'une notification interne à MVA (sans modifier les données du client)
|
||||||
notifyDuplicateViaFormspree(contact);
|
notifyDuplicateViaFormspree(contact);
|
||||||
|
|
||||||
// Email "Ravis de te revoir" via EmailJS désactivé (= post-migration 2026-05-07).
|
// Email "Ravis de te revoir" via Worker + Resend (= footer 2026 cohérent)
|
||||||
// Le compte EmailJS n'est plus accessible (Melissa-only) et son template a un
|
sendWelcomeBackEmail(contact);
|
||||||
// footer "(c) 2025" obsolète. Le client a déjà sa référence affichée via
|
|
||||||
// `showAlreadyRegistered` ci-dessus — l'email est cosmétique. À ré-implémenter
|
|
||||||
// via Worker + Resend en follow-up si jugé utile.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showError() {
|
function showError() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user