-
Notifications
You must be signed in to change notification settings - Fork 516
[codex] Add analytics overview filters #1496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
ba947ec
f087ab9
3e99ccc
3f03d94
174f3a4
322c785
a1fff45
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -101,7 +101,7 @@ export const POST = createSmartRouteHandler({ | |
| inserted: yupNumber().defined(), | ||
| }).defined(), | ||
| }), | ||
| async handler({ auth, body }) { | ||
| async handler({ auth, body }, fullReq) { | ||
| if (!auth.tenancy.config.apps.installed["analytics"]?.enabled) { | ||
| throw new KnownErrors.AnalyticsNotEnabled(); | ||
| } | ||
|
|
@@ -134,18 +134,36 @@ export const POST = createSmartRouteHandler({ | |
|
|
||
| const clickhouseClient = getClickhouseAdminClient(); | ||
|
|
||
| const rows = body.events.map((event) => ({ | ||
| event_type: event.event_type, | ||
| event_at: new Date(event.event_at_ms), | ||
| data: stripLoneSurrogates(event.data), | ||
| project_id: projectId, | ||
| branch_id: branchId, | ||
| user_id: userId, | ||
| team_id: null, | ||
| refresh_token_id: refreshTokenId, | ||
| session_replay_id: recentSession?.id ?? null, | ||
| session_replay_segment_id: body.session_replay_segment_id, | ||
| })); | ||
| // Server-side fallback: stamp the request's User-Agent into the event data | ||
| // blob when the client didn't already include one. This lets the analytics | ||
| // overview aggregate device/browser/OS breakdowns even for older clients. | ||
| const headerUserAgent = (() => { | ||
| const raw = fullReq.headers["user-agent"]; | ||
| if (Array.isArray(raw)) return raw[0] ?? null; | ||
| return (raw as string | undefined) ?? null; | ||
| })(); | ||
|
|
||
| const rows = body.events.map((event) => { | ||
| const baseData = (typeof event.data === "object" && !Array.isArray(event.data)) | ||
| ? (event.data as Record<string, unknown>) | ||
| : {}; | ||
|
Comment on lines
+137
to
+149
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
FILE="apps/backend/src/app/api/latest/analytics/events/batch/route.tsx"
# Show the relevant region with line numbers
sed -n '110,190p' "$FILE"
# Also show the surrounding validation/error handling in the handler for context
rg -n "throw new|StatusError|StackAssertionError|BadRequest|analytics event data|Invalid" "$FILE" || true
# Show imports to confirm which error types/utilities are available in this file
sed -n '1,120p' "$FILE"Repository: hexclave/stack-auth Length of output: 8501 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Find where createSmartRouteHandler is implemented
rg -n "function createSmartRouteHandler|createSmartRouteHandler\s*=" -S "apps/backend/src" || true
# If found, display relevant error-handling sections
# (limit output by focusing on common catch blocks)
FILE=$(rg -l "createSmartRouteHandler" apps/backend/src | head -n 20 | tail -n 1)
echo "Last matching file: $FILE"
if [ -f "$FILE" ]; then
rg -n "catch\s*\(|StatusError|StackAssertionError|BadRequest|500" "$FILE" || true
sed -n '1,200p' "$FILE" | cat
fi
# Also scan for this handler file references to StatusError vs unhandled errors
rg -n "StackAssertionError" apps/backend/src/app/api/latest/analytics/events/batch/route.tsx || trueRepository: hexclave/stack-auth Length of output: 6250 🏁 Script executed: #!/bin/bash
set -euo pipefail
ROUTE="apps/backend/src/app/api/latest/analytics/events/batch/route.tsx"
SMART="apps/backend/src/route-handlers/smart-route-handler.tsx"
echo "=== route.tsx (analytics batch) lines 120-190 ==="
sed -n '120,190p' "$ROUTE"
echo
echo "=== smart-route-handler.tsx: show error handling region ==="
# Print around likely catch blocks / error serialization
rg -n "catch\s*\(|StatusError|StackAssertionError|HexclaveAssertionError|serialize|toResponse|response" "$SMART" | head -n 50
# Also dump a window around the first substantial match near line ~216
sed -n '180,320p' "$SMART"Repository: hexclave/stack-auth Length of output: 10436 🏁 Script executed: #!/bin/bash
set -euo pipefail
SMART="apps/backend/src/route-handlers/smart-route-handler.tsx"
echo "=== smart-route-handler.tsx: catchError + handleApiRequest region ==="
# show from around class InternalServerError through catchError and createResponse usage
sed -n '1,120p' "$SMART"
echo
echo "=== smart-route-handler.tsx: handleApiRequest/catch path ==="
# locate handleApiRequest implementation region if present in this file
rg -n "function handleApiRequest|handleApiRequest\s*\(" "$SMART"
# print around last match
MATCH_LINE=$(rg -n "handleApiRequest\s*\(" "$SMART" | head -n 1 | cut -d: -f1 || true)
if [ -n "${MATCH_LINE:-}" ]; then
START=$((MATCH_LINE-60))
END=$((MATCH_LINE+120))
sed -n "${START},${END}p" "$SMART"
fi
echo
echo "=== smart-route-handler.tsx: createResponse / errorToNiceString usage ==="
rg -n "createResponse|errorToNiceString|toResponse|InternalServerError|captureError|catchError" "$SMART"
sed -n '120,220p' "$SMART"Repository: hexclave/stack-auth Length of output: 20725 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the remainder of the analytics batch route for any other assumptions about event.data
ROUTE="apps/backend/src/app/api/latest/analytics/events/batch/route.tsx"
echo "=== Full event mapping usages in ${ROUTE} ==="
rg -n "event\.data|user_agent|mergedData|baseData" "$ROUTE"
echo
echo "=== Event schema section (request validation) ==="
# print around where yupObject defines events/data
rg -n "events:|yupMixed|event_type|event_at_ms|data:" "$ROUTE"
sed -n '60,120p' "$ROUTE"
echo
echo "=== Search other analytics endpoints for similar event.data handling ==="
# locate other analytics event batch routes
rg -n "analytics/events/batch|analytics_internal\.events|event_type:|yupMixed\(\)\.defined\(\)" apps/backend/src/app/api/latest/analytics -S || true
# If there are other routes with similar map/normalization, inspect them
MATCHES=$(rg -l "analytics_internal\.events|yupMixed\(\)\.defined\(\)" apps/backend/src/app/api/latest/analytics -S || true)
echo
echo "Matched files:"
echo "$MATCHES" | sed 's/^/ - /'
if [ -n "${MATCHES:-}" ]; then
for f in $(echo "$MATCHES" | head -n 5); do
echo
echo "---- ${f} (context around normalization) ----"
rg -n "map\\(\\(event\\)|event\\.data|user_agent|mergedData|baseData|stripLoneSurrogates" "$f" || true
sed -n '1,220p' "$f" | head -n 60 || true
done
fiRepository: hexclave/stack-auth Length of output: 7351 Remove
Proposed fix+function isRecord(value: unknown): value is Record<string, unknown> {
+ return value != null && typeof value === "object" && !Array.isArray(value);
+}
+
const headerUserAgent = (() => {
const raw = fullReq.headers["user-agent"];
if (Array.isArray(raw)) return raw[0] ?? null;
- return (raw as string | undefined) ?? null;
+ return typeof raw === "string" ? raw : null;
})();
const rows = body.events.map((event) => {
- const baseData = (typeof event.data === "object" && !Array.isArray(event.data))
- ? (event.data as Record<string, unknown>)
- : {};
+ if (!isRecord(event.data)) {
+ throw new StatusError(StatusError.BadRequest, "Invalid analytics event data payload");
+ }
+ const baseData = event.data;🤖 Prompt for AI Agents |
||
| const existingUa = baseData.user_agent; | ||
| const mergedData = (existingUa == null || existingUa === "") | ||
| ? { ...baseData, user_agent: headerUserAgent } | ||
| : baseData; | ||
| return ({ | ||
| event_type: event.event_type, | ||
| event_at: new Date(event.event_at_ms), | ||
| data: stripLoneSurrogates(mergedData), | ||
| project_id: projectId, | ||
| branch_id: branchId, | ||
| user_id: userId, | ||
| team_id: null, | ||
| refresh_token_id: refreshTokenId, | ||
| session_replay_id: recentSession?.id ?? null, | ||
| session_replay_segment_id: body.session_replay_segment_id, | ||
| }); | ||
| }); | ||
|
|
||
| await clickhouseClient.insert({ | ||
| table: "analytics_internal.events", | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: When
event.dataisnull,typeof null === "object"istruein JavaScript, sobaseDatawill benull. The subsequentbaseData.user_agentaccess will throw aTypeError. Add a null check to the condition.Prompt for AI agents