site-mva-global-fret/cloudflare-worker/hubspot-proxy.js
MVA Global Fret 1970b10089 Sécurise form-handler : retire token côté client, prépare proxy Worker
- Supprime HUBSPOT_SERVICE_KEY du code JS public (évite l'exposition du token)
- Remplace l'appel CORS-bloqué vers api.hubapi.com par un appel au proxy
  Cloudflare Worker (WORKER_PROXY_URL, vide par défaut = sans blocage)
- Ajoute cloudflare-worker/hubspot-proxy.js : code complet du Worker à
  déployer gratuitement depuis dash.cloudflare.com (sans CLI ni Node.js)
- Quand WORKER_PROXY_URL est renseigné, la détection doublon est active :
  un client existant voit son message "déjà inscrit" sans re-soumission

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 14:13:17 +02:00

99 lines
3.4 KiB
JavaScript

// ============================================================
// MVA Global Fret — Cloudflare Worker : Proxy HubSpot
// ============================================================
// Ce Worker contourne le CORS en faisant l'appel HubSpot
// côté serveur (Cloudflare), puis renvoie le résultat
// au navigateur avec les bons en-têtes CORS.
//
// DÉPLOIEMENT (gratuit, sans CLI) :
// 1. Aller sur https://dash.cloudflare.com/ → Workers & Pages
// 2. Créer un Worker → coller ce code → Enregistrer et déployer
// 3. Copier l'URL du Worker (ex: https://mva-proxy.xxx.workers.dev)
// 4. Coller cette URL dans js/form-handler.js à la constante WORKER_PROXY_URL
//
// SÉCURITÉ :
// - Stocker le token dans une variable d'environnement Cloudflare :
// Workers & Pages → Votre Worker → Paramètres → Variables et secrets
// Nom : HUBSPOT_TOKEN Valeur : pat-eu1-e3c92146-bb17-45fe-8d77-0c665fc4df3b
// - Ce token est en lecture seule (crm.objects.contacts.read uniquement)
// ============================================================
export default {
async fetch(request, env) {
// En-têtes CORS autorisés
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
};
// Réponse au preflight CORS (OPTIONS)
if (request.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
// Seule la méthode POST est acceptée
if (request.method !== 'POST') {
return new Response('Method Not Allowed', { status: 405 });
}
// Lecture du token (variable d'env Cloudflare ou fallback hardcodé)
const token = (env && env.HUBSPOT_TOKEN)
? env.HUBSPOT_TOKEN
: 'pat-eu1-e3c92146-bb17-45fe-8d77-0c665fc4df3b';
try {
const { email } = await request.json();
if (!email || typeof email !== 'string') {
return new Response(
JSON.stringify({ error: 'Email requis' }),
{ status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
// Appel HubSpot CRM (pas de CORS côté Worker)
const hsResponse = await fetch(
'https://api.hubapi.com/crm/v3/objects/contacts/search',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify({
filterGroups: [{
filters: [{
propertyName: 'email',
operator: 'EQ',
value: email.toLowerCase().trim(),
}]
}],
properties: ['firstname', 'lastname', 'email', 'reference_client'],
}),
}
);
if (!hsResponse.ok) {
return new Response(
JSON.stringify({ error: `HubSpot error: ${hsResponse.status}` }),
{ status: 502, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
const data = await hsResponse.json();
return new Response(JSON.stringify(data), {
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
} catch (err) {
return new Response(
JSON.stringify({ error: err.message }),
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
},
};