import { BadRequestException, Injectable } from "@nestjs/common"; import slugify from "slugify"; import { DataSource, In, Not } from "typeorm"; import { AdminMessage, AdsMessage, UserMessage } from "../../../common/enums/message.enum"; import { PaginationUtils } from "../../utils/providers/pagination.utils"; import { PasswordService } from "../../utils/providers/password.service"; import { CreateAdminDto } from "../DTO/create-admin.dto"; import { CreateRoleDto } from "../DTO/create-role.dto"; import { SearchAdminQueryDto } from "../DTO/search-admins-query.dto"; import { SearchRolesQueryDto } from "../DTO/search-roles.dto"; import { UpdateAdminDto } from "../DTO/update-admin.dto"; import { RoleEnum } from "../enums/role.enum"; import { PermissionsRepository } from "../repositories/permissions.repository"; import { RoleRepository } from "../repositories/roles.repository"; import { UserRepository } from "../repositories/users.repository"; @Injectable() export class AdminsService { constructor( private readonly userRepository: UserRepository, private readonly rolesRepository: RoleRepository, private readonly permissionsRepository: PermissionsRepository, private readonly passwordService: PasswordService, private readonly dataSource: DataSource, ) {} async createAdmin(createDto: CreateAdminDto) { const queryRunner = this.dataSource.createQueryRunner(); try { await queryRunner.connect(); await queryRunner.startTransaction(); const existEmail = await queryRunner.manager.findOneBy(this.userRepository.target, { email: createDto.email }); if (existEmail) throw new BadRequestException(UserMessage.EMAIL_EXIST); const existPhone = await queryRunner.manager.findOneBy(this.userRepository.target, { phone: createDto.phone }); if (existPhone) throw new BadRequestException(UserMessage.PHONE_EXIST); const role = await queryRunner.manager.findOne(this.rolesRepository.target, { where: { id: createDto.roleId }, relations: { permissions: true }, }); if (!role) throw new BadRequestException(AdsMessage.ROLE_NOT_FOUND); if (createDto.password !== createDto.repeatPassword) throw new BadRequestException(AdminMessage.PASSWORD_NOT_MATCH); const hashedPassword = await this.passwordService.hashPassword(createDto.password); const userName = slugify(`${createDto.firstName} ${Date.now().toString().slice(-5)}`, { lower: true, trim: true }); await queryRunner.manager.save(this.rolesRepository.target, role); const adminUser = queryRunner.manager.create(this.userRepository.target, { ...createDto, roles: [role], password: hashedPassword, userName, }); await queryRunner.manager.save(this.userRepository.target, adminUser); await queryRunner.commitTransaction(); return { message: AdminMessage.ADMIN_CREATED, adminUser, }; } catch (error) { await queryRunner.rollbackTransaction(); throw error; } finally { await queryRunner.release(); } } /************************************************************ */ async updateAdmin(id: string, updateDto: UpdateAdminDto) { const queryRunner = this.dataSource.createQueryRunner(); try { await queryRunner.connect(); await queryRunner.startTransaction(); const admin = await queryRunner.manager.findOne(this.userRepository.target, { where: { id, roles: { isAdmin: true } }, relations: { roles: true }, }); if (!admin) throw new BadRequestException(UserMessage.ADMIN_NOT_FOUND); if (updateDto.email && updateDto.email !== admin.email) { const existEmail = await queryRunner.manager.findOneBy(this.userRepository.target, { email: updateDto.email, id: Not(id) }); if (existEmail) throw new BadRequestException(UserMessage.EMAIL_EXIST); } if (updateDto.phone && updateDto.phone !== admin.phone) { const existPhone = await queryRunner.manager.findOneBy(this.userRepository.target, { phone: updateDto.phone, id: Not(id) }); if (existPhone) throw new BadRequestException(UserMessage.PHONE_EXIST); } if (updateDto.roleId) { const role = await queryRunner.manager.findOne(this.rolesRepository.target, { where: { id: updateDto.roleId }, relations: { permissions: true }, }); if (!role) throw new BadRequestException(AdsMessage.ROLE_NOT_FOUND); await queryRunner.manager.save(this.rolesRepository.target, role); admin.roles = [role]; await queryRunner.manager.save(this.userRepository.target, admin); } if (updateDto.password) { if (updateDto.password !== updateDto.repeatPassword) throw new BadRequestException(AdminMessage.PASSWORD_NOT_MATCH); admin.password = await this.passwordService.hashPassword(updateDto.password); } await queryRunner.manager.save(this.userRepository.target, { ...admin, ...updateDto, password: admin.password }); await queryRunner.commitTransaction(); return { message: AdminMessage.ADMIN_UPDATED, }; } catch (error) { await queryRunner.rollbackTransaction(); throw error; } finally { await queryRunner.release(); } } /************************************************************ */ async deleteAdmin(id: string) { const admin = await this.userRepository.findOne({ where: { id, roles: { isAdmin: true } } }); if (!admin) throw new BadRequestException(AdminMessage.ADMIN_NOT_FOUND); await this.userRepository.softDelete(id); return { message: AdminMessage.ADMIN_DELETED, }; } /************************************************************ */ async createRole(createDto: CreateRoleDto) { const existRole = await this.rolesRepository.findOne({ where: { name: createDto.name } }); if (existRole) throw new BadRequestException(AdminMessage.ROLE_EXIST); const permissions = await this.permissionsRepository.findBy({ id: In(createDto.permissions) }); if (permissions.length !== createDto.permissions.length) throw new BadRequestException(AdminMessage.PERMISSIONS_NOT_FOUND); const role = this.rolesRepository.create({ ...createDto, permissions, isAdmin: true }); await this.rolesRepository.save(role); return { message: AdminMessage.ROLE_CREATED, role, }; } /************************************************************ */ async getAdmins(queryDto: SearchAdminQueryDto) { const [users, count] = await this.userRepository.getAdmins(queryDto); return { users, count, paginate: true, }; } /************************************************************ */ async getAdminById(id: string) { const admin = await this.userRepository.findOne({ where: { id, roles: { isAdmin: true } }, relations: { roles: true } }); if (!admin) throw new BadRequestException(AdminMessage.ADMIN_NOT_FOUND); return { admin }; } /************************************************************ */ async getPermissions() { const permissions = await this.permissionsRepository.find({ select: { id: true, name: true } }); return { permissions, }; } /************************************************************ */ async getAdminPermissions(userId: string) { const user = await this.userRepository.findOne({ where: { id: userId }, relations: { roles: { permissions: true } } }); if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND); return { permissions: user.roles[0].permissions, }; } /************************************************************ */ async getRoles(queryDto: SearchRolesQueryDto) { const { limit, skip } = PaginationUtils(queryDto); const queryBuilder = this.rolesRepository.createQueryBuilder("role"); queryBuilder .where("role.isAdmin = :isAdmin", { isAdmin: true }) // .leftJoinAndSelect("role.permissions", "permissions") .loadRelationCountAndMap("role.userCount", "role.users"); if (queryDto.q) { queryBuilder.andWhere("role.name ILIKE :q", { q: `%${queryDto.q}%` }); } queryBuilder.take(limit).skip(skip).orderBy("role.createdAt", "DESC"); const [roles, count] = await queryBuilder.getManyAndCount(); return { roles, count, paginate: true, }; } /************************************************************ */ async getSuperAdmins() { return this.userRepository.find({ where: { roles: { isAdmin: true, name: RoleEnum.SUPER_ADMIN } } }); } }