refactor: change the role to roles and many to many
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import { Logger } from "@nestjs/common";
|
||||
|
||||
import { connectionSource } from "./connection";
|
||||
// import { seedAdmin } from "./seeders/admin.seeder";
|
||||
import { seedAdmin } from "./seeders/admin.seeder";
|
||||
import { seedCityAndProvince } from "./seeders/iran-city.seeder";
|
||||
// import { seedNotifSettings } from "./seeders/notif-setting.seeder";
|
||||
// import { seedPaymentGateways } from "./seeders/payment-gateway.seeder";
|
||||
// import { seedRole } from "./seeders/role.seeder";
|
||||
import { seedNotifSettings } from "./seeders/notif-setting.seeder";
|
||||
import { seedPaymentGateways } from "./seeders/payment-gateway.seeder";
|
||||
import { seedPermissionsAndRoles } from "./seeders/role.seeder";
|
||||
|
||||
const logger = new Logger("Seeder");
|
||||
|
||||
@@ -13,11 +13,11 @@ export const runSeeder = async () => {
|
||||
await connectionSource.initialize();
|
||||
logger.log("start seeding database");
|
||||
//
|
||||
// await seedRole(connectionSource, logger);
|
||||
// await seedAdmin(connectionSource, logger);
|
||||
// await seedNotifSettings(connectionSource, logger);
|
||||
// await seedPaymentGateways(connectionSource, logger);
|
||||
await seedCityAndProvince(connectionSource, logger);
|
||||
await seedPermissionsAndRoles(connectionSource, logger);
|
||||
await seedAdmin(connectionSource, logger);
|
||||
await seedNotifSettings(connectionSource, logger);
|
||||
await seedPaymentGateways(connectionSource, logger);
|
||||
|
||||
logger.log("seeding completed");
|
||||
process.exit(0);
|
||||
|
||||
@@ -26,7 +26,7 @@ export const seedAdmin = async (dataSource: DataSource, logger: Logger) => {
|
||||
|
||||
const hashedPassword = await hash(defaultAdmin.password!, 10);
|
||||
|
||||
const admin = userRepo.create({ ...defaultAdmin, password: hashedPassword, role: adminRole });
|
||||
const admin = userRepo.create({ ...defaultAdmin, password: hashedPassword, roles: [adminRole] });
|
||||
await userRepo.save(admin);
|
||||
logger.log("admin created successfully");
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,21 +1,68 @@
|
||||
import { Logger } from "@nestjs/common";
|
||||
import { DataSource } from "typeorm";
|
||||
|
||||
import { Permission } from "../../src/modules/users/entities/permission.entity";
|
||||
import { Role } from "../../src/modules/users/entities/role.entity";
|
||||
import { PermissionEnum } from "../../src/modules/users/enums/permission.enum";
|
||||
import { RoleEnum } from "../../src/modules/users/enums/role.enum";
|
||||
|
||||
const roles = [{ name: RoleEnum.SUPER_ADMIN }, { name: RoleEnum.ADMIN }, { name: RoleEnum.VISITOR }, { name: RoleEnum.USER }];
|
||||
// Define permissions
|
||||
const permissions = Object.values(PermissionEnum).map((perm) => ({ name: perm }));
|
||||
|
||||
export const seedRole = async (dataSource: DataSource, logger: Logger) => {
|
||||
// Define roles with their default permissions
|
||||
const rolesWithPermissions: { name: RoleEnum; permissions: PermissionEnum[] }[] = [
|
||||
{
|
||||
name: RoleEnum.ADMIN,
|
||||
permissions: Object.values(PermissionEnum),
|
||||
},
|
||||
{
|
||||
name: RoleEnum.AGENT,
|
||||
permissions: [PermissionEnum.CUSTOMERS, PermissionEnum.TICKETS, PermissionEnum.TRANSACTIONS],
|
||||
},
|
||||
{
|
||||
name: RoleEnum.DEVELOPER,
|
||||
permissions: [PermissionEnum.SERVICES, PermissionEnum.LOGS, PermissionEnum.LEARNINGS],
|
||||
},
|
||||
{
|
||||
name: RoleEnum.USER,
|
||||
permissions: [],
|
||||
},
|
||||
];
|
||||
|
||||
export const seedPermissionsAndRoles = async (dataSource: DataSource, logger: Logger) => {
|
||||
try {
|
||||
const permissionRepo = dataSource.getRepository(Permission);
|
||||
const roleRepo = dataSource.getRepository(Role);
|
||||
for (const role of roles) {
|
||||
const newRole = roleRepo.create(role);
|
||||
await roleRepo.save(newRole);
|
||||
|
||||
// Seed Permissions
|
||||
logger.log("Seeding Permissions...");
|
||||
for (const perm of permissions) {
|
||||
const existingPerm = await permissionRepo.findOne({ where: { name: perm.name } });
|
||||
if (!existingPerm) {
|
||||
const newPerm = permissionRepo.create(perm);
|
||||
await permissionRepo.save(newPerm);
|
||||
}
|
||||
logger.log("Role seeded successfully");
|
||||
}
|
||||
logger.log("Permissions seeded successfully");
|
||||
|
||||
// Fetch all saved permissions
|
||||
const allPermissions = await permissionRepo.find();
|
||||
|
||||
// Seed Roles with permissions
|
||||
logger.log("Seeding Roles with Permissions...");
|
||||
for (const roleData of rolesWithPermissions) {
|
||||
let role = await roleRepo.findOne({ where: { name: roleData.name }, relations: ["permissions"] });
|
||||
|
||||
if (!role) {
|
||||
role = roleRepo.create({ name: roleData.name, permissions: [] });
|
||||
}
|
||||
|
||||
role.permissions = allPermissions.filter((perm) => roleData.permissions.includes(perm.name as PermissionEnum));
|
||||
await roleRepo.save(role);
|
||||
}
|
||||
logger.log("Roles and Permissions seeded successfully");
|
||||
} catch (error) {
|
||||
logger.error("Error in seeding role", error);
|
||||
logger.error("Error in seeding roles and permissions", error);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,8 +2,9 @@ import { UseGuards, applyDecorators } from "@nestjs/common";
|
||||
import { ApiBearerAuth } from "@nestjs/swagger";
|
||||
|
||||
import { JwtAuthGuard } from "../../modules/auth/guards/auth.guard";
|
||||
import { PermissionsGuard } from "../../modules/auth/guards/permission.guard";
|
||||
import { RoleGuard } from "../../modules/auth/guards/role.guard";
|
||||
|
||||
export function AuthGuards() {
|
||||
return applyDecorators(UseGuards(JwtAuthGuard, RoleGuard), ApiBearerAuth("authorization"));
|
||||
return applyDecorators(UseGuards(JwtAuthGuard, RoleGuard, PermissionsGuard), ApiBearerAuth("authorization"));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { SetMetadata } from "@nestjs/common";
|
||||
|
||||
import { PermissionEnum } from "../../modules/users/enums/permission.enum";
|
||||
|
||||
export const PERMISSION_KEY = "permissions";
|
||||
export const PermissionsDec = (...permissions: PermissionEnum[]) => SetMetadata(PERMISSION_KEY, permissions);
|
||||
@@ -1,15 +1,16 @@
|
||||
import { ExecutionContext, createParamDecorator } from "@nestjs/common";
|
||||
import { FastifyRequest } from "fastify";
|
||||
|
||||
import { User } from "../../modules/users/entities/user.entity";
|
||||
import { ITokenPayload } from "../../modules/auth/interfaces/IToken-payload";
|
||||
|
||||
declare module "fastify" {
|
||||
interface FastifyRequest {
|
||||
user?: Omit<User, "password">;
|
||||
// user?: Omit<User, "password">;
|
||||
user?: ITokenPayload;
|
||||
}
|
||||
}
|
||||
|
||||
export const UserDec = createParamDecorator((data: keyof Omit<User, "password"> | undefined, ctx: ExecutionContext) => {
|
||||
export const UserDec = createParamDecorator((data: keyof ITokenPayload | undefined, ctx: ExecutionContext) => {
|
||||
const req = ctx.switchToHttp().getRequest<FastifyRequest>();
|
||||
const user = req.user;
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ export class AnnouncementController {
|
||||
}
|
||||
|
||||
@AuthGuards()
|
||||
@Roles(RoleEnum.ADMIN, RoleEnum.USER, RoleEnum.VISITOR)
|
||||
@Roles(RoleEnum.ADMIN, RoleEnum.USER)
|
||||
@ApiProperty({
|
||||
description: "Get all announcements ===> login as user",
|
||||
})
|
||||
@@ -47,7 +47,7 @@ export class AnnouncementController {
|
||||
}
|
||||
|
||||
@AuthGuards()
|
||||
@Roles(RoleEnum.ADMIN, RoleEnum.USER, RoleEnum.VISITOR)
|
||||
@Roles(RoleEnum.ADMIN, RoleEnum.USER)
|
||||
@ApiProperty({
|
||||
description: "Get one announcements with id",
|
||||
})
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { CanActivate, ExecutionContext, ForbiddenException, Injectable } from "@nestjs/common";
|
||||
import { Reflector } from "@nestjs/core";
|
||||
import { FastifyRequest } from "fastify";
|
||||
|
||||
import { PERMISSION_KEY } from "../../../common/decorators/permission.decorator";
|
||||
import { AuthMessage } from "../../../common/enums/message.enum";
|
||||
import { PermissionEnum } from "../../users/enums/permission.enum";
|
||||
|
||||
@Injectable()
|
||||
export class PermissionsGuard implements CanActivate {
|
||||
constructor(private reflector: Reflector) {}
|
||||
|
||||
canActivate(context: ExecutionContext) {
|
||||
const requiredPermissions = this.reflector.getAllAndOverride<PermissionEnum[]>(PERMISSION_KEY, [
|
||||
context.getHandler(),
|
||||
context.getClass(),
|
||||
]);
|
||||
|
||||
if (!requiredPermissions) return true;
|
||||
|
||||
const request = context.switchToHttp().getRequest<FastifyRequest>();
|
||||
const user = request.user;
|
||||
|
||||
if (!user) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS);
|
||||
|
||||
const hasPermission = requiredPermissions.every((perm) => user.permissions.includes(perm));
|
||||
|
||||
if (!hasPermission) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ export class RoleGuard implements CanActivate {
|
||||
|
||||
if (!user) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS);
|
||||
|
||||
const hasRequiredRole = requiredRole.includes(user.role as unknown as RoleEnum);
|
||||
const hasRequiredRole = requiredRole.some((role) => user.roles.includes(role));
|
||||
if (!hasRequiredRole) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { PermissionEnum } from "../../users/enums/permission.enum";
|
||||
import { RoleEnum } from "../../users/enums/role.enum";
|
||||
|
||||
export interface ITokenPayload {
|
||||
sub: string;
|
||||
role: RoleEnum;
|
||||
id: string;
|
||||
roles: RoleEnum[];
|
||||
permissions: PermissionEnum[];
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import { DataSource } from "typeorm";
|
||||
import { TokensService } from "./tokens.service";
|
||||
import { AuthMessage, UserMessage } from "../../../common/enums/message.enum";
|
||||
import { NotificationsService } from "../../notifications/providers/notifications.service";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
import { RoleEnum } from "../../users/enums/role.enum";
|
||||
import { UsersService } from "../../users/providers/users.service";
|
||||
import { checkUserRole } from "../../utils/providers/checkRole.utils";
|
||||
import { OTPService } from "../../utils/providers/otp.service";
|
||||
import { PasswordService } from "../../utils/providers/password.service";
|
||||
import { SmsService } from "../../utils/providers/sms.service";
|
||||
@@ -68,7 +70,7 @@ export class AuthService {
|
||||
const hashedPassword = await this.passwordService.hashPassword(completeRegistrationDto.password);
|
||||
const user = await this.usersService.createUser(completeRegistrationDto, hashedPassword, queryRunner);
|
||||
|
||||
const tokens = this.tokensService.generateAccessAndRefreshToken({ sub: user.id, role: user.role.name });
|
||||
const tokens = this.generateAccessAndRefreshToken(user);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
@@ -90,7 +92,7 @@ export class AuthService {
|
||||
|
||||
const user = await this.checkUserLoginCredentialWithEmail(email, password);
|
||||
|
||||
const tokens = this.tokensService.generateAccessAndRefreshToken({ sub: user.id, role: user.role.name });
|
||||
const tokens = this.generateAccessAndRefreshToken(user);
|
||||
|
||||
await this.notificationService.createLoginNotification(user.id);
|
||||
|
||||
@@ -107,8 +109,10 @@ export class AuthService {
|
||||
|
||||
const user = await this.checkUserLoginCredentialWithEmail(email, password);
|
||||
|
||||
if (user.role.name !== RoleEnum.ADMIN) throw new BadRequestException(AuthMessage.NOT_ADMIN);
|
||||
const tokens = this.tokensService.generateAccessAndRefreshToken({ sub: user.id, role: user.role.name });
|
||||
const hasAccess = checkUserRole(user.roles, RoleEnum.ADMIN);
|
||||
if (!hasAccess) throw new BadRequestException(AuthMessage.NOT_ADMIN);
|
||||
|
||||
const tokens = this.generateAccessAndRefreshToken(user);
|
||||
|
||||
return {
|
||||
message: AuthMessage.PASSWORD_LOGIN_SUCCESS,
|
||||
@@ -134,7 +138,9 @@ export class AuthService {
|
||||
if (!user) throw new BadRequestException(AuthMessage.PHONE_NOT_FOUND);
|
||||
|
||||
//check the if the method call is from admin or not
|
||||
if (isAdmin && user.role.name !== RoleEnum.ADMIN) throw new BadRequestException(AuthMessage.NOT_ADMIN);
|
||||
|
||||
const hasAccess = checkUserRole(user.roles, RoleEnum.ADMIN);
|
||||
if (isAdmin && !hasAccess) throw new BadRequestException(AuthMessage.NOT_ADMIN);
|
||||
|
||||
const existCode = await this.otpService.checkExistOtp(phone, "LOGIN");
|
||||
if (existCode) {
|
||||
@@ -163,7 +169,7 @@ export class AuthService {
|
||||
|
||||
const user = await this.checkUserLoginCredentialWithPhone(phone, code);
|
||||
|
||||
const tokens = this.tokensService.generateAccessAndRefreshToken({ sub: user.id, role: user.role.name });
|
||||
const tokens = this.generateAccessAndRefreshToken(user);
|
||||
|
||||
await this.notificationService.createLoginNotification(user.id);
|
||||
return {
|
||||
@@ -180,9 +186,12 @@ export class AuthService {
|
||||
|
||||
const user = await this.checkUserLoginCredentialWithPhone(phone, code);
|
||||
|
||||
if (user.role.name !== RoleEnum.ADMIN) throw new BadRequestException(AuthMessage.NOT_ADMIN);
|
||||
const hasAccess = checkUserRole(user.roles, RoleEnum.ADMIN);
|
||||
|
||||
if (!hasAccess) throw new BadRequestException(AuthMessage.NOT_ADMIN);
|
||||
|
||||
const tokens = this.generateAccessAndRefreshToken(user);
|
||||
|
||||
const tokens = this.tokensService.generateAccessAndRefreshToken({ sub: user.id, role: user.role.name });
|
||||
return {
|
||||
message: AuthMessage.LOGIN_SUCCESS,
|
||||
...tokens,
|
||||
@@ -231,4 +240,13 @@ export class AuthService {
|
||||
|
||||
return user;
|
||||
}
|
||||
//****************** */
|
||||
//****************** */
|
||||
private generateAccessAndRefreshToken(user: User) {
|
||||
return this.tokensService.generateAccessAndRefreshToken({
|
||||
id: user.id,
|
||||
roles: user.roles.map((r) => r.name),
|
||||
permissions: user.roles.flatMap((r) => (r?.permissions?.length ? r.permissions.map((p) => p.name) : [])),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,6 @@ export class JwtStrategy extends PassportStrategy(Strategy, JWT_STRATEGY_NAME) {
|
||||
}
|
||||
|
||||
async validate(payload: ITokenPayload) {
|
||||
return { id: payload.sub, role: payload.role };
|
||||
return { id: payload.id, roles: payload.roles, permissions: payload.permissions };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ export class DanakServicesController {
|
||||
@AuthGuards()
|
||||
@Roles(RoleEnum.USER, RoleEnum.ADMIN)
|
||||
@Get(":id")
|
||||
getDanakServiceById(@Param() paramDto: ParamDto, @UserDec("role") userRole: RoleEnum) {
|
||||
getDanakServiceById(@Param() paramDto: ParamDto, @UserDec("roles") userRole: RoleEnum[]) {
|
||||
return this.danakServicesService.getDanakServiceByIdWithSubs(paramDto.id, userRole);
|
||||
}
|
||||
|
||||
|
||||
@@ -173,9 +173,10 @@ export class DanakServicesService {
|
||||
}
|
||||
/******************************************** */
|
||||
|
||||
async getDanakServiceByIdWithSubs(serviceId: string, role: RoleEnum) {
|
||||
async getDanakServiceByIdWithSubs(serviceId: string, roles: RoleEnum[]) {
|
||||
const hasAccess = roles.includes(RoleEnum.ADMIN);
|
||||
const danakService = await this.danakServicesRepository.findOne({
|
||||
where: { id: serviceId, ...(role !== RoleEnum.ADMIN && { isActive: true, subscriptionPlans: { isActive: true } }) },
|
||||
where: { id: serviceId, ...(hasAccess && { isActive: true, subscriptionPlans: { isActive: true } }) },
|
||||
relations: { images: true, subscriptionPlans: true },
|
||||
});
|
||||
if (!danakService) throw new BadRequestException(ServiceMessage.SERVICE_NOT_FOUND_BY_ID);
|
||||
|
||||
@@ -46,8 +46,8 @@ export class InvoicesController {
|
||||
@AuthGuards()
|
||||
@Roles(RoleEnum.ADMIN, RoleEnum.USER)
|
||||
@Get(":id")
|
||||
getInvoiceById(@Param() paramDto: ParamDto, @UserDec("role") role: RoleEnum, @UserDec("id") userId: string) {
|
||||
return this.invoiceService.getInvoiceById(paramDto.id, role, userId);
|
||||
getInvoiceById(@Param() paramDto: ParamDto, @UserDec("roles") roles: RoleEnum[], @UserDec("id") userId: string) {
|
||||
return this.invoiceService.getInvoiceById(paramDto.id, roles, userId);
|
||||
}
|
||||
|
||||
@Post(":id/apply-discount")
|
||||
|
||||
@@ -126,11 +126,10 @@ export class InvoicesService {
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
|
||||
async getInvoiceById(invoiceId: string, role: RoleEnum, userId: string) {
|
||||
async getInvoiceById(invoiceId: string, roles: RoleEnum[], userId: string) {
|
||||
let invoice: Invoice | null;
|
||||
|
||||
if (role === RoleEnum.ADMIN) {
|
||||
if (roles.includes(RoleEnum.ADMIN)) {
|
||||
invoice = await this.invoiceRepository.findOne({
|
||||
where: { id: invoiceId },
|
||||
relations: { items: { subscriptionPlan: true }, user: true },
|
||||
|
||||
@@ -15,7 +15,6 @@ import { Roles } from "../../common/decorators/roles.decorator";
|
||||
import { UserDec } from "../../common/decorators/user.decorator";
|
||||
import { PaginationDto } from "../../common/DTO/pagination.dto";
|
||||
import { ParamDto } from "../../common/DTO/param.dto";
|
||||
import { Role } from "../users/entities/role.entity";
|
||||
import { RoleEnum } from "../users/enums/role.enum";
|
||||
|
||||
@Controller("payments")
|
||||
@@ -124,8 +123,8 @@ export class PaymentsController {
|
||||
@Roles(RoleEnum.ADMIN, RoleEnum.USER)
|
||||
@ApiOperation({ summary: "get bank account" })
|
||||
@Get("bank-account")
|
||||
getBankAccounts(@UserDec("role") userRole: Role) {
|
||||
return this.paymentsService.getBankAccounts(userRole);
|
||||
getBankAccounts(@UserDec("roles") roles: RoleEnum[]) {
|
||||
return this.paymentsService.getBankAccounts(roles);
|
||||
}
|
||||
|
||||
///------------------- Deposit transfer -----------------------------
|
||||
|
||||
@@ -7,7 +7,6 @@ import { DataSource, Not, QueryRunner } from "typeorm";
|
||||
|
||||
import { PaginationDto } from "../../../common/DTO/pagination.dto";
|
||||
import { CommonMessage, PaymentMessage, UserMessage, WalletMessage } from "../../../common/enums/message.enum";
|
||||
import { Role } from "../../users/entities/role.entity";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
import { RoleEnum } from "../../users/enums/role.enum";
|
||||
import { PaginationUtils } from "../../utils/providers/pagination.utils";
|
||||
@@ -263,11 +262,10 @@ export class PaymentsService {
|
||||
await this.bankAccountsRepository.save({ ...bankAccount, ...updateDto });
|
||||
}
|
||||
//*********************************** */
|
||||
async getBankAccounts(role: Role) {
|
||||
const userRole = role as unknown as RoleEnum;
|
||||
async getBankAccounts(roles: RoleEnum[]) {
|
||||
let bankAccounts: BankAccount[];
|
||||
|
||||
if (userRole === RoleEnum.USER) {
|
||||
if (roles.includes(RoleEnum.USER)) {
|
||||
bankAccounts = await this.bankAccountsRepository.find({ where: { isActive: true } });
|
||||
} else {
|
||||
bankAccounts = await this.bankAccountsRepository.find();
|
||||
|
||||
@@ -3,7 +3,6 @@ import { DataSource, Not } from "typeorm";
|
||||
|
||||
import { ParamDto } from "../../../common/DTO/param.dto";
|
||||
import { CommonMessage, TicketMessageEnum, UserMessage } from "../../../common/enums/message.enum";
|
||||
import { Role } from "../../users/entities/role.entity";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
import { RoleEnum } from "../../users/enums/role.enum";
|
||||
import { UsersService } from "../../users/providers/users.service";
|
||||
@@ -182,16 +181,15 @@ export class TicketsService {
|
||||
|
||||
//******************************** */
|
||||
|
||||
async createTicketMessage(ticketId: string, createDto: CreateTicketMessageDto, userId: string, role: Role) {
|
||||
async createTicketMessage(ticketId: string, createDto: CreateTicketMessageDto, userId: string, roles: RoleEnum[]) {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
const userRole = role as unknown as RoleEnum;
|
||||
let ticket: null | Ticket = null;
|
||||
|
||||
if (userRole === RoleEnum.ADMIN) {
|
||||
if (roles.includes(RoleEnum.ADMIN)) {
|
||||
ticket = await queryRunner.manager.findOneBy(Ticket, { id: ticketId });
|
||||
} else {
|
||||
ticket = await queryRunner.manager.findOneBy(Ticket, { id: ticketId, user: { id: userId } });
|
||||
@@ -206,7 +204,7 @@ export class TicketsService {
|
||||
|
||||
const ticketMessage = queryRunner.manager.create(TicketMessage, { ...createDto, author: user, ticket });
|
||||
|
||||
if (userRole === RoleEnum.ADMIN && ticket.status === TicketStatus.PENDING) {
|
||||
if (roles.includes(RoleEnum.ADMIN) && ticket.status === TicketStatus.PENDING) {
|
||||
ticket.status = TicketStatus.ANSWERED;
|
||||
}
|
||||
|
||||
@@ -236,12 +234,11 @@ export class TicketsService {
|
||||
|
||||
//******************************** */
|
||||
|
||||
async getTicketMessages(ticketId: string, userId: string, role: Role) {
|
||||
const userRole = role as unknown as RoleEnum;
|
||||
async getTicketMessages(ticketId: string, userId: string, roles: RoleEnum[]) {
|
||||
let ticket: null | Ticket = null;
|
||||
|
||||
//
|
||||
if (userRole === RoleEnum.ADMIN) {
|
||||
if (roles.includes(RoleEnum.ADMIN)) {
|
||||
ticket = await this.ticketsRepository.findTicketById(ticketId);
|
||||
} else {
|
||||
ticket = await this.ticketsRepository.findTicketById(ticketId, userId);
|
||||
@@ -257,11 +254,10 @@ export class TicketsService {
|
||||
|
||||
//******************************** */
|
||||
|
||||
async closeTicketByUser(ticketId: string, userId: string, role: Role) {
|
||||
const userRole = role as unknown as RoleEnum;
|
||||
async closeTicketByUser(ticketId: string, userId: string, roles: RoleEnum[]) {
|
||||
let ticket: null | Ticket = null;
|
||||
|
||||
if (userRole === RoleEnum.ADMIN) {
|
||||
if (roles.includes(RoleEnum.ADMIN)) {
|
||||
ticket = await this.ticketsRepository.findTicketById(ticketId);
|
||||
} else {
|
||||
ticket = await this.ticketsRepository.findTicketById(ticketId, userId);
|
||||
|
||||
@@ -29,7 +29,7 @@ export class TicketMessagesRepository extends Repository<TicketMessage> {
|
||||
id: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
role: {
|
||||
roles: {
|
||||
id: true,
|
||||
name: false,
|
||||
},
|
||||
|
||||
@@ -10,10 +10,12 @@ import { UpdateTicketCategoryDto } from "./DTO/update-ticket-category.dto";
|
||||
import { TicketsService } from "./providers/tickets.service";
|
||||
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||
import { Pagination } from "../../common/decorators/pagination.decorator";
|
||||
import { PermissionsDec } from "../../common/decorators/permission.decorator";
|
||||
import { Roles } from "../../common/decorators/roles.decorator";
|
||||
import { UserDec } from "../../common/decorators/user.decorator";
|
||||
import { ParamDto } from "../../common/DTO/param.dto";
|
||||
import { User } from "../users/entities/user.entity";
|
||||
import { PermissionEnum } from "../users/enums/permission.enum";
|
||||
import { RoleEnum } from "../users/enums/role.enum";
|
||||
|
||||
@Controller("tickets")
|
||||
@@ -26,6 +28,7 @@ export class TicketsController {
|
||||
@ApiOperation({ summary: "Create ticket category => admin route" })
|
||||
@AuthGuards()
|
||||
@Roles(RoleEnum.ADMIN)
|
||||
@PermissionsDec(PermissionEnum.TICKETS)
|
||||
@Post("category")
|
||||
createTicketCategory(@Body() createDto: CreateTicketCategoryDto) {
|
||||
return this.ticketsService.createTicketCategory(createDto);
|
||||
@@ -34,6 +37,7 @@ export class TicketsController {
|
||||
@ApiOperation({ summary: "Update ticket category => admin route" })
|
||||
@AuthGuards()
|
||||
@Roles(RoleEnum.ADMIN)
|
||||
@PermissionsDec(PermissionEnum.TICKETS)
|
||||
@Patch("category/:id")
|
||||
updateCategory(@Param() paramDto: ParamDto, @Body() updateCategoryDto: UpdateTicketCategoryDto) {
|
||||
return this.ticketsService.updateCategory(paramDto, updateCategoryDto);
|
||||
@@ -43,6 +47,7 @@ export class TicketsController {
|
||||
@AuthGuards()
|
||||
@Roles(RoleEnum.ADMIN, RoleEnum.USER)
|
||||
@Get("categories")
|
||||
@PermissionsDec(PermissionEnum.TICKETS)
|
||||
getTicketCategories() {
|
||||
return this.ticketsService.getTicketCategories();
|
||||
}
|
||||
@@ -98,7 +103,7 @@ export class TicketsController {
|
||||
@Roles(RoleEnum.USER, RoleEnum.ADMIN)
|
||||
@Post(":id/messages")
|
||||
createTicketMessage(@Param() paramDto: ParamDto, @Body() createDto: CreateTicketMessageDto, @UserDec() user: User) {
|
||||
return this.ticketsService.createTicketMessage(paramDto.id, createDto, user.id, user.role);
|
||||
return this.ticketsService.createTicketMessage(paramDto.id, createDto, user.id, user.roles as unknown as RoleEnum[]);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "Get all ticket messages of user" })
|
||||
@@ -106,7 +111,7 @@ export class TicketsController {
|
||||
@Roles(RoleEnum.USER, RoleEnum.ADMIN)
|
||||
@Get(":id/messages")
|
||||
getTicketMessages(@Param() paramDto: ParamDto, @UserDec() user: User) {
|
||||
return this.ticketsService.getTicketMessages(paramDto.id, user.id, user.role);
|
||||
return this.ticketsService.getTicketMessages(paramDto.id, user.id, user.roles as unknown as RoleEnum[]);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "close ticket by user or admin" })
|
||||
@@ -115,6 +120,6 @@ export class TicketsController {
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Post(":id/close")
|
||||
closedTicketByUser(@Param() paramDto: ParamDto, @UserDec() user: User) {
|
||||
return this.ticketsService.closeTicketByUser(paramDto.id, user.id, user.role);
|
||||
return this.ticketsService.closeTicketByUser(paramDto.id, user.id, user.roles as unknown as RoleEnum[]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Column, Entity, ManyToMany } from "typeorm";
|
||||
|
||||
import { Role } from "./role.entity";
|
||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||
import { PermissionEnum } from "../enums/permission.enum";
|
||||
|
||||
@Entity()
|
||||
export class Permission extends BaseEntity {
|
||||
@Column({ type: "enum", enum: PermissionEnum, nullable: false, unique: true })
|
||||
name: PermissionEnum;
|
||||
|
||||
@ManyToMany(() => Role, (role) => role.permissions)
|
||||
roles: Role[];
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Column, Entity } from "typeorm";
|
||||
import { Column, Entity, JoinTable, ManyToMany } from "typeorm";
|
||||
|
||||
import { Permission } from "./permission.entity";
|
||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||
import { RoleEnum } from "../enums/role.enum";
|
||||
|
||||
@@ -7,4 +8,8 @@ import { RoleEnum } from "../enums/role.enum";
|
||||
export class Role extends BaseEntity {
|
||||
@Column({ type: "enum", enum: RoleEnum, default: RoleEnum.USER, nullable: false, unique: true })
|
||||
name: RoleEnum;
|
||||
|
||||
@ManyToMany(() => Permission, (permission) => permission.roles)
|
||||
@JoinTable({ name: "role_permission_relation" })
|
||||
permissions: Permission[];
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Exclude } from "class-transformer";
|
||||
import { Column, Entity, ManyToMany, ManyToOne, OneToMany, OneToOne } from "typeorm";
|
||||
import { Column, Entity, JoinTable, ManyToMany, OneToMany, OneToOne } from "typeorm";
|
||||
|
||||
import { LegalUser } from "./legal-user.entity";
|
||||
import { RealUser } from "./real-user.entity";
|
||||
@@ -47,8 +47,17 @@ export class User extends BaseEntity {
|
||||
nationalCode: string;
|
||||
|
||||
//-----------------------------------------
|
||||
@ManyToOne(() => Role, { eager: true, onDelete: "RESTRICT", nullable: false })
|
||||
role: Role;
|
||||
// @ManyToOne(() => Role, { eager: true, onDelete: "RESTRICT", nullable: false })
|
||||
// role: Role;
|
||||
|
||||
@ManyToMany(() => Role)
|
||||
@JoinTable({ name: "user_role_relation" })
|
||||
roles: Role[];
|
||||
|
||||
@ManyToMany(() => UserGroup, (group) => group.users)
|
||||
groups: UserGroup[];
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
@OneToMany(() => Ticket, (ticket) => ticket.user)
|
||||
tickets: Ticket[];
|
||||
@@ -56,9 +65,6 @@ export class User extends BaseEntity {
|
||||
@OneToMany(() => TicketMessage, (ticketMessage) => ticketMessage.author)
|
||||
ticketMessage: TicketMessage[];
|
||||
|
||||
@ManyToMany(() => UserGroup, (group) => group.users)
|
||||
groups: UserGroup[];
|
||||
|
||||
@OneToMany(() => Criticism, (criticism) => criticism.user)
|
||||
criticisms: Criticism[];
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
export enum PermissionEnum {
|
||||
SERVICES = "services",
|
||||
CUSTOMERS = "customers",
|
||||
AGENTS = "agents",
|
||||
DEVELOPERS = "developers",
|
||||
INVOICES = "invoices",
|
||||
TRANSACTIONS = "transactions",
|
||||
DISCOUNTS = "discounts",
|
||||
ADMINS = "admins",
|
||||
TICKETS = "tickets",
|
||||
CRITICISMS = "criticisms",
|
||||
CONTACTS_US = "contacts_us",
|
||||
ADVERTISEMENTS = "advertisements",
|
||||
ANNOUNCEMENTS = "announcements",
|
||||
BLOGS = "blogs",
|
||||
LEARNINGS = "learnings",
|
||||
LOGS = "logs",
|
||||
SETTINGS = "settings",
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
export enum RoleEnum {
|
||||
SUPER_ADMIN = "super_admin",
|
||||
ADMIN = "admin",
|
||||
USER = "user",
|
||||
VISITOR = "visitor",
|
||||
DEVELOPER = "developer",
|
||||
AGENT = "agent",
|
||||
}
|
||||
|
||||
@@ -107,13 +107,13 @@ export class UsersService {
|
||||
/************************************************************ */
|
||||
|
||||
async createUser(registerDto: CompleteRegistrationDto, hashedPassword: string, queryRunner: QueryRunner): Promise<User> {
|
||||
const role = await queryRunner.manager.findOne(Role, { where: { name: RoleEnum.USER } });
|
||||
const role = await queryRunner.manager.findOneBy(Role, { name: RoleEnum.USER });
|
||||
if (!role) throw new BadRequestException(UserMessage.ROLE_NOT_FOUND);
|
||||
|
||||
const user = queryRunner.manager.create(User, {
|
||||
...registerDto,
|
||||
password: hashedPassword,
|
||||
role,
|
||||
roles: [role],
|
||||
});
|
||||
await queryRunner.manager.save(user);
|
||||
|
||||
@@ -264,7 +264,7 @@ export class UsersService {
|
||||
const customer = await this.userRepository.findOne({
|
||||
where: {
|
||||
id: userId,
|
||||
role: {
|
||||
roles: {
|
||||
name: RoleEnum.USER,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -11,20 +11,41 @@ export class UserRepository extends Repository<User> {
|
||||
}
|
||||
|
||||
async findOneWithEmail(email: string): Promise<User | null> {
|
||||
return this.findOneBy({
|
||||
return this.findOne({
|
||||
where: {
|
||||
email,
|
||||
},
|
||||
relations: {
|
||||
roles: {
|
||||
permissions: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async findOneWithPhone(phone: string): Promise<User | null> {
|
||||
return this.findOneBy({
|
||||
return this.findOne({
|
||||
where: {
|
||||
phone,
|
||||
},
|
||||
relations: {
|
||||
roles: {
|
||||
permissions: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async findOneWithUserName(userName: string): Promise<User | null> {
|
||||
return this.findOneBy({
|
||||
return this.findOne({
|
||||
where: {
|
||||
userName,
|
||||
},
|
||||
relations: {
|
||||
roles: {
|
||||
permissions: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Module } from "@nestjs/common";
|
||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
|
||||
import { LegalUser } from "./entities/legal-user.entity";
|
||||
import { Permission } from "./entities/permission.entity";
|
||||
import { Role } from "./entities/role.entity";
|
||||
import { UserGroup } from "./entities/user-group.entity";
|
||||
import { User } from "./entities/user.entity";
|
||||
@@ -19,7 +20,7 @@ import { RealUser } from "./entities/real-user.entity";
|
||||
import { RealUserRepository } from "./repositories/real-user.repository";
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([User, Role, UserGroup, UserSetting, RealUser, LegalUser]), WalletsModule],
|
||||
imports: [TypeOrmModule.forFeature([User, Role, UserGroup, UserSetting, RealUser, LegalUser, Permission]), WalletsModule],
|
||||
providers: [
|
||||
UsersService,
|
||||
UserRepository,
|
||||
|
||||
@@ -23,6 +23,4 @@ export interface ISmsVerifyBody {
|
||||
TemplateId: string;
|
||||
}
|
||||
|
||||
// export type SmsBodyType = ISmsVerifyBody;
|
||||
|
||||
export type TemplateParams = "Code";
|
||||
export type TemplateParams = "VERIFICATIONCODE";
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Role } from "../../users/entities/role.entity";
|
||||
import { RoleEnum } from "../../users/enums/role.enum";
|
||||
|
||||
export function checkUserRole(userRoles: Role[], requiredRole: RoleEnum) {
|
||||
if (!userRoles.some((role) => role.name === requiredRole)) return false;
|
||||
return true;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ export class SmsService {
|
||||
async sendSmsVerifyCode(mobile: string, otpCode: string) {
|
||||
//
|
||||
const smsData: ISmsVerifyBody = {
|
||||
Parameters: [{ name: "Code", value: otpCode }],
|
||||
Parameters: [{ name: "VERIFICATIONCODE", value: otpCode }],
|
||||
Mobile: mobile,
|
||||
TemplateId: this.smsConfigs.SMS_PATTERN_OTP,
|
||||
};
|
||||
@@ -30,7 +30,7 @@ export class SmsService {
|
||||
this.httpService
|
||||
.post<ISmsVerifyResponse>(
|
||||
`${this.smsConfigs.API_URL}/send/verify`,
|
||||
{ smsData },
|
||||
{ ...smsData },
|
||||
{
|
||||
headers: { "X-API-KEY": this.smsConfigs.API_KEY },
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user