diff --git a/packages/cli/bin/n8n b/packages/cli/bin/n8n new file mode 100755 index 0000000000..742ba816f8 --- /dev/null +++ b/packages/cli/bin/n8n @@ -0,0 +1,42 @@ +#!/usr/bin/env node + +var path = require('path'); // tslint:disable-line:no-var-keyword + +// Make sure that it also find the config folder when it +// did get started from another folder that the root one. +process.env.NODE_CONFIG_DIR = process.env.NODE_CONFIG_DIR || path.join(__dirname, 'config'); + +// Check if version should be displayed +var versionFlags = [ // tslint:disable-line:no-var-keyword + '-v', + '-V', + '--version' +]; +if (versionFlags.includes(process.argv.slice(-1)[0])) { + console.log(require('../package').version); + process.exit(0); +} + +if (process.argv.length === 2) { + // When no command is given choose by default start + process.argv.push('start'); +} + +var command = process.argv[2]; // tslint:disable-line:no-var-keyword + +// Check if the command the user did enter is supported else stop +var supportedCommands = [ // tslint:disable-line:no-var-keyword + 'execute', + 'help', + 'start', +]; + +if (!supportedCommands.includes(command)) { + console.log('\nThe command "' + command + '" is not known!\n'); + process.argv.pop(); + process.argv.push('--help'); +} + +require('@oclif/command').run() +.then(require('@oclif/command/flush')) +.catch(require('@oclif/errors/handle')); diff --git a/packages/cli/bin/n8n.cmd b/packages/cli/bin/n8n.cmd new file mode 100755 index 0000000000..0a983f5069 --- /dev/null +++ b/packages/cli/bin/n8n.cmd @@ -0,0 +1,3 @@ +@echo off + +node "%~dp0\n8n" %* diff --git a/packages/cli/commands/execute.ts b/packages/cli/commands/execute.ts index 7c69435625..448af3de3d 100644 --- a/packages/cli/commands/execute.ts +++ b/packages/cli/commands/execute.ts @@ -1,6 +1,9 @@ -import Vorpal = require('vorpal'); -import { Args } from 'vorpal'; import { promises as fs } from 'fs'; +import { Command, flags } from '@oclif/command'; +import { + UserSettings, +} from "n8n-core"; + import { ActiveExecutions, Db, @@ -13,133 +16,157 @@ import { WorkflowHelpers, WorkflowRunner, } from "../src"; -import { - UserSettings, -} from "n8n-core"; -module.exports = (vorpal: Vorpal) => { - return vorpal - .command('execute') - // @ts-ignore - .description('Executes a given workflow') - .option('--file ', - 'The path to a workflow file to execute') - .option('--id ', - 'The id of the workflow to execute') - .option('\n') - // TODO: Add validation - // .validate((args: Args) => { - // }) - .action(async (args: Args) => { - // Start directly with the init of the database to improve startup time - const startDbInitPromise = Db.init(); +export class Execute extends Command { + static description = '\nExecutes a given workflow'; - // Load all node and credential types - const loadNodesAndCredentials = LoadNodesAndCredentials(); - const loadNodesAndCredentialsPromise = loadNodesAndCredentials.init(); + static examples = [ + `$ n8n execute --id=5`, + `$ n8n execute --file=workflow.json`, + ]; - if (!args.options.id && !args.options.file) { - GenericHelpers.logOutput(`Either option "--id" or "--file" have to be set!`); - return Promise.resolve(); - } + static flags = { + help: flags.help({ char: 'h' }), + file: flags.string({ + description: 'path to a workflow file to execute', + }), + id: flags.string({ + description: 'id of the workflow to execute', + }), + }; - if (args.options.id && args.options.file) { - GenericHelpers.logOutput(`Either "id" or "file" can be set never both!`); - return Promise.resolve(); - } - let workflowId: string | undefined; - let workflowData: IWorkflowBase | undefined = undefined; - if (args.options.file) { - // Path to workflow is given - try { - workflowData = JSON.parse(await fs.readFile(args.options.file, 'utf8')); - } catch (error) { - if (error.code === 'ENOENT') { - GenericHelpers.logOutput(`The file "${args.options.file}" could not be found.`); - return; - } + async run() { + const { flags } = this.parse(Execute); - throw error; - } + // Start directly with the init of the database to improve startup time + const startDbInitPromise = Db.init(); - // Do a basic check if the data in the file looks right - // TODO: Later check with the help of TypeScript data if it is valid or not - if (workflowData === undefined || workflowData.nodes === undefined || workflowData.connections === undefined) { - GenericHelpers.logOutput(`The file "${args.options.file}" does not contain valid workflow data.`); - return; - } - workflowId = workflowData.id!.toString(); - } + // Load all node and credential types + const loadNodesAndCredentials = LoadNodesAndCredentials(); + const loadNodesAndCredentialsPromise = loadNodesAndCredentials.init(); - // Wait till the database is ready - await startDbInitPromise; + if (!flags.id && !flags.file) { + GenericHelpers.logOutput(`Either option "--id" or "--file" have to be set!`); + return; + } - if (args.options.id) { - // Id of workflow is given - workflowId = args.options.id; - workflowData = await Db.collections!.Workflow!.findOne(workflowId); - if (workflowData === undefined) { - GenericHelpers.logOutput(`The workflow with the id "${workflowId}" does not exist.`); - return; - } - } - - // Make sure the settings exist - await UserSettings.prepareUserSettings(); - - // Wait till the n8n-packages have been read - await loadNodesAndCredentialsPromise; - - // Add the found types to an instance other parts of the application can use - const nodeTypes = NodeTypes(); - await nodeTypes.init(loadNodesAndCredentials.nodeTypes); - - if (!WorkflowHelpers.isWorkflowIdValid(workflowId)) { - workflowId = undefined; - } - - // Check if the workflow contains the required "Start" node - // "requiredNodeTypes" are also defined in editor-ui/views/NodeView.vue - const requiredNodeTypes = ['n8n-nodes-base.start']; - let startNodeFound = false; - for (const node of workflowData!.nodes) { - if (requiredNodeTypes.includes(node.type)) { - startNodeFound = true; - } - } - - if (startNodeFound === false) { - // If the workflow does not contain a start-node we can not know what - // should be executed and with which data to start. - GenericHelpers.logOutput(`The workflow does not contain a "Start" node. So it can not be executed.`); - return Promise.resolve(); - } + if (flags.id && flags.file) { + GenericHelpers.logOutput(`Either "id" or "file" can be set never both!`); + return; + } + let workflowId: string | undefined; + let workflowData: IWorkflowBase | undefined = undefined; + if (flags.file) { + // Path to workflow is given try { - const credentials = await WorkflowCredentials(workflowData!.nodes); + workflowData = JSON.parse(await fs.readFile(flags.file, 'utf8')); + } catch (error) { + if (error.code === 'ENOENT') { + GenericHelpers.logOutput(`The file "${flags.file}" could not be found.`); + return; + } - const runData: IWorkflowExecutionDataProcess = { - credentials, - executionMode: 'cli', - workflowData: workflowData!, - }; + throw error; + } - const workflowRunner = new WorkflowRunner(); - const executionId = await workflowRunner.run(runData); - - const activeExecutions = ActiveExecutions.getInstance(); - const data = await activeExecutions.getPostExecutePromise(executionId); - - console.log('Execution was successfull:'); - console.log('===================================='); - console.log(JSON.stringify(data, null, 2)); - } catch (e) { - console.error('GOT ERROR'); - console.log('===================================='); - console.error(e); + // Do a basic check if the data in the file looks right + // TODO: Later check with the help of TypeScript data if it is valid or not + if (workflowData === undefined || workflowData.nodes === undefined || workflowData.connections === undefined) { + GenericHelpers.logOutput(`The file "${flags.file}" does not contain valid workflow data.`); return; } - }); -}; + workflowId = workflowData.id!.toString(); + } + + // Wait till the database is ready + await startDbInitPromise; + + if (flags.id) { + // Id of workflow is given + workflowId = flags.id; + workflowData = await Db.collections!.Workflow!.findOne(workflowId); + if (workflowData === undefined) { + GenericHelpers.logOutput(`The workflow with the id "${workflowId}" does not exist.`); + return; + } + } + + // Make sure the settings exist + await UserSettings.prepareUserSettings(); + + // Wait till the n8n-packages have been read + await loadNodesAndCredentialsPromise; + + // Add the found types to an instance other parts of the application can use + const nodeTypes = NodeTypes(); + await nodeTypes.init(loadNodesAndCredentials.nodeTypes); + + if (!WorkflowHelpers.isWorkflowIdValid(workflowId)) { + workflowId = undefined; + } + + // Check if the workflow contains the required "Start" node + // "requiredNodeTypes" are also defined in editor-ui/views/NodeView.vue + const requiredNodeTypes = ['n8n-nodes-base.start']; + let startNodeFound = false; + for (const node of workflowData!.nodes) { + if (requiredNodeTypes.includes(node.type)) { + startNodeFound = true; + } + } + + if (startNodeFound === false) { + // If the workflow does not contain a start-node we can not know what + // should be executed and with which data to start. + GenericHelpers.logOutput(`The workflow does not contain a "Start" node. So it can not be executed.`); + return Promise.resolve(); + } + + try { + const credentials = await WorkflowCredentials(workflowData!.nodes); + + const runData: IWorkflowExecutionDataProcess = { + credentials, + executionMode: 'cli', + workflowData: workflowData!, + }; + + const workflowRunner = new WorkflowRunner(); + const executionId = await workflowRunner.run(runData); + + const activeExecutions = ActiveExecutions.getInstance(); + const data = await activeExecutions.getPostExecutePromise(executionId); + + if (data === undefined) { + throw new Error('Workflow did not return any data!'); + } + + if (data.data.resultData.error) { + this.log('Execution was NOT successfull:'); + this.log('===================================='); + this.log(JSON.stringify(data, null, 2)); + + // console.log(data.data.resultData.error); + const error = new Error(data.data.resultData.error.message); + error.stack = data.data.resultData.error.stack; + throw error; + } + + this.log('Execution was successfull:'); + this.log('===================================='); + this.log(JSON.stringify(data, null, 2)); + } catch (e) { + console.error('\nGOT ERROR'); + console.log('===================================='); + console.error(e.message); + console.error(e.stack); + this.exit(1); + return; + } + + this.exit(); + } +} diff --git a/packages/cli/commands/start.ts b/packages/cli/commands/start.ts index f673a83297..e4297eb380 100644 --- a/packages/cli/commands/start.ts +++ b/packages/cli/commands/start.ts @@ -1,10 +1,12 @@ -import Vorpal = require('vorpal'); -import { Args } from 'vorpal'; -import * as config from '../config'; - -const open = require('open'); - import * as localtunnel from 'localtunnel'; +import { + UserSettings, +} from "n8n-core"; +import { Command, flags } from '@oclif/command'; +const open = require('open'); +import { promisify } from "util"; + +import * as config from '../config'; import { ActiveWorkflowRunner, CredentialTypes, @@ -15,175 +17,189 @@ import { TestWebhooks, Server, } from "../src"; -import { - UserSettings, -} from "n8n-core"; -import { promisify } from "util"; const tunnel = promisify(localtunnel); let activeWorkflowRunner: ActiveWorkflowRunner.ActiveWorkflowRunner | undefined; let processExistCode = 0; -/** - * Opens the UI in browser - * - */ -function openBrowser() { - const editorUrl = GenericHelpers.getBaseUrl(); - open(editorUrl, { wait: true }) - .catch((error: Error) => { - console.log(`\nWas not able to open URL in browser. Please open manually by visiting:\n${editorUrl}\n`); - }); -} +export class Start extends Command { + static description = 'Starts n8n. Makes Web-UI available and starts active workflows'; + + static examples = [ + `$ n8n start`, + `$ n8n start --tunnel`, + `$ n8n start -o`, + `$ n8n start --tunnel -o`, + ]; + + static flags = { + help: flags.help({ char: 'h' }), + open: flags.boolean({ + char: 'o', + description: 'opens the UI automatically in browser', + }), + tunnel: flags.boolean({ + description: 'runs the webhooks via a hooks.n8n.cloud tunnel server. Use only for testing and development!', + }), + }; -module.exports = (vorpal: Vorpal) => { - return vorpal - .command('start') - // @ts-ignore - .description('Starts n8n. Makes Web-UI available and starts active workflows') - .option('-o --open', - 'Opens the UI automatically in browser') - .option('--tunnel', - 'Runs the webhooks via a hooks.n8n.cloud tunnel server (use only for testing and development)') - .option('\n') - // TODO: Add validation - // .validate((args: Args) => { - // }) - .action((args: Args) => { + /** + * Opens the UI in browser + */ + static openBrowser() { + const editorUrl = GenericHelpers.getBaseUrl(); - if (process.pid === 1) { - console.error(`The n8n node process should not run as process with ID 1 because that will cause + open(editorUrl, { wait: true }) + .catch((error: Error) => { + console.log(`\nWas not able to open URL in browser. Please open manually by visiting:\n${editorUrl}\n`); + }); + } + + + /** + * Stoppes the n8n in a graceful way. + * Make for example sure that all the webhooks from third party services + * get removed. + */ + static async stopProcess() { + console.log(`\nStopping n8n...`); + + setTimeout(() => { + // In case that something goes wrong with shutdown we + // kill after max. 30 seconds no matter what + process.exit(processExistCode); + }, 30000); + + const removePromises = []; + if (activeWorkflowRunner !== undefined) { + removePromises.push(activeWorkflowRunner.removeAll()); + } + + // Remove all test webhooks + const testWebhooks = TestWebhooks.getInstance(); + removePromises.push(testWebhooks.removeAll()); + + await Promise.all(removePromises); + + process.exit(processExistCode); + } + + + async run() { + // Make sure that n8n shuts down gracefully if possible + process.on('SIGTERM', Start.stopProcess); + process.on('SIGINT', Start.stopProcess); + + const { flags } = this.parse(Start); + + if (process.pid === 1) { + this.error(`The n8n node process should not run as process with ID 1 because that will cause problems with shutting everything down correctly. If started with docker use the flag "--init" to fix this problem!`); - return; - } + return; + } - // TODO: Start here the the script in a subprocess which can get restarted when new nodes get added and so new packages have to get installed + // Wrap that the process does not close but we can still use async + (async () => { + try { + // Start directly with the init of the database to improve startup time + const startDbInitPromise = Db.init(); - // npm install / rm (in other process) - // restart process depending on exit code (lets say 50 means restart) + // Make sure the settings exist + const userSettings = await UserSettings.prepareUserSettings(); - // Wrap that the process does not close but we can still use async - (async () => { - try { - // Start directly with the init of the database to improve startup time - const startDbInitPromise = Db.init(); + // Load all node and credential types + const loadNodesAndCredentials = LoadNodesAndCredentials(); + await loadNodesAndCredentials.init(); - // Make sure the settings exist - const userSettings = await UserSettings.prepareUserSettings(); + // Add the found types to an instance other parts of the application can use + const nodeTypes = NodeTypes(); + await nodeTypes.init(loadNodesAndCredentials.nodeTypes); + const credentialTypes = CredentialTypes(); + await credentialTypes.init(loadNodesAndCredentials.credentialTypes); - // Load all node and credential types - const loadNodesAndCredentials = LoadNodesAndCredentials(); - await loadNodesAndCredentials.init(); + // Wait till the database is ready + await startDbInitPromise; - // Add the found types to an instance other parts of the application can use - const nodeTypes = NodeTypes(); - await nodeTypes.init(loadNodesAndCredentials.nodeTypes); - const credentialTypes = CredentialTypes(); - await credentialTypes.init(loadNodesAndCredentials.credentialTypes); + if (flags.tunnel === true) { + this.log('\nWaiting for tunnel ...'); - // Wait till the database is ready - await startDbInitPromise; + if (userSettings.tunnelSubdomain === undefined) { + // When no tunnel subdomain did exist yet create a new random one + const availableCharacters = 'abcdefghijklmnopqrstuvwxyz0123456789'; + userSettings.tunnelSubdomain = Array.from({ length: 24 }).map(() => { + return availableCharacters.charAt(Math.floor(Math.random() * availableCharacters.length)); + }).join(''); - if (args.options.tunnel !== undefined) { - console.log('\nWaiting for tunnel ...'); - - if (userSettings.tunnelSubdomain === undefined) { - // When no tunnel subdomain did exist yet create a new random one - const availableCharacters = 'abcdefghijklmnopqrstuvwxyz0123456789'; - userSettings.tunnelSubdomain = Array.from({ length: 24 }).map(() => { - return availableCharacters.charAt(Math.floor(Math.random() * availableCharacters.length)); - }).join(''); - - await UserSettings.writeUserSettings(userSettings); - } - - const tunnelSettings: localtunnel.TunnelConfig = { - host: 'https://hooks.n8n.cloud', - subdomain: userSettings.tunnelSubdomain, - }; - - const port = config.get('port') as number; - - // @ts-ignore - const webhookTunnel = await tunnel(port, tunnelSettings); - - process.env.WEBHOOK_TUNNEL_URL = webhookTunnel.url + '/'; - console.log(`Tunnel URL: ${process.env.WEBHOOK_TUNNEL_URL}\n`); + await UserSettings.writeUserSettings(userSettings); } - await Server.start(); + const tunnelSettings: localtunnel.TunnelConfig = { + host: 'https://hooks.n8n.cloud', + subdomain: userSettings.tunnelSubdomain, + }; - // Start to get active workflows and run their triggers - activeWorkflowRunner = ActiveWorkflowRunner.getInstance(); - await activeWorkflowRunner.init(); + const port = config.get('port') as number; - const editorUrl = GenericHelpers.getBaseUrl(); - console.log(`\nEditor is now accessible via:\n${editorUrl}`); + // @ts-ignore + const webhookTunnel = await tunnel(port, tunnelSettings); - // Allow to open n8n editor by pressing "o" - if (Boolean(process.stdout.isTTY) && process.stdin.setRawMode) { - process.stdin.setRawMode(true); - process.stdin.resume(); - process.stdin.setEncoding('utf8'); - let inputText = ''; + process.env.WEBHOOK_TUNNEL_URL = webhookTunnel.url + '/'; + this.log(`Tunnel URL: ${process.env.WEBHOOK_TUNNEL_URL}\n`); + this.log('IMPORTANT! Do not share with anybody as it would give people access to your n8n instance!'); + } - if (args.options.browser !== undefined) { - openBrowser(); - } - console.log(`\nPress "o" to open in Browser.`); - process.stdin.on("data", (key) => { - if (key === 'o') { - openBrowser(); + await Server.start(); + + // Start to get active workflows and run their triggers + activeWorkflowRunner = ActiveWorkflowRunner.getInstance(); + await activeWorkflowRunner.init(); + + const editorUrl = GenericHelpers.getBaseUrl(); + this.log(`\nEditor is now accessible via:\n${editorUrl}`); + + // Allow to open n8n editor by pressing "o" + if (Boolean(process.stdout.isTTY) && process.stdin.setRawMode) { + process.stdin.setRawMode(true); + process.stdin.resume(); + process.stdin.setEncoding('utf8'); + let inputText = ''; + + if (flags.open === true) { + Start.openBrowser(); + } + this.log(`\nPress "o" to open in Browser.`); + process.stdin.on("data", (key) => { + if (key === 'o') { + Start.openBrowser(); + inputText = ''; + } else if (key.charCodeAt(0) === 3) { + // Ctrl + c got pressed + Start.stopProcess(); + } else { + // When anything else got pressed, record it and send it on enter into the child process + if (key.charCodeAt(0) === 13) { + // send to child process and print in terminal + process.stdout.write('\n'); inputText = ''; } else { - // When anything else got pressed, record it and send it on enter into the child process - if (key.charCodeAt(0) === 13) { - // send to child process and print in terminal - process.stdout.write('\n'); - inputText = ''; - } else { - // record it and write into terminal - inputText += key; - process.stdout.write(key); - } + // record it and write into terminal + inputText += key; + process.stdout.write(key); } - }); - } - } catch (error) { - console.error(`There was an error: ${error.message}`); - - processExistCode = 1; - // @ts-ignore - process.emit('SIGINT'); + } + }); } - })(); + } catch (error) { + this.error(`There was an error: ${error.message}`); - - vorpal.sigint(async () => { - console.log(`\nStopping n8n...`); - - setTimeout(() => { - // In case that something goes wrong with shutdown we - // kill after max. 30 seconds no matter what - process.exit(processExistCode); - }, 30000); - - const removePromises = []; - if (activeWorkflowRunner !== undefined) { - removePromises.push(activeWorkflowRunner.removeAll()); - } - - // Remove all test webhooks - const testWebhooks = TestWebhooks.getInstance(); - removePromises.push(testWebhooks.removeAll()); - - await Promise.all(removePromises); - - process.exit(processExistCode); - }); - }); -}; + processExistCode = 1; + // @ts-ignore + process.emit('SIGINT'); + } + })(); + } +} diff --git a/packages/cli/index.ts b/packages/cli/index.ts deleted file mode 100644 index 6d06a230d1..0000000000 --- a/packages/cli/index.ts +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env node - -import { join as pathJoin } from 'path'; - -// Make sure that it also find the config folder when it -// did get started from another folder that the root one. -process.env.NODE_CONFIG_DIR = process.env.NODE_CONFIG_DIR || pathJoin(__dirname, 'config'); - -import Vorpal = require('vorpal'); -import { GenericHelpers } from './src'; - -// Check if version should be displayed -const versionFlags = [ - '-v', - '-V', - '--version' -]; -if (versionFlags.includes(process.argv.slice(-1)[0])) { - console.log(require('../package').version); - process.exit(0); -} - -if (process.argv.length === 2) { - // When no command is given choose by default start - process.argv.push('start'); -} - -const command = process.argv[2]; - -// Check if the command the user did enter is supported else stop -const supportedCommands = [ - 'execute', - 'help', - 'start', -]; - -if (!supportedCommands.includes(command)) { - GenericHelpers.logOutput(`The command "${command}" is not known!`); - process.argv.push('help'); -} - -const vorpal = new Vorpal(); -vorpal - .use(require('./commands/execute')) - .use(require('./commands/start')) - .delimiter('') - .show() - .parse(process.argv); - - -process - .on('unhandledRejection', (reason, p) => { - console.error(reason, 'Unhandled Rejection at Promise', p); - }) - .on('uncaughtException', err => { - console.error(err, 'Uncaught Exception thrown'); - process.exit(1); - }); diff --git a/packages/cli/package.json b/packages/cli/package.json index e4e8618ea4..1d75f8625b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -13,6 +13,10 @@ }, "main": "dist/index", "types": "dist/src/index.d.ts", + "oclif": { + "commands": "./dist/commands", + "bin": "n8n" + }, "scripts": { "build": "tsc", "dev": "nodemon", @@ -22,7 +26,7 @@ "watch": "tsc --watch" }, "bin": { - "n8n": "./dist/index.js" + "n8n": "./bin/n8n" }, "keywords": [ "automate", @@ -36,6 +40,7 @@ "node": ">=8.0.0" }, "files": [ + "bin", "dist" ], "devDependencies": { @@ -51,7 +56,6 @@ "@types/open": "^6.1.0", "@types/parseurl": "^1.3.1", "@types/request-promise-native": "^1.0.15", - "@types/vorpal": "^1.11.0", "jest": "^23.6.0", "nodemon": "^1.19.1", "sails-disk": "^1.0.1", @@ -60,6 +64,8 @@ "typescript": "~3.5.2" }, "dependencies": { + "@oclif/command": "^1.5.18", + "@oclif/errors": "^1.2.2", "basic-auth": "^2.0.1", "body-parser": "^1.18.3", "compression": "^1.7.4", @@ -82,8 +88,7 @@ "request-promise-native": "^1.0.7", "sqlite3": "^4.0.6", "sse-channel": "^3.1.1", - "typeorm": "^0.2.16", - "vorpal": "^1.12.0" + "typeorm": "^0.2.16" }, "jest": { "transform": { diff --git a/packages/cli/src/TestWebhooks.ts b/packages/cli/src/TestWebhooks.ts index 5706bbceff..19bccad3ec 100644 --- a/packages/cli/src/TestWebhooks.ts +++ b/packages/cli/src/TestWebhooks.ts @@ -20,8 +20,6 @@ import { WorkflowExecuteMode, } from 'n8n-workflow'; -const pushInstance = Push.getInstance(); - export class TestWebhooks { @@ -91,6 +89,7 @@ export class TestWebhooks { // Inform editor-ui that webhook got received if (this.testWebhookData[webhookKey].sessionId !== undefined) { + const pushInstance = Push.getInstance(); pushInstance.send('testWebhookReceived', { workflowId: webhookData.workflow.id, executionId }, this.testWebhookData[webhookKey].sessionId!); } @@ -167,6 +166,7 @@ export class TestWebhooks { // Inform editor-ui that webhook got received if (this.testWebhookData[webhookKey].sessionId !== undefined) { try { + const pushInstance = Push.getInstance(); pushInstance.send('testWebhookDeleted', { workflowId }, this.testWebhookData[webhookKey].sessionId!); } catch (error) { // Could not inform editor, probably is not connected anymore. So sipmly go on. diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index 5ab89792e6..c51fecc659 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -27,8 +27,6 @@ import { import * as config from '../config'; -const pushInstance = Push.getInstance(); - /** * Checks if there was an error and if errorWorkflow is defined. If so it collects @@ -90,6 +88,7 @@ export function pushExecutionFinished(fullRunData: IRun, executionIdActive: stri retryOf, }; + const pushInstance = Push.getInstance(); pushInstance.send('executionFinished', sendData); } @@ -114,6 +113,7 @@ const hooks = (mode: WorkflowExecuteMode, workflowData: IWorkflowBase, execution return; } + const pushInstance = Push.getInstance(); pushInstance.send('nodeExecuteBefore', { executionId, nodeName, @@ -127,6 +127,7 @@ const hooks = (mode: WorkflowExecuteMode, workflowData: IWorkflowBase, execution return; } + const pushInstance = Push.getInstance(); pushInstance.send('nodeExecuteAfter', { executionId, nodeName, @@ -137,6 +138,7 @@ const hooks = (mode: WorkflowExecuteMode, workflowData: IWorkflowBase, execution workflowExecuteBefore: [ async (): Promise => { // Push data to editor-ui once workflow finished + const pushInstance = Push.getInstance(); pushInstance.send('executionStarted', { executionId, mode,