mission-tab deletemission

This commit is contained in:
alma 2026-01-04 13:23:17 +01:00
parent 4141f51550
commit 4543e4fb8a
2 changed files with 369 additions and 3 deletions

View File

@ -0,0 +1,342 @@
# Mapping N8N Workflow - Mission Deletion
## 📋 Vue d'Ensemble
Ce document décrit le mapping entre les données de notre API et le format attendu par le workflow N8N `NeahMissionDelete_Pro`.
---
## 🔄 Workflow N8N - Structure
### Nodes du Workflow
1. **Webhook Delete** : Reçoit POST sur `/mission-delete`
2. **Process Delete Data** : Transforme les données d'entrée
3. **Get Keycloak Token** : Obtient un token d'authentification
4. **Delete Gitea Repo** : Supprime le repository Gitea (continueOnFail: true)
5. **Close Leantime Project** : Ferme le projet Leantime (continueOnFail: true)
6. **Delete Outline Collection** : Supprime la collection Outline (continueOnFail: true)
7. **Close RocketChat Channel** : Ferme le canal RocketChat (continueOnFail: true)
8. **Combine Results** : Combine les résultats de toutes les suppressions
9. **Save Deletion To API** : Envoie les résultats à l'API
---
## 📊 Mapping des Données
### Données Envoyées par Notre API
```typescript
{
missionId: string,
name: string,
repoName: string, // Extrait de giteaRepositoryUrl
leantimeProjectId: number | null,
documentationCollectionId: string, // Mappé depuis outlineCollectionId
rocketchatChannelId: string, // Mappé depuis rocketChatChannelId
// Champs originaux conservés 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
}
}
```
### Données Attendues par 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: "..."
}
}
```
---
## 🔧 Transformations Effectuées
### 1. Extraction du Nom du Repository Gitea
**Problème** : Notre base stocke `giteaRepositoryUrl` (URL complète), mais N8N attend `repoName` (nom seul)
**Solution** : Extraction du nom depuis l'URL
```typescript
// Format possible:
// - https://gite.slm-lab.net/alma/repo-name
// - https://gite.slm-lab.net/api/v1/repos/alma/repo-name
let repoName = '';
if (mission.giteaRepositoryUrl) {
try {
const url = new URL(mission.giteaRepositoryUrl);
const pathParts = url.pathname.split('/').filter(Boolean);
repoName = pathParts[pathParts.length - 1] || '';
} catch (error) {
// Fallback: extraction regex
const match = mission.giteaRepositoryUrl.match(/\/([^\/]+)\/?$/);
repoName = match ? match[1] : '';
}
}
```
**Exemples** :
- `https://gite.slm-lab.net/alma/mission-abc``mission-abc`
- `https://gite.slm-lab.net/api/v1/repos/alma/mission-xyz``mission-xyz`
### 2. Mapping des Champs
| Notre Base de Données | N8N Attend | Transformation |
|----------------------|------------|----------------|
| `giteaRepositoryUrl` | `repoName` | Extraction du nom depuis URL |
| `leantimeProjectId` | `leantimeProjectId` | Direct (converti en number) |
| `outlineCollectionId` | `documentationCollectionId` | Direct mapping |
| `rocketChatChannelId` | `rocketchatChannelId` | Direct mapping (lowercase 'c') |
---
## 🎯 Actions N8N par Service
### 1. Gitea Repository
**Node** : "Delete Gitea Repo"
- **Méthode** : DELETE
- **URL** : `{{ GITEA_API_URL }}/repos/{{ GITEA_OWNER }}/{{ repoName }}`
- **Headers** : `Authorization: token {{ GITEA_API_TOKEN }}`
- **ContinueOnFail** : `true` (continue même si échoue)
**Résultat attendu** : Status 204 (No Content) = succès
### 2. Leantime Project
**Node** : "Close Leantime Project"
- **Méthode** : POST
- **URL** : `{{ LEANTIME_API_URL }}/api/jsonrpc`
- **Headers** : `X-API-Key: {{ LEANTIME_API_TOKEN }}`
- **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. Outline Collection
**Node** : "Delete Outline Collection"
- **Méthode** : POST
- **URL** : `{{ OUTLINE_API_URL }}/api/collections.delete`
- **Headers** : `Authorization: Bearer {{ OUTLINE_API_TOKEN }}`
- **Body** : `{ "id": "{{ collectionId }}" }`
- **ContinueOnFail** : `true`
**Résultat attendu** : Status 200 = succès
### 4. RocketChat Channel
**Node** : "Close RocketChat Channel"
- **Méthode** : POST
- **URL** : `{{ ROCKETCHAT_API_URL }}/api/v1/channels.close`
- **Headers** :
- `X-Auth-Token: {{ ROCKETCHAT_AUTH_TOKEN }}`
- `X-User-Id: {{ ROCKETCHAT_USER_ID }}`
- **Body** : `{ "roomId": "{{ rocketChatRoomId }}" }`
- **ContinueOnFail** : `true`
**Note** : Le canal est **fermé**, pas supprimé
---
## 📤 Réponse N8N
### Format de Réponse (Combine Results)
```javascript
{
status: "deleted",
timestamp: "2024-01-01T12:00:00.000Z",
details: {
gitea: true || "already_deleted",
leantime: true || false,
outline: true || false,
rocketchat: true || false
}
}
```
### Envoi à l'API (Save Deletion To API)
Le workflow envoie ensuite les résultats à :
- **URL** : `{{ MISSION_API_URL }}/mission-deleted`
- **Méthode** : POST
- **Headers** : `Authorization: Bearer {{ Keycloak Token }}`
- **Body** :
```json
{
"status": "archived",
"results": {
"gitea": true,
"leantime": true,
"outline": true,
"rocketchat": true
}
}
```
---
## ⚠️ Points d'Attention
### 1. Gestion des Erreurs
- Tous les nodes de suppression ont `continueOnFail: true`
- Si une suppression échoue, le workflow continue avec les autres
- Les résultats indiquent quelles suppressions ont réussi/échoué
### 2. Différences de Comportement
- **Gitea** : Suppression complète du repository
- **Leantime** : Fermeture (status: "closed"), pas suppression
- **Outline** : Suppression complète de la collection
- **RocketChat** : Fermeture du canal, pas suppression
### 3. Extraction du Repo Name
- L'extraction doit gérer différents formats d'URL
- Si l'extraction échoue, `repoName` sera vide
- Le workflow N8N gérera le cas où `repoName` est vide
### 4. Mapping des Champs
- **documentationCollectionId** : Mappé depuis `outlineCollectionId`
- **rocketchatChannelId** : Mappé depuis `rocketChatChannelId` (attention au 'c' minuscule)
- **leantimeProjectId** : Converti en number (0 si null)
---
## 🔍 Debugging
### Logs à Surveiller
1. **Extraction repo name** :
```
Extracted repo name from URL: { url: "...", repoName: "..." }
```
2. **Données envoyées à N8N** :
```
Sending deletion data to N8N: { ... }
```
3. **Résultat N8N** :
```
N8N Deletion Workflow Result: { success: true, results: {...} }
```
### Vérifications
1. **Repo name extrait correctement** ?
- Vérifier les logs d'extraction
- Format attendu : nom simple sans URL
2. **Mapping des champs correct** ?
- `documentationCollectionId` = `outlineCollectionId`
- `rocketchatChannelId` = `rocketChatChannelId`
3. **N8N a reçu les données** ?
- Vérifier les logs N8N
- Vérifier le webhook a été appelé
---
## 📝 Exemple Complet
### Données en Base
```typescript
{
id: "abc-123",
name: "Mission Example",
giteaRepositoryUrl: "https://gite.slm-lab.net/alma/mission-example",
leantimeProjectId: "123",
outlineCollectionId: "collection-456",
rocketChatChannelId: "channel-789"
}
```
### Données Envoyées à N8N
```typescript
{
missionId: "abc-123",
name: "Mission Example",
repoName: "mission-example", // Extrait de l'URL
leantimeProjectId: 123, // Converti en number
documentationCollectionId: "collection-456", // Mappé
rocketchatChannelId: "channel-789", // Mappé (lowercase 'c')
giteaRepositoryUrl: "https://gite.slm-lab.net/alma/mission-example",
outlineCollectionId: "collection-456",
rocketChatChannelId: "channel-789",
config: {
N8N_API_KEY: "...",
MISSION_API_URL: "https://hub.slm-lab.net"
}
}
```
### Données Traitées par N8N
```javascript
{
missionData: {
repoName: "mission-example",
leantimeId: 123,
collectionId: "collection-456",
rocketChatRoomId: "channel-789"
},
config: { ... }
}
```
---
**Document généré le** : $(date)
**Version** : 1.0
**Workflow N8N** : NeahMissionDelete_Pro
**Webhook URL** : https://brain.slm-lab.net/webhook-test/mission-delete

