feat: surface in-memory audit log entries#27
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR adds an in-memory audit event store to pgconsole and surfaces it through a new
Confidence Score: 4/5Safe to merge; the auth gate, config validation, and test coverage are solid. The main area to watch is unbounded memory growth when retention_days is not configured. The feature is well-structured and the admin permission check is in place. The primary concern is that listAuditEvents does a full O(n) filter-and-reverse over the entire event store on every 5-second poll, and without retention_days the store grows indefinitely — including auth events that are never exposed in the UI. Neither issue causes incorrect behavior today, but they could become operational problems on long-running, high-traffic instances. server/lib/audit.ts — the listAuditEvents scan strategy and the absence of a hard entry-count ceiling when retention_days is unset. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Browser as Admin Browser
participant AuditLogPage as AuditLog.tsx
participant Hook as useAuditLogEntries
participant RPC as QueryService.getAuditLogEntries
participant Store as auditEvents[]
participant Config as getAuditRetentionDays
Browser->>AuditLogPage: Navigate to /audit-log
AuditLogPage->>Hook: "enabled = hasAdmin && connections.length > 0"
loop Every 5 seconds
Hook->>RPC: "GetAuditLogEntries(connectionId, limit=100)"
RPC->>RPC: requirePermission(user, connectionId, 'admin')
RPC->>Store: listAuditEvents(connectionId, limit)
Store->>Config: getAuditRetentionDays()
Config-->>Store: retentionDays (or undefined)
Store->>Store: pruneRetainedEvents()
Store->>Store: filter + reverse + slice(0, limit)
Store-->>RPC: AuditEvent[]
RPC-->>Hook: GetAuditLogEntriesResponse
Hook-->>AuditLogPage: entries[]
AuditLogPage-->>Browser: Render table (newest first)
end
Note over Store: auditEvents[] grows on every auditSQL() / auditExport() call
%%{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 as Admin Browser
participant AuditLogPage as AuditLog.tsx
participant Hook as useAuditLogEntries
participant RPC as QueryService.getAuditLogEntries
participant Store as auditEvents[]
participant Config as getAuditRetentionDays
Browser->>AuditLogPage: Navigate to /audit-log
AuditLogPage->>Hook: "enabled = hasAdmin && connections.length > 0"
loop Every 5 seconds
Hook->>RPC: "GetAuditLogEntries(connectionId, limit=100)"
RPC->>RPC: requirePermission(user, connectionId, 'admin')
RPC->>Store: listAuditEvents(connectionId, limit)
Store->>Config: getAuditRetentionDays()
Config-->>Store: retentionDays (or undefined)
Store->>Store: pruneRetainedEvents()
Store->>Store: filter + reverse + slice(0, limit)
Store-->>RPC: AuditEvent[]
RPC-->>Hook: GetAuditLogEntriesResponse
Hook-->>AuditLogPage: entries[]
AuditLogPage-->>Browser: Render table (newest first)
end
Note over Store: auditEvents[] grows on every auditSQL() / auditExport() call
Reviews (1): Last reviewed commit: "feat: surface in-memory audit log entrie..." | Re-trigger Greptile |
There was a problem hiding this comment.
Pull request overview
Adds an in-memory audit-event buffer on the server and surfaces connection-scoped audit entries via a new QueryService RPC, then renders them on the admin-only /audit-log page with polling. This complements the existing “JSON lines to stdout” audit logging by enabling quick in-app inspection of recent activity.
Changes:
- Persist emitted audit events in memory with optional
[audit].retention_dayspruning, plus a server RPC to fetch connection-scoped entries. - Add a React Query hook and UI table rendering for
/audit-logwith newest-first polling. - Update example config and docs; add focused Vitest coverage for config parsing and ordering/limit behavior.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
server/lib/audit.ts |
Stores audit events in memory, prunes by retention, and exposes listAuditEvents. |
server/services/query-service.ts |
Adds getAuditLogEntries handler with admin permission gating and event-to-RPC mapping. |
proto/query.proto |
Introduces GetAuditLogEntries RPC and AuditLogEntry message. |
src/hooks/useQuery.ts |
Adds useAuditLogEntries hook and query key for polling audit entries. |
src/pages/AuditLog.tsx |
Renders audit entries for admins in a table with status/metadata columns. |
server/lib/config.ts |
Adds [audit].retention_days parsing and getters. |
tests/audit.test.ts |
Tests audit config parsing and event store ordering/limits. |
pgconsole.example.toml |
Documents optional [audit] retention_days config. |
docs/features/audit-log.mdx |
Documents the in-app audit log page and retention behavior. |
docs/configuration/config.mdx |
Adds configuration reference for audit retention. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
7463f55 to
ab016f9
Compare
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
ab016f9 to
815edaf
Compare
815edaf to
e3c83a5
Compare
e3c83a5 to
ca126ab
Compare
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Summary
/audit-logentries for admins with newest-first polling[general.audit].retention_daysconfig, example TOML, docs, and focused testsNotes
src/genfiles are ignored in this repo;pnpm genregenerates them fromproto/query.protoTests
pnpm test tests/audit.test.tspnpm build