From 2774c25a6106ddd7bd946b49e4a6e4dff1b71b67 Mon Sep 17 00:00:00 2001 From: serge Date: Thu, 7 May 2026 18:37:10 +0300 Subject: [PATCH] chore: post-review cleanup (3 Important + dead code purge -116 lines) (#10) --- cloudflare-worker/DEPLOIEMENT.md | 2 +- cloudflare-worker/hubspot-proxy.js | 93 +++++-------- confirmation.html | 3 - contact.html | 1 - js/form-handler.js | 203 +++++++++-------------------- 5 files changed, 93 insertions(+), 209 deletions(-) diff --git a/cloudflare-worker/DEPLOIEMENT.md b/cloudflare-worker/DEPLOIEMENT.md index cb60e7b..8c031d8 100644 --- a/cloudflare-worker/DEPLOIEMENT.md +++ b/cloudflare-worker/DEPLOIEMENT.md @@ -55,7 +55,7 @@ Dans **Paramètres → Stockage et bases de données → Bindings KV** : 3. Namespace : **Créer** un nouveau namespace nommé `mva-welcome-tracker` 4. Sauvegarder -Si déployé via `wrangler` : mettre à jour `wrangler.toml` avec l'ID du namespace KV créé (remplacer `REPLACE_AT_DEPLOY_TIME`). +Si déployé via `wrangler` : `wrangler.toml` contient déjà l'ID du namespace KV (`c02656ba22064923ab1c6db06b0f4a56` sur le compte CF `sergemind4s@gmail.com`). Pour un autre compte, recréer le namespace via `wrangler kv namespace create WELCOME_KV` puis remplacer l'ID dans `wrangler.toml`. ### 4. Vérifier le scope HubSpot diff --git a/cloudflare-worker/hubspot-proxy.js b/cloudflare-worker/hubspot-proxy.js index a0c4bdd..d8508bd 100644 --- a/cloudflare-worker/hubspot-proxy.js +++ b/cloudflare-worker/hubspot-proxy.js @@ -35,8 +35,6 @@ // ============================================================ const HUBSPOT_API = 'https://api.hubapi.com'; -const HUBSPOT_PORTAL_ID = '148163754'; -const HUBSPOT_FORM_GUID = '1d9b75c9-8b60-4966-aa18-4bf503452e9a'; const corsHeaders = { 'Access-Control-Allow-Origin' : '*', @@ -271,43 +269,6 @@ export default { } } - // ── action: listSubscriptions (debug : trouver les IDs) ── - if (action === 'listSubscriptions') { - // Endpoint legacy email/public/v1 nécessite scope content au lieu de - // communication_preferences (que notre token n'a pas) - const r = await fetch(`${HUBSPOT_API}/email/public/v1/subscriptions`, { - headers: { 'Authorization': `Bearer ${token}` }, - }); - return jsonResponse(await r.json()); - } - - // ── action: subscribe ──────────────────────────────────── - // Inscrit un contact à un type d'abonnement marketing (déclenche - // l'envoi du mail de double opt-in si DOI activé au niveau compte). - if (action === 'subscribe') { - if (!email || typeof email !== 'string') { - return jsonResponse({ error: 'Email requis' }, 400); - } - const subId = body.subscriptionId; - if (!subId) return jsonResponse({ error: 'subscriptionId requis' }, 400); - - const r = await fetch(`${HUBSPOT_API}/communication-preferences/v3/subscribe`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}`, - }, - body: JSON.stringify({ - emailAddress: email.toLowerCase().trim(), - subscriptionId: subId, - legalBasis: 'LEGITIMATE_INTEREST_CLIENT', - legalBasisExplanation: 'Soumission du formulaire MVA Global Fret', - }), - }); - const data = await r.text(); - return jsonResponse({ status: r.status, body: data }); - } - // ── action par défaut : vérification doublon par email ── if (!email || typeof email !== 'string') { return jsonResponse({ error: 'Email requis' }, 400); @@ -348,32 +309,46 @@ async function searchContactByEmail(token, email) { } async function getNextRef(token) { - const res = await fetch(`${HUBSPOT_API}/crm/v3/objects/contacts/search`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}`, - }, - body: JSON.stringify({ + // Paginate through ALL HubSpot contacts with `reference_client` property to + // find the true numeric maximum. Previous version used `limit: 100` without + // pagination — produced collisions once the contact count exceeded 100 + // because HubSpot search results don't guarantee ordering by ref. With + // pagination, we walk the full set: 100 per page × N pages until no more. + // For ~1000 contacts = 10 API calls. Acceptable cost given that this runs + // once per signup confirmation (= rare path). + let maxNum = 0; + let after; // undefined on first iteration + do { + const body = { filterGroups: [{ filters: [{ propertyName: 'reference_client', operator: 'HAS_PROPERTY' }], }], properties: ['reference_client'], limit: 100, - }), - }); - if (!res.ok) { - throw new Error(`HubSpot search failed: ${res.status}`); - } - const data = await res.json(); - let maxNum = 0; - (data.results || []).forEach(c => { - const m = (c.properties?.reference_client || '').match(/^MVA-(\d+)$/); - if (m) { - const n = parseInt(m[1], 10); - if (n > maxNum) maxNum = n; + }; + if (after) body.after = after; + + const res = await fetch(`${HUBSPOT_API}/crm/v3/objects/contacts/search`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}`, + }, + body: JSON.stringify(body), + }); + if (!res.ok) { + throw new Error(`HubSpot search failed: ${res.status}`); } - }); + const data = await res.json(); + (data.results || []).forEach(c => { + const m = (c.properties?.reference_client || '').match(/^MVA-(\d+)$/); + if (m) { + const n = parseInt(m[1], 10); + if (n > maxNum) maxNum = n; + } + }); + after = data.paging?.next?.after; + } while (after); return 'MVA-' + String(maxNum + 1).padStart(3, '0'); } diff --git a/confirmation.html b/confirmation.html index c3e37ac..dcef293 100644 --- a/confirmation.html +++ b/confirmation.html @@ -13,9 +13,6 @@ - - -