import { Controller, Get, Post, Put, Delete, Param, Body, Query, UseInterceptors, Inject, } from '@nestjs/common'; import { CacheInterceptor, CacheKey, CacheTTL, CACHE_MANAGER, } from '@nestjs/cache-manager'; import * as cacheManager from 'cache-manager'; import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger'; import { Roles } from '../../common/decorators'; import { PrismaService } from '../../database/prisma.service'; import { PaginationDto } from '../../common/dto/pagination.dto'; import { ApiResponse, createSuccessResponse, createPaginatedResponse, PaginatedData, } from '../../common/types/api-response.type'; import { plainToInstance } from 'class-transformer'; import { UserResponseDto } from '../users/dto/user.dto'; import { PermissionResponseDto, RolePermissionResponseDto, RoleResponseDto, UserRoleResponseDto, } from './dto/admin.dto'; @ApiTags('Admin') @ApiBearerAuth() @Controller('admin') @Roles('admin') export class AdminController { constructor( private readonly prisma: PrismaService, @Inject(CACHE_MANAGER) private cacheManager: cacheManager.Cache, ) {} // ================== Users Management ================== @Get('users') @ApiOperation({ summary: 'Get all users (admin)' }) async getAllUsers( @Query() pagination: PaginationDto, ): Promise>> { const { skip, take, orderBy } = pagination; const [users, total] = await Promise.all([ this.prisma.user.findMany({ skip, take, orderBy, include: { roles: { include: { role: true, }, }, }, }), this.prisma.user.count(), ]); const dtos = plainToInstance( UserResponseDto, users, ) as unknown as UserResponseDto[]; return createPaginatedResponse( dtos, total, pagination.page || 1, pagination.limit || 10, ); } @Put('users/:id/toggle-active') @ApiOperation({ summary: 'Toggle user active status' }) async toggleUserActive( @Param('id') id: string, ): Promise> { const user = await this.prisma.user.findUnique({ where: { id } }); const updated = await this.prisma.user.update({ where: { id }, data: { isActive: !user?.isActive }, }); return createSuccessResponse( plainToInstance(UserResponseDto, updated), 'User status updated', ); } @Post('users/:userId/roles/:roleId') @ApiOperation({ summary: 'Assign role to user' }) async assignRole( @Param('userId') userId: string, @Param('roleId') roleId: string, ): Promise> { const userRole = await this.prisma.userRole.create({ data: { userId, roleId }, }); return createSuccessResponse( plainToInstance(UserRoleResponseDto, userRole), 'Role assigned to user', ); } @Delete('users/:userId/roles/:roleId') @ApiOperation({ summary: 'Remove role from user' }) async removeRole( @Param('userId') userId: string, @Param('roleId') roleId: string, ): Promise> { await this.prisma.userRole.deleteMany({ where: { userId, roleId }, }); return createSuccessResponse(null, 'Role removed from user'); } // ================== Roles Management ================== @Get('roles') @UseInterceptors(CacheInterceptor) @CacheKey('roles_list') @CacheTTL(60 * 1000) @ApiOperation({ summary: 'Get all roles' }) async getAllRoles(): Promise> { const roles = await this.prisma.role.findMany({ include: { permissions: { include: { permission: true, }, }, _count: { select: { users: true }, }, }, }); // Transform Prisma structure to DTO structure const transformedRoles = roles.map((role) => ({ ...role, permissions: role.permissions.map((rp) => rp.permission), })); return createSuccessResponse( plainToInstance( RoleResponseDto, transformedRoles, ) as unknown as RoleResponseDto[], ); } @Post('roles') @ApiOperation({ summary: 'Create a new role' }) async createRole( @Body() data: { name: string; description?: string }, ): Promise> { const role = await this.prisma.role.create({ data }); await this.cacheManager.del('roles_list'); return createSuccessResponse( plainToInstance(RoleResponseDto, role), 'Role created', 201, ); } @Put('roles/:id') @ApiOperation({ summary: 'Update a role' }) async updateRole( @Param('id') id: string, @Body() data: { name?: string; description?: string }, ): Promise> { const role = await this.prisma.role.update({ where: { id }, data }); await this.cacheManager.del('roles_list'); return createSuccessResponse( plainToInstance(RoleResponseDto, role), 'Role updated', ); } @Delete('roles/:id') @ApiOperation({ summary: 'Delete a role' }) async deleteRole(@Param('id') id: string): Promise> { await this.prisma.role.delete({ where: { id } }); await this.cacheManager.del('roles_list'); return createSuccessResponse(null, 'Role deleted'); } // ================== Permissions Management ================== @Get('permissions') @UseInterceptors(CacheInterceptor) @CacheKey('permissions_list') @CacheTTL(60 * 1000) @ApiOperation({ summary: 'Get all permissions' }) async getAllPermissions(): Promise> { const permissions = await this.prisma.permission.findMany(); return createSuccessResponse( plainToInstance( PermissionResponseDto, permissions, ) as unknown as PermissionResponseDto[], ); } @Post('permissions') @ApiOperation({ summary: 'Create a new permission' }) async createPermission( @Body() data: { name: string; description?: string; resource: string; action: string; }, ): Promise> { const permission = await this.prisma.permission.create({ data }); await this.cacheManager.del('permissions_list'); return createSuccessResponse( plainToInstance(PermissionResponseDto, permission), 'Permission created', 201, ); } @Post('roles/:roleId/permissions/:permissionId') @ApiOperation({ summary: 'Assign permission to role' }) async assignPermission( @Param('roleId') roleId: string, @Param('permissionId') permissionId: string, ): Promise> { const rolePermission = await this.prisma.rolePermission.create({ data: { roleId, permissionId }, }); // Invalidate roles_list because permissions are nested in roles await this.cacheManager.del('roles_list'); return createSuccessResponse( plainToInstance(RolePermissionResponseDto, rolePermission), 'Permission assigned to role', ); } @Delete('roles/:roleId/permissions/:permissionId') @ApiOperation({ summary: 'Remove permission from role' }) async removePermission( @Param('roleId') roleId: string, @Param('permissionId') permissionId: string, ): Promise> { await this.prisma.rolePermission.deleteMany({ where: { roleId, permissionId }, }); // Invalidate roles_list because permissions are nested in roles await this.cacheManager.del('roles_list'); return createSuccessResponse(null, 'Permission removed from role'); } }