Add admin Audit Log view (task-84)

Original task: Show admins a history of sensitive actions. Forced group
deletions already write to the new `audit_logs` table; admins now have
a UI to browse and filter those entries.

API
- New OpenAPI endpoint GET /admin/audit-logs (admin-only) with optional
  filters: action (exact), from / to (YYYY-MM-DD, inclusive), limit
  (1-200, default 50), offset (default 0).
- New schemas: AuditLogActor, AuditLogEntry, AuditLogList. List
  response includes paginated `entries`, `totalCount`, `nextOffset`,
  and a distinct `actions` list to power the filter dropdown.
- New `audit` tag added to the spec; ran orval codegen so
  api-client-react / api-zod expose useListAuditLogs et al.
- Implemented `artifacts/api-server/src/routes/audit.ts` and registered
  it in routes/index.ts. Validates date format / order, joins users for
  actor info, and orders newest-first.

UI (artifacts/tx-os/src/pages/admin.tsx)
- New "Audit log" entry under the User Management nav group (icon:
  ScrollText). Section deep-link works via #section=audit-log.
- New AuditLogPanel + AuditLogRow components: action chip + target,
  formatted timestamp, actor avatar/name, expandable JSON metadata.
- Filters bar: action dropdown (populated from API's distinct
  actions), from/to date inputs, Apply / Reset, plus a "Load more"
  control that increases the page size (caps at 200, the API limit).
  Read-only by design.
- Added en/ar locale strings for nav.auditLog and the audit subtree.

Verification
- Typecheck: pnpm -w run typecheck (clean).
- API smoke tested via curl: 401 unauthenticated, validation errors
  for bad / inverted dates, returns the seeded `group.force_delete`
  entry once produced.
- End-to-end browser test (testing skill): logged in as admin,
  navigated to /admin#section=audit-log, verified the row, expanded
  metadata, exercised filters (empty state + reset).

No deviations from the task description.

Replit-Task-Id: 5b5bf9b1-6937-43c3-85c9-81f0c19a5e49
This commit is contained in:
riyadhafraa
2026-04-27 11:28:20 +00:00
parent c05ae786fc
commit 1ca5d5ae4b
10 changed files with 799 additions and 3 deletions
+135
View File
@@ -32,6 +32,8 @@ tags:
description: Dashboard stats
- name: storage
description: Object storage upload and serving endpoints
- name: audit
description: Admin audit log
paths:
/healthz:
@@ -1657,6 +1659,64 @@ paths:
schema:
$ref: "#/components/schemas/ErrorResponse"
/admin/audit-logs:
get:
operationId: listAuditLogs
tags: [audit]
summary: List audit log entries (admin)
description: |
Returns recent entries from the audit log, newest first. Admin only.
Filter by action (exact match) and/or a date range (inclusive).
parameters:
- in: query
name: action
required: false
schema:
type: string
description: Exact action name to filter by (e.g. "group.force_delete").
- in: query
name: from
required: false
schema:
type: string
format: date
description: Start date (inclusive, YYYY-MM-DD UTC).
- in: query
name: to
required: false
schema:
type: string
format: date
description: End date (inclusive, YYYY-MM-DD UTC).
- in: query
name: limit
required: false
schema:
type: integer
minimum: 1
maximum: 200
default: 50
- in: query
name: offset
required: false
schema:
type: integer
minimum: 0
default: 0
responses:
"200":
description: Page of audit log entries
content:
application/json:
schema:
$ref: "#/components/schemas/AuditLogList"
"400":
description: Invalid filter parameters
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
components:
schemas:
HealthStatus:
@@ -3032,3 +3092,78 @@ components:
- userId
- username
- count
AuditLogActor:
type: object
properties:
id:
type: integer
username:
type: string
displayNameAr:
type: ["string", "null"]
displayNameEn:
type: ["string", "null"]
avatarUrl:
type: ["string", "null"]
required:
- id
- username
AuditLogEntry:
type: object
properties:
id:
type: integer
action:
type: string
targetType:
type: string
targetId:
type: ["integer", "null"]
metadata:
type: ["object", "null"]
additionalProperties: true
createdAt:
type: string
format: date-time
actor:
oneOf:
- $ref: "#/components/schemas/AuditLogActor"
- type: "null"
required:
- id
- action
- targetType
- targetId
- metadata
- createdAt
- actor
AuditLogList:
type: object
properties:
entries:
type: array
items:
$ref: "#/components/schemas/AuditLogEntry"
totalCount:
type: integer
limit:
type: integer
offset:
type: integer
nextOffset:
type: ["integer", "null"]
actions:
type: array
items:
type: string
description: All distinct action names present in the audit log (for building filter UI).
required:
- entries
- totalCount
- limit
- offset
- nextOffset
- actions