# Admin Feature Documentation Complete reference for the admin role management, authorization, and cross-tenant oversight capabilities in MotoVaultPro. ## Overview The admin feature provides role-based access control for system administrators to manage: - Admin user accounts (create, revoke, reinstate) - Vehicle catalog data (makes, models, years, trims, engines) - Gas stations and user favorites - Complete audit trail of all admin actions ## Architecture ### Backend Feature Capsule Location: `backend/src/features/admin/` Structure: ``` admin/ ├── api/ │ ├── admin.controller.ts - HTTP handlers for admin management │ ├── admin.routes.ts - Route registration │ ├── admin.validation.ts - Input validation schemas │ ├── catalog.controller.ts - Vehicle catalog handlers │ └── stations.controller.ts - Station oversight handlers ├── domain/ │ ├── admin.types.ts - TypeScript type definitions │ ├── admin.service.ts - Admin user management logic │ ├── vehicle-catalog.service.ts - Catalog CRUD logic │ └── station-oversight.service.ts - Station management logic ├── data/ │ └── admin.repository.ts - Database access layer ├── migrations/ │ ├── 001_create_admin_users.sql - Admin tables and seed │ └── 002_create_platform_change_log.sql - Catalog audit log └── tests/ ├── unit/ - Service and guard tests ├── integration/ - Full API endpoint tests └── fixtures/ - Test data ``` ### Core Plugins - **auth.plugin.ts**: Enhanced with `request.userContext` containing `userId`, `email`, `isAdmin`, `adminRecord` - **admin-guard.plugin.ts**: `fastify.requireAdmin` preHandler that checks `admin_users` table and enforces 403 on non-admins ### Frontend Feature Location: `frontend/src/features/admin/` Structure: ``` admin/ ├── types/admin.types.ts - TypeScript types (mirroring backend) ├── api/admin.api.ts - API client functions ├── hooks/ │ ├── useAdminAccess.ts - Verify admin status │ ├── useAdmins.ts - Admin user management │ ├── useCatalog.ts - Vehicle catalog │ └── useStationOverview.ts - Station management ├── pages/ │ ├── AdminUsersPage.tsx - Desktop user management │ ├── AdminCatalogPage.tsx - Desktop catalog management │ └── AdminStationsPage.tsx - Desktop station management ├── mobile/ │ ├── AdminUsersMobileScreen.tsx - Mobile user management │ ├── AdminCatalogMobileScreen.tsx - Mobile catalog management │ └── AdminStationsMobileScreen.tsx - Mobile station management └── __tests__/ - Component and hook tests ``` ## Database Schema ### admin_users table ```sql CREATE TABLE admin_users ( auth0_sub VARCHAR(255) PRIMARY KEY, email VARCHAR(255) NOT NULL UNIQUE, role VARCHAR(50) NOT NULL DEFAULT 'admin', created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, created_by VARCHAR(255) NOT NULL, revoked_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); ``` **Indexes:** - `auth0_sub` (PRIMARY KEY) - OAuth ID from Auth0 - `email` - For admin lookups by email - `created_at` - For audit trails - `revoked_at` - For active admin queries ### admin_audit_logs table ```sql CREATE TABLE admin_audit_logs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), actor_admin_id VARCHAR(255) NOT NULL, target_admin_id VARCHAR(255), action VARCHAR(100) NOT NULL, resource_type VARCHAR(100), resource_id VARCHAR(255), context JSONB, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); ``` **Actions logged:** - CREATE - New admin or resource created - UPDATE - Resource updated - DELETE - Resource deleted - REVOKE - Admin access revoked - REINSTATE - Admin access restored - VIEW - Data accessed (for sensitive operations) ### platform_change_log table ```sql CREATE TABLE platform_change_log ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), change_type VARCHAR(50) NOT NULL, resource_type VARCHAR(100) NOT NULL, resource_id VARCHAR(255), old_value JSONB, new_value JSONB, changed_by VARCHAR(255) NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); ``` **Resource types:** - makes, models, years, trims, engines - stations - users ## API Reference ### Phase 2: Admin Management #### List all admins ``` GET /api/admin/admins Authorization: Bearer Guard: fastify.requireAdmin Response (200): { "total": 2, "admins": [ { "auth0Sub": "auth0|admin1", "email": "admin@motovaultpro.com", "role": "admin", "createdAt": "2024-01-01T00:00:00Z", "createdBy": "system", "revokedAt": null, "updatedAt": "2024-01-01T00:00:00Z" } ] } ``` #### Create admin ``` POST /api/admin/admins Authorization: Bearer Guard: fastify.requireAdmin Content-Type: application/json Request: { "email": "newadmin@example.com", "role": "admin" } Response (201): { "auth0Sub": "auth0|newadmin", "email": "newadmin@example.com", "role": "admin", "createdAt": "2024-01-15T10:30:00Z", "createdBy": "auth0|existing", "revokedAt": null, "updatedAt": "2024-01-15T10:30:00Z" } Audit log entry: { "actor_admin_id": "auth0|existing", "target_admin_id": "auth0|newadmin", "action": "CREATE", "resource_type": "admin_user", "resource_id": "newadmin@example.com", "context": { "email": "newadmin@example.com", "role": "admin" } } ``` #### Revoke admin ``` PATCH /api/admin/admins/:auth0Sub/revoke Authorization: Bearer Guard: fastify.requireAdmin Response (200): { "auth0Sub": "auth0|toadmin", "email": "admin@motovaultpro.com", "role": "admin", "createdAt": "2024-01-01T00:00:00Z", "createdBy": "system", "revokedAt": "2024-01-15T10:35:00Z", "updatedAt": "2024-01-15T10:35:00Z" } Errors: - 400 Bad Request - Last active admin (cannot revoke) - 403 Forbidden - Not an admin - 404 Not Found - Admin not found ``` #### Reinstate admin ``` PATCH /api/admin/admins/:auth0Sub/reinstate Authorization: Bearer Guard: fastify.requireAdmin Response (200): { "auth0Sub": "auth0|toadmin", "email": "admin@motovaultpro.com", "role": "admin", "createdAt": "2024-01-01T00:00:00Z", "createdBy": "system", "revokedAt": null, "updatedAt": "2024-01-15T10:40:00Z" } ``` #### Audit logs ``` GET /api/admin/audit-logs?limit=100&offset=0 Authorization: Bearer Guard: fastify.requireAdmin Response (200): { "total": 150, "logs": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "actor_admin_id": "auth0|admin1", "target_admin_id": "auth0|admin2", "action": "CREATE", "resource_type": "admin_user", "resource_id": "admin2@motovaultpro.com", "context": { "email": "admin2@motovaultpro.com", "role": "admin" }, "created_at": "2024-01-15T10:30:00Z" } ] } ``` ### Phase 3: Catalog CRUD All catalog endpoints follow RESTful patterns: ``` GET /api/admin/catalog/{resource} - List all GET /api/admin/catalog/{parent}/{parentId}/{resource} - List by parent POST /api/admin/catalog/{resource} - Create PUT /api/admin/catalog/{resource}/:id - Update DELETE /api/admin/catalog/{resource}/:id - Delete ``` **Resources:** makes, models, years, trims, engines **Example: Get all makes** ``` GET /api/admin/catalog/makes Guard: fastify.requireAdmin Response (200): { "total": 42, "makes": [ { "id": "1", "name": "Toyota", "createdAt": "...", "updatedAt": "..." }, { "id": "2", "name": "Honda", "createdAt": "...", "updatedAt": "..." } ] } ``` **Cache invalidation:** All mutations invalidate `platform:*` Redis keys **Audit trail:** All mutations recorded in `platform_change_log` with old and new values ### Phase 4: Station Oversight #### List all stations ``` GET /api/admin/stations?limit=100&offset=0&search=query Guard: fastify.requireAdmin Response (200): { "total": 1250, "stations": [ { "id": "station-1", "placeId": "ChIJxxx", "name": "Shell Station Downtown", "address": "123 Main St", "latitude": 40.7128, "longitude": -74.0060, "createdAt": "...", "deletedAt": null } ] } ``` #### Create station ``` POST /api/admin/stations Guard: fastify.requireAdmin Content-Type: application/json Request: { "placeId": "ChIJxxx", "name": "New Station", "address": "456 Oak Ave", "latitude": 40.7580, "longitude": -73.9855 } Response (201): Station object with all fields Cache invalidation: - mvp:stations:* - All station caches - mvp:stations:search:* - Search result caches ``` #### Delete station (soft or hard) ``` DELETE /api/admin/stations/:stationId?force=false Guard: fastify.requireAdmin Query parameters: - force=false (default) - Soft delete (set deleted_at) - force=true - Hard delete (permanent removal) Response (204 No Content) ``` #### User station management ``` GET /api/admin/users/:userId/stations Guard: fastify.requireAdmin Response (200): { "userId": "auth0|user123", "stations": [...] } ``` ## Authorization Rules ### Admin Guard The `fastify.requireAdmin` preHandler enforces: 1. **JWT validation** - User must be authenticated 2. **Admin check** - User must exist in `admin_users` table 3. **Active status** - User's `revoked_at` must be NULL 4. **Error response** - Returns 403 Forbidden with message "Admin access required" ### Last Admin Protection The system maintains at least one active admin: - Cannot revoke the last active admin (returns 400 Bad Request) - Prevents system lockout - Enforced in `AdminService.revokeAdmin()` ### Audit Trail All admin actions logged: - Actor admin ID (who performed action) - Target admin ID (who was affected, if applicable) - Action type (CREATE, UPDATE, DELETE, REVOKE, REINSTATE) - Resource type and ID - Context (relevant data, like old/new values) - Timestamp ## Security Considerations ### Input Validation All inputs validated using Zod schemas: - Email format and uniqueness - Role enum validation - Required field presence - Type checking ### Parameterized Queries All database operations use parameterized queries: ```typescript // Good - Parameterized const result = await pool.query( 'SELECT * FROM admin_users WHERE email = $1', [email] ); // Bad - SQL concatenation (never done) const result = await pool.query( `SELECT * FROM admin_users WHERE email = '${email}'` ); ``` ### Transaction Support Catalog mutations wrapped in transactions: ```sql BEGIN; -- INSERT/UPDATE/DELETE operations COMMIT; -- or ROLLBACK on error ``` ### Cache Invalidation Prevents stale data: - All catalog mutations invalidate `platform:*` keys - All station mutations invalidate `mvp:stations:*` keys - User station mutations invalidate `mvp:stations:saved:{userId}` ## Deployment ### Prerequisites 1. **Database migrations** - Run all migrations before deploying 2. **Initial admin** - First admin seeded automatically in migration 3. **Auth0 configuration** - Admin user must exist in Auth0 ### Deployment Steps ```bash # 1. Build containers make rebuild # 2. Run migrations (automatically on startup) docker compose exec mvp-backend npm run migrate # 3. Verify admin user created docker compose exec mvp-backend npm run verify-admin # 4. Check backend health curl https://motovaultpro.com/api/health # 5. Verify frontend build curl https://motovaultpro.com ``` ### Rollback If issues occur: ```bash # Revoke problematic admin docker compose exec mvp-backend npm run admin:revoke admin@motovaultpro.com # Reinstate previous admin docker compose exec mvp-backend npm run admin:reinstate # Downgrade admin feature (keep data) docker compose down git checkout previous-version make rebuild make start ``` ## Testing ### Backend Unit Tests Location: `backend/src/features/admin/tests/unit/` ```bash npm test -- features/admin/tests/unit ``` Tests: - Admin guard authorization logic - Admin service business rules - Repository error handling - Last admin protection ### Backend Integration Tests Location: `backend/src/features/admin/tests/integration/` ```bash npm test -- features/admin/tests/integration ``` Tests: - Full API endpoints - Database persistence - Audit logging - Admin guard in request context - CRUD operations - Cache invalidation - Permission enforcement ### Frontend Tests Location: `frontend/src/features/admin/__tests__/` ```bash docker compose exec mvp-frontend npm test ``` Tests: - useAdminAccess hook (loading, admin, non-admin, error states) - Admin page rendering - Admin route guards - Navigation ### E2E Testing 1. **Desktop workflow** - Navigate to `/garage/settings` - Verify "Admin Console" card visible (if admin) - Click "User Management" - Verify admin list loads - Try to create new admin (if permitted) 2. **Mobile workflow** - Open app on mobile viewport (375px) - Navigate to settings - Verify admin section visible (if admin) - Tap "Users" button - Verify admin list loads ## Monitoring & Troubleshooting ### Common Issues **Issue: "Admin access required" (403 Forbidden)** - Verify user in `admin_users` table - Check `revoked_at` is NULL - Verify JWT token valid - Check Auth0 configuration **Issue: Stale catalog data** - Verify Redis is running - Check cache invalidation logs - Manually flush: `redis-cli DEL 'mvp:platform:*'` **Issue: Audit log not recording** - Check `admin_audit_logs` table exists - Verify migrations ran - Check database connection ### Logs View admin-related logs: ```bash # Backend logs make logs | grep -i admin # Check specific action docker compose exec mvp-backend psql -U postgres -d motovaultpro \ -c "SELECT * FROM admin_audit_logs WHERE action = 'CREATE' ORDER BY created_at DESC LIMIT 10;" # Check revoked admins docker compose exec mvp-backend psql -U postgres -d motovaultpro \ -c "SELECT email, revoked_at FROM admin_users WHERE revoked_at IS NOT NULL;" ``` ## Next Steps ### Planned Enhancements 1. **Role-based permissions** - Extend from binary admin to granular roles (admin, catalog_editor, station_manager) 2. **2FA for admins** - Enhanced security with two-factor authentication 3. **Admin impersonation** - Test user issues as admin without changing password 4. **Bulk operations** - Import/export catalog data 5. **Advanced analytics** - Admin activity dashboards ## References - Backend feature: `backend/src/features/admin/README.md` - Frontend feature: `frontend/src/features/admin/` (see individual files) - Architecture: `docs/PLATFORM-SERVICES.md` - Testing: `docs/TESTING.md`