n8n/packages/cli/src/commands/BaseCommand.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

155 lines
4.8 KiB
TypeScript
Raw Normal View History

import { Command } from '@oclif/command';
import { ExitError } from '@oclif/errors';
import { Container } from 'typedi';
import { LoggerProxy, ErrorReporterProxy as ErrorReporter, sleep } from 'n8n-workflow';
import type { IUserSettings } from 'n8n-core';
import { BinaryDataManager, UserSettings } from 'n8n-core';
import type { AbstractServer } from '@/AbstractServer';
import { getLogger } from '@/Logger';
import config from '@/config';
import * as Db from '@/Db';
import * as CrashJournal from '@/CrashJournal';
import { inTest } from '@/constants';
import { CredentialTypes } from '@/CredentialTypes';
import { CredentialsOverwrites } from '@/CredentialsOverwrites';
import { initErrorHandling } from '@/ErrorReporting';
import { ExternalHooks } from '@/ExternalHooks';
import { NodeTypes } from '@/NodeTypes';
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import type { IExternalHooksClass, N8nInstanceType } from '@/Interfaces';
import { InternalHooks } from '@/InternalHooks';
feat: Support feature flag evaluation server side (#5511) * feat(editor): roll out schema view * feat(editor): add posthog tracking * refactor: use composables * refactor: clean up console log * refactor: clean up impl * chore: clean up impl * fix: fix demo var * chore: add comment * refactor: clean up * chore: wrap error func * refactor: clean up import * refactor: make store * feat: enable rudderstack usebeacon, move event to unload * chore: clean up alert * refactor: move tracking from hooks * fix: reload flags on login * fix: add func to setup * fix: clear duplicate import * chore: add console to tesT * chore: add console to tesT * fix: try reload * chore: randomize instnace id for testing * chore: randomize instnace id for testing * chore: add console logs for testing * chore: move random id to fe * chore: use query param for testing * feat: update PostHog api endpoint * feat: update rs host * feat: update rs host * feat: update rs endpoints * refactor: use api host for BE events as well * refactor: refactor out posthog client * feat: add feature flags to login * feat: add feature flags to login * feat: get feature flags to work * feat: add created at to be events * chore: add todos * chore: clean up store * chore: add created at to identify * feat: add posthog config to settings * feat: add bootstrapping * chore: clean up * chore: fix build * fix: get dates to work * fix: get posthog to recognize dates * chore: refactor * fix: update back to number * fix: update key * fix: get experiment evals to work * feat: add posthog to signup router * feat: add feature flags on sign up * chore: clean up * fix: fix import * chore: clean up loading script * feat: add timeout, fix: script loader * fix: test timeout and get working on 8080 * refactor: move out posthog * feat: add experiment tracking * fix: clear tracked on reset * fix: fix signup bug * fix: handle errors when telmetry is disabled * refactor: remove redundant await * fix: add back posthog to telemetry * test: fix test * test: fix test * test: add tests for posthog client * lint: fix * fix: fix issue with slow decide endpoint * lint: fix * lint: fix * lint: fix * lint: fix * chore: address PR feedback * chore: address PR feedback * feat: add onboarding experiment
2023-02-21 00:35:35 -08:00
import { PostHogClient } from '@/posthog';
import { License } from '@/License';
import { ExternalSecretsManager } from '@/ExternalSecrets/ExternalSecretsManager.ee';
export abstract class BaseCommand extends Command {
protected logger = LoggerProxy.init(getLogger());
protected externalHooks: IExternalHooksClass;
protected loadNodesAndCredentials: LoadNodesAndCredentials;
protected nodeTypes: NodeTypes;
protected userSettings: IUserSettings;
protected instanceId: string;
protected server?: AbstractServer;
async init(): Promise<void> {
await initErrorHandling();
process.once('SIGTERM', async () => this.stopProcess());
process.once('SIGINT', async () => this.stopProcess());
// Make sure the settings exist
this.userSettings = await UserSettings.prepareUserSettings();
this.loadNodesAndCredentials = Container.get(LoadNodesAndCredentials);
await this.loadNodesAndCredentials.init();
this.nodeTypes = Container.get(NodeTypes);
this.nodeTypes.init();
const credentialTypes = Container.get(CredentialTypes);
CredentialsOverwrites(credentialTypes);
await Db.init().catch(async (error: Error) =>
this.exitWithCrash('There was an error initializing DB', error),
);
await this.server?.init();
await Db.migrate().catch(async (error: Error) =>
this.exitWithCrash('There was an error running database migrations', error),
);
const dbType = config.getEnv('database.type');
if (['mysqldb', 'mariadb'].includes(dbType)) {
LoggerProxy.warn(
'Support for MySQL/MariaDB has been deprecated and will be removed with an upcoming version of n8n. Please migrate to PostgreSQL.',
);
}
if (process.env.EXECUTIONS_PROCESS === 'own') {
LoggerProxy.warn(
'Own mode has been deprecated and will be removed in a future version of n8n. If you need the isolation and performance gains, please consider using queue mode.',
);
}
this.instanceId = this.userSettings.instanceId ?? '';
await Container.get(PostHogClient).init(this.instanceId);
await Container.get(InternalHooks).init(this.instanceId);
}
protected async stopProcess() {
// This needs to be overridden
}
protected async initCrashJournal() {
await CrashJournal.init();
}
protected async exitSuccessFully() {
try {
await CrashJournal.cleanup();
} finally {
process.exit();
}
}
protected async exitWithCrash(message: string, error: unknown) {
ErrorReporter.error(new Error(message, { cause: error }), { level: 'fatal' });
await sleep(2000);
process.exit(1);
}
async initBinaryManager() {
const binaryDataConfig = config.getEnv('binaryDataManager');
await BinaryDataManager.init(binaryDataConfig, true);
}
async initExternalHooks() {
this.externalHooks = Container.get(ExternalHooks);
await this.externalHooks.init();
}
async initLicense(instanceType: N8nInstanceType = 'main'): Promise<void> {
refactor(core): Implement soft-deletions for executions (#7092) Based on #7065 | Story: https://linear.app/n8n/issue/PAY-771 n8n on filesystem mode marks binary data to delete on manual execution deletion, on unsaved execution completion, and on every execution pruning cycle. We later prune binary data in a separate cycle via these marker files, based on the configured TTL. In the context of introducing an S3 client to manage binary data, the filesystem mode's mark-and-prune setup is too tightly coupled to the general binary data management client interface. This PR... - Ensures the deletion of an execution causes the deletion of any binary data associated to it. This does away with the need for binary data TTL and simplifies the filesystem mode's mark-and-prune setup. - Refactors all execution deletions (including pruning) to cause soft deletions, hard-deletes soft-deleted executions based on the existing pruning config, and adjusts execution endpoints to filter out soft-deleted executions. This reduces DB load, and keeps binary data around long enough for users to access it when building workflows with unsaved executions. - Moves all execution pruning work from an execution lifecycle hook to `execution.repository.ts`. This keeps related logic in a single place. - Removes all marking logic from the binary data manager. This simplifies the interface that the S3 client will meet. - Adds basic sanity-check tests to pruning logic and execution deletion. Out of scope: - Improving existing pruning logic. - Improving existing execution repository logic. - Adjusting dir structure for filesystem mode. --------- Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
2023-09-20 06:21:42 -07:00
config.set('generic.instanceType', instanceType);
const license = Container.get(License);
await license.init(this.instanceId, instanceType);
const activationKey = config.getEnv('license.activationKey');
if (activationKey) {
const hasCert = (await license.loadCertStr()).length > 0;
if (hasCert) {
return LoggerProxy.debug('Skipping license activation');
}
try {
LoggerProxy.debug('Attempting license activation');
await license.activate(activationKey);
} catch (e) {
LoggerProxy.error('Could not activate license', e as Error);
}
}
}
async initExternalSecrets() {
const secretsManager = Container.get(ExternalSecretsManager);
await secretsManager.init();
}
async finally(error: Error | undefined) {
if (inTest || this.id === 'start') return;
if (Db.connectionState.connected) {
await sleep(100); // give any in-flight query some time to finish
await Db.close();
}
const exitCode = error instanceof ExitError ? error.oclif.exit : error ? 1 : 0;
this.exit(exitCode);
}
}