import { Controller, Get, Post, Param, UseGuards, Query, BadRequestException, } from "@nestjs/common"; import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from "@nestjs/swagger"; import { AdminGuard } from "./guards/admin.guard"; import { AuditService, AuditAction } from "@bff/infra/audit/audit.service"; import { UsersService } from "@bff/modules/users/users.service"; @ApiTags("auth-admin") @ApiBearerAuth() @UseGuards(AdminGuard) @Controller("auth/admin") export class AuthAdminController { constructor( private auditService: AuditService, private usersService: UsersService ) {} @Get("audit-logs") @ApiOperation({ summary: "Get audit logs (admin only)" }) @ApiResponse({ status: 200, description: "Audit logs retrieved" }) async getAuditLogs( @Query("page") page: string = "1", @Query("limit") limit: string = "50", @Query("action") action?: AuditAction, @Query("userId") userId?: string ) { const pageNum = parseInt(page, 10); const limitNum = parseInt(limit, 10); const skip = (pageNum - 1) * limitNum; if (Number.isNaN(pageNum) || Number.isNaN(limitNum) || pageNum < 1 || limitNum < 1) { throw new BadRequestException("Invalid pagination parameters"); } const where: { action?: AuditAction; userId?: string } = {}; if (action) where.action = action; if (userId) where.userId = userId; const [logs, total] = await Promise.all([ this.auditService.prismaClient.auditLog.findMany({ where, include: { user: { select: { id: true, email: true, firstName: true, lastName: true, }, }, }, orderBy: { createdAt: "desc" }, skip, take: limitNum, }), this.auditService.prismaClient.auditLog.count({ where }), ]); return { logs, pagination: { page: pageNum, limit: limitNum, total, totalPages: Math.ceil(total / limitNum), }, }; } @Post("unlock-account/:userId") @ApiOperation({ summary: "Unlock user account (admin only)" }) @ApiResponse({ status: 200, description: "Account unlocked" }) async unlockAccount(@Param("userId") userId: string) { const user = await this.usersService.findById(userId); if (!user) { throw new BadRequestException("User not found"); } await this.usersService.update(userId, { failedLoginAttempts: 0, lockedUntil: null, }); await this.auditService.log({ userId, action: AuditAction.ACCOUNT_UNLOCKED, resource: "auth", details: { adminAction: true, email: user.email }, success: true, }); return { message: "Account unlocked successfully" }; } @Get("security-stats") @ApiOperation({ summary: "Get security statistics (admin only)" }) @ApiResponse({ status: 200, description: "Security stats retrieved" }) async getSecurityStats() { const today = new Date(new Date().setHours(0, 0, 0, 0)); const [totalUsers, lockedAccounts, failedLoginsToday, successfulLoginsToday] = await Promise.all([ this.auditService.prismaClient.user.count(), this.auditService.prismaClient.user.count({ where: { lockedUntil: { gt: new Date(), }, }, }), this.auditService.prismaClient.auditLog.count({ where: { action: AuditAction.LOGIN_FAILED, createdAt: { gte: today, }, }, }), this.auditService.prismaClient.auditLog.count({ where: { action: AuditAction.LOGIN_SUCCESS, createdAt: { gte: today, }, }, }), ]); return { totalUsers, lockedAccounts, failedLoginsToday, successfulLoginsToday, securityEventsToday: failedLoginsToday + successfulLoginsToday, }; } }