diff --git a/docker/images/n8n-custom/Dockerfile b/docker/images/n8n-custom/Dockerfile index 05d08a7db5..9136e15b17 100644 --- a/docker/images/n8n-custom/Dockerfile +++ b/docker/images/n8n-custom/Dockerfile @@ -29,7 +29,7 @@ FROM node:14.15-alpine USER root -RUN apk add --update graphicsmagick tzdata tini su-exec +RUN apk add --update graphicsmagick tzdata tini su-exec git WORKDIR /data diff --git a/docker/images/n8n-debian/Dockerfile b/docker/images/n8n-debian/Dockerfile index bfe10565aa..95c79828e4 100644 --- a/docker/images/n8n-debian/Dockerfile +++ b/docker/images/n8n-debian/Dockerfile @@ -6,7 +6,7 @@ RUN if [ -z "$N8N_VERSION" ] ; then echo "The N8N_VERSION argument is missing!" RUN \ apt-get update && \ - apt-get -y install graphicsmagick gosu + apt-get -y install graphicsmagick gosu git # Set a custom user to not have n8n run as root USER root diff --git a/docker/images/n8n-rpi/Dockerfile b/docker/images/n8n-rpi/Dockerfile index 329b7e7dfe..8a3f94838e 100644 --- a/docker/images/n8n-rpi/Dockerfile +++ b/docker/images/n8n-rpi/Dockerfile @@ -6,7 +6,7 @@ RUN if [ -z "$N8N_VERSION" ] ; then echo "The N8N_VERSION argument is missing!" RUN \ apt-get update && \ - apt-get -y install graphicsmagick gosu + apt-get -y install graphicsmagick gosu git RUN npm_config_user=root npm install -g full-icu n8n@${N8N_VERSION} diff --git a/packages/cli/package.json b/packages/cli/package.json index d8790254aa..74c4d914a9 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.122.1", + "version": "0.124.1", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", @@ -106,10 +106,10 @@ "localtunnel": "^2.0.0", "lodash.get": "^4.4.2", "mysql2": "~2.2.0", - "n8n-core": "~0.73.0", - "n8n-editor-ui": "~0.92.0", - "n8n-nodes-base": "~0.119.1", - "n8n-workflow": "~0.60.0", + "n8n-core": "~0.74.0", + "n8n-editor-ui": "~0.94.1", + "n8n-nodes-base": "~0.121.0", + "n8n-workflow": "~0.61.1", "oauth-1.0a": "^2.2.6", "open": "^7.0.0", "pg": "^8.3.0", @@ -118,7 +118,8 @@ "sqlite3": "^5.0.1", "sse-channel": "^3.1.1", "tslib": "1.14.1", - "typeorm": "^0.2.30" + "typeorm": "^0.2.30", + "winston": "^3.3.3" }, "jest": { "transform": { diff --git a/packages/cli/src/ActiveWorkflowRunner.ts b/packages/cli/src/ActiveWorkflowRunner.ts index c323e8373e..66d4ec02e2 100644 --- a/packages/cli/src/ActiveWorkflowRunner.ts +++ b/packages/cli/src/ActiveWorkflowRunner.ts @@ -313,7 +313,6 @@ export class ActiveWorkflowRunner { try { await Db.collections.Webhook?.insert(webhook); - const webhookExists = await workflow.runWebhookMethod('checkExists', webhookData, NodeExecuteFunctions, mode, activation, false); if (webhookExists !== true) { // If webhook does not exist yet create it @@ -341,7 +340,7 @@ export class ActiveWorkflowRunner { errorMessage = error.message; } - throw new Error(errorMessage); + throw error; } } // Save static data! diff --git a/packages/cli/src/Interfaces.ts b/packages/cli/src/Interfaces.ts index 19796bb997..df965c50e7 100644 --- a/packages/cli/src/Interfaces.ts +++ b/packages/cli/src/Interfaces.ts @@ -336,12 +336,45 @@ export interface IPackageVersions { cli: string; } -export interface IPushData { - data: IPushDataExecutionFinished | IPushDataNodeExecuteAfter | IPushDataNodeExecuteBefore | IPushDataTestWebhook | IPushDataConsoleMessage; - type: IPushDataType; -} +export type IPushDataType = IPushData['type']; -export type IPushDataType = 'executionFinished' | 'executionStarted' | 'nodeExecuteAfter' | 'nodeExecuteBefore' | 'sendConsoleMessage' | 'testWebhookDeleted' | 'testWebhookReceived'; +export type IPushData = + | PushDataExecutionFinished + | PushDataExecutionStarted + | PushDataExecuteAfter + | PushDataExecuteBefore + | PushDataConsoleMessage + | PushDataTestWebhook; + +type PushDataExecutionFinished = { + data: IPushDataExecutionFinished; + type: 'executionFinished'; +}; + +type PushDataExecutionStarted = { + data: IPushDataExecutionStarted; + type: 'executionStarted'; +}; + +type PushDataExecuteAfter = { + data: IPushDataNodeExecuteAfter; + type: 'nodeExecuteAfter'; +}; + +type PushDataExecuteBefore = { + data: IPushDataNodeExecuteBefore; + type: 'nodeExecuteBefore'; +}; + +type PushDataConsoleMessage = { + data: IPushDataConsoleMessage; + type: 'sendConsoleMessage'; +}; + +type PushDataTestWebhook = { + data: IPushDataTestWebhook; + type: 'testWebhookDeleted' | 'testWebhookReceived'; +}; export interface IPushDataExecutionFinished { data: IRun; diff --git a/packages/cli/src/ResponseHelper.ts b/packages/cli/src/ResponseHelper.ts index 014589e0b8..465fdb5dde 100644 --- a/packages/cli/src/ResponseHelper.ts +++ b/packages/cli/src/ResponseHelper.ts @@ -93,6 +93,10 @@ export function sendErrorResponse(res: Response, error: ResponseError) { message: 'Unknown error', }; + if (error.name === 'NodeApiError') { + Object.assign(response, error); + } + if (error.errorCode) { response.code = error.errorCode; } diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index b7ce5394b5..f19c88289e 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -673,10 +673,13 @@ class App { await WorkflowHelpers.validateWorkflow(updateData); await Db.collections.Workflow!.update(id, updateData).catch(WorkflowHelpers.throwDuplicateEntryError); - const tablePrefix = config.get('database.tablePrefix'); - await TagHelpers.removeRelations(req.params.id, tablePrefix); - if (tags?.length) { - await TagHelpers.createRelations(req.params.id, tags, tablePrefix); + if (tags) { + const tablePrefix = config.get('database.tablePrefix'); + await TagHelpers.removeRelations(req.params.id, tablePrefix); + + if (tags.length) { + await TagHelpers.createRelations(req.params.id, tags, tablePrefix); + } } // We sadly get nothing back from "update". Neither if it updated a record diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index 570baf536b..9d0478d997 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -387,7 +387,12 @@ function hookFunctionsSave(parentProcessMode?: string): IWorkflowExecuteHooks { } // Leave log message before flatten as that operation increased memory usage a lot and the chance of a crash is highest here - Logger.debug(`Save execution data to database for execution ID ${this.executionId}`, { executionId: this.executionId, workflowId: this.workflowData.id }); + Logger.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 = ResponseHelper.flattenExecutionData(fullExecutionData); @@ -404,6 +409,12 @@ function hookFunctionsSave(parentProcessMode?: string): IWorkflowExecuteHooks { executeErrorWorkflow(this.workflowData, fullRunData, this.mode, this.executionId, this.retryOf); } } catch (error) { + Logger.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); } @@ -438,14 +449,8 @@ function hookFunctionsSaveWorker(): IWorkflowExecuteHooks { } } - // Check config to know if execution should be saved or not - let saveDataErrorExecution = config.get('executions.saveDataOnError') as string; - if (this.workflowData.settings !== undefined) { - saveDataErrorExecution = (this.workflowData.settings.saveDataErrorExecution as string) || saveDataErrorExecution; - } - const workflowDidSucceed = !fullRunData.data.resultData.error; - if (workflowDidSucceed === false && saveDataErrorExecution === 'none') { + if (workflowDidSucceed === false) { executeErrorWorkflow(this.workflowData, fullRunData, this.mode, undefined, this.retryOf); } @@ -473,7 +478,6 @@ function hookFunctionsSaveWorker(): IWorkflowExecuteHooks { if (fullRunData.finished === true && this.retryOf !== undefined) { // If the retry was successful save the reference it on the original execution - // await Db.collections.Execution!.save(executionData as IExecutionFlattedDb); await Db.collections.Execution!.update(this.retryOf, { retrySuccessId: this.executionId }); } } catch (error) { @@ -663,7 +667,7 @@ export async function executeWorkflow(workflowInfo: IExecuteWorkflowInfo, additi } -export function sendMessageToUI(source: string, message: string) { +export function sendMessageToUI(source: string, message: any) { // tslint:disable-line:no-any if (this.sessionId === undefined) { return; } diff --git a/packages/cli/src/WorkflowRunnerProcess.ts b/packages/cli/src/WorkflowRunnerProcess.ts index 109bfda98c..3dbc77cf6c 100644 --- a/packages/cli/src/WorkflowRunnerProcess.ts +++ b/packages/cli/src/WorkflowRunnerProcess.ts @@ -137,7 +137,7 @@ export class WorkflowRunnerProcess { const additionalData = await WorkflowExecuteAdditionalData.getBase(this.data.credentials, undefined, workflowTimeout <= 0 ? undefined : Date.now() + workflowTimeout * 1000); additionalData.hooks = this.getProcessForwardHooks(); - additionalData.sendMessageToUI = async (source: string, message: string) => { + additionalData.sendMessageToUI = async (source: string, message: any) => { // tslint:disable-line:no-any if (workflowRunner.data!.executionMode !== 'manual') { return; } diff --git a/packages/cli/src/databases/mysqldb/migrations/1620826335440-UniqueWorkflowNames.ts b/packages/cli/src/databases/mysqldb/migrations/1620826335440-UniqueWorkflowNames.ts index defe831fd3..3dcf379028 100644 --- a/packages/cli/src/databases/mysqldb/migrations/1620826335440-UniqueWorkflowNames.ts +++ b/packages/cli/src/databases/mysqldb/migrations/1620826335440-UniqueWorkflowNames.ts @@ -13,27 +13,27 @@ export class UniqueWorkflowNames1620826335440 implements MigrationInterface { `); for (const { name } of workflowNames) { - - const duplicates = await queryRunner.query(` + const [duplicatesQuery, parameters] = queryRunner.connection.driver.escapeQueryWithParameters(` SELECT id, name FROM ${tablePrefix}workflow_entity - WHERE name = '${name}' + WHERE name = :name ORDER BY createdAt ASC - `); + `, { name }, {}); + + const duplicates = await queryRunner.query(duplicatesQuery, parameters); if (duplicates.length > 1) { - await Promise.all(duplicates.map(({ id, name }: { id: number; name: string; }, index: number) => { if (index === 0) return Promise.resolve(); - return queryRunner.query(` + const [updateQuery, updateParams] = queryRunner.connection.driver.escapeQueryWithParameters(` UPDATE ${tablePrefix}workflow_entity - SET name = '${name} ${index + 1}' + SET name = :name WHERE id = '${id}' - `); + `, { name: `${name} ${index + 1}`}, {}); + + return queryRunner.query(updateQuery, updateParams); })); - } - } await queryRunner.query('ALTER TABLE `' + tablePrefix + 'workflow_entity` ADD UNIQUE INDEX `IDX_' + tablePrefix + '943d8f922be094eb507cb9a7f9` (`name`)'); diff --git a/packages/cli/src/databases/postgresdb/migrations/1620824779533-UniqueWorkflowNames.ts b/packages/cli/src/databases/postgresdb/migrations/1620824779533-UniqueWorkflowNames.ts index 8c12e24003..ab6adb2995 100644 --- a/packages/cli/src/databases/postgresdb/migrations/1620824779533-UniqueWorkflowNames.ts +++ b/packages/cli/src/databases/postgresdb/migrations/1620824779533-UniqueWorkflowNames.ts @@ -12,34 +12,33 @@ export class UniqueWorkflowNames1620824779533 implements MigrationInterface { tablePrefix = schema + '.' + tablePrefix; } - const workflowNames = await queryRunner.query(` SELECT name FROM ${tablePrefix}workflow_entity `); for (const { name } of workflowNames) { - - const duplicates = await queryRunner.query(` + const [duplicatesQuery, parameters] = queryRunner.connection.driver.escapeQueryWithParameters(` SELECT id, name FROM ${tablePrefix}workflow_entity - WHERE name = '${name}' + WHERE name = :name ORDER BY "createdAt" ASC - `); + `, { name }, {}); + + const duplicates = await queryRunner.query(duplicatesQuery, parameters); if (duplicates.length > 1) { - await Promise.all(duplicates.map(({ id, name }: { id: number; name: string; }, index: number) => { if (index === 0) return Promise.resolve(); - return queryRunner.query(` + const [updateQuery, updateParams] = queryRunner.connection.driver.escapeQueryWithParameters(` UPDATE ${tablePrefix}workflow_entity - SET name = '${name} ${index + 1}' + SET name = :name WHERE id = '${id}' - `); + `, { name: `${name} ${index + 1}`}, {}); + + return queryRunner.query(updateQuery, updateParams); })); - } - } await queryRunner.query(`CREATE UNIQUE INDEX "IDX_${tablePrefixPure}a252c527c4c89237221fe2c0ab" ON ${tablePrefix}workflow_entity ("name") `); diff --git a/packages/cli/src/databases/sqlite/migrations/1620821879465-UniqueWorkflowNames.ts b/packages/cli/src/databases/sqlite/migrations/1620821879465-UniqueWorkflowNames.ts index 2f38cf35bd..732b080ed0 100644 --- a/packages/cli/src/databases/sqlite/migrations/1620821879465-UniqueWorkflowNames.ts +++ b/packages/cli/src/databases/sqlite/migrations/1620821879465-UniqueWorkflowNames.ts @@ -13,27 +13,27 @@ export class UniqueWorkflowNames1620821879465 implements MigrationInterface { `); for (const { name } of workflowNames) { - - const duplicates = await queryRunner.query(` + const [duplicatesQuery, parameters] = queryRunner.connection.driver.escapeQueryWithParameters(` SELECT id, name FROM "${tablePrefix}workflow_entity" - WHERE name = "${name}" + WHERE name = :name ORDER BY createdAt ASC - `); + `, { name }, {}); + + const duplicates = await queryRunner.query(duplicatesQuery, parameters); if (duplicates.length > 1) { - await Promise.all(duplicates.map(({ id, name }: { id: number; name: string; }, index: number) => { if (index === 0) return Promise.resolve(); - return queryRunner.query(` + const [updateQuery, updateParams] = queryRunner.connection.driver.escapeQueryWithParameters(` UPDATE "${tablePrefix}workflow_entity" - SET name = "${name} ${index + 1}" + SET name = :name WHERE id = '${id}' - `); + `, { name: `${name} ${index + 1}`}, {}); + + return queryRunner.query(updateQuery, updateParams); })); - } - } await queryRunner.query(`CREATE UNIQUE INDEX "IDX_${tablePrefix}943d8f922be094eb507cb9a7f9" ON "${tablePrefix}workflow_entity" ("name") `); diff --git a/packages/core/package.json b/packages/core/package.json index 5924681d11..949103dee5 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "0.73.0", + "version": "0.74.0", "description": "Core functionality of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", @@ -47,7 +47,7 @@ "file-type": "^14.6.2", "lodash.get": "^4.4.2", "mime-types": "^2.1.27", - "n8n-workflow": "~0.60.0", + "n8n-workflow": "~0.61.0", "oauth-1.0a": "^2.2.6", "p-cancelable": "^2.0.0", "request": "^2.88.2", diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 5b5698bf18..218eb097d4 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -749,7 +749,7 @@ export function getExecuteFunctions(workflow: Workflow, runExecutionData: IRunEx return workflow.getStaticData(type, node); }, prepareOutputData: NodeHelpers.prepareOutputData, - sendMessageToUI(message: string): void { + sendMessageToUI(message: any): void { // tslint:disable-line:no-any if (mode !== 'manual') { return; } diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 085fbfe03d..ce692a13f3 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "0.92.0", + "version": "0.94.1", "description": "Workflow Editor UI for n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", @@ -67,7 +67,7 @@ "lodash.debounce": "^4.0.8", "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", - "n8n-workflow": "~0.60.0", + "n8n-workflow": "~0.61.0", "node-sass": "^4.12.0", "normalize-wheel": "^1.0.1", "prismjs": "^1.17.1", diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index b674cbf730..3dab3901ee 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -355,12 +355,45 @@ export interface IExecutionDeleteFilter { ids?: string[]; } -export interface IPushData { - data: IPushDataExecutionFinished | IPushDataNodeExecuteAfter | IPushDataNodeExecuteBefore | IPushDataTestWebhook | IPushDataConsoleMessage; - type: IPushDataType; -} +export type IPushDataType = IPushData['type']; -export type IPushDataType = 'executionFinished' | 'executionStarted' | 'nodeExecuteAfter' | 'nodeExecuteBefore' | 'sendConsoleMessage' | 'testWebhookDeleted' | 'testWebhookReceived'; +export type IPushData = + | PushDataExecutionFinished + | PushDataExecutionStarted + | PushDataExecuteAfter + | PushDataExecuteBefore + | PushDataConsoleMessage + | PushDataTestWebhook; + +type PushDataExecutionFinished = { + data: IPushDataExecutionFinished; + type: 'executionFinished'; +}; + +type PushDataExecutionStarted = { + data: IPushDataExecutionStarted; + type: 'executionStarted'; +}; + +type PushDataExecuteAfter = { + data: IPushDataNodeExecuteAfter; + type: 'nodeExecuteAfter'; +}; + +type PushDataExecuteBefore = { + data: IPushDataNodeExecuteBefore; + type: 'nodeExecuteBefore'; +}; + +type PushDataConsoleMessage = { + data: IPushDataConsoleMessage; + type: 'sendConsoleMessage'; +}; + +type PushDataTestWebhook = { + data: IPushDataTestWebhook; + type: 'testWebhookDeleted' | 'testWebhookReceived'; +}; export interface IPushDataExecutionStarted { executionId: string; diff --git a/packages/editor-ui/src/api/helpers.ts b/packages/editor-ui/src/api/helpers.ts index ea4d16e10c..df3f5d8ddd 100644 --- a/packages/editor-ui/src/api/helpers.ts +++ b/packages/editor-ui/src/api/helpers.ts @@ -68,9 +68,14 @@ export async function makeRestApiRequest(context: IRestApiContext, method: Metho const errorResponseData = error.response.data; if (errorResponseData !== undefined && errorResponseData.message !== undefined) { + if (errorResponseData.name === 'NodeApiError') { + errorResponseData.httpStatusCode = error.response.status; + throw errorResponseData; + } + throw new ResponseError(errorResponseData.message, {errorCode: errorResponseData.code, httpStatusCode: error.response.status, stack: errorResponseData.stack}); } throw error; } -} \ No newline at end of file +} diff --git a/packages/editor-ui/src/components/Error/NodeViewError.vue b/packages/editor-ui/src/components/Error/NodeViewError.vue index 2c8cc2f056..ccb5532463 100644 --- a/packages/editor-ui/src/components/Error/NodeViewError.vue +++ b/packages/editor-ui/src/components/Error/NodeViewError.vue @@ -37,10 +37,11 @@ Data below may contain sensitive information. Proceed with caution when sharing.
- + + + The exact cause can sadly not displayed right now as the returned data is too large. +
@@ -67,13 +71,14 @@