feat(telemetry): collections, universal server-fn coverage, beacon, client tracker

- activities indexes + client_events/client_errors/rollups/alerts collections
- global function middleware via createStart; reads skipped on success
- actor stash, denial dedup, control-flow error guards
- api route logging, auth event overrides, beacon endpoint, web vitals
- centralized admin role checks, shared skeletons, award-badges fix
- extracted deriveRunState with tests
This commit is contained in:
2026-08-25 21:33:25 -07:00
parent 15aeb64b19
commit e2863c1f6c
70 changed files with 2545 additions and 275 deletions
+56 -31
View File
@@ -1,12 +1,18 @@
import { createMiddleware } from "@tanstack/react-start";
import { getRequest } from "@tanstack/react-start/server";
import { isRedirect } from "@tanstack/react-router";
import { Logger } from "@/lib/logger";
import { redactValue } from "@/lib/redact";
import { redactArguments } from "@/lib/telemetry/redact-args";
import {
getRequestActor,
markDeniedRecorded,
wasDeniedRecorded,
} from "@/lib/telemetry/request-context.server";
import type { ActivityInput } from "@/lib/pocketbase/services/activities";
const logger = new Logger("Activities");
const recordActivity = (activity: ActivityInput) => {
export const recordActivity = (activity: ActivityInput) => {
import("@/lib/pocketbase/client")
.then(async ({ pbAdmin }) => {
await pbAdmin.authPromise;
@@ -17,19 +23,6 @@ const recordActivity = (activity: ActivityInput) => {
});
};
const MAX_ARGUMENTS_CHARS = 2048;
const redactArguments = (data: unknown): unknown => {
if (data === undefined) return undefined;
const redacted = redactValue(data);
const serialized = JSON.stringify(redacted) ?? "";
if (serialized.length > MAX_ARGUMENTS_CHARS) {
return { truncated: serialized.slice(0, MAX_ARGUMENTS_CHARS) };
}
return redacted;
};
const auditErrorMessage = (
error?: { code?: string; userMessage?: string }
): string => {
@@ -48,11 +41,32 @@ const serverFnName = (request: Request, meta?: ServerFnMeta): string => {
return pathParts[pathParts.length - 1] || "unknown";
};
// Successful reads are skipped (client page views cover usage); read failures
// are always recorded. Naming is uniform across every feature server.ts.
export const READ_FN_NAME = /^(get|list|search|fetch|preview|is[A-Z])/;
const readSampleRate = (): number => {
const raw = Number(process.env.TELEMETRY_READ_SAMPLE);
return Number.isFinite(raw) && raw > 0 ? Math.min(raw, 1) : 0;
};
const shouldRecordSuccess = (name: string): boolean => {
if (!READ_FN_NAME.test(name)) return true;
const sample = readSampleRate();
return sample > 0 && Math.random() < sample;
};
// Thrown Responses/redirects are control flow (session refresh 401s, route
// redirects), not failures.
const isControlFlowError = (error: unknown): boolean =>
error instanceof Response || isRedirect(error);
export const recordDeniedServerFn = (
request: Request,
actor?: Actor,
meta?: ServerFnMeta
) => {
markDeniedRecorded(request);
recordActivity({
name: serverFnName(request, meta),
player: actor?.player_id,
@@ -69,10 +83,15 @@ export const serverFnLoggingMiddleware = createMiddleware({
const request = getRequest();
const name = serverFnName(request, serverFnMeta);
const playerId = (context as any)?.metadata?.player_id as string | undefined;
const userAgent = request.headers.get("user-agent") || undefined;
const safeArgs = redactArguments(data);
// Registered globally, so session middleware runs inside this one; the actor
// is stashed on the Request by getSessionContextCore and read after next().
const resolveActor = (): string | undefined =>
getRequestActor(request)?.playerId ??
((context as any)?.metadata?.player_id as string | undefined);
const startTime = Date.now();
try {
@@ -86,29 +105,35 @@ export const serverFnLoggingMiddleware = createMiddleware({
"success" in envelope &&
(envelope as { success: boolean }).success === false;
recordActivity({
name,
player: playerId,
duration,
success: !failed,
error: failed
? auditErrorMessage(
(envelope as { error?: { code?: string; userMessage?: string } })
.error
)
: undefined,
arguments: safeArgs,
user_agent: userAgent,
});
if (failed || shouldRecordSuccess(name)) {
recordActivity({
name,
player: resolveActor(),
duration,
success: !failed,
error: failed
? auditErrorMessage(
(envelope as { error?: { code?: string; userMessage?: string } })
.error
)
: undefined,
arguments: safeArgs,
user_agent: userAgent,
});
}
return result;
} catch (error) {
if (isControlFlowError(error) || wasDeniedRecorded(request)) {
throw error;
}
const duration = Date.now() - startTime;
const errorMessage = error instanceof Error ? error.message : String(error);
recordActivity({
name,
player: playerId,
player: resolveActor(),
duration,
success: false,
error: errorMessage,