diff --git a/.DS_Store b/.DS_Store index 590b46fd..a683ba31 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/Missionspre.json b/Missionspre.json deleted file mode 100644 index c7511a37..00000000 --- a/Missionspre.json +++ /dev/null @@ -1,554 +0,0 @@ -{ - "name": "Missions", - "nodes": [ - { - "parameters": { - "jsCode": "const missionData = $input.item.json; console.log('Raw input:', JSON.stringify($input.item, null, 2)); console.log('Mission data:', JSON.stringify(missionData, null, 2)); const sanitizeName = (name) => { console.log('Sanitizing name:', name); if (!name || typeof name !== 'string') { console.log('Invalid name, using default'); return 'unnamed-mission'; } const timestamp = new Date().getTime(); const uniqueSuffix = `-${timestamp}`; const sanitized = name.toLowerCase().replace(/[^\\w\\s-]/g, '').replace(/\\s+/g, '-').trim() + uniqueSuffix; console.log('Sanitized result:', sanitized); return sanitized; }; const formatDate = (date) => { if (!date) return ''; const d = new Date(date); return d.toISOString().split('T')[0]; }; const missionName = missionData?.missionOriginal?.body?.name || missionData?.body?.name || missionData?.name || 'Unnamed Mission'; console.log('Extracted mission name:', missionName); const output = { missionOriginal: missionData, missionProcessed: { name: missionName, sanitizedName: sanitizeName(missionName), intention: missionData?.missionOriginal?.body?.intention || missionData?.body?.intention || missionData?.intention || '', description: missionData?.missionOriginal?.body?.intention || missionData?.body?.intention || missionData?.intention || 'Mission documentation', startDate: formatDate(new Date()), endDate: formatDate(new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)), missionType: missionData?.missionOriginal?.body?.missionType || missionData?.body?.missionType || missionData?.missionType || 'default', guardians: missionData?.missionOriginal?.body?.guardians || missionData?.body?.guardians || missionData?.guardians || {}, volunteers: missionData?.missionOriginal?.body?.volunteers || missionData?.body?.volunteers || missionData?.volunteers || [], profils: missionData?.missionOriginal?.body?.profils || missionData?.body?.profils || missionData?.profils || [], services: missionData?.missionOriginal?.body?.services || missionData?.body?.services || missionData?.services || [], clientId: (missionData?.missionOriginal?.body?.missionType === 'interne' || missionData?.body?.missionType === 'interne' || missionData?.missionType === 'interne') ? 1 : 2, rocketChatUsernames: [] }, config: { GITEA_API_URL: \"https://gite.slm-lab.net/api/v1\", GITEA_API_TOKEN: \"310645d564cbf752be1fe3b42582a3d5f5d0bddd\", GITEA_OWNER: \"alma\", LEANTIME_API_URL: \"https://agilite.slm-lab.net\", LEANTIME_API_TOKEN: \"lt_lsdShQdoYHaPUWuL07XZR1Rf3GeySsIs_UDlll3VJPk5EwAuILpMC4BwzJ9MZFRrb\", ROCKETCHAT_API_URL: \"https://parole.slm-lab.net/\", ROCKETCHAT_AUTH_TOKEN: \"w91TYgkH-Z67Oz72usYdkW5TZLLRwnre7qyAhp7aHJB\", ROCKETCHAT_USER_ID: \"Tpuww59PJKsrGNQJB\", OUTLINE_API_URL: \"https://chapitre.slm-lab.net/api\", OUTLINE_API_TOKEN: \"ol_api_tlLlANBfcoJ4l7zA8GOcpduAeL6QyBTcYvEnlN\", MISSION_API_URL: \"https://hub.slm-lab.net\", N8N_API_KEY: \"LwgeE1ntADD20OuWC88S3pR0EaO7FtO4\", KEYCLOAK_BASE_URL: \"https://connect.slm-lab.net\", KEYCLOAK_REALM: \"cercle\", KEYCLOAK_CLIENT_ID: \"lab\", KEYCLOAK_CLIENT_SECRET: \"LwgeE1ntADD20OuWC88S3pR0EaO7FtO4\" } }; const guardians = missionData?.missionOriginal?.body?.guardians || missionData?.body?.guardians || missionData?.guardians || {}; if (guardians) { for (const role in guardians) { const user = guardians[role]; if (user) output.missionProcessed.rocketChatUsernames.push(user); } } const volunteers = missionData?.missionOriginal?.body?.volunteers || missionData?.body?.volunteers || missionData?.volunteers || []; if (Array.isArray(volunteers)) { output.missionProcessed.rocketChatUsernames.push(...volunteers); } output.missionProcessed.rocketChatUsernames = [...new Set(output.missionProcessed.rocketChatUsernames)]; console.log('Final processed data:', { name: output.missionProcessed.name, sanitizedName: output.missionProcessed.sanitizedName, rocketChatUsernames: output.missionProcessed.rocketChatUsernames }); return output;" - }, - "name": "Process Mission Data", - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 260, - 560 - ], - "id": "900e5ca6-b081-4f80-a7c5-ecde88c4ee3d" - }, - { - "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 - } - ], - "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": [] -} \ No newline at end of file diff --git a/app/api/missions/route.ts b/app/api/missions/route.ts index cfc22b55..b69f715e 100644 --- a/app/api/missions/route.ts +++ b/app/api/missions/route.ts @@ -236,6 +236,12 @@ export async function POST(request: Request) { try { // Trigger the n8n workflow + console.log('About to trigger n8n workflow with data:', { + missionId: mission.id, + name: mission.name, + creatorId: auth.userId + }); + const n8nService = new N8nService(); const workflowResult = await n8nService.createMission({ ...body, @@ -243,8 +249,11 @@ export async function POST(request: Request) { creatorId: auth.userId }); + console.log('Received workflow result:', workflowResult); + // Update mission with integration results, even if some failed if (workflowResult.results) { + console.log('Processing workflow results:', workflowResult.results); const updateData: any = {}; // Only update fields that were successfully created @@ -261,6 +270,8 @@ export async function POST(request: Request) { updateData.outlineCollectionId = workflowResult.results.docCollection.id; } + console.log('Updating mission with integration data:', updateData); + // Only update if we have any successful integrations if (Object.keys(updateData).length > 0) { await prisma.mission.update({ diff --git a/lib/services/n8n-service.ts b/lib/services/n8n-service.ts index d6b88b91..3d89217a 100644 --- a/lib/services/n8n-service.ts +++ b/lib/services/n8n-service.ts @@ -4,7 +4,8 @@ export class N8nService { private webhookUrl: string; constructor() { - this.webhookUrl = process.env.N8N_WEBHOOK_URL || 'https://brain.slm-lab.net/webhook/mission-created'; + this.webhookUrl = process.env.N8N_WEBHOOK_URL || 'https://brain.slm-lab.net/webhook-test/mission-created'; + console.log('N8nService initialized with webhook URL:', this.webhookUrl); } /** @@ -17,18 +18,21 @@ export class N8nService { console.log('Triggering n8n workflow for mission creation:', { name: missionData.name, missionType: missionData.missionType, - webhookUrl: this.webhookUrl + webhookUrl: this.webhookUrl, + fullData: JSON.stringify(missionData, null, 2) }); const response = await axios.post(this.webhookUrl, missionData, { headers: { 'Content-Type': 'application/json' - } + }, + timeout: 30000 // 30 second timeout }); console.log('n8n workflow response:', { status: response.status, statusText: response.statusText, + headers: response.headers, data: response.data }); @@ -60,7 +64,8 @@ export class N8nService { } catch (error) { console.error('Error triggering n8n workflow:', { error: error instanceof Error ? error.message : String(error), - webhookUrl: this.webhookUrl + webhookUrl: this.webhookUrl, + errorDetails: error instanceof Error ? error.stack : undefined }); throw new Error(`Failed to trigger mission creation workflow: ${error instanceof Error ? error.message : String(error)}`); } diff --git a/reacct.json b/reacct.json new file mode 100644 index 00000000..c6a581f6 --- /dev/null +++ b/reacct.json @@ -0,0 +1,705 @@ +{ + "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',\n 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": "lastNodeJson" + } + }, + "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": [] +} \ No newline at end of file