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

696 lines
18 KiB
Markdown

# Analyse Performance et Préparation Production - Neah
**Date:** $(date)
**Auteur:** Analyse Senior Developer
**Version:** 1.0
---
## 📋 Table des Matières
1. [Résumé Exécutif](#résumé-exécutif)
2. [Analyse des Performances](#analyse-des-performances)
3. [Problèmes de Configuration Production](#problèmes-de-configuration-production)
4. [Sécurité](#sécurité)
5. [Recommandations Prioritaires](#recommandations-prioritaires)
6. [Checklist de Mise en Production](#checklist-de-mise-en-production)
---
## 🎯 Résumé Exécutif
### État Actuel
-**Architecture solide**: Next.js 16, Prisma, Redis, PostgreSQL
-**Bonnes pratiques**: Logging structuré, gestion d'erreurs, cache Redis
- ⚠️ **Problèmes critiques**: Configuration DB pool, timeouts HTTP, logs console
- ⚠️ **Production readiness**: 70% - Nécessite corrections avant déploiement
### Priorités
1. **CRITIQUE** - Configuration pool de connexions Prisma
2. **CRITIQUE** - Correction Dockerfile (migrate dev → deploy)
3. **HAUTE** - Ajout de timeouts sur toutes les requêtes HTTP
4. **HAUTE** - Remplacement console.log par logger structuré
5. **MOYENNE** - Optimisation images Next.js
6. **MOYENNE** - Rate limiting et circuit breakers
---
## 🔍 Analyse des Performances
### 1. Base de Données (PostgreSQL)
#### ❌ Problèmes Identifiés
**1.1 Pool de Connexions Non Configuré**
```typescript
// lib/prisma.ts - ACTUEL
export const prisma = new PrismaClient({
datasources: { db: { url: env.DATABASE_URL } },
log: process.env.NODE_ENV === 'production' ? [] : ['query'],
})
```
**Problème:**
- Pas de limite de connexions
- Risque d'épuisement du pool sous charge
- Pas de configuration de timeout
- Pas de monitoring des connexions
**Impact:** 🔴 **CRITIQUE** - Peut causer des timeouts et crashes en production
**Solution Recommandée:**
```typescript
// lib/prisma.ts - RECOMMANDÉ
export const prisma = new PrismaClient({
datasources: {
db: {
url: env.DATABASE_URL
}
},
log: process.env.NODE_ENV === 'production' ? [] : ['error', 'warn'],
})
// Configuration du pool via DATABASE_URL
// DATABASE_URL=postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=20
```
**Configuration DATABASE_URL recommandée:**
```
postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=20&connect_timeout=10
```
**1.2 Requêtes N+1 Potentielles**
**Fichiers à vérifier:**
- `app/api/missions/route.ts` - Include multiples relations
- `app/api/missions/all/route.ts` - Include missionUsers avec user
- `app/api/calendars/route.ts` - Vérifier les requêtes imbriquées
**Recommandation:** Utiliser `select` au lieu de `include` quand possible, ou utiliser `Prisma.raw` pour des requêtes optimisées.
---
### 2. Requêtes HTTP Externes
#### ❌ Problèmes Identifiés
**2.1 Timeouts Manquants**
**Fichiers affectés:**
- `lib/services/n8n-service.ts` - Pas de timeout sur fetch
- `app/api/missions/[missionId]/generate-plan/route.ts` - Pas de timeout
- `app/api/users/[userId]/route.ts` - Pas de timeout sur Leantime API
- `app/api/rocket-chat/messages/route.ts` - Timeouts partiels
- `app/api/leantime/tasks/route.ts` - Pas de timeout
**Exemple problématique:**
```typescript
// ❌ MAUVAIS - app/api/missions/[missionId]/generate-plan/route.ts
const response = await fetch(webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },
body: JSON.stringify(webhookData),
});
// Pas de timeout - peut bloquer indéfiniment
```
**Solution Recommandée:**
```typescript
// ✅ BON
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000); // 30s timeout
try {
const response = await fetch(webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },
body: JSON.stringify(webhookData),
signal: controller.signal,
});
clearTimeout(timeoutId);
// ... handle response
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error('Request timeout after 30s');
}
throw error;
}
```
**2.2 Pas de Circuit Breaker**
**Problème:** Si un service externe (N8N, Leantime, RocketChat) est down, toutes les requêtes échouent sans retry intelligent.
**Recommandation:** Implémenter un circuit breaker pattern:
```typescript
// lib/utils/circuit-breaker.ts
class CircuitBreaker {
private failures = 0;
private lastFailureTime = 0;
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
async execute<T>(fn: () => Promise<T>): Promise<T> {
if (this.state === 'OPEN') {
if (Date.now() - this.lastFailureTime > 60000) {
this.state = 'HALF_OPEN';
} else {
throw new Error('Circuit breaker is OPEN');
}
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private onSuccess() {
this.failures = 0;
this.state = 'CLOSED';
}
private onFailure() {
this.failures++;
this.lastFailureTime = Date.now();
if (this.failures >= 5) {
this.state = 'OPEN';
}
}
}
```
---
### 3. Cache et Optimisations
#### ✅ Points Positifs
- Redis utilisé pour cache emails, notifications, messages
- Request deduplication implémentée (`lib/utils/request-deduplication.ts`)
- Cache avec TTL approprié
#### ⚠️ Améliorations Possibles
**3.1 Cache Database Queries**
```typescript
// Exemple: app/api/missions/route.ts
// Ajouter cache pour les requêtes fréquentes
const cacheKey = `missions:${userId}:${limit}:${offset}`;
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);
const missions = await prisma.mission.findMany(...);
await redis.setex(cacheKey, 60, JSON.stringify(missions)); // 60s TTL
```
**3.2 Optimisation Images Next.js**
**Problème actuel:**
```javascript
// next.config.mjs
images: {
unoptimized: true, // ❌ Désactive l'optimisation
}
```
**Solution:**
```javascript
images: {
unoptimized: false, // ✅ Active l'optimisation
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 750, 828, 1080, 1200],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
}
```
---
### 4. WebSocket et Connexions Longues
#### ⚠️ Problèmes Identifiés
**4.1 RocketChat Call Listener**
- `lib/services/rocketchat-call-listener.ts`
- Pas de rate limiting sur reconnexions
- Pas de backoff exponentiel optimal
- Logs console.log excessifs (35 occurrences)
**Recommandations:**
```typescript
// Améliorer le backoff
private reconnectDelay = 3000;
private maxReconnectAttempts = 10;
// ✅ MEILLEUR
private getReconnectDelay(): number {
return Math.min(3000 * Math.pow(2, this.reconnectAttempts), 30000);
}
```
**4.2 IMAP Connection Pool**
- `lib/services/email-service.ts`
- Pool bien géré mais peut être optimisé
- Timeout de 30 minutes peut être réduit
---
### 5. Logging et Monitoring
#### ❌ Problèmes Critiques
**5.1 Console.log en Production**
**Statistiques:**
- 110 occurrences de `console.log/error/warn/debug` dans `lib/services/`
- 35 dans `rocketchat-call-listener.ts`
- 19 dans `refresh-manager.ts`
- 17 dans `prefetch-service.ts`
**Impact:**
- Performance dégradée (console.log est synchrone)
- Logs non structurés
- Pas de centralisation
**Solution:**
```typescript
// Remplacer tous les console.log par logger
// ❌ AVANT
console.log('[ROCKETCHAT] Message received', data);
// ✅ APRÈS
logger.debug('[ROCKETCHAT] Message received', { data });
```
**Action:** Créer un script de migration:
```bash
# scripts/replace-console-logs.sh
find lib/services -name "*.ts" -exec sed -i '' 's/console\.log/logger.debug/g' {} \;
find lib/services -name "*.ts" -exec sed -i '' 's/console\.error/logger.error/g' {} \;
find lib/services -name "*.ts" -exec sed -i '' 's/console\.warn/logger.warn/g' {} \;
```
**5.2 Pas de Métriques de Performance**
**Recommandation:** Ajouter des métriques:
```typescript
// lib/utils/metrics.ts
export function trackApiCall(endpoint: string, duration: number) {
// Envoyer à un service de métriques (DataDog, New Relic, etc.)
logger.info('API_CALL', { endpoint, duration });
}
```
---
## 🏗️ Problèmes de Configuration Production
### 1. Dockerfile
#### ❌ Problème Critique
**Fichier:** `Dockerfile`
```dockerfile
# ❌ MAUVAIS - Ligne 18
RUN npx prisma migrate dev --name init
```
**Problème:**
- `migrate dev` crée une nouvelle migration même si la DB est à jour
- Ne doit JAMAIS être utilisé en production
- Peut causer des conflits de migrations
**Solution:**
```dockerfile
# ✅ BON
# Ne pas faire de migrations dans le Dockerfile
# Les migrations doivent être appliquées séparément avant le déploiement
# RUN npx prisma migrate deploy # Seulement si nécessaire, et seulement en production
```
**Recommandation:** Utiliser `Dockerfile.prod` qui est mieux configuré, mais vérifier qu'il n'utilise pas `migrate dev`.
### 2. Next.js Configuration
#### ⚠️ Problèmes
**Fichier:** `next.config.mjs`
```javascript
// ❌ PROBLÉMATIQUE
eslint: {
ignoreDuringBuilds: true, // Cache les erreurs ESLint
},
typescript: {
ignoreBuildErrors: true, // Cache les erreurs TypeScript
},
images: {
unoptimized: true, // Désactive l'optimisation d'images
},
```
**Recommandation:**
```javascript
// ✅ MEILLEUR
eslint: {
ignoreDuringBuilds: false, // Ou au moins en staging
},
typescript: {
ignoreBuildErrors: false, // Corriger les erreurs TypeScript
},
images: {
unoptimized: false, // Activer l'optimisation
formats: ['image/avif', 'image/webp'],
},
```
### 3. Variables d'Environnement
#### ⚠️ Vérifications Nécessaires
**Variables critiques à vérifier:**
- `DATABASE_URL` - Doit inclure les paramètres de pool
- `REDIS_URL` - Doit être configuré avec timeout
- `NEXTAUTH_SECRET` - Doit être unique et fort
- Tous les tokens API (N8N, Leantime, RocketChat)
**Recommandation:** Créer un script de validation:
```typescript
// scripts/validate-env.ts
const requiredVars = [
'DATABASE_URL',
'NEXTAUTH_SECRET',
'NEXTAUTH_URL',
// ...
];
for (const varName of requiredVars) {
if (!process.env[varName]) {
throw new Error(`Missing required environment variable: ${varName}`);
}
}
```
---
## 🔒 Sécurité
### 1. Secrets dans les Logs
#### ⚠️ Risque Modéré
**Problème:** Vérifier que les mots de passe/tokens ne sont pas loggés.
**Fichiers à vérifier:**
- `lib/services/email-service.ts` - Credentials IMAP
- `app/api/courrier/test-connection/route.ts` - Password dans logs
**Solution actuelle (bonne):**
```typescript
// ✅ BON - app/api/courrier/test-connection/route.ts
console.log('Testing connection with:', {
...body,
password: body.password ? '***' : undefined // Masqué
});
```
### 2. Rate Limiting
#### ❌ Manquant
**Problème:** Pas de rate limiting sur les API routes.
**Recommandation:** Implémenter avec `@upstash/ratelimit` ou middleware Next.js:
```typescript
// middleware.ts
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, '10 s'),
});
export async function middleware(request: NextRequest) {
const ip = request.ip ?? '127.0.0.1';
const { success } = await ratelimit.limit(ip);
if (!success) {
return new Response('Rate limit exceeded', { status: 429 });
}
}
```
### 3. CORS et Headers Sécurité
#### ✅ Bon
**Fichier:** `next.config.mjs`
- Content-Security-Policy configuré
- Headers de sécurité à ajouter si nécessaire
---
## 🎯 Recommandations Prioritaires
### Priorité 1 - CRITIQUE (Avant Production)
1. **✅ Configurer le pool de connexions Prisma**
- Modifier `DATABASE_URL` avec `connection_limit=10&pool_timeout=20`
- Tester sous charge
2. **✅ Corriger le Dockerfile**
- Retirer `migrate dev`
- Utiliser `migrate deploy` uniquement si nécessaire
3. **✅ Ajouter des timeouts sur toutes les requêtes HTTP**
- Créer un utilitaire `fetchWithTimeout`
- Appliquer à tous les `fetch()` externes
4. **✅ Remplacer console.log par logger**
- Script de migration automatique
- Vérifier tous les fichiers
### Priorité 2 - HAUTE (Semaine 1)
5. **✅ Implémenter Circuit Breaker**
- Pour N8N, Leantime, RocketChat
- Retry avec backoff exponentiel
6. **✅ Activer l'optimisation d'images Next.js**
- Modifier `next.config.mjs`
- Tester les performances
7. **✅ Ajouter Rate Limiting**
- Sur les API routes critiques
- Monitoring des limites
### Priorité 3 - MOYENNE (Semaine 2-3)
8. **✅ Cache des requêtes DB fréquentes**
- Missions, Calendars, Users
- TTL approprié (60-300s)
9. **✅ Monitoring et Métriques**
- Intégrer Sentry ou équivalent
- Métriques de performance (APM)
10. **✅ Optimiser les requêtes N+1**
- Audit des `include` Prisma
- Utiliser `select` quand possible
---
## ✅ Checklist de Mise en Production
### Pré-Déploiement
#### Configuration
- [ ] Configurer `DATABASE_URL` avec pool settings
- [ ] Vérifier toutes les variables d'environnement
- [ ] Créer script de validation des env vars
- [ ] Configurer les secrets dans Vercel (pas en code)
#### Code
- [ ] Corriger le Dockerfile (retirer `migrate dev`)
- [ ] Ajouter timeouts sur toutes les requêtes HTTP
- [ ] Remplacer tous les `console.log` par `logger`
- [ ] Activer l'optimisation d'images Next.js
- [ ] Corriger les erreurs TypeScript/ESLint (ou documenter pourquoi ignorées)
#### Base de Données
- [ ] Appliquer toutes les migrations Prisma
- [ ] Créer des index sur les colonnes fréquemment queryées
- [ ] Configurer les sauvegardes automatiques
- [ ] Tester la restauration depuis backup
#### Sécurité
- [ ] Implémenter rate limiting
- [ ] Vérifier qu'aucun secret n'est loggé
- [ ] Configurer CORS correctement
- [ ] Activer HTTPS uniquement
#### Monitoring
- [ ] Configurer Sentry ou équivalent
- [ ] Créer endpoint `/api/health`
- [ ] Configurer les alertes Vercel
- [ ] Documenter les procédures d'alerte
### Déploiement
#### Staging
- [ ] Déployer en staging d'abord
- [ ] Tester toutes les fonctionnalités critiques
- [ ] Vérifier les performances sous charge
- [ ] Tester les scénarios d'erreur
#### Production
- [ ] Appliquer les migrations Prisma
- [ ] Déployer sur Vercel
- [ ] Vérifier les health checks
- [ ] Monitorer les logs les premières heures
- [ ] Tester les fonctionnalités critiques
### Post-Déploiement
#### Monitoring
- [ ] Surveiller les métriques de performance
- [ ] Vérifier les logs d'erreurs
- [ ] Monitorer l'utilisation DB/Redis
- [ ] Vérifier les alertes fonctionnent
#### Optimisation Continue
- [ ] Analyser les requêtes DB lentes
- [ ] Optimiser les endpoints les plus utilisés
- [ ] Ajuster les TTL de cache selon l'usage
- [ ] Réviser les timeouts selon les métriques
---
## 📊 Métriques de Succès
### Performance
- **Temps de réponse API:** < 200ms (p95)
- **Temps de chargement page:** < 2s (First Contentful Paint)
- **Taux d'erreur:** < 0.1%
- **Uptime:** > 99.9%
### Base de Données
- **Connexions actives:** < 80% du pool max
- **Requêtes lentes:** < 1% des requêtes > 1s
- **Taux de cache hit:** > 70% pour les requêtes fréquentes
### Infrastructure
- **CPU utilisation:** < 70% en moyenne
- **Mémoire:** < 80% en moyenne
- **Disque:** < 80% utilisé
---
## 🔧 Scripts Utiles
### Validation Environnement
```bash
# scripts/validate-production.sh
#!/bin/bash
set -e
echo "Validating production environment..."
# Vérifier les variables d'environnement
node scripts/validate-env.ts
# Vérifier les migrations
npx prisma migrate status
# Vérifier la connexion DB
npx prisma db execute --stdin <<< "SELECT 1"
# Vérifier Redis
redis-cli -h $REDIS_HOST -p $REDIS_PORT ping
echo "✅ All checks passed"
```
### Migration Console.log
```bash
# scripts/migrate-console-logs.sh
#!/bin/bash
find lib/services app/api -name "*.ts" -type f | while read file; do
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"
done
echo "✅ Console logs migrated to logger"
```
### Health Check
```typescript
// app/api/health/route.ts
import { NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { getRedisClient } from '@/lib/redis';
export async function GET() {
const health = {
status: 'ok',
timestamp: new Date().toISOString(),
checks: {
database: 'unknown',
redis: 'unknown',
},
};
// Check DB
try {
await prisma.$queryRaw`SELECT 1`;
health.checks.database = 'ok';
} catch (error) {
health.checks.database = 'error';
health.status = 'degraded';
}
// Check Redis
try {
const redis = getRedisClient();
await redis.ping();
health.checks.redis = 'ok';
} catch (error) {
health.checks.redis = 'error';
health.status = 'degraded';
}
return NextResponse.json(health, {
status: health.status === 'ok' ? 200 : 503,
});
}
```
---
## 📚 Ressources
- [Prisma Connection Pooling](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management)
- [Next.js Production Checklist](https://nextjs.org/docs/deployment#production-checklist)
- [Vercel Best Practices](https://vercel.com/docs/concepts/deployments/best-practices)
- [PostgreSQL Performance Tuning](https://www.postgresql.org/docs/current/performance-tips.html)
---
## 📝 Notes Finales
Cette analyse identifie les problèmes critiques à résoudre avant la mise en production. Les priorités 1 doivent être traitées immédiatement, les priorités 2 dans la semaine, et les priorités 3 peuvent être planifiées après le déploiement initial.
**Recommandation:** Créer des tickets pour chaque point de la checklist et les suivre jusqu'à résolution complète.
---
**Dernière mise à jour:** $(date)