forked from wrenn/wrenn
Add audit log infrastructure and GET /v1/audit-logs endpoint
Introduces an append-only audit trail for all user and system actions: sandbox lifecycle (create/pause/resume/destroy/auto-pause), snapshots, team rename, API key create/revoke, member add/remove/leave/role_update, and BYOC host add/delete/marked_down/marked_up. - New audit_logs table (migration) with team_id, actor, resource, action, scope (team|admin), status (success|info|warning|error), metadata, and created_at - AuditLogger (internal/audit) with named fire-and-forget methods per event; system actor used for background events (HostMonitor, TTL reaper) - GET /v1/audit-logs: JWT-only, cursor pagination (max 200), multi-value filters for resource_type and action (comma-sep or repeated params); members see team-scoped events only, admins/owners see all - AuthContext extended with APIKeyID + APIKeyName so API key requests record meaningful actor identity - HostMonitor wired with AuditLogger for auto-pause and host marked_down
This commit is contained in:
28
db/migrations/20260324220743_audit_logs.sql
Normal file
28
db/migrations/20260324220743_audit_logs.sql
Normal file
@ -0,0 +1,28 @@
|
||||
-- +goose Up
|
||||
|
||||
CREATE TABLE audit_logs (
|
||||
id TEXT PRIMARY KEY,
|
||||
team_id TEXT NOT NULL,
|
||||
actor_type TEXT NOT NULL, -- 'user', 'api_key', 'system'
|
||||
actor_id TEXT, -- user_id or api_key_id; NULL for system
|
||||
actor_name TEXT, -- display name snapshotted at write time; NULL for system
|
||||
resource_type TEXT NOT NULL, -- 'sandbox', 'snapshot', 'team', 'api_key', 'member', 'host'
|
||||
resource_id TEXT, -- primary ID of the affected resource; NULL when not applicable
|
||||
action TEXT NOT NULL, -- 'create', 'pause', 'resume', 'destroy', 'delete', 'rename',
|
||||
-- 'revoke', 'add', 'remove', 'leave', 'role_update',
|
||||
-- 'marked_down', 'marked_up'
|
||||
scope TEXT NOT NULL, -- 'team' or 'admin'
|
||||
status TEXT NOT NULL, -- 'success', 'info', 'warning', 'error'
|
||||
metadata JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Primary access pattern: team feed sorted newest-first with cursor pagination.
|
||||
CREATE INDEX idx_audit_logs_team_time ON audit_logs (team_id, created_at DESC);
|
||||
|
||||
-- Secondary index: filtered by resource_type and action within a team.
|
||||
CREATE INDEX idx_audit_logs_team_resource ON audit_logs (team_id, resource_type, action, created_at DESC);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
DROP TABLE audit_logs;
|
||||
@ -0,0 +1,14 @@
|
||||
-- name: InsertAuditLog :exec
|
||||
INSERT INTO audit_logs (id, team_id, actor_type, actor_id, actor_name, resource_type, resource_id, action, scope, status, metadata)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11);
|
||||
|
||||
-- name: ListAuditLogs :many
|
||||
SELECT * FROM audit_logs
|
||||
WHERE team_id = $1
|
||||
AND scope = ANY($2::text[])
|
||||
AND (cardinality($3::text[]) = 0 OR resource_type = ANY($3::text[]))
|
||||
AND (cardinality($4::text[]) = 0 OR action = ANY($4::text[]))
|
||||
AND ($5::timestamptz IS NULL OR created_at < $5
|
||||
OR (created_at = $5 AND id < $6))
|
||||
ORDER BY created_at DESC, id DESC
|
||||
LIMIT $7;
|
||||
|
||||
Reference in New Issue
Block a user