/** * @ai-summary Admin feature routes * @ai-context Registers admin API endpoints with proper guards */ import { FastifyPluginAsync } from 'fastify'; import { AdminController } from './admin.controller'; import { UsersController } from './users.controller'; import { CreateAdminInput, AdminIdInput, BulkCreateAdminInput, BulkRevokeAdminInput, BulkReinstateAdminInput, BulkDeleteCatalogInput, CatalogEntity } from './admin.validation'; import { ListUsersQueryInput, UserIdInput, UpdateTierInput, DeactivateUserInput, UpdateProfileInput, PromoteToAdminInput, } from './users.validation'; import { CatalogController } from './catalog.controller'; import { VehicleCatalogService } from '../domain/vehicle-catalog.service'; import { CatalogImportService } from '../domain/catalog-import.service'; import { PlatformCacheService } from '../../platform/domain/platform-cache.service'; import { cacheService } from '../../../core/config/redis'; import { pool } from '../../../core/config/database'; import { CommunityStationsController } from '../../stations/api/community-stations.controller'; import { registerBackupRoutes } from '../../backup/api/backup.routes'; export const adminRoutes: FastifyPluginAsync = async (fastify) => { const adminController = new AdminController(); const usersController = new UsersController(); // Initialize community stations dependencies const communityStationsController = new CommunityStationsController(); // Initialize catalog dependencies const platformCacheService = new PlatformCacheService(cacheService); const catalogService = new VehicleCatalogService(pool, platformCacheService); const catalogImportService = new CatalogImportService(pool, platformCacheService); const catalogController = new CatalogController(catalogService); catalogController.setImportService(catalogImportService); // Admin access verification (used by frontend auth checks) fastify.get('/admin/verify', { preHandler: [fastify.authenticate] // Requires JWT, does NOT require admin role }, adminController.verifyAccess.bind(adminController)); // Phase 2: Admin management endpoints // GET /api/admin/admins - List all admin users fastify.get('/admin/admins', { preHandler: [fastify.requireAdmin], handler: adminController.listAdmins.bind(adminController) }); // POST /api/admin/admins - Create new admin fastify.post<{ Body: CreateAdminInput }>('/admin/admins', { preHandler: [fastify.requireAdmin], handler: adminController.createAdmin.bind(adminController) }); // PATCH /api/admin/admins/:id/revoke - Revoke admin access fastify.patch<{ Params: AdminIdInput }>('/admin/admins/:id/revoke', { preHandler: [fastify.requireAdmin], handler: adminController.revokeAdmin.bind(adminController) }); // PATCH /api/admin/admins/:id/reinstate - Restore revoked admin fastify.patch<{ Params: AdminIdInput }>('/admin/admins/:id/reinstate', { preHandler: [fastify.requireAdmin], handler: adminController.reinstateAdmin.bind(adminController) }); // NOTE: GET /api/admin/audit-logs moved to audit-log feature (centralized audit logging) // POST /api/admin/admins/bulk - Create multiple admins fastify.post<{ Body: BulkCreateAdminInput }>('/admin/admins/bulk', { preHandler: [fastify.requireAdmin], handler: adminController.bulkCreateAdmins.bind(adminController) }); // PATCH /api/admin/admins/bulk-revoke - Revoke multiple admins fastify.patch<{ Body: BulkRevokeAdminInput }>('/admin/admins/bulk-revoke', { preHandler: [fastify.requireAdmin], handler: adminController.bulkRevokeAdmins.bind(adminController) }); // PATCH /api/admin/admins/bulk-reinstate - Reinstate multiple admins fastify.patch<{ Body: BulkReinstateAdminInput }>('/admin/admins/bulk-reinstate', { preHandler: [fastify.requireAdmin], handler: adminController.bulkReinstateAdmins.bind(adminController) }); // ============================================ // Admin Stats endpoint (dashboard widgets) // ============================================ // GET /api/admin/stats - Get admin dashboard stats (total users, total vehicles) fastify.get('/admin/stats', { preHandler: [fastify.requireAdmin], handler: usersController.getAdminStats.bind(usersController) }); // ============================================ // User Management endpoints (subscription tiers, deactivation) // ============================================ // GET /api/admin/users - List all users with pagination and filters fastify.get<{ Querystring: ListUsersQueryInput }>('/admin/users', { preHandler: [fastify.requireAdmin], handler: usersController.listUsers.bind(usersController) }); // GET /api/admin/users/:userId - Get single user details fastify.get<{ Params: UserIdInput }>('/admin/users/:userId', { preHandler: [fastify.requireAdmin], handler: usersController.getUser.bind(usersController) }); // GET /api/admin/users/:userId/vehicles - Get user's vehicles (admin view) fastify.get<{ Params: UserIdInput }>('/admin/users/:userId/vehicles', { preHandler: [fastify.requireAdmin], handler: usersController.getUserVehicles.bind(usersController) }); // PATCH /api/admin/users/:userId/tier - Update subscription tier fastify.patch<{ Params: UserIdInput; Body: UpdateTierInput }>('/admin/users/:userId/tier', { preHandler: [fastify.requireAdmin], handler: usersController.updateTier.bind(usersController) }); // PATCH /api/admin/users/:userId/deactivate - Soft delete user fastify.patch<{ Params: UserIdInput; Body: DeactivateUserInput }>('/admin/users/:userId/deactivate', { preHandler: [fastify.requireAdmin], handler: usersController.deactivateUser.bind(usersController) }); // PATCH /api/admin/users/:userId/reactivate - Restore deactivated user fastify.patch<{ Params: UserIdInput }>('/admin/users/:userId/reactivate', { preHandler: [fastify.requireAdmin], handler: usersController.reactivateUser.bind(usersController) }); // PATCH /api/admin/users/:userId/profile - Update user email/displayName fastify.patch<{ Params: UserIdInput; Body: UpdateProfileInput }>('/admin/users/:userId/profile', { preHandler: [fastify.requireAdmin], handler: usersController.updateProfile.bind(usersController) }); // PATCH /api/admin/users/:userId/promote - Promote user to admin fastify.patch<{ Params: UserIdInput; Body: PromoteToAdminInput }>('/admin/users/:userId/promote', { preHandler: [fastify.requireAdmin], handler: usersController.promoteToAdmin.bind(usersController) }); // DELETE /api/admin/users/:userId - Hard delete user (permanent) fastify.delete<{ Params: UserIdInput }>('/admin/users/:userId', { preHandler: [fastify.requireAdmin], handler: usersController.hardDeleteUser.bind(usersController) }); // Phase 3: Catalog CRUD endpoints // Makes endpoints fastify.get('/admin/catalog/makes', { preHandler: [fastify.requireAdmin], handler: catalogController.getMakes.bind(catalogController) }); fastify.post('/admin/catalog/makes', { preHandler: [fastify.requireAdmin], handler: catalogController.createMake.bind(catalogController) }); fastify.put('/admin/catalog/makes/:makeId', { preHandler: [fastify.requireAdmin], handler: catalogController.updateMake.bind(catalogController) }); fastify.delete('/admin/catalog/makes/:makeId', { preHandler: [fastify.requireAdmin], handler: catalogController.deleteMake.bind(catalogController) }); // Models endpoints fastify.get('/admin/catalog/makes/:makeId/models', { preHandler: [fastify.requireAdmin], handler: catalogController.getModels.bind(catalogController) }); fastify.post('/admin/catalog/models', { preHandler: [fastify.requireAdmin], handler: catalogController.createModel.bind(catalogController) }); fastify.put('/admin/catalog/models/:modelId', { preHandler: [fastify.requireAdmin], handler: catalogController.updateModel.bind(catalogController) }); fastify.delete('/admin/catalog/models/:modelId', { preHandler: [fastify.requireAdmin], handler: catalogController.deleteModel.bind(catalogController) }); // Years endpoints fastify.get('/admin/catalog/models/:modelId/years', { preHandler: [fastify.requireAdmin], handler: catalogController.getYears.bind(catalogController) }); fastify.post('/admin/catalog/years', { preHandler: [fastify.requireAdmin], handler: catalogController.createYear.bind(catalogController) }); fastify.put('/admin/catalog/years/:yearId', { preHandler: [fastify.requireAdmin], handler: catalogController.updateYear.bind(catalogController) }); fastify.delete('/admin/catalog/years/:yearId', { preHandler: [fastify.requireAdmin], handler: catalogController.deleteYear.bind(catalogController) }); // Trims endpoints fastify.get('/admin/catalog/years/:yearId/trims', { preHandler: [fastify.requireAdmin], handler: catalogController.getTrims.bind(catalogController) }); fastify.post('/admin/catalog/trims', { preHandler: [fastify.requireAdmin], handler: catalogController.createTrim.bind(catalogController) }); fastify.put('/admin/catalog/trims/:trimId', { preHandler: [fastify.requireAdmin], handler: catalogController.updateTrim.bind(catalogController) }); fastify.delete('/admin/catalog/trims/:trimId', { preHandler: [fastify.requireAdmin], handler: catalogController.deleteTrim.bind(catalogController) }); // Engines endpoints fastify.get('/admin/catalog/trims/:trimId/engines', { preHandler: [fastify.requireAdmin], handler: catalogController.getEngines.bind(catalogController) }); fastify.post('/admin/catalog/engines', { preHandler: [fastify.requireAdmin], handler: catalogController.createEngine.bind(catalogController) }); fastify.put('/admin/catalog/engines/:engineId', { preHandler: [fastify.requireAdmin], handler: catalogController.updateEngine.bind(catalogController) }); fastify.delete('/admin/catalog/engines/:engineId', { preHandler: [fastify.requireAdmin], handler: catalogController.deleteEngine.bind(catalogController) }); // Change logs endpoint fastify.get('/admin/catalog/change-logs', { preHandler: [fastify.requireAdmin], handler: catalogController.getChangeLogs.bind(catalogController) }); // Search endpoint - full-text search across vehicle_options fastify.get('/admin/catalog/search', { preHandler: [fastify.requireAdmin], handler: catalogController.searchCatalog.bind(catalogController) }); // Cascade delete endpoints - delete entity and all its children fastify.delete('/admin/catalog/makes/:makeId/cascade', { preHandler: [fastify.requireAdmin], handler: catalogController.deleteMakeCascade.bind(catalogController) }); fastify.delete('/admin/catalog/models/:modelId/cascade', { preHandler: [fastify.requireAdmin], handler: catalogController.deleteModelCascade.bind(catalogController) }); fastify.delete('/admin/catalog/years/:yearId/cascade', { preHandler: [fastify.requireAdmin], handler: catalogController.deleteYearCascade.bind(catalogController) }); fastify.delete('/admin/catalog/trims/:trimId/cascade', { preHandler: [fastify.requireAdmin], handler: catalogController.deleteTrimCascade.bind(catalogController) }); // Import/Export endpoints fastify.post('/admin/catalog/import/preview', { preHandler: [fastify.requireAdmin], handler: catalogController.importPreview.bind(catalogController) }); fastify.post('/admin/catalog/import/apply', { preHandler: [fastify.requireAdmin], handler: catalogController.importApply.bind(catalogController) }); fastify.get('/admin/catalog/export', { preHandler: [fastify.requireAdmin], handler: catalogController.exportCatalog.bind(catalogController) }); // Bulk delete endpoint fastify.delete<{ Params: { entity: CatalogEntity }; Body: BulkDeleteCatalogInput }>('/admin/catalog/:entity/bulk-delete', { preHandler: [fastify.requireAdmin], handler: catalogController.bulkDeleteCatalogEntity.bind(catalogController) }); // Community gas station submission oversight // GET /api/admin/community-stations - List all submissions with filters fastify.get('/admin/community-stations', { preHandler: [fastify.requireAdmin], handler: communityStationsController.listAllSubmissions.bind(communityStationsController) }); // GET /api/admin/community-stations/pending - Get pending review queue fastify.get('/admin/community-stations/pending', { preHandler: [fastify.requireAdmin], handler: communityStationsController.getPendingQueue.bind(communityStationsController) }); // PATCH /api/admin/community-stations/:id/review - Approve or reject submission fastify.patch('/admin/community-stations/:id/review', { preHandler: [fastify.requireAdmin], handler: communityStationsController.reviewStation.bind(communityStationsController) }); // ============================================ // Backup & Restore endpoints // ============================================ await registerBackupRoutes(fastify, { pool }); };