View File

@ -332,17 +332,41 @@ export async function DELETE(
console.log('=== Starting N8N Deletion Workflow ==='); console.log('=== Starting N8N Deletion Workflow ===');
const n8nService = new N8nService(); const n8nService = new N8nService();
// Extract repo name from giteaRepositoryUrl if present
// Format: https://gite.slm-lab.net/alma/repo-name or https://gite.slm-lab.net/api/v1/repos/alma/repo-name
let repoName = '';
if (mission.giteaRepositoryUrl) {
try {
const url = new URL(mission.giteaRepositoryUrl);
// Extract repo name from path (last segment)
const pathParts = url.pathname.split('/').filter(Boolean);
repoName = pathParts[pathParts.length - 1] || '';
console.log('Extracted repo name from URL:', { url: mission.giteaRepositoryUrl, repoName });
} catch (error) {
console.error('Error extracting repo name from URL:', error);
// If URL parsing fails, try to extract from the string directly
const match = mission.giteaRepositoryUrl.match(/\/([^\/]+)\/?$/);
repoName = match ? match[1] : '';
}
}
// Prepare data according to N8N workflow expectations
// The workflow expects: repoName, leantimeProjectId, documentationCollectionId, rocketchatChannelId
const n8nDeletionData = { const n8nDeletionData = {
missionId: mission.id, missionId: mission.id,
name: mission.name, name: mission.name,
leantimeProjectId: mission.leantimeProjectId, repoName: repoName, // N8N expects repoName, not giteaRepositoryUrl
leantimeProjectId: mission.leantimeProjectId || 0,
documentationCollectionId: mission.outlineCollectionId || '', // N8N expects documentationCollectionId
rocketchatChannelId: mission.rocketChatChannelId || '', // N8N expects rocketchatChannelId (lowercase 'c')
// Keep original fields for reference
giteaRepositoryUrl: mission.giteaRepositoryUrl,
outlineCollectionId: mission.outlineCollectionId, outlineCollectionId: mission.outlineCollectionId,
rocketChatChannelId: mission.rocketChatChannelId, rocketChatChannelId: mission.rocketChatChannelId,
giteaRepositoryUrl: mission.giteaRepositoryUrl,
penpotProjectId: mission.penpotProjectId, penpotProjectId: mission.penpotProjectId,
config: { config: {
N8N_API_KEY: process.env.N8N_API_KEY, N8N_API_KEY: process.env.N8N_API_KEY,
MISSION_API_URL: process.env.NEXT_PUBLIC_API_URL MISSION_API_URL: process.env.NEXT_PUBLIC_API_URL || 'https://hub.slm-lab.net'
} }
}; };