# 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(fn: () => Promise): Promise { 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)