diff --git a/reacct.json b/reacct.json index 1658a755..c06e0d51 100644 --- a/reacct.json +++ b/reacct.json @@ -3,8 +3,7 @@ "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;" + "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", @@ -17,48 +16,55 @@ }, { "parameters": { - "method": "PUT", - "url": "={{ $node['Process Mission Data'].json.config.MINIO_API_URL + '/missions/' + $node['Process Mission Data'].json.missionProcessed.sanitizedName + '/logo.png' }}", + "functionCode": "const input = $input.item.json;\nconst logoData = input.missionProcessed.logo?.data;\nif (!logoData) return input;\n\n// Convert base64 to buffer\nconst base64Data = logoData.replace(/^data:image\\/\\w+;base64,/, '');\nconst buffer = Buffer.from(base64Data, 'base64');\n\n// Create a proper file object for MinIO\nconst fileData = {\n data: buffer,\n name: input.missionProcessed.logo.name || 'logo.png',\n type: input.missionProcessed.logo.type || 'image/png'\n};\n\nreturn {\n json: {\n ...input,\n missionProcessed: {\n ...input.missionProcessed,\n logo: fileData\n }\n }\n};" + }, + "name": "Decode Logo Data", + "type": "n8n-nodes-base.function", + "typeVersion": 1, + "position": [ + -20, + 500 + ], + "id": "decode-logo-data" + }, + { + "parameters": { + "method": "POST", + "url": "={{ $node['Decode Logo Data'].json.config.MINIO_API_URL + '/api/upload/logo' }}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Content-Type", - "value": "image/png" + "value": "multipart/form-data" }, { - "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' }}" + "name": "x-api-key", + "value": "={{ $node['Decode Logo Data'].json.config.N8N_API_KEY }}" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ + { + "name": "missionId", + "value": "={{ $node['Decode Logo Data'].json.missionProcessed.sanitizedName }}" + }, { "name": "file", - "value": "={{ $node['Process Mission Data'].json.missionProcessed.logo?.data || null }}", + "value": "={{ $node['Decode Logo Data'].json.missionProcessed.logo.data }}", "type": "file" } ] }, "options": { - "bodyContentType": "binary", + "bodyContentType": "multipart-form-data", "response": { "response": { "fullResponse": true } - }, - "timeout": 30000, - "allowUnauthorizedCerts": true + } } }, "name": "Upload Mission Logo to MinIO", @@ -87,7 +93,7 @@ }, { "name": "x-api-key", - "value": "={{ $node['Process Mission Data'].json.config.MINIO_SECRET_KEY }}" + "value": "={{ $node['Process Mission Data'].json.config.N8N_API_KEY }}" } ] }, @@ -100,7 +106,7 @@ }, { "name": "files", - "value": "={{ $node['Process Mission Data'].json.missionProcessed.attachments.map(a => a.data).filter(Boolean) }}", + "value": "={{ $node['Process Mission Data'].json.missionProcessed.attachments }}", "type": "file" } ] @@ -560,9 +566,30 @@ "id": "d5afe297-cf2b-4f33-ae11-fcbcf5643586" } ], - "pinData": {}, "connections": { + "Mission Created Webhook": { + "main": [ + [ + { + "node": "Process Mission Data", + "type": "main", + "index": 0 + } + ] + ] + }, "Process Mission Data": { + "main": [ + [ + { + "node": "Decode Logo Data", + "type": "main", + "index": 0 + } + ] + ] + }, + "Decode Logo Data": { "main": [ [ { @@ -648,6 +675,39 @@ ] ] }, + "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": [ [ @@ -680,17 +740,6 @@ } ] ] - }, - "Mission Created Webhook": { - "main": [ - [ - { - "node": "Process Mission Data", - "type": "main", - "index": 0 - } - ] - ] } }, "active": true, @@ -704,4 +753,4 @@ }, "id": "ybWcDYBfbg8FBe0r", "tags": [] -} \ No newline at end of file +} \ No newline at end of file