analytics: make each weekly funnel stage separately measurable (FRE-366)
Gap 1 — click and served share one label: trackDownload now takes a
stage ("click" | "served") carried on a new VisitorAnalytics.stage
column; the Nook door and the tRPC proxy record clicks, the DMG file
route records served. summariseDownloads() and the /analytics downloads
block are untouched — the stage is a separate column, so the path
grouping cannot change.
Gap 2 — checkout start records nothing: the checkout route records a
created Stripe session through the shared trackRequest writer (same
referrer, campaign, country dimensions as every door) under its own
path, and STAGES.checkout reads it instead of the /checkout page proxy.
Gap 3 — loopback addresses, no host dimension: every writer now
resolves the client address through resolveClientIp, which trusts
x-forwarded-for only when the direct peer is the loopback production
proxy (trust boundary stated in request-client.ts), and records the
host on each row through the same site-context extractor routing uses.
stage and host reach the table through VISITOR_ANALYTICS_ADDED_COLUMNS;
the weekly job applies the same bootstrap before reading so it renders
seven stages against a database no deploy has migrated yet.
The weekly read splits downloadClick/dmgServed, scopes visits to
nook.freno.me, and computes its caveats from the window: pre-instrumentation
rows are stated and excluded, and the loopback warning now fires only
when rows recorded since the instrumentation still carry it.
This commit is contained in:
@@ -64,6 +64,10 @@ export interface AnalyticsEntry {
|
||||
ttfb?: number | null;
|
||||
domLoad?: number | null;
|
||||
loadComplete?: number | null;
|
||||
/** Funnel stage for download rows ("click" | "served"); null elsewhere. */
|
||||
stage?: string | null;
|
||||
/** Host the client addressed, e.g. "nook.freno.me" — the site dimension. */
|
||||
host?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,7 +128,9 @@ async function flushAnalyticsBuffer(): Promise<void> {
|
||||
entry.inp || null,
|
||||
entry.ttfb || null,
|
||||
entry.domLoad || null,
|
||||
entry.loadComplete || null
|
||||
entry.loadComplete || null,
|
||||
entry.stage || null,
|
||||
entry.host || null
|
||||
]
|
||||
});
|
||||
}
|
||||
@@ -258,6 +264,8 @@ export async function queryAnalytics(
|
||||
utm_medium: row.utm_medium as string | null,
|
||||
utm_campaign: row.utm_campaign as string | null,
|
||||
duration_ms: row.duration_ms as number | null,
|
||||
stage: row.stage as string | null,
|
||||
host: row.host as string | null,
|
||||
created_at: row.created_at as string
|
||||
}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user