mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
refactor(core): Use an IoC container to manage singleton classes [Part-2] (no-changelog) (#5690)
* use typedi for UserManagementMailer * use typedi for SamlService * fix typos * use typedi for Queue * use typedi for License * convert some more code to use typedi
This commit is contained in:
parent
c07f838ce6
commit
9bd7529193
|
@ -302,7 +302,7 @@ export abstract class AbstractServer {
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
protected setupWaitingWebhookEndpoint() {
|
protected setupWaitingWebhookEndpoint() {
|
||||||
const endpoint = this.endpointWebhookWaiting;
|
const endpoint = this.endpointWebhookWaiting;
|
||||||
const waitingWebhooks = new WaitingWebhooks();
|
const waitingWebhooks = Container.get(WaitingWebhooks);
|
||||||
|
|
||||||
// Register all webhook-waiting requests
|
// Register all webhook-waiting requests
|
||||||
this.app.all(`/${endpoint}/*`, async (req, res) => {
|
this.app.all(`/${endpoint}/*`, async (req, res) => {
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
/* eslint-disable prefer-template */
|
|
||||||
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
|
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
|
||||||
/* eslint-disable no-param-reassign */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
|
@ -25,7 +22,7 @@ import type {
|
||||||
IWorkflowExecutionDataProcess,
|
IWorkflowExecutionDataProcess,
|
||||||
} from '@/Interfaces';
|
} from '@/Interfaces';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
import * as ResponseHelper from '@/ResponseHelper';
|
||||||
import * as WorkflowHelpers from '@/WorkflowHelpers';
|
import { isWorkflowIdValid } from '@/utils';
|
||||||
import { Service } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
|
@ -60,7 +57,7 @@ export class ActiveExecutions {
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflowId = executionData.workflowData.id;
|
const workflowId = executionData.workflowData.id;
|
||||||
if (workflowId !== undefined && WorkflowHelpers.isWorkflowIdValid(workflowId)) {
|
if (workflowId !== undefined && isWorkflowIdValid(workflowId)) {
|
||||||
fullExecutionData.workflowId = workflowId;
|
fullExecutionData.workflowId = workflowId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
|
|
||||||
import { Container, Service } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
import { ActiveWorkflows, NodeExecuteFunctions } from 'n8n-core';
|
import { ActiveWorkflows, NodeExecuteFunctions } from 'n8n-core';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
|
@ -82,7 +82,11 @@ export class ActiveWorkflowRunner {
|
||||||
[key: string]: IQueuedWorkflowActivations;
|
[key: string]: IQueuedWorkflowActivations;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
constructor(private externalHooks: ExternalHooks) {}
|
constructor(
|
||||||
|
private activeExecutions: ActiveExecutions,
|
||||||
|
private externalHooks: ExternalHooks,
|
||||||
|
private nodeTypes: NodeTypes,
|
||||||
|
) {}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
async init() {
|
async init() {
|
||||||
|
@ -271,14 +275,13 @@ export class ActiveWorkflowRunner {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeTypes = Container.get(NodeTypes);
|
|
||||||
const workflow = new Workflow({
|
const workflow = new Workflow({
|
||||||
id: webhook.workflowId,
|
id: webhook.workflowId,
|
||||||
name: workflowData.name,
|
name: workflowData.name,
|
||||||
nodes: workflowData.nodes,
|
nodes: workflowData.nodes,
|
||||||
connections: workflowData.connections,
|
connections: workflowData.connections,
|
||||||
active: workflowData.active,
|
active: workflowData.active,
|
||||||
nodeTypes,
|
nodeTypes: this.nodeTypes,
|
||||||
staticData: workflowData.staticData,
|
staticData: workflowData.staticData,
|
||||||
settings: workflowData.settings,
|
settings: workflowData.settings,
|
||||||
});
|
});
|
||||||
|
@ -514,14 +517,13 @@ export class ActiveWorkflowRunner {
|
||||||
throw new Error(`Could not find workflow with id "${workflowId}"`);
|
throw new Error(`Could not find workflow with id "${workflowId}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeTypes = Container.get(NodeTypes);
|
|
||||||
const workflow = new Workflow({
|
const workflow = new Workflow({
|
||||||
id: workflowId,
|
id: workflowId,
|
||||||
name: workflowData.name,
|
name: workflowData.name,
|
||||||
nodes: workflowData.nodes,
|
nodes: workflowData.nodes,
|
||||||
connections: workflowData.connections,
|
connections: workflowData.connections,
|
||||||
active: workflowData.active,
|
active: workflowData.active,
|
||||||
nodeTypes,
|
nodeTypes: this.nodeTypes,
|
||||||
staticData: workflowData.staticData,
|
staticData: workflowData.staticData,
|
||||||
settings: workflowData.settings,
|
settings: workflowData.settings,
|
||||||
});
|
});
|
||||||
|
@ -638,7 +640,7 @@ export class ActiveWorkflowRunner {
|
||||||
|
|
||||||
if (donePromise) {
|
if (donePromise) {
|
||||||
executePromise.then((executionId) => {
|
executePromise.then((executionId) => {
|
||||||
Container.get(ActiveExecutions)
|
this.activeExecutions
|
||||||
.getPostExecutePromise(executionId)
|
.getPostExecutePromise(executionId)
|
||||||
.then(donePromise.resolve)
|
.then(donePromise.resolve)
|
||||||
.catch(donePromise.reject);
|
.catch(donePromise.reject);
|
||||||
|
@ -695,7 +697,7 @@ export class ActiveWorkflowRunner {
|
||||||
|
|
||||||
if (donePromise) {
|
if (donePromise) {
|
||||||
executePromise.then((executionId) => {
|
executePromise.then((executionId) => {
|
||||||
Container.get(ActiveExecutions)
|
this.activeExecutions
|
||||||
.getPostExecutePromise(executionId)
|
.getPostExecutePromise(executionId)
|
||||||
.then(donePromise.resolve)
|
.then(donePromise.resolve)
|
||||||
.catch(donePromise.reject);
|
.catch(donePromise.reject);
|
||||||
|
@ -782,14 +784,13 @@ export class ActiveWorkflowRunner {
|
||||||
if (!workflowData) {
|
if (!workflowData) {
|
||||||
throw new Error(`Could not find workflow with id "${workflowId}".`);
|
throw new Error(`Could not find workflow with id "${workflowId}".`);
|
||||||
}
|
}
|
||||||
const nodeTypes = Container.get(NodeTypes);
|
|
||||||
workflowInstance = new Workflow({
|
workflowInstance = new Workflow({
|
||||||
id: workflowId,
|
id: workflowId,
|
||||||
name: workflowData.name,
|
name: workflowData.name,
|
||||||
nodes: workflowData.nodes,
|
nodes: workflowData.nodes,
|
||||||
connections: workflowData.connections,
|
connections: workflowData.connections,
|
||||||
active: workflowData.active,
|
active: workflowData.active,
|
||||||
nodeTypes,
|
nodeTypes: this.nodeTypes,
|
||||||
staticData: workflowData.staticData,
|
staticData: workflowData.staticData,
|
||||||
settings: workflowData.settings,
|
settings: workflowData.settings,
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import { AES, enc } from 'crypto-js';
|
import { AES, enc } from 'crypto-js';
|
||||||
import type { Entry as LdapUser } from 'ldapts';
|
import type { Entry as LdapUser } from 'ldapts';
|
||||||
import { Filter } from 'ldapts/filters/Filter';
|
import { Filter } from 'ldapts/filters/Filter';
|
||||||
|
import { Container } from 'typedi';
|
||||||
import { UserSettings } from 'n8n-core';
|
import { UserSettings } from 'n8n-core';
|
||||||
import { validate } from 'jsonschema';
|
import { validate } from 'jsonschema';
|
||||||
import * as Db from '@/Db';
|
import * as Db from '@/Db';
|
||||||
|
@ -23,15 +24,14 @@ import {
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import type { ConnectionSecurity, LdapConfig } from './types';
|
import type { ConnectionSecurity, LdapConfig } from './types';
|
||||||
import { jsonParse, LoggerProxy as Logger } from 'n8n-workflow';
|
import { jsonParse, LoggerProxy as Logger } from 'n8n-workflow';
|
||||||
import { getLicense } from '@/License';
|
import { License } from '@/License';
|
||||||
import { Container } from 'typedi';
|
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the LDAP feature is disabled in the instance
|
* Check whether the LDAP feature is disabled in the instance
|
||||||
*/
|
*/
|
||||||
export const isLdapEnabled = (): boolean => {
|
export const isLdapEnabled = (): boolean => {
|
||||||
const license = getLicense();
|
const license = Container.get(License);
|
||||||
return isUserManagementEnabled() && (config.getEnv(LDAP_ENABLED) || license.isLdapEnabled());
|
return isUserManagementEnabled() && (config.getEnv(LDAP_ENABLED) || license.isLdapEnabled());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { getLogger } from './Logger';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import * as Db from '@/Db';
|
import * as Db from '@/Db';
|
||||||
import { LICENSE_FEATURES, N8N_VERSION, SETTINGS_LICENSE_CERT_KEY } from './constants';
|
import { LICENSE_FEATURES, N8N_VERSION, SETTINGS_LICENSE_CERT_KEY } from './constants';
|
||||||
|
import { Service } from 'typedi';
|
||||||
|
|
||||||
async function loadCertStr(): Promise<TLicenseContainerStr> {
|
async function loadCertStr(): Promise<TLicenseContainerStr> {
|
||||||
const databaseSettings = await Db.collections.Settings.findOne({
|
const databaseSettings = await Db.collections.Settings.findOne({
|
||||||
|
@ -27,6 +28,7 @@ async function saveCertStr(value: TLicenseContainerStr): Promise<void> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Service()
|
||||||
export class License {
|
export class License {
|
||||||
private logger: ILogger;
|
private logger: ILogger;
|
||||||
|
|
||||||
|
@ -160,13 +162,3 @@ export class License {
|
||||||
return (this.getFeatureValue('planName') ?? 'Community') as string;
|
return (this.getFeatureValue('planName') ?? 'Community') as string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let licenseInstance: License | undefined;
|
|
||||||
|
|
||||||
export function getLicense(): License {
|
|
||||||
if (licenseInstance === undefined) {
|
|
||||||
licenseInstance = new License();
|
|
||||||
}
|
|
||||||
|
|
||||||
return licenseInstance;
|
|
||||||
}
|
|
||||||
|
|
6
packages/cli/src/PublicApi/types.d.ts
vendored
6
packages/cli/src/PublicApi/types.d.ts
vendored
|
@ -7,7 +7,7 @@ import type { Role } from '@db/entities/Role';
|
||||||
|
|
||||||
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
|
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
|
||||||
|
|
||||||
import type * as UserManagementMailer from '@/UserManagement/email/UserManagementMailer';
|
import type { UserManagementMailer } from '@/UserManagement/email';
|
||||||
|
|
||||||
import type { Risk } from '@/audit/types';
|
import type { Risk } from '@/audit/types';
|
||||||
|
|
||||||
|
@ -26,10 +26,10 @@ export type AuthenticatedRequest<
|
||||||
> = express.Request<RouteParams, ResponseBody, RequestBody, RequestQuery> & {
|
> = express.Request<RouteParams, ResponseBody, RequestBody, RequestQuery> & {
|
||||||
user: User;
|
user: User;
|
||||||
globalMemberRole?: Role;
|
globalMemberRole?: Role;
|
||||||
mailer?: UserManagementMailer.UserManagementMailer;
|
mailer?: UserManagementMailer;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PaginatatedRequest = AuthenticatedRequest<
|
export type PaginatedRequest = AuthenticatedRequest<
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import type express from 'express';
|
import type express from 'express';
|
||||||
|
|
||||||
import type { AuthenticatedRequest, PaginatatedRequest } from '../../../types';
|
import type { AuthenticatedRequest, PaginatedRequest } from '../../../types';
|
||||||
import { decodeCursor } from '../services/pagination.service';
|
import { decodeCursor } from '../services/pagination.service';
|
||||||
|
|
||||||
export const authorize =
|
export const authorize =
|
||||||
|
@ -22,7 +22,7 @@ export const authorize =
|
||||||
};
|
};
|
||||||
|
|
||||||
export const validCursor = (
|
export const validCursor = (
|
||||||
req: PaginatatedRequest,
|
req: PaginatedRequest,
|
||||||
res: express.Response,
|
res: express.Response,
|
||||||
next: express.NextFunction,
|
next: express.NextFunction,
|
||||||
): express.Response | void => {
|
): express.Response | void => {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import type Bull from 'bull';
|
import type Bull from 'bull';
|
||||||
import type { RedisOptions } from 'ioredis';
|
import type { RedisOptions } from 'ioredis';
|
||||||
|
import { Service } from 'typedi';
|
||||||
import type { IExecuteResponsePromiseData } from 'n8n-workflow';
|
import type { IExecuteResponsePromiseData } from 'n8n-workflow';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { ActiveExecutions } from '@/ActiveExecutions';
|
import { ActiveExecutions } from '@/ActiveExecutions';
|
||||||
import * as WebhookHelpers from '@/WebhookHelpers';
|
import * as WebhookHelpers from '@/WebhookHelpers';
|
||||||
import { Container } from 'typedi';
|
|
||||||
|
|
||||||
export type JobId = Bull.JobId;
|
export type JobId = Bull.JobId;
|
||||||
export type Job = Bull.Job<JobData>;
|
export type Job = Bull.Job<JobData>;
|
||||||
|
@ -24,6 +24,7 @@ export interface WebhookResponse {
|
||||||
response: IExecuteResponsePromiseData;
|
response: IExecuteResponsePromiseData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Service()
|
||||||
export class Queue {
|
export class Queue {
|
||||||
private jobQueue: JobQueue;
|
private jobQueue: JobQueue;
|
||||||
|
|
||||||
|
@ -91,14 +92,3 @@ export class Queue {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let activeQueueInstance: Queue | undefined;
|
|
||||||
|
|
||||||
export async function getInstance(): Promise<Queue> {
|
|
||||||
if (activeQueueInstance === undefined) {
|
|
||||||
activeQueueInstance = new Queue(Container.get(ActiveExecutions));
|
|
||||||
await activeQueueInstance.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
return activeQueueInstance;
|
|
||||||
}
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ import timezones from 'google-timezones-json';
|
||||||
import history from 'connect-history-api-fallback';
|
import history from 'connect-history-api-fallback';
|
||||||
|
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import * as Queue from '@/Queue';
|
import { Queue } from '@/Queue';
|
||||||
import { getSharedWorkflowIds } from '@/WorkflowHelpers';
|
import { getSharedWorkflowIds } from '@/WorkflowHelpers';
|
||||||
|
|
||||||
import { workflowsController } from '@/workflows/workflows.controller';
|
import { workflowsController } from '@/workflows/workflows.controller';
|
||||||
|
@ -103,7 +103,7 @@ import {
|
||||||
isUserManagementEnabled,
|
isUserManagementEnabled,
|
||||||
whereClause,
|
whereClause,
|
||||||
} from '@/UserManagement/UserManagementHelper';
|
} from '@/UserManagement/UserManagementHelper';
|
||||||
import { getInstance as getMailerInstance } from '@/UserManagement/email';
|
import { UserManagementMailer } from '@/UserManagement/email';
|
||||||
import * as Db from '@/Db';
|
import * as Db from '@/Db';
|
||||||
import type {
|
import type {
|
||||||
ICredentialsDb,
|
ICredentialsDb,
|
||||||
|
@ -367,16 +367,23 @@ class Server extends AbstractServer {
|
||||||
|
|
||||||
const logger = LoggerProxy;
|
const logger = LoggerProxy;
|
||||||
const internalHooks = Container.get(InternalHooks);
|
const internalHooks = Container.get(InternalHooks);
|
||||||
const mailer = getMailerInstance();
|
const mailer = Container.get(UserManagementMailer);
|
||||||
const postHog = this.postHog;
|
const postHog = this.postHog;
|
||||||
const samlService = SamlService.getInstance();
|
const samlService = Container.get(SamlService);
|
||||||
|
|
||||||
const controllers: object[] = [
|
const controllers: object[] = [
|
||||||
new AuthController({ config, internalHooks, repositories, logger, postHog }),
|
new AuthController({ config, internalHooks, repositories, logger, postHog }),
|
||||||
new OwnerController({ config, internalHooks, repositories, logger }),
|
new OwnerController({ config, internalHooks, repositories, logger }),
|
||||||
new MeController({ externalHooks, internalHooks, repositories, logger }),
|
new MeController({ externalHooks, internalHooks, repositories, logger }),
|
||||||
new NodeTypesController({ config, nodeTypes }),
|
new NodeTypesController({ config, nodeTypes }),
|
||||||
new PasswordResetController({ config, externalHooks, internalHooks, repositories, logger }),
|
new PasswordResetController({
|
||||||
|
config,
|
||||||
|
externalHooks,
|
||||||
|
internalHooks,
|
||||||
|
mailer,
|
||||||
|
repositories,
|
||||||
|
logger,
|
||||||
|
}),
|
||||||
new TagsController({ config, repositories, externalHooks }),
|
new TagsController({ config, repositories, externalHooks }),
|
||||||
new TranslationController(config, this.credentialTypes),
|
new TranslationController(config, this.credentialTypes),
|
||||||
new UsersController({
|
new UsersController({
|
||||||
|
@ -480,6 +487,10 @@ class Server extends AbstractServer {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (config.getEnv('executions.mode') === 'queue') {
|
||||||
|
await Container.get(Queue).init();
|
||||||
|
}
|
||||||
|
|
||||||
await handleLdapInit();
|
await handleLdapInit();
|
||||||
|
|
||||||
this.registerControllers(ignoredEndpoints);
|
this.registerControllers(ignoredEndpoints);
|
||||||
|
@ -509,7 +520,7 @@ class Server extends AbstractServer {
|
||||||
// set up the initial environment
|
// set up the initial environment
|
||||||
if (isSamlLicensed()) {
|
if (isSamlLicensed()) {
|
||||||
try {
|
try {
|
||||||
await SamlService.getInstance().init();
|
await Container.get(SamlService).init();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
LoggerProxy.error(`SAML initialization failed: ${error.message}`);
|
LoggerProxy.error(`SAML initialization failed: ${error.message}`);
|
||||||
}
|
}
|
||||||
|
@ -953,7 +964,7 @@ class Server extends AbstractServer {
|
||||||
ResponseHelper.send(
|
ResponseHelper.send(
|
||||||
async (req: ExecutionRequest.GetAllCurrent): Promise<IExecutionsSummary[]> => {
|
async (req: ExecutionRequest.GetAllCurrent): Promise<IExecutionsSummary[]> => {
|
||||||
if (config.getEnv('executions.mode') === 'queue') {
|
if (config.getEnv('executions.mode') === 'queue') {
|
||||||
const queue = await Queue.getInstance();
|
const queue = Container.get(Queue);
|
||||||
const currentJobs = await queue.getJobs(['active', 'waiting']);
|
const currentJobs = await queue.getJobs(['active', 'waiting']);
|
||||||
|
|
||||||
const currentlyRunningQueueIds = currentJobs.map((job) => job.data.executionId);
|
const currentlyRunningQueueIds = currentJobs.map((job) => job.data.executionId);
|
||||||
|
@ -1096,7 +1107,7 @@ class Server extends AbstractServer {
|
||||||
} as IExecutionsStopData;
|
} as IExecutionsStopData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const queue = await Queue.getInstance();
|
const queue = Container.get(Queue);
|
||||||
const currentJobs = await queue.getJobs(['active', 'waiting']);
|
const currentJobs = await queue.getJobs(['active', 'waiting']);
|
||||||
|
|
||||||
const job = currentJobs.find((job) => job.data.executionId === req.params.id);
|
const job = currentJobs.find((job) => job.data.executionId === req.params.id);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import type express from 'express';
|
import type express from 'express';
|
||||||
import { compare, genSaltSync, hash } from 'bcryptjs';
|
import { compare, genSaltSync, hash } from 'bcryptjs';
|
||||||
|
import Container from 'typedi';
|
||||||
|
|
||||||
import * as Db from '@/Db';
|
import * as Db from '@/Db';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
import * as ResponseHelper from '@/ResponseHelper';
|
||||||
|
@ -13,7 +14,7 @@ import type { Role } from '@db/entities/Role';
|
||||||
import type { AuthenticatedRequest } from '@/requests';
|
import type { AuthenticatedRequest } from '@/requests';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { getWebhookBaseUrl } from '@/WebhookHelpers';
|
import { getWebhookBaseUrl } from '@/WebhookHelpers';
|
||||||
import { getLicense } from '@/License';
|
import { License } from '@/License';
|
||||||
import { RoleService } from '@/role/role.service';
|
import { RoleService } from '@/role/role.service';
|
||||||
import type { PostHogClient } from '@/posthog';
|
import type { PostHogClient } from '@/posthog';
|
||||||
|
|
||||||
|
@ -55,7 +56,7 @@ export function isUserManagementEnabled(): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isSharingEnabled(): boolean {
|
export function isSharingEnabled(): boolean {
|
||||||
const license = getLicense();
|
const license = Container.get(License);
|
||||||
return (
|
return (
|
||||||
isUserManagementEnabled() &&
|
isUserManagementEnabled() &&
|
||||||
(config.getEnv('enterprise.features.sharing') || license.isSharingEnabled())
|
(config.getEnv('enterprise.features.sharing') || license.isSharingEnabled())
|
||||||
|
|
|
@ -1,9 +1,3 @@
|
||||||
export interface UserManagementMailerImplementation {
|
|
||||||
init: () => Promise<void>;
|
|
||||||
sendMail: (mailData: MailData) => Promise<SendEmailResult>;
|
|
||||||
verifyConnection: () => Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type InviteEmailData = {
|
export type InviteEmailData = {
|
||||||
email: string;
|
email: string;
|
||||||
firstName?: string;
|
firstName?: string;
|
||||||
|
|
|
@ -3,9 +3,9 @@ import type { Transporter } from 'nodemailer';
|
||||||
import { createTransport } from 'nodemailer';
|
import { createTransport } from 'nodemailer';
|
||||||
import { ErrorReporterProxy as ErrorReporter, LoggerProxy as Logger } from 'n8n-workflow';
|
import { ErrorReporterProxy as ErrorReporter, LoggerProxy as Logger } from 'n8n-workflow';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import type { MailData, SendEmailResult, UserManagementMailerImplementation } from './Interfaces';
|
import type { MailData, SendEmailResult } from './Interfaces';
|
||||||
|
|
||||||
export class NodeMailer implements UserManagementMailerImplementation {
|
export class NodeMailer {
|
||||||
private transport?: Transporter;
|
private transport?: Transporter;
|
||||||
|
|
||||||
async init(): Promise<void> {
|
async init(): Promise<void> {
|
||||||
|
|
|
@ -2,13 +2,9 @@ import { existsSync } from 'fs';
|
||||||
import { readFile } from 'fs/promises';
|
import { readFile } from 'fs/promises';
|
||||||
import Handlebars from 'handlebars';
|
import Handlebars from 'handlebars';
|
||||||
import { join as pathJoin } from 'path';
|
import { join as pathJoin } from 'path';
|
||||||
|
import { Service } from 'typedi';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import type {
|
import type { InviteEmailData, PasswordResetData, SendEmailResult } from './Interfaces';
|
||||||
InviteEmailData,
|
|
||||||
PasswordResetData,
|
|
||||||
SendEmailResult,
|
|
||||||
UserManagementMailerImplementation,
|
|
||||||
} from './Interfaces';
|
|
||||||
import { NodeMailer } from './NodeMailer';
|
import { NodeMailer } from './NodeMailer';
|
||||||
|
|
||||||
type Template = HandlebarsTemplateDelegate<unknown>;
|
type Template = HandlebarsTemplateDelegate<unknown>;
|
||||||
|
@ -36,8 +32,9 @@ async function getTemplate(
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Service()
|
||||||
export class UserManagementMailer {
|
export class UserManagementMailer {
|
||||||
private mailer: UserManagementMailerImplementation | undefined;
|
private mailer: NodeMailer | undefined;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// Other implementations can be used in the future.
|
// Other implementations can be used in the future.
|
||||||
|
@ -81,12 +78,3 @@ export class UserManagementMailer {
|
||||||
return result ?? { emailSent: false };
|
return result ?? { emailSent: false };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mailerInstance: UserManagementMailer | undefined;
|
|
||||||
|
|
||||||
export function getInstance(): UserManagementMailer {
|
|
||||||
if (mailerInstance === undefined) {
|
|
||||||
mailerInstance = new UserManagementMailer();
|
|
||||||
}
|
|
||||||
return mailerInstance;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
import { getInstance, UserManagementMailer } from './UserManagementMailer';
|
import { UserManagementMailer } from './UserManagementMailer';
|
||||||
|
|
||||||
export { getInstance, UserManagementMailer };
|
export { UserManagementMailer };
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
LoggerProxy as Logger,
|
LoggerProxy as Logger,
|
||||||
WorkflowOperationError,
|
WorkflowOperationError,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
import { Service } from 'typedi';
|
||||||
import type { FindManyOptions, ObjectLiteral } from 'typeorm';
|
import type { FindManyOptions, ObjectLiteral } from 'typeorm';
|
||||||
import { Not, LessThanOrEqual } from 'typeorm';
|
import { Not, LessThanOrEqual } from 'typeorm';
|
||||||
import { DateUtils } from 'typeorm/util/DateUtils';
|
import { DateUtils } from 'typeorm/util/DateUtils';
|
||||||
|
@ -17,7 +18,6 @@ import { DateUtils } from 'typeorm/util/DateUtils';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import * as Db from '@/Db';
|
import * as Db from '@/Db';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
import * as ResponseHelper from '@/ResponseHelper';
|
||||||
import { ActiveExecutions } from '@/ActiveExecutions';
|
|
||||||
import type {
|
import type {
|
||||||
IExecutionFlattedDb,
|
IExecutionFlattedDb,
|
||||||
IExecutionsStopData,
|
IExecutionsStopData,
|
||||||
|
@ -25,12 +25,9 @@ import type {
|
||||||
} from '@/Interfaces';
|
} from '@/Interfaces';
|
||||||
import { WorkflowRunner } from '@/WorkflowRunner';
|
import { WorkflowRunner } from '@/WorkflowRunner';
|
||||||
import { getWorkflowOwner } from '@/UserManagement/UserManagementHelper';
|
import { getWorkflowOwner } from '@/UserManagement/UserManagementHelper';
|
||||||
import { Container, Service } from 'typedi';
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class WaitTracker {
|
export class WaitTracker {
|
||||||
activeExecutionsInstance: ActiveExecutions;
|
|
||||||
|
|
||||||
private waitingExecutions: {
|
private waitingExecutions: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
executionId: string;
|
executionId: string;
|
||||||
|
@ -41,8 +38,6 @@ export class WaitTracker {
|
||||||
mainTimer: NodeJS.Timeout;
|
mainTimer: NodeJS.Timeout;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.activeExecutionsInstance = Container.get(ActiveExecutions);
|
|
||||||
|
|
||||||
// Poll every 60 seconds a list of upcoming executions
|
// Poll every 60 seconds a list of upcoming executions
|
||||||
this.mainTimer = setInterval(() => {
|
this.mainTimer = setInterval(() => {
|
||||||
this.getWaitingExecutions();
|
this.getWaitingExecutions();
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
/* eslint-disable no-param-reassign */
|
/* eslint-disable no-param-reassign */
|
||||||
import type { INode, WebhookHttpMethod } from 'n8n-workflow';
|
import type { INode, WebhookHttpMethod } from 'n8n-workflow';
|
||||||
import { NodeHelpers, Workflow, LoggerProxy as Logger } from 'n8n-workflow';
|
import { NodeHelpers, Workflow, LoggerProxy as Logger } from 'n8n-workflow';
|
||||||
|
import { Service } from 'typedi';
|
||||||
import type express from 'express';
|
import type express from 'express';
|
||||||
|
|
||||||
import * as Db from '@/Db';
|
import * as Db from '@/Db';
|
||||||
|
@ -13,9 +13,11 @@ import { NodeTypes } from '@/NodeTypes';
|
||||||
import type { IExecutionResponse, IResponseCallbackData, IWorkflowDb } from '@/Interfaces';
|
import type { IExecutionResponse, IResponseCallbackData, IWorkflowDb } from '@/Interfaces';
|
||||||
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
|
import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData';
|
||||||
import { getWorkflowOwner } from '@/UserManagement/UserManagementHelper';
|
import { getWorkflowOwner } from '@/UserManagement/UserManagementHelper';
|
||||||
import { Container } from 'typedi';
|
|
||||||
|
|
||||||
|
@Service()
|
||||||
export class WaitingWebhooks {
|
export class WaitingWebhooks {
|
||||||
|
constructor(private nodeTypes: NodeTypes) {}
|
||||||
|
|
||||||
async executeWebhook(
|
async executeWebhook(
|
||||||
httpMethod: WebhookHttpMethod,
|
httpMethod: WebhookHttpMethod,
|
||||||
fullPath: string,
|
fullPath: string,
|
||||||
|
@ -79,14 +81,13 @@ export class WaitingWebhooks {
|
||||||
|
|
||||||
const { workflowData } = fullExecutionData;
|
const { workflowData } = fullExecutionData;
|
||||||
|
|
||||||
const nodeTypes = Container.get(NodeTypes);
|
|
||||||
const workflow = new Workflow({
|
const workflow = new Workflow({
|
||||||
id: workflowData.id!.toString(),
|
id: workflowData.id!.toString(),
|
||||||
name: workflowData.name,
|
name: workflowData.name,
|
||||||
nodes: workflowData.nodes,
|
nodes: workflowData.nodes,
|
||||||
connections: workflowData.connections,
|
connections: workflowData.connections,
|
||||||
active: workflowData.active,
|
active: workflowData.active,
|
||||||
nodeTypes,
|
nodeTypes: this.nodeTypes,
|
||||||
staticData: workflowData.staticData,
|
staticData: workflowData.staticData,
|
||||||
settings: workflowData.settings,
|
settings: workflowData.settings,
|
||||||
});
|
});
|
||||||
|
|
|
@ -66,7 +66,7 @@ import * as ResponseHelper from '@/ResponseHelper';
|
||||||
import * as WebhookHelpers from '@/WebhookHelpers';
|
import * as WebhookHelpers from '@/WebhookHelpers';
|
||||||
import * as WorkflowHelpers from '@/WorkflowHelpers';
|
import * as WorkflowHelpers from '@/WorkflowHelpers';
|
||||||
import { getWorkflowOwner } from '@/UserManagement/UserManagementHelper';
|
import { getWorkflowOwner } from '@/UserManagement/UserManagementHelper';
|
||||||
import { findSubworkflowStart } from '@/utils';
|
import { findSubworkflowStart, isWorkflowIdValid } from '@/utils';
|
||||||
import { PermissionChecker } from './UserManagement/PermissionChecker';
|
import { PermissionChecker } from './UserManagement/PermissionChecker';
|
||||||
import { WorkflowsService } from './workflows/workflows.services';
|
import { WorkflowsService } from './workflows/workflows.services';
|
||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
|
@ -530,11 +530,7 @@ function hookFunctionsSave(parentProcessMode?: string): IWorkflowExecuteHooks {
|
||||||
const isManualMode = [this.mode, parentProcessMode].includes('manual');
|
const isManualMode = [this.mode, parentProcessMode].includes('manual');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (
|
if (!isManualMode && isWorkflowIdValid(this.workflowData.id) && newStaticData) {
|
||||||
!isManualMode &&
|
|
||||||
WorkflowHelpers.isWorkflowIdValid(this.workflowData.id) &&
|
|
||||||
newStaticData
|
|
||||||
) {
|
|
||||||
// Workflow is saved so update in database
|
// Workflow is saved so update in database
|
||||||
try {
|
try {
|
||||||
await WorkflowHelpers.saveStaticDataById(
|
await WorkflowHelpers.saveStaticDataById(
|
||||||
|
@ -641,7 +637,7 @@ function hookFunctionsSave(parentProcessMode?: string): IWorkflowExecuteHooks {
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflowId = this.workflowData.id;
|
const workflowId = this.workflowData.id;
|
||||||
if (WorkflowHelpers.isWorkflowIdValid(workflowId)) {
|
if (isWorkflowIdValid(workflowId)) {
|
||||||
fullExecutionData.workflowId = workflowId;
|
fullExecutionData.workflowId = workflowId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -729,7 +725,7 @@ function hookFunctionsSaveWorker(): IWorkflowExecuteHooks {
|
||||||
newStaticData: IDataObject,
|
newStaticData: IDataObject,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
if (WorkflowHelpers.isWorkflowIdValid(this.workflowData.id) && newStaticData) {
|
if (isWorkflowIdValid(this.workflowData.id) && newStaticData) {
|
||||||
// Workflow is saved so update in database
|
// Workflow is saved so update in database
|
||||||
try {
|
try {
|
||||||
await WorkflowHelpers.saveStaticDataById(
|
await WorkflowHelpers.saveStaticDataById(
|
||||||
|
@ -776,7 +772,7 @@ function hookFunctionsSaveWorker(): IWorkflowExecuteHooks {
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflowId = this.workflowData.id;
|
const workflowId = this.workflowData.id;
|
||||||
if (WorkflowHelpers.isWorkflowIdValid(workflowId)) {
|
if (isWorkflowIdValid(workflowId)) {
|
||||||
fullExecutionData.workflowId = workflowId;
|
fullExecutionData.workflowId = workflowId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
|
import { Container } from 'typedi';
|
||||||
import type {
|
import type {
|
||||||
IDataObject,
|
IDataObject,
|
||||||
IExecuteData,
|
IExecuteData,
|
||||||
|
@ -32,7 +33,7 @@ import type { User } from '@db/entities/User';
|
||||||
import { whereClause } from '@/UserManagement/UserManagementHelper';
|
import { whereClause } from '@/UserManagement/UserManagementHelper';
|
||||||
import omit from 'lodash.omit';
|
import omit from 'lodash.omit';
|
||||||
import { PermissionChecker } from './UserManagement/PermissionChecker';
|
import { PermissionChecker } from './UserManagement/PermissionChecker';
|
||||||
import { Container } from 'typedi';
|
import { isWorkflowIdValid } from './utils';
|
||||||
|
|
||||||
const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType');
|
const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType');
|
||||||
|
|
||||||
|
@ -74,15 +75,6 @@ export function getDataLastExecutedNodeData(inputData: IRun): ITaskData | undefi
|
||||||
return lastNodeRunData;
|
return lastNodeRunData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns if the given id is a valid workflow id
|
|
||||||
*
|
|
||||||
* @param {(string | null | undefined)} id The id to check
|
|
||||||
*/
|
|
||||||
export function isWorkflowIdValid(id: string | null | undefined): boolean {
|
|
||||||
return !(typeof id === 'string' && isNaN(parseInt(id, 10)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the error workflow
|
* Executes the error workflow
|
||||||
*
|
*
|
||||||
|
|
|
@ -44,7 +44,8 @@ import type {
|
||||||
IWorkflowExecutionDataProcessWithExecution,
|
IWorkflowExecutionDataProcessWithExecution,
|
||||||
} from '@/Interfaces';
|
} from '@/Interfaces';
|
||||||
import { NodeTypes } from '@/NodeTypes';
|
import { NodeTypes } from '@/NodeTypes';
|
||||||
import * as Queue from '@/Queue';
|
import type { Job, JobData, JobQueue, JobResponse } from '@/Queue';
|
||||||
|
import { Queue } from '@/Queue';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
import * as ResponseHelper from '@/ResponseHelper';
|
||||||
import * as WebhookHelpers from '@/WebhookHelpers';
|
import * as WebhookHelpers from '@/WebhookHelpers';
|
||||||
import * as WorkflowHelpers from '@/WorkflowHelpers';
|
import * as WorkflowHelpers from '@/WorkflowHelpers';
|
||||||
|
@ -63,7 +64,7 @@ export class WorkflowRunner {
|
||||||
|
|
||||||
push: Push;
|
push: Push;
|
||||||
|
|
||||||
jobQueue: Queue.JobQueue;
|
jobQueue: JobQueue;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.push = Container.get(Push);
|
this.push = Container.get(Push);
|
||||||
|
@ -167,7 +168,7 @@ export class WorkflowRunner {
|
||||||
await initErrorHandling();
|
await initErrorHandling();
|
||||||
|
|
||||||
if (executionsMode === 'queue') {
|
if (executionsMode === 'queue') {
|
||||||
const queue = await Queue.getInstance();
|
const queue = Container.get(Queue);
|
||||||
this.jobQueue = queue.getBullObjectInstance();
|
this.jobQueue = queue.getBullObjectInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,7 +435,7 @@ export class WorkflowRunner {
|
||||||
this.activeExecutions.attachResponsePromise(executionId, responsePromise);
|
this.activeExecutions.attachResponsePromise(executionId, responsePromise);
|
||||||
}
|
}
|
||||||
|
|
||||||
const jobData: Queue.JobData = {
|
const jobData: JobData = {
|
||||||
executionId,
|
executionId,
|
||||||
loadStaticData: !!loadStaticData,
|
loadStaticData: !!loadStaticData,
|
||||||
};
|
};
|
||||||
|
@ -451,7 +452,7 @@ export class WorkflowRunner {
|
||||||
removeOnComplete: true,
|
removeOnComplete: true,
|
||||||
removeOnFail: true,
|
removeOnFail: true,
|
||||||
};
|
};
|
||||||
let job: Queue.Job;
|
let job: Job;
|
||||||
let hooks: WorkflowHooks;
|
let hooks: WorkflowHooks;
|
||||||
try {
|
try {
|
||||||
job = await this.jobQueue.add(jobData, jobOptions);
|
job = await this.jobQueue.add(jobData, jobOptions);
|
||||||
|
@ -485,7 +486,7 @@ export class WorkflowRunner {
|
||||||
async (resolve, reject, onCancel) => {
|
async (resolve, reject, onCancel) => {
|
||||||
onCancel.shouldReject = false;
|
onCancel.shouldReject = false;
|
||||||
onCancel(async () => {
|
onCancel(async () => {
|
||||||
const queue = await Queue.getInstance();
|
const queue = Container.get(Queue);
|
||||||
await queue.stopJob(job);
|
await queue.stopJob(job);
|
||||||
|
|
||||||
// We use "getWorkflowHooksWorkerExecuter" as "getWorkflowHooksWorkerMain" does not contain the
|
// We use "getWorkflowHooksWorkerExecuter" as "getWorkflowHooksWorkerMain" does not contain the
|
||||||
|
@ -503,11 +504,11 @@ export class WorkflowRunner {
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
const jobData: Promise<Queue.JobResponse> = job.finished();
|
const jobData: Promise<JobResponse> = job.finished();
|
||||||
|
|
||||||
const queueRecoveryInterval = config.getEnv('queue.bull.queueRecoveryInterval');
|
const queueRecoveryInterval = config.getEnv('queue.bull.queueRecoveryInterval');
|
||||||
|
|
||||||
const racingPromises: Array<Promise<Queue.JobResponse | object>> = [jobData];
|
const racingPromises: Array<Promise<JobResponse | object>> = [jobData];
|
||||||
|
|
||||||
let clearWatchdogInterval;
|
let clearWatchdogInterval;
|
||||||
if (queueRecoveryInterval > 0) {
|
if (queueRecoveryInterval > 0) {
|
||||||
|
|
|
@ -54,9 +54,9 @@ import config from '@/config';
|
||||||
import { generateFailedExecutionFromError } from '@/WorkflowHelpers';
|
import { generateFailedExecutionFromError } from '@/WorkflowHelpers';
|
||||||
import { initErrorHandling } from '@/ErrorReporting';
|
import { initErrorHandling } from '@/ErrorReporting';
|
||||||
import { PermissionChecker } from '@/UserManagement/PermissionChecker';
|
import { PermissionChecker } from '@/UserManagement/PermissionChecker';
|
||||||
import { getLicense } from './License';
|
import { License } from '@/License';
|
||||||
import { InternalHooks } from './InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
import { PostHogClient } from './posthog';
|
import { PostHogClient } from '@/posthog';
|
||||||
|
|
||||||
class WorkflowRunnerProcess {
|
class WorkflowRunnerProcess {
|
||||||
data: IWorkflowExecutionDataProcessWithExecution | undefined;
|
data: IWorkflowExecutionDataProcessWithExecution | undefined;
|
||||||
|
@ -127,7 +127,7 @@ class WorkflowRunnerProcess {
|
||||||
// Init db since we need to read the license.
|
// Init db since we need to read the license.
|
||||||
await Db.init();
|
await Db.init();
|
||||||
|
|
||||||
const license = getLicense();
|
const license = Container.get(License);
|
||||||
await license.init(instanceId);
|
await license.init(instanceId);
|
||||||
|
|
||||||
// Start timeout for the execution
|
// Start timeout for the execution
|
||||||
|
|
|
@ -6,11 +6,10 @@ import { ExecutionBaseError } from 'n8n-workflow';
|
||||||
|
|
||||||
import { ActiveExecutions } from '@/ActiveExecutions';
|
import { ActiveExecutions } from '@/ActiveExecutions';
|
||||||
import * as Db from '@/Db';
|
import * as Db from '@/Db';
|
||||||
import * as WorkflowHelpers from '@/WorkflowHelpers';
|
|
||||||
import { WorkflowRunner } from '@/WorkflowRunner';
|
import { WorkflowRunner } from '@/WorkflowRunner';
|
||||||
import type { IWorkflowExecutionDataProcess } from '@/Interfaces';
|
import type { IWorkflowExecutionDataProcess } from '@/Interfaces';
|
||||||
import { getInstanceOwner } from '@/UserManagement/UserManagementHelper';
|
import { getInstanceOwner } from '@/UserManagement/UserManagementHelper';
|
||||||
import { findCliWorkflowStart } from '@/utils';
|
import { findCliWorkflowStart, isWorkflowIdValid } from '@/utils';
|
||||||
import { initEvents } from '@/events';
|
import { initEvents } from '@/events';
|
||||||
import { BaseCommand } from './BaseCommand';
|
import { BaseCommand } from './BaseCommand';
|
||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
|
@ -101,7 +100,7 @@ export class Execute extends BaseCommand {
|
||||||
throw new Error('Failed to retrieve workflow data for requested workflow');
|
throw new Error('Failed to retrieve workflow data for requested workflow');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!WorkflowHelpers.isWorkflowIdValid(workflowId)) {
|
if (!isWorkflowIdValid(workflowId)) {
|
||||||
workflowId = undefined;
|
workflowId = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ import { EDITOR_UI_DIST_DIR, GENERATED_STATIC_DIR } from '@/constants';
|
||||||
import { eventBus } from '@/eventbus';
|
import { eventBus } from '@/eventbus';
|
||||||
import { BaseCommand } from './BaseCommand';
|
import { BaseCommand } from './BaseCommand';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
import { getLicense } from '@/License';
|
import { License } from '@/License';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
|
||||||
const open = require('open');
|
const open = require('open');
|
||||||
|
@ -183,7 +183,7 @@ export class Start extends BaseCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
async initLicense(): Promise<void> {
|
async initLicense(): Promise<void> {
|
||||||
const license = getLicense();
|
const license = Container.get(License);
|
||||||
await license.init(this.instanceId);
|
await license.init(this.instanceId);
|
||||||
|
|
||||||
const activationKey = config.getEnv('license.activationKey');
|
const activationKey = config.getEnv('license.activationKey');
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import type PCancelable from 'p-cancelable';
|
import type PCancelable from 'p-cancelable';
|
||||||
|
import { Container } from 'typedi';
|
||||||
|
|
||||||
import { flags } from '@oclif/command';
|
import { flags } from '@oclif/command';
|
||||||
import { WorkflowExecute } from 'n8n-core';
|
import { WorkflowExecute } from 'n8n-core';
|
||||||
|
@ -15,7 +16,8 @@ import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData'
|
||||||
import { PermissionChecker } from '@/UserManagement/PermissionChecker';
|
import { PermissionChecker } from '@/UserManagement/PermissionChecker';
|
||||||
|
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import * as Queue from '@/Queue';
|
import type { Job, JobId, JobQueue, JobResponse, WebhookResponse } from '@/Queue';
|
||||||
|
import { Queue } from '@/Queue';
|
||||||
import { getWorkflowOwner } from '@/UserManagement/UserManagementHelper';
|
import { getWorkflowOwner } from '@/UserManagement/UserManagementHelper';
|
||||||
import { generateFailedExecutionFromError } from '@/WorkflowHelpers';
|
import { generateFailedExecutionFromError } from '@/WorkflowHelpers';
|
||||||
import { N8N_VERSION } from '@/constants';
|
import { N8N_VERSION } from '@/constants';
|
||||||
|
@ -38,7 +40,7 @@ export class Worker extends BaseCommand {
|
||||||
[key: string]: PCancelable<IRun>;
|
[key: string]: PCancelable<IRun>;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
static jobQueue: Queue.JobQueue;
|
static jobQueue: JobQueue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop n8n in a graceful way.
|
* Stop n8n in a graceful way.
|
||||||
|
@ -86,7 +88,7 @@ export class Worker extends BaseCommand {
|
||||||
await this.exitSuccessFully();
|
await this.exitSuccessFully();
|
||||||
}
|
}
|
||||||
|
|
||||||
async runJob(job: Queue.Job, nodeTypes: INodeTypes): Promise<Queue.JobResponse> {
|
async runJob(job: Job, nodeTypes: INodeTypes): Promise<JobResponse> {
|
||||||
const { executionId, loadStaticData } = job.data;
|
const { executionId, loadStaticData } = job.data;
|
||||||
const executionDb = await Db.collections.Execution.findOneBy({ id: executionId });
|
const executionDb = await Db.collections.Execution.findOneBy({ id: executionId });
|
||||||
|
|
||||||
|
@ -179,7 +181,7 @@ export class Worker extends BaseCommand {
|
||||||
|
|
||||||
additionalData.hooks.hookFunctions.sendResponse = [
|
additionalData.hooks.hookFunctions.sendResponse = [
|
||||||
async (response: IExecuteResponsePromiseData): Promise<void> => {
|
async (response: IExecuteResponsePromiseData): Promise<void> => {
|
||||||
const progress: Queue.WebhookResponse = {
|
const progress: WebhookResponse = {
|
||||||
executionId,
|
executionId,
|
||||||
response: WebhookHelpers.encodeWebhookResponse(response),
|
response: WebhookHelpers.encodeWebhookResponse(response),
|
||||||
};
|
};
|
||||||
|
@ -238,7 +240,8 @@ export class Worker extends BaseCommand {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
const redisConnectionTimeoutLimit = config.getEnv('queue.bull.redis.timeoutThreshold');
|
const redisConnectionTimeoutLimit = config.getEnv('queue.bull.redis.timeoutThreshold');
|
||||||
|
|
||||||
const queue = await Queue.getInstance();
|
const queue = Container.get(Queue);
|
||||||
|
await queue.init();
|
||||||
Worker.jobQueue = queue.getBullObjectInstance();
|
Worker.jobQueue = queue.getBullObjectInstance();
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
Worker.jobQueue.process(flags.concurrency, async (job) => this.runJob(job, this.nodeTypes));
|
Worker.jobQueue.process(flags.concurrency, async (job) => this.runJob(job, this.nodeTypes));
|
||||||
|
@ -248,7 +251,7 @@ export class Worker extends BaseCommand {
|
||||||
this.logger.info(` * Concurrency: ${flags.concurrency}`);
|
this.logger.info(` * Concurrency: ${flags.concurrency}`);
|
||||||
this.logger.info('');
|
this.logger.info('');
|
||||||
|
|
||||||
Worker.jobQueue.on('global:progress', (jobId: Queue.JobId, progress) => {
|
Worker.jobQueue.on('global:progress', (jobId: JobId, progress) => {
|
||||||
// Progress of a job got updated which does get used
|
// Progress of a job got updated which does get used
|
||||||
// to communicate that a job got canceled.
|
// to communicate that a job got canceled.
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
hashPassword,
|
hashPassword,
|
||||||
validatePassword,
|
validatePassword,
|
||||||
} from '@/UserManagement/UserManagementHelper';
|
} from '@/UserManagement/UserManagementHelper';
|
||||||
import * as UserManagementMailer from '@/UserManagement/email';
|
import type { UserManagementMailer } from '@/UserManagement/email';
|
||||||
|
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import type { ILogger } from 'n8n-workflow';
|
import type { ILogger } from 'n8n-workflow';
|
||||||
|
@ -35,6 +35,8 @@ export class PasswordResetController {
|
||||||
|
|
||||||
private readonly internalHooks: IInternalHooksClass;
|
private readonly internalHooks: IInternalHooksClass;
|
||||||
|
|
||||||
|
private readonly mailer: UserManagementMailer;
|
||||||
|
|
||||||
private readonly userRepository: Repository<User>;
|
private readonly userRepository: Repository<User>;
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
|
@ -42,18 +44,21 @@ export class PasswordResetController {
|
||||||
logger,
|
logger,
|
||||||
externalHooks,
|
externalHooks,
|
||||||
internalHooks,
|
internalHooks,
|
||||||
|
mailer,
|
||||||
repositories,
|
repositories,
|
||||||
}: {
|
}: {
|
||||||
config: Config;
|
config: Config;
|
||||||
logger: ILogger;
|
logger: ILogger;
|
||||||
externalHooks: IExternalHooksClass;
|
externalHooks: IExternalHooksClass;
|
||||||
internalHooks: IInternalHooksClass;
|
internalHooks: IInternalHooksClass;
|
||||||
|
mailer: UserManagementMailer;
|
||||||
repositories: Pick<IDatabaseCollections, 'User'>;
|
repositories: Pick<IDatabaseCollections, 'User'>;
|
||||||
}) {
|
}) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.externalHooks = externalHooks;
|
this.externalHooks = externalHooks;
|
||||||
this.internalHooks = internalHooks;
|
this.internalHooks = internalHooks;
|
||||||
|
this.mailer = mailer;
|
||||||
this.userRepository = repositories.User;
|
this.userRepository = repositories.User;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,8 +131,7 @@ export class PasswordResetController {
|
||||||
url.searchParams.append('token', resetPasswordToken);
|
url.searchParams.append('token', resetPasswordToken);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const mailer = UserManagementMailer.getInstance();
|
await this.mailer.passwordReset({
|
||||||
await mailer.passwordReset({
|
|
||||||
email,
|
email,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { getLicense } from '@/License';
|
import { License } from '@/License';
|
||||||
|
import { Container } from 'typedi';
|
||||||
|
|
||||||
export function isLogStreamingEnabled(): boolean {
|
export function isLogStreamingEnabled(): boolean {
|
||||||
const license = getLicense();
|
const license = Container.get(License);
|
||||||
return config.getEnv('enterprise.features.logStreaming') || license.isLogStreamingEnabled();
|
return config.getEnv('enterprise.features.logStreaming') || license.isLogStreamingEnabled();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
import { Container } from 'typedi';
|
||||||
import type { IExecutionFlattedDb } from '@/Interfaces';
|
import type { IExecutionFlattedDb } from '@/Interfaces';
|
||||||
import type { ExecutionStatus } from 'n8n-workflow';
|
import type { ExecutionStatus } from 'n8n-workflow';
|
||||||
import { getLicense } from '@/License';
|
import { License } from '@/License';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
|
|
||||||
export function getStatusUsingPreviousExecutionStatusMethod(
|
export function getStatusUsingPreviousExecutionStatusMethod(
|
||||||
|
@ -20,7 +21,7 @@ export function getStatusUsingPreviousExecutionStatusMethod(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isAdvancedExecutionFiltersEnabled(): boolean {
|
export function isAdvancedExecutionFiltersEnabled(): boolean {
|
||||||
const license = getLicense();
|
const license = Container.get(License);
|
||||||
return (
|
return (
|
||||||
config.getEnv('enterprise.features.advancedExecutionFilters') ||
|
config.getEnv('enterprise.features.advancedExecutionFilters') ||
|
||||||
license.isAdvancedExecutionFiltersEnabled()
|
license.isAdvancedExecutionFiltersEnabled()
|
||||||
|
|
|
@ -26,7 +26,7 @@ import type {
|
||||||
IWorkflowExecutionDataProcess,
|
IWorkflowExecutionDataProcess,
|
||||||
} from '@/Interfaces';
|
} from '@/Interfaces';
|
||||||
import { NodeTypes } from '@/NodeTypes';
|
import { NodeTypes } from '@/NodeTypes';
|
||||||
import * as Queue from '@/Queue';
|
import { Queue } from '@/Queue';
|
||||||
import type { ExecutionRequest } from '@/requests';
|
import type { ExecutionRequest } from '@/requests';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
import * as ResponseHelper from '@/ResponseHelper';
|
||||||
import { getSharedWorkflowIds } from '@/WorkflowHelpers';
|
import { getSharedWorkflowIds } from '@/WorkflowHelpers';
|
||||||
|
@ -197,7 +197,7 @@ export class ExecutionsService {
|
||||||
const executingWorkflowIds: string[] = [];
|
const executingWorkflowIds: string[] = [];
|
||||||
|
|
||||||
if (config.getEnv('executions.mode') === 'queue') {
|
if (config.getEnv('executions.mode') === 'queue') {
|
||||||
const queue = await Queue.getInstance();
|
const queue = Container.get(Queue);
|
||||||
const currentJobs = await queue.getJobs(['active', 'waiting']);
|
const currentJobs = await queue.getJobs(['active', 'waiting']);
|
||||||
executingWorkflowIds.push(...currentJobs.map(({ data }) => data.executionId));
|
executingWorkflowIds.push(...currentJobs.map(({ data }) => data.executionId));
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ export * from './WaitingWebhooks';
|
||||||
export * from './WorkflowCredentials';
|
export * from './WorkflowCredentials';
|
||||||
export * from './WorkflowRunner';
|
export * from './WorkflowRunner';
|
||||||
|
|
||||||
import { ActiveExecutions } from './ActiveExecutions';
|
|
||||||
import * as Db from './Db';
|
import * as Db from './Db';
|
||||||
import * as GenericHelpers from './GenericHelpers';
|
import * as GenericHelpers from './GenericHelpers';
|
||||||
import * as ResponseHelper from './ResponseHelper';
|
import * as ResponseHelper from './ResponseHelper';
|
||||||
|
@ -19,7 +18,6 @@ import * as WorkflowExecuteAdditionalData from './WorkflowExecuteAdditionalData'
|
||||||
import * as WorkflowHelpers from './WorkflowHelpers';
|
import * as WorkflowHelpers from './WorkflowHelpers';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ActiveExecutions,
|
|
||||||
Db,
|
Db,
|
||||||
GenericHelpers,
|
GenericHelpers,
|
||||||
ResponseHelper,
|
ResponseHelper,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { getLicense } from '@/License';
|
import { Container } from 'typedi';
|
||||||
|
import { License } from '@/License';
|
||||||
import type { ILicenseReadResponse } from '@/Interfaces';
|
import type { ILicenseReadResponse } from '@/Interfaces';
|
||||||
import * as Db from '@/Db';
|
import * as Db from '@/Db';
|
||||||
|
|
||||||
|
@ -11,7 +12,7 @@ export class LicenseService {
|
||||||
// Helper for getting the basic license data that we want to return
|
// Helper for getting the basic license data that we want to return
|
||||||
static async getLicenseData(): Promise<ILicenseReadResponse> {
|
static async getLicenseData(): Promise<ILicenseReadResponse> {
|
||||||
const triggerCount = await LicenseService.getActiveTriggerCount();
|
const triggerCount = await LicenseService.getActiveTriggerCount();
|
||||||
const license = getLicense();
|
const license = Container.get(License);
|
||||||
const mainPlan = license.getMainPlan();
|
const mainPlan = license.getMainPlan();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { getLogger } from '@/Logger';
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
import * as ResponseHelper from '@/ResponseHelper';
|
||||||
import type { ILicensePostResponse, ILicenseReadResponse } from '@/Interfaces';
|
import type { ILicensePostResponse, ILicenseReadResponse } from '@/Interfaces';
|
||||||
import { LicenseService } from './License.service';
|
import { LicenseService } from './License.service';
|
||||||
import { getLicense } from '@/License';
|
import { License } from '@/License';
|
||||||
import type { AuthenticatedRequest, LicenseRequest } from '@/requests';
|
import type { AuthenticatedRequest, LicenseRequest } from '@/requests';
|
||||||
import { isInstanceOwner } from '@/PublicApi/v1/handlers/users/users.service';
|
import { isInstanceOwner } from '@/PublicApi/v1/handlers/users/users.service';
|
||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
|
@ -69,7 +69,7 @@ licenseController.post(
|
||||||
'/activate',
|
'/activate',
|
||||||
ResponseHelper.send(async (req: LicenseRequest.Activate): Promise<ILicensePostResponse> => {
|
ResponseHelper.send(async (req: LicenseRequest.Activate): Promise<ILicensePostResponse> => {
|
||||||
// Call the license manager activate function and tell it to throw an error
|
// Call the license manager activate function and tell it to throw an error
|
||||||
const license = getLicense();
|
const license = Container.get(License);
|
||||||
try {
|
try {
|
||||||
await license.activate(req.body.activationKey);
|
await license.activate(req.body.activationKey);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -111,7 +111,7 @@ licenseController.post(
|
||||||
'/renew',
|
'/renew',
|
||||||
ResponseHelper.send(async (): Promise<ILicensePostResponse> => {
|
ResponseHelper.send(async (): Promise<ILicensePostResponse> => {
|
||||||
// Call the license manager activate function and tell it to throw an error
|
// Call the license manager activate function and tell it to throw an error
|
||||||
const license = getLicense();
|
const license = Container.get(License);
|
||||||
try {
|
try {
|
||||||
await license.renew();
|
await license.renew();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { NoXss } from '@db/utils/customValidators';
|
||||||
import type { PublicUser, IExecutionDeleteFilter, IWorkflowDb } from '@/Interfaces';
|
import type { PublicUser, IExecutionDeleteFilter, IWorkflowDb } from '@/Interfaces';
|
||||||
import type { Role } from '@db/entities/Role';
|
import type { Role } from '@db/entities/Role';
|
||||||
import type { User } from '@db/entities/User';
|
import type { User } from '@db/entities/User';
|
||||||
import type * as UserManagementMailer from '@/UserManagement/email/UserManagementMailer';
|
import type { UserManagementMailer } from '@/UserManagement/email';
|
||||||
|
|
||||||
export class UserUpdatePayload implements Pick<User, 'email' | 'firstName' | 'lastName'> {
|
export class UserUpdatePayload implements Pick<User, 'email' | 'firstName' | 'lastName'> {
|
||||||
@IsEmail()
|
@IsEmail()
|
||||||
|
@ -46,7 +46,7 @@ export type AuthenticatedRequest<
|
||||||
RequestQuery = {},
|
RequestQuery = {},
|
||||||
> = Omit<express.Request<RouteParams, ResponseBody, RequestBody, RequestQuery>, 'user'> & {
|
> = Omit<express.Request<RouteParams, ResponseBody, RequestBody, RequestQuery>, 'user'> & {
|
||||||
user: User;
|
user: User;
|
||||||
mailer?: UserManagementMailer.UserManagementMailer;
|
mailer?: UserManagementMailer;
|
||||||
globalMemberRole?: Role;
|
globalMemberRole?: Role;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import type express from 'express';
|
import type express from 'express';
|
||||||
|
import { Service } from 'typedi';
|
||||||
import * as Db from '@/Db';
|
import * as Db from '@/Db';
|
||||||
import type { User } from '@/databases/entities/User';
|
import type { User } from '@/databases/entities/User';
|
||||||
import { jsonParse, LoggerProxy } from 'n8n-workflow';
|
import { jsonParse, LoggerProxy } from 'n8n-workflow';
|
||||||
|
@ -26,9 +27,8 @@ import type { SamlLoginBinding } from './types';
|
||||||
import type { BindingContext, PostBindingContext } from 'samlify/types/src/entity';
|
import type { BindingContext, PostBindingContext } from 'samlify/types/src/entity';
|
||||||
import { validateMetadata, validateResponse } from './samlValidator';
|
import { validateMetadata, validateResponse } from './samlValidator';
|
||||||
|
|
||||||
|
@Service()
|
||||||
export class SamlService {
|
export class SamlService {
|
||||||
private static instance: SamlService;
|
|
||||||
|
|
||||||
private identityProviderInstance: IdentityProviderInstance | undefined;
|
private identityProviderInstance: IdentityProviderInstance | undefined;
|
||||||
|
|
||||||
private _samlPreferences: SamlPreferences = {
|
private _samlPreferences: SamlPreferences = {
|
||||||
|
@ -65,13 +65,6 @@ export class SamlService {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static getInstance(): SamlService {
|
|
||||||
if (!SamlService.instance) {
|
|
||||||
SamlService.instance = new SamlService();
|
|
||||||
}
|
|
||||||
return SamlService.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
async init(): Promise<void> {
|
async init(): Promise<void> {
|
||||||
await this.loadFromDbAndApplySamlPreferences();
|
await this.loadFromDbAndApplySamlPreferences();
|
||||||
setSchemaValidator({
|
setSchemaValidator({
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
import { Container } from 'typedi';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import * as Db from '@/Db';
|
import * as Db from '@/Db';
|
||||||
import { AuthIdentity } from '../../databases/entities/AuthIdentity';
|
import { AuthIdentity } from '@db/entities/AuthIdentity';
|
||||||
import { User } from '../../databases/entities/User';
|
import { User } from '@db/entities/User';
|
||||||
import { getLicense } from '../../License';
|
import { License } from '@/License';
|
||||||
import { AuthError } from '../../ResponseHelper';
|
import { AuthError } from '@/ResponseHelper';
|
||||||
import { hashPassword, isUserManagementEnabled } from '../../UserManagement/UserManagementHelper';
|
import { hashPassword, isUserManagementEnabled } from '@/UserManagement/UserManagementHelper';
|
||||||
import type { SamlPreferences } from './types/samlPreferences';
|
import type { SamlPreferences } from './types/samlPreferences';
|
||||||
import type { SamlUserAttributes } from './types/samlUserAttributes';
|
import type { SamlUserAttributes } from './types/samlUserAttributes';
|
||||||
import type { FlowResult } from 'samlify/types/src/flow';
|
import type { FlowResult } from 'samlify/types/src/flow';
|
||||||
|
@ -44,7 +45,7 @@ export function setSamlLoginLabel(label: string): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isSamlLicensed(): boolean {
|
export function isSamlLicensed(): boolean {
|
||||||
const license = getLicense();
|
const license = Container.get(License);
|
||||||
return (
|
return (
|
||||||
isUserManagementEnabled() &&
|
isUserManagementEnabled() &&
|
||||||
(license.isSamlEnabled() || config.getEnv(SAML_ENTERPRISE_FEATURE_ENABLED))
|
(license.isSamlEnabled() || config.getEnv(SAML_ENTERPRISE_FEATURE_ENABLED))
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { LoggerProxy } from 'n8n-workflow';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import type { IExecutionTrackProperties } from '@/Interfaces';
|
import type { IExecutionTrackProperties } from '@/Interfaces';
|
||||||
import { getLogger } from '@/Logger';
|
import { getLogger } from '@/Logger';
|
||||||
import { getLicense } from '@/License';
|
import { License } from '@/License';
|
||||||
import { LicenseService } from '@/license/License.service';
|
import { LicenseService } from '@/license/License.service';
|
||||||
import { N8N_VERSION } from '@/constants';
|
import { N8N_VERSION } from '@/constants';
|
||||||
import { Service } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
|
@ -39,7 +39,7 @@ export class Telemetry {
|
||||||
|
|
||||||
private executionCountsBuffer: IExecutionsBuffer = {};
|
private executionCountsBuffer: IExecutionsBuffer = {};
|
||||||
|
|
||||||
constructor(private postHog: PostHogClient) {}
|
constructor(private postHog: PostHogClient, private license: License) {}
|
||||||
|
|
||||||
setInstanceId(instanceId: string) {
|
setInstanceId(instanceId: string) {
|
||||||
this.instanceId = instanceId;
|
this.instanceId = instanceId;
|
||||||
|
@ -97,8 +97,8 @@ export class Telemetry {
|
||||||
|
|
||||||
// License info
|
// License info
|
||||||
const pulsePacket = {
|
const pulsePacket = {
|
||||||
plan_name_current: getLicense().getPlanName(),
|
plan_name_current: this.license.getPlanName(),
|
||||||
quota: getLicense().getTriggerLimit(),
|
quota: this.license.getTriggerLimit(),
|
||||||
usage: await LicenseService.getActiveTriggerCount(),
|
usage: await LicenseService.getActiveTriggerCount(),
|
||||||
};
|
};
|
||||||
allPromises.push(this.track('pulse', pulsePacket));
|
allPromises.push(this.track('pulse', pulsePacket));
|
||||||
|
|
|
@ -3,6 +3,13 @@ import { CliWorkflowOperationError, SubworkflowOperationError } from 'n8n-workfl
|
||||||
import type { INode } from 'n8n-workflow';
|
import type { INode } from 'n8n-workflow';
|
||||||
import { START_NODES } from './constants';
|
import { START_NODES } from './constants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if the given id is a valid workflow id
|
||||||
|
*/
|
||||||
|
export function isWorkflowIdValid(id: string | null | undefined): boolean {
|
||||||
|
return !(typeof id === 'string' && isNaN(parseInt(id, 10)));
|
||||||
|
}
|
||||||
|
|
||||||
function findWorkflowStart(executionMode: 'integrated' | 'cli') {
|
function findWorkflowStart(executionMode: 'integrated' | 'cli') {
|
||||||
return function (nodes: INode[]) {
|
return function (nodes: INode[]) {
|
||||||
const executeWorkflowTriggerNode = nodes.find(
|
const executeWorkflowTriggerNode = nodes.find(
|
||||||
|
|
|
@ -41,7 +41,7 @@ import type { User } from '@db/entities/User';
|
||||||
import { getLogger } from '@/Logger';
|
import { getLogger } from '@/Logger';
|
||||||
import { loadPublicApiVersions } from '@/PublicApi/';
|
import { loadPublicApiVersions } from '@/PublicApi/';
|
||||||
import { issueJWT } from '@/auth/jwt';
|
import { issueJWT } from '@/auth/jwt';
|
||||||
import * as UserManagementMailer from '@/UserManagement/email/UserManagementMailer';
|
import { UserManagementMailer } from '@/UserManagement/email/UserManagementMailer';
|
||||||
import {
|
import {
|
||||||
AUTHLESS_ENDPOINTS,
|
AUTHLESS_ENDPOINTS,
|
||||||
COMMUNITY_NODE_VERSION,
|
COMMUNITY_NODE_VERSION,
|
||||||
|
@ -177,7 +177,7 @@ export async function initTestServer({
|
||||||
if (functionEndpoints.length) {
|
if (functionEndpoints.length) {
|
||||||
const externalHooks = Container.get(ExternalHooks);
|
const externalHooks = Container.get(ExternalHooks);
|
||||||
const internalHooks = Container.get(InternalHooks);
|
const internalHooks = Container.get(InternalHooks);
|
||||||
const mailer = UserManagementMailer.getInstance();
|
const mailer = Container.get(UserManagementMailer);
|
||||||
const repositories = Db.collections;
|
const repositories = Db.collections;
|
||||||
|
|
||||||
for (const group of functionEndpoints) {
|
for (const group of functionEndpoints) {
|
||||||
|
@ -226,6 +226,7 @@ export async function initTestServer({
|
||||||
logger,
|
logger,
|
||||||
externalHooks,
|
externalHooks,
|
||||||
internalHooks,
|
internalHooks,
|
||||||
|
mailer,
|
||||||
repositories,
|
repositories,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
@ -20,7 +20,7 @@ import * as testDb from './shared/testDb';
|
||||||
import type { AuthAgent } from './shared/types';
|
import type { AuthAgent } from './shared/types';
|
||||||
import * as utils from './shared/utils';
|
import * as utils from './shared/utils';
|
||||||
|
|
||||||
import * as UserManagementMailer from '@/UserManagement/email/UserManagementMailer';
|
import { UserManagementMailer } from '@/UserManagement/email/UserManagementMailer';
|
||||||
import { NodeMailer } from '@/UserManagement/email/NodeMailer';
|
import { NodeMailer } from '@/UserManagement/email/NodeMailer';
|
||||||
|
|
||||||
jest.mock('@/UserManagement/email/NodeMailer');
|
jest.mock('@/UserManagement/email/NodeMailer');
|
||||||
|
@ -512,7 +512,7 @@ test('UserManagementMailer expect NodeMailer.verifyConnection not be called when
|
||||||
const mockVerifyConnection = jest.spyOn(NodeMailer.prototype, 'verifyConnection');
|
const mockVerifyConnection = jest.spyOn(NodeMailer.prototype, 'verifyConnection');
|
||||||
mockVerifyConnection.mockImplementation(async () => {});
|
mockVerifyConnection.mockImplementation(async () => {});
|
||||||
|
|
||||||
const userManagementMailer = UserManagementMailer.getInstance();
|
const userManagementMailer = new UserManagementMailer();
|
||||||
// NodeMailer.verifyConnection gets called only explicitly
|
// NodeMailer.verifyConnection gets called only explicitly
|
||||||
expect(async () => await userManagementMailer.verifyConnection()).rejects.toThrow();
|
expect(async () => await userManagementMailer.verifyConnection()).rejects.toThrow();
|
||||||
|
|
||||||
|
@ -531,7 +531,7 @@ test('UserManagementMailer expect NodeMailer.verifyConnection to be called when
|
||||||
config.set('userManagement.emails.smtp.host', 'host');
|
config.set('userManagement.emails.smtp.host', 'host');
|
||||||
config.set('userManagement.emails.mode', 'smtp');
|
config.set('userManagement.emails.mode', 'smtp');
|
||||||
|
|
||||||
const userManagementMailer = new UserManagementMailer.UserManagementMailer();
|
const userManagementMailer = new UserManagementMailer();
|
||||||
// NodeMailer.verifyConnection gets called only explicitly
|
// NodeMailer.verifyConnection gets called only explicitly
|
||||||
expect(async () => await userManagementMailer.verifyConnection()).not.toThrow();
|
expect(async () => await userManagementMailer.verifyConnection()).not.toThrow();
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,8 @@ import { Container } from 'typedi';
|
||||||
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
|
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
|
||||||
import { mockInstance } from '../integration/shared/utils';
|
import { mockInstance } from '../integration/shared/utils';
|
||||||
import { Push } from '@/push';
|
import { Push } from '@/push';
|
||||||
|
import { ActiveExecutions } from '@/ActiveExecutions';
|
||||||
|
import { NodeTypes } from '@/NodeTypes';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO:
|
* TODO:
|
||||||
|
@ -157,12 +159,17 @@ describe('ActiveWorkflowRunner', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
externalHooks = mock();
|
externalHooks = mock();
|
||||||
activeWorkflowRunner = new ActiveWorkflowRunner(externalHooks);
|
activeWorkflowRunner = new ActiveWorkflowRunner(
|
||||||
|
new ActiveExecutions(),
|
||||||
|
externalHooks,
|
||||||
|
Container.get(NodeTypes),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
await activeWorkflowRunner.removeAll();
|
await activeWorkflowRunner.removeAll();
|
||||||
databaseActiveWorkflowsCount = 0;
|
databaseActiveWorkflowsCount = 0;
|
||||||
|
databaseActiveWorkflowsList = [];
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Telemetry } from '@/telemetry';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { flushPromises } from './Helpers';
|
import { flushPromises } from './Helpers';
|
||||||
import { PostHogClient } from '@/posthog';
|
import { PostHogClient } from '@/posthog';
|
||||||
|
import { mock } from 'jest-mock-extended';
|
||||||
|
|
||||||
jest.unmock('@/telemetry');
|
jest.unmock('@/telemetry');
|
||||||
jest.mock('@/license/License.service', () => {
|
jest.mock('@/license/License.service', () => {
|
||||||
|
@ -45,7 +46,7 @@ describe('Telemetry', () => {
|
||||||
const postHog = new PostHogClient();
|
const postHog = new PostHogClient();
|
||||||
postHog.init(instanceId);
|
postHog.init(instanceId);
|
||||||
|
|
||||||
telemetry = new Telemetry(postHog);
|
telemetry = new Telemetry(postHog, mock());
|
||||||
telemetry.setInstanceId(instanceId);
|
telemetry.setInstanceId(instanceId);
|
||||||
(telemetry as any).rudderStack = {
|
(telemetry as any).rudderStack = {
|
||||||
flush: () => {},
|
flush: () => {},
|
||||||
|
|
Loading…
Reference in a new issue