Some checks failed
Deploy to Staging / Build Images (pull_request) Successful in 6m41s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 52s
Deploy to Staging / Verify Staging (pull_request) Failing after 4m7s
Deploy to Staging / Notify Staging Ready (pull_request) Has been skipped
Deploy to Staging / Notify Staging Failure (pull_request) Successful in 9s
Backend test fixtures: - Replace auth0|xxx format with UUID in all test userId values - Update admin tests for new id/userProfileId schema - Add missing deletionRequestedAt/deletionScheduledFor to auth test mocks - Fix admin integration test supertest usage (app.server) Frontend: - AdminUser type: auth0Sub -> id + userProfileId - admin.api.ts: all user management methods use userId (UUID) params - useUsers/useAdmins hooks: auth0Sub -> userId/id in mutations - AdminUsersPage + AdminUsersMobileScreen: user.auth0Sub -> user.id - Remove encodeURIComponent (UUIDs don't need encoding) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Admin Feature
Self-contained feature capsule for MotoVaultPro admin role and access control management.
Architecture
admin/
├── api/
│ ├── admin.controller.ts # HTTP request handlers
│ └── admin.routes.ts # Route registration
├── domain/
│ ├── admin.types.ts # TypeScript interfaces
│ └── admin.service.ts # Business logic
├── data/
│ └── admin.repository.ts # Database access (parameterized queries)
├── migrations/
│ └── 001_create_admin_users.sql # Database schema
└── tests/
├── unit/ # Service/guard tests
└── integration/ # API endpoint tests
Database Schema
admin_users table
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
);
admin_audit_logs table
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
);
Usage
Phase 1: Access Control Foundations
Provides:
AdminRepository- Database access with parameterized queriesAdminService- Business logic for admin operationsadmin-guardplugin - Authorization enforcement (decorator on Fastify)request.userContext- Enhanced withisAdmin,adminRecord
Admin Dashboard Stats
Provides admin dashboard statistics:
GET /api/admin/stats- Get total users and vehicles counts
Response:
{
"totalUsers": 150,
"totalVehicles": 287
}
User Management APIs
Provides:
GET /api/admin/users- List all users with pagination/filtersGET /api/admin/users/:auth0Sub- Get single user detailsGET /api/admin/users/:auth0Sub/vehicles- Get user's vehicles (admin view)PATCH /api/admin/users/:auth0Sub/tier- Update subscription tierPATCH /api/admin/users/:auth0Sub/deactivate- Deactivate userPATCH /api/admin/users/:auth0Sub/reactivate- Reactivate userPATCH /api/admin/users/:auth0Sub/profile- Update user profilePATCH /api/admin/users/:auth0Sub/promote- Promote to adminDELETE /api/admin/users/:auth0Sub- Hard delete user (GDPR)
User Vehicles Endpoint:
GET /api/admin/users/:auth0Sub/vehicles
Returns minimal vehicle data for privacy (Year/Make/Model only):
{
"vehicles": [
{ "year": 2022, "make": "Toyota", "model": "Camry" },
{ "year": 2020, "make": "Honda", "model": "Civic" }
]
}
Admin Management APIs
Provides:
GET /api/admin/admins- List all adminsPOST /api/admin/admins- Add adminPATCH /api/admin/admins/:auth0Sub/revoke- Revoke adminPATCH /api/admin/admins/:auth0Sub/reinstate- Reinstate adminGET /api/admin/audit-logs- View audit trail
Phase 3: Platform Catalog CRUD (COMPLETED)
Provides complete CRUD operations for platform vehicle catalog data:
Makes:
GET /api/admin/catalog/makes- List all makesPOST /api/admin/catalog/makes- Create new makePUT /api/admin/catalog/makes/:makeId- Update makeDELETE /api/admin/catalog/makes/:makeId- Delete make
Models:
GET /api/admin/catalog/makes/:makeId/models- List models for a makePOST /api/admin/catalog/models- Create new modelPUT /api/admin/catalog/models/:modelId- Update modelDELETE /api/admin/catalog/models/:modelId- Delete model
Years:
GET /api/admin/catalog/models/:modelId/years- List years for a modelPOST /api/admin/catalog/years- Create new yearPUT /api/admin/catalog/years/:yearId- Update yearDELETE /api/admin/catalog/years/:yearId- Delete year
Trims:
GET /api/admin/catalog/years/:yearId/trims- List trims for a yearPOST /api/admin/catalog/trims- Create new trimPUT /api/admin/catalog/trims/:trimId- Update trimDELETE /api/admin/catalog/trims/:trimId- Delete trim
Engines:
GET /api/admin/catalog/trims/:trimId/engines- List engines for a trimPOST /api/admin/catalog/engines- Create new enginePUT /api/admin/catalog/engines/:engineId- Update engineDELETE /api/admin/catalog/engines/:engineId- Delete engine
Change Logs:
GET /api/admin/catalog/change-logs?limit=100&offset=0- Retrieve platform catalog change history
Features:
- All mutations wrapped in database transactions
- Automatic cache invalidation (platform:* keys)
- Complete audit trail in platform_change_log table
- Referential integrity validation (prevents orphan deletions)
- requireAdmin guard on all endpoints
Phase 4: Station Oversight
Provides:
GET /api/admin/stations- List all stations globally with pagination and searchPOST /api/admin/stations- Create new stationPUT /api/admin/stations/:stationId- Update station detailsDELETE /api/admin/stations/:stationId- Delete station (soft delete by default, ?force=true for hard delete)GET /api/admin/users/:userId/stations- List user's saved stationsDELETE /api/admin/users/:userId/stations/:stationId- Remove user's saved station (soft delete by default, ?force=true for hard delete)
All station mutations invalidate related Redis caches and log audit trails.
Extending the Feature
Adding a new admin endpoint
- Add handler method to
AdminController - Register route in
admin.routes.tswithapp.requireAdminguard - Add service method if business logic needed
- Add repository method for database operations
Example:
// In admin.routes.ts
fastify.get('/admin/users', {
preHandler: [fastify.requireAdmin]
}, adminController.getUsers.bind(adminController));
// In AdminController
async getUsers(request: FastifyRequest, reply: FastifyReply) {
const actorId = request.userContext?.userId;
const users = await this.adminService.getAllUsers();
return reply.code(200).send(users);
}
// Audit logging
await this.adminService.logAuditAction(actorId, 'VIEW', null, 'users');
Security Considerations
- Admin Guard: All admin endpoints require
preHandler: [fastify.requireAdmin] - Parameterized Queries: All database operations use parameterized queries (no SQL concatenation)
- Audit Logging: All sensitive actions logged with actor, target, action, and context
- Last Admin Protection: Cannot revoke the last active admin
- Soft Deletes: Admins are soft-deleted (revoked_at), never hard-deleted
Testing
Unit tests (no database)
npm test -- features/admin/tests/unit
Tests:
- Admin guard authorization logic
- Admin service business rules
- Repository error handling
Integration tests (with database)
npm test -- features/admin/tests/integration
Tests:
- Full API endpoints
- Database persistence
- Audit logging
- Admin guard in request context
Migrations
Run migrations during container startup:
docker compose exec mvp-backend npm run migrate
Initial seed: First admin user is seeded in migration with:
auth0_sub:system|bootstrapemail:admin@motovaultpro.comrole:admin