mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 09:36:44 +00:00
feat: Proxy all RudderStack frontend telemetry events through the backend (#17177)
Co-authored-by: Nikhil Kuriakose <nikhil.kuriakose@n8n.io>
This commit is contained in:
@@ -31,6 +31,8 @@ describe('NpsSurvey', () => {
|
||||
config: {
|
||||
key: 'test',
|
||||
url: 'https://telemetry-test.n8n.io',
|
||||
proxy: 'http://localhost:5678/rest/telemetry/proxy',
|
||||
sourceConfig: 'http://localhost:5678/rest/telemetry/rudderstack',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -77,6 +79,8 @@ describe('NpsSurvey', () => {
|
||||
config: {
|
||||
key: 'test',
|
||||
url: 'https://telemetry-test.n8n.io',
|
||||
proxy: 'http://localhost:5678/rest/telemetry/proxy',
|
||||
sourceConfig: 'http://localhost:5678/rest/telemetry/rudderstack',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ export interface IVersionNotificationSettings {
|
||||
export interface ITelemetryClientConfig {
|
||||
url: string;
|
||||
key: string;
|
||||
proxy: string;
|
||||
sourceConfig: string;
|
||||
}
|
||||
|
||||
export interface ITelemetrySettings {
|
||||
|
||||
@@ -130,6 +130,7 @@
|
||||
"formidable": "3.5.4",
|
||||
"handlebars": "4.7.8",
|
||||
"helmet": "8.1.0",
|
||||
"http-proxy-middleware": "^3.0.5",
|
||||
"infisical-node": "1.3.0",
|
||||
"ioredis": "5.3.2",
|
||||
"isbot": "3.6.13",
|
||||
|
||||
59
packages/cli/src/controllers/telemetry.controller.ts
Normal file
59
packages/cli/src/controllers/telemetry.controller.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { GlobalConfig } from '@n8n/config';
|
||||
import { AuthenticatedRequest } from '@n8n/db';
|
||||
import { Get, Post, RestController } from '@n8n/decorators';
|
||||
import { NextFunction, Response } from 'express';
|
||||
import { createProxyMiddleware, fixRequestBody } from 'http-proxy-middleware';
|
||||
|
||||
@RestController('/telemetry')
|
||||
export class TelemetryController {
|
||||
proxy;
|
||||
|
||||
constructor(private readonly globalConfig: GlobalConfig) {
|
||||
this.proxy = createProxyMiddleware({
|
||||
target: this.globalConfig.diagnostics.frontendConfig.split(';')[1],
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
'^/proxy/': '/', // /proxy/v1/track -> /v1/track
|
||||
},
|
||||
on: {
|
||||
proxyReq: (proxyReq, req) => {
|
||||
proxyReq.removeHeader('cookie');
|
||||
fixRequestBody(proxyReq, req);
|
||||
return;
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@Post('/proxy/:version/track', { skipAuth: true, rateLimit: { limit: 100, windowMs: 60_000 } })
|
||||
async track(req: AuthenticatedRequest, res: Response, next: NextFunction) {
|
||||
await this.proxy(req, res, next);
|
||||
}
|
||||
|
||||
@Post('/proxy/:version/identify', { skipAuth: true, rateLimit: true })
|
||||
async identify(req: AuthenticatedRequest, res: Response, next: NextFunction) {
|
||||
await this.proxy(req, res, next);
|
||||
}
|
||||
|
||||
@Post('/proxy/:version/page', { skipAuth: true, rateLimit: { limit: 50, windowMs: 60_000 } })
|
||||
async page(req: AuthenticatedRequest, res: Response, next: NextFunction) {
|
||||
await this.proxy(req, res, next);
|
||||
}
|
||||
@Get('/rudderstack/sourceConfig', { skipAuth: true, rateLimit: { limit: 50, windowMs: 60_000 } })
|
||||
async sourceConfig() {
|
||||
const response = await fetch('https://api-rs.n8n.io/sourceConfig', {
|
||||
headers: {
|
||||
authorization:
|
||||
'Basic ' + btoa(`${this.globalConfig.diagnostics.frontendConfig.split(';')[0]}:`),
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch source config: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const config: unknown = await response.json();
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
@@ -151,6 +151,10 @@ export class Server extends AbstractServer {
|
||||
this.logger.warn(`SAML initialization failed: ${(error as Error).message}`);
|
||||
}
|
||||
|
||||
if (this.globalConfig.diagnostics.enabled) {
|
||||
await import('@/controllers/telemetry.controller');
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
// OIDC
|
||||
// ----------------------------------------
|
||||
|
||||
@@ -89,13 +89,15 @@ export class FrontendService {
|
||||
if (telemetrySettings.enabled) {
|
||||
const conf = this.globalConfig.diagnostics.frontendConfig;
|
||||
const [key, url] = conf.split(';');
|
||||
const proxy = `${instanceBaseUrl}/${restEndpoint}/telemetry/proxy`;
|
||||
const sourceConfig = `${instanceBaseUrl}/${restEndpoint}/telemetry/rudderstack`;
|
||||
|
||||
if (!key || !url) {
|
||||
this.logger.warn('Diagnostics frontend config is invalid');
|
||||
telemetrySettings.enabled = false;
|
||||
}
|
||||
|
||||
telemetrySettings.config = { key, url };
|
||||
telemetrySettings.config = { key, url, proxy, sourceConfig };
|
||||
}
|
||||
|
||||
this.settings = {
|
||||
|
||||
@@ -16,7 +16,7 @@ describe('telemetry', () => {
|
||||
setActivePinia(createPinia());
|
||||
settingsStore = useSettingsStore();
|
||||
telemetry.init(
|
||||
{ enabled: true, config: { url: '', key: '' } },
|
||||
{ enabled: true, config: { proxy: '', key: '', sourceConfig: '', url: '' } },
|
||||
{ versionCli: '1', instanceId: '1' },
|
||||
);
|
||||
});
|
||||
|
||||
@@ -47,7 +47,7 @@ export class Telemetry {
|
||||
if (!telemetrySettings.enabled || !telemetrySettings.config || this.rudderStack) return;
|
||||
|
||||
const {
|
||||
config: { key, url },
|
||||
config: { key, proxy, sourceConfig },
|
||||
} = telemetrySettings;
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
@@ -57,10 +57,10 @@ export class Telemetry {
|
||||
|
||||
const logging = logLevel === 'debug' ? { logLevel: 'DEBUG' } : {};
|
||||
|
||||
this.initRudderStack(key, url, {
|
||||
this.initRudderStack(key, proxy, {
|
||||
integrations: { All: false },
|
||||
loadIntegration: false,
|
||||
configUrl: 'https://api-rs.n8n.io',
|
||||
configUrl: sourceConfig,
|
||||
...logging,
|
||||
});
|
||||
|
||||
@@ -201,7 +201,7 @@ export class Telemetry {
|
||||
}
|
||||
}
|
||||
|
||||
private initRudderStack(key: string, url: string, options: IDataObject) {
|
||||
private initRudderStack(key: string, proxy: string, options: IDataObject) {
|
||||
window.rudderanalytics = window.rudderanalytics || [];
|
||||
if (!this.rudderStack) {
|
||||
return;
|
||||
@@ -252,7 +252,7 @@ export class Telemetry {
|
||||
};
|
||||
|
||||
this.rudderStack.loadJS();
|
||||
this.rudderStack.load(key, url, options);
|
||||
this.rudderStack.load(key, proxy, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
37
pnpm-lock.yaml
generated
37
pnpm-lock.yaml
generated
@@ -1429,6 +1429,9 @@ importers:
|
||||
helmet:
|
||||
specifier: 8.1.0
|
||||
version: 8.1.0
|
||||
http-proxy-middleware:
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5
|
||||
infisical-node:
|
||||
specifier: 1.3.0
|
||||
version: 1.3.0
|
||||
@@ -7089,6 +7092,9 @@ packages:
|
||||
'@types/html-to-text@9.0.4':
|
||||
resolution: {integrity: sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ==}
|
||||
|
||||
'@types/http-proxy@1.17.16':
|
||||
resolution: {integrity: sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==}
|
||||
|
||||
'@types/humanize-duration@3.27.1':
|
||||
resolution: {integrity: sha512-K3e+NZlpCKd6Bd/EIdqjFJRFHbrq5TzPPLwREk5Iv/YoIjQrs6ljdAUCo+Lb2xFlGNOjGSE0dqsVD19cZL137w==}
|
||||
|
||||
@@ -10849,6 +10855,14 @@ packages:
|
||||
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
http-proxy-middleware@3.0.5:
|
||||
resolution: {integrity: sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
|
||||
http-proxy@1.18.1:
|
||||
resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
http-signature@1.4.0:
|
||||
resolution: {integrity: sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==}
|
||||
engines: {node: '>=0.10'}
|
||||
@@ -21621,6 +21635,10 @@ snapshots:
|
||||
|
||||
'@types/html-to-text@9.0.4': {}
|
||||
|
||||
'@types/http-proxy@1.17.16':
|
||||
dependencies:
|
||||
'@types/node': 20.19.1
|
||||
|
||||
'@types/humanize-duration@3.27.1': {}
|
||||
|
||||
'@types/imap@0.8.40':
|
||||
@@ -26311,6 +26329,25 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
http-proxy-middleware@3.0.5:
|
||||
dependencies:
|
||||
'@types/http-proxy': 1.17.16
|
||||
debug: 4.4.1(supports-color@8.1.1)
|
||||
http-proxy: 1.18.1(debug@4.4.1)
|
||||
is-glob: 4.0.3
|
||||
is-plain-object: 5.0.0
|
||||
micromatch: 4.0.8
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
http-proxy@1.18.1(debug@4.4.1):
|
||||
dependencies:
|
||||
eventemitter3: 4.0.7
|
||||
follow-redirects: 1.15.9(debug@4.4.1)
|
||||
requires-port: 1.0.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
|
||||
http-signature@1.4.0:
|
||||
dependencies:
|
||||
assert-plus: 1.0.0
|
||||
|
||||
Reference in New Issue
Block a user