feat(audit): P3-08 — audit log UI with filters

This commit is contained in:
2026-05-05 07:49:25 +01:00
parent cb3260b89c
commit 3f36bcd0b0
9 changed files with 617 additions and 3 deletions
+32
View File
@@ -38,6 +38,38 @@ func (s *Store) GetUserByID(ctx context.Context, id string) (*User, error) {
return scanUser(row)
}
// ListUsers returns every user, sorted by username. Used by surfaces
// that need to render a user-id → username map (audit log filter,
// "ack'd by" projections).
func (s *Store) ListUsers(ctx context.Context) ([]User, error) {
rows, err := s.db.QueryContext(ctx,
`SELECT id, username, password_hash, role, created_at, last_login_at
FROM users ORDER BY username`)
if err != nil {
return nil, fmt.Errorf("store: list users: %w", err)
}
defer func() { _ = rows.Close() }()
var out []User
for rows.Next() {
var u User
var role string
var lastLogin sql.NullString
var created string
if err := rows.Scan(&u.ID, &u.Username, &u.PasswordHash, &role, &created, &lastLogin); err != nil {
return nil, fmt.Errorf("store: scan user row: %w", err)
}
u.Role = Role(role)
t, _ := time.Parse(time.RFC3339Nano, created)
u.CreatedAt = t
if lastLogin.Valid {
t, _ := time.Parse(time.RFC3339Nano, lastLogin.String)
u.LastLoginAt = &t
}
out = append(out, u)
}
return out, rows.Err()
}
// CountUsers returns the total number of user rows. The first-run
// bootstrap uses this to detect a fresh install.
func (s *Store) CountUsers(ctx context.Context) (int, error) {