2021-08-29 11:58:11 -07:00
|
|
|
/* eslint-disable @typescript-eslint/unbound-method */
|
2022-04-08 10:37:27 -07:00
|
|
|
import { BinaryDataManager, UserSettings } from 'n8n-core';
|
2021-02-09 14:32:40 -08:00
|
|
|
import { Command, flags } from '@oclif/command';
|
|
|
|
|
2023-01-11 09:28:35 -08:00
|
|
|
import { LoggerProxy, ErrorReporterProxy as ErrorReporter, sleep } from 'n8n-workflow';
|
2022-11-09 06:25:00 -08:00
|
|
|
import config from '@/config';
|
|
|
|
import * as ActiveExecutions from '@/ActiveExecutions';
|
|
|
|
import { CredentialsOverwrites } from '@/CredentialsOverwrites';
|
|
|
|
import { CredentialTypes } from '@/CredentialTypes';
|
|
|
|
import * as Db from '@/Db';
|
|
|
|
import { ExternalHooks } from '@/ExternalHooks';
|
|
|
|
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
|
|
|
|
import { NodeTypes } from '@/NodeTypes';
|
|
|
|
import { InternalHooksManager } from '@/InternalHooksManager';
|
2023-01-04 02:38:48 -08:00
|
|
|
import { WebhookServer } from '@/WebhookServer';
|
2022-11-09 06:25:00 -08:00
|
|
|
import { getLogger } from '@/Logger';
|
|
|
|
import { initErrorHandling } from '@/ErrorReporting';
|
|
|
|
import * as CrashJournal from '@/CrashJournal';
|
2021-02-09 14:32:40 -08:00
|
|
|
|
2023-01-11 09:28:35 -08:00
|
|
|
const exitWithCrash = async (message: string, error: unknown) => {
|
|
|
|
ErrorReporter.error(new Error(message, { cause: error }), { level: 'fatal' });
|
|
|
|
await sleep(2000);
|
|
|
|
process.exit(1);
|
|
|
|
};
|
|
|
|
|
|
|
|
const exitSuccessFully = async () => {
|
|
|
|
try {
|
|
|
|
await CrashJournal.cleanup();
|
|
|
|
} finally {
|
|
|
|
process.exit();
|
|
|
|
}
|
|
|
|
};
|
2021-02-09 14:32:40 -08:00
|
|
|
|
|
|
|
export class Webhook extends Command {
|
|
|
|
static description = 'Starts n8n webhook process. Intercepts only production URLs.';
|
|
|
|
|
2022-12-29 03:20:43 -08:00
|
|
|
static examples = ['$ n8n webhook'];
|
2021-02-09 14:32:40 -08:00
|
|
|
|
|
|
|
static flags = {
|
|
|
|
help: flags.help({ char: 'h' }),
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2022-11-08 08:06:00 -08:00
|
|
|
* Stops n8n in a graceful way.
|
2021-02-09 14:32:40 -08:00
|
|
|
* Make for example sure that all the webhooks from third party services
|
|
|
|
* get removed.
|
|
|
|
*/
|
2021-08-29 11:58:11 -07:00
|
|
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
2021-02-09 14:32:40 -08:00
|
|
|
static async stopProcess() {
|
2022-12-29 03:20:43 -08:00
|
|
|
LoggerProxy.info('\nStopping n8n...');
|
2021-02-09 14:32:40 -08:00
|
|
|
|
|
|
|
try {
|
|
|
|
const externalHooks = ExternalHooks();
|
|
|
|
await externalHooks.run('n8n.stop', []);
|
|
|
|
|
2023-01-11 09:28:35 -08:00
|
|
|
setTimeout(async () => {
|
2021-02-09 14:32:40 -08:00
|
|
|
// In case that something goes wrong with shutdown we
|
|
|
|
// kill after max. 30 seconds no matter what
|
2023-01-11 09:28:35 -08:00
|
|
|
await exitSuccessFully();
|
2021-02-09 14:32:40 -08:00
|
|
|
}, 30000);
|
|
|
|
|
|
|
|
// Wait for active workflow executions to finish
|
|
|
|
const activeExecutionsInstance = ActiveExecutions.getInstance();
|
|
|
|
let executingWorkflows = activeExecutionsInstance.getActiveExecutions();
|
|
|
|
|
|
|
|
let count = 0;
|
|
|
|
while (executingWorkflows.length !== 0) {
|
|
|
|
if (count++ % 4 === 0) {
|
2021-08-29 11:58:11 -07:00
|
|
|
LoggerProxy.info(
|
|
|
|
`Waiting for ${executingWorkflows.length} active executions to finish...`,
|
|
|
|
);
|
2021-02-09 14:32:40 -08:00
|
|
|
}
|
2021-08-29 11:58:11 -07:00
|
|
|
// eslint-disable-next-line no-await-in-loop
|
2022-11-08 08:06:00 -08:00
|
|
|
await sleep(500);
|
2021-02-09 14:32:40 -08:00
|
|
|
executingWorkflows = activeExecutionsInstance.getActiveExecutions();
|
|
|
|
}
|
|
|
|
} catch (error) {
|
2023-01-11 09:28:35 -08:00
|
|
|
await exitWithCrash('There was an error shutting down n8n.', error);
|
2021-02-09 14:32:40 -08:00
|
|
|
}
|
|
|
|
|
2023-01-11 09:28:35 -08:00
|
|
|
await exitSuccessFully();
|
2021-02-09 14:32:40 -08:00
|
|
|
}
|
|
|
|
|
2021-08-29 11:58:11 -07:00
|
|
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
2021-02-09 14:32:40 -08:00
|
|
|
async run() {
|
2023-01-04 02:38:48 -08:00
|
|
|
if (config.getEnv('executions.mode') !== 'queue') {
|
|
|
|
/**
|
|
|
|
* It is technically possible to run without queues but
|
|
|
|
* there are 2 known bugs when running in this mode:
|
|
|
|
* - Executions list will be problematic as the main process
|
|
|
|
* is not aware of current executions in the webhook processes
|
|
|
|
* and therefore will display all current executions as error
|
|
|
|
* as it is unable to determine if it is still running or crashed
|
|
|
|
* - You cannot stop currently executing jobs from webhook processes
|
|
|
|
* when running without queues as the main process cannot talk to
|
|
|
|
* the webhook processes to communicate workflow execution interruption.
|
|
|
|
*/
|
|
|
|
|
|
|
|
this.error('Webhook processes can only run with execution mode as queue.');
|
|
|
|
}
|
|
|
|
|
2021-05-01 20:43:01 -07:00
|
|
|
const logger = getLogger();
|
|
|
|
LoggerProxy.init(logger);
|
|
|
|
|
2021-02-09 14:32:40 -08:00
|
|
|
// Make sure that n8n shuts down gracefully if possible
|
2022-11-08 08:06:00 -08:00
|
|
|
process.once('SIGTERM', Webhook.stopProcess);
|
|
|
|
process.once('SIGINT', Webhook.stopProcess);
|
|
|
|
|
2023-01-02 03:14:39 -08:00
|
|
|
await initErrorHandling();
|
2022-11-08 08:06:00 -08:00
|
|
|
await CrashJournal.init();
|
2021-02-09 14:32:40 -08:00
|
|
|
|
2023-01-04 02:38:48 -08:00
|
|
|
try {
|
|
|
|
// Start directly with the init of the database to improve startup time
|
2023-01-11 09:28:35 -08:00
|
|
|
const startDbInitPromise = Db.init().catch(async (error: Error) =>
|
|
|
|
exitWithCrash('There was an error initializing DB', error),
|
|
|
|
);
|
2021-03-25 03:23:54 -07:00
|
|
|
|
2023-01-04 02:38:48 -08:00
|
|
|
// Make sure the settings exist
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
|
|
await UserSettings.prepareUserSettings();
|
2021-02-09 14:32:40 -08:00
|
|
|
|
2023-01-04 02:38:48 -08:00
|
|
|
// Load all node and credential types
|
|
|
|
const loadNodesAndCredentials = LoadNodesAndCredentials();
|
|
|
|
await loadNodesAndCredentials.init();
|
2021-02-09 14:32:40 -08:00
|
|
|
|
2023-01-04 02:38:48 -08:00
|
|
|
// Add the found types to an instance other parts of the application can use
|
|
|
|
const nodeTypes = NodeTypes(loadNodesAndCredentials);
|
|
|
|
const credentialTypes = CredentialTypes(loadNodesAndCredentials);
|
2021-02-09 14:32:40 -08:00
|
|
|
|
2023-01-04 02:38:48 -08:00
|
|
|
// Load the credentials overwrites if any exist
|
2023-01-30 05:42:30 -08:00
|
|
|
CredentialsOverwrites(credentialTypes);
|
2023-01-04 02:38:48 -08:00
|
|
|
|
|
|
|
// Load all external hooks
|
|
|
|
const externalHooks = ExternalHooks();
|
|
|
|
await externalHooks.init();
|
|
|
|
|
|
|
|
// Wait till the database is ready
|
|
|
|
await startDbInitPromise;
|
|
|
|
|
|
|
|
const instanceId = await UserSettings.getInstanceId();
|
|
|
|
await InternalHooksManager.init(instanceId, nodeTypes);
|
|
|
|
|
|
|
|
const binaryDataConfig = config.getEnv('binaryDataManager');
|
|
|
|
await BinaryDataManager.init(binaryDataConfig);
|
|
|
|
|
|
|
|
const server = new WebhookServer();
|
|
|
|
await server.start();
|
|
|
|
|
|
|
|
console.info('Webhook listener waiting for requests.');
|
|
|
|
} catch (error) {
|
2023-01-11 09:28:35 -08:00
|
|
|
await exitWithCrash('Exiting due to error.', error);
|
2023-01-04 02:38:48 -08:00
|
|
|
}
|
2021-02-09 14:32:40 -08:00
|
|
|
}
|
|
|
|
}
|