Spaces:
Sleeping
Sleeping
update user info, create validate service
Browse files- backend/src/modules/authentication/authentication.module.ts +3 -1
- backend/src/modules/authentication/authentication.service.ts +8 -4
- backend/src/modules/authentication/rbac-policy.ts +0 -9
- backend/src/modules/user/dto/update-user-dto.ts +31 -0
- backend/src/modules/user/user.controller.ts +9 -1
- backend/src/modules/user/user.module.ts +16 -1
- backend/src/modules/user/user.service.ts +36 -2
- backend/src/validate/validate.service.spec.ts +18 -0
- backend/src/validate/validate.service.ts +25 -0
backend/src/modules/authentication/authentication.module.ts
CHANGED
@@ -7,6 +7,7 @@ import { AuthenticationGuard } from './authentication.guard.js';
|
|
7 |
import { APP_GUARD } from '@nestjs/core';
|
8 |
import { ConfigModule, ConfigService } from '@nestjs/config';
|
9 |
import { RolesGuard } from './authorization/roles.guard.js';
|
|
|
10 |
@Module({
|
11 |
imports: [
|
12 |
UserModule,
|
@@ -28,7 +29,8 @@ import { RolesGuard } from './authorization/roles.guard.js';
|
|
28 |
{
|
29 |
provide: APP_GUARD,
|
30 |
useClass: RolesGuard,
|
31 |
-
}
|
|
|
32 |
],
|
33 |
controllers: [AuthenticationController],
|
34 |
exports: [AuthenticationService],
|
|
|
7 |
import { APP_GUARD } from '@nestjs/core';
|
8 |
import { ConfigModule, ConfigService } from '@nestjs/config';
|
9 |
import { RolesGuard } from './authorization/roles.guard.js';
|
10 |
+
import { ValidateService } from '../../validate/validate.service.js';
|
11 |
@Module({
|
12 |
imports: [
|
13 |
UserModule,
|
|
|
29 |
{
|
30 |
provide: APP_GUARD,
|
31 |
useClass: RolesGuard,
|
32 |
+
},
|
33 |
+
ValidateService
|
34 |
],
|
35 |
controllers: [AuthenticationController],
|
36 |
exports: [AuthenticationService],
|
backend/src/modules/authentication/authentication.service.ts
CHANGED
@@ -4,11 +4,13 @@ import * as bcrypt from 'bcrypt';
|
|
4 |
import { JwtService } from '@nestjs/jwt';
|
5 |
import { SignUpDto } from './dto/sign-up.dto.js';
|
6 |
import { UserEntity } from '../../entities/user.entity.js';
|
|
|
7 |
@Injectable()
|
8 |
export class AuthenticationService {
|
9 |
constructor(
|
10 |
private usersService: UserService,
|
11 |
private jwtService: JwtService,
|
|
|
12 |
) {}
|
13 |
|
14 |
async signIn(
|
@@ -20,10 +22,12 @@ export class AuthenticationService {
|
|
20 |
user = await this.usersService.findOneByField('email', username);
|
21 |
else
|
22 |
user = await this.usersService.findOneByField('phone_number', username);
|
23 |
-
|
|
|
|
|
24 |
throw new UnauthorizedException();
|
25 |
}
|
26 |
-
const payload = { sub: user.id, username: user.
|
27 |
return {
|
28 |
access_token: await this.jwtService.signAsync(payload),
|
29 |
};
|
@@ -39,8 +43,8 @@ export class AuthenticationService {
|
|
39 |
password
|
40 |
} = signUpDto
|
41 |
|
42 |
-
await this.checkExistField('email', email);
|
43 |
-
await this.checkExistField('phone_number', phone_number)
|
44 |
const hash_password = await bcrypt.hash(password, 10)
|
45 |
|
46 |
const user = await this.usersService.create({
|
|
|
4 |
import { JwtService } from '@nestjs/jwt';
|
5 |
import { SignUpDto } from './dto/sign-up.dto.js';
|
6 |
import { UserEntity } from '../../entities/user.entity.js';
|
7 |
+
import { ValidateService } from '../../validate/validate.service.js';
|
8 |
@Injectable()
|
9 |
export class AuthenticationService {
|
10 |
constructor(
|
11 |
private usersService: UserService,
|
12 |
private jwtService: JwtService,
|
13 |
+
private validatService: ValidateService
|
14 |
) {}
|
15 |
|
16 |
async signIn(
|
|
|
22 |
user = await this.usersService.findOneByField('email', username);
|
23 |
else
|
24 |
user = await this.usersService.findOneByField('phone_number', username);
|
25 |
+
const compare = await bcrypt.compare(pass, user.hash_password)
|
26 |
+
console.log(compare);
|
27 |
+
if (( !user || !compare )) {
|
28 |
throw new UnauthorizedException();
|
29 |
}
|
30 |
+
const payload = { sub: user.id, username: user.full_name, roles: user.role.role_name};
|
31 |
return {
|
32 |
access_token: await this.jwtService.signAsync(payload),
|
33 |
};
|
|
|
43 |
password
|
44 |
} = signUpDto
|
45 |
|
46 |
+
await this.validatService.checkExistField('email', email);
|
47 |
+
await this.validatService.checkExistField('phone_number', phone_number)
|
48 |
const hash_password = await bcrypt.hash(password, 10)
|
49 |
|
50 |
const user = await this.usersService.create({
|
backend/src/modules/authentication/rbac-policy.ts
DELETED
@@ -1,9 +0,0 @@
|
|
1 |
-
import { RolesBuilder } from 'nest-access-control';
|
2 |
-
import { Role } from './enums';
|
3 |
-
|
4 |
-
export const RBAC_POLICY: RolesBuilder = new RolesBuilder();
|
5 |
-
|
6 |
-
// prettier-ignore
|
7 |
-
RBAC_POLICY
|
8 |
-
.grant(Role.ADMIN)
|
9 |
-
.read()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/src/modules/user/dto/update-user-dto.ts
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { IsEmail, IsMobilePhone, IsOptional, IsString } from "class-validator";
|
2 |
+
import { Column } from "typeorm";
|
3 |
+
|
4 |
+
export class UpdateUserDto{
|
5 |
+
|
6 |
+
@IsOptional()
|
7 |
+
avatar: string;
|
8 |
+
|
9 |
+
@IsOptional()
|
10 |
+
full_name: string;
|
11 |
+
|
12 |
+
@IsOptional()
|
13 |
+
@IsMobilePhone('vi-VN')
|
14 |
+
phone_number: string;
|
15 |
+
|
16 |
+
@IsOptional()
|
17 |
+
address: string;
|
18 |
+
|
19 |
+
@IsOptional()
|
20 |
+
@IsEmail()
|
21 |
+
email: string;
|
22 |
+
|
23 |
+
@IsOptional()
|
24 |
+
role_id: string;
|
25 |
+
|
26 |
+
@IsOptional()
|
27 |
+
hash_password: string;
|
28 |
+
|
29 |
+
@IsOptional()
|
30 |
+
is_valid: boolean;
|
31 |
+
}
|
backend/src/modules/user/user.controller.ts
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
-
import { Controller, Get, Param } from '@nestjs/common';
|
2 |
import { UserService } from './user.service.js';
|
3 |
import { UserEntity } from 'src/entities/user.entity.js';
|
|
|
4 |
|
5 |
@Controller('users')
|
6 |
export class UsersController {
|
@@ -12,4 +13,11 @@ export class UsersController {
|
|
12 |
): Promise<UserEntity | undefined> {
|
13 |
return this.usersService.findOne(username);
|
14 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
}
|
|
|
1 |
+
import { Body, Controller, Get, Param, Post, Request } from '@nestjs/common';
|
2 |
import { UserService } from './user.service.js';
|
3 |
import { UserEntity } from 'src/entities/user.entity.js';
|
4 |
+
import { UpdateUserDto } from './dto/update-user-dto.js';
|
5 |
|
6 |
@Controller('users')
|
7 |
export class UsersController {
|
|
|
13 |
): Promise<UserEntity | undefined> {
|
14 |
return this.usersService.findOne(username);
|
15 |
}
|
16 |
+
|
17 |
+
@Post('updateUser')
|
18 |
+
async updateUser(@Request() req){
|
19 |
+
const userId = req.user.sub;
|
20 |
+
const updateUserDto = req.body;
|
21 |
+
return this.usersService.updateUserById(userId, updateUserDto);
|
22 |
+
}
|
23 |
}
|
backend/src/modules/user/user.module.ts
CHANGED
@@ -1,8 +1,23 @@
|
|
1 |
import { Module } from '@nestjs/common';
|
2 |
import { UserService } from './user.service.js';
|
|
|
|
|
|
|
|
|
3 |
|
4 |
@Module({
|
5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
exports: [UserService],
|
7 |
})
|
8 |
export class UserModule {}
|
|
|
1 |
import { Module } from '@nestjs/common';
|
2 |
import { UserService } from './user.service.js';
|
3 |
+
import { ValidateService } from '../../validate/validate.service.js';
|
4 |
+
import { UsersController } from './user.controller.js';
|
5 |
+
import { JwtModule } from '@nestjs/jwt';
|
6 |
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
7 |
|
8 |
@Module({
|
9 |
+
imports: [
|
10 |
+
JwtModule.registerAsync({
|
11 |
+
imports: [ConfigModule], // Nhập ConfigModule để sử dụng ConfigService
|
12 |
+
useFactory: async (configService: ConfigService) => ({
|
13 |
+
secret: configService.get<string>('JWT_KEY'), // Lấy giá trị từ biến môi trường
|
14 |
+
signOptions: { expiresIn: '7d' }, // Thời gian hết hạn token
|
15 |
+
}),
|
16 |
+
inject: [ConfigService], // Inject ConfigService vào factory
|
17 |
+
}),
|
18 |
+
],
|
19 |
+
controllers: [UsersController],
|
20 |
+
providers: [UserService, ValidateService],
|
21 |
exports: [UserService],
|
22 |
})
|
23 |
export class UserModule {}
|
backend/src/modules/user/user.service.ts
CHANGED
@@ -1,12 +1,19 @@
|
|
1 |
-
import { Injectable } from '@nestjs/common';
|
2 |
import { UserEntity } from '../../entities/user.entity.js';
|
3 |
import { SignUpDto } from '../authentication/dto/sign-up.dto.js';
|
|
|
|
|
|
|
|
|
4 |
|
5 |
export type User = any;
|
6 |
|
7 |
@Injectable()
|
8 |
export class UserService {
|
9 |
-
constructor(
|
|
|
|
|
|
|
10 |
|
11 |
async findOne(username: string): Promise<UserEntity | undefined> {
|
12 |
return UserEntity.findOne({ where: { full_name: username } });
|
@@ -31,4 +38,31 @@ export class UserService {
|
|
31 |
relations: ['role']
|
32 |
});
|
33 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
}
|
|
|
1 |
+
import { Body, forwardRef, Inject, Injectable, NotFoundException } from '@nestjs/common';
|
2 |
import { UserEntity } from '../../entities/user.entity.js';
|
3 |
import { SignUpDto } from '../authentication/dto/sign-up.dto.js';
|
4 |
+
import { UpdateUserDto } from './dto/update-user-dto.js';
|
5 |
+
import { ValidateService } from '../../validate/validate.service.js';
|
6 |
+
import * as bcrypt from 'bcrypt';
|
7 |
+
import { JwtService } from '@nestjs/jwt';
|
8 |
|
9 |
export type User = any;
|
10 |
|
11 |
@Injectable()
|
12 |
export class UserService {
|
13 |
+
constructor(
|
14 |
+
private validateService: ValidateService,
|
15 |
+
private jwtService: JwtService,
|
16 |
+
) {}
|
17 |
|
18 |
async findOne(username: string): Promise<UserEntity | undefined> {
|
19 |
return UserEntity.findOne({ where: { full_name: username } });
|
|
|
38 |
relations: ['role']
|
39 |
});
|
40 |
}
|
41 |
+
|
42 |
+
async updateUserById(userId: string, updateUserDto: UpdateUserDto){
|
43 |
+
|
44 |
+
await this.validateService.checkExistField('email', updateUserDto.email);
|
45 |
+
await this.validateService.checkExistField('phone_number', updateUserDto.phone_number);
|
46 |
+
|
47 |
+
const user = await UserEntity.findOne({
|
48 |
+
where: { id: userId },
|
49 |
+
relations: ['role']
|
50 |
+
});
|
51 |
+
if (!user) {
|
52 |
+
throw new NotFoundException(`User with ID ${userId} not found`);
|
53 |
+
}
|
54 |
+
|
55 |
+
Object.assign(user, updateUserDto);
|
56 |
+
if (updateUserDto.hash_password) {
|
57 |
+
const saltRounds = 10;
|
58 |
+
user.hash_password = await bcrypt.hash(updateUserDto.hash_password, saltRounds); // Mã hóa mật khẩu
|
59 |
+
}
|
60 |
+
await UserEntity.save(user);
|
61 |
+
|
62 |
+
const payload = { sub: user.id, username: user.full_name, roles: user.role.role_name };
|
63 |
+
const token = await this.jwtService.signAsync(payload)
|
64 |
+
return {
|
65 |
+
access_token: token
|
66 |
+
};
|
67 |
+
}
|
68 |
}
|
backend/src/validate/validate.service.spec.ts
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Test, TestingModule } from '@nestjs/testing';
|
2 |
+
import { ValidateService } from './validate.service';
|
3 |
+
|
4 |
+
describe('ValidateService', () => {
|
5 |
+
let service: ValidateService;
|
6 |
+
|
7 |
+
beforeEach(async () => {
|
8 |
+
const module: TestingModule = await Test.createTestingModule({
|
9 |
+
providers: [ValidateService],
|
10 |
+
}).compile();
|
11 |
+
|
12 |
+
service = module.get<ValidateService>(ValidateService);
|
13 |
+
});
|
14 |
+
|
15 |
+
it('should be defined', () => {
|
16 |
+
expect(service).toBeDefined();
|
17 |
+
});
|
18 |
+
});
|
backend/src/validate/validate.service.ts
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// validation.service.ts
|
2 |
+
import { Injectable, ConflictException } from '@nestjs/common';
|
3 |
+
import { UserEntity } from '../entities/user.entity.js';
|
4 |
+
|
5 |
+
@Injectable()
|
6 |
+
export class ValidateService {
|
7 |
+
constructor() {}
|
8 |
+
|
9 |
+
// Kiểm tra xem giá trị của một trường có tồn tại không
|
10 |
+
async checkExistField(fieldName: string, fieldValue: any): Promise<void> {
|
11 |
+
if (fieldValue === null || fieldValue === undefined) {
|
12 |
+
return; // Nếu giá trị null hoặc undefined, không cần kiểm tra
|
13 |
+
}
|
14 |
+
|
15 |
+
// Tìm trong database theo tên và giá trị của trường
|
16 |
+
const existingUser = await UserEntity.findOne({
|
17 |
+
where: { [fieldName]: fieldValue },
|
18 |
+
relations: ['role']
|
19 |
+
});
|
20 |
+
|
21 |
+
if (existingUser) {
|
22 |
+
throw new ConflictException(`${fieldName} is already taken`);
|
23 |
+
}
|
24 |
+
}
|
25 |
+
}
|