refactor: changed the project structure
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Exclude } from 'class-transformer';
|
||||
import {
|
||||
IsEmail,
|
||||
IsNotEmpty,
|
||||
IsString,
|
||||
Matches,
|
||||
MaxLength,
|
||||
MinLength,
|
||||
} from 'class-validator';
|
||||
|
||||
export class CreateUserDto {
|
||||
@ApiProperty({ example: 'Mahdi' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(20)
|
||||
firstName: string;
|
||||
|
||||
@ApiProperty({ example: 'Rafie' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
lastName: string;
|
||||
|
||||
@ApiProperty({ example: 'mahdirafie@gmail.com' })
|
||||
@IsEmail()
|
||||
@MaxLength(100)
|
||||
email: string;
|
||||
|
||||
@ApiProperty({ example: '09123456789' })
|
||||
@Matches(/^09\d{9}$/, {
|
||||
message: 'Phone number must be a valid Iranian mobile number.',
|
||||
})
|
||||
phone: string;
|
||||
|
||||
@ApiProperty({ example: 'MahdiPassword@5!' })
|
||||
@IsString()
|
||||
@MinLength(8)
|
||||
password: string;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Entity, Column, ManyToMany, JoinTable, OneToMany } from 'typeorm';
|
||||
import { BaseEntity } from 'src/common/entities/base.entity';
|
||||
import { Exclude } from 'class-transformer';
|
||||
import { UserRole } from './user-role.entity';
|
||||
import { Complaint } from 'src/modules/complaint/entities/complaint.entity';
|
||||
|
||||
@Entity('users')
|
||||
export class User extends BaseEntity {
|
||||
@Column({ type: 'varchar', length: 20 })
|
||||
firstName: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 20 })
|
||||
lastName: string;
|
||||
|
||||
@Column({ unique: true, type: 'varchar', length: 100 })
|
||||
email: string;
|
||||
|
||||
@Column({ unique: true, type: 'varchar', length: 11 })
|
||||
phone: string;
|
||||
|
||||
@Column({})
|
||||
@Exclude()
|
||||
password: string;
|
||||
|
||||
@Column({
|
||||
name: 'refresh_token',
|
||||
nullable: true,
|
||||
select: false,
|
||||
})
|
||||
@Exclude()
|
||||
refreshToken: string;
|
||||
|
||||
@OneToMany(() => UserRole, (userRole) => userRole.user, {
|
||||
cascade: true
|
||||
})
|
||||
roles: UserRole[];
|
||||
|
||||
@OneToMany(() => Complaint, (complaint) => complaint.complainant, {
|
||||
cascade: true
|
||||
})
|
||||
complaints: Complaint[];
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Repository } from 'typeorm';
|
||||
import { User } from '../entities/user.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class UserRepository extends Repository<User> {
|
||||
constructor(@InjectRepository(User) userRepository: Repository<User>) {
|
||||
super(
|
||||
userRepository.target,
|
||||
userRepository.manager,
|
||||
userRepository.queryRunner,
|
||||
);
|
||||
}
|
||||
|
||||
findUserWithRolesByPhone(phone: string): Promise<User | null> {
|
||||
return this.createQueryBuilder('users')
|
||||
.leftJoinAndSelect('users.roles', 'roles')
|
||||
.where("users.phone = :phone", {phone})
|
||||
.select(
|
||||
[
|
||||
'users',
|
||||
'roles.id',
|
||||
'roles.role'
|
||||
]
|
||||
).getOne();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
|
||||
import { ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||
import { CreateUserDto } from './DTO/create-user.dto';
|
||||
import { User } from './entities/user.entity';
|
||||
import { UsersService } from './users.service';
|
||||
import { CreateUserRoleDto } from './DTO/create-user-role.dto';
|
||||
|
||||
@Controller('users')
|
||||
export class UsersController {
|
||||
|
||||
constructor(
|
||||
private readonly userService: UsersService
|
||||
) {}
|
||||
|
||||
@Post('signup')
|
||||
@ApiOperation({summary: 'Create a new user!'})
|
||||
@ApiResponse({status: 201, description: 'User created successfully!'})
|
||||
@ApiResponse({status: 400, description: 'Bad Request from User!'})
|
||||
signupUser(@Body() body: CreateUserDto): Promise<User> {
|
||||
return this.userService.createUser(body);
|
||||
}
|
||||
|
||||
@Post(':userId/roles')
|
||||
createRole(
|
||||
@Param('userId') userId: string,
|
||||
@Body() userRoleDto: CreateUserRoleDto
|
||||
) {
|
||||
return this.userService.createUserRole(userId, userRoleDto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { UsersController } from './users.controller';
|
||||
import { UsersService } from './users.service';
|
||||
import { UserRepository } from './repositories/user.repository';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { User } from './entities/user.entity';
|
||||
import { UserRoleRepository } from './repositories/user-role.repository';
|
||||
import { UserRole } from './entities/user-role.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([User, UserRole])],
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService, UserRepository, UserRoleRepository],
|
||||
exports: [UsersService],
|
||||
})
|
||||
export class UsersModule {}
|
||||
@@ -0,0 +1,117 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { UserRepository } from './repositories/user.repository';
|
||||
import { User } from './entities/user.entity';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import { CreateUserDto } from './DTO/create-user.dto';
|
||||
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()
|
||||
export class UsersService {
|
||||
constructor(
|
||||
private readonly userRepository: UserRepository,
|
||||
private readonly userRoleRepository: UserRoleRepository,
|
||||
) {}
|
||||
|
||||
async findOneOrFail(id: string): Promise<User> {
|
||||
const user = await this.userRepository.findOne({ where: { id } });
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundException(UserMessages.USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async findOneByPhoneOrFail(phone: string): Promise<User> {
|
||||
const user = await this.userRepository.findOne({
|
||||
where: { phone },
|
||||
relations: {
|
||||
roles: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundException(UserMessages.USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async findUserWithRolesByPhoneOrFail(phone: string): Promise<User> {
|
||||
const user = await this.userRepository.findUserWithRolesByPhone(phone);
|
||||
|
||||
if (!user)
|
||||
throw new NotFoundException(UserMessages.USER_NOT_FOUND);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async updateRefreshToken(id: string, newRefreshToken: string) {
|
||||
const user = await this.findOneOrFail(id);
|
||||
|
||||
user.refreshToken = newRefreshToken;
|
||||
await this.userRepository.save(user);
|
||||
}
|
||||
|
||||
async createUser(userDto: CreateUserDto): Promise<User> {
|
||||
const existingUser = await this.userRepository.findOne({
|
||||
where: [
|
||||
{
|
||||
phone: userDto.phone,
|
||||
},
|
||||
{
|
||||
email: userDto.email,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (existingUser)
|
||||
throw new BadRequestException(UserMessages.USER_ALREADY_EXISTS);
|
||||
|
||||
const hashedPassword: string = await bcrypt.hash(userDto.password, 10);
|
||||
|
||||
const user = this.userRepository.create({
|
||||
...userDto,
|
||||
password: hashedPassword,
|
||||
});
|
||||
|
||||
await this.userRepository.save(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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user