mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-21 02:56:40 -08:00
refactor(core): Port diagnostics config (#11761)
This commit is contained in:
parent
61696c3db3
commit
a544b74d87
30
packages/@n8n/config/src/configs/diagnostics.config.ts
Normal file
30
packages/@n8n/config/src/configs/diagnostics.config.ts
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import { CacheConfig } from './configs/cache.config';
|
import { CacheConfig } from './configs/cache.config';
|
||||||
import { CredentialsConfig } from './configs/credentials.config';
|
import { CredentialsConfig } from './configs/credentials.config';
|
||||||
import { DatabaseConfig } from './configs/database.config';
|
import { DatabaseConfig } from './configs/database.config';
|
||||||
|
import { DiagnosticsConfig } from './configs/diagnostics.config';
|
||||||
import { EndpointsConfig } from './configs/endpoints.config';
|
import { EndpointsConfig } from './configs/endpoints.config';
|
||||||
import { EventBusConfig } from './configs/event-bus.config';
|
import { EventBusConfig } from './configs/event-bus.config';
|
||||||
import { ExternalSecretsConfig } from './configs/external-secrets.config';
|
import { ExternalSecretsConfig } from './configs/external-secrets.config';
|
||||||
|
@ -117,4 +118,7 @@ export class GlobalConfig {
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
pruning: PruningConfig;
|
pruning: PruningConfig;
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
diagnostics: DiagnosticsConfig;
|
||||||
}
|
}
|
||||||
|
|
|
@ -282,6 +282,15 @@ describe('GlobalConfig', () => {
|
||||||
hardDeleteInterval: 15,
|
hardDeleteInterval: 15,
|
||||||
softDeleteInterval: 60,
|
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', () => {
|
it('should use all default values when no env variables are defined', () => {
|
||||||
|
|
|
@ -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: {
|
defaultLocale: {
|
||||||
doc: 'Default locale for the UI',
|
doc: 'Default locale for the UI',
|
||||||
format: String,
|
format: String,
|
||||||
|
|
|
@ -2,7 +2,6 @@ import type { GlobalConfig } from '@n8n/config';
|
||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
import type { IWorkflowBase } from 'n8n-workflow';
|
import type { IWorkflowBase } from 'n8n-workflow';
|
||||||
|
|
||||||
import config from '@/config';
|
|
||||||
import { N8N_VERSION } from '@/constants';
|
import { N8N_VERSION } from '@/constants';
|
||||||
import type { WorkflowEntity } from '@/databases/entities/workflow-entity';
|
import type { WorkflowEntity } from '@/databases/entities/workflow-entity';
|
||||||
import type { ProjectRelationRepository } from '@/databases/repositories/project-relation.repository';
|
import type { ProjectRelationRepository } from '@/databases/repositories/project-relation.repository';
|
||||||
|
@ -66,7 +65,7 @@ describe('TelemetryEventRelay', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
config.set('diagnostics.enabled', true);
|
globalConfig.diagnostics.enabled = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@ -75,7 +74,7 @@ describe('TelemetryEventRelay', () => {
|
||||||
|
|
||||||
describe('init', () => {
|
describe('init', () => {
|
||||||
it('with diagnostics enabled, should init telemetry and register listeners', async () => {
|
it('with diagnostics enabled, should init telemetry and register listeners', async () => {
|
||||||
config.set('diagnostics.enabled', true);
|
globalConfig.diagnostics.enabled = true;
|
||||||
const telemetryEventRelay = new TelemetryEventRelay(
|
const telemetryEventRelay = new TelemetryEventRelay(
|
||||||
eventService,
|
eventService,
|
||||||
telemetry,
|
telemetry,
|
||||||
|
@ -96,7 +95,7 @@ describe('TelemetryEventRelay', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('with diagnostics disabled, should neither init telemetry nor register listeners', async () => {
|
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(
|
const telemetryEventRelay = new TelemetryEventRelay(
|
||||||
eventService,
|
eventService,
|
||||||
telemetry,
|
telemetry,
|
||||||
|
|
|
@ -37,7 +37,7 @@ export class TelemetryEventRelay extends EventRelay {
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
if (!config.getEnv('diagnostics.enabled')) return;
|
if (!this.globalConfig.diagnostics.enabled) return;
|
||||||
|
|
||||||
await this.telemetry.init();
|
await this.telemetry.init();
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { mock } from 'jest-mock-extended';
|
||||||
import { InstanceSettings } from 'n8n-core';
|
import { InstanceSettings } from 'n8n-core';
|
||||||
import { PostHog } from 'posthog-node';
|
import { PostHog } from 'posthog-node';
|
||||||
|
|
||||||
import config from '@/config';
|
|
||||||
import { PostHogClient } from '@/posthog';
|
import { PostHogClient } from '@/posthog';
|
||||||
import { mockInstance } from '@test/mocking';
|
import { mockInstance } from '@test/mocking';
|
||||||
|
|
||||||
|
@ -20,12 +19,11 @@ describe('PostHog', () => {
|
||||||
const globalConfig = mock<GlobalConfig>({ logging: { level: 'debug' } });
|
const globalConfig = mock<GlobalConfig>({ logging: { level: 'debug' } });
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
config.set('diagnostics.config.posthog.apiKey', apiKey);
|
globalConfig.diagnostics.posthogConfig = { apiKey, apiHost };
|
||||||
config.set('diagnostics.config.posthog.apiHost', apiHost);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
config.set('diagnostics.enabled', true);
|
globalConfig.diagnostics.enabled = true;
|
||||||
jest.resetAllMocks();
|
jest.resetAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -37,7 +35,7 @@ describe('PostHog', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not initialize or track if diagnostics are not enabled', async () => {
|
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);
|
const ph = new PostHogClient(instanceSettings, globalConfig);
|
||||||
await ph.init();
|
await ph.init();
|
||||||
|
|
|
@ -4,7 +4,6 @@ import type { FeatureFlags, ITelemetryTrackProperties } from 'n8n-workflow';
|
||||||
import type { PostHog } from 'posthog-node';
|
import type { PostHog } from 'posthog-node';
|
||||||
import { Service } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
|
|
||||||
import config from '@/config';
|
|
||||||
import type { PublicUser } from '@/interfaces';
|
import type { PublicUser } from '@/interfaces';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
|
@ -17,14 +16,14 @@ export class PostHogClient {
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
const enabled = config.getEnv('diagnostics.enabled');
|
const { enabled, posthogConfig } = this.globalConfig.diagnostics;
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { PostHog } = await import('posthog-node');
|
const { PostHog } = await import('posthog-node');
|
||||||
this.postHog = new PostHog(config.getEnv('diagnostics.config.posthog.apiKey'), {
|
this.postHog = new PostHog(posthogConfig.apiKey, {
|
||||||
host: config.getEnv('diagnostics.config.posthog.apiHost'),
|
host: posthogConfig.apiHost,
|
||||||
});
|
});
|
||||||
|
|
||||||
const logLevel = this.globalConfig.logging.level;
|
const logLevel = this.globalConfig.logging.level;
|
||||||
|
|
|
@ -103,7 +103,7 @@ export class InstanceRiskReporter implements RiskReporter {
|
||||||
};
|
};
|
||||||
|
|
||||||
settings.telemetry = {
|
settings.telemetry = {
|
||||||
diagnosticsEnabled: config.getEnv('diagnostics.enabled'),
|
diagnosticsEnabled: this.globalConfig.diagnostics.enabled,
|
||||||
};
|
};
|
||||||
|
|
||||||
return settings;
|
return settings;
|
||||||
|
|
|
@ -39,7 +39,7 @@ describe('WorkflowStatisticsService', () => {
|
||||||
});
|
});
|
||||||
Object.assign(entityManager, { connection: dataSource });
|
Object.assign(entityManager, { connection: dataSource });
|
||||||
|
|
||||||
config.set('diagnostics.enabled', true);
|
globalConfig.diagnostics.enabled = true;
|
||||||
config.set('deployment.type', 'n8n-testing');
|
config.set('deployment.type', 'n8n-testing');
|
||||||
mocked(ownershipService.getWorkflowProjectCached).mockResolvedValue(fakeProject);
|
mocked(ownershipService.getWorkflowProjectCached).mockResolvedValue(fakeProject);
|
||||||
mocked(ownershipService.getPersonalProjectOwnerCached).mockResolvedValue(fakeUser);
|
mocked(ownershipService.getPersonalProjectOwnerCached).mockResolvedValue(fakeUser);
|
||||||
|
|
|
@ -66,11 +66,11 @@ export class FrontendService {
|
||||||
const restEndpoint = this.globalConfig.endpoints.rest;
|
const restEndpoint = this.globalConfig.endpoints.rest;
|
||||||
|
|
||||||
const telemetrySettings: ITelemetrySettings = {
|
const telemetrySettings: ITelemetrySettings = {
|
||||||
enabled: config.getEnv('diagnostics.enabled'),
|
enabled: this.globalConfig.diagnostics.enabled,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (telemetrySettings.enabled) {
|
if (telemetrySettings.enabled) {
|
||||||
const conf = config.getEnv('diagnostics.config.frontend');
|
const conf = this.globalConfig.diagnostics.frontendConfig;
|
||||||
const [key, url] = conf.split(';');
|
const [key, url] = conf.split(';');
|
||||||
|
|
||||||
if (!key || !url) {
|
if (!key || !url) {
|
||||||
|
@ -122,15 +122,15 @@ export class FrontendService {
|
||||||
instanceId: this.instanceSettings.instanceId,
|
instanceId: this.instanceSettings.instanceId,
|
||||||
telemetry: telemetrySettings,
|
telemetry: telemetrySettings,
|
||||||
posthog: {
|
posthog: {
|
||||||
enabled: config.getEnv('diagnostics.enabled'),
|
enabled: this.globalConfig.diagnostics.enabled,
|
||||||
apiHost: config.getEnv('diagnostics.config.posthog.apiHost'),
|
apiHost: this.globalConfig.diagnostics.posthogConfig.apiHost,
|
||||||
apiKey: config.getEnv('diagnostics.config.posthog.apiKey'),
|
apiKey: this.globalConfig.diagnostics.posthogConfig.apiKey,
|
||||||
autocapture: false,
|
autocapture: false,
|
||||||
disableSessionRecording: config.getEnv('deployment.type') !== 'cloud',
|
disableSessionRecording: config.getEnv('deployment.type') !== 'cloud',
|
||||||
debug: this.globalConfig.logging.level === 'debug',
|
debug: this.globalConfig.logging.level === 'debug',
|
||||||
},
|
},
|
||||||
personalizationSurveyEnabled:
|
personalizationSurveyEnabled:
|
||||||
config.getEnv('personalization.enabled') && config.getEnv('diagnostics.enabled'),
|
config.getEnv('personalization.enabled') && this.globalConfig.diagnostics.enabled,
|
||||||
defaultLocale: config.getEnv('defaultLocale'),
|
defaultLocale: config.getEnv('defaultLocale'),
|
||||||
userManagement: {
|
userManagement: {
|
||||||
quota: this.license.getUsersLimit(),
|
quota: this.license.getUsersLimit(),
|
||||||
|
|
|
@ -21,6 +21,10 @@ describe('Telemetry', () => {
|
||||||
const instanceId = 'Telemetry unit test';
|
const instanceId = 'Telemetry unit test';
|
||||||
const testDateTime = new Date('2022-01-01 00:00:00');
|
const testDateTime = new Date('2022-01-01 00:00:00');
|
||||||
const instanceSettings = mockInstance(InstanceSettings, { instanceId });
|
const instanceSettings = mockInstance(InstanceSettings, { instanceId });
|
||||||
|
const globalConfig = mock<GlobalConfig>({
|
||||||
|
diagnostics: { enabled: true },
|
||||||
|
logging: { level: 'info', outputs: ['console'] },
|
||||||
|
});
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
// @ts-expect-error Spying on private method
|
// @ts-expect-error Spying on private method
|
||||||
|
@ -28,7 +32,6 @@ describe('Telemetry', () => {
|
||||||
|
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
jest.setSystemTime(testDateTime);
|
jest.setSystemTime(testDateTime);
|
||||||
config.set('diagnostics.enabled', true);
|
|
||||||
config.set('deployment.type', 'n8n-testing');
|
config.set('deployment.type', 'n8n-testing');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -45,14 +48,7 @@ describe('Telemetry', () => {
|
||||||
const postHog = new PostHogClient(instanceSettings, mock());
|
const postHog = new PostHogClient(instanceSettings, mock());
|
||||||
await postHog.init();
|
await postHog.init();
|
||||||
|
|
||||||
telemetry = new Telemetry(
|
telemetry = new Telemetry(mock(), postHog, mock(), instanceSettings, mock(), globalConfig);
|
||||||
mock(),
|
|
||||||
postHog,
|
|
||||||
mock(),
|
|
||||||
instanceSettings,
|
|
||||||
mock(),
|
|
||||||
mock<GlobalConfig>({ logging: { level: 'info', outputs: ['console'] } }),
|
|
||||||
);
|
|
||||||
// @ts-expect-error Assigning to private property
|
// @ts-expect-error Assigning to private property
|
||||||
telemetry.rudderStack = mockRudderStack;
|
telemetry.rudderStack = mockRudderStack;
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { InstanceSettings } from 'n8n-core';
|
||||||
import type { ITelemetryTrackProperties } from 'n8n-workflow';
|
import type { ITelemetryTrackProperties } from 'n8n-workflow';
|
||||||
import { Container, Service } from 'typedi';
|
import { Container, Service } from 'typedi';
|
||||||
|
|
||||||
import config from '@/config';
|
|
||||||
import { LOWEST_SHUTDOWN_PRIORITY, N8N_VERSION } from '@/constants';
|
import { LOWEST_SHUTDOWN_PRIORITY, N8N_VERSION } from '@/constants';
|
||||||
import { ProjectRelationRepository } from '@/databases/repositories/project-relation.repository';
|
import { ProjectRelationRepository } from '@/databases/repositories/project-relation.repository';
|
||||||
import { ProjectRepository } from '@/databases/repositories/project.repository';
|
import { ProjectRepository } from '@/databases/repositories/project.repository';
|
||||||
|
@ -54,10 +53,9 @@ export class Telemetry {
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
const enabled = config.getEnv('diagnostics.enabled');
|
const { enabled, backendConfig } = this.globalConfig.diagnostics;
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
const conf = config.getEnv('diagnostics.config.backend');
|
const [key, dataPlaneUrl] = backendConfig.split(';');
|
||||||
const [key, dataPlaneUrl] = conf.split(';');
|
|
||||||
|
|
||||||
if (!key || !dataPlaneUrl) {
|
if (!key || !dataPlaneUrl) {
|
||||||
this.logger.warn('Diagnostics backend config is invalid');
|
this.logger.warn('Diagnostics backend config is invalid');
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import { GlobalConfig } from '@n8n/config';
|
||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
import { NodeConnectionType } from 'n8n-workflow';
|
import { NodeConnectionType } from 'n8n-workflow';
|
||||||
import Container from 'typedi';
|
import Container from 'typedi';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
import config from '@/config';
|
|
||||||
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
|
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
|
||||||
import { generateNanoId } from '@/databases/utils/generators';
|
import { generateNanoId } from '@/databases/utils/generators';
|
||||||
import { INSTANCE_REPORT, WEBHOOK_VALIDATOR_NODE_TYPES } from '@/security-audit/constants';
|
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 () => {
|
test('should report security settings', async () => {
|
||||||
config.set('diagnostics.enabled', true);
|
Container.get(GlobalConfig).diagnostics.enabled = true;
|
||||||
|
|
||||||
const testAudit = await securityAuditService.run(['instance']);
|
const testAudit = await securityAuditService.run(['instance']);
|
||||||
|
|
||||||
const section = getRiskSection(
|
const section = getRiskSection(
|
||||||
|
|
Loading…
Reference in a new issue