diff --git a/packages/@n8n/api-types/src/push/execution.ts b/packages/@n8n/api-types/src/push/execution.ts index 3c7459dec5715..9c723e2817f8a 100644 --- a/packages/@n8n/api-types/src/push/execution.ts +++ b/packages/@n8n/api-types/src/push/execution.ts @@ -12,6 +12,13 @@ type ExecutionStarted = { }; }; +type ExecutionWaiting = { + type: 'executionWaiting'; + data: { + executionId: string; + }; +}; + type ExecutionFinished = { type: 'executionFinished'; data: { @@ -45,6 +52,7 @@ type NodeExecuteAfter = { export type ExecutionPushMessage = | ExecutionStarted + | ExecutionWaiting | ExecutionFinished | ExecutionRecovered | NodeExecuteBefore diff --git a/packages/@n8n/config/src/configs/diagnostics.config.ts b/packages/@n8n/config/src/configs/diagnostics.config.ts new file mode 100644 index 0000000000000..58e4740b35191 --- /dev/null +++ b/packages/@n8n/config/src/configs/diagnostics.config.ts @@ -0,0 +1,30 @@ +import { Config, Env, Nested } from '../decorators'; + +@Config +class PostHogConfig { + /** API key for PostHog. */ + @Env('N8N_DIAGNOSTICS_POSTHOG_API_KEY') + apiKey: string = 'phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo'; + + /** API host for PostHog. */ + @Env('N8N_DIAGNOSTICS_POSTHOG_API_HOST') + apiHost: string = 'https://ph.n8n.io'; +} + +@Config +export class DiagnosticsConfig { + /** Whether diagnostics are enabled. */ + @Env('N8N_DIAGNOSTICS_ENABLED') + enabled: boolean = false; + + /** Diagnostics config for frontend. */ + @Env('N8N_DIAGNOSTICS_CONFIG_FRONTEND') + frontendConfig: string = '1zPn9bgWPzlQc0p8Gj1uiK6DOTn;https://telemetry.n8n.io'; + + /** Diagnostics config for backend. */ + @Env('N8N_DIAGNOSTICS_CONFIG_BACKEND') + backendConfig: string = '1zPn7YoGC3ZXE9zLeTKLuQCB4F6;https://telemetry.n8n.io'; + + @Nested + posthogConfig: PostHogConfig; +} diff --git a/packages/@n8n/config/src/index.ts b/packages/@n8n/config/src/index.ts index 0a89535ee32ab..a1c0a1f43bed6 100644 --- a/packages/@n8n/config/src/index.ts +++ b/packages/@n8n/config/src/index.ts @@ -1,6 +1,7 @@ import { CacheConfig } from './configs/cache.config'; import { CredentialsConfig } from './configs/credentials.config'; import { DatabaseConfig } from './configs/database.config'; +import { DiagnosticsConfig } from './configs/diagnostics.config'; import { EndpointsConfig } from './configs/endpoints.config'; import { EventBusConfig } from './configs/event-bus.config'; import { ExternalSecretsConfig } from './configs/external-secrets.config'; @@ -117,4 +118,7 @@ export class GlobalConfig { @Nested pruning: PruningConfig; + + @Nested + diagnostics: DiagnosticsConfig; } diff --git a/packages/@n8n/config/test/config.test.ts b/packages/@n8n/config/test/config.test.ts index e8f6e549a9d9a..c60431e97a9b0 100644 --- a/packages/@n8n/config/test/config.test.ts +++ b/packages/@n8n/config/test/config.test.ts @@ -282,6 +282,15 @@ describe('GlobalConfig', () => { hardDeleteInterval: 15, softDeleteInterval: 60, }, + diagnostics: { + enabled: false, + frontendConfig: '1zPn9bgWPzlQc0p8Gj1uiK6DOTn;https://telemetry.n8n.io', + backendConfig: '1zPn7YoGC3ZXE9zLeTKLuQCB4F6;https://telemetry.n8n.io', + posthogConfig: { + apiKey: 'phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo', + apiHost: 'https://ph.n8n.io', + }, + }, }; it('should use all default values when no env variables are defined', () => { diff --git a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAi/EmbeddingsAzureOpenAi.node.ts b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAi/EmbeddingsAzureOpenAi.node.ts index a75a93c9f4058..8c178543fcb2d 100644 --- a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAi/EmbeddingsAzureOpenAi.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsAzureOpenAi/EmbeddingsAzureOpenAi.node.ts @@ -87,6 +87,36 @@ export class EmbeddingsAzureOpenAi implements INodeType { 'Maximum amount of time a request is allowed to take in seconds. Set to -1 for no timeout.', type: 'number', }, + { + displayName: 'Dimensions', + name: 'dimensions', + default: undefined, + description: + 'The number of dimensions the resulting output embeddings should have. Only supported in text-embedding-3 and later models.', + type: 'options', + options: [ + { + name: '256', + value: 256, + }, + { + name: '512', + value: 512, + }, + { + name: '1024', + value: 1024, + }, + { + name: '1536', + value: 1536, + }, + { + name: '3072', + value: 3072, + }, + ], + }, ], }, ], @@ -105,6 +135,7 @@ export class EmbeddingsAzureOpenAi implements INodeType { batchSize?: number; stripNewLines?: boolean; timeout?: number; + dimensions?: number | undefined; }; if (options.timeout === -1) { diff --git a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsOpenAI/EmbeddingsOpenAi.node.ts b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsOpenAI/EmbeddingsOpenAi.node.ts index 167581ed2ec37..aececc09aed74 100644 --- a/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsOpenAI/EmbeddingsOpenAi.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/embeddings/EmbeddingsOpenAI/EmbeddingsOpenAi.node.ts @@ -135,6 +135,36 @@ export class EmbeddingsOpenAi implements INodeType { type: 'collection', default: {}, options: [ + { + displayName: 'Dimensions', + name: 'dimensions', + default: undefined, + description: + 'The number of dimensions the resulting output embeddings should have. Only supported in text-embedding-3 and later models.', + type: 'options', + options: [ + { + name: '256', + value: 256, + }, + { + name: '512', + value: 512, + }, + { + name: '1024', + value: 1024, + }, + { + name: '1536', + value: 1536, + }, + { + name: '3072', + value: 3072, + }, + ], + }, { displayName: 'Base URL', name: 'baseURL', @@ -179,6 +209,7 @@ export class EmbeddingsOpenAi implements INodeType { batchSize?: number; stripNewLines?: boolean; timeout?: number; + dimensions?: number | undefined; }; if (options.timeout === -1) { diff --git a/packages/cli/src/config/schema.ts b/packages/cli/src/config/schema.ts index 9f8bc452320e3..54fa07e7f5dcb 100644 --- a/packages/cli/src/config/schema.ts +++ b/packages/cli/src/config/schema.ts @@ -296,43 +296,6 @@ export const schema = { }, }, - diagnostics: { - enabled: { - doc: 'Whether diagnostic mode is enabled.', - format: Boolean, - default: true, - env: 'N8N_DIAGNOSTICS_ENABLED', - }, - config: { - posthog: { - apiKey: { - doc: 'API key for PostHog', - format: String, - default: 'phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo', - env: 'N8N_DIAGNOSTICS_POSTHOG_API_KEY', - }, - apiHost: { - doc: 'API host for PostHog', - format: String, - default: 'https://ph.n8n.io', - env: 'N8N_DIAGNOSTICS_POSTHOG_API_HOST', - }, - }, - frontend: { - doc: 'Diagnostics config for frontend.', - format: String, - default: '1zPn9bgWPzlQc0p8Gj1uiK6DOTn;https://telemetry.n8n.io', - env: 'N8N_DIAGNOSTICS_CONFIG_FRONTEND', - }, - backend: { - doc: 'Diagnostics config for backend.', - format: String, - default: '1zPn7YoGC3ZXE9zLeTKLuQCB4F6;https://telemetry.n8n.io', - env: 'N8N_DIAGNOSTICS_CONFIG_BACKEND', - }, - }, - }, - defaultLocale: { doc: 'Default locale for the UI', format: String, 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 7e98877dc7360..58d694e5560ec 100644 --- a/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts +++ b/packages/cli/src/events/__tests__/telemetry-event-relay.test.ts @@ -2,7 +2,6 @@ import type { GlobalConfig } from '@n8n/config'; import { mock } from 'jest-mock-extended'; import type { IWorkflowBase } from 'n8n-workflow'; -import config from '@/config'; import { N8N_VERSION } from '@/constants'; import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import type { ProjectRelationRepository } from '@/databases/repositories/project-relation.repository'; @@ -66,7 +65,7 @@ describe('TelemetryEventRelay', () => { }); beforeEach(() => { - config.set('diagnostics.enabled', true); + globalConfig.diagnostics.enabled = true; }); afterEach(() => { @@ -75,7 +74,7 @@ describe('TelemetryEventRelay', () => { describe('init', () => { it('with diagnostics enabled, should init telemetry and register listeners', async () => { - config.set('diagnostics.enabled', true); + globalConfig.diagnostics.enabled = true; const telemetryEventRelay = new TelemetryEventRelay( eventService, telemetry, @@ -96,7 +95,7 @@ describe('TelemetryEventRelay', () => { }); it('with diagnostics disabled, should neither init telemetry nor register listeners', async () => { - config.set('diagnostics.enabled', false); + globalConfig.diagnostics.enabled = false; const telemetryEventRelay = new TelemetryEventRelay( eventService, telemetry, diff --git a/packages/cli/src/events/relays/telemetry.event-relay.ts b/packages/cli/src/events/relays/telemetry.event-relay.ts index 88f954ab93225..9e7b02659743b 100644 --- a/packages/cli/src/events/relays/telemetry.event-relay.ts +++ b/packages/cli/src/events/relays/telemetry.event-relay.ts @@ -37,7 +37,7 @@ export class TelemetryEventRelay extends EventRelay { } async init() { - if (!config.getEnv('diagnostics.enabled')) return; + if (!this.globalConfig.diagnostics.enabled) return; await this.telemetry.init(); diff --git a/packages/cli/src/execution-lifecycle-hooks/__tests__/save-execution-progress.test.ts b/packages/cli/src/execution-lifecycle-hooks/__tests__/save-execution-progress.test.ts index b0db5becac714..d89f2fb734d2f 100644 --- a/packages/cli/src/execution-lifecycle-hooks/__tests__/save-execution-progress.test.ts +++ b/packages/cli/src/execution-lifecycle-hooks/__tests__/save-execution-progress.test.ts @@ -1,5 +1,4 @@ import { - deepCopy, ErrorReporterProxy, type IRunExecutionData, type ITaskData, @@ -87,37 +86,6 @@ test('should update execution when saving progress is enabled', async () => { expect(reporterSpy).not.toHaveBeenCalled(); }); -test('should update execution when saving progress is disabled, but waitTill is defined', async () => { - jest.spyOn(fnModule, 'toSaveSettings').mockReturnValue({ - ...commonSettings, - progress: false, - }); - - const reporterSpy = jest.spyOn(ErrorReporterProxy, 'error'); - - executionRepository.findSingleExecution.mockResolvedValue({} as IExecutionResponse); - - const args = deepCopy(commonArgs); - args[4].waitTill = new Date(); - await saveExecutionProgress(...args); - - expect(executionRepository.updateExistingExecution).toHaveBeenCalledWith('some-execution-id', { - data: { - executionData: undefined, - resultData: { - lastNodeExecuted: 'My Node', - runData: { - 'My Node': [{}], - }, - }, - startData: {}, - }, - status: 'running', - }); - - expect(reporterSpy).not.toHaveBeenCalled(); -}); - test('should report error on failure', async () => { jest.spyOn(fnModule, 'toSaveSettings').mockReturnValue({ ...commonSettings, diff --git a/packages/cli/src/execution-lifecycle-hooks/save-execution-progress.ts b/packages/cli/src/execution-lifecycle-hooks/save-execution-progress.ts index 6cd1cfd08f78c..ca9899e1ec1a2 100644 --- a/packages/cli/src/execution-lifecycle-hooks/save-execution-progress.ts +++ b/packages/cli/src/execution-lifecycle-hooks/save-execution-progress.ts @@ -16,7 +16,7 @@ export async function saveExecutionProgress( ) { const saveSettings = toSaveSettings(workflowData.settings); - if (!saveSettings.progress && !executionData.waitTill) return; + if (!saveSettings.progress) return; const logger = Container.get(Logger); diff --git a/packages/cli/src/execution-lifecycle-hooks/to-save-settings.ts b/packages/cli/src/execution-lifecycle-hooks/to-save-settings.ts index 7a25adaeba912..a7af8f3ddc233 100644 --- a/packages/cli/src/execution-lifecycle-hooks/to-save-settings.ts +++ b/packages/cli/src/execution-lifecycle-hooks/to-save-settings.ts @@ -18,20 +18,20 @@ export function toSaveSettings(workflowSettings: IWorkflowSettings = {}) { PROGRESS: config.getEnv('executions.saveExecutionProgress'), }; + const { + saveDataErrorExecution = DEFAULTS.ERROR, + saveDataSuccessExecution = DEFAULTS.SUCCESS, + saveManualExecutions = DEFAULTS.MANUAL, + saveExecutionProgress = DEFAULTS.PROGRESS, + } = workflowSettings; + return { - error: workflowSettings.saveDataErrorExecution - ? workflowSettings.saveDataErrorExecution !== 'none' - : DEFAULTS.ERROR !== 'none', - success: workflowSettings.saveDataSuccessExecution - ? workflowSettings.saveDataSuccessExecution !== 'none' - : DEFAULTS.SUCCESS !== 'none', - manual: - workflowSettings === undefined || workflowSettings.saveManualExecutions === 'DEFAULT' - ? DEFAULTS.MANUAL - : (workflowSettings.saveManualExecutions ?? DEFAULTS.MANUAL), - progress: - workflowSettings === undefined || workflowSettings.saveExecutionProgress === 'DEFAULT' - ? DEFAULTS.PROGRESS - : (workflowSettings.saveExecutionProgress ?? DEFAULTS.PROGRESS), + error: saveDataErrorExecution === 'DEFAULT' ? DEFAULTS.ERROR : saveDataErrorExecution === 'all', + success: + saveDataSuccessExecution === 'DEFAULT' + ? DEFAULTS.SUCCESS + : saveDataSuccessExecution === 'all', + manual: saveManualExecutions === 'DEFAULT' ? DEFAULTS.MANUAL : saveManualExecutions, + progress: saveExecutionProgress === 'DEFAULT' ? DEFAULTS.PROGRESS : saveExecutionProgress, }; } diff --git a/packages/cli/src/posthog/__tests__/posthog.test.ts b/packages/cli/src/posthog/__tests__/posthog.test.ts index 5c8fe282bfcbf..5e11d247733a8 100644 --- a/packages/cli/src/posthog/__tests__/posthog.test.ts +++ b/packages/cli/src/posthog/__tests__/posthog.test.ts @@ -3,7 +3,6 @@ import { mock } from 'jest-mock-extended'; import { InstanceSettings } from 'n8n-core'; import { PostHog } from 'posthog-node'; -import config from '@/config'; import { PostHogClient } from '@/posthog'; import { mockInstance } from '@test/mocking'; @@ -20,12 +19,11 @@ describe('PostHog', () => { const globalConfig = mock({ logging: { level: 'debug' } }); beforeAll(() => { - config.set('diagnostics.config.posthog.apiKey', apiKey); - config.set('diagnostics.config.posthog.apiHost', apiHost); + globalConfig.diagnostics.posthogConfig = { apiKey, apiHost }; }); beforeEach(() => { - config.set('diagnostics.enabled', true); + globalConfig.diagnostics.enabled = true; jest.resetAllMocks(); }); @@ -37,7 +35,7 @@ describe('PostHog', () => { }); it('does not initialize or track if diagnostics are not enabled', async () => { - config.set('diagnostics.enabled', false); + globalConfig.diagnostics.enabled = false; const ph = new PostHogClient(instanceSettings, globalConfig); await ph.init(); diff --git a/packages/cli/src/posthog/index.ts b/packages/cli/src/posthog/index.ts index 8dec9755b38bb..be025c8a85050 100644 --- a/packages/cli/src/posthog/index.ts +++ b/packages/cli/src/posthog/index.ts @@ -4,7 +4,6 @@ import type { FeatureFlags, ITelemetryTrackProperties } from 'n8n-workflow'; import type { PostHog } from 'posthog-node'; import { Service } from 'typedi'; -import config from '@/config'; import type { PublicUser } from '@/interfaces'; @Service() @@ -17,14 +16,14 @@ export class PostHogClient { ) {} async init() { - const enabled = config.getEnv('diagnostics.enabled'); + const { enabled, posthogConfig } = this.globalConfig.diagnostics; if (!enabled) { return; } const { PostHog } = await import('posthog-node'); - this.postHog = new PostHog(config.getEnv('diagnostics.config.posthog.apiKey'), { - host: config.getEnv('diagnostics.config.posthog.apiHost'), + this.postHog = new PostHog(posthogConfig.apiKey, { + host: posthogConfig.apiHost, }); const logLevel = this.globalConfig.logging.level; 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 b0d6ccfad345c..fafad308ad662 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 @@ -103,7 +103,7 @@ export class InstanceRiskReporter implements RiskReporter { }; settings.telemetry = { - diagnosticsEnabled: config.getEnv('diagnostics.enabled'), + diagnosticsEnabled: this.globalConfig.diagnostics.enabled, }; return settings; diff --git a/packages/cli/src/services/__tests__/workflow-statistics.service.test.ts b/packages/cli/src/services/__tests__/workflow-statistics.service.test.ts index fdecb7ae5abb0..6d28dbe563485 100644 --- a/packages/cli/src/services/__tests__/workflow-statistics.service.test.ts +++ b/packages/cli/src/services/__tests__/workflow-statistics.service.test.ts @@ -39,7 +39,7 @@ describe('WorkflowStatisticsService', () => { }); Object.assign(entityManager, { connection: dataSource }); - config.set('diagnostics.enabled', true); + globalConfig.diagnostics.enabled = true; config.set('deployment.type', 'n8n-testing'); mocked(ownershipService.getWorkflowProjectCached).mockResolvedValue(fakeProject); mocked(ownershipService.getPersonalProjectOwnerCached).mockResolvedValue(fakeUser); diff --git a/packages/cli/src/services/frontend.service.ts b/packages/cli/src/services/frontend.service.ts index 081ef5fd4a820..db77bd418d3f1 100644 --- a/packages/cli/src/services/frontend.service.ts +++ b/packages/cli/src/services/frontend.service.ts @@ -66,11 +66,11 @@ export class FrontendService { const restEndpoint = this.globalConfig.endpoints.rest; const telemetrySettings: ITelemetrySettings = { - enabled: config.getEnv('diagnostics.enabled'), + enabled: this.globalConfig.diagnostics.enabled, }; if (telemetrySettings.enabled) { - const conf = config.getEnv('diagnostics.config.frontend'); + const conf = this.globalConfig.diagnostics.frontendConfig; const [key, url] = conf.split(';'); if (!key || !url) { @@ -122,15 +122,15 @@ export class FrontendService { instanceId: this.instanceSettings.instanceId, telemetry: telemetrySettings, posthog: { - enabled: config.getEnv('diagnostics.enabled'), - apiHost: config.getEnv('diagnostics.config.posthog.apiHost'), - apiKey: config.getEnv('diagnostics.config.posthog.apiKey'), + enabled: this.globalConfig.diagnostics.enabled, + apiHost: this.globalConfig.diagnostics.posthogConfig.apiHost, + apiKey: this.globalConfig.diagnostics.posthogConfig.apiKey, autocapture: false, disableSessionRecording: config.getEnv('deployment.type') !== 'cloud', debug: this.globalConfig.logging.level === 'debug', }, personalizationSurveyEnabled: - config.getEnv('personalization.enabled') && config.getEnv('diagnostics.enabled'), + config.getEnv('personalization.enabled') && this.globalConfig.diagnostics.enabled, defaultLocale: config.getEnv('defaultLocale'), userManagement: { quota: this.license.getUsersLimit(), diff --git a/packages/cli/src/telemetry/__tests__/telemetry.test.ts b/packages/cli/src/telemetry/__tests__/telemetry.test.ts index 04a6cecfcada7..d0f9fae3cdf1c 100644 --- a/packages/cli/src/telemetry/__tests__/telemetry.test.ts +++ b/packages/cli/src/telemetry/__tests__/telemetry.test.ts @@ -21,6 +21,10 @@ describe('Telemetry', () => { const instanceId = 'Telemetry unit test'; const testDateTime = new Date('2022-01-01 00:00:00'); const instanceSettings = mockInstance(InstanceSettings, { instanceId }); + const globalConfig = mock({ + diagnostics: { enabled: true }, + logging: { level: 'info', outputs: ['console'] }, + }); beforeAll(() => { // @ts-expect-error Spying on private method @@ -28,7 +32,6 @@ describe('Telemetry', () => { jest.useFakeTimers(); jest.setSystemTime(testDateTime); - config.set('diagnostics.enabled', true); config.set('deployment.type', 'n8n-testing'); }); @@ -45,14 +48,7 @@ describe('Telemetry', () => { const postHog = new PostHogClient(instanceSettings, mock()); await postHog.init(); - telemetry = new Telemetry( - mock(), - postHog, - mock(), - instanceSettings, - mock(), - mock({ logging: { level: 'info', outputs: ['console'] } }), - ); + telemetry = new Telemetry(mock(), postHog, mock(), instanceSettings, mock(), globalConfig); // @ts-expect-error Assigning to private property telemetry.rudderStack = mockRudderStack; }); diff --git a/packages/cli/src/telemetry/index.ts b/packages/cli/src/telemetry/index.ts index d9a8e590f4fb3..a8d39d898ee8a 100644 --- a/packages/cli/src/telemetry/index.ts +++ b/packages/cli/src/telemetry/index.ts @@ -5,7 +5,6 @@ import { InstanceSettings } from 'n8n-core'; import type { ITelemetryTrackProperties } from 'n8n-workflow'; import { Container, Service } from 'typedi'; -import config from '@/config'; import { LOWEST_SHUTDOWN_PRIORITY, N8N_VERSION } from '@/constants'; import { ProjectRelationRepository } from '@/databases/repositories/project-relation.repository'; import { ProjectRepository } from '@/databases/repositories/project.repository'; @@ -54,10 +53,9 @@ export class Telemetry { ) {} async init() { - const enabled = config.getEnv('diagnostics.enabled'); + const { enabled, backendConfig } = this.globalConfig.diagnostics; if (enabled) { - const conf = config.getEnv('diagnostics.config.backend'); - const [key, dataPlaneUrl] = conf.split(';'); + const [key, dataPlaneUrl] = backendConfig.split(';'); if (!key || !dataPlaneUrl) { this.logger.warn('Diagnostics backend config is invalid'); diff --git a/packages/cli/src/webhooks/webhook-helpers.ts b/packages/cli/src/webhooks/webhook-helpers.ts index 72628b8351695..6110584f7e555 100644 --- a/packages/cli/src/webhooks/webhook-helpers.ts +++ b/packages/cli/src/webhooks/webhook-helpers.ts @@ -464,6 +464,11 @@ export async function executeWebhook( projectId: project?.id, }; + // When resuming from a wait node, copy over the pushRef from the execution-data + if (!runData.pushRef) { + runData.pushRef = runExecutionData.pushRef; + } + let responsePromise: IDeferredPromise | undefined; if (responseMode === 'responseNode') { responsePromise = createDeferredPromise(); diff --git a/packages/cli/src/workflow-execute-additional-data.ts b/packages/cli/src/workflow-execute-additional-data.ts index 08d6ba09e41a6..97322f4fe0230 100644 --- a/packages/cli/src/workflow-execute-additional-data.ts +++ b/packages/cli/src/workflow-execute-additional-data.ts @@ -307,7 +307,7 @@ function hookFunctionsPush(): IWorkflowExecuteHooks { }, ], workflowExecuteAfter: [ - async function (this: WorkflowHooks): Promise { + async function (this: WorkflowHooks, fullRunData: IRun): Promise { const { pushRef, executionId } = this; if (pushRef === undefined) return; @@ -318,7 +318,9 @@ function hookFunctionsPush(): IWorkflowExecuteHooks { workflowId, }); - pushInstance.send('executionFinished', { executionId }, pushRef); + const pushType = + fullRunData.status === 'waiting' ? 'executionWaiting' : 'executionFinished'; + pushInstance.send(pushType, { executionId }, pushRef); }, ], }; @@ -430,22 +432,21 @@ function hookFunctionsSave(): IWorkflowExecuteHooks { (executionStatus === 'success' && !saveSettings.success) || (executionStatus !== 'success' && !saveSettings.error); - if (shouldNotSave && !fullRunData.waitTill) { - if (!fullRunData.waitTill && !isManualMode) { - executeErrorWorkflow( - this.workflowData, - fullRunData, - this.mode, - this.executionId, - this.retryOf, - ); - await Container.get(ExecutionRepository).hardDelete({ - workflowId: this.workflowData.id, - executionId: this.executionId, - }); + if (shouldNotSave && !fullRunData.waitTill && !isManualMode) { + executeErrorWorkflow( + this.workflowData, + fullRunData, + this.mode, + this.executionId, + this.retryOf, + ); - return; - } + await Container.get(ExecutionRepository).hardDelete({ + workflowId: this.workflowData.id, + executionId: this.executionId, + }); + + return; } // Although it is treated as IWorkflowBase here, it's being instantiated elsewhere with properties that may be sensitive @@ -1110,6 +1111,9 @@ export function getWorkflowHooksWorkerMain( hookFunctions.nodeExecuteAfter = []; hookFunctions.workflowExecuteAfter = [ async function (this: WorkflowHooks, fullRunData: IRun): Promise { + // Don't delete executions before they are finished + if (!fullRunData.finished) return; + const executionStatus = determineFinalExecutionStatus(fullRunData); const saveSettings = toSaveSettings(this.workflowData.settings); diff --git a/packages/cli/templates/form-trigger.handlebars b/packages/cli/templates/form-trigger.handlebars index 57d93cb29144b..02611f5b5b285 100644 --- a/packages/cli/templates/form-trigger.handlebars +++ b/packages/cli/templates/form-trigger.handlebars @@ -740,14 +740,6 @@ } return; - }).then(() => { - window.addEventListener('storage', function(event) { - if (event.key === 'n8n_redirect_to_next_form_test_page' && event.newValue) { - const newUrl = event.newValue; - localStorage.removeItem('n8n_redirect_to_next_form_test_page'); - window.location.replace(newUrl); - } - }); }) .catch(function (error) { console.error('Error:', error); diff --git a/packages/cli/test/integration/security-audit/instance-risk-reporter.test.ts b/packages/cli/test/integration/security-audit/instance-risk-reporter.test.ts index 928667b518d77..58a2a2c9a8793 100644 --- a/packages/cli/test/integration/security-audit/instance-risk-reporter.test.ts +++ b/packages/cli/test/integration/security-audit/instance-risk-reporter.test.ts @@ -1,9 +1,9 @@ +import { GlobalConfig } from '@n8n/config'; import { mock } from 'jest-mock-extended'; import { NodeConnectionType } from 'n8n-workflow'; import Container from 'typedi'; import { v4 as uuid } from 'uuid'; -import config from '@/config'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; import { generateNanoId } from '@/databases/utils/generators'; import { INSTANCE_REPORT, WEBHOOK_VALIDATOR_NODE_TYPES } from '@/security-audit/constants'; @@ -239,8 +239,7 @@ test('should not report outdated instance when up to date', async () => { }); test('should report security settings', async () => { - config.set('diagnostics.enabled', true); - + Container.get(GlobalConfig).diagnostics.enabled = true; const testAudit = await securityAuditService.run(['instance']); const section = getRiskSection( diff --git a/packages/core/src/WorkflowExecute.ts b/packages/core/src/WorkflowExecute.ts index 2ae12908aca69..c6e0316038cc7 100644 --- a/packages/core/src/WorkflowExecute.ts +++ b/packages/core/src/WorkflowExecute.ts @@ -916,7 +916,6 @@ export class WorkflowExecute { let nodeSuccessData: INodeExecutionData[][] | null | undefined; let runIndex: number; let startTime: number; - let taskData: ITaskData; if (this.runExecutionData.startData === undefined) { this.runExecutionData.startData = {}; @@ -1446,13 +1445,13 @@ export class WorkflowExecute { this.runExecutionData.resultData.runData[executionNode.name] = []; } - taskData = { + const taskData: ITaskData = { hints: executionHints, startTime, executionTime: new Date().getTime() - startTime, source: !executionData.source ? [] : executionData.source.main, metadata: executionData.metadata, - executionStatus: 'success', + executionStatus: this.runExecutionData.waitTill ? 'waiting' : 'success', }; if (executionError !== undefined) { diff --git a/packages/editor-ui/src/components/InputPanel.vue b/packages/editor-ui/src/components/InputPanel.vue index 169c61de46f97..a3ddc8ed64203 100644 --- a/packages/editor-ui/src/components/InputPanel.vue +++ b/packages/editor-ui/src/components/InputPanel.vue @@ -212,7 +212,10 @@ const activeNodeType = computed(() => { return nodeTypesStore.getNodeType(activeNode.value.type, activeNode.value.typeVersion); }); -const waitingMessage = computed(() => waitingNodeTooltip()); +const waitingMessage = computed(() => { + const parentNode = parentNodes.value[0]; + return parentNode && waitingNodeTooltip(workflowsStore.getNodeByName(parentNode.name)); +}); watch( inputMode, diff --git a/packages/editor-ui/src/components/NodeExecuteButton.vue b/packages/editor-ui/src/components/NodeExecuteButton.vue index 43b4dfa7dcfd4..f801f0701c45e 100644 --- a/packages/editor-ui/src/components/NodeExecuteButton.vue +++ b/packages/editor-ui/src/components/NodeExecuteButton.vue @@ -65,7 +65,7 @@ const lastPopupCountUpdate = ref(0); const codeGenerationInProgress = ref(false); const router = useRouter(); -const { runWorkflow, runWorkflowResolvePending, stopCurrentExecution } = useRunWorkflow({ router }); +const { runWorkflow, stopCurrentExecution } = useRunWorkflow({ router }); const workflowsStore = useWorkflowsStore(); const externalHooks = useExternalHooks(); @@ -353,17 +353,10 @@ async function onClick() { telemetry.track('User clicked execute node button', telemetryPayload); await externalHooks.run('nodeExecuteButton.onClick', telemetryPayload); - if (workflowsStore.isWaitingExecution) { - await runWorkflowResolvePending({ - destinationNode: props.nodeName, - source: 'RunData.ExecuteNodeButton', - }); - } else { - await runWorkflow({ - destinationNode: props.nodeName, - source: 'RunData.ExecuteNodeButton', - }); - } + await runWorkflow({ + destinationNode: props.nodeName, + source: 'RunData.ExecuteNodeButton', + }); emit('execute'); } diff --git a/packages/editor-ui/src/components/OutputPanel.vue b/packages/editor-ui/src/components/OutputPanel.vue index 0503ad5c94f45..0ca7e3830eab4 100644 --- a/packages/editor-ui/src/components/OutputPanel.vue +++ b/packages/editor-ui/src/components/OutputPanel.vue @@ -352,7 +352,7 @@ const activatePane = () => {