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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user