feat: added user role

This commit is contained in:
2026-07-24 20:39:10 +03:30
parent 71529f7d24
commit cefddfeeda
11 changed files with 144 additions and 35 deletions
+2 -1
View File
@@ -9,10 +9,11 @@ import { OTP } from './entities/otp.entity';
import { OtpRepository } from './repositories/otp.repository'; import { OtpRepository } from './repositories/otp.repository';
import { UsersModule } from 'src/users/users.module'; import { UsersModule } from 'src/users/users.module';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { UserRole } from 'src/users/entities/user-role.entity';
@Module({ @Module({
imports: [ imports: [
TypeOrmModule.forFeature([User, OTP]), TypeOrmModule.forFeature([OTP]),
JwtModule.register({}), JwtModule.register({}),
UtilsModule, UtilsModule,
UsersModule, UsersModule,
+4
View File
@@ -10,4 +10,8 @@ export enum OTPMessages {
OTP_EXPIRED = 'این کد منسوخ شده است لطفا دوباره درخواست بدهید!', OTP_EXPIRED = 'این کد منسوخ شده است لطفا دوباره درخواست بدهید!',
OTP_TOO_MANY_TRIES = 'تعداد تلاش ها بیش از حد مجاز است لطفا دوباره درخواست کد بدهید!', OTP_TOO_MANY_TRIES = 'تعداد تلاش ها بیش از حد مجاز است لطفا دوباره درخواست کد بدهید!',
OTP_INVALID = 'کد وارد شده نا معتبر است!' OTP_INVALID = 'کد وارد شده نا معتبر است!'
}
export enum UserRoleMessages {
USER_ROLE_ALREADY_EXISTS = 'نقش مورد نظر برای کاربر مد نظر وجود دارد!'
} }
+12
View File
@@ -0,0 +1,12 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsEnum } from "class-validator";
import { RoleType } from "../enums/user-role.enum";
export class CreateUserRoleDto {
@ApiProperty({
enum: RoleType,
example: RoleType.NORMAL_USER
})
@IsEnum(RoleType)
role: RoleType;
}
+22
View File
@@ -0,0 +1,22 @@
import { BaseEntity } from "src/common/entities/base.entity";
import { Column, Entity, JoinColumn, ManyToOne, Unique } from "typeorm";
import { RoleType } from "../enums/user-role.enum";
import { User } from "./user.entity";
@Entity('user_roles')
@Unique(['user', 'role'])
export class UserRole extends BaseEntity {
@Column({
type: 'enum',
enum: RoleType,
default: RoleType.NORMAL_USER
})
role: RoleType;
@ManyToOne(() => User, (user) => user.roles, {
nullable: false,
onDelete: 'CASCADE'
})
@JoinColumn({ name: 'userId' })
user: User;
}
+28 -23
View File
@@ -1,31 +1,36 @@
import { Entity, Column } from "typeorm"; import { Entity, Column, ManyToMany, JoinTable, OneToMany } from 'typeorm';
import { BaseEntity } from "src/common/entities/base.entity"; import { BaseEntity } from 'src/common/entities/base.entity';
import { Exclude } from "class-transformer"; import { Exclude } from 'class-transformer';
import { UserRole } from './user-role.entity';
@Entity('users') @Entity('users')
export class User extends BaseEntity { export class User extends BaseEntity {
@Column({type: 'varchar', length: 20}) @Column({ type: 'varchar', length: 20 })
firstName: string; firstName: string;
@Column({type: 'varchar', length: 20}) @Column({ type: 'varchar', length: 20 })
lastName: string; lastName: string;
@Column({unique: true, type: 'varchar', length: 100}) @Column({ unique: true, type: 'varchar', length: 100 })
email: string; email: string;
@Column({unique: true, type: 'varchar', length: 11}) @Column({ unique: true, type: 'varchar', length: 11 })
phone: string; phone: string;
@Column({ @Column({})
}) @Exclude()
@Exclude() password: string;
password: string;
@Column({ @Column({
name: 'refresh_token', name: 'refresh_token',
nullable: true, nullable: true,
select: false select: false,
}) })
@Exclude() @Exclude()
refreshToken: string; refreshToken: string;
}
@OneToMany(() => UserRole, (userRole) => userRole.user, {
cascade: true
})
roles: UserRole[];
}
+5
View File
@@ -0,0 +1,5 @@
export enum RoleType {
NORMAL_USER = 'normal_user',
ADMIN = 'admin',
EXPERT = 'expert'
}
@@ -0,0 +1,17 @@
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Injectable } from '@nestjs/common';
import { UserRole } from '../entities/user-role.entity';
@Injectable()
export class UserRoleRepository extends Repository<UserRole> {
constructor(
@InjectRepository(UserRole) userRoleRepository: Repository<UserRole>,
) {
super(
userRoleRepository.target,
userRoleRepository.manager,
userRoleRepository.queryRunner,
);
}
}
@@ -1,5 +1,5 @@
import { Repository } from "typeorm"; import { Repository } from "typeorm";
import { User } from "./entities/user.entity"; import { User } from "../entities/user.entity";
import { InjectRepository } from "@nestjs/typeorm"; import { InjectRepository } from "@nestjs/typeorm";
import { Injectable } from "@nestjs/common"; import { Injectable } from "@nestjs/common";
+10 -1
View File
@@ -1,8 +1,9 @@
import { Body, Controller, Get, Post } from '@nestjs/common'; import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { ApiOperation, ApiResponse } from '@nestjs/swagger'; import { ApiOperation, ApiResponse } from '@nestjs/swagger';
import { CreateUserDto } from './DTO/create-user.dto'; import { CreateUserDto } from './DTO/create-user.dto';
import { User } from './entities/user.entity'; import { User } from './entities/user.entity';
import { UsersService } from './users.service'; import { UsersService } from './users.service';
import { CreateUserRoleDto } from './DTO/create-user-role.dto';
@Controller('users') @Controller('users')
export class UsersController { export class UsersController {
@@ -18,4 +19,12 @@ export class UsersController {
signupUser(@Body() body: CreateUserDto): Promise<User> { signupUser(@Body() body: CreateUserDto): Promise<User> {
return this.userService.createUser(body); return this.userService.createUser(body);
} }
@Post(':userId/roles')
createRole(
@Param('userId') userId: string,
@Body() userRoleDto: CreateUserRoleDto
) {
return this.userService.createUserRole(userId, userRoleDto);
}
} }
+6 -6
View File
@@ -1,16 +1,16 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { UsersController } from './users.controller'; import { UsersController } from './users.controller';
import { UsersService } from './users.service'; import { UsersService } from './users.service';
import { UserRepository } from './user.repository'; import { UserRepository } from './repositories/user.repository';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity'; import { User } from './entities/user.entity';
import { UserRoleRepository } from './repositories/user-role.repository';
import { UserRole } from './entities/user-role.entity';
@Module({ @Module({
imports: [ imports: [TypeOrmModule.forFeature([User, UserRole])],
TypeOrmModule.forFeature([User])
],
controllers: [UsersController], controllers: [UsersController],
providers: [UsersService, UserRepository], providers: [UsersService, UserRepository, UserRoleRepository],
exports: [UsersService] exports: [UsersService],
}) })
export class UsersModule {} export class UsersModule {}
+37 -3
View File
@@ -3,15 +3,21 @@ import {
Injectable, Injectable,
NotFoundException, NotFoundException,
} from '@nestjs/common'; } from '@nestjs/common';
import { UserRepository } from './user.repository'; import { UserRepository } from './repositories/user.repository';
import { User } from './entities/user.entity'; import { User } from './entities/user.entity';
import * as bcrypt from 'bcrypt'; import * as bcrypt from 'bcrypt';
import { CreateUserDto } from './DTO/create-user.dto'; import { CreateUserDto } from './DTO/create-user.dto';
import { UserMessages } from 'src/common/enums/messages.enum'; import { UserMessages, UserRoleMessages } from 'src/common/enums/messages.enum';
import { UserRole } from './entities/user-role.entity';
import { CreateUserRoleDto } from './DTO/create-user-role.dto';
import { UserRoleRepository } from './repositories/user-role.repository';
@Injectable() @Injectable()
export class UsersService { export class UsersService {
constructor(private readonly userRepository: UserRepository) {} constructor(
private readonly userRepository: UserRepository,
private readonly userRoleRepository: UserRoleRepository,
) {}
async findOneOrFail(id: string): Promise<User> { async findOneOrFail(id: string): Promise<User> {
const user = await this.userRepository.findOne({ where: { id } }); const user = await this.userRepository.findOne({ where: { id } });
@@ -59,4 +65,32 @@ export class UsersService {
return user; return user;
} }
async createUserRole(
userId: string,
userRoleDto: CreateUserRoleDto,
): Promise<UserRole> {
const existingRole = await this.userRoleRepository.findOne({
where: {
user: {
id: userId,
},
role: userRoleDto.role,
},
});
if (existingRole)
throw new BadRequestException(UserRoleMessages.USER_ROLE_ALREADY_EXISTS);
const userRole = this.userRoleRepository.create({
user: {
id: userId,
},
role: userRoleDto.role,
});
await this.userRoleRepository.save(userRole);
return userRole;
}
} }