NeahStable/PRODUCTION_FIXES_APPLIED.md
2026-01-16 18:22:20 +01:00

291 lines
8.0 KiB
Markdown

# Corrections Appliquées pour la Production
Ce document liste les corrections critiques appliquées suite à l'analyse de performance et de préparation à la production.
## ✅ Corrections Appliquées
### 1. Utilitaire fetchWithTimeout
**Fichier créé:** `lib/utils/fetch-with-timeout.ts`
**Problème résolu:** Requêtes HTTP sans timeout pouvant bloquer indéfiniment.
**Utilisation:**
```typescript
import { fetchWithTimeout, fetchJsonWithTimeout } from '@/lib/utils/fetch-with-timeout';
// Exemple 1: Fetch simple avec timeout
const response = await fetchWithTimeout('https://api.example.com/data', {
method: 'GET',
timeout: 10000, // 10 secondes
headers: { 'Authorization': 'Bearer token' }
});
// Exemple 2: Fetch avec parsing JSON automatique
const data = await fetchJsonWithTimeout<MyType>('https://api.example.com/data', {
method: 'POST',
timeout: 30000, // 30 secondes
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
```
**Migration recommandée:**
Remplacer tous les `fetch()` dans:
- `lib/services/n8n-service.ts`
- `app/api/missions/[missionId]/generate-plan/route.ts`
- `app/api/users/[userId]/route.ts`
- `app/api/leantime/tasks/route.ts`
- `app/api/rocket-chat/messages/route.ts`
### 2. Correction Dockerfile
**Fichier modifié:** `Dockerfile`
**Problème résolu:** Utilisation de `migrate dev` en production (créerait de nouvelles migrations).
**Changement:**
```dockerfile
# ❌ AVANT
RUN npx prisma migrate dev --name init
# ✅ APRÈS
# NOTE: Migrations should be run separately before deployment
# DO NOT use 'migrate dev' in production
# Use 'prisma migrate deploy' instead, run separately before container start
```
**Action requise:** Utiliser `Dockerfile.prod` pour la production, qui est déjà correctement configuré.
### 3. Script de Validation d'Environnement
**Fichier créé:** `scripts/validate-env.ts`
**Problème résolu:** Variables d'environnement manquantes non détectées avant déploiement.
**Utilisation:**
```bash
# Valider les variables d'environnement
npm run validate:env
# Ou directement
ts-node scripts/validate-env.ts
```
**Fonctionnalités:**
- ✅ Vérifie toutes les variables requises
- ✅ Valide le format des URLs
- ✅ Vérifie la force de NEXTAUTH_SECRET
- ✅ Recommande les paramètres de pool DB
- ✅ Affiche des warnings pour les variables optionnelles
**Ajouté dans package.json:**
```json
"validate:env": "ts-node scripts/validate-env.ts"
```
## 🔄 Migrations à Effectuer
### Priorité 1 - CRITIQUE
#### 1. Remplacer fetch() par fetchWithTimeout()
**Fichiers à modifier:**
1. **lib/services/n8n-service.ts**
```typescript
// ❌ AVANT
const response = await fetch(this.webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-api-key': this.apiKey },
body: JSON.stringify(cleanData),
});
// ✅ APRÈS
import { fetchWithTimeout } from '@/lib/utils/fetch-with-timeout';
const response = await fetchWithTimeout(this.webhookUrl, {
method: 'POST',
timeout: 30000, // 30 secondes
headers: { 'Content-Type': 'application/json', 'x-api-key': this.apiKey },
body: JSON.stringify(cleanData),
});
```
2. **app/api/missions/[missionId]/generate-plan/route.ts**
```typescript
// ❌ AVANT
const response = await fetch(webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },
body: JSON.stringify(webhookData),
});
// ✅ APRÈS
import { fetchWithTimeout } from '@/lib/utils/fetch-with-timeout';
const response = await fetchWithTimeout(webhookUrl, {
method: 'POST',
timeout: 30000,
headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },
body: JSON.stringify(webhookData),
});
```
3. **app/api/users/[userId]/route.ts** (getLeantimeUserId)
```typescript
// ❌ AVANT
const userResponse = await fetch('https://agilite.slm-lab.net/api/jsonrpc', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': process.env.LEANTIME_TOKEN || '' },
body: JSON.stringify({ ... }),
});
// ✅ APRÈS
import { fetchJsonWithTimeout } from '@/lib/utils/fetch-with-timeout';
const userData = await fetchJsonWithTimeout('https://agilite.slm-lab.net/api/jsonrpc', {
method: 'POST',
timeout: 10000, // 10 secondes
headers: { 'Content-Type': 'application/json', 'X-API-Key': process.env.LEANTIME_TOKEN || '' },
body: JSON.stringify({ ... }),
});
```
#### 2. Configurer le Pool de Connexions Prisma
**Action:** Modifier `DATABASE_URL` dans les variables d'environnement:
```bash
# ❌ AVANT
DATABASE_URL=postgresql://user:pass@host:5432/db
# ✅ APRÈS
DATABASE_URL=postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=20&connect_timeout=10
```
**Paramètres recommandés:**
- `connection_limit=10` - Limite le nombre de connexions simultanées
- `pool_timeout=20` - Timeout pour obtenir une connexion du pool (secondes)
- `connect_timeout=10` - Timeout pour établir une connexion (secondes)
**Note:** Ajuster `connection_limit` selon la charge attendue et les limites du serveur PostgreSQL.
#### 3. Remplacer console.log par logger
**Script de migration:**
```bash
# Créer scripts/migrate-console-logs.sh
#!/bin/bash
find lib/services app/api -name "*.ts" -type f | while read file; do
# Sauvegarder d'abord
cp "$file" "$file.bak"
# Remplacer
sed -i '' 's/console\.log(/logger.debug(/g' "$file"
sed -i '' 's/console\.error(/logger.error(/g' "$file"
sed -i '' 's/console\.warn(/logger.warn(/g' "$file"
sed -i '' 's/console\.debug(/logger.debug(/g' "$file"
# Vérifier que logger est importé
if ! grep -q "import.*logger" "$file" && grep -q "logger\." "$file"; then
# Ajouter l'import en haut du fichier
sed -i '' '1i\
import { logger } from '\''@/lib/logger'\'';
' "$file"
fi
done
echo "✅ Console logs migrated to logger"
echo "⚠️ Review changes and remove .bak files after verification"
```
**Fichiers prioritaires:**
- `lib/services/rocketchat-call-listener.ts` (35 occurrences)
- `lib/services/refresh-manager.ts` (19 occurrences)
- `lib/services/prefetch-service.ts` (17 occurrences)
### Priorité 2 - HAUTE
#### 4. Activer l'Optimisation d'Images Next.js
**Fichier:** `next.config.mjs`
```javascript
// ❌ AVANT
images: {
unoptimized: true,
},
// ✅ APRÈS
images: {
unoptimized: false,
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 750, 828, 1080, 1200],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
},
```
#### 5. Implémenter Circuit Breaker
Créer `lib/utils/circuit-breaker.ts` (voir exemple dans `PERFORMANCE_AND_PRODUCTION_ANALYSIS.md`).
**Services à protéger:**
- N8N webhooks
- Leantime API
- RocketChat API
## 📋 Checklist de Migration
- [ ] Remplacer tous les `fetch()` par `fetchWithTimeout()` dans les fichiers listés
- [ ] Configurer `DATABASE_URL` avec les paramètres de pool
- [ ] Exécuter le script de migration console.log
- [ ] Vérifier que tous les fichiers utilisent `logger` au lieu de `console`
- [ ] Activer l'optimisation d'images dans `next.config.mjs`
- [ ] Tester toutes les fonctionnalités après les changements
- [ ] Valider l'environnement avec `npm run validate:env`
- [ ] Déployer en staging et tester
- [ ] Monitorer les performances après déploiement
## 🧪 Tests Recommandés
### Test de Timeout
```typescript
// Test que les timeouts fonctionnent
const start = Date.now();
try {
await fetchWithTimeout('https://httpstat.us/200?sleep=5000', {
timeout: 2000, // 2 secondes
});
} catch (error) {
const duration = Date.now() - start;
console.assert(duration < 3000, 'Timeout should occur before 3 seconds');
console.assert(error.message.includes('timeout'), 'Error should mention timeout');
}
```
### Test de Validation d'Environnement
```bash
# Tester avec variables manquantes
unset DATABASE_URL
npm run validate:env
# Devrait échouer avec message clair
# Tester avec toutes les variables
# Devrait réussir
npm run validate:env
```
## 📚 Documentation
Voir `PERFORMANCE_AND_PRODUCTION_ANALYSIS.md` pour:
- Analyse complète des problèmes
- Recommandations détaillées
- Métriques de succès
- Checklist complète de mise en production
---
**Dernière mise à jour:** $(date)