CognxSafeTrack commited on
Commit ·
17a3e29
1
Parent(s): b8d93e2
docs: add HuggingFace → Railway migration plan
Browse files
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.
|