mission-tab deletemission
This commit is contained in:
parent
4543e4fb8a
commit
9824e54843
308
MISSION_INTEGRATION_IDS_FIX.md
Normal file
308
MISSION_INTEGRATION_IDS_FIX.md
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
# Fix : Sauvegarde des IDs d'Intégration N8N
|
||||||
|
|
||||||
|
## 🔍 Problème Identifié
|
||||||
|
|
||||||
|
Lors de la suppression d'une mission, le webhook N8N reçoit des IDs vides :
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"repoName": "",
|
||||||
|
"leantimeProjectId": 0,
|
||||||
|
"documentationCollectionId": "",
|
||||||
|
"rocketchatChannelId": ""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cause** : Les IDs retournés par N8N lors de la création des intégrations n'étaient **jamais sauvegardés en base**.
|
||||||
|
|
||||||
|
### Workflow Problématique
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Frontend → POST /api/missions
|
||||||
|
↓
|
||||||
|
2. Backend crée mission en Prisma
|
||||||
|
↓
|
||||||
|
3. Backend upload fichiers Minio
|
||||||
|
↓
|
||||||
|
4. Backend → POST N8N webhook (mission-created)
|
||||||
|
↓
|
||||||
|
5. N8N crée intégrations (Gitea, Leantime, Outline, RocketChat)
|
||||||
|
↓
|
||||||
|
6. N8N → POST /mission-created ❌ ENDPOINT N'EXISTAIT PAS
|
||||||
|
↓
|
||||||
|
7. IDs jamais sauvegardés ❌
|
||||||
|
```
|
||||||
|
|
||||||
|
### Conséquence
|
||||||
|
|
||||||
|
Lors de la suppression :
|
||||||
|
- Les IDs sont `null` en base
|
||||||
|
- On envoie des valeurs vides à N8N
|
||||||
|
- N8N ne peut pas supprimer/fermer les intégrations
|
||||||
|
- Les ressources externes restent orphelines
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Solution Implémentée
|
||||||
|
|
||||||
|
### 1. Endpoint `/mission-created` Créé
|
||||||
|
|
||||||
|
**Fichier** : `app/api/missions/mission-created/route.ts`
|
||||||
|
|
||||||
|
**Fonctionnalités** :
|
||||||
|
- ✅ Reçoit les IDs des intégrations créées par N8N
|
||||||
|
- ✅ Vérifie l'API key (`x-api-key` header)
|
||||||
|
- ✅ Trouve la mission par `name` + `creatorId`
|
||||||
|
- ✅ Met à jour la mission avec les IDs
|
||||||
|
- ✅ Mappe correctement les champs :
|
||||||
|
- `gitRepoUrl` → `giteaRepositoryUrl`
|
||||||
|
- `documentationCollectionId` → `outlineCollectionId`
|
||||||
|
- `rocketchatChannelId` → `rocketChatChannelId`
|
||||||
|
- `leantimeProjectId` → `leantimeProjectId` (converti en string)
|
||||||
|
|
||||||
|
### 2. Format des Données
|
||||||
|
|
||||||
|
**N8N envoie** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Mission Example",
|
||||||
|
"creatorId": "user-id",
|
||||||
|
"gitRepoUrl": "https://gite.slm-lab.net/alma/mission-example",
|
||||||
|
"leantimeProjectId": "123",
|
||||||
|
"documentationCollectionId": "collection-456",
|
||||||
|
"rocketchatChannelId": "channel-789"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Endpoint sauvegarde** :
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
giteaRepositoryUrl: "https://gite.slm-lab.net/alma/mission-example",
|
||||||
|
leantimeProjectId: "123",
|
||||||
|
outlineCollectionId: "collection-456",
|
||||||
|
rocketChatChannelId: "channel-789"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Sécurité
|
||||||
|
|
||||||
|
- ✅ Vérification de l'API key (`x-api-key` header)
|
||||||
|
- ✅ Validation des champs requis (`name`, `creatorId`)
|
||||||
|
- ✅ Gestion d'erreurs complète
|
||||||
|
- ✅ Logging détaillé pour debugging
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Nouveau Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Frontend → POST /api/missions
|
||||||
|
↓
|
||||||
|
2. Backend crée mission en Prisma
|
||||||
|
↓
|
||||||
|
3. Backend upload fichiers Minio
|
||||||
|
↓
|
||||||
|
4. Backend → POST N8N webhook (mission-created)
|
||||||
|
↓
|
||||||
|
5. N8N crée intégrations (Gitea, Leantime, Outline, RocketChat)
|
||||||
|
↓
|
||||||
|
6. N8N → POST /mission-created ✅ ENDPOINT EXISTE MAINTENANT
|
||||||
|
↓
|
||||||
|
7. Backend sauvegarde les IDs ✅
|
||||||
|
↓
|
||||||
|
8. Mission complète avec tous les IDs ✅
|
||||||
|
```
|
||||||
|
|
||||||
|
**Lors de la suppression** :
|
||||||
|
```
|
||||||
|
1. Frontend → DELETE /api/missions/[id]
|
||||||
|
↓
|
||||||
|
2. Backend récupère mission (avec IDs sauvegardés)
|
||||||
|
↓
|
||||||
|
3. Backend extrait/mappe les données
|
||||||
|
↓
|
||||||
|
4. Backend → POST N8N webhook (mission-delete)
|
||||||
|
↓
|
||||||
|
5. N8N reçoit les IDs ✅
|
||||||
|
↓
|
||||||
|
6. N8N supprime/ferme les intégrations ✅
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Format de Requête
|
||||||
|
|
||||||
|
### POST /api/missions/mission-created
|
||||||
|
|
||||||
|
**Headers** :
|
||||||
|
```
|
||||||
|
Content-Type: application/json
|
||||||
|
x-api-key: {N8N_API_KEY}
|
||||||
|
Authorization: Bearer {keycloak_token} (optionnel)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Body** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Mission Example",
|
||||||
|
"creatorId": "user-uuid",
|
||||||
|
"gitRepoUrl": "https://gite.slm-lab.net/alma/mission-example",
|
||||||
|
"leantimeProjectId": "123",
|
||||||
|
"documentationCollectionId": "collection-456",
|
||||||
|
"rocketchatChannelId": "channel-789",
|
||||||
|
"niveau": "default",
|
||||||
|
"intention": "...",
|
||||||
|
"description": "...",
|
||||||
|
"donneurDOrdre": "...",
|
||||||
|
"projection": "...",
|
||||||
|
"missionType": "remote"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Réponse Succès** (200) :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "Mission updated successfully",
|
||||||
|
"mission": {
|
||||||
|
"id": "mission-uuid",
|
||||||
|
"name": "Mission Example",
|
||||||
|
"giteaRepositoryUrl": "https://gite.slm-lab.net/alma/mission-example",
|
||||||
|
"leantimeProjectId": "123",
|
||||||
|
"outlineCollectionId": "collection-456",
|
||||||
|
"rocketChatChannelId": "channel-789"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Réponse Erreur** (400/404/500) :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "Error message",
|
||||||
|
"details": "Detailed error information"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ Missions Existantes
|
||||||
|
|
||||||
|
**Problème** : Les missions créées avant cette correction n'ont pas leurs IDs sauvegardés.
|
||||||
|
|
||||||
|
**Solutions possibles** :
|
||||||
|
|
||||||
|
### Option 1 : Migration Manuelle
|
||||||
|
Pour chaque mission existante, récupérer les IDs depuis les services externes et les mettre à jour manuellement.
|
||||||
|
|
||||||
|
### Option 2 : Script de Migration
|
||||||
|
Créer un script qui :
|
||||||
|
1. Liste toutes les missions sans IDs
|
||||||
|
2. Interroge les services externes (si possible)
|
||||||
|
3. Met à jour les missions
|
||||||
|
|
||||||
|
### Option 3 : Re-création
|
||||||
|
Supprimer et recréer les missions (si acceptable).
|
||||||
|
|
||||||
|
**Recommandation** : Option 1 pour les missions critiques, Option 2 pour un grand nombre.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 Tests
|
||||||
|
|
||||||
|
### Test 1 : Création de Mission
|
||||||
|
|
||||||
|
1. Créer une nouvelle mission via le frontend
|
||||||
|
2. Vérifier que N8N appelle `/mission-created`
|
||||||
|
3. Vérifier que la mission en base a les IDs sauvegardés :
|
||||||
|
```sql
|
||||||
|
SELECT id, name, giteaRepositoryUrl, leantimeProjectId,
|
||||||
|
outlineCollectionId, rocketChatChannelId
|
||||||
|
FROM Mission
|
||||||
|
WHERE name = 'Mission Test';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test 2 : Suppression de Mission
|
||||||
|
|
||||||
|
1. Supprimer une mission avec IDs sauvegardés
|
||||||
|
2. Vérifier que N8N reçoit les IDs :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"repoName": "mission-example",
|
||||||
|
"leantimeProjectId": 123,
|
||||||
|
"documentationCollectionId": "collection-456",
|
||||||
|
"rocketchatChannelId": "channel-789"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. Vérifier que N8N supprime/ferme les intégrations
|
||||||
|
|
||||||
|
### Test 3 : API Key
|
||||||
|
|
||||||
|
1. Appeler `/mission-created` sans `x-api-key` → 401
|
||||||
|
2. Appeler avec mauvais `x-api-key` → 401
|
||||||
|
3. Appeler avec bon `x-api-key` → 200
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Logs à Surveiller
|
||||||
|
|
||||||
|
### Création
|
||||||
|
|
||||||
|
```
|
||||||
|
=== Mission Created Webhook Received ===
|
||||||
|
Received mission-created data: { ... }
|
||||||
|
Found mission: { id: "...", name: "..." }
|
||||||
|
Updating giteaRepositoryUrl: ...
|
||||||
|
Updating leantimeProjectId: ...
|
||||||
|
Mission updated successfully: { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Suppression
|
||||||
|
|
||||||
|
```
|
||||||
|
=== Starting N8N Deletion Workflow ===
|
||||||
|
Extracted repo name from URL: { url: "...", repoName: "..." }
|
||||||
|
Sending deletion data to N8N: { ... }
|
||||||
|
N8N Deletion Workflow Result: { success: true, ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Configuration Requise
|
||||||
|
|
||||||
|
### Variables d'Environnement
|
||||||
|
|
||||||
|
```env
|
||||||
|
N8N_API_KEY=your-api-key-here
|
||||||
|
NEXT_PUBLIC_API_URL=https://hub.slm-lab.net
|
||||||
|
```
|
||||||
|
|
||||||
|
### N8N Workflow
|
||||||
|
|
||||||
|
Le workflow N8N doit appeler :
|
||||||
|
- **URL** : `{{ MISSION_API_URL }}/mission-created`
|
||||||
|
- **Méthode** : POST
|
||||||
|
- **Headers** :
|
||||||
|
- `Content-Type: application/json`
|
||||||
|
- `x-api-key: {{ N8N_API_KEY }}`
|
||||||
|
- `Authorization: Bearer {{ Keycloak Token }}` (optionnel)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Checklist
|
||||||
|
|
||||||
|
- [x] Endpoint `/mission-created` créé
|
||||||
|
- [x] Vérification API key implémentée
|
||||||
|
- [x] Mapping des champs correct
|
||||||
|
- [x] Gestion d'erreurs complète
|
||||||
|
- [x] Logging détaillé
|
||||||
|
- [ ] Tests manuels effectués
|
||||||
|
- [ ] Migration des missions existantes (si nécessaire)
|
||||||
|
- [ ] Documentation N8N mise à jour
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Date de correction** : $(date)
|
||||||
|
**Version** : 1.0
|
||||||
|
**Fichiers modifiés** :
|
||||||
|
- `app/api/missions/mission-created/route.ts` (nouveau)
|
||||||
|
|
||||||
673
N8N_COMPLETE_WORKFLOW_MAPPING.md
Normal file
673
N8N_COMPLETE_WORKFLOW_MAPPING.md
Normal file
@ -0,0 +1,673 @@
|
|||||||
|
# Mapping Complet N8N - Création et Suppression de Mission
|
||||||
|
|
||||||
|
## 📋 Vue d'Ensemble
|
||||||
|
|
||||||
|
Ce document décrit le mapping complet entre notre API et les workflows N8N pour la création et la suppression de missions, basé sur les workflows réels partagés.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Workflow de Création - NeahMissionCreate
|
||||||
|
|
||||||
|
### Structure du Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
Webhook (mission-created)
|
||||||
|
↓
|
||||||
|
Process Mission Data
|
||||||
|
↓
|
||||||
|
Get Keycloak Token
|
||||||
|
↓
|
||||||
|
Process Token
|
||||||
|
↓
|
||||||
|
Debug Service Data
|
||||||
|
↓
|
||||||
|
Merge Paths
|
||||||
|
↓
|
||||||
|
IF Run Integrations
|
||||||
|
├─ IF Needs Git Repository
|
||||||
|
│ ├─ Create Git Repository (si Gite ou Calcul)
|
||||||
|
│ ├─ Create Readme
|
||||||
|
│ └─ Git Wiki
|
||||||
|
├─ Create Documentation Collection
|
||||||
|
├─ Create Leantime Project
|
||||||
|
│ └─ Leantime Avatar
|
||||||
|
└─ Create RocketChat Channel
|
||||||
|
↓
|
||||||
|
Combine Results
|
||||||
|
↓
|
||||||
|
Save Mission To API (POST /mission-created)
|
||||||
|
↓
|
||||||
|
Process Results
|
||||||
|
↓
|
||||||
|
Respond To Webhook
|
||||||
|
```
|
||||||
|
|
||||||
|
### Données Envoyées par Notre API → N8N
|
||||||
|
|
||||||
|
**Endpoint** : `POST https://brain.slm-lab.net/webhook/mission-created`
|
||||||
|
|
||||||
|
**Format** :
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
name: string,
|
||||||
|
oddScope: string[],
|
||||||
|
niveau: string,
|
||||||
|
intention: string,
|
||||||
|
missionType: string,
|
||||||
|
donneurDOrdre: string,
|
||||||
|
projection: string,
|
||||||
|
services: string[],
|
||||||
|
participation: string,
|
||||||
|
profils: string[],
|
||||||
|
guardians: {
|
||||||
|
"gardien-temps": userId,
|
||||||
|
"gardien-parole": userId,
|
||||||
|
"gardien-memoire": userId
|
||||||
|
},
|
||||||
|
volunteers: string[],
|
||||||
|
creatorId: string,
|
||||||
|
logo: {
|
||||||
|
data: "data:image/png;base64,...",
|
||||||
|
name: string,
|
||||||
|
type: string
|
||||||
|
},
|
||||||
|
attachments: Array<{
|
||||||
|
data: "data:...;base64,...",
|
||||||
|
name: string,
|
||||||
|
type: string
|
||||||
|
}>,
|
||||||
|
config: {
|
||||||
|
N8N_API_KEY: string,
|
||||||
|
MISSION_API_URL: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Traitement N8N - Process Mission Data
|
||||||
|
|
||||||
|
Le node "Process Mission Data" transforme les données en :
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
missionOriginal: { ... }, // Données originales
|
||||||
|
missionProcessed: {
|
||||||
|
name: "Mission Example",
|
||||||
|
sanitizedName: "mission-example", // Nom nettoyé pour URLs
|
||||||
|
intention: "...",
|
||||||
|
description: "...",
|
||||||
|
startDate: "2024-01-01",
|
||||||
|
endDate: "2024-01-31",
|
||||||
|
missionType: "remote",
|
||||||
|
guardians: { ... },
|
||||||
|
volunteers: [ ... ],
|
||||||
|
profils: [ ... ],
|
||||||
|
services: ["Gite", "ArtLab"], // Détermine quelles intégrations créer
|
||||||
|
clientId: 2,
|
||||||
|
rocketChatUsernames: [userId1, userId2, ...], // Gardiens + volontaires
|
||||||
|
logo: { data: "...", name: "...", type: "..." },
|
||||||
|
attachments: [ ... ]
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
GITEA_API_URL: "https://gite.slm-lab.net/api/v1",
|
||||||
|
GITEA_API_TOKEN: "...",
|
||||||
|
GITEA_OWNER: "alma",
|
||||||
|
LEANTIME_API_URL: "https://agilite.slm-lab.net",
|
||||||
|
LEANTIME_API_TOKEN: "...",
|
||||||
|
ROCKETCHAT_API_URL: "https://parole.slm-lab.net/",
|
||||||
|
ROCKETCHAT_AUTH_TOKEN: "...",
|
||||||
|
ROCKETCHAT_USER_ID: "...",
|
||||||
|
OUTLINE_API_URL: "https://chapitre.slm-lab.net/api",
|
||||||
|
OUTLINE_API_TOKEN: "...",
|
||||||
|
MISSION_API_URL: "https://hub.slm-lab.net",
|
||||||
|
// ... autres configs
|
||||||
|
},
|
||||||
|
creatorId: "user-id"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Intégrations Créées par N8N
|
||||||
|
|
||||||
|
#### 1. Gitea Repository (Conditionnel)
|
||||||
|
|
||||||
|
**Condition** : `services.includes('Gite') || services.includes('Calcul')`
|
||||||
|
|
||||||
|
**Node** : "Create Git Repository"
|
||||||
|
- **Méthode** : POST
|
||||||
|
- **URL** : `{{ GITEA_API_URL }}/user/repos`
|
||||||
|
- **Body** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "{{ sanitizedName }}",
|
||||||
|
"private": true,
|
||||||
|
"auto_init": true,
|
||||||
|
"avatar_url": "{{ logo.data }}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Résultat** : `{ html_url: "https://gite.slm-lab.net/alma/mission-example" }`
|
||||||
|
|
||||||
|
**Actions supplémentaires** :
|
||||||
|
- Create Readme : Crée un document README dans Outline
|
||||||
|
- Git Wiki : Configure le wiki externe du repo vers Outline
|
||||||
|
|
||||||
|
#### 2. Leantime Project
|
||||||
|
|
||||||
|
**Node** : "Create Leantime Project"
|
||||||
|
- **Méthode** : POST
|
||||||
|
- **URL** : `{{ LEANTIME_API_URL }}/api/jsonrpc`
|
||||||
|
- **Body** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"method": "leantime.rpc.Projects.Projects.addProject",
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"params": {
|
||||||
|
"values": {
|
||||||
|
"name": "{{ name }}",
|
||||||
|
"clientId": {{ clientId }},
|
||||||
|
"details": "{{ intention }}",
|
||||||
|
"type": "project",
|
||||||
|
"start": "{{ startDate }}",
|
||||||
|
"end": "{{ endDate }}",
|
||||||
|
"status": "open",
|
||||||
|
"psettings": "restricted",
|
||||||
|
"avatar": "{{ logo.data }}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Résultat** : `{ result: [projectId] }` (array avec 1 élément)
|
||||||
|
|
||||||
|
**Action supplémentaire** :
|
||||||
|
- Leantime Avatar : Met à jour l'avatar du projet
|
||||||
|
|
||||||
|
#### 3. Outline Collection
|
||||||
|
|
||||||
|
**Node** : "Create Documentation Collection"
|
||||||
|
- **Méthode** : POST
|
||||||
|
- **URL** : `{{ OUTLINE_API_URL }}/api/collections.create`
|
||||||
|
- **Body** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "{{ sanitizedName }}",
|
||||||
|
"description": "{{ description }}",
|
||||||
|
"permission": "read",
|
||||||
|
"private": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Résultat** : `{ data: { id: "collection-id", url: "/collection/..." } }`
|
||||||
|
|
||||||
|
#### 4. RocketChat Channel
|
||||||
|
|
||||||
|
**Node** : "Create RocketChat Channel"
|
||||||
|
- **Méthode** : POST
|
||||||
|
- **URL** : `{{ ROCKETCHAT_API_URL }}/api/v1/channels.create`
|
||||||
|
- **Body** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "{{ sanitizedName }}",
|
||||||
|
"members": [{{ rocketChatUsernames }}],
|
||||||
|
"readOnly": false,
|
||||||
|
"avatarUrl": "{{ logo.data }}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Résultat** : `{ channel: { _id: "channel-id", ... } }`
|
||||||
|
|
||||||
|
### Save Mission To API - Retour vers Notre API
|
||||||
|
|
||||||
|
**Node** : "Save Mission To API"
|
||||||
|
- **Méthode** : POST
|
||||||
|
- **URL** : `{{ MISSION_API_URL }}/mission-created`
|
||||||
|
- **Headers** :
|
||||||
|
- `Content-Type: application/json`
|
||||||
|
- `Authorization: Bearer {{ Keycloak Token }}`
|
||||||
|
- `x-api-key: {{ N8N_API_KEY }}`
|
||||||
|
- **Body** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "{{ name }}",
|
||||||
|
"niveau": "{{ niveau }}",
|
||||||
|
"intention": "{{ intention }}",
|
||||||
|
"description": "{{ description }}",
|
||||||
|
"gitRepoUrl": "{{ gitRepo.html_url }}",
|
||||||
|
"leantimeProjectId": "{{ leantimeProject.result[0] }}",
|
||||||
|
"documentationCollectionId": "{{ docCollection.data.id }}",
|
||||||
|
"rocketchatChannelId": "{{ rocketChatChannel.channel._id }}",
|
||||||
|
"donneurDOrdre": "{{ donneurDOrdre }}",
|
||||||
|
"projection": "{{ projection }}",
|
||||||
|
"missionType": "{{ missionType }}",
|
||||||
|
"creatorId": "{{ creatorId }}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**⚠️ IMPORTANT** : Cet endpoint `/mission-created` n'existe **PAS** actuellement dans notre codebase. Il devrait :
|
||||||
|
1. Recevoir les IDs des intégrations créées
|
||||||
|
2. Mettre à jour la mission en base avec ces IDs
|
||||||
|
3. Mapper les champs :
|
||||||
|
- `gitRepoUrl` → `giteaRepositoryUrl`
|
||||||
|
- `documentationCollectionId` → `outlineCollectionId`
|
||||||
|
- `rocketchatChannelId` → `rocketChatChannelId`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗑️ Workflow de Suppression - NeahMissionDelete_Pro
|
||||||
|
|
||||||
|
### Structure du Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
Webhook Delete (mission-delete)
|
||||||
|
↓
|
||||||
|
Process Delete Data
|
||||||
|
↓
|
||||||
|
Get Keycloak Token
|
||||||
|
↓
|
||||||
|
[En parallèle]
|
||||||
|
├─ Delete Gitea Repo
|
||||||
|
├─ Close Leantime Project
|
||||||
|
├─ Delete Outline Collection
|
||||||
|
└─ Close RocketChat Channel
|
||||||
|
↓
|
||||||
|
Combine Results
|
||||||
|
↓
|
||||||
|
Save Deletion To API (POST /mission-deleted)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Données Envoyées par Notre API → N8N
|
||||||
|
|
||||||
|
**Endpoint** : `POST https://brain.slm-lab.net/webhook-test/mission-delete`
|
||||||
|
|
||||||
|
**Format** :
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
missionId: string,
|
||||||
|
name: string,
|
||||||
|
repoName: string, // ✅ Extrait de giteaRepositoryUrl
|
||||||
|
leantimeProjectId: number | 0, // ✅ Converti en number
|
||||||
|
documentationCollectionId: string, // ✅ Mappé depuis outlineCollectionId
|
||||||
|
rocketchatChannelId: string, // ✅ Mappé depuis rocketChatChannelId
|
||||||
|
// Champs originaux pour référence
|
||||||
|
giteaRepositoryUrl: string | null,
|
||||||
|
outlineCollectionId: string | null,
|
||||||
|
rocketChatChannelId: string | null,
|
||||||
|
penpotProjectId: string | null,
|
||||||
|
config: {
|
||||||
|
N8N_API_KEY: string,
|
||||||
|
MISSION_API_URL: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Traitement N8N - Process Delete Data
|
||||||
|
|
||||||
|
Le node "Process Delete Data" transforme les données en :
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
missionData: {
|
||||||
|
repoName: input.repoName || '',
|
||||||
|
leantimeId: input.leantimeProjectId || 0,
|
||||||
|
collectionId: input.documentationCollectionId || '',
|
||||||
|
rocketChatRoomId: input.rocketchatChannelId || ''
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
GITEA_API_URL: "https://gite.slm-lab.net/api/v1",
|
||||||
|
GITEA_API_TOKEN: "...",
|
||||||
|
GITEA_OWNER: "alma",
|
||||||
|
LEANTIME_API_URL: "https://agilite.slm-lab.net",
|
||||||
|
LEANTIME_API_TOKEN: "...",
|
||||||
|
ROCKETCHAT_API_URL: "https://parole.slm-lab.net/",
|
||||||
|
ROCKETCHAT_AUTH_TOKEN: "...",
|
||||||
|
ROCKETCHAT_USER_ID: "...",
|
||||||
|
OUTLINE_API_URL: "https://chapitre.slm-lab.net/api",
|
||||||
|
OUTLINE_API_TOKEN: "...",
|
||||||
|
MISSION_API_URL: "https://hub.slm-lab.net",
|
||||||
|
KEYCLOAK_BASE_URL: "https://connect.slm-lab.net",
|
||||||
|
KEYCLOAK_REALM: "cercle",
|
||||||
|
KEYCLOAK_CLIENT_ID: "lab",
|
||||||
|
KEYCLOAK_CLIENT_SECRET: "..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Actions de Suppression N8N
|
||||||
|
|
||||||
|
#### 1. Delete Gitea Repo
|
||||||
|
|
||||||
|
**Node** : "Delete Gitea Repo"
|
||||||
|
- **Méthode** : DELETE
|
||||||
|
- **URL** : `{{ GITEA_API_URL }}/repos/{{ GITEA_OWNER }}/{{ repoName }}`
|
||||||
|
- **Headers** : `Authorization: token {{ GITEA_API_TOKEN }}`
|
||||||
|
- **ContinueOnFail** : `true`
|
||||||
|
- **Résultat attendu** : Status 204 = succès
|
||||||
|
|
||||||
|
#### 2. Close Leantime Project
|
||||||
|
|
||||||
|
**Node** : "Close Leantime Project"
|
||||||
|
- **Méthode** : POST
|
||||||
|
- **URL** : `{{ LEANTIME_API_URL }}/api/jsonrpc`
|
||||||
|
- **Body** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"method": "leantime.rpc.Projects.Projects.patch",
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"params": {
|
||||||
|
"id": {{ leantimeId }},
|
||||||
|
"params": { "status": "closed" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **ContinueOnFail** : `true`
|
||||||
|
- **Note** : Le projet est **fermé** (status: "closed"), pas supprimé
|
||||||
|
|
||||||
|
#### 3. Delete Outline Collection
|
||||||
|
|
||||||
|
**Node** : "Delete Outline Collection"
|
||||||
|
- **Méthode** : POST
|
||||||
|
- **URL** : `{{ OUTLINE_API_URL }}/api/collections.delete`
|
||||||
|
- **Body** : `{ "id": "{{ collectionId }}" }`
|
||||||
|
- **ContinueOnFail** : `true`
|
||||||
|
- **Résultat attendu** : Status 200 = succès
|
||||||
|
|
||||||
|
#### 4. Close RocketChat Channel
|
||||||
|
|
||||||
|
**Node** : "Close RocketChat Channel"
|
||||||
|
- **Méthode** : POST
|
||||||
|
- **URL** : `{{ ROCKETCHAT_API_URL }}/api/v1/channels.close`
|
||||||
|
- **Body** : `{ "roomId": "{{ rocketChatRoomId }}" }`
|
||||||
|
- **ContinueOnFail** : `true`
|
||||||
|
- **Note** : Le canal est **fermé**, pas supprimé
|
||||||
|
|
||||||
|
### Combine Results
|
||||||
|
|
||||||
|
Le node "Combine Results" combine les résultats :
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
status: "deleted",
|
||||||
|
timestamp: "2024-01-01T12:00:00.000Z",
|
||||||
|
details: {
|
||||||
|
gitea: true || "already_deleted",
|
||||||
|
leantime: true || false,
|
||||||
|
outline: true || false,
|
||||||
|
rocketchat: true || false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Save Deletion To API - Retour vers Notre API
|
||||||
|
|
||||||
|
**Node** : "Save Deletion To API"
|
||||||
|
- **Méthode** : POST
|
||||||
|
- **URL** : `{{ MISSION_API_URL }}/mission-deleted`
|
||||||
|
- **Headers** :
|
||||||
|
- `Authorization: Bearer {{ Keycloak Token }}`
|
||||||
|
- **Body** :
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "archived",
|
||||||
|
"results": {
|
||||||
|
"gitea": true,
|
||||||
|
"leantime": true,
|
||||||
|
"outline": true,
|
||||||
|
"rocketchat": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**⚠️ IMPORTANT** : Cet endpoint `/mission-deleted` n'existe **PAS** actuellement dans notre codebase. Il pourrait servir à :
|
||||||
|
1. Confirmer la suppression
|
||||||
|
2. Logger les résultats
|
||||||
|
3. Nettoyer des données supplémentaires si nécessaire
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Mapping Complet des Champs
|
||||||
|
|
||||||
|
### Création (Notre API → N8N → Retour)
|
||||||
|
|
||||||
|
| Notre Base | Envoyé à N8N | N8N Crée | Retour N8N | Stocké en Base |
|
||||||
|
|-----------|--------------|----------|------------|----------------|
|
||||||
|
| - | `name` | - | `name` | `name` |
|
||||||
|
| - | `services` | Détermine intégrations | - | `services` |
|
||||||
|
| - | `logo.data` | Avatar/Logo | - | `logo` (path) |
|
||||||
|
| - | - | Gitea Repo | `gitRepoUrl` | `giteaRepositoryUrl` |
|
||||||
|
| - | - | Leantime Project | `leantimeProjectId` | `leantimeProjectId` |
|
||||||
|
| - | - | Outline Collection | `documentationCollectionId` | `outlineCollectionId` |
|
||||||
|
| - | - | RocketChat Channel | `rocketchatChannelId` | `rocketChatChannelId` |
|
||||||
|
|
||||||
|
### Suppression (Notre Base → N8N)
|
||||||
|
|
||||||
|
| Notre Base | Extrait/Transformé | Envoyé à N8N | N8N Attend |
|
||||||
|
|-----------|-------------------|--------------|------------|
|
||||||
|
| `giteaRepositoryUrl` | Extraction nom | `repoName` | `repoName` |
|
||||||
|
| `leantimeProjectId` | Converti en number | `leantimeProjectId` | `leantimeId` |
|
||||||
|
| `outlineCollectionId` | Direct | `documentationCollectionId` | `collectionId` |
|
||||||
|
| `rocketChatChannelId` | Direct | `rocketchatChannelId` | `rocketChatRoomId` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Transformations Clés
|
||||||
|
|
||||||
|
### 1. Extraction du Nom du Repository Gitea
|
||||||
|
|
||||||
|
**Problème** : Notre base stocke l'URL complète, N8N attend le nom seul
|
||||||
|
|
||||||
|
**Solution** :
|
||||||
|
```typescript
|
||||||
|
// Format: https://gite.slm-lab.net/alma/mission-example
|
||||||
|
// ou: https://gite.slm-lab.net/api/v1/repos/alma/mission-example
|
||||||
|
|
||||||
|
let repoName = '';
|
||||||
|
if (giteaRepositoryUrl) {
|
||||||
|
try {
|
||||||
|
const url = new URL(giteaRepositoryUrl);
|
||||||
|
const pathParts = url.pathname.split('/').filter(Boolean);
|
||||||
|
repoName = pathParts[pathParts.length - 1] || '';
|
||||||
|
} catch (error) {
|
||||||
|
const match = giteaRepositoryUrl.match(/\/([^\/]+)\/?$/);
|
||||||
|
repoName = match ? match[1] : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Mapping des Champs
|
||||||
|
|
||||||
|
**Création** :
|
||||||
|
- N8N retourne `gitRepoUrl` → On stocke `giteaRepositoryUrl`
|
||||||
|
- N8N retourne `documentationCollectionId` → On stocke `outlineCollectionId`
|
||||||
|
- N8N retourne `rocketchatChannelId` → On stocke `rocketChatChannelId`
|
||||||
|
|
||||||
|
**Suppression** :
|
||||||
|
- On stocke `giteaRepositoryUrl` → On envoie `repoName` (extrait)
|
||||||
|
- On stocke `outlineCollectionId` → On envoie `documentationCollectionId`
|
||||||
|
- On stocke `rocketChatChannelId` → On envoie `rocketchatChannelId`
|
||||||
|
|
||||||
|
### 3. Conversion de Types
|
||||||
|
|
||||||
|
**Leantime Project ID** :
|
||||||
|
- Stocké en base : `string | null`
|
||||||
|
- Envoyé à N8N : `number | 0` (converti)
|
||||||
|
- N8N attend : `number` (dans `leantimeId`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ Endpoints Manquants
|
||||||
|
|
||||||
|
### 1. POST /mission-created
|
||||||
|
|
||||||
|
**Rôle** : Recevoir les IDs des intégrations créées par N8N
|
||||||
|
|
||||||
|
**Format attendu** :
|
||||||
|
```typescript
|
||||||
|
POST /mission-created
|
||||||
|
Headers: {
|
||||||
|
Authorization: "Bearer {keycloak_token}",
|
||||||
|
x-api-key: "{N8N_API_KEY}"
|
||||||
|
}
|
||||||
|
Body: {
|
||||||
|
name: string,
|
||||||
|
niveau: string,
|
||||||
|
intention: string,
|
||||||
|
description: string,
|
||||||
|
gitRepoUrl: string, // À mapper vers giteaRepositoryUrl
|
||||||
|
leantimeProjectId: string, // À mapper vers leantimeProjectId
|
||||||
|
documentationCollectionId: string, // À mapper vers outlineCollectionId
|
||||||
|
rocketchatChannelId: string, // À mapper vers rocketChatChannelId
|
||||||
|
donneurDOrdre: string,
|
||||||
|
projection: string,
|
||||||
|
missionType: string,
|
||||||
|
creatorId: string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Action requise** :
|
||||||
|
1. Trouver la mission par `name` + `creatorId`
|
||||||
|
2. Mettre à jour avec les IDs retournés
|
||||||
|
3. Mapper les champs correctement
|
||||||
|
|
||||||
|
### 2. POST /mission-deleted
|
||||||
|
|
||||||
|
**Rôle** : Confirmer la suppression (optionnel)
|
||||||
|
|
||||||
|
**Format attendu** :
|
||||||
|
```typescript
|
||||||
|
POST /mission-deleted
|
||||||
|
Headers: {
|
||||||
|
Authorization: "Bearer {keycloak_token}"
|
||||||
|
}
|
||||||
|
Body: {
|
||||||
|
status: "archived",
|
||||||
|
results: {
|
||||||
|
gitea: boolean,
|
||||||
|
leantime: boolean,
|
||||||
|
outline: boolean,
|
||||||
|
rocketchat: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Action requise** :
|
||||||
|
- Logger les résultats
|
||||||
|
- Potentiellement nettoyer des données supplémentaires
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Flow Complet - Vue d'Ensemble
|
||||||
|
|
||||||
|
### Création
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Frontend → POST /api/missions
|
||||||
|
↓
|
||||||
|
2. Backend crée mission en Prisma
|
||||||
|
↓
|
||||||
|
3. Backend upload fichiers Minio
|
||||||
|
↓
|
||||||
|
4. Backend → POST N8N webhook (mission-created)
|
||||||
|
↓
|
||||||
|
5. N8N crée intégrations (Gitea, Leantime, Outline, RocketChat)
|
||||||
|
↓
|
||||||
|
6. N8N → POST /mission-created (⚠️ endpoint manquant)
|
||||||
|
↓
|
||||||
|
7. Backend met à jour mission avec IDs (⚠️ non implémenté)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Suppression
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Frontend → DELETE /api/missions/[id]
|
||||||
|
↓
|
||||||
|
2. Backend récupère mission
|
||||||
|
↓
|
||||||
|
3. Backend extrait/mappe les données
|
||||||
|
↓
|
||||||
|
4. Backend → POST N8N webhook (mission-delete)
|
||||||
|
↓
|
||||||
|
5. N8N supprime/ferme intégrations
|
||||||
|
↓
|
||||||
|
6. N8N → POST /mission-deleted (⚠️ endpoint manquant)
|
||||||
|
↓
|
||||||
|
7. Backend supprime logo Minio
|
||||||
|
↓
|
||||||
|
8. Backend supprime attachments Minio
|
||||||
|
↓
|
||||||
|
9. Backend supprime mission Prisma (CASCADE)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Notes Importantes
|
||||||
|
|
||||||
|
### 1. Noms de Champs Incohérents
|
||||||
|
|
||||||
|
- **Création** : N8N retourne `gitRepoUrl`, `documentationCollectionId`, `rocketchatChannelId`
|
||||||
|
- **Suppression** : N8N attend `repoName`, `documentationCollectionId`, `rocketchatChannelId`
|
||||||
|
- **Notre Base** : Stocke `giteaRepositoryUrl`, `outlineCollectionId`, `rocketChatChannelId`
|
||||||
|
|
||||||
|
**Solution** : Mapping cohérent dans les deux sens
|
||||||
|
|
||||||
|
### 2. Endpoint /mission-created Manquant
|
||||||
|
|
||||||
|
Actuellement, les IDs retournés par N8N ne sont **PAS** sauvegardés en base. Il faudrait :
|
||||||
|
- Créer l'endpoint `/mission-created`
|
||||||
|
- Trouver la mission (par `name` + `creatorId` ou `missionId`)
|
||||||
|
- Mettre à jour avec les IDs
|
||||||
|
|
||||||
|
### 3. Services Conditionnels
|
||||||
|
|
||||||
|
- **Gitea** : Créé seulement si `services.includes('Gite') || services.includes('Calcul')`
|
||||||
|
- **Leantime** : Toujours créé
|
||||||
|
- **Outline** : Toujours créé
|
||||||
|
- **RocketChat** : Toujours créé
|
||||||
|
|
||||||
|
### 4. Gestion d'Erreurs
|
||||||
|
|
||||||
|
- Tous les nodes N8N ont `continueOnFail: true`
|
||||||
|
- Les erreurs sont loggées mais n'arrêtent pas le workflow
|
||||||
|
- Les résultats indiquent quelles intégrations ont réussi/échoué
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 Points de Debugging
|
||||||
|
|
||||||
|
### Création
|
||||||
|
|
||||||
|
1. **Vérifier données envoyées à N8N** :
|
||||||
|
```
|
||||||
|
Sending to N8N: { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Vérifier réponse N8N** :
|
||||||
|
```
|
||||||
|
N8N Workflow Result: { success: true, results: {...} }
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Vérifier endpoint /mission-created** :
|
||||||
|
- Doit recevoir les IDs
|
||||||
|
- Doit mettre à jour la mission
|
||||||
|
|
||||||
|
### Suppression
|
||||||
|
|
||||||
|
1. **Vérifier extraction repoName** :
|
||||||
|
```
|
||||||
|
Extracted repo name from URL: { url: "...", repoName: "..." }
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Vérifier données envoyées à N8N** :
|
||||||
|
```
|
||||||
|
Sending deletion data to N8N: { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Vérifier réponse N8N** :
|
||||||
|
```
|
||||||
|
N8N Deletion Workflow Result: { success: true, results: {...} }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Document généré le** : $(date)
|
||||||
|
**Version** : 1.0
|
||||||
|
**Workflows N8N** :
|
||||||
|
- NeahMissionCreate (création)
|
||||||
|
- NeahMissionDelete_Pro (suppression)
|
||||||
|
|
||||||
187
app/api/missions/mission-created/route.ts
Normal file
187
app/api/missions/mission-created/route.ts
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { prisma } from '@/lib/prisma';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /api/missions/mission-created
|
||||||
|
*
|
||||||
|
* Endpoint appelé par N8N après la création des intégrations externes.
|
||||||
|
* Reçoit les IDs des intégrations créées et met à jour la mission en base.
|
||||||
|
*
|
||||||
|
* Headers attendus:
|
||||||
|
* - Authorization: Bearer {keycloak_token} (optionnel, vérifié via x-api-key)
|
||||||
|
* - x-api-key: {N8N_API_KEY}
|
||||||
|
*
|
||||||
|
* Body attendu (format N8N):
|
||||||
|
* {
|
||||||
|
* name: string,
|
||||||
|
* creatorId: string,
|
||||||
|
* gitRepoUrl?: string,
|
||||||
|
* leantimeProjectId?: string,
|
||||||
|
* documentationCollectionId?: string,
|
||||||
|
* rocketchatChannelId?: string,
|
||||||
|
* // ... autres champs optionnels
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
try {
|
||||||
|
console.log('=== Mission Created Webhook Received ===');
|
||||||
|
|
||||||
|
// Vérifier l'API key
|
||||||
|
const apiKey = request.headers.get('x-api-key');
|
||||||
|
const expectedApiKey = process.env.N8N_API_KEY;
|
||||||
|
|
||||||
|
if (!expectedApiKey) {
|
||||||
|
console.error('N8N_API_KEY not configured in environment');
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Server configuration error' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apiKey !== expectedApiKey) {
|
||||||
|
console.error('Invalid API key:', {
|
||||||
|
received: apiKey ? 'present' : 'missing',
|
||||||
|
expected: expectedApiKey ? 'configured' : 'missing'
|
||||||
|
});
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Unauthorized' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = await request.json();
|
||||||
|
console.log('Received mission-created data:', JSON.stringify(body, null, 2));
|
||||||
|
|
||||||
|
// Validation des champs requis
|
||||||
|
if (!body.name || !body.creatorId) {
|
||||||
|
console.error('Missing required fields:', {
|
||||||
|
hasName: !!body.name,
|
||||||
|
hasCreatorId: !!body.creatorId
|
||||||
|
});
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Missing required fields: name and creatorId' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trouver la mission par name + creatorId
|
||||||
|
// On cherche la mission la plus récente avec ce nom et ce créateur
|
||||||
|
const mission = await prisma.mission.findFirst({
|
||||||
|
where: {
|
||||||
|
name: body.name,
|
||||||
|
creatorId: body.creatorId
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: 'desc' // Prendre la plus récente
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!mission) {
|
||||||
|
console.error('Mission not found:', {
|
||||||
|
name: body.name,
|
||||||
|
creatorId: body.creatorId
|
||||||
|
});
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Mission not found' },
|
||||||
|
{ status: 404 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Found mission:', {
|
||||||
|
id: mission.id,
|
||||||
|
name: mission.name,
|
||||||
|
currentIntegrationIds: {
|
||||||
|
gitea: mission.giteaRepositoryUrl,
|
||||||
|
leantime: mission.leantimeProjectId,
|
||||||
|
outline: mission.outlineCollectionId,
|
||||||
|
rocketChat: mission.rocketChatChannelId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Préparer les données de mise à jour
|
||||||
|
const updateData: {
|
||||||
|
giteaRepositoryUrl?: string | null;
|
||||||
|
leantimeProjectId?: string | null;
|
||||||
|
outlineCollectionId?: string | null;
|
||||||
|
rocketChatChannelId?: string | null;
|
||||||
|
} = {};
|
||||||
|
|
||||||
|
// Mapper les champs N8N vers notre schéma Prisma
|
||||||
|
if (body.gitRepoUrl !== undefined) {
|
||||||
|
updateData.giteaRepositoryUrl = body.gitRepoUrl || null;
|
||||||
|
console.log('Updating giteaRepositoryUrl:', body.gitRepoUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body.leantimeProjectId !== undefined) {
|
||||||
|
// N8N peut retourner un number, on le convertit en string
|
||||||
|
updateData.leantimeProjectId = body.leantimeProjectId
|
||||||
|
? String(body.leantimeProjectId)
|
||||||
|
: null;
|
||||||
|
console.log('Updating leantimeProjectId:', updateData.leantimeProjectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body.documentationCollectionId !== undefined) {
|
||||||
|
updateData.outlineCollectionId = body.documentationCollectionId || null;
|
||||||
|
console.log('Updating outlineCollectionId:', updateData.outlineCollectionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body.rocketchatChannelId !== undefined) {
|
||||||
|
updateData.rocketChatChannelId = body.rocketchatChannelId || null;
|
||||||
|
console.log('Updating rocketChatChannelId:', updateData.rocketChatChannelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier qu'il y a au moins un champ à mettre à jour
|
||||||
|
if (Object.keys(updateData).length === 0) {
|
||||||
|
console.warn('No integration IDs to update');
|
||||||
|
return NextResponse.json({
|
||||||
|
message: 'Mission found but no integration IDs provided',
|
||||||
|
mission: {
|
||||||
|
id: mission.id,
|
||||||
|
name: mission.name
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour la mission
|
||||||
|
const updatedMission = await prisma.mission.update({
|
||||||
|
where: { id: mission.id },
|
||||||
|
data: updateData
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Mission updated successfully:', {
|
||||||
|
id: updatedMission.id,
|
||||||
|
name: updatedMission.name,
|
||||||
|
updatedFields: Object.keys(updateData),
|
||||||
|
newIntegrationIds: {
|
||||||
|
gitea: updatedMission.giteaRepositoryUrl,
|
||||||
|
leantime: updatedMission.leantimeProjectId,
|
||||||
|
outline: updatedMission.outlineCollectionId,
|
||||||
|
rocketChat: updatedMission.rocketChatChannelId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
message: 'Mission updated successfully',
|
||||||
|
mission: {
|
||||||
|
id: updatedMission.id,
|
||||||
|
name: updatedMission.name,
|
||||||
|
giteaRepositoryUrl: updatedMission.giteaRepositoryUrl,
|
||||||
|
leantimeProjectId: updatedMission.leantimeProjectId,
|
||||||
|
outlineCollectionId: updatedMission.outlineCollectionId,
|
||||||
|
rocketChatChannelId: updatedMission.rocketChatChannelId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in mission-created webhook:', error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
error: 'Failed to update mission',
|
||||||
|
details: error instanceof Error ? error.message : String(error)
|
||||||
|
},
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user