mission-tab
This commit is contained in:
parent
eb71deca88
commit
6cf131921b
162
CAPROVER_NGINX_FIX.md
Normal file
162
CAPROVER_NGINX_FIX.md
Normal file
@ -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
|
||||||
|
|
||||||
930
MISSIONS_CENTRALE_WORKFLOW_ANALYSIS.md
Normal file
930
MISSIONS_CENTRALE_WORKFLOW_ANALYSIS.md
Normal file
@ -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]
|
||||||
|
<img src="/api/missions/image/missions/{id}/logo.png" />
|
||||||
|
↓
|
||||||
|
[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
|
||||||
|
|
||||||
155
NGINX_HEADER_SIZE_FIX.md
Normal file
155
NGINX_HEADER_SIZE_FIX.md
Normal file
@ -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
|
||||||
|
|
||||||
@ -85,11 +85,20 @@ export async function GET(request: Request) {
|
|||||||
// Get total count
|
// Get total count
|
||||||
const totalCount = await prisma.mission.count({ where });
|
const totalCount = await prisma.mission.count({ where });
|
||||||
|
|
||||||
// Transform logo paths to public URLs
|
// Transform missions to include public URLs (same format as /api/missions)
|
||||||
const missionsWithPublicUrls = missions.map(mission => ({
|
const missionsWithPublicUrls = missions.map(mission => {
|
||||||
...mission,
|
console.log('Processing mission logo:', {
|
||||||
logo: mission.logo ? `/api/missions/image/${mission.logo}` : null
|
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({
|
return NextResponse.json({
|
||||||
missions: missionsWithPublicUrls,
|
missions: missionsWithPublicUrls,
|
||||||
|
|||||||
136
caprover-nginx-template-fixed.conf
Normal file
136
caprover-nginx-template-fixed.conf
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
32
nginx-config-fix.conf
Normal file
32
nginx-config-fix.conf
Normal file
@ -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 ...
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user