n8n
This commit is contained in:
parent
2afc3b5a28
commit
0afa69d8ca
1
.env
1
.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
|
||||
|
||||
420
Missions.json
Normal file
420
Missions.json
Normal file
@ -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": []
|
||||
}
|
||||
@ -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) {
|
||||
@ -158,93 +159,97 @@ 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);
|
||||
// Trigger the n8n workflow
|
||||
const n8nService = new N8nService();
|
||||
const workflowResult = await n8nService.createMission({
|
||||
...body,
|
||||
missionId: mission.id,
|
||||
creatorId: userId
|
||||
});
|
||||
|
||||
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 });
|
||||
// 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) {
|
||||
|
||||
@ -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({
|
||||
|
||||
40
lib/services/n8n-service.ts
Normal file
40
lib/services/n8n-service.ts
Normal file
@ -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<any> {
|
||||
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)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user