diff --git a/lib/services/n8n-service.ts b/lib/services/n8n-service.ts index 9ce0ec20..7de48e01 100644 --- a/lib/services/n8n-service.ts +++ b/lib/services/n8n-service.ts @@ -15,6 +15,17 @@ export class N8nService { console.log('Triggering n8n workflow with data:', JSON.stringify(data, null, 2)); console.log('Using webhook URL:', this.webhookUrl); + // Log the full request details + const requestDetails = { + url: this.webhookUrl, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: data + }; + console.log('Full request details:', JSON.stringify(requestDetails, null, 2)); + const response = await fetch(this.webhookUrl, { method: 'POST', headers: { @@ -35,69 +46,16 @@ export class N8nService { const responseText = await response.text(); console.log('Raw response from n8n:', responseText); - // Handle 'allEntries' response - if (responseText === 'allEntries') { - console.log('Received allEntries response, workflow triggered successfully'); - return { - success: true, - results: { - leantimeProjectId: null, - outlineCollectionId: null, - rocketChatChannelId: null, - giteaRepositoryUrl: null - } - }; - } - + // Try to parse the response as JSON try { - // Try to parse as JSON const result = JSON.parse(responseText); - console.log('Parsed JSON response:', JSON.stringify(result, null, 2)); - - // Handle lastNodeJson format - if (result.lastNodeJson) { - try { - const parsedResults = JSON.parse(result.lastNodeJson); - return { - success: true, - results: { - leantimeProjectId: parsedResults.leantimeProjectId?.toString() || null, - outlineCollectionId: parsedResults.outlineCollectionId?.toString() || null, - rocketChatChannelId: parsedResults.rocketChatChannelId?.toString() || null, - giteaRepositoryUrl: parsedResults.giteaRepositoryUrl || null - } - }; - } catch (parseError) { - console.error('Error parsing lastNodeJson:', parseError); - return { - success: false, - error: 'Failed to parse n8n response', - results: { - leantimeProjectId: null, - outlineCollectionId: null, - rocketChatChannelId: null, - giteaRepositoryUrl: null - } - }; - } - } - - // Handle regular JSON response - const integrationResults = result.results || result; - console.log('Integration results:', JSON.stringify(integrationResults, null, 2)); - + console.log('Parsed workflow result:', JSON.stringify(result, null, 2)); return { success: true, - results: { - leantimeProjectId: integrationResults.leantimeProjectId?.toString() || null, - outlineCollectionId: integrationResults.outlineCollectionId?.toString() || null, - rocketChatChannelId: integrationResults.rocketChatChannelId?.toString() || null, - giteaRepositoryUrl: integrationResults.giteaRepositoryUrl || null - } + results: result }; } catch (parseError) { - console.error('Error parsing response:', parseError); - // If parsing fails, return a default success response + console.log('Response is not JSON, treating as workflow trigger confirmation'); return { success: true, results: { @@ -112,13 +70,7 @@ export class N8nService { console.error('Error triggering n8n workflow:', error); return { success: false, - error: error instanceof Error ? error.message : 'Unknown error', - results: { - leantimeProjectId: null, - outlineCollectionId: null, - rocketChatChannelId: null, - giteaRepositoryUrl: null - } + error: error instanceof Error ? error.message : 'Unknown error' }; } } diff --git a/reacct.json b/reacct.json index c06e0d51..217b08bf 100644 --- a/reacct.json +++ b/reacct.json @@ -3,7 +3,8 @@ "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;" + "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", @@ -551,7 +552,7 @@ "httpMethod": "POST", "path": "mission-created", "options": { - "responseData": "allEntries", + "responseData": "lastNodeJson", "responseContentType": "application/json" } },