Skip to content

Cloud Functions

Toutes les fonctions sont en europe-west9 (Paris). Runtime Node.js 22, génération 2.

Authentification & comptes

initUserAccount (callable)

Crée le document users/{uid} avec les champs par défaut (plan: 'starter', subscription_status: 'pending', RGPD à false) et le sous-document profil. Idempotent (no-op si le compte existe déjà).

setAdminClaim / removeAdminClaim (callable, admin)

Pose / retire le custom claim admin sur un compte Firebase Auth. Réservé aux admins existants.

Vérification d'email

sendVerificationCode (callable)

Génère un code numérique à 6 chiffres, stocke son hash SHA-256 dans users/{uid}/private/email_verification et l'envoie par email via Resend. TTL : 10 min. Cooldown de 60 s avant un nouvel envoi.

verifyEmailCode (callable)

Compare le code soumis au hash stocké, marque le compte comme emailVerified sur succès. Max 5 tentatives avant brûlage du code.

RGPD

recordConsent (callable)

Enregistre les consentements CGU et politique de confidentialité avec horodatage (Article 7).

exportUserData (callable)

Article 15 + 20 : exporte toutes les données de l'utilisateur (compte, profil, contacts, entreprises, opportunités, devis, factures, tâches, événements, activités, données auth) au format JSON.

deleteUserAccount (callable)

Article 17 (droit à l'oubli) : supprime intégralement les données Firestore, les fichiers Storage et le compte Firebase Auth. Demande la chaîne de confirmation DELETE_MY_ACCOUNT pour éviter une suppression accidentelle.

Contacts

previewContactImport (callable)

Parse côté serveur un CSV soumis par l'utilisateur (texte brut, max ~5 Mo via le transport callable, max 1000 lignes traitées). Applique le mapping colonnes → champs Klapy fourni par l'assistant, valide chaque ligne (email, téléphone E.164, présence d'au moins un nom et d'au moins un moyen de contact), et identifie les doublons : par email ou téléphone contre les contacts existants, et entre lignes du même fichier.

Aucune écriture Firestore. Retourne :

  • detected_headers : en-têtes détectés
  • suggested_mapping : mapping auto-suggéré
  • rows[] : lignes normalisées avec statut (issues, duplicate_of, duplicate_in_file)
  • summary : compteurs (importable, duplicates_existing, duplicates_in_file, invalid)
  • truncated : true si le fichier dépassait 1000 lignes

Utilisé pour alimenter l'étape « aperçu » de l'assistant d'import.

commitContactImport (callable)

Persiste l'import. Re-parse le CSV côté serveur (jamais confiance aux données client modifiées). Appelle assertQuota(uid, 'contacts', toCreate.length) : rejet resource-exhausted si l'opération dépasse le plafond du plan.

Écritures par lots de 450 documents (limite Firestore : 500). Chaque contact créé reçoit un champ imported_from: { import_id, source_row } pour permettre une annulation future. Un document d'audit est écrit dans users/{uid}/imports/{importId} avec : source, mapping, compteurs, troncature éventuelle, premières 100 erreurs.

Retourne { import_id, created, skipped_duplicates, skipped_invalid, skipped_manual, errors }.

Compteurs (triggers)

onContactCreate (Firestore trigger)

Déclencheur : onDocumentCreated('users/{uid}/contacts/{contactId}'). Incrémente users/{uid}.contacts_count de 1 via FieldValue.increment(1) (atomique).

onContactDelete (Firestore trigger)

Déclencheur : onDocumentDeleted('users/{uid}/contacts/{contactId}'). Décrémente contacts_count de 1. Avale silencieusement les erreurs (cas où le doc utilisateur a déjà été supprimé par la cascade RGPD).

Maintenance

adminBackfillUsage (callable, admin)

Recalcule les compteurs dénormalisés à partir des données réelles (sweep des sous-collections) et normalise les téléphones legacy au format E.164 sur les contacts existants.

Paramètres :

  • uid? : si fourni, ne traite qu'un utilisateur (sinon tous)
  • dry_run?: true : reporte ce qui serait modifié, sans écrire

Retourne un diff par utilisateur : { contacts_count_before, contacts_count_after, phones_normalized, phones_invalid }.

Idempotent. À utiliser après déploiement de nouveaux triggers, en cas de dérive constatée, ou comme sweep planifié hebdomadaire (à câbler).

Admin back-office (klapy-admin)

adminListUsers (callable, admin)

Liste paginée des utilisateurs (compte + extrait profil) avec recherche.

adminGetUser (callable, admin)

Détail complet d'un utilisateur (compte, profil, dernier login, statut auth).

adminSuspendUser (callable, admin)

Désactive un compte Firebase Auth (suspension, réversible).

adminGetStats (callable, admin)

Compteurs agrégés de la plateforme (utilisateurs par plan, par statut d'abonnement) via count() Firestore.

Helpers internes

Les fichiers suivants sont importés par les fonctions ci-dessus mais ne sont pas eux-mêmes exposés :

  • lib/quotas.ts : LIMITS, assertQuota(), getUsage(). Source de vérité des plafonds par plan.
  • lib/phone.ts : normalizePhone(raw, defaultCountry='FR') → E.164 ou null.
  • lib/contactImport.ts : parsing CSV pur (papaparse), détection de mapping, dédoublonnage. Réutilisé par previewContactImport et commitContactImport pour garantir que l'aperçu correspond exactement à l'écriture.