multi role
Build and Deploy Docker Images / build_and_deploy (push) Has been cancelled

This commit is contained in:
2026-07-22 12:19:37 +03:30
parent 10035f3f25
commit c925d2e539
6 changed files with 60 additions and 26 deletions
+3 -2
View File
@@ -7,6 +7,7 @@ import { SSOClientService } from "./sso-client.service";
import { TokensService } from "./tokens.service";
import { AuthMessage } from "../../../common/enums/message.enum";
import { User } from "../../users/entities/user.entity";
import { getPermissionsFromRoles } from "../../users/utils/role-permissions.util";
import { ITokenPayload } from "../interfaces/IToken-payload";
@Injectable()
@@ -46,7 +47,7 @@ export class SSOService {
const payload: ITokenPayload = {
id: user.id,
isAdmin: user.roles.some((r) => r.isAdmin),
permissions: user.roles.flatMap((r) => (r?.permissions?.length ? r.permissions.map((p) => p.name) : [])),
permissions: getPermissionsFromRoles(user.roles),
audience: clientId,
};
@@ -105,7 +106,7 @@ export class SSOService {
const payload: ITokenPayload = {
id: user.id,
isAdmin: user.roles.some((r) => r.isAdmin),
permissions: user.roles.flatMap((r) => (r?.permissions?.length ? r.permissions.map((p) => p.name) : [])),
permissions: getPermissionsFromRoles(user.roles),
audience: clientId,
};
+2 -1
View File
@@ -9,6 +9,7 @@ import { DataSource, QueryRunner } from "typeorm";
import { AuthMessage } from "../../../common/enums/message.enum";
import { User } from "../../users/entities/user.entity";
import { RefreshTokensRepository } from "../../users/repositories/refresh-token.repository";
import { getPermissionsFromRoles } from "../../users/utils/role-permissions.util";
import { ITokenPayload } from "../interfaces/IToken-payload";
import { ResellerType } from "../DTO/verify-reseller-otp.dto";
@@ -28,7 +29,7 @@ export class TokensService {
{
id: user.id,
isAdmin: user.roles.some((r) => r.isAdmin),
permissions: user.roles.flatMap((r) => (r?.permissions?.length ? r.permissions.map((p) => p.name) : [])),
permissions: getPermissionsFromRoles(user.roles),
},
queryRunner,
);
+6 -4
View File
@@ -1,5 +1,5 @@
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { IsEmail, IsMobilePhone, IsNotEmpty, IsOptional, IsString, IsUUID, IsUrl, Length, MinLength } from "class-validator";
import { ArrayNotEmpty, IsArray, IsEmail, IsMobilePhone, IsNotEmpty, IsOptional, IsString, IsUUID, IsUrl, Length, MinLength } from "class-validator";
import { AdminMessage, AuthMessage } from "../../../common/enums/message.enum";
@@ -22,9 +22,11 @@ export class CreateAdminDto {
phone: string;
@IsNotEmpty({ message: AdminMessage.ROLE_REQUIRED })
@IsUUID("4", { message: AdminMessage.ROLE_ID_SHOULD_BE_UUID })
@ApiProperty({ description: "Role id", example: "123e4567-e89b-12d3-a456-426614174000" })
roleId: string;
@IsArray({ message: AdminMessage.ROLE_REQUIRED })
@ArrayNotEmpty({ message: AdminMessage.ROLE_REQUIRED })
@IsUUID("4", { each: true, message: AdminMessage.ROLE_ID_SHOULD_BE_UUID })
@ApiProperty({ description: "Role ids", example: ["123e4567-e89b-12d3-a456-426614174000"], type: [String] })
roleIds: string[];
@IsNotEmpty({ message: AuthMessage.FIRST_NAME_NOT_EMPTY })
@IsString({ message: AuthMessage.FIRST_NAME_NOT_EMPTY })
+19 -19
View File
@@ -18,6 +18,7 @@ import { RoleEnum } from "../enums/role.enum";
import { PermissionsRepository } from "../repositories/permissions.repository";
import { RoleRepository } from "../repositories/roles.repository";
import { UserRepository } from "../repositories/users.repository";
import { getPermissionEntitiesFromRoles } from "../utils/role-permissions.util";
@Injectable()
export class AdminsService {
@@ -43,22 +44,22 @@ export class AdminsService {
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 },
const roles = await queryRunner.manager.find(this.rolesRepository.target, {
where: { id: In(createDto.roleIds), isAdmin: true },
relations: { permissions: true },
});
if (!role) throw new BadRequestException(AdsMessage.ROLE_NOT_FOUND);
if (roles.length !== createDto.roleIds.length) 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 { roleIds: _roleIds, repeatPassword: _repeatPassword, ...adminData } = createDto;
const adminUser = queryRunner.manager.create(this.userRepository.target, {
...createDto,
roles: [role],
...adminData,
roles,
password: hashedPassword,
userName,
});
@@ -75,7 +76,7 @@ export class AdminsService {
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin created new admin: ${createDto.email}`,
metadata: {
roleId: createDto.roleId,
roleIds: createDto.roleIds,
createdBy: "admin",
email: createDto.email,
phone: createDto.phone,
@@ -118,16 +119,14 @@ export class AdminsService {
if (existPhone) throw new BadRequestException(UserMessage.PHONE_EXIST);
}
if (updateDto.roleId) {
const role = await queryRunner.manager.findOne(this.rolesRepository.target, {
where: { id: updateDto.roleId },
if (updateDto.roleIds?.length) {
const roles = await queryRunner.manager.find(this.rolesRepository.target, {
where: { id: In(updateDto.roleIds), isAdmin: true },
relations: { permissions: true },
});
if (!role) throw new BadRequestException(AdsMessage.ROLE_NOT_FOUND);
if (roles.length !== updateDto.roleIds.length) throw new BadRequestException(AdsMessage.ROLE_NOT_FOUND);
await queryRunner.manager.save(this.rolesRepository.target, role);
admin.roles = [role];
admin.roles = roles;
await queryRunner.manager.save(this.userRepository.target, admin);
}
@@ -138,7 +137,8 @@ export class AdminsService {
admin.password = await this.passwordService.hashPassword(updateDto.password);
}
await queryRunner.manager.save(this.userRepository.target, { ...admin, ...updateDto, password: admin.password });
const { roleIds: _roleIds, repeatPassword: _repeatPassword, ...updateData } = updateDto;
await queryRunner.manager.save(this.userRepository.target, { ...admin, ...updateData, password: admin.password });
await queryRunner.commitTransaction();
await this.accessLogService.logUpdate("User", admin.id, UserType.ADMIN, {
@@ -150,7 +150,7 @@ export class AdminsService {
userType: UserType.ADMIN,
actionDescription: `Admin updated admin: ${admin.email}`,
metadata: {
roleId: updateDto.roleId,
roleIds: updateDto.roleIds,
},
oldValues: JSON.stringify({ ...admin }),
newValues: JSON.stringify({ ...updateDto }),
@@ -169,7 +169,7 @@ export class AdminsService {
/************************************************************ */
async deleteAdmin(id: string, userIpAndHeaders: IUserIpAndHeaders) {
const admin = await this.userRepository.findOne({ where: { id, roles: { isAdmin: true } } });
const admin = await this.userRepository.findOne({ where: { id, roles: { isAdmin: true } }, relations: { roles: true } });
if (!admin) throw new BadRequestException(AdminMessage.ADMIN_NOT_FOUND);
await this.userRepository.softDelete(id);
@@ -182,7 +182,7 @@ export class AdminsService {
userAgent: userIpAndHeaders.headers["user-agent"],
actionDescription: `Admin deleted admin: ${admin.email}`,
metadata: {
roleId: admin.roles[0].id,
roleIds: admin.roles.map((role) => role.id),
email: admin.email,
phone: admin.phone,
},
@@ -303,7 +303,7 @@ export class AdminsService {
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
return {
permissions: user.roles[0].permissions,
permissions: getPermissionEntitiesFromRoles(user.roles),
};
}
/************************************************************ */
@@ -0,0 +1,11 @@
{
"folders": [
{
"path": "../../../.."
},
{
"path": "../../../../../danak-admin"
}
],
"settings": {}
}
@@ -0,0 +1,19 @@
import { PermissionEnum } from "../enums/permission.enum";
import { Role } from "../entities/role.entity";
import { Permission } from "../entities/permission.entity";
export function getPermissionsFromRoles(roles: Role[]): PermissionEnum[] {
return [...new Set(roles.flatMap((role) => (role?.permissions?.length ? role.permissions.map((permission) => permission.name) : [])))];
}
export function getPermissionEntitiesFromRoles(roles: Role[]): Permission[] {
const permissionsById = new Map<string, Permission>();
for (const role of roles) {
for (const permission of role?.permissions ?? []) {
permissionsById.set(permission.id, permission);
}
}
return [...permissionsById.values()];
}