diff --git a/packages/@n8n/config/src/configs/workflow-history.config.ts b/packages/@n8n/config/src/configs/workflow-history.config.ts new file mode 100644 index 0000000000..c2295d8978 --- /dev/null +++ b/packages/@n8n/config/src/configs/workflow-history.config.ts @@ -0,0 +1,12 @@ +import { Config, Env } from '../decorators'; + +@Config +export class WorkflowHistoryConfig { + /** Whether to save workflow history versions. */ + @Env('N8N_WORKFLOW_HISTORY_ENABLED') + enabled: boolean = true; + + /** Time (in hours) to keep workflow history versions for. `-1` means forever. */ + @Env('N8N_WORKFLOW_HISTORY_PRUNE_TIME') + pruneTime: number = -1; +} diff --git a/packages/@n8n/config/src/index.ts b/packages/@n8n/config/src/index.ts index c5ea38f5f4..404e5ec40e 100644 --- a/packages/@n8n/config/src/index.ts +++ b/packages/@n8n/config/src/index.ts @@ -27,6 +27,7 @@ import { TagsConfig } from './configs/tags.config'; import { TemplatesConfig } from './configs/templates.config'; import { UserManagementConfig } from './configs/user-management.config'; import { VersionNotificationsConfig } from './configs/version-notifications.config'; +import { WorkflowHistoryConfig } from './configs/workflow-history.config'; import { WorkflowsConfig } from './configs/workflows.config'; import { Config, Env, Nested } from './decorators'; @@ -149,4 +150,7 @@ export class GlobalConfig { @Nested partialExecutions: PartialExecutionsConfig; + + @Nested + workflowHistory: WorkflowHistoryConfig; } diff --git a/packages/@n8n/config/test/config.test.ts b/packages/@n8n/config/test/config.test.ts index dd289053d5..129e405f23 100644 --- a/packages/@n8n/config/test/config.test.ts +++ b/packages/@n8n/config/test/config.test.ts @@ -315,6 +315,10 @@ describe('GlobalConfig', () => { partialExecutions: { version: 2, }, + workflowHistory: { + enabled: true, + pruneTime: -1, + }, }; it('should use all default values when no env variables are defined', () => { diff --git a/packages/cli/src/config/schema.ts b/packages/cli/src/config/schema.ts index bd9ee9016c..5a391e0d5f 100644 --- a/packages/cli/src/config/schema.ts +++ b/packages/cli/src/config/schema.ts @@ -301,22 +301,6 @@ export const schema = { }, }, - workflowHistory: { - enabled: { - doc: 'Whether to save workflow history versions', - format: Boolean, - default: true, - env: 'N8N_WORKFLOW_HISTORY_ENABLED', - }, - - pruneTime: { - doc: 'Time (in hours) to keep workflow history versions for', - format: Number, - default: -1, - env: 'N8N_WORKFLOW_HISTORY_PRUNE_TIME', - }, - }, - proxy_hops: { format: Number, default: 0, diff --git a/packages/cli/src/services/frontend.service.ts b/packages/cli/src/services/frontend.service.ts index 0d3e2c7156..8183645570 100644 --- a/packages/cli/src/services/frontend.service.ts +++ b/packages/cli/src/services/frontend.service.ts @@ -320,7 +320,7 @@ export class FrontendService { debugInEditor: this.license.isDebugInEditorLicensed(), binaryDataS3: isS3Available && isS3Selected && isS3Licensed, workflowHistory: - this.license.isWorkflowHistoryLicensed() && config.getEnv('workflowHistory.enabled'), + this.license.isWorkflowHistoryLicensed() && this.globalConfig.workflowHistory.enabled, workerView: this.license.isWorkerViewLicensed(), advancedPermissions: this.license.isAdvancedPermissionsLicensed(), apiKeyScopes: this.license.isApiKeyScopesEnabled(), @@ -344,7 +344,7 @@ export class FrontendService { this.settings.variables.limit = this.license.getVariablesLimit(); } - if (this.license.isWorkflowHistoryLicensed() && config.getEnv('workflowHistory.enabled')) { + if (this.globalConfig.workflowHistory.enabled && this.license.isWorkflowHistoryLicensed()) { Object.assign(this.settings.workflowHistory, { pruneTime: getWorkflowHistoryPruneTime(), licensePruneTime: getWorkflowHistoryLicensePruneTime(), diff --git a/packages/cli/src/workflows/workflow-history.ee/__tests__/workflow-history-helper.ee.test.ts b/packages/cli/src/workflows/workflow-history.ee/__tests__/workflow-history-helper.ee.test.ts index 70e00d2c6d..997976e11c 100644 --- a/packages/cli/src/workflows/workflow-history.ee/__tests__/workflow-history-helper.ee.test.ts +++ b/packages/cli/src/workflows/workflow-history.ee/__tests__/workflow-history-helper.ee.test.ts @@ -1,9 +1,12 @@ -import config from '@/config'; +import { GlobalConfig } from '@n8n/config'; +import { Container } from '@n8n/di'; + import { License } from '@/license'; import { getWorkflowHistoryPruneTime } from '@/workflows/workflow-history.ee/workflow-history-helper.ee'; import { mockInstance } from '@test/mocking'; let licensePruneTime = -1; +const globalConfig = Container.get(GlobalConfig); beforeAll(async () => { mockInstance(License, { @@ -15,39 +18,39 @@ beforeAll(async () => { beforeEach(() => { licensePruneTime = -1; - config.set('workflowHistory.pruneTime', -1); + globalConfig.workflowHistory.pruneTime = -1; }); describe('getWorkflowHistoryPruneTime', () => { test('should return -1 (infinite) if config and license are -1', () => { licensePruneTime = -1; - config.set('workflowHistory.pruneTime', -1); + globalConfig.workflowHistory.pruneTime = -1; expect(getWorkflowHistoryPruneTime()).toBe(-1); }); test('should return config time if license is infinite and config is not', () => { licensePruneTime = -1; - config.set('workflowHistory.pruneTime', 24); + globalConfig.workflowHistory.pruneTime = 24; expect(getWorkflowHistoryPruneTime()).toBe(24); }); test('should return license time if config is infinite and license is not', () => { licensePruneTime = 25; - config.set('workflowHistory.pruneTime', -1); + globalConfig.workflowHistory.pruneTime = -1; expect(getWorkflowHistoryPruneTime()).toBe(25); }); test('should return lowest of config and license time if both are not -1', () => { licensePruneTime = 26; - config.set('workflowHistory.pruneTime', 100); + globalConfig.workflowHistory.pruneTime = 100; expect(getWorkflowHistoryPruneTime()).toBe(26); licensePruneTime = 100; - config.set('workflowHistory.pruneTime', 27); + globalConfig.workflowHistory.pruneTime = 27; expect(getWorkflowHistoryPruneTime()).toBe(27); }); diff --git a/packages/cli/src/workflows/workflow-history.ee/workflow-history-helper.ee.ts b/packages/cli/src/workflows/workflow-history.ee/workflow-history-helper.ee.ts index c8b06851e7..3d9010fa32 100644 --- a/packages/cli/src/workflows/workflow-history.ee/workflow-history-helper.ee.ts +++ b/packages/cli/src/workflows/workflow-history.ee/workflow-history-helper.ee.ts @@ -1,6 +1,6 @@ +import { GlobalConfig } from '@n8n/config'; import { Container } from '@n8n/di'; -import config from '@/config'; import { License } from '@/license'; export function isWorkflowHistoryLicensed() { @@ -9,7 +9,7 @@ export function isWorkflowHistoryLicensed() { } export function isWorkflowHistoryEnabled() { - return isWorkflowHistoryLicensed() && config.getEnv('workflowHistory.enabled'); + return isWorkflowHistoryLicensed() && Container.get(GlobalConfig).workflowHistory.enabled; } export function getWorkflowHistoryLicensePruneTime() { @@ -19,7 +19,7 @@ export function getWorkflowHistoryLicensePruneTime() { // Time in hours export function getWorkflowHistoryPruneTime(): number { const licenseTime = Container.get(License).getWorkflowHistoryPruneLimit(); - const configTime = config.getEnv('workflowHistory.pruneTime'); + const configTime = Container.get(GlobalConfig).workflowHistory.pruneTime; // License is infinite and config time is infinite if (licenseTime === -1) { diff --git a/packages/cli/test/integration/workflow-history-manager.test.ts b/packages/cli/test/integration/workflow-history-manager.test.ts index 12dd037357..070fcbc3de 100644 --- a/packages/cli/test/integration/workflow-history-manager.test.ts +++ b/packages/cli/test/integration/workflow-history-manager.test.ts @@ -1,8 +1,8 @@ +import { GlobalConfig } from '@n8n/config'; import { Container } from '@n8n/di'; import { In } from '@n8n/typeorm'; import { DateTime } from 'luxon'; -import config from '@/config'; import { WorkflowHistoryRepository } from '@/databases/repositories/workflow-history.repository'; import { License } from '@/license'; import { WorkflowHistoryManager } from '@/workflows/workflow-history.ee/workflow-history-manager.ee'; @@ -16,19 +16,21 @@ describe('Workflow History Manager', () => { const license = mockInstance(License); let repo: WorkflowHistoryRepository; let manager: WorkflowHistoryManager; + let globalConfig: GlobalConfig; beforeAll(async () => { await testDb.init(); repo = Container.get(WorkflowHistoryRepository); manager = Container.get(WorkflowHistoryManager); + globalConfig = Container.get(GlobalConfig); }); beforeEach(async () => { await testDb.truncate(['Workflow']); jest.clearAllMocks(); - config.set('workflowHistory.enabled', true); - config.set('workflowHistory.pruneTime', -1); + globalConfig.workflowHistory.enabled = true; + globalConfig.workflowHistory.pruneTime = -1; license.isWorkflowHistoryLicensed.mockReturnValue(true); license.getWorkflowHistoryPruneLimit.mockReturnValue(-1); @@ -64,7 +66,7 @@ describe('Workflow History Manager', () => { }); test('should not prune when licensed but disabled', async () => { - config.set('workflowHistory.enabled', false); + globalConfig.workflowHistory.enabled = false; await createWorkflowHistory(); await pruneAndAssertCount(); }); @@ -75,7 +77,7 @@ describe('Workflow History Manager', () => { }); test('should prune when config prune time is not -1 (infinite)', async () => { - config.set('workflowHistory.pruneTime', 24); + globalConfig.workflowHistory.pruneTime = 24; await createWorkflowHistory(); await pruneAndAssertCount(0); }); @@ -88,7 +90,7 @@ describe('Workflow History Manager', () => { }); test('should only prune versions older than prune time', async () => { - config.set('workflowHistory.pruneTime', 24); + globalConfig.workflowHistory.pruneTime = 24; const recentVersions = await createWorkflowHistory(0); const oldVersions = await createWorkflowHistory();