CognxSafeTrack commited on
Commit
17a3e29
·
1 Parent(s): b8d93e2

docs: add HuggingFace → Railway migration plan

Browse files
Files changed (1) hide show
  1. docs/migration-hf-to-railway/plan.md +250 -0
docs/migration-hf-to-railway/plan.md ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Plan de Migration — HuggingFace → Railway
2
+
3
+ **Objectif :** Supprimer HuggingFace du chemin critique de l'API, consolider sur Railway, et conserver l'accès aux modèles HF via l'Inference API.
4
+
5
+ **Statut :** 🟡 À faire — après finalisation du review Meta
6
+ **Durée estimée :** 2–3 heures avec tests inclus
7
+ **Risque :** Moyen — le bot WhatsApp peut être coupé quelques minutes pendant le basculement du webhook
8
+
9
+ ---
10
+
11
+ ## Architecture actuelle (à migrer)
12
+
13
+ ```
14
+ Netlify (Frontend)
15
+ │ VITE_API_URL = https://safetrack-edtech.hf.space
16
+
17
+ HuggingFace Space [port 7860] ← API Fastify (POINT DE DÉFAILLANCE)
18
+ │ hiberne, pas de SLA, SSE instable
19
+ │ BullMQ (Redis)
20
+
21
+ Railway "whatsapp-worker" ← PM2 : API (7860) + Worker Bridge (8082)
22
+ └── seul à pouvoir appeler graph.facebook.com
23
+ ```
24
+
25
+ ## Architecture cible (après migration)
26
+
27
+ ```
28
+ Netlify (Frontend)
29
+ │ VITE_API_URL = https://xamle-api-production-xxxx.up.railway.app
30
+
31
+ Railway Service 1 : "api" ← API Fastify [PORT=7860]
32
+ │ BullMQ (Redis partagé) ← auth, orgs, contacts, stream SSE...
33
+
34
+ Railway Service 2 : "worker" ← Worker BullMQ [PORT=8082, pas exposé]
35
+ └── appelle graph.facebook.com (Meta WhatsApp)
36
+ └── appelle api-inference.huggingface.co (modèles HF via HF_TOKEN)
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Étape 0 — Prérequis (avant de commencer)
42
+
43
+ ### 0.1 Variables d'environnement à noter
44
+ Récupère ces valeurs depuis HuggingFace Space (Settings → Variables) et Railway :
45
+
46
+ | Variable | Où la trouver | Usage |
47
+ |---|---|---|
48
+ | `DATABASE_URL` | HF Secrets ou Railway | Neon PostgreSQL |
49
+ | `REDIS_URL` | HF Secrets ou Railway | BullMQ + cache |
50
+ | `JWT_SECRET` | HF Secrets | Auth |
51
+ | `ADMIN_API_KEY` | HF Secrets | Bridge interne |
52
+ | `ENCRYPTION_SECRET` | HF Secrets | Chiffrement tokens |
53
+ | `OPENAI_API_KEY` | HF Secrets | OpenAI |
54
+ | `GOOGLE_AI_API_KEY` | HF Secrets | Gemini |
55
+ | `WHATSAPP_VERIFY_TOKEN` | HF Secrets | Vérification webhook Meta |
56
+ | `META_GRAPH_API_VERSION` | HF Secrets | `v22.0` |
57
+ | `HF_TOKEN` | HuggingFace account → Settings → Tokens | **Nouveau** — pour l'Inference API |
58
+
59
+ ### 0.2 Choisir une fenêtre de maintenance
60
+ Faire la migration en dehors des heures de pointe. Le bot sera coupé ~5 minutes pendant le basculement du webhook Meta.
61
+
62
+ ---
63
+
64
+ ## Étape 1 — Créer le service API sur Railway
65
+
66
+ ### 1.1 Nouveau service Railway
67
+ 1. Dashboard Railway → **New Service** → **GitHub Repo** → `blackpanthere/Edtech`
68
+ 2. Nom du service : `api`
69
+ 3. Root Directory : `/` (racine du monorepo)
70
+ 4. Builder : **Dockerfile** (détecté automatiquement)
71
+
72
+ ### 1.2 Configurer la variable SERVICES
73
+ Dans Railway → service `api` → Variables :
74
+ ```
75
+ SERVICES=api
76
+ ```
77
+ Cette variable contrôle ce que PM2 démarre dans `ecosystem.config.js`. Avec `SERVICES=api`, seul le processus API (port 7860) démarre — pas le Worker.
78
+
79
+ Sur le service `worker` existant, vérifier que :
80
+ ```
81
+ SERVICES=worker
82
+ ```
83
+ ou ne pas définir `SERVICES` (par défaut PM2 lance les deux, ce qui est le comportement actuel).
84
+
85
+ ### 1.3 Copier toutes les variables d'environnement
86
+ Dans Railway → service `api` → Variables, ajouter toutes les variables du tableau de l'étape 0, plus :
87
+ ```
88
+ NODE_ENV=production
89
+ META_GRAPH_API_VERSION=v22.0
90
+ PORT=7860
91
+ HF_TOKEN=hf_xxxxxxxxxxxx ← nouveau, pour l'Inference API HuggingFace
92
+ ```
93
+
94
+ ### 1.4 Générer un domaine public
95
+ Railway → service `api` → Settings → **Generate Domain**
96
+ Noter l'URL générée : `https://api-production-xxxx.up.railway.app`
97
+
98
+ ### 1.5 Vérifier le démarrage
99
+ Dans les logs Railway du service `api`, vérifier :
100
+ ```
101
+ 🚀 API Gateway running on http://0.0.0.0:7860
102
+ ✅ [META] Graph API version: v22.0
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Étape 2 — Adapter le Worker pour n'exposer que le bridge
108
+
109
+ Le service `worker` actuel fait tourner les deux processus (API + Worker). Après migration, il ne doit plus faire tourner l'API (pour éviter deux instances).
110
+
111
+ Dans Railway → service `worker` → Variables :
112
+ ```
113
+ SERVICES=worker
114
+ ```
115
+
116
+ Redémarrer le service et vérifier les logs :
117
+ ```
118
+ 🚀 WhatsApp Worker + Bridge listening on port 8082
119
+ ```
120
+ L'API ne doit plus apparaître dans les logs du Worker.
121
+
122
+ ---
123
+
124
+ ## Étape 3 — Ajouter l'Inference API HuggingFace
125
+
126
+ Pour continuer à utiliser les modèles HF depuis Railway, ajouter l'appel via l'API :
127
+
128
+ **Fichier :** `packages/ai-sdk/src/providers/huggingface.ts` (à créer si inexistant)
129
+
130
+ ```typescript
131
+ const HF_API_URL = 'https://api-inference.huggingface.co/models';
132
+
133
+ export async function callHuggingFaceModel(
134
+ modelId: string,
135
+ inputs: string,
136
+ hfToken: string
137
+ ): Promise<string> {
138
+ const res = await fetch(`${HF_API_URL}/${modelId}`, {
139
+ method: 'POST',
140
+ headers: {
141
+ 'Authorization': `Bearer ${hfToken}`,
142
+ 'Content-Type': 'application/json'
143
+ },
144
+ body: JSON.stringify({ inputs })
145
+ });
146
+ if (!res.ok) throw new Error(`HF Inference API error: ${res.status}`);
147
+ const data = await res.json();
148
+ return Array.isArray(data) ? data[0]?.generated_text ?? '' : data?.generated_text ?? '';
149
+ }
150
+ ```
151
+
152
+ **Variable d'environnement à ajouter sur Railway (api + worker) :**
153
+ ```
154
+ HF_TOKEN=hf_xxxxxxxxxxxx
155
+ ```
156
+
157
+ ---
158
+
159
+ ## Étape 4 — Basculer le webhook Meta (⚠️ fenêtre de coupure)
160
+
161
+ C'est l'étape critique. Le bot sera coupé pendant ce changement.
162
+
163
+ ### 4.1 Vérifier que le nouveau service API répond
164
+ ```bash
165
+ curl https://api-production-xxxx.up.railway.app/v1/whatsapp/webhook?hub.mode=subscribe&hub.verify_token=TON_VERIFY_TOKEN&hub.challenge=test
166
+ # Doit retourner : test
167
+ ```
168
+
169
+ ### 4.2 Changer l'URL webhook dans Meta
170
+ 1. Ouvrir [developers.facebook.com](https://developers.facebook.com) → ton App → WhatsApp → Configuration
171
+ 2. **Webhook URL** : remplacer l'URL HF par `https://api-production-xxxx.up.railway.app/v1/whatsapp/webhook`
172
+ 3. **Verify Token** : identique à `WHATSAPP_VERIFY_TOKEN` dans Railway
173
+ 4. Cliquer **Vérifier et Enregistrer**
174
+ 5. Meta envoie une requête GET de vérification → le service Railway doit répondre 200
175
+
176
+ ### 4.3 Tester le bot
177
+ Envoyer un message WhatsApp test → vérifier dans les logs Railway que le job est bien enqueueué et traité.
178
+
179
+ ---
180
+
181
+ ## Étape 5 — Basculer le frontend Netlify
182
+
183
+ 1. Netlify → Site → **Environment Variables**
184
+ 2. `VITE_API_URL` : remplacer `https://safetrack-edtech.hf.space` par `https://api-production-xxxx.up.railway.app`
185
+ 3. **Trigger Deploy** (ou attendre le prochain push)
186
+ 4. Vérifier sur le frontend :
187
+ - Login fonctionne
188
+ - Page `/clients` charge les organisations
189
+ - SSE connecté (pas de 401 dans la console)
190
+ - Import Excel fonctionne
191
+
192
+ ---
193
+
194
+ ## Étape 6 — Désactiver HuggingFace Space
195
+
196
+ ⚠️ Ne faire cette étape que si tout fonctionne depuis ≥ 24h.
197
+
198
+ 1. HuggingFace → Space `safetrack/edtech` → Settings → **Pause Space**
199
+ (ne pas supprimer tout de suite — garder en pause pour rollback rapide)
200
+ 2. Après 1 semaine sans incident → supprimer le Space si souhaité
201
+
202
+ ---
203
+
204
+ ## Plan de rollback (si ça casse)
205
+
206
+ Si le bot ne fonctionne plus après la migration :
207
+
208
+ 1. **Remettre l'URL webhook Meta** → `https://safetrack-edtech.hf.space/v1/whatsapp/webhook`
209
+ 2. **Remettre `VITE_API_URL` Netlify** → `https://safetrack-edtech.hf.space`
210
+ 3. **Relancer le HF Space** si en pause → HuggingFace → Space → Resume
211
+ 4. Durée du rollback : ~5 minutes
212
+
213
+ ---
214
+
215
+ ## Checklist de validation finale
216
+
217
+ ### Backend Railway (service `api`)
218
+ - [ ] Logs de démarrage : `🚀 API Gateway running on http://0.0.0.0:7860`
219
+ - [ ] Logs de démarrage : `✅ [META] Graph API version: v22.0`
220
+ - [ ] `GET /health` → `200 OK`
221
+ - [ ] `GET /v1/whatsapp/webhook?hub.mode=subscribe&...` → répond le challenge
222
+ - [ ] Variables d'env identiques à HF (DATABASE_URL, REDIS_URL, JWT_SECRET…)
223
+
224
+ ### Worker Railway (service `worker`)
225
+ - [ ] Logs : `🚀 WhatsApp Worker + Bridge listening on port 8082`
226
+ - [ ] L'API Fastify NE démarre PAS dans ce service (`SERVICES=worker`)
227
+ - [ ] Les jobs BullMQ sont consommés (logs : `[WORKER] Processing job`)
228
+
229
+ ### Frontend Netlify
230
+ - [ ] `VITE_API_URL` mis à jour
231
+ - [ ] Login fonctionne
232
+ - [ ] Page `/clients` : Daily Limit affiche une valeur (pas `—`)
233
+ - [ ] SSE connecté : pas de 401 dans la console navigateur
234
+ - [ ] Import Excel : sélecteur de fichier s'ouvre sans erreur
235
+
236
+ ### Bot WhatsApp
237
+ - [ ] Envoyer un message test → réponse reçue en < 10 secondes
238
+ - [ ] Vérifier logs Railway Worker : job `handle-inbound` traité sans erreur
239
+
240
+ ---
241
+
242
+ ## Points d'attention spécifiques à ce projet
243
+
244
+ > Le bug "bot WhatsApp coupé" lors de la précédente tentative de migration venait probablement d'une ou plusieurs de ces causes :
245
+ > - `ADMIN_API_KEY` différente entre HF et Railway → le bridge refusait les appels internes
246
+ > - `REDIS_URL` différente → les jobs n'étaient pas partagés entre l'API et le Worker
247
+ > - `WHATSAPP_VERIFY_TOKEN` absente sur Railway → Meta rejetait la vérification du webhook
248
+ > - `SERVICES` non défini → PM2 lançait deux fois l'API, le Worker tournait en double
249
+
250
+ > **Règle :** L'API et le Worker DOIVENT partager exactement le même `REDIS_URL` et le même `ADMIN_API_KEY`. Ce sont les deux variables qui font fonctionner la communication interne.