diff --git a/CAPROVER_NGINX_FIX.md b/CAPROVER_NGINX_FIX.md new file mode 100644 index 00000000..1781503b --- /dev/null +++ b/CAPROVER_NGINX_FIX.md @@ -0,0 +1,162 @@ +# Fix Nginx CapRover - Erreur "upstream sent too big header" + +## 🔍 ProblĂšme + +Erreur 502 avec message Nginx : +``` +upstream sent too big header while reading response header from upstream +``` + +**Cause** : Le cookie de session NextAuth (JWT avec tokens Keycloak) dĂ©passe 4KB, la limite par dĂ©faut de Nginx. + +## ✅ Solution : Modifier la configuration CapRover + +### Option 1 : Via CapRover Dashboard (RECOMMANDÉ) + +1. **Aller dans CapRover Dashboard** +2. **SĂ©lectionner votre app** (hub.slm-lab.net) +3. **Aller dans "HTTP Settings"** +4. **Cliquer sur "Edit Nginx Configuration"** (si disponible) +5. **OU aller dans "App Configs" → "nginx"** + +### Option 2 : Modifier le template Nginx directement + +Si vous avez accĂšs au serveur CapRover, modifier le template dans : +- `/captain/templates/nginx.conf` (template principal) +- OU crĂ©er un override dans votre app + +## 📝 Configuration Ă  Ajouter + +**Dans le bloc `location /`**, ajouter ces directives **AVANT** `proxy_pass` : + +```nginx +location / { + # ============================================ + # FIX: Augmenter la limite des headers pour NextAuth + # ============================================ + proxy_buffer_size 16k; + proxy_buffers 8 16k; + proxy_busy_buffers_size 32k; + large_client_header_buffers 4 32k; + + # Timeouts (pour Ă©viter les timeouts) + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + + # Configuration proxy existante + proxy_pass $upstream; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # WebSocket support (si activĂ©) + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_http_version 1.1; +} +``` + +## 🔧 Configuration ComplĂšte ModifiĂ©e + +Voici le bloc `location /` complet avec les corrections : + +```nginx +location / { + # FIX: Headers trop grands pour NextAuth + proxy_buffer_size 16k; + proxy_buffers 8 16k; + proxy_busy_buffers_size 32k; + large_client_header_buffers 4 32k; + + # Timeouts + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + + # Proxy configuration + proxy_pass $upstream; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # WebSocket (si activĂ©) + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_http_version 1.1; +} +``` + +## 🎯 MĂ©thode via CapRover Dashboard + +### Si CapRover permet l'Ă©dition Nginx : + +1. **Dashboard CapRover** → Votre app +2. **"App Configs"** → **"nginx"** +3. **Ajouter dans "Custom Nginx Configuration"** : + +```nginx +location / { + proxy_buffer_size 16k; + proxy_buffers 8 16k; + proxy_busy_buffers_size 32k; + large_client_header_buffers 4 32k; +} +``` + +4. **Sauvegarder** → CapRover regĂ©nĂšre la config + +### Si CapRover ne permet pas l'Ă©dition : + +**Option A** : Modifier le template CapRover (avancĂ©) +- AccĂ©der au serveur CapRover +- Modifier `/captain/templates/nginx.conf` +- RedĂ©marrer CapRover + +**Option B** : CrĂ©er un fichier de configuration personnalisĂ© +- CrĂ©er un fichier dans votre app +- L'inclure dans la config Nginx + +## 🔄 AprĂšs Modification + +1. **VĂ©rifier la config Nginx** : +```bash +sudo nginx -t +``` + +2. **Recharger Nginx** : +```bash +sudo systemctl reload nginx +# OU si CapRover gĂšre Nginx +docker exec captain-nginx nginx -s reload +``` + +3. **Tester la connexion** : +- Se connecter via Keycloak +- VĂ©rifier que l'erreur 502 ne se produit plus + +## 📊 Explication + +**Avant** : +- Limite par dĂ©faut Nginx : 4KB pour les headers +- Cookie NextAuth : ~4-7KB (JWT avec tokens Keycloak) +- RĂ©sultat : ❌ Erreur 502 + +**AprĂšs** : +- Limite augmentĂ©e : 32KB pour les headers +- Cookie NextAuth : ~4-7KB +- RĂ©sultat : ✅ Fonctionne + +## ⚠ Note Importante + +Si vous modifiez le template CapRover directement, **vos modifications seront Ă©crasĂ©es** lors d'une mise Ă  jour de CapRover. + +**Recommandation** : Utiliser la mĂ©thode "Custom Nginx Configuration" dans CapRover si disponible, ou documenter vos modifications pour les rĂ©appliquer aprĂšs mise Ă  jour. + +--- + +**Document créé le** : $(date) +**PrioritĂ©** : HAUTE - RĂ©sout l'erreur 502 + diff --git a/MISSIONS_CENTRALE_WORKFLOW_ANALYSIS.md b/MISSIONS_CENTRALE_WORKFLOW_ANALYSIS.md new file mode 100644 index 00000000..1955b506 --- /dev/null +++ b/MISSIONS_CENTRALE_WORKFLOW_ANALYSIS.md @@ -0,0 +1,930 @@ +# Analyse ComplĂšte : Pages Missions et Centrale - Workflow Complet + +## 📋 Table des MatiĂšres +1. [Vue d'ensemble](#vue-densemble) +2. [Architecture des Pages](#architecture-des-pages) +3. [Workflow de Navigation](#workflow-de-navigation) +4. [Workflow de CrĂ©ation de Mission](#workflow-de-crĂ©ation-de-mission) +5. [Workflow de Consultation](#workflow-de-consultation) +6. [API Routes](#api-routes) +7. [Base de DonnĂ©es](#base-de-donnĂ©es) +8. [IntĂ©grations Externes](#intĂ©grations-externes) +9. [Stockage de Fichiers](#stockage-de-fichiers) +10. [Composants RĂ©utilisables](#composants-rĂ©utilisables) + +--- + +## 🎯 Vue d'ensemble + +### Page "Centrale" +- **Route**: `/missions` +- **Nom dans le menu**: "Centrale" +- **AccĂšs**: RĂŽles `entrepreneurship` ou `admin` (dĂ©fini dans `components/main-nav.tsx`) +- **Description**: Centre d'Administration et de Pilotage (CAP) - Interface principale pour gĂ©rer les missions + +### Page "Missions" +- **Route principale**: `/missions` +- **Sous-routes**: + - `/missions` - Liste des missions de l'utilisateur + - `/missions/new` - CrĂ©ation d'une nouvelle mission + - `/missions/[missionId]` - DĂ©tails d'une mission + - `/missions/[missionId]/edit` - Édition d'une mission + +### Page "Mission Tab" (Tableau des Missions) +- **Route**: `/mission-tab` +- **Description**: Vue publique de toutes les missions disponibles +- **Sous-routes**: + - `/mission-tab` - Liste de toutes les missions + - `/mission-tab/[missionId]` - DĂ©tails d'une mission (vue publique) + +--- + +## đŸ—ïž Architecture des Pages + +### 1. Layout Principal - Missions (`app/missions/layout.tsx`) + +**Structure**: +``` +┌─────────────────────────────────────────┐ +│ Sidebar (CAP) - Fond rose clair │ +│ ┌───────────────────────────────────┐ │ +│ │ CAP │ │ +│ │ Centre d'Administration et de │ │ +│ │ Pilotage │ │ +│ └───────────────────────────────────┘ │ +│ ‱ Mes Missions (/missions) │ +│ ‱ Nouvelle Mission (/missions/new) │ +└─────────────────────────────────────────┘ +│ Contenu Principal (children) │ +└─────────────────────────────────────────┘ +``` + +**FonctionnalitĂ©s**: +- Sidebar fixe avec navigation +- Fond rose clair (`bg-pink-50`) pour la sidebar +- Fond blanc pour le contenu principal +- Navigation active highlightĂ©e + +### 2. Page Liste des Missions (`app/missions/page.tsx`) + +**FonctionnalitĂ©s**: +- Affichage en grille (responsive: 1/2/3 colonnes) +- Recherche par nom, niveau, type, ODD scope +- Filtrage en temps rĂ©el +- Cartes de mission avec: + - Logo (ou initiales si pas de logo) + - Nom de la mission + - Badge niveau (A/B/C/S) avec couleurs + - IcĂŽne ODD (Objectifs de DĂ©veloppement Durable) + - Services associĂ©s + - Description (intention) tronquĂ©e + - Date de crĂ©ation + - Bouton "Voir dĂ©tails" + +**API utilisĂ©e**: `GET /api/missions` +- Retourne uniquement les missions oĂč l'utilisateur est: + - CrĂ©ateur (`creatorId`) + - Ou membre (`missionUsers`) + +### 3. Page CrĂ©ation de Mission (`app/missions/new/page.tsx`) + +**Composant principal**: `MissionsAdminPanel` +- Formulaire multi-onglets (5 onglets) +- Navigation sĂ©quentielle avec boutons PrĂ©cĂ©dent/Suivant + +### 4. Page DĂ©tails Mission (`app/missions/[missionId]/page.tsx`) + +**FonctionnalitĂ©s**: +- Affichage complet des informations +- Logo de la mission +- Grille d'informations (Type, Donneur d'ordre, DurĂ©e, Niveau, Participation, ODD) +- Description complĂšte +- Liste des documents/attachments +- Profils recherchĂ©s +- Services +- Bouton de suppression (si crĂ©ateur ou admin) + +**API utilisĂ©e**: `GET /api/missions/[missionId]` + +### 5. Page Mission Tab (`app/mission-tab/page.tsx`) + +**DiffĂ©rences avec `/missions`**: +- Affiche **TOUTES** les missions (pas de filtre utilisateur) +- API utilisĂ©e: `GET /api/missions/all` +- Vue publique pour dĂ©couvrir toutes les missions disponibles + +--- + +## 🔄 Workflow de Navigation + +### AccĂšs Ă  la Centrale + +``` +1. Utilisateur connectĂ© avec rĂŽle "entrepreneurship" ou "admin" + ↓ +2. Menu dĂ©roulant utilisateur (MainNav) + ↓ +3. Clic sur "Centrale" (href: '/missions') + ↓ +4. Redirection vers /missions + ↓ +5. Layout Missions s'affiche avec sidebar CAP + ↓ +6. Page Liste des Missions (/missions/page.tsx) +``` + +### Navigation dans la Centrale + +``` +┌─────────────────────────────────────────┐ +│ Sidebar CAP │ +│ ├─ Mes Missions (/missions) │ +│ └─ Nouvelle Mission (/missions/new) │ +└─────────────────────────────────────────┘ + │ │ + │ │ + â–Œ â–Œ +┌─────────────────┐ ┌──────────────────┐ +│ Liste Missions │ │ CrĂ©ation Mission │ +│ │ │ │ +│ [Carte Mission] │ │ [Formulaire] │ +│ └─â–ș DĂ©tails │ │ │ +└─────────────────┘ └──────────────────┘ + │ + â–Œ +┌─────────────────┐ +│ DĂ©tails Mission │ +│ │ +│ [Éditer] │ +│ [Supprimer] │ +└─────────────────┘ +``` + +--- + +## 🚀 Workflow de CrĂ©ation de Mission + +### Étape 1: AccĂšs au Formulaire +``` +User → /missions/new → MissionsAdminPanel +``` + +### Étape 2: Formulaire Multi-Onglets + +**Onglet 1: General** +- Nom de la mission (requis) +- Logo (upload) +- ODD scope (requis) - SĂ©lection parmi 17 ODD +- Niveau (requis) - A/B/C/S +- Intention (requis) - Description avec Ă©diteur de texte + +**Onglet 2: Details** +- Type de mission (requis) - Remote/Onsite/Hybrid +- Donneur d'ordre (requis) - Individu/ONG/Start-ups +- Projection (requis) - Short/Medium/Long term +- Services - Checkboxes (Gite, ArtLab, Calcul) +- Participation (requis) - Volontaire/Cooptation +- Profils - Checkboxes (DataIntelligence, Expression, Mediation, Investigation, Coding, Lean) + +**Onglet 3: Attachments** +- Upload de fichiers (PDF, DOC, DOCX, XLS, XLSX, JPG, JPEG, PNG) +- Liste des fichiers sĂ©lectionnĂ©s +- Upload immĂ©diat vers Minio (bucket 'missions') + +**Onglet 4: Skills** +- Liste de compĂ©tences (non fonctionnel actuellement - placeholders) + +**Onglet 5: Membres** +- **Les Gardiens de l'Intention** (3 gardiens requis): + - Gardien du Temps + - Gardien de la Parole + - Gardien de la MĂ©moire +- **Volontaires** (optionnel) +- Recherche d'utilisateurs ou groupes +- Assignation de rĂŽles + +### Étape 3: Validation et Soumission + +**Validation**: +```typescript +const requiredFields = { + name: !!missionData.name, + oddScope: Array.isArray(missionData.oddScope) && missionData.oddScope.length > 0, + niveau: !!missionData.niveau, + intention: !!missionData.intention, + missionType: !!missionData.missionType, + donneurDOrdre: !!missionData.donneurDOrdre, + projection: !!missionData.projection, + participation: !!missionData.participation, + gardiens: gardienDuTemps !== null && + gardienDeLaParole !== null && + gardienDeLaMemoire !== null +} +``` + +**Soumission**: +```typescript +POST /api/missions +Body: { + name, oddScope, niveau, intention, missionType, + donneurDOrdre, projection, services, profils, + participation, guardians, volunteers, logo, attachments +} +``` + +### Étape 4: Traitement Backend + +**SĂ©quence d'exĂ©cution**: + +1. **CrĂ©ation de la mission en base de donnĂ©es** + ```typescript + prisma.mission.create({ + data: { name, oddScope, niveau, intention, ... } + }) + ``` + +2. **CrĂ©ation des MissionUsers (gardiens + volontaires)** + ```typescript + prisma.missionUser.createMany({ + data: [ + { missionId, userId, role: 'gardien-temps' }, + { missionId, userId, role: 'gardien-parole' }, + { missionId, userId, role: 'gardien-memoire' }, + { missionId, userId, role: 'volontaire' }, // pour chaque volontaire + ] + }) + ``` + +3. **Upload du logo vers Minio** + - Path: `missions/{missionId}/logo{extension}` + - Bucket: `missions` + - Mise Ă  jour du champ `logo` dans la mission + +4. **Upload des attachments vers Minio** + - Path: `missions/{missionId}/attachments/{filename}` + - CrĂ©ation des enregistrements `Attachment` en base + +5. **VĂ©rification des fichiers dans Minio** + - VĂ©rifie que tous les fichiers sont bien prĂ©sents avant de continuer + +6. **DĂ©clenchement du workflow N8N** + ```typescript + n8nService.triggerMissionCreation({ + ...missionData, + creatorId, + logoPath, + config: { N8N_API_KEY, MISSION_API_URL } + }) + ``` + +7. **IntĂ©grations externes (via N8N)**: + - CrĂ©ation projet Leantime (si applicable) + - CrĂ©ation collection Outline (si applicable) + - CrĂ©ation canal RocketChat (si applicable) + - CrĂ©ation repository Gitea (si applicable) + - CrĂ©ation projet Penpot (si applicable) + +8. **Retour succĂšs/erreur** + - Si succĂšs: Redirection vers `/missions` + - Si erreur: Nettoyage des fichiers uploadĂ©s + message d'erreur + +--- + +## 👀 Workflow de Consultation + +### Consultation Liste des Missions + +**Route**: `/missions` ou `/mission-tab` + +**Flux**: +``` +1. Chargement de la page + ↓ +2. useEffect → fetch('/api/missions') ou fetch('/api/missions/all') + ↓ +3. Affichage du loader + ↓ +4. RĂ©ception des donnĂ©es + ↓ +5. Transformation des donnĂ©es: + - Ajout des logoUrl (si logo existe) + - Formatage des dates + - Calcul des couleurs de badges + - Extraction des infos ODD + ↓ +6. Filtrage par terme de recherche (si prĂ©sent) + ↓ +7. Affichage en grille +``` + +**Recherche**: +- Filtre en temps rĂ©el sur: nom, niveau, type, ODD scope +- Pas de requĂȘte API supplĂ©mentaire (filtrage cĂŽtĂ© client) + +### Consultation DĂ©tails Mission + +**Route**: `/missions/[missionId]` ou `/mission-tab/[missionId]` + +**Flux**: +``` +1. Chargement de la page + ↓ +2. RĂ©cupĂ©ration du missionId depuis les params + ↓ +3. useEffect → fetch(`/api/missions/${missionId}`) + ↓ +4. Affichage du loader + ↓ +5. RĂ©ception des donnĂ©es complĂštes: + - Mission avec tous les champs + - Creator (id, email) + - MissionUsers (avec user details) + - Attachments (avec publicUrl) + ↓ +6. Transformation: + - Ajout des logoUrl + - Formatage des dates + - Labels pour les types/niveaux + - URLs publiques pour les attachments + ↓ +7. Affichage des sections: + - Header avec nom et logo + - Grille d'informations + - Description + - Documents + - Profils recherchĂ©s + - Services + - Actions (Éditer/Supprimer) +``` + +--- + +## 🔌 API Routes + +### 1. `GET /api/missions` +**Fichier**: `app/api/missions/route.ts` + +**FonctionnalitĂ©**: Liste les missions de l'utilisateur connectĂ© + +**Filtres**: +- `limit` (default: 10) +- `offset` (default: 0) +- `search` (recherche dans name et intention) +- `name` (filtre exact) + +**Where Clause**: +```typescript +{ + OR: [ + { creatorId: userId }, + { missionUsers: { some: { userId } } } + ] +} +``` + +**Retour**: +```json +{ + "missions": [ + { + "id": "...", + "name": "...", + "logo": "missions/{id}/logo.png", + "logoUrl": "/api/missions/image/missions/{id}/logo.png", + "oddScope": ["odd-3"], + "niveau": "a", + "missionType": "remote", + "projection": "short", + "services": ["Gite"], + "intention": "...", + "createdAt": "...", + "creator": { "id": "...", "email": "..." }, + "missionUsers": [...], + "attachments": [...] + } + ], + "pagination": { + "total": 10, + "offset": 0, + "limit": 10 + } +} +``` + +### 2. `POST /api/missions` +**Fichier**: `app/api/missions/route.ts` + +**FonctionnalitĂ©**: CrĂ©e une nouvelle mission + +**Body**: +```typescript +{ + name: string; + oddScope: string[]; + niveau?: string; + intention?: string; + missionType?: string; + donneurDOrdre?: string; + projection?: string; + services?: string[]; + profils?: string[]; + participation?: string; + guardians?: { + "gardien-temps": string; + "gardien-parole": string; + "gardien-memoire": string; + }; + volunteers?: string[]; + logo?: { + data: string; // base64 + name?: string; + type?: string; + }; + attachments?: Array<{ + data: string; // base64 + name?: string; + type?: string; + }>; +} +``` + +**Retour**: +```json +{ + "success": true, + "mission": { ... }, + "message": "Mission created successfully with all integrations" +} +``` + +### 3. `GET /api/missions/[missionId]` +**Fichier**: `app/api/missions/[missionId]/route.ts` + +**FonctionnalitĂ©**: RĂ©cupĂšre les dĂ©tails d'une mission + +**ContrĂŽle d'accĂšs**: +- Utilisateur doit ĂȘtre crĂ©ateur OU membre de la mission + +**Retour**: Mission complĂšte avec relations + +### 4. `PUT /api/missions/[missionId]` +**Fichier**: `app/api/missions/[missionId]/route.ts` + +**FonctionnalitĂ©**: Met Ă  jour une mission + +**ContrĂŽle d'accĂšs**: +- CrĂ©ateur OU gardien-temps/gardien-parole + +**Body**: MĂȘme structure que POST (tous les champs optionnels) + +### 5. `DELETE /api/missions/[missionId]` +**Fichier**: `app/api/missions/[missionId]/route.ts` + +**FonctionnalitĂ©**: Supprime une mission + +**ContrĂŽle d'accĂšs**: +- CrĂ©ateur OU admin uniquement + +**Actions**: +- Suppression du logo dans Minio +- Suppression de la mission en base (cascade sur MissionUsers et Attachments) +- TODO: Rollback N8N (non implĂ©mentĂ©) + +### 6. `GET /api/missions/all` +**Fichier**: `app/api/missions/all/route.ts` + +**FonctionnalitĂ©**: Liste TOUTES les missions (pas de filtre utilisateur) + +**DiffĂ©rences avec `/api/missions`**: +- Pas de filtre par utilisateur +- Retourne toutes les missions publiques +- UtilisĂ© par `/mission-tab` + +### 7. `GET /api/missions/image/[...path]` +**Fichier**: `app/api/missions/image/[...path]/route.ts` + +**FonctionnalitĂ©**: Sert les images (logos et attachments) depuis Minio + +**Path**: `missions/{missionId}/logo.png` ou `missions/{missionId}/attachments/{filename}` + +### 8. `POST /api/missions/upload` +**Fichier**: `app/api/missions/upload/route.ts` + +**FonctionnalitĂ©**: Upload de fichiers (logo ou attachments) + +### 9. `GET /api/missions/[missionId]/attachments` +**Fichier**: `app/api/missions/[missionId]/attachments/route.ts` + +**FonctionnalitĂ©**: Liste les attachments d'une mission + +### 10. `POST /api/missions/[missionId]/attachments` +**Fichier**: `app/api/missions/[missionId]/attachments/route.ts` + +**FonctionnalitĂ©**: Ajoute un attachment Ă  une mission existante + +### 11. `DELETE /api/missions/[missionId]/attachments/[attachmentId]` +**Fichier**: `app/api/missions/[missionId]/attachments/[attachmentId]/route.ts` + +**FonctionnalitĂ©**: Supprime un attachment + +--- + +## đŸ—„ïž Base de DonnĂ©es + +### ModĂšle Mission (`prisma/schema.prisma`) + +```prisma +model Mission { + id String @id @default(uuid()) + name String + logo String? // Path dans Minio + oddScope String[] // CatĂ©gories ODD + niveau String // A/B/C/S + intention String // Description + missionType String // remote/onsite/hybrid + donneurDOrdre String // individual/group/organization + projection String // short/medium/long + services String[] // ["Gite", "ArtLab", "Calcul"] + participation String? // volontaire/cooptation + profils String[] // ["DataIntelligence", ...] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + creator User @relation(fields: [creatorId], references: [id]) + creatorId String + attachments Attachment[] + missionUsers MissionUser[] + + // IntĂ©grations externes + leantimeProjectId String? + outlineCollectionId String? + rocketChatChannelId String? + giteaRepositoryUrl String? + penpotProjectId String? + + @@index([creatorId]) +} +``` + +### ModĂšle MissionUser + +```prisma +model MissionUser { + id String @id @default(uuid()) + role String // 'gardien-temps', 'gardien-parole', 'gardien-memoire', 'volontaire' + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + mission Mission @relation(fields: [missionId], references: [id]) + missionId String + user User @relation(fields: [userId], references: [id]) + userId String + + @@unique([missionId, userId, role]) + @@index([missionId]) + @@index([userId]) +} +``` + +### ModĂšle Attachment + +```prisma +model Attachment { + id String @id @default(uuid()) + filename String + filePath String // Path dans Minio: missions/{missionId}/attachments/{filename} + fileType String // MIME type + fileSize Int + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + mission Mission @relation(fields: [missionId], references: [id]) + missionId String + uploader User @relation(fields: [uploaderId], references: [id]) + uploaderId String + + @@index([missionId]) + @@index([uploaderId]) +} +``` + +--- + +## 🔗 IntĂ©grations Externes + +### Service N8N (`lib/services/n8n-service.ts`) + +**Webhook URL**: `https://brain.slm-lab.net/webhook/mission-created` + +**DonnĂ©es envoyĂ©es**: +```typescript +{ + name, oddScope, niveau, intention, missionType, + donneurDOrdre, projection, services, participation, + profils, guardians, volunteers, creatorId, + config: { + N8N_API_KEY, + MISSION_API_URL + } +} +``` + +**Workflow N8N dĂ©clenche**: +1. CrĂ©ation projet Leantime (si applicable) +2. CrĂ©ation collection Outline (si applicable) +3. CrĂ©ation canal RocketChat (si applicable) +4. CrĂ©ation repository Gitea (si applicable) +5. CrĂ©ation projet Penpot (si applicable) + +**Retour**: +```typescript +{ + success: boolean; + results?: { + leantimeProjectId?: string; + outlineCollectionId?: string; + rocketChatChannelId?: string; + giteaRepositoryUrl?: string; + penpotProjectId?: string; + failedServices?: { + gitRepo?: boolean; + leantimeProject?: boolean; + docCollection?: boolean; + rocketChatChannel?: boolean; + } + }; + error?: string; +} +``` + +**Rollback** (non implĂ©mentĂ©): +- Webhook: `https://brain.slm-lab.net/webhook/mission-rollback` +- AppelĂ© lors de la suppression d'une mission + +--- + +## 📩 Stockage de Fichiers + +### Minio Configuration + +**Endpoint**: `https://dome-api.slm-lab.net` +**Bucket**: `missions` +**Credentials**: HardcodĂ©s dans `lib/mission-uploads.ts` (⚠ Ă  sĂ©curiser) + +### Structure des Chemins + +**Logo**: +``` +missions/{missionId}/logo{extension} +Exemple: missions/abc-123/logo.png +``` + +**Attachments**: +``` +missions/{missionId}/attachments/{filename} +Exemple: missions/abc-123/attachments/document.pdf +``` + +### URLs Publiques + +**Format**: `/api/missions/image/{path}` + +**Exemples**: +- Logo: `/api/missions/image/missions/{missionId}/logo.png` +- Attachment: `/api/missions/image/missions/{missionId}/attachments/document.pdf` + +### Fonctions Utilitaires (`lib/mission-uploads.ts`) + +- `getMissionLogoPath()` - GĂ©nĂšre le chemin du logo +- `getMissionAttachmentPath()` - GĂ©nĂšre le chemin d'un attachment +- `uploadMissionLogo()` - Upload un logo vers Minio +- `uploadMissionAttachment()` - Upload un attachment vers Minio +- `deleteMissionLogo()` - Supprime un logo (TODO) +- `deleteMissionAttachment()` - Supprime un attachment +- `getMissionFileUrl()` - Construit l'URL publique +- `ensureMissionsPrefix()` - Normalise le chemin + +--- + +## đŸ§© Composants RĂ©utilisables + +### 1. `MissionsAdminPanel` (`components/missions/missions-admin-panel.tsx`) + +**FonctionnalitĂ©s**: +- Formulaire multi-onglets +- Gestion des gardiens et volontaires +- Upload de fichiers +- Validation complĂšte +- Soumission vers API + +**Props**: Aucune (composant autonome) + +**State**: +- `missionData` - DonnĂ©es de la mission +- `selectedServices` - Services sĂ©lectionnĂ©s +- `selectedProfils` - Profils sĂ©lectionnĂ©s +- `gardienDuTemps`, `gardienDeLaParole`, `gardienDeLaMemoire` - IDs des gardiens +- `volontaires` - Array d'IDs de volontaires +- `activeTab` - Onglet actif +- `isSubmitting` - État de soumission + +### 2. `FileUpload` (`components/missions/file-upload.tsx`) + +**FonctionnalitĂ©s**: +- Upload de logo ou attachment +- Conversion en base64 +- Preview pour les images + +**Props**: +- `type`: 'logo' | 'attachment' +- `isNewMission`: boolean +- `onFileSelect`: (fileData) => void + +### 3. `AttachmentsList` (`components/missions/attachments-list.tsx`) + +**FonctionnalitĂ©s**: +- Liste des attachments d'une mission +- Upload de nouveaux attachments +- Suppression d'attachments + +**Props**: +- `missionId`: string +- `allowUpload`: boolean +- `allowDelete`: boolean + +### 4. `MissionsFrame` (`components/missions/missions-frame.tsx`) + +**FonctionnalitĂ©s**: Wrapper iframe (non utilisĂ© actuellement) + +--- + +## 🔐 ContrĂŽles d'AccĂšs + +### Page Centrale (`/missions`) +- **RĂŽles requis**: `entrepreneurship` ou `admin` +- VĂ©rifiĂ© dans `components/main-nav.tsx` via `hasRole()` + +### API Routes +- **Authentification**: Session NextAuth requise +- **GET /api/missions**: Missions oĂč user est crĂ©ateur ou membre +- **GET /api/missions/all**: Toutes les missions (authentifiĂ©) +- **GET /api/missions/[id]**: CrĂ©ateur ou membre +- **PUT /api/missions/[id]**: CrĂ©ateur ou gardien-temps/gardien-parole +- **DELETE /api/missions/[id]**: CrĂ©ateur ou admin uniquement + +--- + +## 📊 Flux de DonnĂ©es Complet + +### CrĂ©ation de Mission + +``` +[Frontend] + MissionsAdminPanel + ↓ (soumission) + POST /api/missions + ↓ +[Backend] + 1. Validation + 2. prisma.mission.create() + 3. prisma.missionUser.createMany() + 4. uploadMissionLogo() → Minio + 5. uploadMissionAttachment() → Minio (pour chaque attachment) + 6. prisma.attachment.create() (pour chaque attachment) + 7. verifyFileExists() (vĂ©rification Minio) + 8. n8nService.triggerMissionCreation() + ↓ +[N8N Workflow] + - CrĂ©ation Leantime + - CrĂ©ation Outline + - CrĂ©ation RocketChat + - CrĂ©ation Gitea + - CrĂ©ation Penpot + ↓ +[Backend] + 9. Retour succĂšs/erreur + ↓ +[Frontend] + Redirection → /missions +``` + +### Consultation de Mission + +``` +[Frontend] + MissionsPage ou MissionDetailPage + ↓ + fetch('/api/missions') ou fetch('/api/missions/[id]') + ↓ +[Backend] + 1. VĂ©rification session + 2. Query Prisma avec relations + 3. Transformation des paths en URLs publiques + 4. Retour JSON + ↓ +[Frontend] + Affichage des donnĂ©es +``` + +### Affichage d'Image + +``` +[Frontend] + + ↓ +[Backend] + GET /api/missions/image/[...path] + ↓ + Lecture depuis Minio + ↓ + Stream vers client +``` + +--- + +## 🎹 Styles et UI + +### Couleurs des Badges Niveau +- **A (Apprentissage)**: `bg-green-100 text-green-800` +- **B (Basique)**: `bg-blue-100 text-blue-800` +- **C (Complexe)**: `bg-purple-100 text-purple-800` +- **S (SpĂ©cial)**: `bg-amber-100 text-amber-800` + +### Layout Sidebar CAP +- **Fond**: `bg-pink-50` +- **Bordure**: `border-pink-100` +- **Largeur**: `234px` fixe + +### Grille de Missions +- **Mobile**: 1 colonne +- **Tablet**: 2 colonnes (`md:grid-cols-2`) +- **Desktop**: 3 colonnes (`lg:grid-cols-3`) + +--- + +## 🐛 Points d'Attention + +1. **Credentials Minio hardcodĂ©s** dans `lib/mission-uploads.ts` - À dĂ©placer vers variables d'environnement +2. **Rollback N8N non implĂ©mentĂ©** lors de la suppression +3. **Skills tab non fonctionnel** - Placeholders uniquement +4. **Presigned URLs non implĂ©mentĂ©es** - Upload direct uniquement +5. **Gestion d'erreurs N8N** - Partielle (continue mĂȘme si certaines intĂ©grations Ă©chouent) + +--- + +## 📝 Notes Techniques + +### Types TypeScript + +**Mission Interface** (utilisĂ©e dans les pages): +```typescript +interface Mission { + id: string; + name: string; + logo?: string; + logoUrl?: string; + oddScope: string[]; + niveau: string; + missionType: string; + projection: string; + participation?: string; + services?: string[]; + profils?: string[]; + intention?: string; + donneurDOrdre?: string; + createdAt: string; + creator: User; + missionUsers: MissionUser[]; + attachments?: Attachment[]; +} +``` + +### Validation + +**CĂŽtĂ© Frontend**: Validation dans `MissionsAdminPanel.validateMission()` +**CĂŽtĂ© Backend**: Validation minimale (name et oddScope requis) + +### Gestion d'Erreurs + +- **Frontend**: Toast notifications via `useToast()` +- **Backend**: Retour JSON avec `error` et `details` +- **N8N**: Retour avec `success` et `failedServices` pour erreurs partielles + +--- + +## 🔄 Évolutions Possibles + +1. **Pagination** cĂŽtĂ© client pour les listes +2. **Filtres avancĂ©s** (par niveau, type, ODD, etc.) +3. **Recherche full-text** dans l'intention +4. **Export** des missions (PDF, CSV) +5. **Notifications** lors de l'assignation Ă  une mission +6. **Statistiques** des missions +7. **Timeline** des activitĂ©s d'une mission +8. **Commentaires** sur les missions +9. **États** des missions (brouillon, publiĂ©e, terminĂ©e, etc.) +10. **Permissions granulaires** par rĂŽle de gardien + +--- + +**Document gĂ©nĂ©rĂ© le**: $(date) +**Version**: 1.0 +**Auteur**: Analyse automatique du codebase + diff --git a/NGINX_HEADER_SIZE_FIX.md b/NGINX_HEADER_SIZE_FIX.md new file mode 100644 index 00000000..a87abc53 --- /dev/null +++ b/NGINX_HEADER_SIZE_FIX.md @@ -0,0 +1,155 @@ +# Fix Erreur 502 - Headers trop grands (Nginx) + +## 🔍 ProblĂšme IdentifiĂ© + +**Erreur Nginx** : +``` +upstream sent too big header while reading response header from upstream +``` + +**Cause** : Le cookie de session NextAuth est trop grand (> 4KB par dĂ©faut dans Nginx). Le JWT contient : +- `accessToken` (Keycloak) - ~1-2KB +- `refreshToken` (Keycloak) - ~1-2KB +- `idToken` (Keycloak) - ~1-2KB +- DonnĂ©es utilisateur (roles, etc.) - ~500B-1KB +- **Total** : ~4-7KB, ce qui dĂ©passe la limite Nginx par dĂ©faut + +## ✅ Solutions + +### Solution 1 : Augmenter la limite Nginx (RECOMMANDÉ) + +**Fichier** : Configuration Nginx (gĂ©nĂ©ralement `/etc/nginx/sites-available/hub.slm-lab.net` ou similaire) + +**Ajouter dans le bloc `server` ou `location`** : +```nginx +server { + # ... autres configs ... + + # Augmenter la taille maximale des headers + proxy_buffer_size 16k; + proxy_buffers 8 16k; + proxy_busy_buffers_size 32k; + large_client_header_buffers 4 32k; + + # SpĂ©cifiquement pour les headers de rĂ©ponse + proxy_headers_hash_max_size 512; + proxy_headers_hash_bucket_size 128; + + # ... reste de la config ... +} +``` + +**OU** pour une solution plus simple, ajouter seulement : +```nginx +server { + # ... autres configs ... + + # Augmenter la limite des headers + large_client_header_buffers 4 32k; + + # ... reste de la config ... +} +``` + +**Puis redĂ©marrer Nginx** : +```bash +sudo nginx -t # VĂ©rifier la config +sudo systemctl reload nginx # Ou sudo service nginx reload +``` + +### Solution 2 : RĂ©duire la taille du JWT (ALTERNATIVE) + +Si on ne peut pas modifier Nginx, on peut rĂ©duire la taille du JWT en ne stockant pas tous les tokens. + +**Modification** : `app/api/auth/options.ts` + +**Option A** : Ne pas stocker `idToken` dans le JWT (si pas nĂ©cessaire) +```typescript +// Dans JWT callback +token.idToken = account.id_token ?? ''; // ❌ Supprimer cette ligne +``` + +**Option B** : Stocker seulement les tokens nĂ©cessaires +```typescript +// Stocker seulement accessToken et refreshToken +// idToken peut ĂȘtre rĂ©cupĂ©rĂ© depuis Keycloak si nĂ©cessaire +``` + +**Note** : Cette solution rĂ©duit la fonctionnalitĂ©. La Solution 1 est prĂ©fĂ©rable. + +## 🔧 Configuration Nginx ComplĂšte RecommandĂ©e + +```nginx +server { + listen 443 ssl http2; + server_name hub.slm-lab.net; + + # ... SSL config ... + + # Augmenter les limites pour les gros headers NextAuth + proxy_buffer_size 16k; + proxy_buffers 8 16k; + proxy_busy_buffers_size 32k; + large_client_header_buffers 4 32k; + + # Timeouts + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + + location / { + proxy_pass http://172.16.0.102:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + + # Headers pour NextAuth + proxy_set_header Cookie $http_cookie; + } +} +``` + +## 📊 VĂ©rification + +**AprĂšs modification Nginx** : + +1. **Tester la config** : +```bash +sudo nginx -t +``` + +2. **Recharger Nginx** : +```bash +sudo systemctl reload nginx +``` + +3. **Tester la connexion** : +- Se connecter via Keycloak +- VĂ©rifier que l'erreur 502 ne se produit plus +- VĂ©rifier les logs Nginx pour confirmer + +## 🎯 Cause Technique + +NextAuth crĂ©e un cookie JWT qui contient : +- Le JWT encryptĂ© avec `NEXTAUTH_SECRET` +- Le JWT contient tous les tokens Keycloak +- La taille totale peut dĂ©passer 4KB + +Nginx a une limite par dĂ©faut de 4KB pour les headers de rĂ©ponse. Quand Next.js essaie de renvoyer un cookie > 4KB, Nginx rejette avec "upstream sent too big header". + +## ✅ Solution ImmĂ©diate + +**Action** : Modifier la configuration Nginx pour augmenter `large_client_header_buffers` Ă  au moins `4 32k` ou `8 16k`. + +**Impact** : RĂ©sout immĂ©diatement l'erreur 502. + +--- + +**Document créé le** : $(date) +**PrioritĂ©** : HAUTE - C'est la cause de l'erreur 502 + diff --git a/app/api/missions/all/route.ts b/app/api/missions/all/route.ts index 9f65c6a7..adde3e86 100644 --- a/app/api/missions/all/route.ts +++ b/app/api/missions/all/route.ts @@ -85,11 +85,20 @@ export async function GET(request: Request) { // Get total count const totalCount = await prisma.mission.count({ where }); - // Transform logo paths to public URLs - const missionsWithPublicUrls = missions.map(mission => ({ - ...mission, - logo: mission.logo ? `/api/missions/image/${mission.logo}` : null - })); + // Transform missions to include public URLs (same format as /api/missions) + const missionsWithPublicUrls = missions.map(mission => { + console.log('Processing mission logo:', { + missionId: mission.id, + logo: mission.logo, + constructedUrl: mission.logo ? `/api/missions/image/${mission.logo}` : null + }); + + return { + ...mission, + logoUrl: mission.logo ? `/api/missions/image/${mission.logo}` : null, + logo: mission.logo, // Keep original logo path + }; + }); return NextResponse.json({ missions: missionsWithPublicUrls, diff --git a/caprover-nginx-template-fixed.conf b/caprover-nginx-template-fixed.conf new file mode 100644 index 00000000..6d17e923 --- /dev/null +++ b/caprover-nginx-template-fixed.conf @@ -0,0 +1,136 @@ +<% +if (s.forceSsl) { +%> + server { + + listen 80; + + server_name <%-s.publicDomain%>; + + # Used by Lets Encrypt + location /.well-known/acme-challenge/ { + root <%-s.staticWebRoot%>; + } + + # Used by CapRover for health check + location /.well-known/captain-identifier { + root <%-s.staticWebRoot%>; + } + + location / { + return 302 https://$http_host$request_uri; + } + } +<% +} +%> + + +server { + + <% + if (!s.forceSsl) { + %> + listen 80; + <% + } + if (s.hasSsl) { + %> + listen 443 ssl; + http2 on; + ssl_certificate <%-s.crtPath%>; + ssl_certificate_key <%-s.keyPath%>; + <% + } + if (s.logAccessPath) { + %> + access_log <%-s.logAccessPath%>; + <% + } + %> + + client_max_body_size 500m; + + server_name <%-s.publicDomain%>; + + # 127.0.0.11 is DNS set up by Docker, see: + # https://docs.docker.com/engine/userguide/networking/configure-dns/ + # https://github.com/moby/moby/issues/20026 + resolver 127.0.0.11 valid=10s; + # IMPORTANT!! If you are here from an old thread to set a custom port, you do not need to modify this port manually here!! + # Simply change the Container HTTP Port from the dashboard HTTP panel + set $upstream http://172.16.0.102:3000; + + location / { + + <% + if (s.redirectToPath) { + %> + return 302 <%-s.redirectToPath%>$request_uri; + <% + } else { + %> + + <% + if (s.httpBasicAuthPath) { + %> + auth_basic "Restricted Access"; + auth_basic_user_file <%-s.httpBasicAuthPath%>; + <% + } + %> + + # ============================================ + # FIX: Augmenter la limite des headers pour NextAuth + # RĂ©sout l'erreur "upstream sent too big header" + # ============================================ + proxy_buffer_size 16k; + proxy_buffers 8 16k; + proxy_busy_buffers_size 32k; + large_client_header_buffers 4 32k; + + # Timeouts pour Ă©viter les timeouts + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + + proxy_pass $upstream; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + <% + if (s.websocketSupport) { + %> + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_http_version 1.1; + <% + } + %> + + + <% + } + %> + + } + + # Used by Lets Encrypt + location /.well-known/acme-challenge/ { + root <%-s.staticWebRoot%>; + } + + # Used by CapRover for health check + location /.well-known/captain-identifier { + root <%-s.staticWebRoot%>; + } + + error_page 502 /captain_502_custom_error_page.html; + location = /captain_502_custom_error_page.html { + root <%-s.customErrorPagesDirectory%>; + internal; + } +} + diff --git a/nginx-config-fix.conf b/nginx-config-fix.conf new file mode 100644 index 00000000..0eb84564 --- /dev/null +++ b/nginx-config-fix.conf @@ -0,0 +1,32 @@ +# Configuration Nginx pour corriger l'erreur "upstream sent too big header" +# À ajouter dans votre configuration Nginx pour hub.slm-lab.net + +server { + # ... votre config existante ... + + # ============================================ + # FIX: Augmenter la limite des headers + # ============================================ + # Ces directives augmentent la taille maximale des headers + # pour permettre les gros cookies NextAuth (JWT avec tokens Keycloak) + + # Taille des buffers pour les headers + proxy_buffer_size 16k; + proxy_buffers 8 16k; + proxy_busy_buffers_size 32k; + + # Limite pour les gros headers clients (et rĂ©ponses) + large_client_header_buffers 4 32k; + + # Hash tables pour les headers (optionnel mais recommandĂ©) + proxy_headers_hash_max_size 512; + proxy_headers_hash_bucket_size 128; + + # Timeouts (pour Ă©viter les timeouts pendant le traitement) + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + + # ... reste de votre config ... +} +