{ "name": "Missions", "nodes": [ { "parameters": { "jsCode": "const missionData = $input.item.json;\nconst sanitizeName = (name) => {\n if (!name || typeof name !== 'string') return 'unnamed-mission';\n const timestamp = new Date().getTime();\n const uniqueSuffix = `-${timestamp}`;\n return name.toLowerCase().replace(/[^\\w\\s-]/g, '').replace(/\\s+/g, '-').trim() + uniqueSuffix;\n};\nconst formatDate = (date) => {\n if (!date) return '';\n const d = new Date(date);\n return d.toISOString().split('T')[0];\n};\nconst missionName = missionData?.missionOriginal?.body?.name || missionData?.body?.name || missionData?.name || 'Unnamed Mission';\n\n// Prepare file data for MinIO\nconst prepareFileData = (file) => {\n if (!file) return null;\n return {\n data: file.data || file,\n name: file.name || 'logo.png',\n type: file.type || 'image/png'\n };\n};\n\nconst output = {\n missionOriginal: missionData,\n missionProcessed: {\n name: missionName,\n sanitizedName: sanitizeName(missionName),\n intention: missionData?.missionOriginal?.body?.intention || missionData?.body?.intention || missionData?.intention || '',\n description: missionData?.missionOriginal?.body?.intention || missionData?.body?.intention || missionData?.intention || 'Mission documentation',\n startDate: formatDate(new Date()),\n endDate: formatDate(new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)),\n missionType: missionData?.missionOriginal?.body?.missionType || missionData?.body?.missionType || missionData?.missionType || 'default',\n guardians: missionData?.missionOriginal?.body?.guardians || missionData?.body?.guardians || missionData?.guardians || {},\n volunteers: missionData?.missionOriginal?.body?.volunteers || missionData?.body?.volunteers || missionData?.volunteers || [],\n profils: missionData?.missionOriginal?.body?.profils || missionData?.body?.profils || missionData?.profils || [],\n services: missionData?.missionOriginal?.body?.services || missionData?.body?.services || missionData?.services || [],\n clientId: (missionData?.missionOriginal?.body?.missionType === 'interne' || missionData?.body?.missionType === 'interne' || missionData?.missionType === 'interne') ? 1 : 2,\n rocketChatUsernames: [],\n logo: prepareFileData(missionData?.logo),\n attachments: Array.isArray(missionData?.attachments) ? missionData.attachments.map(prepareFileData) : []\n },\n config: {\n GITEA_API_URL: \"https://gite.slm-lab.net/api/v1\",\n GITEA_API_TOKEN: \"310645d564cbf752be1fe3b42582a3d5f5d0bddd\",\n GITEA_OWNER: \"alma\",\n LEANTIME_API_URL: \"https://agilite.slm-lab.net\",\n LEANTIME_API_TOKEN: \"lt_lsdShQdoYHaPUWuL07XZR1Rf3GeySsIs_UDlll3VJPk5EwAuILpMC4BwzJ9MZFRrb\",\n ROCKETCHAT_API_URL: \"https://parole.slm-lab.net/\",\n ROCKETCHAT_AUTH_TOKEN: \"w91TYgkH-Z67Oz72usYdkW5TZLLRwnre7qyAhp7aHJB\",\n ROCKETCHAT_USER_ID: \"Tpuww59PJKsrGNQJB\",\n OUTLINE_API_URL: \"https://chapitre.slm-lab.net/api\",\n OUTLINE_API_TOKEN: \"ol_api_tlLlANBfcoJ4l7zA8GOcpduAeL6QyBTcYvEnlN\",\n MISSION_API_URL: \"https://hub.slm-lab.net\",\n N8N_API_KEY: \"LwgeE1ntADD20OuWC88S3pR0EaO7FtO4\",\n KEYCLOAK_BASE_URL: \"https://connect.slm-lab.net\",\n KEYCLOAK_REALM: \"cercle\",\n KEYCLOAK_CLIENT_ID: \"lab\",\n KEYCLOAK_CLIENT_SECRET: \"LwgeE1ntADD20OuWC88S3P0EaO7FtO4\",\n MINIO_API_URL: \"https://minio.slm-lab.net\"\n }\n};\n\nconst guardians = missionData?.missionOriginal?.body?.guardians || missionData?.body?.guardians || missionData?.guardians || {};\nif (guardians) {\n for (const role in guardians) {\n const user = guardians[role];\n if (user) output.missionProcessed.rocketChatUsernames.push(user);\n }\n}\nconst volunteers = missionData?.missionOriginal?.body?.volunteers || missionData?.body?.volunteers || missionData?.volunteers || [];\nif (Array.isArray(volunteers)) {\n output.missionProcessed.rocketChatUsernames.push(...volunteers);\n}\noutput.missionProcessed.rocketChatUsernames = [...new Set(output.missionProcessed.rocketChatUsernames)];\n\nreturn output;" }, "name": "Process Mission Data", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [60, 560], "id": "process-mission-data" }, { "parameters": { "method": "POST", "url": "={{ $node['Process Mission Data'].json.config.GITEA_API_URL + '/user/repos' }}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "application/json" }, { "name": "Authorization", "value": "={{ 'Bearer ' + $node['Process Mission Data'].json.config.GITEA_API_TOKEN }}" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ { "name": "name", "value": "={{ $node[\"Process Mission Data\"].json.missionProcessed.name }}" }, { "name": "private", "value": "={{ true }}" }, { "name": "auto_init", "value": "={{ true }}" } ] }, "options": {} }, "name": "Create Git Repository", "type": "n8n-nodes-base.httpRequest", "typeVersion": 3, "position": [ 460, 460 ], "id": "71eea3d3-2a75-4a14-9bc6-c23ca49be59a", "continueOnFail": true }, { "parameters": { "method": "POST", "url": "={{ $node['Process Mission Data'].json.config.LEANTIME_API_URL + '/api/jsonrpc' }}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "application/json" }, { "name": "X-API-Key", "value": "={{ $node['Process Mission Data'].json.config.LEANTIME_API_TOKEN }}" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ { "name": "method", "value": "leantime.rpc.Projects.Projects.addProject" }, { "name": "jsonrpc", "value": "2.0" }, { "name": "id", "value": "1" }, { "name": "params", "value": "={{ { \n \"values\": {\n \"name\": $node[\"Process Mission Data\"].json.missionProcessed.name,\n \"clientId\": $node[\"Process Mission Data\"].json.missionProcessed.clientId,\n \"details\": $node[\"Process Mission Data\"].json.missionProcessed.intention,\n \"type\": \"project\",\n \"start\": $node[\"Process Mission Data\"].json.missionProcessed.startDate,\n \"end\": $node[\"Process Mission Data\"].json.missionProcessed.endDate,\n \"status\": \"open\",\n \"psettings\": \"restricted\"\n }\n} }}" } ] }, "options": { "response": { "response": { "fullResponse": true } } } }, "name": "Create Leantime Project", "type": "n8n-nodes-base.httpRequest", "typeVersion": 3, "position": [ 460, 560 ], "id": "0cb15b0b-f718-4455-8e52-6ad8ceb563eb", "continueOnFail": true }, { "parameters": { "method": "POST", "url": "={{ $node['Process Mission Data'].json.config.OUTLINE_API_URL + '/collections.create' }}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "application/json" }, { "name": "Authorization", "value": "={{ 'Bearer ' + $node['Process Mission Data'].json.config.OUTLINE_API_TOKEN }}" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ { "name": "name", "value": "={{ $node[\"Process Mission Data\"].json.missionProcessed.name }}" }, { "name": "description", "value": "={{ $node[\"Process Mission Data\"].json.missionProcessed.description }}" }, { "name": "color", "value": "#4f46e5" }, { "name": "permission", "value": "read" }, { "name": "private", "value": "={{ true }}" } ] }, "options": {} }, "name": "Create Documentation Collection", "type": "n8n-nodes-base.httpRequest", "typeVersion": 3, "position": [ 460, 760 ], "id": "ed034321-54b1-4e59-b204-c93619561fec", "continueOnFail": true }, { "parameters": { "method": "POST", "url": "={{ $node['Process Mission Data'].json.config.KEYCLOAK_BASE_URL + '/realms/' + $node['Process Mission Data'].json.config.KEYCLOAK_REALM + '/protocol/openid-connect/token' }}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "application/x-www-form-urlencoded" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ { "name": "grant_type", "value": "client_credentials" }, { "name": "client_id", "value": "={{ $node['Process Mission Data'].json.config.KEYCLOAK_CLIENT_ID }}" }, { "name": "client_secret", "value": "={{ $node['Process Mission Data'].json.config.KEYCLOAK_CLIENT_SECRET }}" }, { "name": "scope", "value": "openid profile email" } ] }, "options": { "bodyContentType": "form-urlencoded", "response": { "response": { "fullResponse": true } }, "timeout": 30000 } }, "name": "Get Keycloak Token", "type": "n8n-nodes-base.httpRequest", "typeVersion": 3, "position": [ 460, 460 ], "id": "get-keycloak-token", "continueOnFail": false }, { "parameters": { "functionCode": "const input = $input.item.json;\nconsole.log('KEYCLOAK RESPONSE:', JSON.stringify(input, null, 2));\n\n// Extract the access token from the response body\nconst access_token = input.body?.access_token;\nif (!access_token) {\n throw new Error('No access token received from Keycloak');\n}\n\nreturn {\n json: {\n access_token: access_token\n }\n};" }, "name": "Process Token", "type": "n8n-nodes-base.function", "typeVersion": 1, "position": [ 580, 460 ], "id": "process-token" }, { "parameters": { "functionCode": "// Get all inputs\nconst inputs = $input.item.json;\n\n// Initialize results object\nconst results = {\n gitRepo: null,\n leantimeProject: null,\n docCollection: null,\n rocketChatChannel: null\n};\n\n// Process each input\nif (Array.isArray(inputs)) {\n // First input is gitRepo\n if (inputs[0] && typeof inputs[0] === 'object') {\n results.gitRepo = inputs[0];\n }\n \n // Second input is leantimeProject\n if (inputs[1] && typeof inputs[1] === 'object') {\n // Extract project ID from Leantime response\n const leantimeResponse = inputs[1];\n if (leantimeResponse && leantimeResponse.result) {\n results.leantimeProject = {\n id: leantimeResponse.result,\n name: leantimeResponse.name || ''\n };\n }\n }\n \n // Third input is docCollection\n if (inputs[2] && typeof inputs[2] === 'object') {\n results.docCollection = inputs[2];\n }\n \n // Fourth input is rocketChatChannel\n if (inputs[3] && typeof inputs[3] === 'object') {\n results.rocketChatChannel = inputs[3];\n }\n}\n\n// If we got a single object, it's the gitRepo\nif (!Array.isArray(inputs) && inputs && typeof inputs === 'object') {\n results.gitRepo = inputs;\n}\n\nconsole.log('Input:', JSON.stringify(inputs, null, 2));\nconsole.log('Combined results:', JSON.stringify(results, null, 2));\nreturn results;" }, "name": "Combine Results", "type": "n8n-nodes-base.function", "typeVersion": 1, "position": [ 560, 560 ], "id": "combine-results" }, { "parameters": { "method": "POST", "url": "={{ $node['Process Mission Data'].json.config.MISSION_API_URL + '/api/missions' }}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "application/json" }, { "name": "x-api-key", "value": "={{ $node['Process Mission Data'].json.config.N8N_API_KEY }}" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ { "name": "name", "value": "={{ $node['Process Mission Data'].json.missionProcessed.name }}" }, { "name": "niveau", "value": "={{ $node['Process Mission Data'].json.missionProcessed.niveau || 'default' }}" }, { "name": "intention", "value": "={{ $node['Process Mission Data'].json.missionProcessed.intention }}" }, { "name": "description", "value": "={{ $node['Process Mission Data'].json.missionProcessed.description }}" }, { "name": "gitRepoUrl", "value": "={{ $node['Combine Results'].json.gitRepo?.html_url || '' }}" }, { "name": "leantimeProjectId", "value": "={{ $node['Combine Results'].json.leantimeProject?.id || '' }}" }, { "name": "documentationCollectionId", "value": "={{ $node['Combine Results'].json.docCollection?.id || '' }}" }, { "name": "rocketchatChannelId", "value": "={{ $node['Combine Results'].json.rocketChatChannel?.channel?._id || '' }}" }, { "name": "donneurDOrdre", "value": "={{ $node['Process Mission Data'].json.missionProcessed.donneurDOrdre || 'default' }}" }, { "name": "projection", "value": "={{ $node['Process Mission Data'].json.missionProcessed.projection || 'default' }}" }, { "name": "missionType", "value": "={{ $node['Process Mission Data'].json.missionProcessed.missionType || 'default' }}" } ] }, "options": { "response": { "response": { "fullResponse": true } }, "timeout": 30000 } }, "name": "Save Mission To API", "type": "n8n-nodes-base.httpRequest", "typeVersion": 3, "position": [ 780, 460 ], "id": "save-mission-to-api" }, { "parameters": { "jsCode": "// Defensive access to all nodes\nconst missionData = $node['Process Mission Data']?.json?.missionProcessed || {};\nconst integrationResults = $node['Combine Results']?.json || {};\nconst saveMissionResult = $node['Save Mission To API']?.json || {};\n\nconst errors = [];\n\nif (saveMissionResult.error) {\n errors.push(`Failed to save mission: ${saveMissionResult.error.message || 'Unknown error'}`);\n}\nif (!integrationResults.gitRepo?.html_url) {\n errors.push('Git repository creation failed');\n}\nif (!integrationResults.leantimeProject?.id) {\n errors.push('Leantime project creation failed');\n}\nif (!integrationResults.rocketChatChannel?.channel?._id) {\n errors.push('RocketChat channel creation failed');\n}\nif (!integrationResults.docCollection?.id) {\n errors.push('Documentation collection creation failed');\n}\n\nconst output = {\n success: errors.length === 0,\n error: errors.length > 0 ? errors.join('; ') : null,\n errors: errors,\n missionData: missionData,\n integrationResults: integrationResults,\n saveMissionResult: saveMissionResult,\n message: errors.length === 0 ?\n 'Mission integration complete: All systems updated successfully' :\n `Mission integration failed: ${errors.join('; ')}`\n};\n\nconsole.log('Process Results output:', JSON.stringify(output, null, 2));\nreturn output;" }, "name": "Process Results", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 860, 560 ], "id": "aedea1af-dcfc-4361-bb66-f25dcce10b98" }, { "parameters": { "respondWith": "json", "responseBody": "={{ $node[\"Process Results\"].json }}", "options": {} }, "name": "Respond To Webhook", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1, "position": [ 1060, 560 ], "id": "fcecf6cc-52c3-4989-8fad-0cf6e3508601" }, { "parameters": { "httpMethod": "POST", "path": "mission-created", "options": { "responseData": "lastNodeJson" } }, "name": "Mission Created Webhook", "type": "n8n-nodes-base.webhook", "typeVersion": 1, "position": [ -160, 560 ], "webhookId": "mission-created", "id": "11274a5b-b20f-4180-8611-3690dc9a8722" }, { "parameters": { "method": "POST", "url": "={{ $node['Process Mission Data'].json.config.ROCKETCHAT_API_URL + '/api/v1/channels.create' }}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "application/json" }, { "name": "X-Auth-Token", "value": "={{ $node['Process Mission Data'].json.config.ROCKETCHAT_AUTH_TOKEN }}" }, { "name": "X-User-Id", "value": "={{ $node['Process Mission Data'].json.config.ROCKETCHAT_USER_ID }}" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ { "name": "name", "value": "={{ $node[\"Process Mission Data\"].json.missionProcessed.name }}" }, { "name": "members", "value": "={{ $node[\"Process Mission Data\"].json.missionProcessed.rocketChatUsernames }}" }, { "name": "readOnly", "value": "false" } ] }, "options": {} }, "name": "Create RocketChat Channel", "type": "n8n-nodes-base.httpRequest", "typeVersion": 3, "position": [ 460, 660 ], "id": "90b1482e-6455-4c75-a7ae-297393c7aa63", "continueOnFail": true }, { "parameters": { "method": "POST", "url": "={{ $node['Process Mission Data'].json.config.MINIO_API_URL + '/api/upload/logo' }}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "multipart/form-data" }, { "name": "x-api-key", "value": "={{ $node['Process Mission Data'].json.config.N8N_API_KEY }}" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ { "name": "missionId", "value": "={{ $node['Process Mission Data'].json.missionProcessed.sanitizedName }}" }, { "name": "file", "value": "={{ $node['Process Mission Data'].json.missionProcessed.logo }}", "type": "file" } ] }, "options": { "response": { "response": { "fullResponse": true } }, "bodyContentType": "multipart-form-data" } }, "name": "Upload Mission Logo to MinIO", "type": "n8n-nodes-base.httpRequest", "typeVersion": 3, "position": [260, 500], "id": "upload-logo-to-minio", "continueOnFail": false }, { "parameters": { "method": "POST", "url": "={{ $node['Process Mission Data'].json.config.MINIO_API_URL + '/api/upload/attachments' }}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "multipart/form-data" }, { "name": "x-api-key", "value": "={{ $node['Process Mission Data'].json.config.N8N_API_KEY }}" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ { "name": "missionId", "value": "={{ $node['Process Mission Data'].json.missionProcessed.sanitizedName }}" }, { "name": "files", "value": "={{ $node['Process Mission Data'].json.missionProcessed.attachments }}", "type": "file" } ] }, "options": { "response": { "response": { "fullResponse": true } }, "bodyContentType": "multipart-form-data" } }, "name": "Upload Mission Attachments to MinIO", "type": "n8n-nodes-base.httpRequest", "typeVersion": 3, "position": [260, 600], "id": "upload-attachments-to-minio", "continueOnFail": true } ], "pinData": {}, "connections": { "Mission Created Webhook": { "main": [ [ { "node": "Process Mission Data", "type": "main", "index": 0 } ] ] }, "Process Mission Data": { "main": [ [ { "node": "Get Keycloak Token", "type": "main", "index": 0 } ] ] }, "Get Keycloak Token": { "main": [ [ { "node": "Process Token", "type": "main", "index": 0 } ] ] }, "Process Token": { "main": [ [ { "node": "Create Git Repository", "type": "main", "index": 0 }, { "node": "Create Leantime Project", "type": "main", "index": 0 }, { "node": "Create Documentation Collection", "type": "main", "index": 0 }, { "node": "Create RocketChat Channel", "type": "main", "index": 0 } ] ] }, "Create Git Repository": { "main": [ [{ "node": "Combine Results", "type": "main", "index": 0 }] ] }, "Create Leantime Project": { "main": [ [{ "node": "Combine Results", "type": "main", "index": 1 }] ] }, "Create Documentation Collection": { "main": [ [{ "node": "Combine Results", "type": "main", "index": 2 }] ] }, "Create RocketChat Channel": { "main": [ [{ "node": "Combine Results", "type": "main", "index": 3 }] ] }, "Combine Results": { "main": [ [{ "node": "Save Mission To API", "type": "main", "index": 0 }] ] }, "Save Mission To API": { "main": [ [ { "node": "Process Results", "type": "main", "index": 0 } ] ] }, "Process Results": { "main": [ [ { "node": "Respond To Webhook", "type": "main", "index": 0 } ] ] } }, "active": false, "settings": { "executionOrder": "v1" }, "versionId": "45981b34-113c-428c-85fd-fdc5e22afce4", "meta": { "templateCredsSetupCompleted": true, "instanceId": "575d8de48bd511243817deebddae0cc97d73be64c6c4737e5d4e9caddec881d8" }, "id": "Mxg5cbQzEUgTEFNd", "tags": [] }