feat(console): request-origin awareness across audit log and product analytics#1394
Conversation
There was a problem hiding this comment.
Reviewed webapps/console/components/AuditLog/AuditLog.tsx and webapps/console/pages/api/audit-log.ts in this PR, including origin classification and filter-to-query translation. I did not find a concrete correctness, security, or user-visible regression in the changes currently shown on the PR diff.
a98656e to
742d0e6
Compare
There was a problem hiding this comment.
Reviewed and for origin classification/filter correctness, query composition, and user-visible regressions.
No actionable bugs or security/correctness regressions found in this patch. The new MCP origin mapping and origin filter predicates appear consistent between client and API logic.
There was a problem hiding this comment.
Reviewed the changes in , , , , and .
I focused on origin classification consistency (UI/API/CLI/MCP), server-side origin filtering semantics, and telemetry emission changes for non-HTTP callers (including MCP).
No actionable correctness, security, or user-visible regression issues stood out in this diff.
There was a problem hiding this comment.
Reviewed the changes in webapps/console/components/AuditLog/AuditLog.tsx, webapps/console/lib/schema/index.ts, webapps/console/pages/api/audit-log.ts, webapps/console/lib/server/telemetry.ts, and webapps/console/lib/server/config-objects-service.ts.
I focused on origin classification consistency (UI/API/CLI/MCP), server-side origin filtering semantics, and telemetry emission changes for non-HTTP callers (including MCP).
No actionable correctness, security, or user-visible regression issues stood out in this diff.
| originClauses.push({ authType: { notIn: ["bearer", "mcp"] } }); | ||
| break; | ||
| case "cli": | ||
| originClauses.push({ authType: "bearer", tokenId: { startsWith: CLI_PREFIX } }); |
There was a problem hiding this comment.
Potential origin-classification mismatch: this cli predicate relies only on the tokenId prefix, but originFromAuth() classifies bearer rows as CLI when tokenType === "cli" too. /api/user/keys allows creating tokens with type: "cli" and random ids, so those rows can render as CLI in the table (because token.type is fetched) but be excluded by origin=cli (and included by origin=api) here. Should this query also account for token type so filtering matches display/classification logic?
There was a problem hiding this comment.
Good catch — real inconsistency, not a false positive. Fixed in e518494: the cli/api predicates now also resolve the (normally empty) set of type="cli" tokens whose id lacks the jitsu-cli- prefix and fold them in, so the filter matches how originFromAuth / the table classify those rows. The extra lookup only runs when cli/api is requested and returns 0 rows in normal operation (the console CLI endpoint always sets both prefix and type).
The audit log Origin column collapsed any non-bearer authType to "UI", so MCP-authored config changes showed as "UI" despite already being written with authType="mcp". Add a dedicated MCP origin and a UI/API/CLI/MCP filter. Origin isn't a stored column — the API translates the filter to authType predicates (CLI/API split on the jitsu-cli- tokenId prefix), mirroring resolveOrigin on the client. No migration or backfill needed. Follow-up to JITSU-89. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SeUP6H1fsHsJjo2FLKKqUS
Product analytics never recorded where an action came from, and config-object and connection events were gated on an HTTP `req` — so MCP-authored changes emitted no event at all. - Add `originFromAuth()` in lib/schema as the single source of truth for ui/api/cli/mcp; refactor the audit-log Origin column onto it. - withProductAnalytics derives origin from the acting user and stamps it on every track event, so all events now carry `origin`. - Ungate create/update/delete_object and connection_created/deleted so MCP (and any non-HTTP caller) emit them too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SeUP6H1fsHsJjo2FLKKqUS
The cli/api origin filter split only on the jitsu-cli- tokenId prefix, but the table classifies a bearer row as CLI when UserApiToken.type is "cli" too. A type:"cli" token with a non-prefixed id (creatable via /api/user/keys) would render as CLI yet be excluded by origin=cli. Resolve the (normally empty) set of such tokens and fold them into the cli/api predicates so the filter matches originFromAuth / the displayed origin. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SeUP6H1fsHsJjo2FLKKqUS
e518494 to
407a3d4
Compare
There was a problem hiding this comment.
Reviewed the origin-awareness changes across audit-log UI/API and product analytics (including origin mapping/filter inversion and telemetry call sites). I did not find additional actionable correctness, security, or regression issues beyond the already-discussed origin-filter edge case in existing review comments.
|
🚀 |
Follow-up to #1394. ## What Every audit-log filter is now mirrored into the URL query string, so a filtered `/admin/audit-log` view is **shareable and survives reload**: - event type (`type`), severity (`severity`), origin (`origin`) — comma-separated - date range (`from` / `to`, ISO) - admin workspace filter (`ws` = id, `wsName` = label) ## How Shared `AuditLog` component (used by both `/admin/audit-log` and the workspace-scoped view), `components/AuditLog/AuditLog.tsx`: - **Hydrate once** `router.query` is ready → seed filter state from the URL. - **Mirror back** on every filter change via a shallow `router.replace` (no history spam). - The `hydrated` gate is **state, not a ref**, so the write-back effect can't run with the still-empty initial state and wipe the incoming params before hydration commits. - Non-filter params (e.g. the `[workspaceId]` route segment) are preserved — only the managed keys are rewritten. - The workspace **label** is carried in the URL next to its id so the chip renders a name without an extra lookup (the workspace search is keyed by name, not id). - Pagination (`cursor`) is intentionally **not** persisted. ## Testing - `tsc --noEmit` clean (pre-existing errors are unrelated missing dev-deps: `msw`, `testcontainers`, `@storybook/*`). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01SeUP6H1fsHsJjo2FLKKqUS
Makes the console consistently aware of where a request came from — UI, API, CLI, or MCP — across two surfaces: the audit log and product analytics.
1. Audit log — MCP origin + origin filter
bearerauthTypeto UI, so MCP-authored config changes displayed as "UI" eventhough they were already written with
authType="mcp". Added anORIGIN_MCPtag (robot icon).
API route gains an
originquery param translated toauthTypepredicates(CLI/API split on the
jitsu-cli-tokenIdprefix), composed with theexisting workspace/cursor
ORviawhere.AND. No migration/backfill —historical MCP rows already carry
authType="mcp".2. Product analytics — origin on every event
Product analytics never recorded origin, and config-object/connection events
were gated on an HTTP
req, so MCP-authored changes emitted no event at all.withProductAnalyticsderives origin from the acting user andcreateProductAnalyticsstampsoriginonto everytrack()event.create_object/update_object/delete_objectandconnection_created/connection_deletedso MCP (and any non-HTTP caller)emit them too.
delete_objectand connection events keep aproductTelemetryEnabledguard so self-hosted pays for no extra query.Single source of truth
originFromAuth()(inlib/schema, pure/isomorphic) is the one mapping fromauthType/token →ui|api|cli|mcp. The audit-log Origin column and productanalytics both consume it; the audit-log filter predicates are its documented
inverse.
Testing
tsc --noEmitclean for all changed files (pre-existing errors are unrelatedmissing dev-deps:
msw,testcontainers,@storybook/*).Follow-up to
JITSU-89(workspace filter on admin audit log).🤖 Generated with Claude Code
https://claude.ai/code/session_01SeUP6H1fsHsJjo2FLKKqUS