18 KiB
Analyse Performance et Préparation Production - Neah
Date: $(date)
Auteur: Analyse Senior Developer
Version: 1.0
📋 Table des Matières
- Résumé Exécutif
- Analyse des Performances
- Problèmes de Configuration Production
- Sécurité
- Recommandations Prioritaires
- 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
- CRITIQUE - Configuration pool de connexions Prisma
- CRITIQUE - Correction Dockerfile (migrate dev → deploy)
- HAUTE - Ajout de timeouts sur toutes les requêtes HTTP
- HAUTE - Remplacement console.log par logger structuré
- MOYENNE - Optimisation images Next.js
- 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é
// 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:
// 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 relationsapp/api/missions/all/route.ts- Include missionUsers avec userapp/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 fetchapp/api/missions/[missionId]/generate-plan/route.ts- Pas de timeoutapp/api/users/[userId]/route.ts- Pas de timeout sur Leantime APIapp/api/rocket-chat/messages/route.ts- Timeouts partielsapp/api/leantime/tasks/route.ts- Pas de timeout
Exemple problématique:
// ❌ 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:
// ✅ 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:
// 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
// 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:
// next.config.mjs
images: {
unoptimized: true, // ❌ Désactive l'optimisation
}
Solution:
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:
// 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/debugdanslib/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:
// 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:
# 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:
// 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
# ❌ MAUVAIS - Ligne 18
RUN npx prisma migrate dev --name init
Problème:
migrate devcré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:
# ✅ 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
// ❌ 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:
// ✅ 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 poolREDIS_URL- Doit être configuré avec timeoutNEXTAUTH_SECRET- Doit être unique et fort- Tous les tokens API (N8N, Leantime, RocketChat)
Recommandation: Créer un script de validation:
// 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 IMAPapp/api/courrier/test-connection/route.ts- Password dans logs
Solution actuelle (bonne):
// ✅ 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:
// 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)
-
✅ Configurer le pool de connexions Prisma
- Modifier
DATABASE_URLavecconnection_limit=10&pool_timeout=20 - Tester sous charge
- Modifier
-
✅ Corriger le Dockerfile
- Retirer
migrate dev - Utiliser
migrate deployuniquement si nécessaire
- Retirer
-
✅ Ajouter des timeouts sur toutes les requêtes HTTP
- Créer un utilitaire
fetchWithTimeout - Appliquer à tous les
fetch()externes
- Créer un utilitaire
-
✅ Remplacer console.log par logger
- Script de migration automatique
- Vérifier tous les fichiers
Priorité 2 - HAUTE (Semaine 1)
-
✅ Implémenter Circuit Breaker
- Pour N8N, Leantime, RocketChat
- Retry avec backoff exponentiel
-
✅ Activer l'optimisation d'images Next.js
- Modifier
next.config.mjs - Tester les performances
- Modifier
-
✅ Ajouter Rate Limiting
- Sur les API routes critiques
- Monitoring des limites
Priorité 3 - MOYENNE (Semaine 2-3)
-
✅ Cache des requêtes DB fréquentes
- Missions, Calendars, Users
- TTL approprié (60-300s)
-
✅ Monitoring et Métriques
- Intégrer Sentry ou équivalent
- Métriques de performance (APM)
-
✅ Optimiser les requêtes N+1
- Audit des
includePrisma - Utiliser
selectquand possible
- Audit des
✅ Checklist de Mise en Production
Pré-Déploiement
Configuration
- Configurer
DATABASE_URLavec 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.logparlogger - 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
# 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
# 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
// 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
- Next.js Production Checklist
- Vercel Best Practices
- PostgreSQL Performance Tuning
📝 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)