UNPKG

n8n

Version:

n8n Workflow Automation Tool

496 lines 23.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getWorkflowWebhooks = getWorkflowWebhooks; exports.executeWebhook = executeWebhook; const typedi_1 = require("typedi"); const get_1 = __importDefault(require("lodash/get")); const promises_1 = require("stream/promises"); const formidable_1 = __importDefault(require("formidable")); const n8n_core_1 = require("n8n-core"); const n8n_workflow_1 = require("n8n-workflow"); const WorkflowHelpers = __importStar(require("../WorkflowHelpers")); const WorkflowRunner_1 = require("../WorkflowRunner"); const WorkflowExecuteAdditionalData = __importStar(require("../WorkflowExecuteAdditionalData")); const ActiveExecutions_1 = require("../ActiveExecutions"); const workflow_statistics_service_1 = require("../services/workflow-statistics.service"); const ownership_service_1 = require("../services/ownership.service"); const middlewares_1 = require("../middlewares"); const Logger_1 = require("../Logger"); const not_found_error_1 = require("../errors/response-errors/not-found.error"); const internal_server_error_1 = require("../errors/response-errors/internal-server.error"); const unprocessable_error_1 = require("../errors/response-errors/unprocessable.error"); function getWorkflowWebhooks(workflow, additionalData, destinationNode, ignoreRestartWebhooks = false) { const returnData = []; let parentNodes; if (destinationNode !== undefined) { parentNodes = workflow.getParentNodes(destinationNode); parentNodes.push(destinationNode); } for (const node of Object.values(workflow.nodes)) { if (parentNodes !== undefined && !parentNodes.includes(node.name)) { continue; } returnData.push.apply(returnData, n8n_workflow_1.NodeHelpers.getNodeWebhooks(workflow, node, additionalData, ignoreRestartWebhooks)); } return returnData; } const normalizeFormData = (values) => { for (const key in values) { const value = values[key]; if (Array.isArray(value) && value.length === 1) { values[key] = value[0]; } } }; async function executeWebhook(workflow, webhookData, workflowData, workflowStartNode, executionMode, pushRef, runExecutionData, executionId, req, res, responseCallback, destinationNode) { const nodeType = workflow.nodeTypes.getByNameAndVersion(workflowStartNode.type, workflowStartNode.typeVersion); if (nodeType === undefined) { const errorMessage = `The type of the webhook node "${workflowStartNode.name}" is not known`; responseCallback(new Error(errorMessage), {}); throw new internal_server_error_1.InternalServerError(errorMessage); } const additionalKeys = { $executionId: executionId, }; let project = undefined; try { project = await typedi_1.Container.get(ownership_service_1.OwnershipService).getWorkflowProjectCached(workflowData.id); } catch (error) { throw new not_found_error_1.NotFoundError('Cannot find workflow'); } const additionalData = await WorkflowExecuteAdditionalData.getBase(); if (executionId) { additionalData.executionId = executionId; } const responseMode = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription.responseMode, executionMode, additionalKeys, undefined, 'onReceived'); const responseCode = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription.responseCode, executionMode, additionalKeys, undefined, 200); const responseData = workflow.expression.getComplexParameterValue(workflowStartNode, webhookData.webhookDescription.responseData, executionMode, additionalKeys, undefined, 'firstEntryJson'); if (!['onReceived', 'lastNode', 'responseNode'].includes(responseMode)) { const errorMessage = `The response mode '${responseMode}' is not valid!`; responseCallback(new Error(errorMessage), {}); throw new internal_server_error_1.InternalServerError(errorMessage); } additionalData.httpRequest = req; additionalData.httpResponse = res; let binaryData; const nodeVersion = workflowStartNode.typeVersion; if (nodeVersion === 1) { binaryData = workflow.expression.getSimpleParameterValue(workflowStartNode, '={{$parameter["options"]["binaryData"]}}', executionMode, additionalKeys, undefined, false); } let didSendResponse = false; let runExecutionDataMerge = {}; try { let webhookResultData; if (!binaryData) { const { contentType, encoding } = req; if (contentType === 'multipart/form-data') { const form = (0, formidable_1.default)({ multiples: true, encoding: encoding, }); req.body = await new Promise((resolve) => { form.parse(req, async (_err, data, files) => { normalizeFormData(data); normalizeFormData(files); resolve({ data, files }); }); }); } else { if (nodeVersion > 1) { if ((contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('application/json')) || (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('text/plain')) || (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('application/x-www-form-urlencoded')) || (contentType === null || contentType === void 0 ? void 0 : contentType.endsWith('/xml')) || (contentType === null || contentType === void 0 ? void 0 : contentType.endsWith('+xml'))) { await (0, middlewares_1.parseBody)(req); } } else { await (0, middlewares_1.parseBody)(req); } } } try { webhookResultData = await workflow.runWebhook(webhookData, workflowStartNode, additionalData, n8n_core_1.NodeExecuteFunctions, executionMode, runExecutionData !== null && runExecutionData !== void 0 ? runExecutionData : null); typedi_1.Container.get(workflow_statistics_service_1.WorkflowStatisticsService).emit('nodeFetchedData', { workflowId: workflow.id, node: workflowStartNode, }); } catch (err) { const errorMessage = 'Workflow Webhook Error: Workflow could not be started!'; responseCallback(new Error(errorMessage), {}); didSendResponse = true; runExecutionDataMerge = { resultData: { runData: {}, lastNodeExecuted: workflowStartNode.name, error: { ...err, message: err.message, stack: err.stack, }, }, }; webhookResultData = { noWebhookResponse: true, workflowData: [[{ json: {} }]], }; } const additionalKeys = { $executionId: executionId, }; if (webhookData.webhookDescription.responseHeaders !== undefined) { const responseHeaders = workflow.expression.getComplexParameterValue(workflowStartNode, webhookData.webhookDescription.responseHeaders, executionMode, additionalKeys, undefined, undefined); if (responseHeaders !== undefined && responseHeaders.entries !== undefined) { for (const item of responseHeaders.entries) { res.setHeader(item.name, item.value); } } } if (webhookResultData.noWebhookResponse === true && !didSendResponse) { responseCallback(null, { noWebhookResponse: true, }); didSendResponse = true; } if (webhookResultData.workflowData === undefined) { if (webhookResultData.webhookResponse !== undefined) { if (!didSendResponse) { responseCallback(null, { data: webhookResultData.webhookResponse, responseCode, }); didSendResponse = true; } } else { if (!didSendResponse) { responseCallback(null, { data: { message: 'Webhook call received', }, responseCode, }); didSendResponse = true; } } return; } if (responseMode === 'onReceived' && !didSendResponse) { if (responseData === 'noData') { responseCallback(null, { responseCode, }); } else if (responseData) { responseCallback(null, { data: responseData, responseCode, }); } else if (webhookResultData.webhookResponse !== undefined) { responseCallback(null, { data: webhookResultData.webhookResponse, responseCode, }); } else { responseCallback(null, { data: { message: 'Workflow was started', }, responseCode, }); } didSendResponse = true; } const nodeExecutionStack = []; nodeExecutionStack.push({ node: workflowStartNode, data: { main: webhookResultData.workflowData, }, source: null, }); runExecutionData = runExecutionData || { startData: {}, resultData: { runData: {}, }, executionData: { contextData: {}, nodeExecutionStack, waitingExecution: {}, }, }; if (destinationNode && runExecutionData.startData) { runExecutionData.startData.destinationNode = destinationNode; } if (executionId !== undefined) { runExecutionData.executionData.nodeExecutionStack[0].data.main = webhookResultData.workflowData; } if (Object.keys(runExecutionDataMerge).length !== 0) { Object.assign(runExecutionData, runExecutionDataMerge); } let pinData; const usePinData = executionMode === 'manual'; if (usePinData) { pinData = workflowData.pinData; runExecutionData.resultData.pinData = pinData; } const runData = { executionMode, executionData: runExecutionData, pushRef, workflowData, pinData, projectId: project === null || project === void 0 ? void 0 : project.id, }; let responsePromise; if (responseMode === 'responseNode') { responsePromise = await (0, n8n_workflow_1.createDeferredPromise)(); responsePromise .promise() .then(async (response) => { var _a; if (didSendResponse) { return; } const binaryData = (_a = response.body) === null || _a === void 0 ? void 0 : _a.binaryData; if (binaryData === null || binaryData === void 0 ? void 0 : binaryData.id) { res.header(response.headers); const stream = await typedi_1.Container.get(n8n_core_1.BinaryDataService).getAsStream(binaryData.id); stream.pipe(res, { end: false }); await (0, promises_1.finished)(stream); responseCallback(null, { noWebhookResponse: true }); } else if (Buffer.isBuffer(response.body)) { res.header(response.headers); res.end(response.body); responseCallback(null, { noWebhookResponse: true }); } else { const headers = response.headers; let responseCode = response.statusCode; let data = response.body; if (nodeType.description.name === 'formTrigger' && headers.location && String(responseCode).startsWith('3')) { responseCode = 200; data = { redirectURL: headers.location, }; headers.location = undefined; } responseCallback(null, { data, headers, responseCode, }); } process.nextTick(() => res.end()); didSendResponse = true; }) .catch(async (error) => { n8n_workflow_1.ErrorReporterProxy.error(error); typedi_1.Container.get(Logger_1.Logger).error(`Error with Webhook-Response for execution "${executionId}": "${error.message}"`, { executionId, workflowId: workflow.id }); }); } executionId = await typedi_1.Container.get(WorkflowRunner_1.WorkflowRunner).run(runData, true, !didSendResponse, executionId, responsePromise); typedi_1.Container.get(Logger_1.Logger).verbose(`Started execution of workflow "${workflow.name}" from webhook with execution ID ${executionId}`, { executionId }); if (!didSendResponse) { const executePromise = typedi_1.Container.get(ActiveExecutions_1.ActiveExecutions).getPostExecutePromise(executionId); executePromise .then(async (data) => { if (data === undefined) { if (!didSendResponse) { responseCallback(null, { data: { message: 'Workflow executed successfully but no data was returned', }, responseCode, }); didSendResponse = true; } return undefined; } if (usePinData) { data.data.resultData.pinData = pinData; } const returnData = WorkflowHelpers.getDataLastExecutedNodeData(data); if (data.data.resultData.error || (returnData === null || returnData === void 0 ? void 0 : returnData.error) !== undefined) { if (!didSendResponse) { responseCallback(null, { data: { message: 'Error in workflow', }, responseCode: 500, }); } didSendResponse = true; return data; } if (responseMode === 'responseNode' && responsePromise) { await Promise.allSettled([responsePromise.promise()]); return undefined; } if (returnData === undefined) { if (!didSendResponse) { responseCallback(null, { data: { message: 'Workflow executed successfully but the last node did not return any data', }, responseCode, }); } didSendResponse = true; return data; } const additionalKeys = { $executionId: executionId, }; if (!didSendResponse) { let data; if (responseData === 'firstEntryJson') { if (returnData.data.main[0][0] === undefined) { responseCallback(new Error('No item to return got found'), {}); didSendResponse = true; return undefined; } data = returnData.data.main[0][0].json; const responsePropertyName = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription.responsePropertyName, executionMode, additionalKeys, undefined, undefined); if (responsePropertyName !== undefined) { data = (0, get_1.default)(data, responsePropertyName); } const responseContentType = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription.responseContentType, executionMode, additionalKeys, undefined, undefined); if (responseContentType !== undefined) { res.setHeader('Content-Type', responseContentType); if (data !== null && data !== undefined && ['Buffer', 'String'].includes(data.constructor.name)) { res.end(data); } else { res.end(JSON.stringify(data)); } responseCallback(null, { noWebhookResponse: true, }); didSendResponse = true; } } else if (responseData === 'firstEntryBinary') { data = returnData.data.main[0][0]; if (data === undefined) { responseCallback(new Error('No item was found to return'), {}); didSendResponse = true; return undefined; } if (data.binary === undefined) { responseCallback(new Error('No binary data was found to return'), {}); didSendResponse = true; return undefined; } const responseBinaryPropertyName = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription.responseBinaryPropertyName, executionMode, additionalKeys, undefined, 'data'); if (responseBinaryPropertyName === undefined && !didSendResponse) { responseCallback(new Error("No 'responseBinaryPropertyName' is set"), {}); didSendResponse = true; } const binaryData = data.binary[responseBinaryPropertyName]; if (binaryData === undefined && !didSendResponse) { responseCallback(new Error(`The binary property '${responseBinaryPropertyName}' which should be returned does not exist`), {}); didSendResponse = true; } if (!didSendResponse) { res.setHeader('Content-Type', binaryData.mimeType); if (binaryData.id) { const stream = await typedi_1.Container.get(n8n_core_1.BinaryDataService).getAsStream(binaryData.id); stream.pipe(res, { end: false }); await (0, promises_1.finished)(stream); } else { res.write(Buffer.from(binaryData.data, n8n_workflow_1.BINARY_ENCODING)); } responseCallback(null, { noWebhookResponse: true, }); process.nextTick(() => res.end()); } } else if (responseData === 'noData') { data = undefined; } else { data = []; for (const entry of returnData.data.main[0]) { data.push(entry.json); } } if (!didSendResponse) { responseCallback(null, { data, responseCode, }); } } didSendResponse = true; return data; }) .catch((e) => { if (!didSendResponse) { responseCallback(new n8n_workflow_1.ApplicationError('There was a problem executing the workflow', { level: 'warning', cause: e, }), {}); } throw new internal_server_error_1.InternalServerError(e.message); }); } return executionId; } catch (e) { const error = e instanceof unprocessable_error_1.UnprocessableRequestError ? e : new n8n_workflow_1.ApplicationError('There was a problem executing the workflow', { level: 'warning', cause: e, }); if (didSendResponse) throw error; responseCallback(error, {}); return; } } //# sourceMappingURL=WebhookHelpers.js.map