# 🔍 Audit Développeur Senior - Connexion API Next.js ↔️ N8N (Missions) **Date**: $(date) **Auteur**: Audit Développeur Senior **Objectif**: Vérifier et documenter la connexion entre Next.js et N8N pour la gestion des missions --- ## 📋 Table des Matières 1. [Architecture Globale](#architecture-globale) 2. [Flux de Communication](#flux-de-communication) 3. [Endpoints API](#endpoints-api) 4. [Configuration Requise](#configuration-requise) 5. [Sécurité](#sécurité) 6. [Points Critiques à Vérifier](#points-critiques-à-vérifier) 7. [Problèmes Potentiels et Solutions](#problèmes-potentiels-et-solutions) 8. [Tests et Validation](#tests-et-validation) 9. [Recommandations](#recommandations) --- ## 🏗️ Architecture Globale ### Vue d'ensemble ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Next.js │────────▶│ N8N │────────▶│ Intégrations│ │ (API) │ │ (Workflow) │ │ (Gitea, etc)│ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ └─────────────────────────┘ (Callback) ``` ### Composants Principaux 1. **Next.js API Routes** - `POST /api/missions` - Création de mission - `POST /api/missions/mission-created` - Callback de N8N - `GET /api/missions` - Liste des missions 2. **Service N8N** (`lib/services/n8n-service.ts`) - Envoi de données vers N8N - Gestion des webhooks - Gestion des erreurs 3. **N8N Workflows** - Webhook de réception: `/webhook/mission-created` - Création des intégrations externes - Callback vers Next.js --- ## 🔄 Flux de Communication ### 1. Création d'une Mission (Next.js → N8N → Next.js) ``` ┌─────────────────────────────────────────────────────────────────┐ │ ÉTAPE 1: Création Mission dans Next.js │ └─────────────────────────────────────────────────────────────────┘ POST /api/missions ↓ 1. Validation des données 2. Création en base de données (Prisma) 3. Upload des fichiers (logo, attachments) vers Minio 4. Vérification des fichiers ↓ ┌─────────────────────────────────────────────────────────────────┐ │ ÉTAPE 2: Envoi vers N8N │ └─────────────────────────────────────────────────────────────────┘ POST https://brain.slm-lab.net/webhook/mission-created Headers: - Content-Type: application/json - x-api-key: {N8N_API_KEY} Body: { missionId: "uuid", name: "...", oddScope: [...], services: [...], config: { N8N_API_KEY: "...", MISSION_API_URL: "https://api.slm-lab.net/api" }, ... } ↓ ┌─────────────────────────────────────────────────────────────────┐ │ ÉTAPE 3: Traitement N8N │ └─────────────────────────────────────────────────────────────────┘ N8N Workflow: 1. Réception webhook 2. Création Gitea repository (si service "Gite") 3. Création Leantime project (si service "Leantime") 4. Création Outline collection (si service "Documentation") 5. Création RocketChat channel (si service "RocketChat") 6. Préparation des données de callback ↓ ┌─────────────────────────────────────────────────────────────────┐ │ ÉTAPE 4: Callback N8N → Next.js │ └─────────────────────────────────────────────────────────────────┘ POST {MISSION_API_URL}/api/missions/mission-created Headers: - Content-Type: application/json - x-api-key: {N8N_API_KEY} (depuis config.N8N_API_KEY) Body: { missionId: "uuid", gitRepoUrl: "...", leantimeProjectId: "...", documentationCollectionId: "...", rocketchatChannelId: "..." } ↓ ┌─────────────────────────────────────────────────────────────────┐ │ ÉTAPE 5: Mise à jour Mission dans Next.js │ └─────────────────────────────────────────────────────────────────┘ Validation API key Recherche mission par missionId Mise à jour des champs d'intégration: - giteaRepositoryUrl - leantimeProjectId - outlineCollectionId - rocketChatChannelId ``` --- ## 🔌 Endpoints API ### 1. POST /api/missions **Fichier**: `app/api/missions/route.ts` **Fonction**: Créer une nouvelle mission et déclencher le workflow N8N **Authentification**: - Session utilisateur requise (via `getServerSession`) - Vérification: `checkAuth(request)` **Body attendu**: ```typescript { name: string; oddScope: string[]; niveau?: string; intention?: string; missionType?: string; services?: string[]; guardians?: Record; volunteers?: string[]; logo?: { data: string; name?: string; type?: string }; attachments?: Array<{ data: string; name?: string; type?: string }>; } ``` **Réponse**: ```json { "success": true, "mission": { ... }, "message": "Mission created successfully with all integrations" } ``` **Points critiques**: - ✅ Mission créée en base AVANT l'envoi à N8N - ✅ Fichiers uploadés et vérifiés AVANT l'envoi à N8N - ✅ `missionId` inclus dans les données envoyées à N8N - ✅ `config.N8N_API_KEY` et `config.MISSION_API_URL` inclus --- ### 2. POST /api/missions/mission-created **Fichier**: `app/api/missions/mission-created/route.ts` **Fonction**: Recevoir les IDs d'intégration de N8N et mettre à jour la mission **Authentification**: - **API Key** via header `x-api-key` - **PAS** de session utilisateur requise (N8N n'a pas de session) **Headers requis**: ``` x-api-key: {N8N_API_KEY} Content-Type: application/json ``` **Body attendu**: ```typescript { missionId: string; // ✅ Préféré (plus fiable) // OU (fallback pour compatibilité) name: string; creatorId: string; // IDs d'intégration (optionnels) gitRepoUrl?: string; leantimeProjectId?: string | number; documentationCollectionId?: string; rocketchatChannelId?: string; } ``` **Réponse succès**: ```json { "success": true, "message": "Mission updated successfully", "mission": { "id": "...", "name": "...", "giteaRepositoryUrl": "...", "leantimeProjectId": "...", "outlineCollectionId": "...", "rocketChatChannelId": "..." } } ``` **Codes d'erreur**: - `401` - API key invalide ou manquante - `400` - Champs requis manquants - `404` - Mission non trouvée - `500` - Erreur serveur **Points critiques**: - ✅ Validation stricte de l'API key - ✅ Recherche par `missionId` (préféré) ou `name + creatorId` (fallback) - ✅ Conversion `leantimeProjectId` de number vers string si nécessaire - ✅ Mise à jour uniquement des champs fournis --- ## ⚙️ Configuration Requise ### Variables d'Environnement #### 1. N8N_API_KEY (OBLIGATOIRE) ```env N8N_API_KEY=LwgeE1ntADD20OuWC88S3pR0EaO7FtO4 ``` **Usage**: - Envoyé à N8N dans `config.N8N_API_KEY` - N8N l'utilise pour authentifier le callback - Vérifié côté serveur dans `/api/missions/mission-created` **Où configurer**: - `.env.local` (développement) - Variables d'environnement production (CapRover, Vercel, Docker, etc.) **Vérification**: ```typescript // Erreur si non défini if (!process.env.N8N_API_KEY) { logger.error('N8N_API_KEY is not set in environment variables'); } ``` --- #### 2. N8N_WEBHOOK_URL (Optionnel) ```env N8N_WEBHOOK_URL=https://brain.slm-lab.net/webhook/mission-created ``` **Valeur par défaut**: `https://brain.slm-lab.net/webhook/mission-created` **Usage**: URL du webhook N8N pour la création de mission --- #### 3. NEXT_PUBLIC_API_URL (Recommandé) ```env NEXT_PUBLIC_API_URL=https://api.slm-lab.net/api ``` **Usage**: - Envoyé à N8N dans `config.MISSION_API_URL` - N8N l'utilise pour construire l'URL du callback - Format attendu: `{MISSION_API_URL}/api/missions/mission-created` **Valeur par défaut**: `https://api.slm-lab.net/api` --- #### 4. N8N_ROLLBACK_WEBHOOK_URL (Optionnel) ```env N8N_ROLLBACK_WEBHOOK_URL=https://brain.slm-lab.net/webhook/mission-rollback ``` **Usage**: URL du webhook N8N pour le rollback de mission --- ### Configuration N8N Workflow #### Webhook de Réception **Path**: `mission-created` **URL complète**: `https://brain.slm-lab.net/webhook/mission-created` **Méthode**: `POST` **Status**: Doit être **ACTIF** (toggle vert dans N8N) --- #### Node "Save Mission To API" **URL**: ``` {{ $node['Process Mission Data'].json.config.MISSION_API_URL }}/api/missions/mission-created ``` **Méthode**: `POST` **Headers**: ``` Content-Type: application/json x-api-key: {{ $node['Process Mission Data'].json.config.N8N_API_KEY }} ``` **Body**: ```json { "missionId": "{{ $node['Process Mission Data'].json.missionId }}", "gitRepoUrl": "{{ $node['Create Git Repo'].json.url }}", "leantimeProjectId": "{{ $node['Create Leantime Project'].json.id }}", "documentationCollectionId": "{{ $node['Create Outline Collection'].json.id }}", "rocketchatChannelId": "{{ $node['Create RocketChat Channel'].json.id }}" } ``` **Points critiques**: - ✅ Utiliser `config.MISSION_API_URL` (pas d'URL en dur) - ✅ Utiliser `config.N8N_API_KEY` (pas de clé en dur) - ✅ Inclure `missionId` dans le body - ✅ Inclure tous les IDs d'intégration créés --- ## 🔒 Sécurité ### 1. Authentification API Key **Mécanisme**: - N8N envoie `x-api-key` header - Next.js compare avec `process.env.N8N_API_KEY` - Si différent → `401 Unauthorized` **Code de validation** (`app/api/missions/mission-created/route.ts:42`): ```typescript const apiKey = request.headers.get('x-api-key'); const expectedApiKey = process.env.N8N_API_KEY; if (apiKey !== expectedApiKey) { logger.error('Invalid API key', { received: apiKey ? 'present' : 'missing', expected: expectedApiKey ? 'configured' : 'missing' }); return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } ``` **Points critiques**: - ✅ Comparaison stricte (pas de hash, clé en clair) - ✅ Logging des tentatives invalides - ✅ Pas de fallback si clé manquante --- ### 2. Transmission de la Clé API **Flux**: 1. Next.js lit `process.env.N8N_API_KEY` 2. Next.js envoie à N8N dans `config.N8N_API_KEY` 3. N8N stocke temporairement dans le workflow 4. N8N renvoie dans header `x-api-key` lors du callback **Risque**: Si `N8N_API_KEY` est `undefined` au moment de l'envoi: - N8N reçoit `undefined` ou chaîne vide - N8N envoie chaîne vide dans le header - Next.js rejette avec `401` **Solution**: Vérifier que `N8N_API_KEY` est défini avant l'envoi à N8N --- ### 3. Validation des Données **Côté Next.js**: - ✅ Validation des champs requis - ✅ Recherche de mission par `missionId` (plus sûr que `name + creatorId`) - ✅ Conversion de types (number → string pour `leantimeProjectId`) **Côté N8N**: - ⚠️ Pas de validation visible dans le code Next.js - ⚠️ N8N doit valider les données avant création des intégrations --- ## ⚠️ Points Critiques à Vérifier ### 1. Configuration Environnement - [ ] `N8N_API_KEY` est défini dans l'environnement - [ ] `N8N_API_KEY` a la même valeur partout (dev, staging, prod) - [ ] `NEXT_PUBLIC_API_URL` pointe vers la bonne URL - [ ] Application redémarrée après modification des variables --- ### 2. Workflow N8N - [ ] Workflow est **ACTIF** (toggle vert) - [ ] Webhook path est correct: `mission-created` - [ ] Node "Save Mission To API" utilise `config.MISSION_API_URL` - [ ] Node "Save Mission To API" utilise `config.N8N_API_KEY` - [ ] Node "Save Mission To API" inclut `missionId` dans le body - [ ] Tous les IDs d'intégration sont inclus dans le callback --- ### 3. Flux de Données - [ ] `missionId` est envoyé à N8N lors de la création - [ ] `missionId` est renvoyé par N8N dans le callback - [ ] Les IDs d'intégration sont correctement mappés: - `gitRepoUrl` → `giteaRepositoryUrl` - `leantimeProjectId` → `leantimeProjectId` (string) - `documentationCollectionId` → `outlineCollectionId` - `rocketchatChannelId` → `rocketChatChannelId` --- ### 4. Gestion d'Erreurs - [ ] Erreurs N8N sont loggées - [ ] Rollback en cas d'échec (si configuré) - [ ] Messages d'erreur clairs pour debugging - [ ] Pas de données sensibles dans les logs --- ## 🐛 Problèmes Potentiels et Solutions ### Problème 1: 401 Unauthorized **Symptômes**: ``` Invalid API key { received: 'present', expected: 'configured' } ``` **Causes possibles**: 1. `N8N_API_KEY` non défini dans l'environnement 2. `N8N_API_KEY` différent entre Next.js et N8N 3. N8N envoie une clé vide ou `undefined` **Solutions**: 1. Vérifier que `N8N_API_KEY` est défini: ```bash echo $N8N_API_KEY ``` 2. Vérifier la valeur dans N8N: - Ouvrir l'exécution du workflow - Vérifier `config.N8N_API_KEY` dans "Process Mission Data" 3. S'assurer que la même clé est utilisée partout --- ### Problème 2: 404 Mission Not Found **Symptômes**: ``` Mission not found { missionId: "...", name: "...", creatorId: "..." } ``` **Causes possibles**: 1. `missionId` non envoyé par N8N 2. `missionId` incorrect 3. Mission supprimée entre temps **Solutions**: 1. Vérifier que N8N envoie `missionId`: ```json { "missionId": "{{ $node['Process Mission Data'].json.missionId }}" } ``` 2. Vérifier que Next.js envoie `missionId` à N8N: ```typescript config: { missionId: mission.id // ✅ Inclus dans n8nData } ``` 3. Utiliser le fallback `name + creatorId` si `missionId` manquant --- ### Problème 3: 500 Server Configuration Error **Symptômes**: ``` N8N_API_KEY not configured in environment ``` **Cause**: `process.env.N8N_API_KEY` est `undefined` **Solution**: 1. Ajouter `N8N_API_KEY` à `.env.local` ou variables d'environnement 2. Redémarrer l'application 3. Vérifier avec un endpoint de test --- ### Problème 4: 404 Webhook Not Registered **Symptômes**: ``` 404 Error: The requested webhook "mission-created" is not registered. Hint: Click the 'Execute workflow' button on the canvas, then try again. ``` **Cause**: Workflow N8N n'est pas actif **Solution**: 1. Ouvrir le workflow dans N8N 2. Activer le toggle "Active" (devrait être vert) 3. Vérifier que le webhook node est actif --- ### Problème 5: IDs d'Intégration Non Sauvegardés **Symptômes**: - Mission créée mais `giteaRepositoryUrl`, `leantimeProjectId`, etc. sont `null` **Causes possibles**: 1. N8N ne rappelle pas `/api/missions/mission-created` 2. N8N rappelle mais avec des IDs manquants 3. Erreur lors de la mise à jour en base **Solutions**: 1. Vérifier les logs N8N (Executions) 2. Vérifier que le node "Save Mission To API" s'exécute 3. Vérifier les logs Next.js pour "Mission Created Webhook Received" 4. Vérifier que tous les IDs sont inclus dans le body du callback --- ## 🧪 Tests et Validation ### Test 1: Vérifier Configuration **Endpoint de test** (à créer): ```typescript // app/api/test-n8n-config/route.ts import { NextResponse } from 'next/server'; export async function GET() { return NextResponse.json({ hasN8NApiKey: !!process.env.N8N_API_KEY, n8nApiKeyLength: process.env.N8N_API_KEY?.length || 0, n8nWebhookUrl: process.env.N8N_WEBHOOK_URL || 'https://brain.slm-lab.net/webhook/mission-created', missionApiUrl: process.env.NEXT_PUBLIC_API_URL || 'https://api.slm-lab.net/api' }); } ``` **Usage**: `GET /api/test-n8n-config` --- ### Test 2: Tester Webhook N8N ```bash curl -X POST https://brain.slm-lab.net/webhook/mission-created \ -H "Content-Type: application/json" \ -d '{"test": "data"}' ``` **Résultats attendus**: - ✅ `200/400/500` avec erreur workflow: Webhook actif - ❌ `404` avec "webhook not registered": Webhook inactif --- ### Test 3: Tester Callback Endpoint ```bash curl -X POST https://api.slm-lab.net/api/missions/mission-created \ -H "Content-Type: application/json" \ -H "x-api-key: YOUR_N8N_API_KEY" \ -d '{ "missionId": "test-mission-id", "gitRepoUrl": "https://git.example.com/repo", "leantimeProjectId": "123" }' ``` **Résultats attendus**: - ✅ `200` avec `success: true`: API key valide - ❌ `401`: API key invalide - ❌ `404`: Mission non trouvée (normal si missionId de test) --- ### Test 4: Créer une Mission Complète 1. Créer une mission via le frontend 2. Vérifier les logs Next.js: - ✅ "Mission created successfully" - ✅ "Starting N8N workflow" - ✅ "N8N workflow result { success: true }" 3. Vérifier les logs N8N (Executions): - ✅ Workflow exécuté avec succès - ✅ Node "Save Mission To API" exécuté 4. Vérifier la base de données: - ✅ Mission a les IDs d'intégration sauvegardés --- ## 💡 Recommandations ### 1. Amélioration de la Sécurité **Problème actuel**: Clé API en clair, comparaison simple **Recommandations**: - [ ] Utiliser un système de tokens avec expiration - [ ] Implémenter un système de signature HMAC - [ ] Ajouter un rate limiting sur `/api/missions/mission-created` - [ ] Logging des tentatives d'accès invalides avec IP --- ### 2. Amélioration de la Robustesse **Problème actuel**: Pas de retry automatique si N8N échoue **Recommandations**: - [ ] Implémenter un système de retry avec backoff exponentiel - [ ] Queue de messages pour les callbacks manqués - [ ] Webhook de santé pour vérifier que N8N est accessible - [ ] Timeout configurable pour les appels N8N --- ### 3. Amélioration du Debugging **Problème actuel**: Logs dispersés, pas de traçabilité complète **Recommandations**: - [ ] Ajouter un `correlationId` pour tracer une mission de bout en bout - [ ] Logs structurés avec contexte complet - [ ] Dashboard de monitoring des intégrations - [ ] Alertes en cas d'échec répété --- ### 4. Amélioration de la Documentation **Recommandations**: - [ ] Documenter le format exact attendu par N8N - [ ] Exemples de payloads complets - [ ] Diagrammes de séquence détaillés - [ ] Guide de troubleshooting avec cas réels --- ### 5. Tests Automatisés **Recommandations**: - [ ] Tests unitaires pour `N8nService` - [ ] Tests d'intégration pour les endpoints API - [ ] Tests E2E avec mock N8N - [ ] Tests de charge pour vérifier la scalabilité --- ## 📝 Checklist de Vérification Rapide ### Configuration - [ ] `N8N_API_KEY` défini et identique partout - [ ] `NEXT_PUBLIC_API_URL` pointe vers la bonne URL - [ ] Application redémarrée après modifications ### N8N Workflow - [ ] Workflow actif (toggle vert) - [ ] Webhook path: `mission-created` - [ ] Node "Save Mission To API" configuré correctement - [ ] `missionId` inclus dans le callback ### Code Next.js - [ ] `missionId` envoyé à N8N lors de la création - [ ] Validation API key fonctionnelle - [ ] Mapping des champs correct - [ ] Gestion d'erreurs appropriée ### Tests - [ ] Test de création de mission réussi - [ ] IDs d'intégration sauvegardés en base - [ ] Logs sans erreurs critiques --- ## 🔗 Références - **Service N8N**: `lib/services/n8n-service.ts` - **Endpoint création**: `app/api/missions/route.ts` - **Endpoint callback**: `app/api/missions/mission-created/route.ts` - **Documentation N8N**: Voir fichiers `N8N_*.md` dans le projet --- **Document créé le**: $(date) **Dernière mise à jour**: $(date) **Version**: 1.0