d3209b5aab
Maintainer resolved E1 as variant B: the public vitals sink + client collection must be OFF by default (else client_metrics grows unbounded on a self-host deploy with no external pruner, via an unauthenticated public endpoint). - F1: new operator flag CLIENT_TELEMETRY_ENABLED (default OFF), SEPARATE from METRICS_PORT (Grafana reads the table directly, independent of the scrape port). ClientTelemetryModule.register() provides VitalsController ONLY when the flag is true (route absent otherwise); the flag reaches the client via window.CONFIG (config.ts isClientTelemetryEnabled), and initVitals() early-returns when off. - F2/F3 [throttler]: this repo's ThrottlerGuard applies EVERY named throttler to every guarded route unless skipped. The new VITALS bucket therefore (a) newly bound collab-token → 429 behind shared/NAT IPs, and (b) the vitals route didn't skip the stricter public-share-ai (5/min) bucket → effective 5/min not 120. Fix (additive, global config unchanged): vitals.controller @SkipThrottle the other buckets + @Throttle VITALS 120/min; collab-token adds VITALS_THROTTLER to its existing @SkipThrottle (restoring its prior effectively-unthrottled state). - F4: metrics node:http server is closed on shutdown (MetricsServerLifecycle OnModuleDestroy → closeMetricsServer(), fired by enableShutdownHooks). - F5: docSize outside [0, int4-max] drops to null (keeping the event) instead of overflowing int4 and failing the WHOLE batch insert (+ 2 tests). - F6: .env.example documents METRICS_PORT (no default — unset = subsystem OFF) + CLIENT_TELEMETRY_ENABLED; fixed the inaccurate "default 9464" wording. - F7: disabled/non-sampled sessions install ZERO observers — isVitalsActive() (enabled && sampled) gates reportClientMetric AND the page-editor measurePageOpen + dispatchTransaction wrapping. - F8: kept db.d.ts hand-added (wontfix) — this repo HAND-CURATES db.d.ts (verified across recent fork migrations a32fba63/8c5b57eb/fdeede00); codegen would be the deviation. The ClientMetrics interface maps the migration 1:1. Gate: server tsc 0, client tsc 0, server metrics/vitals/telemetry/throttle 21 tests, client route-template 5. No new deps. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
33 lines
1.4 KiB
TypeScript
33 lines
1.4 KiB
TypeScript
import { DynamicModule, Module } from '@nestjs/common';
|
|
import { VitalsController } from './vitals.controller';
|
|
import { VitalsService } from './vitals.service';
|
|
|
|
/**
|
|
* Client perf-telemetry (#355): the public /api/telemetry/vitals sink that
|
|
* persists web-vitals + custom client metrics into `client_metrics`.
|
|
* Named ClientTelemetryModule to avoid confusion with the unrelated
|
|
* integrations/telemetry (product usage ping) module.
|
|
*
|
|
* GATED OFF BY DEFAULT (maintainer decision E1=B). The public, unauthenticated
|
|
* endpoint is only registered when CLIENT_TELEMETRY_ENABLED=true — otherwise the
|
|
* route does NOT exist at all (no anonymous disk-fill surface, and no unbounded
|
|
* `client_metrics` growth on a self-host deploy without an external pruner). The
|
|
* client is told the same flag via window.CONFIG and skips sending when off.
|
|
*/
|
|
@Module({})
|
|
export class ClientTelemetryModule {
|
|
static register(): DynamicModule {
|
|
// Read process.env directly (not EnvironmentService) so the toggle is
|
|
// resolved at module-registration time, identical to how the metrics
|
|
// subsystem reads METRICS_PORT. Absent/anything-but-"true" => OFF.
|
|
const enabled =
|
|
(process.env.CLIENT_TELEMETRY_ENABLED ?? '').toLowerCase() === 'true';
|
|
|
|
return {
|
|
module: ClientTelemetryModule,
|
|
controllers: enabled ? [VitalsController] : [],
|
|
providers: enabled ? [VitalsService] : [],
|
|
};
|
|
}
|
|
}
|