n8n
Version:
n8n Workflow Automation Tool
630 lines • 30 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getWorkflowHooksMain = exports.getWorkflowHooksWorkerMain = exports.getWorkflowHooksWorkerExecuter = exports.getWorkflowHooksIntegrated = exports.getBase = exports.sendMessageToUI = exports.executeWorkflow = exports.getWorkflowData = exports.getRunData = exports.hookFunctionsPreExecute = void 0;
const n8n_core_1 = require("n8n-core");
const n8n_workflow_1 = require("n8n-workflow");
const typeorm_1 = require("typeorm");
const DateUtils_1 = require("typeorm/util/DateUtils");
const config = require("../config");
const _1 = require(".");
const ERROR_TRIGGER_TYPE = config.get('nodes.errorTriggerType');
function executeErrorWorkflow(workflowData, fullRunData, mode, executionId, retryOf) {
let pastExecutionUrl;
if (executionId !== undefined) {
pastExecutionUrl = `${_1.WebhookHelpers.getWebhookBaseUrl()}execution/${executionId}`;
}
if (fullRunData.data.resultData.error !== undefined) {
const workflowErrorData = {
execution: {
id: executionId,
url: pastExecutionUrl,
error: fullRunData.data.resultData.error,
lastNodeExecuted: fullRunData.data.resultData.lastNodeExecuted,
mode,
retryOf,
},
workflow: {
id: workflowData.id !== undefined ? workflowData.id.toString() : undefined,
name: workflowData.name,
},
};
if (workflowData.settings !== undefined &&
workflowData.settings.errorWorkflow &&
!(mode === 'error' &&
workflowData.id &&
workflowData.settings.errorWorkflow.toString() === workflowData.id.toString())) {
n8n_workflow_1.LoggerProxy.verbose(`Start external error workflow`, {
executionId,
errorWorkflowId: workflowData.settings.errorWorkflow.toString(),
workflowId: workflowData.id,
});
_1.WorkflowHelpers.executeErrorWorkflow(workflowData.settings.errorWorkflow, workflowErrorData);
}
else if (mode !== 'error' &&
workflowData.id !== undefined &&
workflowData.nodes.some((node) => node.type === ERROR_TRIGGER_TYPE)) {
n8n_workflow_1.LoggerProxy.verbose(`Start internal error workflow`, { executionId, workflowId: workflowData.id });
_1.WorkflowHelpers.executeErrorWorkflow(workflowData.id.toString(), workflowErrorData);
}
}
}
let throttling = false;
function pruneExecutionData() {
if (!throttling) {
n8n_workflow_1.LoggerProxy.verbose('Pruning execution data from database');
throttling = true;
const timeout = config.get('executions.pruneDataTimeout');
const maxAge = config.get('executions.pruneDataMaxAge');
const date = new Date();
date.setHours(date.getHours() - maxAge);
const utcDate = DateUtils_1.DateUtils.mixedDateToUtcDatetimeString(date);
_1.Db.collections
.Execution.delete({ stoppedAt: typeorm_1.LessThanOrEqual(utcDate) })
.then((data) => setTimeout(() => {
throttling = false;
}, timeout * 1000))
.catch((error) => {
throttling = false;
n8n_workflow_1.LoggerProxy.error(`Failed pruning execution data from database for execution ID ${this.executionId} (hookFunctionsSave)`, Object.assign(Object.assign({}, error), { executionId: this.executionId, sessionId: this.sessionId, workflowId: this.workflowData.id }));
});
}
}
function hookFunctionsPush() {
return {
nodeExecuteBefore: [
async function (nodeName) {
if (this.sessionId === undefined) {
return;
}
n8n_workflow_1.LoggerProxy.debug(`Executing hook on node "${nodeName}" (hookFunctionsPush)`, {
executionId: this.executionId,
sessionId: this.sessionId,
workflowId: this.workflowData.id,
});
const pushInstance = _1.Push.getInstance();
pushInstance.send('nodeExecuteBefore', {
executionId: this.executionId,
nodeName,
}, this.sessionId);
},
],
nodeExecuteAfter: [
async function (nodeName, data) {
if (this.sessionId === undefined) {
return;
}
n8n_workflow_1.LoggerProxy.debug(`Executing hook on node "${nodeName}" (hookFunctionsPush)`, {
executionId: this.executionId,
sessionId: this.sessionId,
workflowId: this.workflowData.id,
});
const pushInstance = _1.Push.getInstance();
pushInstance.send('nodeExecuteAfter', {
executionId: this.executionId,
nodeName,
data,
}, this.sessionId);
},
],
workflowExecuteBefore: [
async function () {
n8n_workflow_1.LoggerProxy.debug(`Executing hook (hookFunctionsPush)`, {
executionId: this.executionId,
sessionId: this.sessionId,
workflowId: this.workflowData.id,
});
if (this.sessionId === undefined) {
return;
}
const pushInstance = _1.Push.getInstance();
pushInstance.send('executionStarted', {
executionId: this.executionId,
mode: this.mode,
startedAt: new Date(),
retryOf: this.retryOf,
workflowId: this.workflowData.id,
sessionId: this.sessionId,
workflowName: this.workflowData.name,
}, this.sessionId);
},
],
workflowExecuteAfter: [
async function (fullRunData, newStaticData) {
n8n_workflow_1.LoggerProxy.debug(`Executing hook (hookFunctionsPush)`, {
executionId: this.executionId,
sessionId: this.sessionId,
workflowId: this.workflowData.id,
});
if (this.sessionId === undefined) {
return;
}
const pushRunData = Object.assign(Object.assign({}, fullRunData), { data: Object.assign(Object.assign({}, fullRunData.data), { resultData: Object.assign(Object.assign({}, fullRunData.data.resultData), { runData: {} }) }) });
n8n_workflow_1.LoggerProxy.debug(`Save execution progress to database for execution ID ${this.executionId} `, {
executionId: this.executionId,
workflowId: this.workflowData.id,
});
const sendData = {
executionId: this.executionId,
data: pushRunData,
retryOf: this.retryOf,
};
const pushInstance = _1.Push.getInstance();
pushInstance.send('executionFinished', sendData, this.sessionId);
},
],
};
}
function hookFunctionsPreExecute(parentProcessMode) {
const externalHooks = _1.ExternalHooks();
return {
workflowExecuteBefore: [
async function (workflow) {
await externalHooks.run('workflow.preExecute', [workflow, this.mode]);
},
],
nodeExecuteAfter: [
async function (nodeName, data, executionData) {
if (this.workflowData.settings !== undefined) {
if (this.workflowData.settings.saveExecutionProgress === false) {
return;
}
if (this.workflowData.settings.saveExecutionProgress !== true &&
!config.get('executions.saveExecutionProgress')) {
return;
}
}
else if (!config.get('executions.saveExecutionProgress')) {
return;
}
try {
n8n_workflow_1.LoggerProxy.debug(`Save execution progress to database for execution ID ${this.executionId} `, { executionId: this.executionId, nodeName });
const execution = await _1.Db.collections.Execution.findOne(this.executionId);
if (execution === undefined) {
return;
}
const fullExecutionData = _1.ResponseHelper.unflattenExecutionData(execution);
if (fullExecutionData.finished) {
return;
}
if (fullExecutionData.data === undefined) {
fullExecutionData.data = {
startData: {},
resultData: {
runData: {},
},
executionData: {
contextData: {},
nodeExecutionStack: [],
waitingExecution: {},
},
};
}
if (Array.isArray(fullExecutionData.data.resultData.runData[nodeName])) {
fullExecutionData.data.resultData.runData[nodeName].push(data);
}
else {
fullExecutionData.data.resultData.runData[nodeName] = [data];
}
fullExecutionData.data.executionData = executionData.executionData;
fullExecutionData.data.resultData.lastNodeExecuted = nodeName;
const flattenedExecutionData = _1.ResponseHelper.flattenExecutionData(fullExecutionData);
await _1.Db.collections.Execution.update(this.executionId, flattenedExecutionData);
}
catch (err) {
n8n_workflow_1.LoggerProxy.error(`Failed saving execution progress to database for execution ID ${this.executionId} (hookFunctionsPreExecute, nodeExecuteAfter)`, Object.assign(Object.assign({}, err), { executionId: this.executionId, sessionId: this.sessionId, workflowId: this.workflowData.id }));
}
},
],
};
}
exports.hookFunctionsPreExecute = hookFunctionsPreExecute;
function hookFunctionsSave(parentProcessMode) {
return {
nodeExecuteBefore: [],
nodeExecuteAfter: [],
workflowExecuteBefore: [],
workflowExecuteAfter: [
async function (fullRunData, newStaticData) {
n8n_workflow_1.LoggerProxy.debug(`Executing hook (hookFunctionsSave)`, {
executionId: this.executionId,
workflowId: this.workflowData.id,
});
if (config.get('executions.pruneData')) {
pruneExecutionData.call(this);
}
const isManualMode = [this.mode, parentProcessMode].includes('manual');
try {
if (!isManualMode &&
_1.WorkflowHelpers.isWorkflowIdValid(this.workflowData.id) &&
newStaticData) {
try {
await _1.WorkflowHelpers.saveStaticDataById(this.workflowData.id, newStaticData);
}
catch (e) {
n8n_workflow_1.LoggerProxy.error(`There was a problem saving the workflow with id "${this.workflowData.id}" to save changed staticData: "${e.message}" (hookFunctionsSave)`, { executionId: this.executionId, workflowId: this.workflowData.id });
}
}
let saveManualExecutions = config.get('executions.saveDataManualExecutions');
if (this.workflowData.settings !== undefined &&
this.workflowData.settings.saveManualExecutions !== undefined) {
saveManualExecutions = this.workflowData.settings.saveManualExecutions;
}
if (isManualMode && !saveManualExecutions && !fullRunData.waitTill) {
await _1.Db.collections.Execution.delete(this.executionId);
return;
}
let saveDataErrorExecution = config.get('executions.saveDataOnError');
let saveDataSuccessExecution = config.get('executions.saveDataOnSuccess');
if (this.workflowData.settings !== undefined) {
saveDataErrorExecution =
this.workflowData.settings.saveDataErrorExecution ||
saveDataErrorExecution;
saveDataSuccessExecution =
this.workflowData.settings.saveDataSuccessExecution ||
saveDataSuccessExecution;
}
const workflowDidSucceed = !fullRunData.data.resultData.error;
if ((workflowDidSucceed && saveDataSuccessExecution === 'none') ||
(!workflowDidSucceed && saveDataErrorExecution === 'none')) {
if (!fullRunData.waitTill) {
if (!isManualMode) {
executeErrorWorkflow(this.workflowData, fullRunData, this.mode, undefined, this.retryOf);
}
await _1.Db.collections.Execution.delete(this.executionId);
return;
}
}
const fullExecutionData = {
data: fullRunData.data,
mode: fullRunData.mode,
finished: fullRunData.finished ? fullRunData.finished : false,
startedAt: fullRunData.startedAt,
stoppedAt: fullRunData.stoppedAt,
workflowData: this.workflowData,
waitTill: fullRunData.waitTill,
};
if (this.retryOf !== undefined) {
fullExecutionData.retryOf = this.retryOf.toString();
}
if (this.workflowData.id !== undefined &&
_1.WorkflowHelpers.isWorkflowIdValid(this.workflowData.id.toString())) {
fullExecutionData.workflowId = this.workflowData.id.toString();
}
n8n_workflow_1.LoggerProxy.debug(`Save execution data to database for execution ID ${this.executionId}`, {
executionId: this.executionId,
workflowId: this.workflowData.id,
finished: fullExecutionData.finished,
stoppedAt: fullExecutionData.stoppedAt,
});
const executionData = _1.ResponseHelper.flattenExecutionData(fullExecutionData);
await _1.Db.collections.Execution.update(this.executionId, executionData);
if (fullRunData.finished === true && this.retryOf !== undefined) {
await _1.Db.collections.Execution.update(this.retryOf, {
retrySuccessId: this.executionId,
});
}
if (!isManualMode) {
executeErrorWorkflow(this.workflowData, fullRunData, this.mode, this.executionId, this.retryOf);
}
}
catch (error) {
n8n_workflow_1.LoggerProxy.error(`Failed saving execution data to DB on execution ID ${this.executionId}`, {
executionId: this.executionId,
workflowId: this.workflowData.id,
error,
});
if (!isManualMode) {
executeErrorWorkflow(this.workflowData, fullRunData, this.mode, undefined, this.retryOf);
}
}
},
],
};
}
function hookFunctionsSaveWorker() {
return {
nodeExecuteBefore: [],
nodeExecuteAfter: [],
workflowExecuteBefore: [],
workflowExecuteAfter: [
async function (fullRunData, newStaticData) {
try {
if (_1.WorkflowHelpers.isWorkflowIdValid(this.workflowData.id) && newStaticData) {
try {
await _1.WorkflowHelpers.saveStaticDataById(this.workflowData.id, newStaticData);
}
catch (e) {
n8n_workflow_1.LoggerProxy.error(`There was a problem saving the workflow with id "${this.workflowData.id}" to save changed staticData: "${e.message}" (workflowExecuteAfter)`, { sessionId: this.sessionId, workflowId: this.workflowData.id });
}
}
const workflowDidSucceed = !fullRunData.data.resultData.error;
if (!workflowDidSucceed) {
executeErrorWorkflow(this.workflowData, fullRunData, this.mode, undefined, this.retryOf);
}
const fullExecutionData = {
data: fullRunData.data,
mode: fullRunData.mode,
finished: fullRunData.finished ? fullRunData.finished : false,
startedAt: fullRunData.startedAt,
stoppedAt: fullRunData.stoppedAt,
workflowData: this.workflowData,
waitTill: fullRunData.data.waitTill,
};
if (this.retryOf !== undefined) {
fullExecutionData.retryOf = this.retryOf.toString();
}
if (this.workflowData.id !== undefined &&
_1.WorkflowHelpers.isWorkflowIdValid(this.workflowData.id.toString())) {
fullExecutionData.workflowId = this.workflowData.id.toString();
}
const executionData = _1.ResponseHelper.flattenExecutionData(fullExecutionData);
await _1.Db.collections.Execution.update(this.executionId, executionData);
if (fullRunData.finished === true && this.retryOf !== undefined) {
await _1.Db.collections.Execution.update(this.retryOf, {
retrySuccessId: this.executionId,
});
}
}
catch (error) {
executeErrorWorkflow(this.workflowData, fullRunData, this.mode, undefined, this.retryOf);
}
},
],
};
}
async function getRunData(workflowData, inputData) {
const mode = 'integrated';
const requiredNodeTypes = ['n8n-nodes-base.start'];
let startNode;
for (const node of workflowData.nodes) {
if (requiredNodeTypes.includes(node.type)) {
startNode = node;
break;
}
}
if (startNode === undefined) {
throw new Error(`The workflow does not contain a "Start" node and can so not be executed.`);
}
inputData = inputData || [
{
json: {},
},
];
const nodeExecutionStack = [];
nodeExecutionStack.push({
node: startNode,
data: {
main: [inputData],
},
});
const runExecutionData = {
startData: {},
resultData: {
runData: {},
},
executionData: {
contextData: {},
nodeExecutionStack,
waitingExecution: {},
},
};
const runData = {
executionMode: mode,
executionData: runExecutionData,
workflowData,
};
return runData;
}
exports.getRunData = getRunData;
async function getWorkflowData(workflowInfo) {
if (workflowInfo.id === undefined && workflowInfo.code === undefined) {
throw new Error(`No information about the workflow to execute found. Please provide either the "id" or "code"!`);
}
if (_1.Db.collections.Workflow === null) {
await _1.Db.init();
}
let workflowData;
if (workflowInfo.id !== undefined) {
workflowData = await _1.Db.collections.Workflow.findOne(workflowInfo.id);
if (workflowData === undefined) {
throw new Error(`The workflow with the id "${workflowInfo.id}" does not exist.`);
}
}
else {
workflowData = workflowInfo.code;
}
return workflowData;
}
exports.getWorkflowData = getWorkflowData;
async function executeWorkflow(workflowInfo, additionalData, inputData, parentExecutionId, loadedWorkflowData, loadedRunData) {
var _a;
const externalHooks = _1.ExternalHooks();
await externalHooks.init();
const nodeTypes = _1.NodeTypes();
const workflowData = loadedWorkflowData !== undefined ? loadedWorkflowData : await getWorkflowData(workflowInfo);
const workflowName = workflowData ? workflowData.name : undefined;
const workflow = new n8n_workflow_1.Workflow({
id: workflowInfo.id,
name: workflowName,
nodes: workflowData.nodes,
connections: workflowData.connections,
active: workflowData.active,
nodeTypes,
staticData: workflowData.staticData,
});
const runData = loadedRunData !== undefined ? loadedRunData : await getRunData(workflowData, inputData);
let executionId;
if (parentExecutionId !== undefined) {
executionId = parentExecutionId;
}
else {
executionId =
parentExecutionId !== undefined
? parentExecutionId
: await _1.ActiveExecutions.getInstance().add(runData);
}
let data;
try {
const additionalDataIntegrated = await getBase();
additionalDataIntegrated.hooks = getWorkflowHooksIntegrated(runData.executionMode, executionId, workflowData, { parentProcessMode: additionalData.hooks.mode });
additionalDataIntegrated.executeWorkflow = additionalData.executeWorkflow;
let subworkflowTimeout = additionalData.executionTimeoutTimestamp;
if (((_a = workflowData.settings) === null || _a === void 0 ? void 0 : _a.executionTimeout) !== undefined &&
workflowData.settings.executionTimeout > 0) {
subworkflowTimeout = Math.min(additionalData.executionTimeoutTimestamp || Number.MAX_SAFE_INTEGER, Date.now() + workflowData.settings.executionTimeout * 1000);
}
additionalDataIntegrated.executionTimeoutTimestamp = subworkflowTimeout;
const runExecutionData = runData.executionData;
const workflowExecute = new n8n_core_1.WorkflowExecute(additionalDataIntegrated, runData.executionMode, runExecutionData);
if (parentExecutionId !== undefined) {
return {
startedAt: new Date(),
workflow,
workflowExecute,
};
}
data = await workflowExecute.processRunExecutionData(workflow);
}
catch (error) {
const fullRunData = {
data: {
resultData: {
error,
runData: {},
},
},
finished: false,
mode: 'integrated',
startedAt: new Date(),
stoppedAt: new Date(),
};
const fullExecutionData = {
data: fullRunData.data,
mode: fullRunData.mode,
finished: fullRunData.finished ? fullRunData.finished : false,
startedAt: fullRunData.startedAt,
stoppedAt: fullRunData.stoppedAt,
workflowData,
};
const executionData = _1.ResponseHelper.flattenExecutionData(fullExecutionData);
await _1.Db.collections.Execution.update(executionId, executionData);
throw Object.assign(Object.assign({}, error), { stack: error.stack });
}
await externalHooks.run('workflow.postExecute', [data, workflowData]);
void _1.InternalHooksManager.getInstance().onWorkflowPostExecute(workflowData, data);
if (data.finished === true) {
await _1.ActiveExecutions.getInstance().remove(executionId, data);
const returnData = _1.WorkflowHelpers.getDataLastExecutedNodeData(data);
return returnData.data.main;
}
await _1.ActiveExecutions.getInstance().remove(executionId, data);
const { error } = data.data.resultData;
throw Object.assign(Object.assign({}, error), { stack: error.stack });
}
exports.executeWorkflow = executeWorkflow;
function sendMessageToUI(source, messages) {
if (this.sessionId === undefined) {
return;
}
try {
const pushInstance = _1.Push.getInstance();
pushInstance.send('sendConsoleMessage', {
source: `Node: "${source}"`,
messages,
}, this.sessionId);
}
catch (error) {
n8n_workflow_1.LoggerProxy.warn(`There was a problem sending messsage to UI: ${error.message}`);
}
}
exports.sendMessageToUI = sendMessageToUI;
async function getBase(currentNodeParameters, executionTimeoutTimestamp) {
const urlBaseWebhook = _1.WebhookHelpers.getWebhookBaseUrl();
const timezone = config.get('generic.timezone');
const webhookBaseUrl = urlBaseWebhook + config.get('endpoints.webhook');
const webhookWaitingBaseUrl = urlBaseWebhook + config.get('endpoints.webhookWaiting');
const webhookTestBaseUrl = urlBaseWebhook + config.get('endpoints.webhookTest');
const encryptionKey = await n8n_core_1.UserSettings.getEncryptionKey();
if (encryptionKey === undefined) {
throw new Error('No encryption key got found to decrypt the credentials!');
}
return {
credentialsHelper: new _1.CredentialsHelper(encryptionKey),
encryptionKey,
executeWorkflow,
restApiUrl: urlBaseWebhook + config.get('endpoints.rest'),
timezone,
webhookBaseUrl,
webhookWaitingBaseUrl,
webhookTestBaseUrl,
currentNodeParameters,
executionTimeoutTimestamp,
};
}
exports.getBase = getBase;
function getWorkflowHooksIntegrated(mode, executionId, workflowData, optionalParameters) {
optionalParameters = optionalParameters || {};
const hookFunctions = hookFunctionsSave(optionalParameters.parentProcessMode);
const preExecuteFunctions = hookFunctionsPreExecute(optionalParameters.parentProcessMode);
for (const key of Object.keys(preExecuteFunctions)) {
if (hookFunctions[key] === undefined) {
hookFunctions[key] = [];
}
hookFunctions[key].push.apply(hookFunctions[key], preExecuteFunctions[key]);
}
return new n8n_workflow_1.WorkflowHooks(hookFunctions, mode, executionId, workflowData, optionalParameters);
}
exports.getWorkflowHooksIntegrated = getWorkflowHooksIntegrated;
function getWorkflowHooksWorkerExecuter(mode, executionId, workflowData, optionalParameters) {
optionalParameters = optionalParameters || {};
const hookFunctions = hookFunctionsSaveWorker();
const preExecuteFunctions = hookFunctionsPreExecute(optionalParameters.parentProcessMode);
for (const key of Object.keys(preExecuteFunctions)) {
if (hookFunctions[key] === undefined) {
hookFunctions[key] = [];
}
hookFunctions[key].push.apply(hookFunctions[key], preExecuteFunctions[key]);
}
return new n8n_workflow_1.WorkflowHooks(hookFunctions, mode, executionId, workflowData, optionalParameters);
}
exports.getWorkflowHooksWorkerExecuter = getWorkflowHooksWorkerExecuter;
function getWorkflowHooksWorkerMain(mode, executionId, workflowData, optionalParameters) {
optionalParameters = optionalParameters || {};
const hookFunctions = hookFunctionsPush();
const preExecuteFunctions = hookFunctionsPreExecute(optionalParameters.parentProcessMode);
for (const key of Object.keys(preExecuteFunctions)) {
if (hookFunctions[key] === undefined) {
hookFunctions[key] = [];
}
hookFunctions[key].push.apply(hookFunctions[key], preExecuteFunctions[key]);
}
hookFunctions.nodeExecuteBefore = [];
hookFunctions.nodeExecuteAfter = [];
return new n8n_workflow_1.WorkflowHooks(hookFunctions, mode, executionId, workflowData, optionalParameters);
}
exports.getWorkflowHooksWorkerMain = getWorkflowHooksWorkerMain;
function getWorkflowHooksMain(data, executionId, isMainProcess = false) {
const hookFunctions = hookFunctionsSave();
const pushFunctions = hookFunctionsPush();
for (const key of Object.keys(pushFunctions)) {
if (hookFunctions[key] === undefined) {
hookFunctions[key] = [];
}
hookFunctions[key].push.apply(hookFunctions[key], pushFunctions[key]);
}
if (isMainProcess) {
const preExecuteFunctions = hookFunctionsPreExecute();
for (const key of Object.keys(preExecuteFunctions)) {
if (hookFunctions[key] === undefined) {
hookFunctions[key] = [];
}
hookFunctions[key].push.apply(hookFunctions[key], preExecuteFunctions[key]);
}
}
return new n8n_workflow_1.WorkflowHooks(hookFunctions, data.executionMode, executionId, data.workflowData, {
sessionId: data.sessionId,
retryOf: data.retryOf,
});
}
exports.getWorkflowHooksMain = getWorkflowHooksMain;
//# sourceMappingURL=WorkflowExecuteAdditionalData.js.map