feat(audit-log): add System tab for instance-level auth events#31
Conversation
The /audit-log page now has two tabs: - Connection: the existing per-connection SQL execution / data export view (unchanged), gated on `admin` for the selected connection. - System: instance-level events not tied to a connection (auth.login / auth.logout) with provider + source IP, gated on instance `owner`. Owner gating (rather than connection-admin) keeps system-wide login/IP activity from leaking to an admin of a single connection. - proto: add GetSystemAuditLogEntries RPC; add provider/ip to AuditLogEntry - audit: add listSystemAuditEvents() (events with no connection) - query-service: owner-gated handler; extract shared toAuditLogEntry mapping - ui: tab switcher; System tab shown only to owners - tests: system vs connection separation + system limit - docs: document the two tabs Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
Adds a second “System” tab to the /audit-log page to surface instance-wide authentication events (login/logout) separately from the existing per-connection audit log, with server-side owner gating to avoid exposing login/IP activity to connection-level admins.
Changes:
- Introduces a new
GetSystemAuditLogEntriesRPC and extendsAuditLogEntrywithproviderandip. - Adds server-side support for listing non-connection-scoped audit events and enforces instance-owner access for system audit log retrieval.
- Updates the UI to use a tabbed audit log view (Connection vs System) and adds test coverage + docs for the split.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
tests/audit.test.ts |
Adds tests for system-vs-connection event separation and system log limiting. |
src/pages/AuditLog.tsx |
Implements tabbed audit log UI and renders system audit table (owner-only). |
src/hooks/useQuery.ts |
Adds a React Query hook + cache key for fetching system audit log entries. |
server/services/query-service.ts |
Adds owner-gated getSystemAuditLogEntries handler and shared event→wire mapping. |
server/lib/audit.ts |
Adds auditLogin/auditLogout events and listSystemAuditEvents(limit) retrieval. |
proto/query.proto |
Adds system audit RPC + request/response messages; adds provider/ip to AuditLogEntry. |
docs/features/audit-log.mdx |
Documents the two audit log tabs and their permission boundaries. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Greptile SummaryThis PR adds a System tab to
Confidence Score: 4/5The owner gate is correctly enforced on both the server and client; no regressions to the existing connection audit log path. The two minor notes — the implicit connection-field filter and the disabled-query loading state — are speculative future risks rather than current defects, so they do not affect merge safety. server/lib/audit.ts — the !('connection' in event) predicate in listSystemAuditEvents is the only place worth a second look if new event types are added. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Browser
participant AuditLog.tsx
participant useSystemAuditLogEntries
participant getSystemAuditLogEntries
participant listSystemAuditEvents
Browser->>AuditLog.tsx: render /audit-log
AuditLog.tsx->>AuditLog.tsx: "useOwner() -> isOwner"
alt "isOwner = true"
AuditLog.tsx->>useSystemAuditLogEntries: "enabled=true"
useSystemAuditLogEntries->>getSystemAuditLogEntries: limit 100
getSystemAuditLogEntries->>getSystemAuditLogEntries: isOwner(email) check
getSystemAuditLogEntries->>listSystemAuditEvents: "limit=100"
listSystemAuditEvents-->>getSystemAuditLogEntries: auth events newest-first
getSystemAuditLogEntries-->>useSystemAuditLogEntries: AuditLogEntry[]
useSystemAuditLogEntries-->>AuditLog.tsx: data
AuditLog.tsx->>Browser: System tab with entries
else "isOwner = false"
AuditLog.tsx->>useSystemAuditLogEntries: "enabled=false"
AuditLog.tsx->>Browser: Connection tab only
end
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Browser
participant AuditLog.tsx
participant useSystemAuditLogEntries
participant getSystemAuditLogEntries
participant listSystemAuditEvents
Browser->>AuditLog.tsx: render /audit-log
AuditLog.tsx->>AuditLog.tsx: "useOwner() -> isOwner"
alt "isOwner = true"
AuditLog.tsx->>useSystemAuditLogEntries: "enabled=true"
useSystemAuditLogEntries->>getSystemAuditLogEntries: limit 100
getSystemAuditLogEntries->>getSystemAuditLogEntries: isOwner(email) check
getSystemAuditLogEntries->>listSystemAuditEvents: "limit=100"
listSystemAuditEvents-->>getSystemAuditLogEntries: auth events newest-first
getSystemAuditLogEntries-->>useSystemAuditLogEntries: AuditLogEntry[]
useSystemAuditLogEntries-->>AuditLog.tsx: data
AuditLog.tsx->>Browser: System tab with entries
else "isOwner = false"
AuditLog.tsx->>useSystemAuditLogEntries: "enabled=false"
AuditLog.tsx->>Browser: Connection tab only
end
Reviews (1): Last reviewed commit: "feat(audit-log): add System tab for inst..." | Re-trigger Greptile |
- listSystemAuditEvents: match system events by explicit action (auth.login/auth.logout) instead of the absence of a `connection` field, so a future connection-less event can't leak into the System tab. - EntriesPanel: only show the loading state while actually fetching; a disabled query (non-owner) now falls through to the empty state instead of spinning forever. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
What
Implements two tabs on
/audit-log(task #6):adminfor the selected connection.auth.login/auth.logout), showing actor, status, provider, and source IP. Gated on instance owner.Why owner-gated (not connection-admin)
Auth events are instance-wide. Gating the System tab on per-connection
adminwould let an admin of a single connection see everyone's login/IP activity. Instanceowner(existing concept:isOwner/ sessionisOwner) is the correct boundary — confirmed with @tianzhou.Changes
GetSystemAuditLogEntriesRPC; addprovider/iptoAuditLogEntry.listSystemAuditEvents(limit)— events with noconnection, newest-first, bounded by limit (mirrorslistAuditEvents).getSystemAuditLogEntrieshandler; extracted a sharedtoAuditLogEntrymapping (also adds provider/ip to the existing handler).AuditLog.tsxtab switcher; System tab + query only enabled for owners.docs/features/audit-log.mdx.Verification
pnpm test tests/audit.test.ts— 12 passedpnpm build— passed (pre-existing Vite/Rolldown warnings only)npx eslinton changed frontend files — cleansrc/genis gitignored; CI regenerates fromproto/query.proto.🤖 Generated with Claude Code