From 0afa69d8cafcb90efd7062c8d597ee5ca0ff875d Mon Sep 17 00:00:00 2001 From: alma Date: Sun, 11 May 2025 16:26:29 +0200 Subject: [PATCH] n8n --- .env | 1 + Missions.json | 420 +++++++++++++++++++ app/api/missions/route.ts | 145 +++---- components/missions/missions-admin-panel.tsx | 103 +---- lib/services/n8n-service.ts | 40 ++ 5 files changed, 545 insertions(+), 164 deletions(-) create mode 100644 Missions.json create mode 100644 lib/services/n8n-service.ts diff --git a/.env b/.env index ae3786d4..174af07f 100644 --- a/.env +++ b/.env @@ -103,3 +103,4 @@ MICROSOFT_CLIENT_ID="afaffea5-4e10-462a-aa64-e73baf642c57" MICROSOFT_CLIENT_SECRET="eIx8Q~N3ZnXTjTsVM3ECZio4G7t.BO6AYlD1-b2h" MICROSOFT_REDIRECT_URI="https://lab.slm-lab.net/ms" MICROSOFT_TENANT_ID="cb4281a9-4a3e-4ff5-9a85-8425dd04e2b2" +N8N_WEBHOOK_URL=https://brain.slm-lab.net/webhook-test/mission-created diff --git a/Missions.json b/Missions.json new file mode 100644 index 00000000..d89d86b8 --- /dev/null +++ b/Missions.json @@ -0,0 +1,420 @@ +{ + "name": "Missions", + "nodes": [ + { + "parameters": { + "jsCode": "// Process and sanitize mission data\nconst missionData = $input.item.json;\n\n// Sanitize mission name for use in various APIs\nconst sanitizeName = (name) => {\n if (!name || typeof name !== 'string') return 'unnamed-mission';\n\n return name.toLowerCase()\n .replace(/[^\\w\\s-]/g, '') // Remove special characters\n .replace(/\\s+/g, '-') // Replace whitespace with hyphens\n .trim();\n};\n\n// Format dates for Leantime API (YYYY-MM-DD)\nconst formatDate = (date) => {\n if (!date) return '';\n const d = new Date(date);\n return d.toISOString().split('T')[0];\n};\n\n// Construct processed mission data\nconst output = {\n missionOriginal: missionData,\n missionProcessed: {\n name: missionData?.name || 'Unnamed Mission',\n sanitizedName: sanitizeName(missionData?.name),\n intention: missionData?.intention || '',\n description: missionData?.intention || 'Mission documentation',\n startDate: formatDate(new Date()),\n endDate: formatDate(new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)), // +30 days\n missionType: missionData?.missionType || 'default',\n guardians: missionData?.guardians || {},\n volunteers: missionData?.volunteers || [],\n profils: missionData?.profils || [],\n services: missionData?.services || [],\n clientId: missionData?.missionType === 'interne' ? 1 : 2,\n rocketChatUsernames: []\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://brain.slm-lab.net/webhook-test/mission-created\"\n }\n};\n\n// Populate RocketChat usernames from guardians and volunteers\nif (missionData?.guardians) {\n for (const role in missionData.guardians) {\n const user = missionData.guardians[role];\n if (user) output.missionProcessed.rocketChatUsernames.push(user);\n }\n}\n\nif (Array.isArray(missionData?.volunteers)) {\n output.missionProcessed.rocketChatUsernames.push(...missionData.volunteers);\n}\n\n// Deduplicate usernames\noutput.missionProcessed.rocketChatUsernames = [...new Set(output.missionProcessed.rocketChatUsernames)];\n\nreturn output;\n" + }, + "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.sanitizedName }}" + }, + { + "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": {} + }, + "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.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.sanitizedName }}" + }, + { + "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": "73934ba8-8ea0-4f15-bf5e-3aa2a64ae57f", + "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.MISSION_API_URL+ '/api/missions' }}", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "Content-Type", + "value": "application/json" + } + ] + }, + "sendBody": true, + "bodyParameters": { + "parameters": [ + { + "name": "body", + "value": "={{ {\n \"name\": $node[\"Process Mission Data\"].json.missionOriginal.name,\n \"logo\": $node[\"Process Mission Data\"].json.missionOriginal.logo || \"\",\n \"oddScope\": $node[\"Process Mission Data\"].json.missionOriginal.oddScope || [],\n \"niveau\": $node[\"Process Mission Data\"].json.missionOriginal.niveau || \"\",\n \"intention\": $node[\"Process Mission Data\"].json.missionOriginal.intention || \"\",\n \"missionType\": $node[\"Process Mission Data\"].json.missionOriginal.missionType || \"\",\n \"donneurDOrdre\": $node[\"Process Mission Data\"].json.missionOriginal.donneurDOrdre || \"\",\n \"projection\": $node[\"Process Mission Data\"].json.missionOriginal.projection || \"\",\n \"services\": $node[\"Process Mission Data\"].json.missionOriginal.services || [],\n \"participation\": $node[\"Process Mission Data\"].json.missionOriginal.participation || \"\",\n \"profils\": $node[\"Process Mission Data\"].json.missionOriginal.profils || [],\n \"guardians\": $node[\"Process Mission Data\"].json.missionOriginal.guardians || {\n \"gardien-temps\": \"\",\n \"gardien-parole\": \"\",\n \"gardien-memoire\": \"\"\n },\n \"volunteers\": $node[\"Process Mission Data\"].json.missionOriginal.volunteers || [],\n \"integrations\": {\n \"gitRepoUrl\": $node[\"Create Git Repository\"].json.html_url || \"\",\n \"leantimeProjectId\": $node[\"Create Leantime Project\"].json.result || \"\",\n \"rocketChatChannelId\": $node[\"Create RocketChat Channel\"].json.channel._id || \"\",\n \"documentationCollectionId\": $node[\"Create Documentation Collection\"].json.id || \"\"\n }\n} }}" + } + ] + }, + "options": {} + }, + "name": "Save Mission To API", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 3, + "position": [ + 660, + 560 + ], + "id": "f74f61e2-f487-42a5-828b-3c040b6471c6", + "continueOnFail": true + }, + { + "parameters": { + "jsCode": "// Collect all integration results\nconst results = {\n webhook: $input.item.json || {},\n gitRepo: $node[\"Create Git Repository\"].json || { error: 'Failed or not executed' },\n leantimeProject: $node[\"Create Leantime Project\"].json || { error: 'Failed or not executed' },\n rocketChatChannel: $node[\"Create RocketChat Channel\"].json || { error: 'Failed or not executed' },\n docCollection: $node[\"Create Documentation Collection\"].json || { error: 'Failed or not executed' },\n missionApi: $node[\"Save Mission To API\"].json || { error: 'Failed or not executed' }\n};\n\n// Check for errors\nconst errors = [];\n\nif (!results.gitRepo.html_url) {\n errors.push('Git repository creation failed');\n}\n\nif (!results.leantimeProject.result) {\n errors.push('Leantime project creation failed');\n}\n\nif (!results.rocketChatChannel.success) {\n errors.push('RocketChat channel creation failed');\n}\n\nif (!results.docCollection.id) {\n errors.push('Documentation collection creation failed');\n}\n\nif (!results.missionApi.id) {\n errors.push('Mission API registration failed');\n}\n\n// Prepare response\nconst output = {\n success: errors.length === 0,\n errors: errors,\n results: results,\n message: errors.length === 0 ? \n 'Mission integration complete: All systems updated successfully' : \n `Mission integration partial: ${errors.join(', ')}`\n};\n\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": "allEntries" + } + }, + "name": "Mission Created Webhook", + "type": "n8n-nodes-base.webhook", + "typeVersion": 1, + "position": [ + -160, + 560 + ], + "webhookId": "mission-created-webhook", + "id": "11274a5b-b20f-4180-8611-3690dc9a8722" + } + ], + "pinData": {}, + "connections": { + "Process Mission Data": { + "main": [ + [ + { + "node": "Create Leantime Project", + "type": "main", + "index": 0 + }, + { + "node": "Create RocketChat Channel", + "type": "main", + "index": 0 + }, + { + "node": "Create Documentation Collection", + "type": "main", + "index": 0 + }, + { + "node": "Create Git Repository", + "type": "main", + "index": 0 + } + ] + ] + }, + "Create Git Repository": { + "main": [ + [ + { + "node": "Save Mission To API", + "type": "main", + "index": 0 + }, + { + "node": "Create Leantime Project", + "type": "main", + "index": 0 + } + ] + ] + }, + "Create Leantime Project": { + "main": [ + [ + { + "node": "Save Mission To API", + "type": "main", + "index": 0 + } + ] + ] + }, + "Create RocketChat Channel": { + "main": [ + [ + { + "node": "Save Mission To API", + "type": "main", + "index": 0 + } + ] + ] + }, + "Create Documentation Collection": { + "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": false, + "settings": { + "executionOrder": "v1" + }, + "versionId": "ab79ce0c-8778-4103-bfb8-7ccf99bc6ea6", + "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 66c100e2..bc5da7cc 100644 --- a/app/api/missions/route.ts +++ b/app/api/missions/route.ts @@ -5,6 +5,7 @@ import { prisma } from '@/lib/prisma'; import { getPublicUrl } from '@/lib/s3'; import { S3_CONFIG } from '@/lib/s3'; import { IntegrationService } from '@/lib/services/integration-service'; +import { N8nService } from '@/lib/services/n8n-service'; // Helper function to check authentication async function checkAuth(request: Request) { @@ -157,94 +158,98 @@ export async function POST(request: Request) { } }, { status: 400 }); } - - // Wrap the mission creation and integration in a transaction - const result = await prisma.$transaction(async (tx: any) => { - // Create the mission - const mission = await tx.mission.create({ - data: { - name, - logo, - oddScope: oddScope || [], - niveau, - intention, - missionType, - donneurDOrdre, - projection, - services: services || [], - participation, - profils: profils || [], - creatorId: userId - } - }); - - // Add guardians if provided - if (guardians) { - const guardianRoles = ['gardien-temps', 'gardien-parole', 'gardien-memoire']; - const guardianEntries = Object.entries(guardians) - .filter(([role, userId]) => guardianRoles.includes(role) && userId) - .map(([role, userId]) => ({ - role, - userId: userId as string, - missionId: mission.id - })); - - if (guardianEntries.length > 0) { - await tx.missionUser.createMany({ - data: guardianEntries - }); - } + + // Create the mission in the database first + const mission = await prisma.mission.create({ + data: { + name, + logo, + oddScope: oddScope || [], + niveau, + intention, + missionType, + donneurDOrdre, + projection, + services: services || [], + participation, + profils: profils || [], + creatorId: userId } - - // Add volunteers if provided - if (volunteers && volunteers.length > 0) { - const volunteerEntries = volunteers.map((userId: string) => ({ - role: 'volontaire', - userId, + }); + + // Add guardians if provided + if (guardians) { + const guardianRoles = ['gardien-temps', 'gardien-parole', 'gardien-memoire']; + const guardianEntries = Object.entries(guardians) + .filter(([role, userId]) => guardianRoles.includes(role) && userId) + .map(([role, userId]) => ({ + role, + userId: userId as string, missionId: mission.id })); - - await tx.missionUser.createMany({ - data: volunteerEntries + + if (guardianEntries.length > 0) { + await prisma.missionUser.createMany({ + data: guardianEntries }); } - - return mission; - }); + } + // Add volunteers if provided + if (volunteers && volunteers.length > 0) { + const volunteerEntries = volunteers.map((userId: string) => ({ + role: 'volontaire', + userId, + missionId: mission.id + })); + + await prisma.missionUser.createMany({ + data: volunteerEntries + }); + } + try { - // Initialize external integrations after transaction completes - const integrationService = new IntegrationService(); - const integrationResult = await integrationService.setupIntegrationsForMission(result.id); - - if (!integrationResult.success) { - // If integration failed, the mission was already deleted in the integration service - return NextResponse.json({ - error: 'Failed to set up external services', - details: integrationResult.error - }, { status: 500 }); + // Trigger the n8n workflow + const n8nService = new N8nService(); + const workflowResult = await n8nService.createMission({ + ...body, + missionId: mission.id, + creatorId: userId + }); + + // Update mission with integration results + if (workflowResult.results) { + await prisma.mission.update({ + where: { id: mission.id }, + data: { + giteaRepositoryUrl: workflowResult.results.gitRepo?.html_url, + leantimeProjectId: workflowResult.results.leantimeProject?.result?.toString(), + rocketChatChannelId: workflowResult.results.rocketChatChannel?.channel?._id, + outlineCollectionId: workflowResult.results.docCollection?.id + } + }); } - + return NextResponse.json({ success: true, mission: { - id: result.id, - name: result.name, - createdAt: result.createdAt + id: mission.id, + name: mission.name, + createdAt: mission.createdAt }, - integrations: { + workflow: { status: 'success', - data: integrationResult.data + data: workflowResult } }); - } catch (integrationError) { - // If there's any unhandled error, delete the mission and report failure - console.error('Integration error:', integrationError); - await (prisma as any).mission.delete({ where: { id: result.id } }); + } catch (workflowError) { + // If workflow fails, delete the mission and report failure + console.error('Workflow error:', workflowError); + await prisma.mission.delete({ where: { id: mission.id } }); return NextResponse.json({ error: 'Failed to set up external services', - details: integrationError instanceof Error ? integrationError.message : String(integrationError) + details: workflowError instanceof Error ? workflowError.message : String(workflowError) }, { status: 500 }); } } catch (error) { diff --git a/components/missions/missions-admin-panel.tsx b/components/missions/missions-admin-panel.tsx index 0e2f86f8..101d1b83 100644 --- a/components/missions/missions-admin-panel.tsx +++ b/components/missions/missions-admin-panel.tsx @@ -417,103 +417,18 @@ export function MissionsAdminPanel() { const data = await response.json(); - const newMissionId = data.mission?.id; - if (!newMissionId) { - throw new Error('Failed to get new mission ID'); - } - - // Upload logo if selected - if (selectedLogoFile) { - console.log('Uploading logo for new mission:', newMissionId); - - const logoFormData = new FormData(); - logoFormData.append('file', selectedLogoFile); - logoFormData.append('missionId', newMissionId); - logoFormData.append('type', 'logo'); - - const logoResponse = await fetch('/api/missions/upload', { - method: 'POST', - body: logoFormData + // Check workflow status + if (data.workflow?.status === 'success') { + toast({ + title: "Mission créée avec succès", + description: "Tous les gardiens ont été assignés et la mission a été enregistrée.", }); - if (!logoResponse.ok) { - console.error('Failed to upload logo - Status:', logoResponse.status, logoResponse.statusText); - try { - const errorJson = await logoResponse.json().catch(() => null); - if (errorJson) { - console.error('Error details:', errorJson); - } else { - console.error('No JSON error details available - Response may be empty'); - // Try to get error details from headers - const headers = Object.fromEntries(logoResponse.headers.entries()); - console.error('Response headers:', headers); - } - } catch (parseError) { - console.error('Error parsing response:', parseError); - } - // Continue with mission creation even if logo upload fails - } else { - console.log('Logo uploaded successfully'); - } + // Redirect to missions list + router.push('/missions'); + } else { + throw new Error(data.workflow?.data?.message || 'Workflow execution failed'); } - - // Upload all attachments if there are any - if (selectedAttachments.length > 0) { - console.log(`Uploading ${selectedAttachments.length} attachments for new mission:`, newMissionId); - - const uploadPromises = selectedAttachments.map(async (file) => { - const attachmentFormData = new FormData(); - attachmentFormData.append('file', file); - attachmentFormData.append('missionId', newMissionId); - attachmentFormData.append('type', 'attachment'); - - try { - const attachmentResponse = await fetch('/api/missions/upload', { - method: 'POST', - body: attachmentFormData - }); - - if (!attachmentResponse.ok) { - console.error(`Failed to upload attachment ${file.name} - Status:`, attachmentResponse.status, attachmentResponse.statusText); - try { - const errorJson = await attachmentResponse.json().catch(() => null); - if (errorJson) { - console.error('Error details:', errorJson); - } else { - console.error('No JSON error details available - Response may be empty'); - // Try to get error details from headers - const headers = Object.fromEntries(attachmentResponse.headers.entries()); - console.error('Response headers:', headers); - } - } catch (parseError) { - console.error('Error parsing response:', parseError); - } - return false; - } else { - console.log(`Attachment ${file.name} uploaded successfully`); - return true; - } - } catch (error) { - console.error(`Error uploading attachment ${file.name}:`, error); - return false; - } - }); - - // Wait for all attachments to upload - const results = await Promise.allSettled(uploadPromises); - const successCount = results.filter(r => r.status === 'fulfilled' && r.value === true).length; - - console.log(`Uploaded ${successCount} of ${selectedAttachments.length} attachments`); - } - - toast({ - title: "Mission créée avec succès", - description: "Tous les gardiens ont été assignés et la mission a été enregistrée.", - }); - - // Redirect to missions list - router.push('/missions'); - } catch (error) { console.error('Error creating mission:', error); toast({ diff --git a/lib/services/n8n-service.ts b/lib/services/n8n-service.ts new file mode 100644 index 00000000..b4eb29ad --- /dev/null +++ b/lib/services/n8n-service.ts @@ -0,0 +1,40 @@ +import axios from 'axios'; + +export class N8nService { + private webhookUrl: string; + + constructor() { + this.webhookUrl = process.env.N8N_WEBHOOK_URL || 'https://brain.slm-lab.net/webhook-test/mission-created'; + } + + /** + * Trigger the mission creation workflow in n8n + * @param missionData The mission data to process + * @returns The workflow execution result + */ + async createMission(missionData: any): Promise { + try { + console.log('Triggering n8n workflow for mission creation:', { + name: missionData.name, + missionType: missionData.missionType + }); + + const response = await axios.post(this.webhookUrl, missionData, { + headers: { + 'Content-Type': 'application/json' + } + }); + + console.log('n8n workflow response:', response.data); + + if (!response.data.success) { + throw new Error(`Workflow execution failed: ${response.data.message}`); + } + + return response.data; + } catch (error) { + console.error('Error triggering n8n workflow:', error); + throw new Error(`Failed to trigger mission creation workflow: ${error instanceof Error ? error.message : String(error)}`); + } + } +} \ No newline at end of file