feat(flags): decouple analytics capture from flag evaluation (POSTHOG_CAPTURE_ENABLED)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled

So a non-prod env can hold the (public) project key and evaluate flags - kill
switches, rollout - WITHOUT shipping analytics events to the shared prod
PostHog project. The posthog-node client (which powers both capture and local
flag eval) now initializes whenever the project key is present; capture() is
gated on POSTHOG_CAPTURE_ENABLED (default on; set "false" on dev). Wired into
the api + worker compose blocks.

This lets dev.sase.tr exercise the kill switches end-to-end while keeping the
prod analytics project clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 01:17:00 +03:00
parent ed45021d71
commit 7bdbe3541b
3 changed files with 23 additions and 6 deletions

View File

@@ -6,18 +6,26 @@ import type { PostHog as PostHogClient } from "posthog-node";
export class PostHogService implements OnModuleDestroy {
private readonly logger = new Logger(PostHogService.name);
private client: PostHogClient | null = null;
private enabled = false;
/** Analytics-capture gate — independent of flag eval so a non-prod env can
* evaluate flags without emitting events to the shared prod project. */
private captureEnabled = false;
/** True once a personal API key is configured → local flag evaluation works. */
private flagsEnabled = false;
constructor(private configService: ConfigService) {
const apiKey = this.configService.get<string>("POSTHOG_API_KEY");
// Capture is gated SEPARATELY from flag evaluation: a non-prod env can set the
// project key to evaluate flags (kill switches, rollout) while keeping capture
// off, so it never ships analytics to the shared prod project. Default on.
this.captureEnabled = this.configService.get<string>("POSTHOG_CAPTURE_ENABLED") !== "false";
if (apiKey) {
this.enabled = true;
// Lazily import posthog-node to avoid requiring it when not configured
// The client powers BOTH capture and local feature-flag evaluation.
// Lazily import posthog-node to avoid requiring it when not configured.
this.initClient(apiKey);
} else {
this.logger.warn("PostHog API key not configured — server-side analytics disabled");
this.logger.warn(
"PostHog project key not configured — server-side analytics & flags disabled",
);
}
}
@@ -50,7 +58,7 @@ export class PostHogService implements OnModuleDestroy {
);
} catch (err) {
this.logger.error("Failed to initialize PostHog client", (err as Error).stack);
this.enabled = false;
this.client = null;
}
}
@@ -69,7 +77,7 @@ export class PostHogService implements OnModuleDestroy {
* Uses distinctId from frontend identity when provided; otherwise uses "server".
*/
capture(event: string, properties?: Record<string, unknown>, distinctId = "server"): void {
if (!this.enabled || !this.client) return;
if (!this.captureEnabled || !this.client) return;
try {
this.client.capture({ distinctId, event, properties });