feat: Centralized audit logging admin interface #10
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Create a dedicated "Admin Logs" section in the admin interface to centralize all audit logging. Currently, an audit logs box exists in the "Vehicle Catalog" section but CRUD operations are not being logged properly.
Requirements
New Admin Section
Log Categories
authvehicleusersystemadminSeverity Levels
info- Normal operationswarning- Potential issueserror- Failed operationsDisplay Columns
Search & Filtering
Export
Retention Policy
Technical Considerations
Backend
Frontend
Acceptance Criteria
Plan: Centralized Audit Logging Admin Interface
Phase: Planning | Agent: Planner | Status: AWAITING_REVIEW
Overview
This plan implements a centralized audit logging system by creating a new unified
audit_logstable with category, severity, and full-text search support. The existingadmin_audit_logsandplatform_change_logtables will remain as-is (no backfill required). A new Admin Logs section will provide unified viewing with search, filters, date range picker, and CSV export. A scheduled cleanup job will enforce 90-day retention.Approach: New Unified Table (Option A-Prime) - cleanest schema, no migration complexity, single source of truth for all new audit events.
Planning Context
Decision Log
audit_logstableuseAuditLogStreamdoes client-side filtering -> fetches all then filters locally -> won't scale with log volume -> server-side WHERE clauses with pagination more efficientactioncolumn withgin_trgm_ops-> enables fast ILIKE queries -> standard pattern for searchDELETE WHERE created_at < NOW() - INTERVAL '90 days'-> simple, reliable, no external dependenciesaudit-logfeature underbackend/src/features/-> matches existing pattern (admin, vehicles, backup) -> self-contained with api/domain/data/migrationsfastify.auditLog.log()-> consistent with CacheService patternRejected Alternatives
Constraints & Assumptions
Known Risks
Invisible Knowledge
Architecture
Data Flow
Why This Structure
Invariants
Milestones
Milestone 1: Database Schema
Files:
backend/src/features/audit-log/migrations/001_create_audit_logs.sqlRequirements:
audit_logstable with columns: id (UUID), category, severity, user_id, action, resource_type, resource_id, details (JSONB), created_atAcceptance Criteria:
Tests:
backend/src/features/audit-log/__tests__/migrations.test.tsMilestone 2: Backend Core (Types, Repository, Service)
Files:
backend/src/features/audit-log/domain/audit-log.types.tsbackend/src/features/audit-log/data/audit-log.repository.tsbackend/src/features/audit-log/domain/audit-log.service.tsbackend/src/features/audit-log/index.tsRequirements:
AuditLogCategory,AuditLogSeverity,AuditLogEntry,AuditLogFiltersAuditLogRepositorywith:create(),search(filters, pagination),exportCsv(filters),cleanup(olderThanDays)AuditLogServicewith:log(category, severity, userId, action, resourceType?, resourceId?, details?)Acceptance Criteria:
service.log()creates entry in databaserepository.search()returns filtered, paginated resultsrepository.cleanup(90)deletes entries older than 90 daysTests:
backend/src/features/audit-log/__tests__/audit-log.service.test.tsMilestone 3: Backend API Routes
Files:
backend/src/features/audit-log/api/audit-log.controller.tsbackend/src/features/audit-log/api/audit-log.routes.tsRequirements:
GET /api/admin/audit-logswith query params: search, category, severity, startDate, endDate, limit, offsetGET /api/admin/audit-logs/exportreturns CSV with Content-Disposition headerrequireAdminpreHandlerAcceptance Criteria:
Tests:
backend/src/features/audit-log/__tests__/audit-log.routes.test.tsMilestone 4: Backend Wiring (Add Logging to Features)
Files:
backend/src/features/auth/api/auth.routes.ts(add login/logout logging)backend/src/features/vehicles/domain/vehicles.service.ts(add CRUD logging)backend/src/features/admin/domain/admin.service.ts(update to use new service)backend/src/features/backup/domain/backup.service.ts(add backup/restore logging)Requirements:
Acceptance Criteria:
Tests:
backend/src/features/audit-log/__tests__/audit-log.integration.test.tsMilestone 5: Frontend AdminLogsPage (Desktop)
Files:
frontend/src/pages/admin/AdminLogsPage.tsxfrontend/src/features/admin/hooks/useAuditLogs.tsfrontend/src/features/admin/api/admin.api.ts(add getAuditLogs, exportAuditLogs)frontend/src/App.tsx(add route)Requirements:
/garage/settings/admin/logsAcceptance Criteria:
Tests:
frontend/src/features/admin/__tests__/AdminLogsPage.test.tsxMilestone 6: Frontend Mobile Screen
Files:
frontend/src/features/admin/mobile/AdminLogsMobileScreen.tsxfrontend/src/App.tsx(add mobile screen case)Requirements:
Acceptance Criteria:
Tests:
frontend/src/features/admin/__tests__/AdminLogsMobileScreen.test.tsxMilestone 7: Cleanup and Retention
Files:
backend/src/features/audit-log/jobs/cleanup.job.tsfrontend/src/pages/admin/AdminCatalogPage.tsx(remove AuditLogPanel)backend/src/features/admin/CLAUDE.md(update)backend/src/features/audit-log/CLAUDE.md(create)backend/src/features/audit-log/README.md(create with architecture)Requirements:
Acceptance Criteria:
Tests:
backend/src/features/audit-log/__tests__/cleanup.job.test.tsMilestone Dependencies
Verdict: AWAITING_REVIEW | Next: QR plan-completeness review
Milestone 1: Database Schema - COMPLETE
Phase: Execution | Agent: Developer | Status: COMPLETE
Deliverables
backend/src/features/audit-log/migrations/001_create_audit_logs.sqlbackend/src/features/audit-log/__tests__/migrations.test.tsSchema Details
Indexes Created
idx_audit_logs_category_created- B-tree for category filteringidx_audit_logs_severity_created- B-tree for severity filteringidx_audit_logs_user_created- B-tree for user filteringidx_audit_logs_created- B-tree for date orderingidx_audit_logs_action_gin- GIN trigram for text searchTests
Next: Milestone 2 - Backend Core (Types, Repository, Service)
Milestone 2: Backend Core - COMPLETE
Phase: Execution | Agent: Developer | Status: COMPLETE
Deliverables
backend/src/features/audit-log/domain/audit-log.types.tsbackend/src/features/audit-log/data/audit-log.repository.tsbackend/src/features/audit-log/domain/audit-log.service.tsbackend/src/features/audit-log/index.tsbackend/src/features/audit-log/__tests__/audit-log.service.test.tsAPI Surface
Types:
AuditLogCategory: 'auth' | 'vehicle' | 'user' | 'system' | 'admin'AuditLogSeverity: 'info' | 'warning' | 'error'AuditLogEntry: Full log entry with all fieldsAuditLogFilters: Search, category, severity, date rangeAuditLogSearchResult: Paginated results with totalAuditLogService Methods:
log(category, severity, userId, action, resourceType?, resourceId?, details?)- Create log entryinfo/warning/error()- Convenience methodssearch(filters, pagination)- Search with filtersgetForExport(filters)- Get all matching for CSVcleanup(days)- Delete old entriesAuditLogRepository Methods:
create()- Insert log entrysearch()- Filtered paginated querygetForExport()- All matching entries (max 10k)cleanup()- DELETE older than N daysTests
Next: Milestone 3 - Backend API Routes
Milestone 3: Backend API Routes - COMPLETE
Phase: Execution | Agent: Developer | Status: COMPLETE
Deliverables
backend/src/features/audit-log/api/audit-log.controller.tsbackend/src/features/audit-log/api/audit-log.routes.tsbackend/src/app.tsbackend/src/features/audit-log/__tests__/audit-log.routes.test.tsAPI Endpoints
GET /api/admin/audit-logs
{ logs: AuditLogEntry[], total: number, limit: number, offset: number }GET /api/admin/audit-logs/export
Features
Tests
Next: Milestone 4 - Backend Wiring (Add logging to features)
Milestone 4: Backend Wiring - COMPLETE
Phase: Execution | Agent: Developer | Status: COMPLETE
Deliverables
backend/src/features/audit-log/audit-log.instance.tsbackend/src/features/audit-log/index.tsbackend/src/features/auth/api/auth.controller.tsbackend/src/features/vehicles/domain/vehicles.service.tsbackend/src/features/admin/domain/admin.service.tsbackend/src/features/backup/api/backup.controller.tsbackend/src/features/audit-log/__tests__/audit-log.integration.test.tsbackend/src/features/audit-log/__tests__/*.test.tsAudit Logging Wiring
Auth (category='auth'):
Vehicle (category='vehicle'):
Admin (category='admin'):
System/Backup (category='system'):
Implementation Pattern
All audit logging uses non-blocking
.catch()pattern to prevent audit failures from breaking main operations:Quality Checks
Next: Milestone 5 - Frontend AdminLogsPage (Desktop)
Milestone 5: Frontend AdminLogsPage (Desktop) - COMPLETE
Phase: Execution | Agent: Developer | Status: COMPLETE
Deliverables
frontend/src/features/admin/types/admin.types.tsfrontend/src/features/admin/api/admin.api.tsfrontend/src/features/admin/hooks/useAuditLogs.tsfrontend/src/pages/admin/AdminLogsPage.tsxfrontend/src/App.tsxFeatures
Types Added:
AuditLogCategory: 'auth' | 'vehicle' | 'user' | 'system' | 'admin'AuditLogSeverity: 'info' | 'warning' | 'error'UnifiedAuditLog: Full log entry interfaceUnifiedAuditLogsResponse: Paginated responseAuditLogFilters: Search and filter paramsAPI Methods:
adminApi.unifiedAuditLogs.list(filters): Fetch paginated logsadminApi.unifiedAuditLogs.export(filters): Export as CSVDesktop Page Features:
Route:
/garage/settings/admin/logsMilestone 6: Frontend Mobile Screen - COMPLETE
Phase: Execution | Agent: Developer | Status: COMPLETE
Deliverables
frontend/src/features/admin/mobile/AdminLogsMobileScreen.tsxfrontend/src/core/store/navigation.tsfrontend/src/App.tsxMobile Features
Navigation: Accessible via hamburger menu as "AdminLogs" screen
Quality Checks
Next: Milestone 7 - Cleanup and Retention
Milestone 7: Cleanup and Retention - COMPLETE
Phase: Execution | Agent: Developer | Status: COMPLETE
Deliverables
backend/src/features/audit-log/jobs/cleanup.job.tsbackend/src/core/scheduler/index.tsfrontend/src/pages/admin/AdminCatalogPage.tsxbackend/src/features/audit-log/CLAUDE.mdbackend/src/features/audit-log/README.mdbackend/src/features/CLAUDE.mdCleanup Job
Schedule: Daily at 3 AM
Retention: 90 days
Implementation: Uses existing scheduler infrastructure with node-cron
Documentation
CLAUDE.md includes:
README.md includes:
Quality Checks
Issue #10 Implementation Complete
All 7 milestones have been completed:
Summary of Deliverables
Backend:
audit_logstable with proper indexesAuditLogServiceandAuditLogRepositoryFrontend:
Ready for testing and PR creation.
Implementation Plan: Login/Logout Audit Logging
Analysis Summary
After comprehensive analysis using codebase exploration, problem analysis, and decision critique:
Finding: The
GET /api/auth/user-statusendpoint is only called from callback pages (CallbackPage.tsx:13,CallbackMobileScreen.tsx:14) after Auth0 redirect - NOT on page refresh. This makes it safe for login tracking with zero frontend changes.Decision: Modify
getUserStatus()for login tracking + create new/auth/track-logoutendpoint for logout tracking.Implementation Plan
1. Login Tracking (Backend Only)
File:
backend/src/features/auth/api/auth.controller.tsAdd audit logging to
getUserStatus():2. Logout Tracking (Backend + Frontend)
Backend:
POST /auth/track-logouttoauth.routes.tstrackLogout()handler toauth.controller.tsFrontend:
Auth0Provider.tsxlogout handler to call/api/auth/track-logoutbefore Auth0 logout (fire-and-forget)Files to Modify
backend/src/features/auth/api/auth.controller.tsgetUserStatus(), addtrackLogout()backend/src/features/auth/api/auth.routes.ts/auth/track-logoutroutefrontend/src/core/auth/Auth0Provider.tsxbackend/src/features/auth/__tests__/auth.integration.test.tsVerification Findings
Starting implementation now.
Implementation Complete: Login/Logout Audit Logging
Changes Made
Backend (
fbde51b):getUserStatus()inauth.controller.tsPOST /auth/track-logoutendpoint for logout trackingauditLogService.info('auth', ...)patternFrontend:
useLogouthook (frontend/src/core/auth/useLogout.ts) that wraps Auth0 logout with audit trackingSettingsPage.tsx(desktop)Layout.tsx(sidebar)MobileSettingsScreen.tsx(mobile)useDeletion.ts(account deletion flow)How It Works
Login tracking:
/api/auth/user-statusafter Auth0 callback'User login'with IP addressLogout tracking:
useLogouthook calls/api/auth/track-logout(fire-and-forget)'User logout'with IP addressVerification
Ready for testing on staging.
Fix: Display user email instead of Auth0 UID in audit logs
The "User" column in the Admin Logs display was showing the raw Auth0 UID (e.g.,
auth0|123456...) instead of a human-readable email address.Changes Made
Backend:
userEmailfield toAuditLogEntrytypeLEFT JOINwithuser_profilestable to fetch user email based on Auth0 subFrontend:
userEmailtoUnifiedAuditLogtypeAdminLogsPage.tsxto display:AdminLogsMobileScreen.tsxCommit
911b7c0- fix: Display user email instead of Auth0 UID in audit logs (refs #10)