Deploy Metering — Système de métriques et facturation
Déployer automatiquement le pipeline complet de métriques sur une application Supabase existante, pour que ses données de consommation soient collectées par le hub central et facturées.
Avant de commencer
OBLIGATOIRE : Lire le blueprint
Avant toute action, lire intégralement la documentation de référence dans le somtech-pack :
features/metering-billing/overview.md → Architecture et décisions
features/metering-billing/database.md → Tables, RPCs, enums, indexes
features/metering-billing/backend.md → Helper, patterns d'instrumentation, Edge Functions
features/metering-billing/implementation-guide.md → Guide pas-à-pas complet
Si le dossier features/metering-billing/ n'est pas dans le projet courant, le chercher dans le somtech-pack synchronisé ou demander à l'utilisateur de faire un somtech_pack_pull.sh.
Phase 0 — Analyse du projet
0.1 Vérifier les prérequis
# Vérifier que c'est un projet Supabase
ls supabase/config.toml
# Vérifier les migrations existantes
ls supabase/migrations/
# Vérifier les Edge Functions existantes
ls supabase/functions/
0.2 Détecter les points d'appel AI
Scanner le projet pour trouver TOUS les appels AI :
grep -rn "completions.create\|messages.create\|embeddings.create\|chat\.create\|anthropic\.\|openai\." src/ supabase/functions/ lib/ app/ --include="*.ts" --include="*.tsx" --include="*.js"
Lister les résultats et les présenter à l'utilisateur pour validation avant de continuer.
0.3 Détecter la stack
Identifier si l'app utilise :
- Deno (Edge Functions Supabase) → variante Deno du helper
- Node.js (Express, Workers, Next.js API routes) → variante Node.js du helper
- Les deux → créer les deux variantes
0.4 Demander confirmation
Présenter à l'utilisateur :
- Le nombre de points d'appel AI trouvés
- La stack détectée (Deno/Node.js)
- La liste des modules qui seront instrumentés
- Demander le
BILLING_PROJECT_ID(ou utiliser le UUID temporaire00000000-0000-0000-0000-000000000000)
Attendre la validation avant de continuer.
Phase 1 — Migration base de données
1.1 Générer le timestamp
TIMESTAMP=$(date -u +"%Y%m%d%H%M%S")
echo "Migration: ${TIMESTAMP}_create_metering_tables.sql"
1.2 Créer la migration
Créer supabase/migrations/${TIMESTAMP}_create_metering_tables.sql avec le contenu exact de database.md (tables, enum, indexes, RPCs).
Inclure :
- Table
ai_token_usageavec RLS - Table
usage_metrics_dailyavec contrainte UNIQUE et RLS - Enum
metric_type_local - RPC
aggregate_daily_usage - RPC
get_monthly_ai_usage - RPC
exec_sql
1.3 Appliquer en local
supabase db reset
1.4 Valider
Exécuter les requêtes de validation de implementation-guide.md étape 1.4.
Phase 2 — Instrumenter les appels AI
2.1 Créer le helper
Créer lib/track-token-usage.ts avec la variante appropriée (Deno ou Node.js) depuis backend.md.
Si l'app a des Edge Functions ET du code Node.js, créer :
lib/track-token-usage.ts→ Node.jssupabase/functions/_shared/track-token-usage.ts→ Deno
2.2 Instrumenter chaque point d'appel
Pour CHAQUE point détecté en phase 0.2 :
- Identifier le pattern (A: OpenAI chat, B: Anthropic, C: Embeddings, D: Streaming)
- Ajouter l'import du helper
- Ajouter l'appel
trackTokenUsage()(SANS await) avec le bon module et modèle - Streaming : S'assurer que
stream_options: { include_usage: true }est présent
2.3 Convention de nommage
Utiliser les noms de modules standardisés (voir database.md section "Convention de nommage des modules").
2.4 Valider
Demander à l'utilisateur de faire un appel AI de test, puis vérifier :
SELECT module, model, input_tokens, output_tokens, created_at
FROM ai_token_usage ORDER BY created_at DESC LIMIT 5;
Phase 3 — Edge Functions
3.1 Générer la clé API
METERING_KEY="mtr_$(openssl rand -hex 32)"
echo "🔑 Clé API metering : ${METERING_KEY}"
echo "⚠️ NOTER CETTE CLÉ — nécessaire pour Orbit"
3.2 Configurer les secrets
supabase secrets set METERING_API_KEY="${METERING_KEY}"
supabase secrets set BILLING_PROJECT_ID="<valeur-fournie-ou-uuid-temporaire>"
3.3 Créer les Edge Functions
Créer les deux fichiers depuis implementation-guide.md :
supabase/functions/collect-usage-metrics/index.tssupabase/functions/get-metering-data/index.ts
3.4 Déployer
supabase functions deploy collect-usage-metrics
supabase functions deploy get-metering-data
3.5 Tester
# Test collect
curl -X POST \
-H "Authorization: Bearer $(supabase status --output json | jq -r '.SERVICE_ROLE_KEY // empty')" \
"$(supabase status --output json | jq -r '.API_URL // empty')/functions/v1/collect-usage-metrics"
# Test get-metering-data
curl -H "X-Metering-API-Key: ${METERING_KEY}" \
"$(supabase status --output json | jq -r '.API_URL // empty')/functions/v1/get-metering-data?action=summary"
Phase 4 — Cron
4.1 Configurer le Vault
Exécuter via MCP Supabase ou SQL Editor :
SELECT vault.create_secret('<SUPABASE_URL>', 'supabase_url');
SELECT vault.create_secret('<ANON_KEY>', 'anon_key');
On utilise l'
anon_key(publishable key) — pas leservice_role_key. L'Edge Function crée son propre client interne avecSUPABASE_SERVICE_ROLE_KEY(auto-injecté). Le JWT sert uniquement à passer la validation d'accès.
4.2 Créer le cron job
SELECT cron.schedule(
'collect-usage-metrics-daily',
'0 2 * * *',
$$
SELECT net.http_post(
url := (SELECT decrypted_secret FROM vault.decrypted_secrets WHERE name = 'supabase_url')
|| '/functions/v1/collect-usage-metrics',
headers := jsonb_build_object(
'Authorization', 'Bearer ' ||
(SELECT decrypted_secret FROM vault.decrypted_secrets WHERE name = 'anon_key'),
'Content-Type', 'application/json'
),
body := '{}'::jsonb
);
$$
);
4.3 Valider
SELECT jobid, jobname, schedule, active FROM cron.job
WHERE jobname = 'collect-usage-metrics-daily';
Phase 5 — Validation finale
Exécuter la checklist complète de implementation-guide.md étape 5.
Résumé à afficher
✅ METERING DÉPLOYÉ
──────────────────
Tables : ai_token_usage, usage_metrics_daily
RPCs : aggregate_daily_usage, get_monthly_ai_usage, exec_sql
Helper : lib/track-token-usage.ts
Points AI : X points instrumentés
Edge Funcs : collect-usage-metrics, get-metering-data
Cron : collect-usage-metrics-daily (2h UTC)
Clé API : mtr_***
📋 À TRANSMETTRE À L'ÉQUIPE ORBIT :
URL metering : https://<projet>.supabase.co/functions/v1/get-metering-data
Clé API : mtr_<clé-complète>
Troubleshooting
| Symptôme | Cause | Solution |
|---|---|---|
cron.schedule échoue |
pg_cron non activé |
Dashboard → Extensions |
net.http_post échoue |
pg_net non activé |
Dashboard → Extensions |
| Secrets vault absents | Vault mal configuré | Re-exécuter vault.create_secret |
| Edge Function 500 | Env var manquante | supabase secrets list |
| Tokens à 0 (streaming) | Manque stream_options |
Ajouter stream_options: { include_usage: true } |
| Métriques à 0 | Pas de données | L'instrumentation n'est pas encore active / pas d'appel AI de test |
Règles critiques
- Ne JAMAIS
awaitletrackTokenUsage()— fire-and-forget uniquement - Streaming : TOUJOURS
stream_options: { include_usage: true } - Créer la migration via fichier — pas de
supabase db push --linked - Tester avec
supabase db resetavant de pousser en prod - Stocker les secrets dans le Vault — jamais en dur dans le SQL du cron