{ "name": "My workflow 10", "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 const result = name.toLowerCase()\n .split('')\n .map(c => {\n if (c >= 'a' && c <= 'z') return c;\n if (c >= '0' && c <= '9') return c;\n if (c === ' ' || c === '-') return c;\n return '';\n })\n .join('')\n .split(' ')\n .filter(Boolean)\n .join('-');\n return result + 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) {\n // Return default logo if no file provided\n return {\n data: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=',\n name: 'default-logo.png',\n type: 'image/png'\n };\n }\n \n // Handle different file formats\n if (typeof file === 'string') {\n return {\n data: file,\n name: 'logo.png',\n type: 'image/png'\n };\n }\n \n if (typeof file === 'object') {\n // Handle binary data\n if (file.data && typeof file.data === 'object' && file.data.data) {\n return {\n data: file.data.data,\n name: file.name || 'logo.png',\n type: file.type || 'image/png'\n };\n }\n \n // Handle base64 data\n if (file.data && typeof file.data === 'string') {\n return {\n data: file.data,\n name: file.name || 'logo.png',\n type: file.type || 'image/png'\n };\n }\n \n // Handle direct object\n return {\n data: file,\n name: file.name || 'logo.png',\ type: file.type || 'image/png'\n };\n }\n \n return null;\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).filter(Boolean) : []\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://dome-api.slm-lab.net\",\n MINIO_SECRET_KEY: \"gbdrqJsXyU4IFxsfz9xdrnQeMRy2eZHeqQRrAeBR\"\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": [ -140, 560 ], "id": "4c53f8e1-285d-4b97-923a-9090ada513f2" }, { "parameters": { "method": "PUT", "url": "={{ $node['Process Mission Data'].json.config.MINIO_API_URL + '/missions/' + $node['Process Mission Data'].json.missionProcessed.sanitizedName + '/logo.png' }}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", "value": "image/png" }, { "name": "x-amz-acl", "value": "public-read" }, { "name": "x-amz-date", "value": "={{ new Date().toISOString().split('.')[0].replace(/[:-]/g, '') + 'Z' }}" }, { "name": "Authorization", "value": "={{ 'AWS4-HMAC-SHA256 Credential=' + $node['Process Mission Data'].json.config.MINIO_SECRET_KEY + '/' + new Date().toISOString().split('T')[0] + '/us-east-1/s3/aws4_request' }}" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ { "name": "file", "value": "={{ $node['Process Mission Data'].json.missionProcessed.logo?.data || null }}", "type": "file" } ] }, "options": { "bodyContentType": "binary", "response": { "response": { "fullResponse": true } }, "timeout": 30000, "allowUnauthorizedCerts": true } }, "name": "Upload Mission Logo to MinIO", "type": "n8n-nodes-base.httpRequest", "typeVersion": 3, "position": [ 60, 500 ], "id": "6d76b33e-d25e-4c1f-b906-f47639facc42", "continueOnFail": true, "executeOnce": false, "maxTries": 3, "waitBetweenTries": 1000 }, { "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.MINIO_SECRET_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.map(a => a.data).filter(Boolean) }}", "type": "file" } ] }, "options": { "bodyContentType": "multipart-form-data", "response": { "response": { "fullResponse": true } } } }, "name": "Upload Mission Attachments to MinIO", "type": "n8n-nodes-base.httpRequest", "typeVersion": 3, "position": [ 60, 600 ], "id": "488895ea-024f-4946-90ec-f83f48f215ee", "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": { "response": { "response": { "fullResponse": true } }, "timeout": 30000 } }, "name": "Get Keycloak Token", "type": "n8n-nodes-base.httpRequest", "typeVersion": 3, "position": [ 260, 560 ], "id": "ed5999b3-f9af-45ae-86ce-f4522c69b5b7" }, { "parameters": { "functionCode": "const input = $input.item.json;\nconst access_token = input.body?.access_token;\nif (!access_token) throw new Error('No access token received from Keycloak');\nreturn { json: { access_token } };" }, "name": "Process Token", "type": "n8n-nodes-base.function", "typeVersion": 1, "position": [ 380, 560 ], "id": "ef75d1c4-e3fd-4e4c-8447-023f2d9af420" }, { "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 }}" }, { "name": "avatar_url", "value": "={{ $node['Upload Mission Logo to MinIO'].json.publicUrl }}" } ] }, "options": {} }, "name": "Create Git Repository", "type": "n8n-nodes-base.httpRequest", "typeVersion": 3, "position": [ 460, 460 ], "id": "a359928d-e372-4334-a91c-8d6d64dba92c", "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": "={{ { values: { name: $node['Process Mission Data'].json.missionProcessed.name, clientId: $node['Process Mission Data'].json.missionProcessed.clientId, details: $node['Process Mission Data'].json.missionProcessed.intention, type: 'project', start: $node['Process Mission Data'].json.missionProcessed.startDate, end: $node['Process Mission Data'].json.missionProcessed.endDate, status: 'open', psettings: 'restricted', avatar: $node['Upload Mission Logo to MinIO'].json.publicUrl } } }}" } ] }, "options": { "response": { "response": { "fullResponse": true } } } }, "name": "Create Leantime Project", "type": "n8n-nodes-base.httpRequest", "typeVersion": 3, "position": [ 460, 560 ], "id": "c4533a11-692c-4d85-a0ab-b463c487b676", "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 }}" }, { "name": "avatarUrl", "value": "={{ $node['Upload Mission Logo to MinIO'].json.publicUrl }}" } ] }, "options": {} }, "name": "Create Documentation Collection", "type": "n8n-nodes-base.httpRequest", "typeVersion": 3, "position": [ 460, 760 ], "id": "9a20119a-e0ea-4e11-af63-c779bcc6ab59", "continueOnFail": true }, { "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" }, { "name": "avatarUrl", "value": "={{ $node['Upload Mission Logo to MinIO'].json.publicUrl }}" } ] }, "options": {} }, "name": "Create RocketChat Channel", "type": "n8n-nodes-base.httpRequest", "typeVersion": 3, "position": [ 460, 660 ], "id": "af78b5b8-cc29-4f06-b25d-04e2e0edfa0c", "continueOnFail": true }, { "parameters": { "functionCode": "// Combine results from all integrations\nconst inputs = $input.item.json;\nconst results = {\n gitRepo: inputs[0],\n leantimeProject: inputs[1],\n docCollection: inputs[2],\n rocketChatChannel: inputs[3]\n};\nreturn results;" }, "name": "Combine Results", "type": "n8n-nodes-base.function", "typeVersion": 1, "position": [ 660, 560 ], "id": "f7529ca9-6507-4108-801e-9d01831a1d22" }, { "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": "logo", "value": "={{ $node['Upload Mission Logo to MinIO'].json.publicUrl }}" }, { "name": "attachments", "value": "={{ $node['Upload Mission Attachments to MinIO'].json.files }}" }, { "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": [ 860, 460 ], "id": "8c3e264c-0cba-4d08-9eeb-b3efecef223e" }, { "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 || {};\nconst errors = [];\nif (saveMissionResult.error) errors.push(`Failed to save mission: ${saveMissionResult.error.message || 'Unknown error'}`);\nif (!integrationResults.gitRepo?.html_url) errors.push('Git repository creation failed');\nif (!integrationResults.leantimeProject?.id) errors.push('Leantime project creation failed');\nif (!integrationResults.rocketChatChannel?.channel?._id) errors.push('RocketChat channel creation failed');\nif (!integrationResults.docCollection?.id) errors.push('Documentation collection creation failed');\nconst output = {\n success: errors.length === 0,\n error: errors.length > 0 ? errors.join('; ') : null,\n errors: errors,\n missionData,\n integrationResults,\n saveMissionResult,\n message: errors.length === 0 ? 'Mission integration complete: All systems updated successfully' : `Mission integration failed: ${errors.join('; ')}`\n};\nreturn output;" }, "name": "Process Results", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 960, 560 ], "id": "b4fde353-fd64-4f29-9ade-0fbde9a09674" }, { "parameters": { "respondWith": "json", "responseBody": "={{ $node[\"Process Results\"].json }}", "options": {} }, "name": "Respond To Webhook", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1, "position": [ 1060, 560 ], "id": "5922f35a-d83e-4c9d-a3cf-9b2cf995a3f8" }, { "parameters": { "httpMethod": "POST", "path": "mission-created", "options": { "responseData": "allEntries", "responseContentType": "application/json" } }, "name": "Mission Created Webhook", "type": "n8n-nodes-base.webhook", "typeVersion": 1, "position": [ -360, 560 ], "webhookId": "mission-created", "id": "d5afe297-cf2b-4f33-ae11-fcbcf5643586" } ], "pinData": {}, "connections": { "Process Mission Data": { "main": [ [ { "node": "Upload Mission Logo to MinIO", "type": "main", "index": 0 }, { "node": "Upload Mission Attachments to MinIO", "type": "main", "index": 0 } ] ] }, "Upload Mission Logo to MinIO": { "main": [ [ { "node": "Get Keycloak Token", "type": "main", "index": 0 } ] ] }, "Upload Mission Attachments to MinIO": { "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 } ] ] }, "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 } ] ] }, "Mission Created Webhook": { "main": [ [ { "node": "Process Mission Data", "type": "main", "index": 0 } ] ] } }, "active": true, "settings": { "executionOrder": "v1" }, "versionId": "6f4f1b22-3e1d-4318-9e21-bc16b700b352", "meta": { "templateCredsSetupCompleted": true, "instanceId": "575d8de48bd511243817deebddae0cc97d73be64c6c4737e5d4e9caddec881d8" }, "id": "ybWcDYBfbg8FBe0r", "tags": [] }