// Package audit appends rows to the audit_log table. Every mutation // in the API funnels through this so the operator can answer // "who did what when?" from a single SELECT. package audit import ( "context" "encoding/json" "github.com/jackc/pgx/v5/pgxpool" ) type Repo struct { Pool *pgxpool.Pool } func New(pool *pgxpool.Pool) *Repo { return &Repo{Pool: pool} } // Log writes one audit_log row. detail is JSON-encodable (typically a // map[string]any) — empty map means "no payload". If pool is nil // (e.g. dev env without DB), Log silently no-ops so handlers don't // have to guard each call site. func (r *Repo) Log(ctx context.Context, actor, action, subject string, detail any, nodeID string) error { if r == nil || r.Pool == nil { return nil } var detailJSON []byte if detail != nil { var err error detailJSON, err = json.Marshal(detail) if err != nil { return err } } var subjectArg any = subject if subject == "" { subjectArg = nil } var nodeArg any = nodeID if nodeID == "" { nodeArg = nil } _, err := r.Pool.Exec(ctx, `INSERT INTO audit_log (actor, action, subject, detail, node_id) VALUES ($1, $2, $3, $4, $5)`, actor, action, subjectArg, detailJSON, nodeArg) return err }