From b1305fe5f146bd590fc7caab568d8dba1568cea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 13 Jun 2025 17:43:07 +0200 Subject: [PATCH] refactor(core): Continue porting legacy schema (#16318) --- .../config/src/configs/deployment.config.ts | 7 +++ .../src/configs/hiring-banner.config.ts | 8 +++ .../@n8n/config/src/configs/mfa.config.ts | 8 +++ .../src/configs/personalization.config.ts | 7 +++ packages/@n8n/config/src/index.ts | 32 +++++++++++ packages/@n8n/config/test/config.test.ts | 15 +++++ packages/cli/src/abstract-server.ts | 4 +- .../concurrency-control.service.test.ts | 41 ++++++++++++-- .../concurrency-control.service.ts | 4 +- packages/cli/src/config/schema.ts | 56 ------------------- .../__tests__/translation.controller.test.ts | 9 +-- .../src/controllers/node-types.controller.ts | 9 ++- .../src/controllers/translation.controller.ts | 9 ++- .../__tests__/telemetry-event-relay.test.ts | 3 + .../events/relays/telemetry.event-relay.ts | 4 +- packages/cli/src/mfa/constants.ts | 1 - packages/cli/src/mfa/helpers.ts | 9 +-- .../risk-reporters/instance-risk-reporter.ts | 3 +- packages/cli/src/services/frontend.service.ts | 14 ++--- ...default-task-runner-disconnect-analyzer.ts | 8 +-- .../__tests__/task-broker.service.test.ts | 8 +-- .../task-broker/task-broker.service.ts | 6 +- 22 files changed, 163 insertions(+), 102 deletions(-) create mode 100644 packages/@n8n/config/src/configs/deployment.config.ts create mode 100644 packages/@n8n/config/src/configs/hiring-banner.config.ts create mode 100644 packages/@n8n/config/src/configs/mfa.config.ts create mode 100644 packages/@n8n/config/src/configs/personalization.config.ts delete mode 100644 packages/cli/src/mfa/constants.ts diff --git a/packages/@n8n/config/src/configs/deployment.config.ts b/packages/@n8n/config/src/configs/deployment.config.ts new file mode 100644 index 0000000000..943d34c2ca --- /dev/null +++ b/packages/@n8n/config/src/configs/deployment.config.ts @@ -0,0 +1,7 @@ +import { Config, Env } from '../decorators'; + +@Config +export class DeploymentConfig { + @Env('N8N_DEPLOYMENT_TYPE') + type: string = 'default'; +} diff --git a/packages/@n8n/config/src/configs/hiring-banner.config.ts b/packages/@n8n/config/src/configs/hiring-banner.config.ts new file mode 100644 index 0000000000..9b73673d83 --- /dev/null +++ b/packages/@n8n/config/src/configs/hiring-banner.config.ts @@ -0,0 +1,8 @@ +import { Config, Env } from '../decorators'; + +@Config +export class HiringBannerConfig { + /** Whether hiring banner in browser console is enabled. */ + @Env('N8N_HIRING_BANNER_ENABLED') + enabled: boolean = true; +} diff --git a/packages/@n8n/config/src/configs/mfa.config.ts b/packages/@n8n/config/src/configs/mfa.config.ts new file mode 100644 index 0000000000..1d30c7ab30 --- /dev/null +++ b/packages/@n8n/config/src/configs/mfa.config.ts @@ -0,0 +1,8 @@ +import { Config, Env } from '../decorators'; + +@Config +export class MfaConfig { + /** Whether to enable multi-factor authentication. */ + @Env('N8N_MFA_ENABLED') + enabled: boolean = true; +} diff --git a/packages/@n8n/config/src/configs/personalization.config.ts b/packages/@n8n/config/src/configs/personalization.config.ts new file mode 100644 index 0000000000..f8d7759531 --- /dev/null +++ b/packages/@n8n/config/src/configs/personalization.config.ts @@ -0,0 +1,7 @@ +import { Config, Env } from '../decorators'; + +@Config +export class PersonalizationConfig { + @Env('N8N_PERSONALIZATION_ENABLED') + enabled: boolean = true; +} diff --git a/packages/@n8n/config/src/index.ts b/packages/@n8n/config/src/index.ts index 89066e9369..d97d938e72 100644 --- a/packages/@n8n/config/src/index.ts +++ b/packages/@n8n/config/src/index.ts @@ -5,17 +5,21 @@ import { AuthConfig } from './configs/auth.config'; import { CacheConfig } from './configs/cache.config'; import { CredentialsConfig } from './configs/credentials.config'; import { DatabaseConfig } from './configs/database.config'; +import { DeploymentConfig } from './configs/deployment.config'; import { DiagnosticsConfig } from './configs/diagnostics.config'; import { EndpointsConfig } from './configs/endpoints.config'; import { EventBusConfig } from './configs/event-bus.config'; import { ExecutionsConfig } from './configs/executions.config'; import { ExternalHooksConfig } from './configs/external-hooks.config'; import { GenericConfig } from './configs/generic.config'; +import { HiringBannerConfig } from './configs/hiring-banner.config'; import { LicenseConfig } from './configs/license.config'; import { LoggingConfig } from './configs/logging.config'; +import { MfaConfig } from './configs/mfa.config'; import { MultiMainSetupConfig } from './configs/multi-main-setup.config'; import { NodesConfig } from './configs/nodes.config'; import { PartialExecutionsConfig } from './configs/partial-executions.config'; +import { PersonalizationConfig } from './configs/personalization.config'; import { PublicApiConfig } from './configs/public-api.config'; import { TaskRunnersConfig } from './configs/runners.config'; import { ScalingModeConfig } from './configs/scaling-mode.config'; @@ -39,6 +43,10 @@ export { LOG_SCOPES } from './configs/logging.config'; export type { LogScope } from './configs/logging.config'; export { WorkflowsConfig } from './configs/workflows.config'; export * from './custom-types'; +export { DeploymentConfig } from './configs/deployment.config'; +export { MfaConfig } from './configs/mfa.config'; +export { HiringBannerConfig } from './configs/hiring-banner.config'; +export { PersonalizationConfig } from './configs/personalization.config'; const protocolSchema = z.enum(['http', 'https']); @@ -146,4 +154,28 @@ export class GlobalConfig { @Nested workflowHistory: WorkflowHistoryConfig; + + @Nested + deployment: DeploymentConfig; + + @Nested + mfa: MfaConfig; + + @Nested + hiringBanner: HiringBannerConfig; + + @Nested + personalization: PersonalizationConfig; + + /** Default locale for the UI. */ + @Env('N8N_DEFAULT_LOCALE') + defaultLocale: string = 'en'; + + /** Whether to hide the page that shows active workflows and executions count. */ + @Env('N8N_HIDE_USAGE_PAGE') + hideUsagePage: boolean = false; + + /** Number of reverse proxies n8n is running behind. */ + @Env('N8N_PROXY_HOPS') + proxy_hops: number = 0; } diff --git a/packages/@n8n/config/test/config.test.ts b/packages/@n8n/config/test/config.test.ts index bea302dbb3..9d87fb3e6e 100644 --- a/packages/@n8n/config/test/config.test.ts +++ b/packages/@n8n/config/test/config.test.ts @@ -33,6 +33,21 @@ describe('GlobalConfig', () => { secure: true, }, }, + defaultLocale: 'en', + hideUsagePage: false, + deployment: { + type: 'default', + }, + mfa: { + enabled: true, + }, + hiringBanner: { + enabled: true, + }, + personalization: { + enabled: true, + }, + proxy_hops: 0, database: { logging: { enabled: false, diff --git a/packages/cli/src/abstract-server.ts b/packages/cli/src/abstract-server.ts index bb9f6954a7..843248bfad 100644 --- a/packages/cli/src/abstract-server.ts +++ b/packages/cli/src/abstract-server.ts @@ -72,7 +72,7 @@ export abstract class AbstractServer { this.app.set('view engine', 'handlebars'); this.app.set('views', TEMPLATES_DIR); - const proxyHops = config.getEnv('proxy_hops'); + const proxyHops = this.globalConfig.proxy_hops; if (proxyHops > 0) this.app.set('trust proxy', proxyHops); this.sslKey = config.getEnv('ssl_key'); @@ -260,7 +260,7 @@ export abstract class AbstractServer { if (!inTest) { this.logger.info(`Version: ${N8N_VERSION}`); - const defaultLocale = config.getEnv('defaultLocale'); + const { defaultLocale } = this.globalConfig; if (defaultLocale !== 'en') { this.logger.info(`Locale: ${defaultLocale}`); } diff --git a/packages/cli/src/concurrency/__tests__/concurrency-control.service.test.ts b/packages/cli/src/concurrency/__tests__/concurrency-control.service.test.ts index 690e15aafe..2a3319553b 100644 --- a/packages/cli/src/concurrency/__tests__/concurrency-control.service.test.ts +++ b/packages/cli/src/concurrency/__tests__/concurrency-control.service.test.ts @@ -1,3 +1,4 @@ +import type { GlobalConfig } from '@n8n/config'; import type { ExecutionRepository } from '@n8n/db'; import { mock } from 'jest-mock-extended'; import type { WorkflowExecuteMode as ExecutionMode } from 'n8n-workflow'; @@ -21,6 +22,7 @@ describe('ConcurrencyControlService', () => { const executionRepository = mock(); const telemetry = mock(); const eventService = mock(); + const globalConfig = mock(); afterEach(() => { config.set('executions.concurrency.productionLimit', -1); @@ -47,6 +49,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); /** @@ -73,7 +76,13 @@ describe('ConcurrencyControlService', () => { /** * Act */ - new ConcurrencyControlService(logger, executionRepository, telemetry, eventService); + new ConcurrencyControlService( + logger, + executionRepository, + telemetry, + eventService, + globalConfig, + ); } catch (error) { /** * Assert @@ -98,6 +107,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); /** @@ -123,6 +133,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); /** @@ -148,6 +159,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); /** @@ -177,6 +189,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); const enqueueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'enqueue'); @@ -203,6 +216,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); const enqueueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'enqueue'); @@ -228,6 +242,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); const enqueueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'enqueue'); @@ -257,6 +272,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); const dequeueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'dequeue'); @@ -283,6 +299,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); const dequeueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'dequeue'); @@ -308,6 +325,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); const dequeueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'dequeue'); @@ -337,6 +355,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); const removeSpy = jest.spyOn(ConcurrencyQueue.prototype, 'remove'); @@ -365,6 +384,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); const removeSpy = jest.spyOn(ConcurrencyQueue.prototype, 'remove'); @@ -391,6 +411,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); const removeSpy = jest.spyOn(ConcurrencyQueue.prototype, 'remove'); @@ -420,6 +441,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); jest @@ -459,6 +481,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); // @ts-expect-error Private property const queue = service.getQueue('webhook'); @@ -485,6 +508,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); // @ts-expect-error Private property const queue = service.getQueue('evaluation'); @@ -515,6 +539,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); const enqueueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'enqueue'); @@ -541,6 +566,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); const enqueueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'enqueue'); @@ -569,6 +595,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); const dequeueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'dequeue'); @@ -594,6 +621,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); const dequeueSpy = jest.spyOn(ConcurrencyQueue.prototype, 'dequeue'); @@ -621,6 +649,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); const removeSpy = jest.spyOn(ConcurrencyQueue.prototype, 'remove'); @@ -646,6 +675,7 @@ describe('ConcurrencyControlService', () => { executionRepository, telemetry, eventService, + globalConfig, ); const removeSpy = jest.spyOn(ConcurrencyQueue.prototype, 'remove'); @@ -675,12 +705,13 @@ describe('ConcurrencyControlService', () => { * Arrange */ config.set('executions.concurrency.productionLimit', CLOUD_TEMP_PRODUCTION_LIMIT); - config.set('deployment.type', 'cloud'); + globalConfig.deployment.type = 'cloud'; const service = new ConcurrencyControlService( logger, executionRepository, telemetry, eventService, + globalConfig, ); /** @@ -708,12 +739,13 @@ describe('ConcurrencyControlService', () => { * Arrange */ config.set('executions.concurrency.productionLimit', CLOUD_TEMP_PRODUCTION_LIMIT); - config.set('deployment.type', 'cloud'); + globalConfig.deployment.type = 'cloud'; const service = new ConcurrencyControlService( logger, executionRepository, telemetry, eventService, + globalConfig, ); /** @@ -740,12 +772,13 @@ describe('ConcurrencyControlService', () => { * Arrange */ config.set('executions.concurrency.productionLimit', CLOUD_TEMP_PRODUCTION_LIMIT); - config.set('deployment.type', 'cloud'); + globalConfig.deployment.type = 'cloud'; const service = new ConcurrencyControlService( logger, executionRepository, telemetry, eventService, + globalConfig, ); /** diff --git a/packages/cli/src/concurrency/concurrency-control.service.ts b/packages/cli/src/concurrency/concurrency-control.service.ts index 2939f83a02..db26032cfc 100644 --- a/packages/cli/src/concurrency/concurrency-control.service.ts +++ b/packages/cli/src/concurrency/concurrency-control.service.ts @@ -1,4 +1,5 @@ import { Logger } from '@n8n/backend-common'; +import { GlobalConfig } from '@n8n/config'; import { ExecutionRepository } from '@n8n/db'; import { Service } from '@n8n/di'; import capitalize from 'lodash/capitalize'; @@ -34,6 +35,7 @@ export class ConcurrencyControlService { private readonly executionRepository: ExecutionRepository, private readonly telemetry: Telemetry, private readonly eventService: EventService, + private readonly globalConfig: GlobalConfig, ) { this.logger = this.logger.scoped('concurrency'); @@ -185,7 +187,7 @@ export class ConcurrencyControlService { } private shouldReport(capacity: number) { - return config.getEnv('deployment.type') === 'cloud' && this.limitsToReport.includes(capacity); + return this.globalConfig.deployment.type === 'cloud' && this.limitsToReport.includes(capacity); } /** diff --git a/packages/cli/src/config/schema.ts b/packages/cli/src/config/schema.ts index be8cec3b7a..e598124615 100644 --- a/packages/cli/src/config/schema.ts +++ b/packages/cli/src/config/schema.ts @@ -165,23 +165,6 @@ export const schema = { env: 'EXTERNAL_FRONTEND_HOOKS_URLS', }, - deployment: { - type: { - format: String, - default: 'default', - env: 'N8N_DEPLOYMENT_TYPE', - }, - }, - - mfa: { - enabled: { - format: Boolean, - default: true, - doc: 'Whether to enable MFA feature in instance.', - env: 'N8N_MFA_ENABLED', - }, - }, - sso: { justInTimeProvisioning: { format: Boolean, @@ -223,38 +206,6 @@ export const schema = { }, }, - hiringBanner: { - enabled: { - doc: 'Whether hiring banner in browser console is enabled.', - format: Boolean, - default: true, - env: 'N8N_HIRING_BANNER_ENABLED', - }, - }, - - personalization: { - enabled: { - doc: 'Whether personalization is enabled.', - format: Boolean, - default: true, - env: 'N8N_PERSONALIZATION_ENABLED', - }, - }, - - defaultLocale: { - doc: 'Default locale for the UI', - format: String, - default: 'en', - env: 'N8N_DEFAULT_LOCALE', - }, - - hideUsagePage: { - format: Boolean, - default: false, - env: 'N8N_HIDE_USAGE_PAGE', - doc: 'Hide or show the usage page', - }, - redis: { prefix: { doc: 'Prefix for all n8n related keys', @@ -285,11 +236,4 @@ export const schema = { env: 'N8N_AI_ENABLED', }, }, - - proxy_hops: { - format: Number, - default: 0, - env: 'N8N_PROXY_HOPS', - doc: 'Number of reverse-proxies n8n is running behind', - }, }; diff --git a/packages/cli/src/controllers/__tests__/translation.controller.test.ts b/packages/cli/src/controllers/__tests__/translation.controller.test.ts index 5164ed820b..9119fefb85 100644 --- a/packages/cli/src/controllers/__tests__/translation.controller.test.ts +++ b/packages/cli/src/controllers/__tests__/translation.controller.test.ts @@ -1,6 +1,6 @@ +import type { GlobalConfig } from '@n8n/config'; import { mock } from 'jest-mock-extended'; -import config from '@/config'; import type { TranslationRequest } from '@/controllers/translation.controller'; import { TranslationController, @@ -10,9 +10,11 @@ import type { CredentialTypes } from '@/credential-types'; import { BadRequestError } from '@/errors/response-errors/bad-request.error'; describe('TranslationController', () => { - const configGetSpy = jest.spyOn(config, 'getEnv'); const credentialTypes = mock(); - const controller = new TranslationController(credentialTypes); + const controller = new TranslationController( + credentialTypes, + mock({ defaultLocale: 'de' }), + ); describe('getCredentialTranslation', () => { it('should throw 400 on invalid credential types', async () => { @@ -28,7 +30,6 @@ describe('TranslationController', () => { it('should return translation json on valid credential types', async () => { const credentialType = 'credential-type'; const req = mock({ query: { credentialType } }); - configGetSpy.mockReturnValue('de'); credentialTypes.recognizes.calledWith(credentialType).mockReturnValue(true); const response = { translation: 'string' }; jest.mock(`${CREDENTIAL_TRANSLATIONS_DIR}/de/credential-type.json`, () => response, { diff --git a/packages/cli/src/controllers/node-types.controller.ts b/packages/cli/src/controllers/node-types.controller.ts index 5d61a590d4..bd8c81901f 100644 --- a/packages/cli/src/controllers/node-types.controller.ts +++ b/packages/cli/src/controllers/node-types.controller.ts @@ -1,21 +1,24 @@ +import { GlobalConfig } from '@n8n/config'; import { Post, RestController } from '@n8n/decorators'; import { Request } from 'express'; import { readFile } from 'fs/promises'; import get from 'lodash/get'; import type { INodeTypeDescription, INodeTypeNameVersion } from 'n8n-workflow'; -import config from '@/config'; import { NodeTypes } from '@/node-types'; @RestController('/node-types') export class NodeTypesController { - constructor(private readonly nodeTypes: NodeTypes) {} + constructor( + private readonly nodeTypes: NodeTypes, + private readonly globalConfig: GlobalConfig, + ) {} @Post('/') async getNodeInfo(req: Request) { const nodeInfos = get(req, 'body.nodeInfos', []) as INodeTypeNameVersion[]; - const defaultLocale = config.getEnv('defaultLocale'); + const defaultLocale = this.globalConfig.defaultLocale; if (defaultLocale === 'en') { return nodeInfos.reduce((acc, { name, version }) => { diff --git a/packages/cli/src/controllers/translation.controller.ts b/packages/cli/src/controllers/translation.controller.ts index 8c78946b21..bed32e3ba8 100644 --- a/packages/cli/src/controllers/translation.controller.ts +++ b/packages/cli/src/controllers/translation.controller.ts @@ -1,9 +1,9 @@ +import { GlobalConfig } from '@n8n/config'; import { Get, RestController } from '@n8n/decorators'; import type { Request } from 'express'; import { access } from 'fs/promises'; import { join } from 'path'; -import config from '@/config'; import { NODES_BASE_DIR } from '@/constants'; import { CredentialTypes } from '@/credential-types'; import { BadRequestError } from '@/errors/response-errors/bad-request.error'; @@ -18,7 +18,10 @@ export declare namespace TranslationRequest { @RestController('/') export class TranslationController { - constructor(private readonly credentialTypes: CredentialTypes) {} + constructor( + private readonly credentialTypes: CredentialTypes, + private readonly globalConfig: GlobalConfig, + ) {} @Get('/credential-translation') async getCredentialTranslation(req: TranslationRequest.Credential) { @@ -27,7 +30,7 @@ export class TranslationController { if (!this.credentialTypes.recognizes(credentialType)) throw new BadRequestError(`Invalid Credential type: "${credentialType}"`); - const defaultLocale = config.getEnv('defaultLocale'); + const { defaultLocale } = this.globalConfig; const translationPath = join( CREDENTIAL_TRANSLATIONS_DIR, defaultLocale, diff --git a/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts b/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts index bdae9009a4..3e88c12792 100644 --- a/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts +++ b/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts @@ -26,6 +26,9 @@ describe('TelemetryEventRelay', () => { const telemetry = mock(); const license = mock(); const globalConfig = mock({ + deployment: { + type: 'default', + }, userManagement: { emails: { mode: 'smtp', diff --git a/packages/cli/src/events/relays/telemetry.event-relay.ts b/packages/cli/src/events/relays/telemetry.event-relay.ts index acaeca20cf..06247afc02 100644 --- a/packages/cli/src/events/relays/telemetry.event-relay.ts +++ b/packages/cli/src/events/relays/telemetry.event-relay.ts @@ -578,7 +578,7 @@ export class TelemetryEventRelay extends EventRelay { } private async workflowSaved({ user, workflow, publicApi }: RelayEventMap['workflow-saved']) { - const isCloudDeployment = config.getEnv('deployment.type') === 'cloud'; + const isCloudDeployment = this.globalConfig.deployment.type === 'cloud'; const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes, { isCloudDeployment, @@ -835,7 +835,7 @@ export class TelemetryEventRelay extends EventRelay { executions_data_prune: this.globalConfig.executions.pruneData, executions_data_max_age: this.globalConfig.executions.pruneDataMaxAge, }, - n8n_deployment_type: config.getEnv('deployment.type'), + n8n_deployment_type: this.globalConfig.deployment.type, n8n_binary_data_mode: this.binaryDataConfig.mode, smtp_set_up: this.globalConfig.userManagement.emails.mode === 'smtp', ldap_allowed: authenticationMethod === 'ldap', diff --git a/packages/cli/src/mfa/constants.ts b/packages/cli/src/mfa/constants.ts deleted file mode 100644 index bf5e34c345..0000000000 --- a/packages/cli/src/mfa/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const MFA_FEATURE_ENABLED = 'mfa.enabled'; diff --git a/packages/cli/src/mfa/helpers.ts b/packages/cli/src/mfa/helpers.ts index 584e9ded06..f3af3a3703 100644 --- a/packages/cli/src/mfa/helpers.ts +++ b/packages/cli/src/mfa/helpers.ts @@ -1,11 +1,8 @@ +import { GlobalConfig } from '@n8n/config'; import { UserRepository } from '@n8n/db'; import { Container } from '@n8n/di'; -import config from '@/config'; - -import { MFA_FEATURE_ENABLED } from './constants'; - -export const isMfaFeatureEnabled = () => config.get(MFA_FEATURE_ENABLED); +export const isMfaFeatureEnabled = () => Container.get(GlobalConfig).mfa.enabled; const isMfaFeatureDisabled = () => !isMfaFeatureEnabled(); @@ -18,7 +15,7 @@ export const handleMfaDisable = async () => { // users, then keep the feature enabled const users = await getUsersWithMfaEnabled(); if (users) { - config.set(MFA_FEATURE_ENABLED, true); + Container.get(GlobalConfig).mfa.enabled = true; } } }; diff --git a/packages/cli/src/security-audit/risk-reporters/instance-risk-reporter.ts b/packages/cli/src/security-audit/risk-reporters/instance-risk-reporter.ts index beeb5e35bf..ed556ccd5c 100644 --- a/packages/cli/src/security-audit/risk-reporters/instance-risk-reporter.ts +++ b/packages/cli/src/security-audit/risk-reporters/instance-risk-reporter.ts @@ -6,7 +6,6 @@ import axios from 'axios'; import { InstanceSettings } from 'n8n-core'; import type { IWorkflowBase } from 'n8n-workflow'; -import config from '@/config'; import { N8N_VERSION } from '@/constants'; import { isApiEnabled } from '@/public-api'; import { @@ -84,7 +83,7 @@ export class InstanceRiskReporter implements RiskReporter { } private getSecuritySettings() { - if (config.getEnv('deployment.type') === 'cloud') return null; + if (this.globalConfig.deployment.type === 'cloud') return null; const settings: Record = {}; diff --git a/packages/cli/src/services/frontend.service.ts b/packages/cli/src/services/frontend.service.ts index a6b8959dd0..e68bf11255 100644 --- a/packages/cli/src/services/frontend.service.ts +++ b/packages/cli/src/services/frontend.service.ts @@ -133,12 +133,12 @@ export class FrontendService { apiHost: this.globalConfig.diagnostics.posthogConfig.apiHost, apiKey: this.globalConfig.diagnostics.posthogConfig.apiKey, autocapture: false, - disableSessionRecording: config.getEnv('deployment.type') !== 'cloud', + disableSessionRecording: this.globalConfig.deployment.type !== 'cloud', debug: this.globalConfig.logging.level === 'debug', }, personalizationSurveyEnabled: - config.getEnv('personalization.enabled') && this.globalConfig.diagnostics.enabled, - defaultLocale: config.getEnv('defaultLocale'), + this.globalConfig.personalization.enabled && this.globalConfig.diagnostics.enabled, + defaultLocale: this.globalConfig.defaultLocale, userManagement: { quota: this.license.getUsersLimit(), showSetupOnFirstLoad: !config.getEnv('userManagement.isInstanceOwnerSetUp'), @@ -170,7 +170,7 @@ export class FrontendService { }, workflowTagsDisabled: this.globalConfig.tags.disabled, logLevel: this.globalConfig.logging.level, - hiringBannerEnabled: config.getEnv('hiringBanner.enabled'), + hiringBannerEnabled: this.globalConfig.hiringBanner.enabled, aiAssistant: { enabled: false, }, @@ -184,7 +184,7 @@ export class FrontendService { communityNodesEnabled: this.globalConfig.nodes.communityPackages.enabled, unverifiedCommunityNodesEnabled: this.globalConfig.nodes.communityPackages.unverifiedEnabled, deployment: { - type: config.getEnv('deployment.type'), + type: this.globalConfig.deployment.type, }, allowedModules: { builtIn: process.env.NODE_FUNCTION_ALLOW_BUILTIN?.split(',') ?? undefined, @@ -217,7 +217,7 @@ export class FrontendService { mfa: { enabled: false, }, - hideUsagePage: config.getEnv('hideUsagePage'), + hideUsagePage: this.globalConfig.hideUsagePage, license: { consumerId: 'unknown', environment: this.globalConfig.license.tenantId === 1 ? 'production' : 'staging', @@ -395,7 +395,7 @@ export class FrontendService { dateRanges: getInsightsAvailableDateRanges(this.licenseState), }); - this.settings.mfa.enabled = config.get('mfa.enabled'); + this.settings.mfa.enabled = this.globalConfig.mfa.enabled; this.settings.executionMode = config.getEnv('executions.mode'); diff --git a/packages/cli/src/task-runners/default-task-runner-disconnect-analyzer.ts b/packages/cli/src/task-runners/default-task-runner-disconnect-analyzer.ts index 1743c531a4..3a38050b63 100644 --- a/packages/cli/src/task-runners/default-task-runner-disconnect-analyzer.ts +++ b/packages/cli/src/task-runners/default-task-runner-disconnect-analyzer.ts @@ -1,6 +1,6 @@ -import { Service } from '@n8n/di'; +import { GlobalConfig } from '@n8n/config'; +import { Container, Service } from '@n8n/di'; -import config from '@/config'; import type { DisconnectAnalyzer, DisconnectErrorOptions, @@ -16,7 +16,7 @@ import { TaskRunnerFailedHeartbeatError } from './errors/task-runner-failed-hear @Service() export class DefaultTaskRunnerDisconnectAnalyzer implements DisconnectAnalyzer { get isCloudDeployment() { - return config.get('deployment.type') === 'cloud'; + return Container.get(GlobalConfig).deployment.type === 'cloud'; } async toDisconnectError(opts: DisconnectErrorOptions): Promise { @@ -25,7 +25,7 @@ export class DefaultTaskRunnerDisconnectAnalyzer implements DisconnectAnalyzer { if (reason === 'failed-heartbeat-check' && heartbeatInterval) { return new TaskRunnerFailedHeartbeatError( heartbeatInterval, - config.get('deployment.type') !== 'cloud', + Container.get(GlobalConfig).deployment.type !== 'cloud', ); } diff --git a/packages/cli/src/task-runners/task-broker/__tests__/task-broker.service.test.ts b/packages/cli/src/task-runners/task-broker/__tests__/task-broker.service.test.ts index b6784826df..a480a774ad 100644 --- a/packages/cli/src/task-runners/task-broker/__tests__/task-broker.service.test.ts +++ b/packages/cli/src/task-runners/task-broker/__tests__/task-broker.service.test.ts @@ -18,7 +18,7 @@ describe('TaskBroker', () => { let taskBroker: TaskBroker; beforeEach(() => { - taskBroker = new TaskBroker(mock(), mock(), mock()); + taskBroker = new TaskBroker(mock(), mock(), mock(), mock()); jest.restoreAllMocks(); }); @@ -724,7 +724,7 @@ describe('TaskBroker', () => { beforeAll(() => { jest.useFakeTimers(); config = mock({ taskTimeout: 30, mode: 'internal' }); - taskBroker = new TaskBroker(mock(), config, runnerLifecycleEvents); + taskBroker = new TaskBroker(mock(), config, runnerLifecycleEvents, mock()); }); afterAll(() => { @@ -844,7 +844,7 @@ describe('TaskBroker', () => { it('[external mode] on timeout, we should instruct the runner to cancel and send error to requester', async () => { const config = mock({ taskTimeout: 30, mode: 'external' }); - taskBroker = new TaskBroker(mock(), config, runnerLifecycleEvents); + taskBroker = new TaskBroker(mock(), config, runnerLifecycleEvents, mock()); jest.spyOn(global, 'clearTimeout'); @@ -895,7 +895,7 @@ describe('TaskBroker', () => { const messageCallback = jest.fn(); const loggerMock = mock(); - taskBroker = new TaskBroker(loggerMock, mock(), mock()); + taskBroker = new TaskBroker(loggerMock, mock(), mock(), mock()); taskBroker.registerRunner(runner, messageCallback); const offer: TaskOffer = { diff --git a/packages/cli/src/task-runners/task-broker/task-broker.service.ts b/packages/cli/src/task-runners/task-broker/task-broker.service.ts index 67df04d4ef..cf4d9cad9b 100644 --- a/packages/cli/src/task-runners/task-broker/task-broker.service.ts +++ b/packages/cli/src/task-runners/task-broker/task-broker.service.ts @@ -1,5 +1,5 @@ import { Logger } from '@n8n/backend-common'; -import { TaskRunnersConfig } from '@n8n/config'; +import { GlobalConfig, TaskRunnersConfig } from '@n8n/config'; import { Service } from '@n8n/di'; import type { BrokerMessage, @@ -10,7 +10,6 @@ import type { import { UnexpectedError, UserError } from 'n8n-workflow'; import { nanoid } from 'nanoid'; -import config from '@/config'; import { Time } from '@/constants'; import { TaskDeferredError } from '@/task-runners/task-broker/errors/task-deferred.error'; import { TaskRejectError } from '@/task-runners/task-broker/errors/task-reject.error'; @@ -91,6 +90,7 @@ export class TaskBroker { private readonly logger: Logger, private readonly taskRunnersConfig: TaskRunnersConfig, private readonly taskRunnerLifecycleEvents: TaskRunnerLifecycleEvents, + private readonly globalConfig: GlobalConfig, ) { if (this.taskRunnersConfig.taskTimeout <= 0) { throw new UserError('Task timeout must be greater than 0'); @@ -471,7 +471,7 @@ export class TaskBroker { taskId, new TaskRunnerExecutionTimeoutError({ taskTimeout, - isSelfHosted: config.getEnv('deployment.type') !== 'cloud', + isSelfHosted: this.globalConfig.deployment.type !== 'cloud', mode, }), );