NeahNew/N8N_COMPLETE_WORKFLOW_MAPPING.md
2026-01-04 14:24:56 +01:00

17 KiB

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 :

{
  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 :

{
  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 :
    {
      "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 :
    {
      "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 :
    {
      "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 :
    {
      "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 :
    {
      "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 :
    • gitRepoUrlgiteaRepositoryUrl
    • documentationCollectionIdoutlineCollectionId
    • rocketchatChannelIdrocketChatChannelId

🗑️ 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 :

{
  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 :

{
  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 :
    {
      "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 :

{
  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 :
    {
      "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 :

// 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 :

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 :

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)