mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-02 07:01:30 -08:00
refactor(core): Use Dependency Injection for all Controller classes (no-changelog) (#8146)
## Review / Merge checklist - [x] PR title and summary are descriptive
This commit is contained in:
parent
518a99e528
commit
f69ddcd796
|
@ -10,7 +10,6 @@ import { N8N_VERSION, inDevelopment, inTest } from '@/constants';
|
||||||
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
||||||
import * as Db from '@/Db';
|
import * as Db from '@/Db';
|
||||||
import { N8nInstanceType } from '@/Interfaces';
|
import { N8nInstanceType } from '@/Interfaces';
|
||||||
import type { IExternalHooksClass } from '@/Interfaces';
|
|
||||||
import { ExternalHooks } from '@/ExternalHooks';
|
import { ExternalHooks } from '@/ExternalHooks';
|
||||||
import { send, sendErrorResponse } from '@/ResponseHelper';
|
import { send, sendErrorResponse } from '@/ResponseHelper';
|
||||||
import { rawBodyReader, bodyParser, corsMiddleware } from '@/middlewares';
|
import { rawBodyReader, bodyParser, corsMiddleware } from '@/middlewares';
|
||||||
|
@ -31,7 +30,7 @@ export abstract class AbstractServer {
|
||||||
|
|
||||||
readonly app: express.Application;
|
readonly app: express.Application;
|
||||||
|
|
||||||
protected externalHooks: IExternalHooksClass;
|
protected externalHooks: ExternalHooks;
|
||||||
|
|
||||||
protected activeWorkflowRunner: ActiveWorkflowRunner;
|
protected activeWorkflowRunner: ActiveWorkflowRunner;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
import { Service } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
import type {
|
import type { IExternalHooksFileData, IExternalHooksFunctions } from '@/Interfaces';
|
||||||
IExternalHooksClass,
|
|
||||||
IExternalHooksFileData,
|
|
||||||
IExternalHooksFunctions,
|
|
||||||
} from '@/Interfaces';
|
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { UserRepository } from '@db/repositories/user.repository';
|
import { UserRepository } from '@db/repositories/user.repository';
|
||||||
import { CredentialsRepository } from '@db/repositories/credentials.repository';
|
import { CredentialsRepository } from '@db/repositories/credentials.repository';
|
||||||
|
@ -13,7 +9,7 @@ import { WorkflowRepository } from '@db/repositories/workflow.repository';
|
||||||
import { ApplicationError } from 'n8n-workflow';
|
import { ApplicationError } from 'n8n-workflow';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class ExternalHooks implements IExternalHooksClass {
|
export class ExternalHooks {
|
||||||
externalHooks: {
|
externalHooks: {
|
||||||
[key: string]: Array<() => {}>;
|
[key: string]: Array<() => {}>;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import { Authorized, Get, Post, RestController, RequireGlobalScope } from '@/decorators';
|
import { Authorized, Get, Post, RestController, RequireGlobalScope } from '@/decorators';
|
||||||
import { ExternalSecretsRequest } from '@/requests';
|
import { ExternalSecretsRequest } from '@/requests';
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import { Service } from 'typedi';
|
|
||||||
import { ExternalSecretsService } from './ExternalSecrets.service.ee';
|
import { ExternalSecretsService } from './ExternalSecrets.service.ee';
|
||||||
import { ExternalSecretsProviderNotFoundError } from '@/errors/external-secrets-provider-not-found.error';
|
import { ExternalSecretsProviderNotFoundError } from '@/errors/external-secrets-provider-not-found.error';
|
||||||
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
||||||
|
|
||||||
@Service()
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/external-secrets')
|
@RestController('/external-secrets')
|
||||||
export class ExternalSecretsController {
|
export class ExternalSecretsController {
|
||||||
|
|
|
@ -44,6 +44,7 @@ import type { CredentialsRepository } from '@db/repositories/credentials.reposit
|
||||||
import type { SettingsRepository } from '@db/repositories/settings.repository';
|
import type { SettingsRepository } from '@db/repositories/settings.repository';
|
||||||
import type { UserRepository } from '@db/repositories/user.repository';
|
import type { UserRepository } from '@db/repositories/user.repository';
|
||||||
import type { WorkflowRepository } from '@db/repositories/workflow.repository';
|
import type { WorkflowRepository } from '@db/repositories/workflow.repository';
|
||||||
|
import type { ExternalHooks } from './ExternalHooks';
|
||||||
import type { LICENSE_FEATURES, LICENSE_QUOTAS } from './constants';
|
import type { LICENSE_FEATURES, LICENSE_QUOTAS } from './constants';
|
||||||
import type { WorkflowWithSharingsAndCredentials } from './workflows/workflows.types';
|
import type { WorkflowWithSharingsAndCredentials } from './workflows/workflows.types';
|
||||||
import type { WorkerJobStatusSummary } from './services/orchestration/worker/types';
|
import type { WorkerJobStatusSummary } from './services/orchestration/worker/types';
|
||||||
|
@ -254,11 +255,6 @@ export interface IExternalHooksFunctions {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IExternalHooksClass {
|
|
||||||
init(): Promise<void>;
|
|
||||||
run(hookName: string, hookParameters?: any[]): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type WebhookCORSRequest = Request & { method: 'OPTIONS' };
|
export type WebhookCORSRequest = Request & { method: 'OPTIONS' };
|
||||||
|
|
||||||
export type WebhookRequest = Request<{ path: string }> & {
|
export type WebhookRequest = Request<{ path: string }> & {
|
||||||
|
@ -326,134 +322,6 @@ export interface ITelemetryUserDeletionData {
|
||||||
migration_user_id?: string;
|
migration_user_id?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IInternalHooksClass {
|
|
||||||
onN8nStop(): Promise<void>;
|
|
||||||
onServerStarted(
|
|
||||||
diagnosticInfo: IDiagnosticInfo,
|
|
||||||
firstWorkflowCreatedAt?: Date,
|
|
||||||
): Promise<unknown[]>;
|
|
||||||
onPersonalizationSurveySubmitted(userId: string, answers: Record<string, string>): Promise<void>;
|
|
||||||
onWorkflowCreated(user: User, workflow: IWorkflowBase, publicApi: boolean): Promise<void>;
|
|
||||||
onWorkflowDeleted(user: User, workflowId: string, publicApi: boolean): Promise<void>;
|
|
||||||
onWorkflowSaved(user: User, workflow: IWorkflowBase, publicApi: boolean): Promise<void>;
|
|
||||||
onWorkflowBeforeExecute(executionId: string, data: IWorkflowExecutionDataProcess): Promise<void>;
|
|
||||||
onWorkflowPostExecute(
|
|
||||||
executionId: string,
|
|
||||||
workflow: IWorkflowBase,
|
|
||||||
runData?: IRun,
|
|
||||||
userId?: string,
|
|
||||||
): Promise<void>;
|
|
||||||
onNodeBeforeExecute(
|
|
||||||
executionId: string,
|
|
||||||
workflow: IWorkflowBase,
|
|
||||||
nodeName: string,
|
|
||||||
): Promise<void>;
|
|
||||||
onNodePostExecute(executionId: string, workflow: IWorkflowBase, nodeName: string): Promise<void>;
|
|
||||||
onUserDeletion(userDeletionData: {
|
|
||||||
user: User;
|
|
||||||
telemetryData: ITelemetryUserDeletionData;
|
|
||||||
publicApi: boolean;
|
|
||||||
}): Promise<void>;
|
|
||||||
onUserInvite(userInviteData: {
|
|
||||||
user: User;
|
|
||||||
target_user_id: string[];
|
|
||||||
public_api: boolean;
|
|
||||||
email_sent: boolean;
|
|
||||||
invitee_role: string;
|
|
||||||
}): Promise<void>;
|
|
||||||
onUserRoleChange(userInviteData: {
|
|
||||||
user: User;
|
|
||||||
target_user_id: string;
|
|
||||||
public_api: boolean;
|
|
||||||
target_user_new_role: string;
|
|
||||||
}): Promise<void>;
|
|
||||||
onUserReinvite(userReinviteData: {
|
|
||||||
user: User;
|
|
||||||
target_user_id: string;
|
|
||||||
public_api: boolean;
|
|
||||||
}): Promise<void>;
|
|
||||||
onUserUpdate(userUpdateData: { user: User; fields_changed: string[] }): Promise<void>;
|
|
||||||
onUserInviteEmailClick(userInviteClickData: { inviter: User; invitee: User }): Promise<void>;
|
|
||||||
onUserPasswordResetEmailClick(userPasswordResetData: { user: User }): Promise<void>;
|
|
||||||
onUserTransactionalEmail(
|
|
||||||
userTransactionalEmailData: {
|
|
||||||
user_id: string;
|
|
||||||
message_type: 'Reset password' | 'New user invite' | 'Resend invite';
|
|
||||||
public_api: boolean;
|
|
||||||
},
|
|
||||||
user?: User,
|
|
||||||
): Promise<void>;
|
|
||||||
onEmailFailed(failedEmailData: {
|
|
||||||
user: User;
|
|
||||||
message_type: 'Reset password' | 'New user invite' | 'Resend invite';
|
|
||||||
public_api: boolean;
|
|
||||||
}): Promise<void>;
|
|
||||||
onUserCreatedCredentials(userCreatedCredentialsData: {
|
|
||||||
user: User;
|
|
||||||
credential_name: string;
|
|
||||||
credential_type: string;
|
|
||||||
credential_id: string;
|
|
||||||
public_api: boolean;
|
|
||||||
}): Promise<void>;
|
|
||||||
|
|
||||||
onUserSharedCredentials(userSharedCredentialsData: {
|
|
||||||
user: User;
|
|
||||||
credential_name: string;
|
|
||||||
credential_type: string;
|
|
||||||
credential_id: string;
|
|
||||||
user_id_sharer: string;
|
|
||||||
user_ids_sharees_added: string[];
|
|
||||||
sharees_removed: number | null;
|
|
||||||
}): Promise<void>;
|
|
||||||
onUserPasswordResetRequestClick(userPasswordResetData: { user: User }): Promise<void>;
|
|
||||||
onInstanceOwnerSetup(instanceOwnerSetupData: { user_id: string }, user?: User): Promise<void>;
|
|
||||||
onUserSignup(
|
|
||||||
user: User,
|
|
||||||
userSignupData: {
|
|
||||||
user_type: AuthProviderType;
|
|
||||||
was_disabled_ldap_user: boolean;
|
|
||||||
},
|
|
||||||
): Promise<void>;
|
|
||||||
onCommunityPackageInstallFinished(installationData: {
|
|
||||||
user: User;
|
|
||||||
input_string: string;
|
|
||||||
package_name: string;
|
|
||||||
success: boolean;
|
|
||||||
package_version?: string;
|
|
||||||
package_node_names?: string[];
|
|
||||||
package_author?: string;
|
|
||||||
package_author_email?: string;
|
|
||||||
failure_reason?: string;
|
|
||||||
}): Promise<void>;
|
|
||||||
onCommunityPackageUpdateFinished(updateData: {
|
|
||||||
user: User;
|
|
||||||
package_name: string;
|
|
||||||
package_version_current: string;
|
|
||||||
package_version_new: string;
|
|
||||||
package_node_names: string[];
|
|
||||||
package_author?: string;
|
|
||||||
package_author_email?: string;
|
|
||||||
}): Promise<void>;
|
|
||||||
onCommunityPackageDeleteFinished(deleteData: {
|
|
||||||
user: User;
|
|
||||||
package_name: string;
|
|
||||||
package_version?: string;
|
|
||||||
package_node_names?: string[];
|
|
||||||
package_author?: string;
|
|
||||||
package_author_email?: string;
|
|
||||||
}): Promise<void>;
|
|
||||||
onApiKeyCreated(apiKeyDeletedData: { user: User; public_api: boolean }): Promise<void>;
|
|
||||||
onApiKeyDeleted(apiKeyDeletedData: { user: User; public_api: boolean }): Promise<void>;
|
|
||||||
onVariableCreated(createData: { variable_type: string }): Promise<void>;
|
|
||||||
onExternalSecretsProviderSettingsSaved(saveData: {
|
|
||||||
user_id?: string;
|
|
||||||
vault_type: string;
|
|
||||||
is_valid: boolean;
|
|
||||||
is_new: boolean;
|
|
||||||
error_message?: string;
|
|
||||||
}): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IVersionNotificationSettings {
|
export interface IVersionNotificationSettings {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
endpoint: string;
|
endpoint: string;
|
||||||
|
@ -839,7 +707,7 @@ export interface PublicUser {
|
||||||
export interface N8nApp {
|
export interface N8nApp {
|
||||||
app: Application;
|
app: Application;
|
||||||
restEndpoint: string;
|
restEndpoint: string;
|
||||||
externalHooks: IExternalHooksClass;
|
externalHooks: ExternalHooks;
|
||||||
activeWorkflowRunner: ActiveWorkflowRunner;
|
activeWorkflowRunner: ActiveWorkflowRunner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ import { TelemetryHelpers } from 'n8n-workflow';
|
||||||
import { get as pslGet } from 'psl';
|
import { get as pslGet } from 'psl';
|
||||||
import type {
|
import type {
|
||||||
IDiagnosticInfo,
|
IDiagnosticInfo,
|
||||||
IInternalHooksClass,
|
|
||||||
ITelemetryUserDeletionData,
|
ITelemetryUserDeletionData,
|
||||||
IWorkflowDb,
|
IWorkflowDb,
|
||||||
IExecutionTrackProperties,
|
IExecutionTrackProperties,
|
||||||
|
@ -49,7 +48,7 @@ function userToPayload(user: User): {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class InternalHooks implements IInternalHooksClass {
|
export class InternalHooks {
|
||||||
constructor(
|
constructor(
|
||||||
private telemetry: Telemetry,
|
private telemetry: Telemetry,
|
||||||
private nodeTypes: NodeTypes,
|
private nodeTypes: NodeTypes,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Container, Service } from 'typedi';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fsPromises from 'fs/promises';
|
import fsPromises from 'fs/promises';
|
||||||
|
|
||||||
import type { DirectoryLoader, Types } from 'n8n-core';
|
import type { Class, DirectoryLoader, Types } from 'n8n-core';
|
||||||
import {
|
import {
|
||||||
CUSTOM_EXTENSION_ENV,
|
CUSTOM_EXTENSION_ENV,
|
||||||
InstanceSettings,
|
InstanceSettings,
|
||||||
|
@ -250,7 +250,7 @@ export class LoadNodesAndCredentials {
|
||||||
* Run a loader of source files of nodes and credentials in a directory.
|
* Run a loader of source files of nodes and credentials in a directory.
|
||||||
*/
|
*/
|
||||||
private async runDirectoryLoader<T extends DirectoryLoader>(
|
private async runDirectoryLoader<T extends DirectoryLoader>(
|
||||||
constructor: new (...args: ConstructorParameters<typeof DirectoryLoader>) => T,
|
constructor: Class<T, ConstructorParameters<typeof DirectoryLoader>>,
|
||||||
dir: string,
|
dir: string,
|
||||||
) {
|
) {
|
||||||
const loader = new constructor(dir, this.excludeNodes, this.includeNodes);
|
const loader = new constructor(dir, this.excludeNodes, this.includeNodes);
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||||
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
|
|
||||||
/* eslint-disable prefer-const */
|
|
||||||
/* eslint-disable @typescript-eslint/no-shadow */
|
/* eslint-disable @typescript-eslint/no-shadow */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
/* 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 { Container, Service } from 'typedi';
|
||||||
|
@ -20,14 +17,9 @@ import type { ServeStaticOptions } from 'serve-static';
|
||||||
import type { FindManyOptions, FindOptionsWhere } from 'typeorm';
|
import type { FindManyOptions, FindOptionsWhere } from 'typeorm';
|
||||||
import { Not, In } from 'typeorm';
|
import { Not, In } from 'typeorm';
|
||||||
|
|
||||||
import { InstanceSettings } from 'n8n-core';
|
import { type Class, InstanceSettings } from 'n8n-core';
|
||||||
|
|
||||||
import type {
|
import type { ExecutionStatus, IExecutionsSummary, IN8nUISettings } from 'n8n-workflow';
|
||||||
ICredentialTypes,
|
|
||||||
ExecutionStatus,
|
|
||||||
IExecutionsSummary,
|
|
||||||
IN8nUISettings,
|
|
||||||
} from 'n8n-workflow';
|
|
||||||
import { jsonParse } from 'n8n-workflow';
|
import { jsonParse } from 'n8n-workflow';
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -52,7 +44,6 @@ import { registerController } from '@/decorators';
|
||||||
import { AuthController } from '@/controllers/auth.controller';
|
import { AuthController } from '@/controllers/auth.controller';
|
||||||
import { BinaryDataController } from '@/controllers/binaryData.controller';
|
import { BinaryDataController } from '@/controllers/binaryData.controller';
|
||||||
import { DynamicNodeParametersController } from '@/controllers/dynamicNodeParameters.controller';
|
import { DynamicNodeParametersController } from '@/controllers/dynamicNodeParameters.controller';
|
||||||
import { LdapController } from '@/controllers/ldap.controller';
|
|
||||||
import { MeController } from '@/controllers/me.controller';
|
import { MeController } from '@/controllers/me.controller';
|
||||||
import { MFAController } from '@/controllers/mfa.controller';
|
import { MFAController } from '@/controllers/mfa.controller';
|
||||||
import { NodeTypesController } from '@/controllers/nodeTypes.controller';
|
import { NodeTypesController } from '@/controllers/nodeTypes.controller';
|
||||||
|
@ -70,9 +61,7 @@ import { isApiEnabled, loadPublicApiVersions } from '@/PublicApi';
|
||||||
import type { ICredentialsOverwrite, IDiagnosticInfo, IExecutionsStopData } from '@/Interfaces';
|
import type { ICredentialsOverwrite, IDiagnosticInfo, IExecutionsStopData } from '@/Interfaces';
|
||||||
import { ActiveExecutions } from '@/ActiveExecutions';
|
import { ActiveExecutions } from '@/ActiveExecutions';
|
||||||
import { CredentialsOverwrites } from '@/CredentialsOverwrites';
|
import { CredentialsOverwrites } from '@/CredentialsOverwrites';
|
||||||
import { CredentialTypes } from '@/CredentialTypes';
|
|
||||||
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
|
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
|
||||||
import { NodeTypes } from '@/NodeTypes';
|
|
||||||
import * as ResponseHelper from '@/ResponseHelper';
|
import * as ResponseHelper from '@/ResponseHelper';
|
||||||
import { WaitTracker } from '@/WaitTracker';
|
import { WaitTracker } from '@/WaitTracker';
|
||||||
import { toHttpNodeParameters } from '@/CurlConverterHelper';
|
import { toHttpNodeParameters } from '@/CurlConverterHelper';
|
||||||
|
@ -91,7 +80,6 @@ import { getStatusUsingPreviousExecutionStatusMethod } from './executions/execut
|
||||||
import { SamlController } from './sso/saml/routes/saml.controller.ee';
|
import { SamlController } from './sso/saml/routes/saml.controller.ee';
|
||||||
import { SamlService } from './sso/saml/saml.service.ee';
|
import { SamlService } from './sso/saml/saml.service.ee';
|
||||||
import { VariablesController } from './environments/variables/variables.controller.ee';
|
import { VariablesController } from './environments/variables/variables.controller.ee';
|
||||||
import { LdapManager } from './Ldap/LdapManager.ee';
|
|
||||||
import {
|
import {
|
||||||
isLdapCurrentAuthenticationMethod,
|
isLdapCurrentAuthenticationMethod,
|
||||||
isSamlCurrentAuthenticationMethod,
|
isSamlCurrentAuthenticationMethod,
|
||||||
|
@ -101,16 +89,10 @@ import { SourceControlController } from '@/environments/sourceControl/sourceCont
|
||||||
|
|
||||||
import type { ExecutionEntity } from '@db/entities/ExecutionEntity';
|
import type { ExecutionEntity } from '@db/entities/ExecutionEntity';
|
||||||
import { ExecutionRepository } from '@db/repositories/execution.repository';
|
import { ExecutionRepository } from '@db/repositories/execution.repository';
|
||||||
import { SettingsRepository } from '@db/repositories/settings.repository';
|
|
||||||
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
|
|
||||||
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
|
|
||||||
import { WorkflowRepository } from '@db/repositories/workflow.repository';
|
import { WorkflowRepository } from '@db/repositories/workflow.repository';
|
||||||
|
|
||||||
import { MfaService } from './Mfa/mfa.service';
|
|
||||||
import { handleMfaDisable, isMfaFeatureEnabled } from './Mfa/helpers';
|
import { handleMfaDisable, isMfaFeatureEnabled } from './Mfa/helpers';
|
||||||
import type { FrontendService } from './services/frontend.service';
|
import type { FrontendService } from './services/frontend.service';
|
||||||
import { RoleService } from './services/role.service';
|
|
||||||
import { UserService } from './services/user.service';
|
|
||||||
import { ActiveWorkflowsController } from './controllers/activeWorkflows.controller';
|
import { ActiveWorkflowsController } from './controllers/activeWorkflows.controller';
|
||||||
import { OrchestrationController } from './controllers/orchestration.controller';
|
import { OrchestrationController } from './controllers/orchestration.controller';
|
||||||
import { WorkflowHistoryController } from './workflows/workflowHistory/workflowHistory.controller.ee';
|
import { WorkflowHistoryController } from './workflows/workflowHistory/workflowHistory.controller.ee';
|
||||||
|
@ -120,7 +102,6 @@ import { RoleController } from './controllers/role.controller';
|
||||||
import { BadRequestError } from './errors/response-errors/bad-request.error';
|
import { BadRequestError } from './errors/response-errors/bad-request.error';
|
||||||
import { NotFoundError } from './errors/response-errors/not-found.error';
|
import { NotFoundError } from './errors/response-errors/not-found.error';
|
||||||
import { MultiMainSetup } from './services/orchestration/main/MultiMainSetup.ee';
|
import { MultiMainSetup } from './services/orchestration/main/MultiMainSetup.ee';
|
||||||
import { PasswordUtility } from './services/password.utility';
|
|
||||||
|
|
||||||
const exec = promisify(callbackExec);
|
const exec = promisify(callbackExec);
|
||||||
|
|
||||||
|
@ -136,16 +117,8 @@ export class Server extends AbstractServer {
|
||||||
|
|
||||||
private loadNodesAndCredentials: LoadNodesAndCredentials;
|
private loadNodesAndCredentials: LoadNodesAndCredentials;
|
||||||
|
|
||||||
private nodeTypes: NodeTypes;
|
|
||||||
|
|
||||||
private credentialTypes: ICredentialTypes;
|
|
||||||
|
|
||||||
private frontendService?: FrontendService;
|
private frontendService?: FrontendService;
|
||||||
|
|
||||||
private postHog: PostHogClient;
|
|
||||||
|
|
||||||
private collaborationService: CollaborationService;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('main');
|
super('main');
|
||||||
|
|
||||||
|
@ -159,8 +132,6 @@ export class Server extends AbstractServer {
|
||||||
|
|
||||||
async start() {
|
async start() {
|
||||||
this.loadNodesAndCredentials = Container.get(LoadNodesAndCredentials);
|
this.loadNodesAndCredentials = Container.get(LoadNodesAndCredentials);
|
||||||
this.credentialTypes = Container.get(CredentialTypes);
|
|
||||||
this.nodeTypes = Container.get(NodeTypes);
|
|
||||||
|
|
||||||
if (!config.getEnv('endpoints.disableUi')) {
|
if (!config.getEnv('endpoints.disableUi')) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
@ -169,7 +140,6 @@ export class Server extends AbstractServer {
|
||||||
|
|
||||||
this.activeExecutionsInstance = Container.get(ActiveExecutions);
|
this.activeExecutionsInstance = Container.get(ActiveExecutions);
|
||||||
this.waitTracker = Container.get(WaitTracker);
|
this.waitTracker = Container.get(WaitTracker);
|
||||||
this.postHog = Container.get(PostHogClient);
|
|
||||||
|
|
||||||
this.presetCredentialsLoaded = false;
|
this.presetCredentialsLoaded = false;
|
||||||
this.endpointPresetCredentials = config.getEnv('credentials.overwrite.endpoint');
|
this.endpointPresetCredentials = config.getEnv('credentials.overwrite.endpoint');
|
||||||
|
@ -241,101 +211,70 @@ export class Server extends AbstractServer {
|
||||||
.then(async (workflow) =>
|
.then(async (workflow) =>
|
||||||
Container.get(InternalHooks).onServerStarted(diagnosticInfo, workflow?.createdAt),
|
Container.get(InternalHooks).onServerStarted(diagnosticInfo, workflow?.createdAt),
|
||||||
);
|
);
|
||||||
this.collaborationService = Container.get(CollaborationService);
|
|
||||||
|
Container.get(CollaborationService);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async registerControllers(ignoredEndpoints: Readonly<string[]>) {
|
private async registerControllers(ignoredEndpoints: Readonly<string[]>) {
|
||||||
const { app, externalHooks, activeWorkflowRunner, nodeTypes, logger } = this;
|
const { app } = this;
|
||||||
setupAuthMiddlewares(app, ignoredEndpoints, this.restEndpoint);
|
setupAuthMiddlewares(app, ignoredEndpoints, this.restEndpoint);
|
||||||
|
|
||||||
const internalHooks = Container.get(InternalHooks);
|
const controllers: Array<Class<object>> = [
|
||||||
const userService = Container.get(UserService);
|
EventBusController,
|
||||||
const postHog = this.postHog;
|
EventBusControllerEE,
|
||||||
const mfaService = Container.get(MfaService);
|
AuthController,
|
||||||
|
LicenseController,
|
||||||
const controllers: object[] = [
|
OAuth1CredentialController,
|
||||||
new EventBusController(),
|
OAuth2CredentialController,
|
||||||
new EventBusControllerEE(),
|
OwnerController,
|
||||||
Container.get(AuthController),
|
MeController,
|
||||||
Container.get(LicenseController),
|
DynamicNodeParametersController,
|
||||||
Container.get(OAuth1CredentialController),
|
NodeTypesController,
|
||||||
Container.get(OAuth2CredentialController),
|
PasswordResetController,
|
||||||
new OwnerController(
|
TagsController,
|
||||||
config,
|
TranslationController,
|
||||||
logger,
|
UsersController,
|
||||||
internalHooks,
|
SamlController,
|
||||||
Container.get(SettingsRepository),
|
SourceControlController,
|
||||||
userService,
|
WorkflowStatisticsController,
|
||||||
Container.get(PasswordUtility),
|
ExternalSecretsController,
|
||||||
postHog,
|
OrchestrationController,
|
||||||
),
|
WorkflowHistoryController,
|
||||||
Container.get(MeController),
|
BinaryDataController,
|
||||||
Container.get(DynamicNodeParametersController),
|
VariablesController,
|
||||||
new NodeTypesController(config, nodeTypes),
|
InvitationController,
|
||||||
Container.get(PasswordResetController),
|
VariablesController,
|
||||||
Container.get(TagsController),
|
RoleController,
|
||||||
new TranslationController(config, this.credentialTypes),
|
ActiveWorkflowsController,
|
||||||
new UsersController(
|
|
||||||
logger,
|
|
||||||
externalHooks,
|
|
||||||
internalHooks,
|
|
||||||
Container.get(SharedCredentialsRepository),
|
|
||||||
Container.get(SharedWorkflowRepository),
|
|
||||||
activeWorkflowRunner,
|
|
||||||
Container.get(RoleService),
|
|
||||||
userService,
|
|
||||||
Container.get(License),
|
|
||||||
),
|
|
||||||
Container.get(SamlController),
|
|
||||||
Container.get(SourceControlController),
|
|
||||||
Container.get(WorkflowStatisticsController),
|
|
||||||
Container.get(ExternalSecretsController),
|
|
||||||
Container.get(OrchestrationController),
|
|
||||||
Container.get(WorkflowHistoryController),
|
|
||||||
Container.get(BinaryDataController),
|
|
||||||
Container.get(VariablesController),
|
|
||||||
new InvitationController(
|
|
||||||
config,
|
|
||||||
logger,
|
|
||||||
internalHooks,
|
|
||||||
externalHooks,
|
|
||||||
Container.get(UserService),
|
|
||||||
Container.get(License),
|
|
||||||
Container.get(PasswordUtility),
|
|
||||||
postHog,
|
|
||||||
),
|
|
||||||
Container.get(VariablesController),
|
|
||||||
Container.get(RoleController),
|
|
||||||
Container.get(ActiveWorkflowsController),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
if (Container.get(MultiMainSetup).isEnabled) {
|
if (Container.get(MultiMainSetup).isEnabled) {
|
||||||
const { DebugController } = await import('./controllers/debug.controller');
|
const { DebugController } = await import('./controllers/debug.controller');
|
||||||
controllers.push(Container.get(DebugController));
|
controllers.push(DebugController);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLdapEnabled()) {
|
if (isLdapEnabled()) {
|
||||||
const { service, sync } = LdapManager.getInstance();
|
const { LdapController } = await require('@/controllers/ldap.controller');
|
||||||
controllers.push(new LdapController(service, sync, internalHooks));
|
controllers.push(LdapController);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.getEnv('nodes.communityPackages.enabled')) {
|
if (config.getEnv('nodes.communityPackages.enabled')) {
|
||||||
const { CommunityPackagesController } = await import(
|
const { CommunityPackagesController } = await import(
|
||||||
'@/controllers/communityPackages.controller'
|
'@/controllers/communityPackages.controller'
|
||||||
);
|
);
|
||||||
controllers.push(Container.get(CommunityPackagesController));
|
controllers.push(CommunityPackagesController);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inE2ETests) {
|
if (inE2ETests) {
|
||||||
const { E2EController } = await import('./controllers/e2e.controller');
|
const { E2EController } = await import('./controllers/e2e.controller');
|
||||||
controllers.push(Container.get(E2EController));
|
controllers.push(E2EController);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMfaFeatureEnabled()) {
|
if (isMfaFeatureEnabled()) {
|
||||||
controllers.push(new MFAController(mfaService));
|
controllers.push(MFAController);
|
||||||
}
|
}
|
||||||
|
|
||||||
controllers.forEach((controller) => registerController(app, config, controller));
|
controllers.forEach((controller) => registerController(app, controller));
|
||||||
}
|
}
|
||||||
|
|
||||||
async configure(): Promise<void> {
|
async configure(): Promise<void> {
|
||||||
|
@ -356,7 +295,7 @@ export class Server extends AbstractServer {
|
||||||
await this.externalHooks.run('frontend.settings', [frontendService.getSettings()]);
|
await this.externalHooks.run('frontend.settings', [frontendService.getSettings()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.postHog.init();
|
await Container.get(PostHogClient).init();
|
||||||
|
|
||||||
const publicApiEndpoint = config.getEnv('publicApi.path');
|
const publicApiEndpoint = config.getEnv('publicApi.path');
|
||||||
const excludeEndpoints = config.getEnv('security.excludeEndpoints');
|
const excludeEndpoints = config.getEnv('security.excludeEndpoints');
|
||||||
|
@ -450,11 +389,7 @@ export class Server extends AbstractServer {
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
this.app.post(
|
this.app.post(
|
||||||
`/${this.restEndpoint}/curl-to-json`,
|
`/${this.restEndpoint}/curl-to-json`,
|
||||||
ResponseHelper.send(
|
ResponseHelper.send(async (req: CurlHelper.ToJson) => {
|
||||||
async (
|
|
||||||
req: CurlHelper.ToJson,
|
|
||||||
res: express.Response,
|
|
||||||
): Promise<{ [key: string]: string }> => {
|
|
||||||
const curlCommand = req.body.curlCommand ?? '';
|
const curlCommand = req.body.curlCommand ?? '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -463,8 +398,7 @@ export class Server extends AbstractServer {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new BadRequestError('Invalid cURL command');
|
throw new BadRequestError('Invalid cURL command');
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
@ -687,9 +621,7 @@ export class Server extends AbstractServer {
|
||||||
// Returns all the available timezones
|
// Returns all the available timezones
|
||||||
this.app.get(
|
this.app.get(
|
||||||
`/${this.restEndpoint}/options/timezones`,
|
`/${this.restEndpoint}/options/timezones`,
|
||||||
ResponseHelper.send(async (req: express.Request, res: express.Response): Promise<object> => {
|
ResponseHelper.send(async () => timezones),
|
||||||
return timezones;
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
@ -701,13 +633,8 @@ export class Server extends AbstractServer {
|
||||||
this.app.get(
|
this.app.get(
|
||||||
`/${this.restEndpoint}/settings`,
|
`/${this.restEndpoint}/settings`,
|
||||||
ResponseHelper.send(
|
ResponseHelper.send(
|
||||||
async (req: express.Request, res: express.Response): Promise<IN8nUISettings> => {
|
async (req: express.Request): Promise<IN8nUISettings> =>
|
||||||
void Container.get(InternalHooks).onFrontendSettingsAPI(
|
frontendService.getSettings(req.headers.sessionid as string),
|
||||||
req.headers.sessionid as string,
|
|
||||||
);
|
|
||||||
|
|
||||||
return frontendService.getSettings();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -766,6 +693,7 @@ export class Server extends AbstractServer {
|
||||||
};
|
};
|
||||||
|
|
||||||
const serveIcons: express.RequestHandler = async (req, res) => {
|
const serveIcons: express.RequestHandler = async (req, res) => {
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
let { scope, packageName } = req.params;
|
let { scope, packageName } = req.params;
|
||||||
if (scope) packageName = `@${scope}/${packageName}`;
|
if (scope) packageName = `@${scope}/${packageName}`;
|
||||||
const filePath = this.loadNodesAndCredentials.resolveIcon(packageName, req.originalUrl);
|
const filePath = this.loadNodesAndCredentials.resolveIcon(packageName, req.originalUrl);
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { initErrorHandling } from '@/ErrorReporting';
|
||||||
import { ExternalHooks } from '@/ExternalHooks';
|
import { ExternalHooks } from '@/ExternalHooks';
|
||||||
import { NodeTypes } from '@/NodeTypes';
|
import { NodeTypes } from '@/NodeTypes';
|
||||||
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
|
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
|
||||||
import type { IExternalHooksClass, N8nInstanceType } from '@/Interfaces';
|
import type { N8nInstanceType } from '@/Interfaces';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
import { PostHogClient } from '@/posthog';
|
import { PostHogClient } from '@/posthog';
|
||||||
import { License } from '@/License';
|
import { License } from '@/License';
|
||||||
|
@ -27,7 +27,7 @@ import { ShutdownService } from '@/shutdown/Shutdown.service';
|
||||||
export abstract class BaseCommand extends Command {
|
export abstract class BaseCommand extends Command {
|
||||||
protected logger = Container.get(Logger);
|
protected logger = Container.get(Logger);
|
||||||
|
|
||||||
protected externalHooks?: IExternalHooksClass;
|
protected externalHooks?: ExternalHooks;
|
||||||
|
|
||||||
protected nodeTypes: NodeTypes;
|
protected nodeTypes: NodeTypes;
|
||||||
|
|
||||||
|
|
|
@ -73,4 +73,3 @@ setGlobalState({
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default config;
|
export default config;
|
||||||
export type Config = typeof config;
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { Service } from 'typedi';
|
|
||||||
import { Authorized, Get, RestController } from '@/decorators';
|
import { Authorized, Get, RestController } from '@/decorators';
|
||||||
import { WorkflowRequest } from '@/requests';
|
import { WorkflowRequest } from '@/requests';
|
||||||
import { ActiveWorkflowsService } from '@/services/activeWorkflows.service';
|
import { ActiveWorkflowsService } from '@/services/activeWorkflows.service';
|
||||||
|
|
||||||
@Service()
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/active-workflows')
|
@RestController('/active-workflows')
|
||||||
export class ActiveWorkflowsController {
|
export class ActiveWorkflowsController {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import { Service } from 'typedi';
|
|
||||||
import { Authorized, Get, Post, RestController } from '@/decorators';
|
import { Authorized, Get, Post, RestController } from '@/decorators';
|
||||||
import { issueCookie, resolveJwt } from '@/auth/jwt';
|
import { issueCookie, resolveJwt } from '@/auth/jwt';
|
||||||
import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from '@/constants';
|
import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from '@/constants';
|
||||||
|
@ -27,7 +26,6 @@ import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
|
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
|
||||||
import { ApplicationError } from 'n8n-workflow';
|
import { ApplicationError } from 'n8n-workflow';
|
||||||
|
|
||||||
@Service()
|
|
||||||
@RestController()
|
@RestController()
|
||||||
export class AuthController {
|
export class AuthController {
|
||||||
constructor(
|
constructor(
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import { Service } from 'typedi';
|
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { BinaryDataService, FileNotFoundError, isValidNonDefaultMode } from 'n8n-core';
|
import { BinaryDataService, FileNotFoundError, isValidNonDefaultMode } from 'n8n-core';
|
||||||
import { Get, RestController } from '@/decorators';
|
import { Get, RestController } from '@/decorators';
|
||||||
import { BinaryDataRequest } from '@/requests';
|
import { BinaryDataRequest } from '@/requests';
|
||||||
|
|
||||||
@RestController('/binary-data')
|
@RestController('/binary-data')
|
||||||
@Service()
|
|
||||||
export class BinaryDataController {
|
export class BinaryDataController {
|
||||||
constructor(private readonly binaryDataService: BinaryDataService) {}
|
constructor(private readonly binaryDataService: BinaryDataService) {}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Service } from 'typedi';
|
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from 'express';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import {
|
import {
|
||||||
|
@ -42,14 +41,13 @@ export function isNpmError(error: unknown): error is { code: number; stdout: str
|
||||||
return typeof error === 'object' && error !== null && 'code' in error && 'stdout' in error;
|
return typeof error === 'object' && error !== null && 'code' in error && 'stdout' in error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Service()
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/community-packages')
|
@RestController('/community-packages')
|
||||||
export class CommunityPackagesController {
|
export class CommunityPackagesController {
|
||||||
constructor(
|
constructor(
|
||||||
private push: Push,
|
private readonly push: Push,
|
||||||
private internalHooks: InternalHooks,
|
private readonly internalHooks: InternalHooks,
|
||||||
private communityPackagesService: CommunityPackagesService,
|
private readonly communityPackagesService: CommunityPackagesService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
// TODO: move this into a new decorator `@IfConfig('executions.mode', 'queue')`
|
// TODO: move this into a new decorator `@IfConfig('executions.mode', 'queue')`
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Service } from 'typedi';
|
|
||||||
import { Get, RestController } from '@/decorators';
|
import { Get, RestController } from '@/decorators';
|
||||||
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
||||||
import { MultiMainSetup } from '@/services/orchestration/main/MultiMainSetup.ee';
|
import { MultiMainSetup } from '@/services/orchestration/main/MultiMainSetup.ee';
|
||||||
|
@ -6,7 +5,6 @@ import { WorkflowRepository } from '@/databases/repositories/workflow.repository
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
|
|
||||||
@RestController('/debug')
|
@RestController('/debug')
|
||||||
@Service()
|
|
||||||
export class DebugController {
|
export class DebugController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly multiMainSetup: MultiMainSetup,
|
private readonly multiMainSetup: MultiMainSetup,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Service } from 'typedi';
|
|
||||||
import type { RequestHandler } from 'express';
|
import type { RequestHandler } from 'express';
|
||||||
import { NextFunction, Response } from 'express';
|
import { NextFunction, Response } from 'express';
|
||||||
import type {
|
import type {
|
||||||
|
@ -22,7 +21,6 @@ const assertMethodName: RequestHandler = (req, res, next) => {
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
@Service()
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/dynamic-node-parameters')
|
@RestController('/dynamic-node-parameters')
|
||||||
export class DynamicNodeParametersController {
|
export class DynamicNodeParametersController {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Request } from 'express';
|
import { Request } from 'express';
|
||||||
import { Container, Service } from 'typedi';
|
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import type { Role } from '@db/entities/Role';
|
import type { Role } from '@db/entities/Role';
|
||||||
|
@ -64,7 +63,6 @@ type PushRequest = Request<
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
@Service()
|
|
||||||
@NoAuthRequired()
|
@NoAuthRequired()
|
||||||
@RestController('/e2e')
|
@RestController('/e2e')
|
||||||
export class E2EController {
|
export class E2EController {
|
||||||
|
@ -89,12 +87,13 @@ export class E2EController {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
license: License,
|
license: License,
|
||||||
private roleRepo: RoleRepository,
|
private readonly roleRepo: RoleRepository,
|
||||||
private settingsRepo: SettingsRepository,
|
private readonly settingsRepo: SettingsRepository,
|
||||||
private userRepo: UserRepository,
|
private readonly userRepo: UserRepository,
|
||||||
private workflowRunner: ActiveWorkflowRunner,
|
private readonly workflowRunner: ActiveWorkflowRunner,
|
||||||
private mfaService: MfaService,
|
private readonly mfaService: MfaService,
|
||||||
private cacheService: CacheService,
|
private readonly cacheService: CacheService,
|
||||||
|
private readonly push: Push,
|
||||||
private readonly passwordUtility: PasswordUtility,
|
private readonly passwordUtility: PasswordUtility,
|
||||||
) {
|
) {
|
||||||
license.isFeatureEnabled = (feature: BooleanLicenseFeature) =>
|
license.isFeatureEnabled = (feature: BooleanLicenseFeature) =>
|
||||||
|
@ -112,14 +111,8 @@ export class E2EController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('/push')
|
@Post('/push')
|
||||||
async push(req: PushRequest) {
|
async pushSend(req: PushRequest) {
|
||||||
const pushInstance = Container.get(Push);
|
this.push.broadcast(req.body.type, req.body.data);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
const sessionId = Object.keys(pushInstance.getBackend().connections as object)[0];
|
|
||||||
|
|
||||||
pushInstance.send(req.body.type, req.body.data, sessionId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch('/feature')
|
@Patch('/feature')
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import Container, { Service } from 'typedi';
|
import { Response } from 'express';
|
||||||
|
|
||||||
|
import config from '@/config';
|
||||||
import { Authorized, NoAuthRequired, Post, RequireGlobalScope, RestController } from '@/decorators';
|
import { Authorized, NoAuthRequired, Post, RequireGlobalScope, RestController } from '@/decorators';
|
||||||
import { issueCookie } from '@/auth/jwt';
|
import { issueCookie } from '@/auth/jwt';
|
||||||
import { RESPONSE_ERROR_MESSAGES } from '@/constants';
|
import { RESPONSE_ERROR_MESSAGES } from '@/constants';
|
||||||
import { Response } from 'express';
|
|
||||||
import { UserRequest } from '@/requests';
|
import { UserRequest } from '@/requests';
|
||||||
import { Config } from '@/config';
|
|
||||||
import { IExternalHooksClass, IInternalHooksClass } from '@/Interfaces';
|
|
||||||
import { License } from '@/License';
|
import { License } from '@/License';
|
||||||
import { UserService } from '@/services/user.service';
|
import { UserService } from '@/services/user.service';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
|
@ -17,20 +16,20 @@ import type { User } from '@/databases/entities/User';
|
||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
|
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
|
||||||
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
|
import { ExternalHooks } from '@/ExternalHooks';
|
||||||
|
|
||||||
@Service()
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/invitations')
|
@RestController('/invitations')
|
||||||
export class InvitationController {
|
export class InvitationController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly config: Config,
|
|
||||||
private readonly logger: Logger,
|
private readonly logger: Logger,
|
||||||
private readonly internalHooks: IInternalHooksClass,
|
private readonly internalHooks: InternalHooks,
|
||||||
private readonly externalHooks: IExternalHooksClass,
|
private readonly externalHooks: ExternalHooks,
|
||||||
private readonly userService: UserService,
|
private readonly userService: UserService,
|
||||||
private readonly license: License,
|
private readonly license: License,
|
||||||
private readonly passwordUtility: PasswordUtility,
|
private readonly passwordUtility: PasswordUtility,
|
||||||
private readonly postHog?: PostHogClient,
|
private readonly postHog: PostHogClient,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,7 +39,7 @@ export class InvitationController {
|
||||||
@Post('/')
|
@Post('/')
|
||||||
@RequireGlobalScope('user:create')
|
@RequireGlobalScope('user:create')
|
||||||
async inviteUser(req: UserRequest.Invite) {
|
async inviteUser(req: UserRequest.Invite) {
|
||||||
const isWithinUsersLimit = Container.get(License).isWithinUsersLimit();
|
const isWithinUsersLimit = this.license.isWithinUsersLimit();
|
||||||
|
|
||||||
if (isSamlLicensedAndEnabled()) {
|
if (isSamlLicensedAndEnabled()) {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
|
@ -58,7 +57,7 @@ export class InvitationController {
|
||||||
throw new UnauthorizedError(RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED);
|
throw new UnauthorizedError(RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.config.getEnv('userManagement.isInstanceOwnerSetUp')) {
|
if (!config.getEnv('userManagement.isInstanceOwnerSetUp')) {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
'Request to send email invite(s) to user(s) failed because the owner account is not set up',
|
'Request to send email invite(s) to user(s) failed because the owner account is not set up',
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import pick from 'lodash/pick';
|
import pick from 'lodash/pick';
|
||||||
import { Authorized, Get, Post, Put, RestController, RequireGlobalScope } from '@/decorators';
|
import { Authorized, Get, Post, Put, RestController, RequireGlobalScope } from '@/decorators';
|
||||||
import { getLdapConfig, getLdapSynchronizations, updateLdapConfig } from '@/Ldap/helpers';
|
import { getLdapConfig, getLdapSynchronizations, updateLdapConfig } from '@/Ldap/helpers';
|
||||||
import { LdapService } from '@/Ldap/LdapService.ee';
|
import { LdapManager } from '@/Ldap/LdapManager.ee';
|
||||||
import { LdapSync } from '@/Ldap/LdapSync.ee';
|
import type { LdapService } from '@/Ldap/LdapService.ee';
|
||||||
|
import type { LdapSync } from '@/Ldap/LdapSync.ee';
|
||||||
import { LdapConfiguration } from '@/Ldap/types';
|
import { LdapConfiguration } from '@/Ldap/types';
|
||||||
import { NON_SENSIBLE_LDAP_CONFIG_PROPERTIES } from '@/Ldap/constants';
|
import { NON_SENSIBLE_LDAP_CONFIG_PROPERTIES } from '@/Ldap/constants';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
|
@ -11,11 +12,15 @@ import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/ldap')
|
@RestController('/ldap')
|
||||||
export class LdapController {
|
export class LdapController {
|
||||||
constructor(
|
private ldapService: LdapService;
|
||||||
private ldapService: LdapService,
|
|
||||||
private ldapSync: LdapSync,
|
private ldapSync: LdapSync;
|
||||||
private internalHooks: InternalHooks,
|
|
||||||
) {}
|
constructor(private readonly internalHooks: InternalHooks) {
|
||||||
|
const { service, sync } = LdapManager.getInstance();
|
||||||
|
this.ldapService = service;
|
||||||
|
this.ldapSync = sync;
|
||||||
|
}
|
||||||
|
|
||||||
@Get('/config')
|
@Get('/config')
|
||||||
@RequireGlobalScope('ldap:manage')
|
@RequireGlobalScope('ldap:manage')
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
import { plainToInstance } from 'class-transformer';
|
import { plainToInstance } from 'class-transformer';
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import { Service } from 'typedi';
|
|
||||||
import { randomBytes } from 'crypto';
|
import { randomBytes } from 'crypto';
|
||||||
import { Authorized, Delete, Get, Patch, Post, RestController } from '@/decorators';
|
import { Authorized, Delete, Get, Patch, Post, RestController } from '@/decorators';
|
||||||
import { PasswordUtility } from '@/services/password.utility';
|
import { PasswordUtility } from '@/services/password.utility';
|
||||||
|
@ -22,7 +21,6 @@ import { ExternalHooks } from '@/ExternalHooks';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
@Service()
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/me')
|
@RestController('/me')
|
||||||
export class MeController {
|
export class MeController {
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import { Service } from 'typedi';
|
|
||||||
import { Authorized, Delete, Get, Post, RestController } from '@/decorators';
|
import { Authorized, Delete, Get, Post, RestController } from '@/decorators';
|
||||||
import { AuthenticatedRequest, MFA } from '@/requests';
|
import { AuthenticatedRequest, MFA } from '@/requests';
|
||||||
import { MfaService } from '@/Mfa/mfa.service';
|
import { MfaService } from '@/Mfa/mfa.service';
|
||||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
@Service()
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/mfa')
|
@RestController('/mfa')
|
||||||
export class MFAController {
|
export class MFAController {
|
||||||
|
|
|
@ -3,22 +3,19 @@ import get from 'lodash/get';
|
||||||
import { Request } from 'express';
|
import { Request } from 'express';
|
||||||
import type { INodeTypeDescription, INodeTypeNameVersion } from 'n8n-workflow';
|
import type { INodeTypeDescription, INodeTypeNameVersion } from 'n8n-workflow';
|
||||||
import { Authorized, Post, RestController } from '@/decorators';
|
import { Authorized, Post, RestController } from '@/decorators';
|
||||||
import { Config } from '@/config';
|
import config from '@/config';
|
||||||
import { NodeTypes } from '@/NodeTypes';
|
import { NodeTypes } from '@/NodeTypes';
|
||||||
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/node-types')
|
@RestController('/node-types')
|
||||||
export class NodeTypesController {
|
export class NodeTypesController {
|
||||||
constructor(
|
constructor(private readonly nodeTypes: NodeTypes) {}
|
||||||
private readonly config: Config,
|
|
||||||
private readonly nodeTypes: NodeTypes,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@Post('/')
|
@Post('/')
|
||||||
async getNodeInfo(req: Request) {
|
async getNodeInfo(req: Request) {
|
||||||
const nodeInfos = get(req, 'body.nodeInfos', []) as INodeTypeNameVersion[];
|
const nodeInfos = get(req, 'body.nodeInfos', []) as INodeTypeNameVersion[];
|
||||||
|
|
||||||
const defaultLocale = this.config.getEnv('defaultLocale');
|
const defaultLocale = config.getEnv('defaultLocale');
|
||||||
|
|
||||||
if (defaultLocale === 'en') {
|
if (defaultLocale === 'en') {
|
||||||
return nodeInfos.reduce<INodeTypeDescription[]>((acc, { name, version }) => {
|
return nodeInfos.reduce<INodeTypeDescription[]>((acc, { name, version }) => {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Service } from 'typedi';
|
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import type { AxiosRequestConfig } from 'axios';
|
import type { AxiosRequestConfig } from 'axios';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
@ -30,7 +29,6 @@ const algorithmMap = {
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@Service()
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/oauth1-credential')
|
@RestController('/oauth1-credential')
|
||||||
export class OAuth1CredentialController extends AbstractOAuthController {
|
export class OAuth1CredentialController extends AbstractOAuthController {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Service } from 'typedi';
|
|
||||||
import type { ClientOAuth2Options } from '@n8n/client-oauth2';
|
import type { ClientOAuth2Options } from '@n8n/client-oauth2';
|
||||||
import { ClientOAuth2 } from '@n8n/client-oauth2';
|
import { ClientOAuth2 } from '@n8n/client-oauth2';
|
||||||
import Csrf from 'csrf';
|
import Csrf from 'csrf';
|
||||||
|
@ -31,7 +30,6 @@ interface CsrfStateParam {
|
||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Service()
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/oauth2-credential')
|
@RestController('/oauth2-credential')
|
||||||
export class OAuth2CredentialController extends AbstractOAuthController {
|
export class OAuth2CredentialController extends AbstractOAuthController {
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import { Authorized, Post, RestController, RequireGlobalScope } from '@/decorators';
|
import { Authorized, Post, RestController, RequireGlobalScope } from '@/decorators';
|
||||||
import { OrchestrationRequest } from '@/requests';
|
import { OrchestrationRequest } from '@/requests';
|
||||||
import { Service } from 'typedi';
|
|
||||||
import { SingleMainSetup } from '@/services/orchestration/main/SingleMainSetup';
|
import { SingleMainSetup } from '@/services/orchestration/main/SingleMainSetup';
|
||||||
import { License } from '../License';
|
import { License } from '@/License';
|
||||||
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/orchestration')
|
@RestController('/orchestration')
|
||||||
@Service()
|
|
||||||
export class OrchestrationController {
|
export class OrchestrationController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly singleMainSetup: SingleMainSetup,
|
private readonly singleMainSetup: SingleMainSetup,
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
|
import { Response } from 'express';
|
||||||
|
|
||||||
|
import config from '@/config';
|
||||||
import { validateEntity } from '@/GenericHelpers';
|
import { validateEntity } from '@/GenericHelpers';
|
||||||
import { Authorized, Post, RestController } from '@/decorators';
|
import { Authorized, Post, RestController } from '@/decorators';
|
||||||
import { PasswordUtility } from '@/services/password.utility';
|
import { PasswordUtility } from '@/services/password.utility';
|
||||||
import { issueCookie } from '@/auth/jwt';
|
import { issueCookie } from '@/auth/jwt';
|
||||||
import { Response } from 'express';
|
|
||||||
import { Config } from '@/config';
|
|
||||||
import { OwnerRequest } from '@/requests';
|
import { OwnerRequest } from '@/requests';
|
||||||
import { IInternalHooksClass } from '@/Interfaces';
|
|
||||||
import { SettingsRepository } from '@db/repositories/settings.repository';
|
import { SettingsRepository } from '@db/repositories/settings.repository';
|
||||||
import { PostHogClient } from '@/posthog';
|
import { PostHogClient } from '@/posthog';
|
||||||
import { UserService } from '@/services/user.service';
|
import { UserService } from '@/services/user.service';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
|
|
||||||
@Authorized(['global', 'owner'])
|
@Authorized(['global', 'owner'])
|
||||||
@RestController('/owner')
|
@RestController('/owner')
|
||||||
export class OwnerController {
|
export class OwnerController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly config: Config,
|
|
||||||
private readonly logger: Logger,
|
private readonly logger: Logger,
|
||||||
private readonly internalHooks: IInternalHooksClass,
|
private readonly internalHooks: InternalHooks,
|
||||||
private readonly settingsRepository: SettingsRepository,
|
private readonly settingsRepository: SettingsRepository,
|
||||||
private readonly userService: UserService,
|
private readonly userService: UserService,
|
||||||
private readonly passwordUtility: PasswordUtility,
|
private readonly passwordUtility: PasswordUtility,
|
||||||
private readonly postHog?: PostHogClient,
|
private readonly postHog: PostHogClient,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,7 +35,7 @@ export class OwnerController {
|
||||||
const { email, firstName, lastName, password } = req.body;
|
const { email, firstName, lastName, password } = req.body;
|
||||||
const { id: userId, globalRole } = req.user;
|
const { id: userId, globalRole } = req.user;
|
||||||
|
|
||||||
if (this.config.getEnv('userManagement.isInstanceOwnerSetUp')) {
|
if (config.getEnv('userManagement.isInstanceOwnerSetUp')) {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
'Request to claim instance ownership failed because instance owner already exists',
|
'Request to claim instance ownership failed because instance owner already exists',
|
||||||
{
|
{
|
||||||
|
@ -94,7 +94,7 @@ export class OwnerController {
|
||||||
{ value: JSON.stringify(true) },
|
{ value: JSON.stringify(true) },
|
||||||
);
|
);
|
||||||
|
|
||||||
this.config.set('userManagement.isInstanceOwnerSetUp', true);
|
config.set('userManagement.isInstanceOwnerSetUp', true);
|
||||||
|
|
||||||
this.logger.debug('Setting isInstanceOwnerSetUp updated successfully', { userId });
|
this.logger.debug('Setting isInstanceOwnerSetUp updated successfully', { userId });
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import { rateLimit } from 'express-rate-limit';
|
import { rateLimit } from 'express-rate-limit';
|
||||||
import { Service } from 'typedi';
|
|
||||||
import { IsNull, Not } from 'typeorm';
|
import { IsNull, Not } from 'typeorm';
|
||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
|
|
||||||
|
@ -31,7 +30,6 @@ const throttle = rateLimit({
|
||||||
message: { message: 'Too many requests' },
|
message: { message: 'Too many requests' },
|
||||||
});
|
});
|
||||||
|
|
||||||
@Service()
|
|
||||||
@RestController()
|
@RestController()
|
||||||
export class PasswordResetController {
|
export class PasswordResetController {
|
||||||
constructor(
|
constructor(
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { License } from '@/License';
|
import { License } from '@/License';
|
||||||
import { Get, RestController } from '@/decorators';
|
import { Get, RestController } from '@/decorators';
|
||||||
import { RoleService } from '@/services/role.service';
|
import { RoleService } from '@/services/role.service';
|
||||||
import { Service } from 'typedi';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
@RestController('/roles')
|
@RestController('/roles')
|
||||||
export class RoleController {
|
export class RoleController {
|
||||||
constructor(
|
constructor(
|
||||||
|
|
|
@ -12,16 +12,14 @@ import {
|
||||||
} from '@/decorators';
|
} from '@/decorators';
|
||||||
import { TagService } from '@/services/tag.service';
|
import { TagService } from '@/services/tag.service';
|
||||||
import { TagsRequest } from '@/requests';
|
import { TagsRequest } from '@/requests';
|
||||||
import { Service } from 'typedi';
|
|
||||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/tags')
|
@RestController('/tags')
|
||||||
@Service()
|
|
||||||
export class TagsController {
|
export class TagsController {
|
||||||
private config = config;
|
private config = config;
|
||||||
|
|
||||||
constructor(private tagService: TagService) {}
|
constructor(private readonly tagService: TagService) {}
|
||||||
|
|
||||||
// TODO: move this into a new decorator `@IfEnabled('workflowTagsDisabled')`
|
// TODO: move this into a new decorator `@IfEnabled('workflowTagsDisabled')`
|
||||||
@Middleware()
|
@Middleware()
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import type { Request } from 'express';
|
import type { Request } from 'express';
|
||||||
import { ICredentialTypes } from 'n8n-workflow';
|
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { access } from 'fs/promises';
|
import { access } from 'fs/promises';
|
||||||
import { Authorized, Get, RestController } from '@/decorators';
|
import { Authorized, Get, RestController } from '@/decorators';
|
||||||
import { Config } from '@/config';
|
import config from '@/config';
|
||||||
import { NODES_BASE_DIR } from '@/constants';
|
import { NODES_BASE_DIR } from '@/constants';
|
||||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
|
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
|
||||||
|
import { CredentialTypes } from '@/CredentialTypes';
|
||||||
|
|
||||||
export const CREDENTIAL_TRANSLATIONS_DIR = 'n8n-nodes-base/dist/credentials/translations';
|
export const CREDENTIAL_TRANSLATIONS_DIR = 'n8n-nodes-base/dist/credentials/translations';
|
||||||
export const NODE_HEADERS_PATH = join(NODES_BASE_DIR, 'dist/nodes/headers');
|
export const NODE_HEADERS_PATH = join(NODES_BASE_DIR, 'dist/nodes/headers');
|
||||||
|
@ -18,10 +18,7 @@ export declare namespace TranslationRequest {
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/')
|
@RestController('/')
|
||||||
export class TranslationController {
|
export class TranslationController {
|
||||||
constructor(
|
constructor(private readonly credentialTypes: CredentialTypes) {}
|
||||||
private config: Config,
|
|
||||||
private credentialTypes: ICredentialTypes,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@Get('/credential-translation')
|
@Get('/credential-translation')
|
||||||
async getCredentialTranslation(req: TranslationRequest.Credential) {
|
async getCredentialTranslation(req: TranslationRequest.Credential) {
|
||||||
|
@ -30,7 +27,7 @@ export class TranslationController {
|
||||||
if (!this.credentialTypes.recognizes(credentialType))
|
if (!this.credentialTypes.recognizes(credentialType))
|
||||||
throw new BadRequestError(`Invalid Credential type: "${credentialType}"`);
|
throw new BadRequestError(`Invalid Credential type: "${credentialType}"`);
|
||||||
|
|
||||||
const defaultLocale = this.config.getEnv('defaultLocale');
|
const defaultLocale = config.getEnv('defaultLocale');
|
||||||
const translationPath = join(
|
const translationPath = join(
|
||||||
CREDENTIAL_TRANSLATIONS_DIR,
|
CREDENTIAL_TRANSLATIONS_DIR,
|
||||||
defaultLocale,
|
defaultLocale,
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { SharedWorkflow } from '@db/entities/SharedWorkflow';
|
||||||
import { RequireGlobalScope, Authorized, Delete, Get, RestController, Patch } from '@/decorators';
|
import { RequireGlobalScope, Authorized, Delete, Get, RestController, Patch } from '@/decorators';
|
||||||
import { ListQuery, UserRequest, UserSettingsUpdatePayload } from '@/requests';
|
import { ListQuery, UserRequest, UserSettingsUpdatePayload } from '@/requests';
|
||||||
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
||||||
import { IExternalHooksClass, IInternalHooksClass } from '@/Interfaces';
|
|
||||||
import type { PublicUser, ITelemetryUserDeletionData } from '@/Interfaces';
|
import type { PublicUser, ITelemetryUserDeletionData } from '@/Interfaces';
|
||||||
import { AuthIdentity } from '@db/entities/AuthIdentity';
|
import { AuthIdentity } from '@db/entities/AuthIdentity';
|
||||||
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
|
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
|
||||||
|
@ -20,14 +19,16 @@ import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
|
||||||
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
||||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
import { License } from '@/License';
|
import { License } from '@/License';
|
||||||
|
import { ExternalHooks } from '@/ExternalHooks';
|
||||||
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/users')
|
@RestController('/users')
|
||||||
export class UsersController {
|
export class UsersController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly logger: Logger,
|
private readonly logger: Logger,
|
||||||
private readonly externalHooks: IExternalHooksClass,
|
private readonly externalHooks: ExternalHooks,
|
||||||
private readonly internalHooks: IInternalHooksClass,
|
private readonly internalHooks: InternalHooks,
|
||||||
private readonly sharedCredentialsRepository: SharedCredentialsRepository,
|
private readonly sharedCredentialsRepository: SharedCredentialsRepository,
|
||||||
private readonly sharedWorkflowRepository: SharedWorkflowRepository,
|
private readonly sharedWorkflowRepository: SharedWorkflowRepository,
|
||||||
private readonly activeWorkflowRunner: ActiveWorkflowRunner,
|
private readonly activeWorkflowRunner: ActiveWorkflowRunner,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Service } from 'typedi';
|
|
||||||
import { Response, NextFunction } from 'express';
|
import { Response, NextFunction } from 'express';
|
||||||
import { Get, Middleware, RestController } from '@/decorators';
|
import { Get, Middleware, RestController } from '@/decorators';
|
||||||
import type { WorkflowStatistics } from '@db/entities/WorkflowStatistics';
|
import type { WorkflowStatistics } from '@db/entities/WorkflowStatistics';
|
||||||
|
@ -18,12 +17,11 @@ interface WorkflowStatisticsData<T> {
|
||||||
manualError: T;
|
manualError: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Service()
|
|
||||||
@RestController('/workflow-stats')
|
@RestController('/workflow-stats')
|
||||||
export class WorkflowStatisticsController {
|
export class WorkflowStatisticsController {
|
||||||
constructor(
|
constructor(
|
||||||
private sharedWorkflowRepository: SharedWorkflowRepository,
|
private readonly sharedWorkflowRepository: SharedWorkflowRepository,
|
||||||
private workflowStatisticsRepository: WorkflowStatisticsRepository,
|
private readonly workflowStatisticsRepository: WorkflowStatisticsRepository,
|
||||||
private readonly logger: Logger,
|
private readonly logger: Logger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
|
import type { Class } from 'n8n-core';
|
||||||
import { generateNanoId } from '../utils/generators';
|
import { generateNanoId } from '../utils/generators';
|
||||||
|
|
||||||
const dbType = config.getEnv('database.type');
|
const dbType = config.getEnv('database.type');
|
||||||
|
@ -27,9 +28,7 @@ const tsColumnOptions: ColumnOptions = {
|
||||||
type: datetimeColumnType,
|
type: datetimeColumnType,
|
||||||
};
|
};
|
||||||
|
|
||||||
type Constructor<T> = new (...args: any[]) => T;
|
function mixinStringId<T extends Class<{}, any[]>>(base: T) {
|
||||||
|
|
||||||
function mixinStringId<T extends Constructor<{}>>(base: T) {
|
|
||||||
class Derived extends base {
|
class Derived extends base {
|
||||||
@PrimaryColumn('varchar')
|
@PrimaryColumn('varchar')
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -44,7 +43,7 @@ function mixinStringId<T extends Constructor<{}>>(base: T) {
|
||||||
return Derived;
|
return Derived;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mixinTimestamps<T extends Constructor<{}>>(base: T) {
|
function mixinTimestamps<T extends Class<{}, any[]>>(base: T) {
|
||||||
class Derived extends base {
|
class Derived extends base {
|
||||||
@CreateDateColumn(tsColumnOptions)
|
@CreateDateColumn(tsColumnOptions)
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
|
|
@ -24,6 +24,7 @@ import { type ServiceClass, ShutdownService } from '@/shutdown/Shutdown.service'
|
||||||
export const OnShutdown =
|
export const OnShutdown =
|
||||||
(priority = 100): MethodDecorator =>
|
(priority = 100): MethodDecorator =>
|
||||||
(prototype, propertyKey, descriptor) => {
|
(prototype, propertyKey, descriptor) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
const serviceClass = prototype.constructor as ServiceClass;
|
const serviceClass = prototype.constructor as ServiceClass;
|
||||||
const methodName = String(propertyKey);
|
const methodName = String(propertyKey);
|
||||||
// TODO: assert that serviceClass is decorated with @Service
|
// TODO: assert that serviceClass is decorated with @Service
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
import { Service } from 'typedi';
|
||||||
import { CONTROLLER_BASE_PATH } from './constants';
|
import { CONTROLLER_BASE_PATH } from './constants';
|
||||||
|
|
||||||
export const RestController =
|
export const RestController =
|
||||||
(basePath: `/${string}` = '/'): ClassDecorator =>
|
(basePath: `/${string}` = '/'): ClassDecorator =>
|
||||||
(target: object) => {
|
(target: object) => {
|
||||||
Reflect.defineMetadata(CONTROLLER_BASE_PATH, basePath, target);
|
Reflect.defineMetadata(CONTROLLER_BASE_PATH, basePath, target);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
|
return Service()(target);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
import { Container } from 'typedi';
|
||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import type { Application, Request, Response, RequestHandler } from 'express';
|
import type { Application, Request, Response, RequestHandler } from 'express';
|
||||||
import type { Config } from '@/config';
|
import type { Class } from 'n8n-core';
|
||||||
|
|
||||||
|
import config from '@/config';
|
||||||
import type { AuthenticatedRequest } from '@/requests';
|
import type { AuthenticatedRequest } from '@/requests';
|
||||||
import { send } from '@/ResponseHelper'; // TODO: move `ResponseHelper.send` to this file
|
import { send } from '@/ResponseHelper'; // TODO: move `ResponseHelper.send` to this file
|
||||||
import {
|
import {
|
||||||
|
@ -21,7 +24,7 @@ import type {
|
||||||
ScopeMetadata,
|
ScopeMetadata,
|
||||||
} from './types';
|
} from './types';
|
||||||
import type { BooleanLicenseFeature } from '@/Interfaces';
|
import type { BooleanLicenseFeature } from '@/Interfaces';
|
||||||
import Container from 'typedi';
|
|
||||||
import { License } from '@/License';
|
import { License } from '@/License';
|
||||||
import type { Scope } from '@n8n/permissions';
|
import type { Scope } from '@n8n/permissions';
|
||||||
import { ApplicationError } from 'n8n-workflow';
|
import { ApplicationError } from 'n8n-workflow';
|
||||||
|
@ -81,9 +84,8 @@ const authFreeRoutes: string[] = [];
|
||||||
export const canSkipAuth = (method: string, path: string): boolean =>
|
export const canSkipAuth = (method: string, path: string): boolean =>
|
||||||
authFreeRoutes.includes(`${method.toLowerCase()} ${path}`);
|
authFreeRoutes.includes(`${method.toLowerCase()} ${path}`);
|
||||||
|
|
||||||
export const registerController = (app: Application, config: Config, cObj: object) => {
|
export const registerController = (app: Application, controllerClass: Class<object>) => {
|
||||||
const controller = cObj as Controller;
|
const controller = Container.get(controllerClass as Class<Controller>);
|
||||||
const controllerClass = controller.constructor;
|
|
||||||
const controllerBasePath = Reflect.getMetadata(CONTROLLER_BASE_PATH, controllerClass) as
|
const controllerBasePath = Reflect.getMetadata(CONTROLLER_BASE_PATH, controllerClass) as
|
||||||
| string
|
| string
|
||||||
| undefined;
|
| undefined;
|
||||||
|
|
|
@ -10,7 +10,6 @@ export const SOURCE_CONTROL_SSH_FOLDER = 'ssh';
|
||||||
export const SOURCE_CONTROL_SSH_KEY_NAME = 'key';
|
export const SOURCE_CONTROL_SSH_KEY_NAME = 'key';
|
||||||
export const SOURCE_CONTROL_DEFAULT_BRANCH = 'main';
|
export const SOURCE_CONTROL_DEFAULT_BRANCH = 'main';
|
||||||
export const SOURCE_CONTROL_ORIGIN = 'origin';
|
export const SOURCE_CONTROL_ORIGIN = 'origin';
|
||||||
export const SOURCE_CONTROL_API_ROOT = 'source-control';
|
|
||||||
export const SOURCE_CONTROL_README = `
|
export const SOURCE_CONTROL_README = `
|
||||||
# n8n Source Control
|
# n8n Source Control
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Container, Service } from 'typedi';
|
|
||||||
import type { PullResult } from 'simple-git';
|
import type { PullResult } from 'simple-git';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { Authorized, Get, Post, Patch, RestController, RequireGlobalScope } from '@/decorators';
|
import { Authorized, Get, Post, Patch, RestController, RequireGlobalScope } from '@/decorators';
|
||||||
|
@ -11,20 +10,20 @@ import { SourceControlRequest } from './types/requests';
|
||||||
import { SourceControlPreferencesService } from './sourceControlPreferences.service.ee';
|
import { SourceControlPreferencesService } from './sourceControlPreferences.service.ee';
|
||||||
import type { SourceControlPreferences } from './types/sourceControlPreferences';
|
import type { SourceControlPreferences } from './types/sourceControlPreferences';
|
||||||
import type { SourceControlledFile } from './types/sourceControlledFile';
|
import type { SourceControlledFile } from './types/sourceControlledFile';
|
||||||
import { SOURCE_CONTROL_API_ROOT, SOURCE_CONTROL_DEFAULT_BRANCH } from './constants';
|
import { SOURCE_CONTROL_DEFAULT_BRANCH } from './constants';
|
||||||
import type { ImportResult } from './types/importResult';
|
import type { ImportResult } from './types/importResult';
|
||||||
import { InternalHooks } from '../../InternalHooks';
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
import { getRepoType } from './sourceControlHelper.ee';
|
import { getRepoType } from './sourceControlHelper.ee';
|
||||||
import { SourceControlGetStatus } from './types/sourceControlGetStatus';
|
import { SourceControlGetStatus } from './types/sourceControlGetStatus';
|
||||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
@Service()
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController(`/${SOURCE_CONTROL_API_ROOT}`)
|
@RestController('/source-control')
|
||||||
export class SourceControlController {
|
export class SourceControlController {
|
||||||
constructor(
|
constructor(
|
||||||
private sourceControlService: SourceControlService,
|
private readonly sourceControlService: SourceControlService,
|
||||||
private sourceControlPreferencesService: SourceControlPreferencesService,
|
private readonly sourceControlPreferencesService: SourceControlPreferencesService,
|
||||||
|
private readonly internalHooks: InternalHooks,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Authorized('none')
|
@Authorized('none')
|
||||||
|
@ -85,7 +84,7 @@ export class SourceControlController {
|
||||||
const resultingPreferences = this.sourceControlPreferencesService.getPreferences();
|
const resultingPreferences = this.sourceControlPreferencesService.getPreferences();
|
||||||
// #region Tracking Information
|
// #region Tracking Information
|
||||||
// located in controller so as to not call this multiple times when updating preferences
|
// located in controller so as to not call this multiple times when updating preferences
|
||||||
void Container.get(InternalHooks).onSourceControlSettingsUpdated({
|
void this.internalHooks.onSourceControlSettingsUpdated({
|
||||||
branch_name: resultingPreferences.branchName,
|
branch_name: resultingPreferences.branchName,
|
||||||
connected: resultingPreferences.connected,
|
connected: resultingPreferences.connected,
|
||||||
read_only_instance: resultingPreferences.branchReadOnly,
|
read_only_instance: resultingPreferences.branchReadOnly,
|
||||||
|
@ -130,7 +129,7 @@ export class SourceControlController {
|
||||||
}
|
}
|
||||||
await this.sourceControlService.init();
|
await this.sourceControlService.init();
|
||||||
const resultingPreferences = this.sourceControlPreferencesService.getPreferences();
|
const resultingPreferences = this.sourceControlPreferencesService.getPreferences();
|
||||||
void Container.get(InternalHooks).onSourceControlSettingsUpdated({
|
void this.internalHooks.onSourceControlSettingsUpdated({
|
||||||
branch_name: resultingPreferences.branchName,
|
branch_name: resultingPreferences.branchName,
|
||||||
connected: resultingPreferences.connected,
|
connected: resultingPreferences.connected,
|
||||||
read_only_instance: resultingPreferences.branchReadOnly,
|
read_only_instance: resultingPreferences.branchReadOnly,
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import { Service } from 'typedi';
|
|
||||||
|
|
||||||
import { VariablesRequest } from '@/requests';
|
import { VariablesRequest } from '@/requests';
|
||||||
import {
|
import {
|
||||||
Authorized,
|
Authorized,
|
||||||
|
@ -17,11 +15,10 @@ import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
||||||
import { VariableValidationError } from '@/errors/variable-validation.error';
|
import { VariableValidationError } from '@/errors/variable-validation.error';
|
||||||
import { VariableCountLimitReachedError } from '@/errors/variable-count-limit-reached.error';
|
import { VariableCountLimitReachedError } from '@/errors/variable-count-limit-reached.error';
|
||||||
|
|
||||||
@Service()
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/variables')
|
@RestController('/variables')
|
||||||
export class VariablesController {
|
export class VariablesController {
|
||||||
constructor(private variablesService: VariablesService) {}
|
constructor(private readonly variablesService: VariablesService) {}
|
||||||
|
|
||||||
@Get('/')
|
@Get('/')
|
||||||
@RequireGlobalScope('variable:list')
|
@RequireGlobalScope('variable:list')
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { Service } from 'typedi';
|
|
||||||
import { Authorized, Get, Post, RequireGlobalScope, RestController } from '@/decorators';
|
import { Authorized, Get, Post, RequireGlobalScope, RestController } from '@/decorators';
|
||||||
import { LicenseRequest } from '@/requests';
|
import { LicenseRequest } from '@/requests';
|
||||||
import { LicenseService } from './license.service';
|
import { LicenseService } from './license.service';
|
||||||
|
|
||||||
@Service()
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/license')
|
@RestController('/license')
|
||||||
export class LicenseController {
|
export class LicenseController {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import { UserManagementMailer } from '@/UserManagement/email';
|
||||||
import type { CommunityPackagesService } from '@/services/communityPackages.service';
|
import type { CommunityPackagesService } from '@/services/communityPackages.service';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
import { UrlService } from './url.service';
|
import { UrlService } from './url.service';
|
||||||
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class FrontendService {
|
export class FrontendService {
|
||||||
|
@ -46,6 +47,7 @@ export class FrontendService {
|
||||||
private readonly mailer: UserManagementMailer,
|
private readonly mailer: UserManagementMailer,
|
||||||
private readonly instanceSettings: InstanceSettings,
|
private readonly instanceSettings: InstanceSettings,
|
||||||
private readonly urlService: UrlService,
|
private readonly urlService: UrlService,
|
||||||
|
private readonly internalHooks: InternalHooks,
|
||||||
) {
|
) {
|
||||||
loadNodesAndCredentials.addPostProcessor(async () => this.generateTypes());
|
loadNodesAndCredentials.addPostProcessor(async () => this.generateTypes());
|
||||||
void this.generateTypes();
|
void this.generateTypes();
|
||||||
|
@ -218,7 +220,9 @@ export class FrontendService {
|
||||||
this.writeStaticJSON('credentials', credentials);
|
this.writeStaticJSON('credentials', credentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSettings(): IN8nUISettings {
|
getSettings(sessionId?: string): IN8nUISettings {
|
||||||
|
void this.internalHooks.onFrontendSettingsAPI(sessionId);
|
||||||
|
|
||||||
const restEndpoint = config.getEnv('endpoints.rest');
|
const restEndpoint = config.getEnv('endpoints.rest');
|
||||||
|
|
||||||
// Update all urls, in case `WEBHOOK_URL` was updated by `--tunnel`
|
// Update all urls, in case `WEBHOOK_URL` was updated by `--tunnel`
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Container, Service } from 'typedi';
|
import { Container, Service } from 'typedi';
|
||||||
import { ApplicationError, ErrorReporterProxy, assert } from 'n8n-workflow';
|
import { ApplicationError, ErrorReporterProxy, assert } from 'n8n-workflow';
|
||||||
|
import type { Class } from 'n8n-core';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
|
|
||||||
export interface ServiceClass {
|
type HandlerFn = () => Promise<void> | void;
|
||||||
new (): Record<string, () => Promise<void> | void>;
|
export type ServiceClass = Class<Record<string, HandlerFn>>;
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShutdownHandler {
|
export interface ShutdownHandler {
|
||||||
serviceClass: ServiceClass;
|
serviceClass: ServiceClass;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { Container, Service } from 'typedi';
|
|
||||||
import {
|
import {
|
||||||
Authorized,
|
Authorized,
|
||||||
Get,
|
Get,
|
||||||
|
@ -36,13 +35,13 @@ import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
import { AuthError } from '@/errors/response-errors/auth.error';
|
import { AuthError } from '@/errors/response-errors/auth.error';
|
||||||
import { UrlService } from '@/services/url.service';
|
import { UrlService } from '@/services/url.service';
|
||||||
|
|
||||||
@Service()
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/sso/saml')
|
@RestController('/sso/saml')
|
||||||
export class SamlController {
|
export class SamlController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly samlService: SamlService,
|
private readonly samlService: SamlService,
|
||||||
private readonly urlService: UrlService,
|
private readonly urlService: UrlService,
|
||||||
|
private readonly internalHooks: InternalHooks,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@NoAuthRequired()
|
@NoAuthRequired()
|
||||||
|
@ -142,7 +141,7 @@ export class SamlController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (loginResult.authenticatedUser) {
|
if (loginResult.authenticatedUser) {
|
||||||
void Container.get(InternalHooks).onUserLoginSuccess({
|
void this.internalHooks.onUserLoginSuccess({
|
||||||
user: loginResult.authenticatedUser,
|
user: loginResult.authenticatedUser,
|
||||||
authenticationMethod: 'saml',
|
authenticationMethod: 'saml',
|
||||||
});
|
});
|
||||||
|
@ -159,7 +158,7 @@ export class SamlController {
|
||||||
return res.status(202).send(loginResult.attributes);
|
return res.status(202).send(loginResult.attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Container.get(InternalHooks).onUserLoginFailed({
|
void this.internalHooks.onUserLoginFailed({
|
||||||
user: loginResult.attributes.email ?? 'unknown',
|
user: loginResult.attributes.email ?? 'unknown',
|
||||||
authenticationMethod: 'saml',
|
authenticationMethod: 'saml',
|
||||||
});
|
});
|
||||||
|
@ -168,7 +167,7 @@ export class SamlController {
|
||||||
if (isConnectionTestRequest(req)) {
|
if (isConnectionTestRequest(req)) {
|
||||||
return res.send(getSamlConnectionTestFailedView((error as Error).message));
|
return res.send(getSamlConnectionTestFailedView((error as Error).message));
|
||||||
}
|
}
|
||||||
void Container.get(InternalHooks).onUserLoginFailed({
|
void this.internalHooks.onUserLoginFailed({
|
||||||
user: 'unknown',
|
user: 'unknown',
|
||||||
authenticationMethod: 'saml',
|
authenticationMethod: 'saml',
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { Authorized, RestController, Get, Middleware } from '@/decorators';
|
import { Authorized, RestController, Get, Middleware } from '@/decorators';
|
||||||
import { WorkflowHistoryRequest } from '@/requests';
|
import { WorkflowHistoryRequest } from '@/requests';
|
||||||
import { Service } from 'typedi';
|
|
||||||
import { WorkflowHistoryService } from './workflowHistory.service.ee';
|
import { WorkflowHistoryService } from './workflowHistory.service.ee';
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from 'express';
|
||||||
import { isWorkflowHistoryEnabled, isWorkflowHistoryLicensed } from './workflowHistoryHelper.ee';
|
import { isWorkflowHistoryEnabled, isWorkflowHistoryLicensed } from './workflowHistoryHelper.ee';
|
||||||
|
@ -12,7 +11,6 @@ import { WorkflowHistoryVersionNotFoundError } from '@/errors/workflow-history-v
|
||||||
|
|
||||||
const DEFAULT_TAKE = 20;
|
const DEFAULT_TAKE = 20;
|
||||||
|
|
||||||
@Service()
|
|
||||||
@Authorized()
|
@Authorized()
|
||||||
@RestController('/workflow-history')
|
@RestController('/workflow-history')
|
||||||
export class WorkflowHistoryController {
|
export class WorkflowHistoryController {
|
||||||
|
|
|
@ -1,22 +1,18 @@
|
||||||
|
import { Container } from 'typedi';
|
||||||
import type { SuperAgentTest } from 'supertest';
|
import type { SuperAgentTest } from 'supertest';
|
||||||
import { SOURCE_CONTROL_API_ROOT } from '@/environments/sourceControl/constants';
|
|
||||||
import * as utils from '../shared/utils/';
|
|
||||||
import type { User } from '@db/entities/User';
|
import type { User } from '@db/entities/User';
|
||||||
import * as UserManagementHelpers from '@/UserManagement/UserManagementHelper';
|
|
||||||
import Container from 'typedi';
|
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { SourceControlPreferencesService } from '@/environments/sourceControl/sourceControlPreferences.service.ee';
|
import { SourceControlPreferencesService } from '@/environments/sourceControl/sourceControlPreferences.service.ee';
|
||||||
import { SourceControlService } from '@/environments/sourceControl/sourceControl.service.ee';
|
import { SourceControlService } from '@/environments/sourceControl/sourceControl.service.ee';
|
||||||
import type { SourceControlledFile } from '@/environments/sourceControl/types/sourceControlledFile';
|
import type { SourceControlledFile } from '@/environments/sourceControl/types/sourceControlledFile';
|
||||||
import { getGlobalMemberRole, getGlobalOwnerRole } from '../shared/db/roles';
|
|
||||||
|
import * as utils from '../shared/utils/';
|
||||||
|
import { getGlobalOwnerRole } from '../shared/db/roles';
|
||||||
import { createUser } from '../shared/db/users';
|
import { createUser } from '../shared/db/users';
|
||||||
|
|
||||||
let authOwnerAgent: SuperAgentTest;
|
let authOwnerAgent: SuperAgentTest;
|
||||||
let authMemberAgent: SuperAgentTest;
|
|
||||||
let owner: User;
|
let owner: User;
|
||||||
let member: User;
|
|
||||||
|
|
||||||
const sharingSpy = jest.spyOn(UserManagementHelpers, 'isSharingEnabled').mockReturnValue(true);
|
|
||||||
|
|
||||||
const testServer = utils.setupTestServer({
|
const testServer = utils.setupTestServer({
|
||||||
endpointGroups: ['sourceControl', 'license', 'auth'],
|
endpointGroups: ['sourceControl', 'license', 'auth'],
|
||||||
|
@ -25,11 +21,8 @@ const testServer = utils.setupTestServer({
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const globalOwnerRole = await getGlobalOwnerRole();
|
const globalOwnerRole = await getGlobalOwnerRole();
|
||||||
const globalMemberRole = await getGlobalMemberRole();
|
|
||||||
owner = await createUser({ globalRole: globalOwnerRole });
|
owner = await createUser({ globalRole: globalOwnerRole });
|
||||||
member = await createUser({ globalRole: globalMemberRole });
|
|
||||||
authOwnerAgent = testServer.authAgentFor(owner);
|
authOwnerAgent = testServer.authAgentFor(owner);
|
||||||
authMemberAgent = testServer.authAgentFor(member);
|
|
||||||
|
|
||||||
Container.get(SourceControlPreferencesService).isSourceControlConnected = () => true;
|
Container.get(SourceControlPreferencesService).isSourceControlConnected = () => true;
|
||||||
});
|
});
|
||||||
|
@ -37,7 +30,7 @@ beforeAll(async () => {
|
||||||
describe('GET /sourceControl/preferences', () => {
|
describe('GET /sourceControl/preferences', () => {
|
||||||
test('should return Source Control preferences', async () => {
|
test('should return Source Control preferences', async () => {
|
||||||
await authOwnerAgent
|
await authOwnerAgent
|
||||||
.get(`/${SOURCE_CONTROL_API_ROOT}/preferences`)
|
.get('/source-control/preferences')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect((res) => {
|
.expect((res) => {
|
||||||
return 'repositoryUrl' in res.body && 'branchName' in res.body;
|
return 'repositoryUrl' in res.body && 'branchName' in res.body;
|
||||||
|
@ -60,7 +53,7 @@ describe('GET /sourceControl/preferences', () => {
|
||||||
] as SourceControlledFile[];
|
] as SourceControlledFile[];
|
||||||
};
|
};
|
||||||
await authOwnerAgent
|
await authOwnerAgent
|
||||||
.get(`/${SOURCE_CONTROL_API_ROOT}/get-status`)
|
.get('/source-control/get-status')
|
||||||
.query({ direction: 'push', preferLocalVersion: 'true', verbose: 'false' })
|
.query({ direction: 'push', preferLocalVersion: 'true', verbose: 'false' })
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect((res) => {
|
.expect((res) => {
|
||||||
|
@ -73,7 +66,7 @@ describe('GET /sourceControl/preferences', () => {
|
||||||
test('refreshing key pairsshould return new rsa key', async () => {
|
test('refreshing key pairsshould return new rsa key', async () => {
|
||||||
config.set('sourceControl.defaultKeyPairType', 'rsa');
|
config.set('sourceControl.defaultKeyPairType', 'rsa');
|
||||||
await authOwnerAgent
|
await authOwnerAgent
|
||||||
.post(`/${SOURCE_CONTROL_API_ROOT}/generate-key-pair`)
|
.post('/source-control/generate-key-pair')
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect((res) => {
|
.expect((res) => {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import type { DataSourceOptions as ConnectionOptions, Repository } from 'typeorm';
|
import type { DataSourceOptions as ConnectionOptions, Repository } from 'typeorm';
|
||||||
import { DataSource as Connection } from 'typeorm';
|
import { DataSource as Connection } from 'typeorm';
|
||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
|
import type { Class } from 'n8n-core';
|
||||||
|
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import * as Db from '@/Db';
|
import * as Db from '@/Db';
|
||||||
|
@ -116,7 +117,7 @@ const repositories = [
|
||||||
*/
|
*/
|
||||||
export async function truncate(names: Array<(typeof repositories)[number]>) {
|
export async function truncate(names: Array<(typeof repositories)[number]>) {
|
||||||
for (const name of names) {
|
for (const name of names) {
|
||||||
const RepositoryClass: { new (): Repository<any> } = (
|
const RepositoryClass: Class<Repository<object>> = (
|
||||||
await import(`@db/repositories/${name.charAt(0).toLowerCase() + name.slice(1)}.repository`)
|
await import(`@db/repositories/${name.charAt(0).toLowerCase() + name.slice(1)}.repository`)
|
||||||
)[`${name}Repository`];
|
)[`${name}Repository`];
|
||||||
await Container.get(RepositoryClass).delete({});
|
await Container.get(RepositoryClass).delete({});
|
||||||
|
|
|
@ -14,14 +14,13 @@ import { rawBodyReader, bodyParser, setupAuthMiddlewares } from '@/middlewares';
|
||||||
import { PostHogClient } from '@/posthog';
|
import { PostHogClient } from '@/posthog';
|
||||||
import { License } from '@/License';
|
import { License } from '@/License';
|
||||||
import { Logger } from '@/Logger';
|
import { Logger } from '@/Logger';
|
||||||
|
import { InternalHooks } from '@/InternalHooks';
|
||||||
|
|
||||||
import { mockInstance } from '../../../shared/mocking';
|
import { mockInstance } from '../../../shared/mocking';
|
||||||
import * as testDb from '../../shared/testDb';
|
import * as testDb from '../../shared/testDb';
|
||||||
import { AUTHLESS_ENDPOINTS, PUBLIC_API_REST_PATH_SEGMENT, REST_PATH_SEGMENT } from '../constants';
|
import { AUTHLESS_ENDPOINTS, PUBLIC_API_REST_PATH_SEGMENT, REST_PATH_SEGMENT } from '../constants';
|
||||||
import type { SetupProps, TestServer } from '../types';
|
import type { SetupProps, TestServer } from '../types';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
|
||||||
import { LicenseMocker } from '../license';
|
import { LicenseMocker } from '../license';
|
||||||
import { PasswordUtility } from '@/services/password.utility';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin to prefix a path segment into a request URL pathname.
|
* Plugin to prefix a path segment into a request URL pathname.
|
||||||
|
@ -76,7 +75,7 @@ export const setupTestServer = ({
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
|
|
||||||
// Mock all telemetry and logging
|
// Mock all telemetry and logging
|
||||||
const logger = mockInstance(Logger);
|
mockInstance(Logger);
|
||||||
mockInstance(InternalHooks);
|
mockInstance(InternalHooks);
|
||||||
mockInstance(PostHogClient);
|
mockInstance(PostHogClient);
|
||||||
|
|
||||||
|
@ -140,12 +139,12 @@ export const setupTestServer = ({
|
||||||
const { VariablesController } = await import(
|
const { VariablesController } = await import(
|
||||||
'@/environments/variables/variables.controller.ee'
|
'@/environments/variables/variables.controller.ee'
|
||||||
);
|
);
|
||||||
registerController(app, config, Container.get(VariablesController));
|
registerController(app, VariablesController);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'license':
|
case 'license':
|
||||||
const { LicenseController } = await import('@/license/license.controller');
|
const { LicenseController } = await import('@/license/license.controller');
|
||||||
registerController(app, config, Container.get(LicenseController));
|
registerController(app, LicenseController);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'metrics':
|
case 'metrics':
|
||||||
|
@ -156,166 +155,108 @@ export const setupTestServer = ({
|
||||||
case 'eventBus':
|
case 'eventBus':
|
||||||
const { EventBusController } = await import('@/eventbus/eventBus.controller');
|
const { EventBusController } = await import('@/eventbus/eventBus.controller');
|
||||||
const { EventBusControllerEE } = await import('@/eventbus/eventBus.controller.ee');
|
const { EventBusControllerEE } = await import('@/eventbus/eventBus.controller.ee');
|
||||||
registerController(app, config, new EventBusController());
|
registerController(app, EventBusController);
|
||||||
registerController(app, config, new EventBusControllerEE());
|
registerController(app, EventBusControllerEE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'auth':
|
case 'auth':
|
||||||
const { AuthController } = await import('@/controllers/auth.controller');
|
const { AuthController } = await import('@/controllers/auth.controller');
|
||||||
registerController(app, config, Container.get(AuthController));
|
registerController(app, AuthController);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'mfa':
|
case 'mfa':
|
||||||
const { MFAController } = await import('@/controllers/mfa.controller');
|
const { MFAController } = await import('@/controllers/mfa.controller');
|
||||||
registerController(app, config, Container.get(MFAController));
|
registerController(app, MFAController);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'ldap':
|
case 'ldap':
|
||||||
const { LdapManager } = await import('@/Ldap/LdapManager.ee');
|
|
||||||
const { handleLdapInit } = await import('@/Ldap/helpers');
|
const { handleLdapInit } = await import('@/Ldap/helpers');
|
||||||
const { LdapController } = await import('@/controllers/ldap.controller');
|
const { LdapController } = await import('@/controllers/ldap.controller');
|
||||||
testServer.license.enable('feat:ldap');
|
testServer.license.enable('feat:ldap');
|
||||||
await handleLdapInit();
|
await handleLdapInit();
|
||||||
const { service, sync } = LdapManager.getInstance();
|
registerController(app, LdapController);
|
||||||
registerController(
|
|
||||||
app,
|
|
||||||
config,
|
|
||||||
new LdapController(service, sync, Container.get(InternalHooks)),
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'saml':
|
case 'saml':
|
||||||
const { setSamlLoginEnabled } = await import('@/sso/saml/samlHelpers');
|
const { setSamlLoginEnabled } = await import('@/sso/saml/samlHelpers');
|
||||||
const { SamlController } = await import('@/sso/saml/routes/saml.controller.ee');
|
const { SamlController } = await import('@/sso/saml/routes/saml.controller.ee');
|
||||||
await setSamlLoginEnabled(true);
|
await setSamlLoginEnabled(true);
|
||||||
registerController(app, config, Container.get(SamlController));
|
registerController(app, SamlController);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'sourceControl':
|
case 'sourceControl':
|
||||||
const { SourceControlController } = await import(
|
const { SourceControlController } = await import(
|
||||||
'@/environments/sourceControl/sourceControl.controller.ee'
|
'@/environments/sourceControl/sourceControl.controller.ee'
|
||||||
);
|
);
|
||||||
registerController(app, config, Container.get(SourceControlController));
|
registerController(app, SourceControlController);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'community-packages':
|
case 'community-packages':
|
||||||
const { CommunityPackagesController } = await import(
|
const { CommunityPackagesController } = await import(
|
||||||
'@/controllers/communityPackages.controller'
|
'@/controllers/communityPackages.controller'
|
||||||
);
|
);
|
||||||
registerController(app, config, Container.get(CommunityPackagesController));
|
registerController(app, CommunityPackagesController);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'me':
|
case 'me':
|
||||||
const { MeController } = await import('@/controllers/me.controller');
|
const { MeController } = await import('@/controllers/me.controller');
|
||||||
registerController(app, config, Container.get(MeController));
|
registerController(app, MeController);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'passwordReset':
|
case 'passwordReset':
|
||||||
const { PasswordResetController } = await import(
|
const { PasswordResetController } = await import(
|
||||||
'@/controllers/passwordReset.controller'
|
'@/controllers/passwordReset.controller'
|
||||||
);
|
);
|
||||||
registerController(app, config, Container.get(PasswordResetController));
|
registerController(app, PasswordResetController);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'owner':
|
case 'owner':
|
||||||
const { UserService } = await import('@/services/user.service');
|
|
||||||
const { SettingsRepository } = await import('@db/repositories/settings.repository');
|
|
||||||
const { OwnerController } = await import('@/controllers/owner.controller');
|
const { OwnerController } = await import('@/controllers/owner.controller');
|
||||||
registerController(
|
registerController(app, OwnerController);
|
||||||
app,
|
|
||||||
config,
|
|
||||||
new OwnerController(
|
|
||||||
config,
|
|
||||||
logger,
|
|
||||||
Container.get(InternalHooks),
|
|
||||||
Container.get(SettingsRepository),
|
|
||||||
Container.get(UserService),
|
|
||||||
Container.get(PasswordUtility),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'users':
|
case 'users':
|
||||||
const { SharedCredentialsRepository } = await import(
|
|
||||||
'@db/repositories/sharedCredentials.repository'
|
|
||||||
);
|
|
||||||
const { SharedWorkflowRepository } = await import(
|
|
||||||
'@db/repositories/sharedWorkflow.repository'
|
|
||||||
);
|
|
||||||
const { ActiveWorkflowRunner } = await import('@/ActiveWorkflowRunner');
|
|
||||||
const { UserService: US } = await import('@/services/user.service');
|
|
||||||
const { ExternalHooks: EH } = await import('@/ExternalHooks');
|
|
||||||
const { RoleService: RS } = await import('@/services/role.service');
|
|
||||||
const { UsersController } = await import('@/controllers/users.controller');
|
const { UsersController } = await import('@/controllers/users.controller');
|
||||||
registerController(
|
registerController(app, UsersController);
|
||||||
app,
|
|
||||||
config,
|
|
||||||
new UsersController(
|
|
||||||
logger,
|
|
||||||
Container.get(EH),
|
|
||||||
Container.get(InternalHooks),
|
|
||||||
Container.get(SharedCredentialsRepository),
|
|
||||||
Container.get(SharedWorkflowRepository),
|
|
||||||
Container.get(ActiveWorkflowRunner),
|
|
||||||
Container.get(RS),
|
|
||||||
Container.get(US),
|
|
||||||
Container.get(License),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'invitations':
|
case 'invitations':
|
||||||
const { InvitationController } = await import('@/controllers/invitation.controller');
|
const { InvitationController } = await import('@/controllers/invitation.controller');
|
||||||
const { ExternalHooks: EHS } = await import('@/ExternalHooks');
|
registerController(app, InvitationController);
|
||||||
const { UserService: USE } = await import('@/services/user.service');
|
|
||||||
|
|
||||||
registerController(
|
|
||||||
app,
|
|
||||||
config,
|
|
||||||
new InvitationController(
|
|
||||||
config,
|
|
||||||
logger,
|
|
||||||
Container.get(InternalHooks),
|
|
||||||
Container.get(EHS),
|
|
||||||
Container.get(USE),
|
|
||||||
Container.get(License),
|
|
||||||
Container.get(PasswordUtility),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'tags':
|
case 'tags':
|
||||||
const { TagsController } = await import('@/controllers/tags.controller');
|
const { TagsController } = await import('@/controllers/tags.controller');
|
||||||
registerController(app, config, Container.get(TagsController));
|
registerController(app, TagsController);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'externalSecrets':
|
case 'externalSecrets':
|
||||||
const { ExternalSecretsController } = await import(
|
const { ExternalSecretsController } = await import(
|
||||||
'@/ExternalSecrets/ExternalSecrets.controller.ee'
|
'@/ExternalSecrets/ExternalSecrets.controller.ee'
|
||||||
);
|
);
|
||||||
registerController(app, config, Container.get(ExternalSecretsController));
|
registerController(app, ExternalSecretsController);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'workflowHistory':
|
case 'workflowHistory':
|
||||||
const { WorkflowHistoryController } = await import(
|
const { WorkflowHistoryController } = await import(
|
||||||
'@/workflows/workflowHistory/workflowHistory.controller.ee'
|
'@/workflows/workflowHistory/workflowHistory.controller.ee'
|
||||||
);
|
);
|
||||||
registerController(app, config, Container.get(WorkflowHistoryController));
|
registerController(app, WorkflowHistoryController);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'binaryData':
|
case 'binaryData':
|
||||||
const { BinaryDataController } = await import('@/controllers/binaryData.controller');
|
const { BinaryDataController } = await import('@/controllers/binaryData.controller');
|
||||||
registerController(app, config, Container.get(BinaryDataController));
|
registerController(app, BinaryDataController);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'role':
|
case 'role':
|
||||||
const { RoleController } = await import('@/controllers/role.controller');
|
const { RoleController } = await import('@/controllers/role.controller');
|
||||||
registerController(app, config, Container.get(RoleController));
|
registerController(app, RoleController);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'debug':
|
case 'debug':
|
||||||
const { DebugController } = await import('@/controllers/debug.controller');
|
const { DebugController } = await import('@/controllers/debug.controller');
|
||||||
registerController(app, config, Container.get(DebugController));
|
registerController(app, DebugController);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
import type { DeepPartial } from 'ts-essentials';
|
import type { DeepPartial } from 'ts-essentials';
|
||||||
|
import type { Class } from 'n8n-core';
|
||||||
|
|
||||||
export const mockInstance = <T>(
|
export const mockInstance = <T>(
|
||||||
ctor: new (...args: unknown[]) => T,
|
serviceClass: Class<T>,
|
||||||
data: DeepPartial<T> | undefined = undefined,
|
data: DeepPartial<T> | undefined = undefined,
|
||||||
) => {
|
) => {
|
||||||
const instance = mock<T>(data);
|
const instance = mock<T>(data);
|
||||||
Container.set(ctor, instance);
|
Container.set(serviceClass, instance);
|
||||||
return instance;
|
return instance;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import type { CookieOptions, Response } from 'express';
|
import type { CookieOptions, Response } from 'express';
|
||||||
import { anyObject, captor, mock } from 'jest-mock-extended';
|
import { anyObject, captor, mock } from 'jest-mock-extended';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import type { IInternalHooksClass } from '@/Interfaces';
|
|
||||||
import type { User } from '@db/entities/User';
|
import type { User } from '@db/entities/User';
|
||||||
import type { SettingsRepository } from '@db/repositories/settings.repository';
|
import type { SettingsRepository } from '@db/repositories/settings.repository';
|
||||||
import type { Config } from '@/config';
|
import config from '@/config';
|
||||||
import type { OwnerRequest } from '@/requests';
|
import type { OwnerRequest } from '@/requests';
|
||||||
import { OwnerController } from '@/controllers/owner.controller';
|
import { OwnerController } from '@/controllers/owner.controller';
|
||||||
import { AUTH_COOKIE_NAME } from '@/constants';
|
import { AUTH_COOKIE_NAME } from '@/constants';
|
||||||
|
@ -16,32 +15,33 @@ import { badPasswords } from '../shared/testData';
|
||||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
import { PasswordUtility } from '@/services/password.utility';
|
import { PasswordUtility } from '@/services/password.utility';
|
||||||
import Container from 'typedi';
|
import Container from 'typedi';
|
||||||
|
import type { InternalHooks } from '@/InternalHooks';
|
||||||
|
|
||||||
describe('OwnerController', () => {
|
describe('OwnerController', () => {
|
||||||
const config = mock<Config>();
|
const configGetSpy = jest.spyOn(config, 'getEnv');
|
||||||
const internalHooks = mock<IInternalHooksClass>();
|
const internalHooks = mock<InternalHooks>();
|
||||||
const userService = mockInstance(UserService);
|
const userService = mockInstance(UserService);
|
||||||
const settingsRepository = mock<SettingsRepository>();
|
const settingsRepository = mock<SettingsRepository>();
|
||||||
mockInstance(License).isWithinUsersLimit.mockReturnValue(true);
|
mockInstance(License).isWithinUsersLimit.mockReturnValue(true);
|
||||||
const controller = new OwnerController(
|
const controller = new OwnerController(
|
||||||
config,
|
|
||||||
mock(),
|
mock(),
|
||||||
internalHooks,
|
internalHooks,
|
||||||
settingsRepository,
|
settingsRepository,
|
||||||
userService,
|
userService,
|
||||||
Container.get(PasswordUtility),
|
Container.get(PasswordUtility),
|
||||||
|
mock(),
|
||||||
);
|
);
|
||||||
|
|
||||||
describe('setupOwner', () => {
|
describe('setupOwner', () => {
|
||||||
it('should throw a BadRequestError if the instance owner is already setup', async () => {
|
it('should throw a BadRequestError if the instance owner is already setup', async () => {
|
||||||
config.getEnv.calledWith('userManagement.isInstanceOwnerSetUp').mockReturnValue(true);
|
configGetSpy.mockReturnValue(true);
|
||||||
await expect(controller.setupOwner(mock(), mock())).rejects.toThrowError(
|
await expect(controller.setupOwner(mock(), mock())).rejects.toThrowError(
|
||||||
new BadRequestError('Instance owner already setup'),
|
new BadRequestError('Instance owner already setup'),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw a BadRequestError if the email is invalid', async () => {
|
it('should throw a BadRequestError if the email is invalid', async () => {
|
||||||
config.getEnv.calledWith('userManagement.isInstanceOwnerSetUp').mockReturnValue(false);
|
configGetSpy.mockReturnValue(false);
|
||||||
const req = mock<OwnerRequest.Post>({ body: { email: 'invalid email' } });
|
const req = mock<OwnerRequest.Post>({ body: { email: 'invalid email' } });
|
||||||
await expect(controller.setupOwner(req, mock())).rejects.toThrowError(
|
await expect(controller.setupOwner(req, mock())).rejects.toThrowError(
|
||||||
new BadRequestError('Invalid email address'),
|
new BadRequestError('Invalid email address'),
|
||||||
|
@ -51,7 +51,7 @@ describe('OwnerController', () => {
|
||||||
describe('should throw if the password is invalid', () => {
|
describe('should throw if the password is invalid', () => {
|
||||||
Object.entries(badPasswords).forEach(([password, errorMessage]) => {
|
Object.entries(badPasswords).forEach(([password, errorMessage]) => {
|
||||||
it(password, async () => {
|
it(password, async () => {
|
||||||
config.getEnv.calledWith('userManagement.isInstanceOwnerSetUp').mockReturnValue(false);
|
configGetSpy.mockReturnValue(false);
|
||||||
const req = mock<OwnerRequest.Post>({ body: { email: 'valid@email.com', password } });
|
const req = mock<OwnerRequest.Post>({ body: { email: 'valid@email.com', password } });
|
||||||
await expect(controller.setupOwner(req, mock())).rejects.toThrowError(
|
await expect(controller.setupOwner(req, mock())).rejects.toThrowError(
|
||||||
new BadRequestError(errorMessage),
|
new BadRequestError(errorMessage),
|
||||||
|
@ -61,7 +61,7 @@ describe('OwnerController', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw a BadRequestError if firstName & lastName are missing ', async () => {
|
it('should throw a BadRequestError if firstName & lastName are missing ', async () => {
|
||||||
config.getEnv.calledWith('userManagement.isInstanceOwnerSetUp').mockReturnValue(false);
|
configGetSpy.mockReturnValue(false);
|
||||||
const req = mock<OwnerRequest.Post>({
|
const req = mock<OwnerRequest.Post>({
|
||||||
body: { email: 'valid@email.com', password: 'NewPassword123', firstName: '', lastName: '' },
|
body: { email: 'valid@email.com', password: 'NewPassword123', firstName: '', lastName: '' },
|
||||||
});
|
});
|
||||||
|
@ -86,7 +86,7 @@ describe('OwnerController', () => {
|
||||||
user,
|
user,
|
||||||
});
|
});
|
||||||
const res = mock<Response>();
|
const res = mock<Response>();
|
||||||
config.getEnv.calledWith('userManagement.isInstanceOwnerSetUp').mockReturnValue(false);
|
configGetSpy.mockReturnValue(false);
|
||||||
userService.save.calledWith(anyObject()).mockResolvedValue(user);
|
userService.save.calledWith(anyObject()).mockResolvedValue(user);
|
||||||
jest.spyOn(jwt, 'sign').mockImplementation(() => 'signed-token');
|
jest.spyOn(jwt, 'sign').mockImplementation(() => 'signed-token');
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
import type { ICredentialTypes } from 'n8n-workflow';
|
import type { ICredentialTypes } from 'n8n-workflow';
|
||||||
import type { Config } from '@/config';
|
import config from '@/config';
|
||||||
import type { TranslationRequest } from '@/controllers/translation.controller';
|
import type { TranslationRequest } from '@/controllers/translation.controller';
|
||||||
import {
|
import {
|
||||||
TranslationController,
|
TranslationController,
|
||||||
|
@ -9,9 +9,9 @@ import {
|
||||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
|
||||||
describe('TranslationController', () => {
|
describe('TranslationController', () => {
|
||||||
const config = mock<Config>();
|
const configGetSpy = jest.spyOn(config, 'getEnv');
|
||||||
const credentialTypes = mock<ICredentialTypes>();
|
const credentialTypes = mock<ICredentialTypes>();
|
||||||
const controller = new TranslationController(config, credentialTypes);
|
const controller = new TranslationController(credentialTypes);
|
||||||
|
|
||||||
describe('getCredentialTranslation', () => {
|
describe('getCredentialTranslation', () => {
|
||||||
it('should throw 400 on invalid credential types', async () => {
|
it('should throw 400 on invalid credential types', async () => {
|
||||||
|
@ -27,7 +27,7 @@ describe('TranslationController', () => {
|
||||||
it('should return translation json on valid credential types', async () => {
|
it('should return translation json on valid credential types', async () => {
|
||||||
const credentialType = 'credential-type';
|
const credentialType = 'credential-type';
|
||||||
const req = mock<TranslationRequest.Credential>({ query: { credentialType } });
|
const req = mock<TranslationRequest.Credential>({ query: { credentialType } });
|
||||||
config.getEnv.calledWith('defaultLocale').mockReturnValue('de');
|
configGetSpy.mockReturnValue('de');
|
||||||
credentialTypes.recognizes.calledWith(credentialType).mockReturnValue(true);
|
credentialTypes.recognizes.calledWith(credentialType).mockReturnValue(true);
|
||||||
const response = { translation: 'string' };
|
const response = { translation: 'string' };
|
||||||
jest.mock(`${CREDENTIAL_TRANSLATIONS_DIR}/de/credential-type.json`, () => response, {
|
jest.mock(`${CREDENTIAL_TRANSLATIONS_DIR}/de/credential-type.json`, () => response, {
|
||||||
|
|
|
@ -5,6 +5,8 @@ import type {
|
||||||
ValidationResult,
|
ValidationResult,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export type Class<T = object, A extends unknown[] = unknown[]> = new (...args: A) => T;
|
||||||
|
|
||||||
export interface IProcessMessage {
|
export interface IProcessMessage {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
data?: any;
|
data?: any;
|
||||||
|
|
|
@ -3,9 +3,10 @@ import { mock } from 'jest-mock-extended';
|
||||||
import { Duplex } from 'stream';
|
import { Duplex } from 'stream';
|
||||||
|
|
||||||
import type { DeepPartial } from 'ts-essentials';
|
import type { DeepPartial } from 'ts-essentials';
|
||||||
|
import type { Class } from '@/Interfaces';
|
||||||
|
|
||||||
export const mockInstance = <T>(
|
export const mockInstance = <T>(
|
||||||
constructor: new (...args: unknown[]) => T,
|
constructor: Class<T>,
|
||||||
data: DeepPartial<T> | undefined = undefined,
|
data: DeepPartial<T> | undefined = undefined,
|
||||||
) => {
|
) => {
|
||||||
const instance = mock<T>(data);
|
const instance = mock<T>(data);
|
||||||
|
|
Loading…
Reference in a new issue