Spaces:
Sleeping
Sleeping
Merge pull request #15 from PBL6-team-CATS/fix/config-env
Browse files- backend/.gitignore +1 -0
- backend/package-lock.json +1 -0
- backend/src/entities/user.entity.ts +8 -1
- backend/src/modules/authentication/README.md +33 -0
- backend/src/modules/authentication/authentication.controller.ts +8 -1
- backend/src/modules/authentication/authentication.guard.ts +4 -2
- backend/src/modules/authentication/authentication.module.ts +8 -5
- backend/src/modules/authentication/authentication.service.ts +31 -0
- backend/src/modules/authentication/constants.ts +0 -5
- backend/src/modules/authentication/dto/sign-in.dto.ts +2 -1
- backend/src/modules/authentication/dto/sign-up.dto.ts +24 -0
- backend/src/modules/user/user.service.ts +15 -1
backend/.gitignore
CHANGED
@@ -54,3 +54,4 @@ pids
|
|
54 |
|
55 |
# Diagnostic reports (https://nodejs.org/api/report.html)
|
56 |
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
|
|
|
54 |
|
55 |
# Diagnostic reports (https://nodejs.org/api/report.html)
|
56 |
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
57 |
+
src/modules/authentication/constants.ts
|
backend/package-lock.json
CHANGED
@@ -3993,6 +3993,7 @@
|
|
3993 |
"version": "16.4.5",
|
3994 |
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
|
3995 |
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
|
|
|
3996 |
"engines": {
|
3997 |
"node": ">=12"
|
3998 |
},
|
|
|
3993 |
"version": "16.4.5",
|
3994 |
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
|
3995 |
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
|
3996 |
+
"license": "BSD-2-Clause",
|
3997 |
"engines": {
|
3998 |
"node": ">=12"
|
3999 |
},
|
backend/src/entities/user.entity.ts
CHANGED
@@ -10,12 +10,14 @@ import {
|
|
10 |
} from 'typeorm';
|
11 |
import { BranchEntity } from './branch.entity.js';
|
12 |
import { RoleEntity } from './role.entity.js';
|
|
|
13 |
|
14 |
@Entity('users')
|
15 |
export class UserEntity extends BaseEntity {
|
16 |
@PrimaryGeneratedColumn('uuid')
|
17 |
id: string;
|
18 |
|
|
|
19 |
@Column({ nullable: true })
|
20 |
avatar: string;
|
21 |
|
@@ -31,21 +33,26 @@ export class UserEntity extends BaseEntity {
|
|
31 |
@Column({ nullable: true })
|
32 |
email: string;
|
33 |
|
|
|
34 |
@Column({ nullable: true })
|
35 |
role_id: number;
|
36 |
-
|
37 |
@Column({ nullable: true })
|
38 |
hash_password: string;
|
39 |
|
|
|
40 |
@Column({ default: true })
|
41 |
is_valid: boolean;
|
42 |
|
|
|
43 |
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
|
44 |
create_at: Date;
|
45 |
|
|
|
46 |
@OneToMany(() => BranchEntity, (branch) => branch.owner)
|
47 |
branches: Relation<BranchEntity>[];
|
48 |
|
|
|
49 |
@ManyToOne(() => RoleEntity)
|
50 |
@JoinColumn({ name: 'role_id'})
|
51 |
role: RoleEntity;
|
|
|
10 |
} from 'typeorm';
|
11 |
import { BranchEntity } from './branch.entity.js';
|
12 |
import { RoleEntity } from './role.entity.js';
|
13 |
+
import { IsOptional } from 'class-validator';
|
14 |
|
15 |
@Entity('users')
|
16 |
export class UserEntity extends BaseEntity {
|
17 |
@PrimaryGeneratedColumn('uuid')
|
18 |
id: string;
|
19 |
|
20 |
+
@IsOptional()
|
21 |
@Column({ nullable: true })
|
22 |
avatar: string;
|
23 |
|
|
|
33 |
@Column({ nullable: true })
|
34 |
email: string;
|
35 |
|
36 |
+
@IsOptional()
|
37 |
@Column({ nullable: true })
|
38 |
role_id: number;
|
39 |
+
|
40 |
@Column({ nullable: true })
|
41 |
hash_password: string;
|
42 |
|
43 |
+
@IsOptional()
|
44 |
@Column({ default: true })
|
45 |
is_valid: boolean;
|
46 |
|
47 |
+
@IsOptional()
|
48 |
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
|
49 |
create_at: Date;
|
50 |
|
51 |
+
@IsOptional()
|
52 |
@OneToMany(() => BranchEntity, (branch) => branch.owner)
|
53 |
branches: Relation<BranchEntity>[];
|
54 |
|
55 |
+
@IsOptional()
|
56 |
@ManyToOne(() => RoleEntity)
|
57 |
@JoinColumn({ name: 'role_id'})
|
58 |
role: RoleEntity;
|
backend/src/modules/authentication/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Set up
|
2 |
+
Hiện tại cái **JWT_KEY** trong file **.env** vì lý do gì đó mà lúc đọc vào thì có lúc undefine, lúc ok nên t để cái JWT_KEY trong file constants.ts (ni t ignore cái file nớ rou)
|
3 |
+
* Vào src/modules/authentication tạo file **constants.ts** với nội dung sau:
|
4 |
+
```javascript
|
5 |
+
export const jwtConstants = {
|
6 |
+
secret: '',
|
7 |
+
};
|
8 |
+
```
|
9 |
+
Trong đó **secret** là một cái JWT key t sẽ cung cấp sau.
|
10 |
+
|
11 |
+
## Signup
|
12 |
+
**Url:** *http://localhost:3000/authentication/signup* <br>
|
13 |
+
**Method:** POST <br>
|
14 |
+
**Input:**
|
15 |
+
* full_name: Tên của người dùng
|
16 |
+
* Email: Tài khoản email, hiện đang để optional do chưa xử lý cái unique
|
17 |
+
* Số điện thoại: Số điện thoại đang để check mã vùng ở Việt Nam
|
18 |
+
* password: Độ dài ít nhất 8 ký tự <br>
|
19 |
+
**Output:**
|
20 |
+
* access_token
|
21 |
+
|
22 |
+
## Login
|
23 |
+
**Url:** *http://localhost:3000/authentication/login* <br>
|
24 |
+
**Method:** POST <br>
|
25 |
+
**Input:**
|
26 |
+
* username: hiện đang để là full_name, sau ni sẽ sửa lại database với code để email và số điện thoại là unique
|
27 |
+
* password: Độ dài ít nhất 8 ký tự
|
28 |
+
**Output:** <br>
|
29 |
+
* access_token
|
30 |
+
|
31 |
+
|
32 |
+
|
33 |
+
|
backend/src/modules/authentication/authentication.controller.ts
CHANGED
@@ -11,7 +11,7 @@ import { AuthenticationGuard } from './authentication.guard.js';
|
|
11 |
import { AuthenticationService } from './authentication.service.js';
|
12 |
import { Public } from './authentication.decorator.js';
|
13 |
import { SignInDto } from './dto/sign-in.dto.js';
|
14 |
-
|
15 |
@Controller('authentication')
|
16 |
export class AuthenticationController {
|
17 |
constructor(private AuthenticationService: AuthenticationService) {}
|
@@ -23,6 +23,13 @@ export class AuthenticationController {
|
|
23 |
return this.AuthenticationService.signIn(signInDto.username, signInDto.password);
|
24 |
}
|
25 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
// @UseGuards(AuthenticationGuard)
|
27 |
@Get('profile')
|
28 |
getProfile(@Request() req) {
|
|
|
11 |
import { AuthenticationService } from './authentication.service.js';
|
12 |
import { Public } from './authentication.decorator.js';
|
13 |
import { SignInDto } from './dto/sign-in.dto.js';
|
14 |
+
import { SignUpDto } from './dto/sign-up.dto.js';
|
15 |
@Controller('authentication')
|
16 |
export class AuthenticationController {
|
17 |
constructor(private AuthenticationService: AuthenticationService) {}
|
|
|
23 |
return this.AuthenticationService.signIn(signInDto.username, signInDto.password);
|
24 |
}
|
25 |
|
26 |
+
@Public()
|
27 |
+
@HttpCode(HttpStatus.OK)
|
28 |
+
@Post('signup')
|
29 |
+
signUp(@Body() signUpDto: SignUpDto) {
|
30 |
+
return this.AuthenticationService.signUp(signUpDto);
|
31 |
+
}
|
32 |
+
|
33 |
// @UseGuards(AuthenticationGuard)
|
34 |
@Get('profile')
|
35 |
getProfile(@Request() req) {
|
backend/src/modules/authentication/authentication.guard.ts
CHANGED
@@ -5,15 +5,17 @@ import {
|
|
5 |
UnauthorizedException,
|
6 |
} from '@nestjs/common';
|
7 |
import { JwtService } from '@nestjs/jwt';
|
8 |
-
import { jwtConstants } from './constants.js';
|
9 |
import { Request } from 'express';
|
10 |
import { Reflector } from '@nestjs/core';
|
11 |
import { IS_PUBLIC_KEY } from './authentication.decorator.js';
|
|
|
|
|
12 |
@Injectable()
|
13 |
export class AuthenticationGuard implements CanActivate {
|
14 |
constructor(
|
15 |
private jwtService: JwtService,
|
16 |
private reflector: Reflector,
|
|
|
17 |
) {}
|
18 |
|
19 |
async canActivate(context: ExecutionContext): Promise<boolean> {
|
@@ -33,7 +35,7 @@ export class AuthenticationGuard implements CanActivate {
|
|
33 |
}
|
34 |
try {
|
35 |
const payload = await this.jwtService.verifyAsync(token, {
|
36 |
-
secret:
|
37 |
});
|
38 |
// 💡 We're assigning the payload to the request object here
|
39 |
// so that we can access it in our route handlers
|
|
|
5 |
UnauthorizedException,
|
6 |
} from '@nestjs/common';
|
7 |
import { JwtService } from '@nestjs/jwt';
|
|
|
8 |
import { Request } from 'express';
|
9 |
import { Reflector } from '@nestjs/core';
|
10 |
import { IS_PUBLIC_KEY } from './authentication.decorator.js';
|
11 |
+
import { ConfigService } from '@nestjs/config';
|
12 |
+
import { buffer } from 'stream/consumers';
|
13 |
@Injectable()
|
14 |
export class AuthenticationGuard implements CanActivate {
|
15 |
constructor(
|
16 |
private jwtService: JwtService,
|
17 |
private reflector: Reflector,
|
18 |
+
private configService: ConfigService
|
19 |
) {}
|
20 |
|
21 |
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
|
35 |
}
|
36 |
try {
|
37 |
const payload = await this.jwtService.verifyAsync(token, {
|
38 |
+
secret: this.configService.get<String>('JWT_KEY') as string,
|
39 |
});
|
40 |
// 💡 We're assigning the payload to the request object here
|
41 |
// so that we can access it in our route handlers
|
backend/src/modules/authentication/authentication.module.ts
CHANGED
@@ -3,16 +3,19 @@ import { AuthenticationService } from './authentication.service.js';
|
|
3 |
import { UserModule } from '../user/user.module.js';
|
4 |
import { JwtModule } from '@nestjs/jwt';
|
5 |
import { AuthenticationController } from './authentication.controller.js';
|
6 |
-
import { jwtConstants } from './constants.js';
|
7 |
import { AuthenticationGuard } from './authentication.guard.js';
|
8 |
import { APP_GUARD } from '@nestjs/core';
|
|
|
9 |
@Module({
|
10 |
imports: [
|
11 |
UserModule,
|
12 |
-
JwtModule.
|
13 |
-
|
14 |
-
|
15 |
-
|
|
|
|
|
|
|
16 |
}),
|
17 |
],
|
18 |
providers: [
|
|
|
3 |
import { UserModule } from '../user/user.module.js';
|
4 |
import { JwtModule } from '@nestjs/jwt';
|
5 |
import { AuthenticationController } from './authentication.controller.js';
|
|
|
6 |
import { AuthenticationGuard } from './authentication.guard.js';
|
7 |
import { APP_GUARD } from '@nestjs/core';
|
8 |
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
9 |
@Module({
|
10 |
imports: [
|
11 |
UserModule,
|
12 |
+
JwtModule.registerAsync({
|
13 |
+
imports: [ConfigModule], // Nhập ConfigModule để sử dụng ConfigService
|
14 |
+
useFactory: async (configService: ConfigService) => ({
|
15 |
+
secret: configService.get<string>('JWT_KEY'), // Lấy giá trị từ biến môi trường
|
16 |
+
signOptions: { expiresIn: '60s' }, // Thời gian hết hạn token
|
17 |
+
}),
|
18 |
+
inject: [ConfigService], // Inject ConfigService vào factory
|
19 |
}),
|
20 |
],
|
21 |
providers: [
|
backend/src/modules/authentication/authentication.service.ts
CHANGED
@@ -2,6 +2,8 @@ import { Injectable, UnauthorizedException } from '@nestjs/common';
|
|
2 |
import { UserService } from '../user/user.service.js';
|
3 |
import * as bcrypt from 'bcrypt';
|
4 |
import { JwtService } from '@nestjs/jwt';
|
|
|
|
|
5 |
@Injectable()
|
6 |
export class AuthenticationService {
|
7 |
constructor(
|
@@ -22,4 +24,33 @@ export class AuthenticationService {
|
|
22 |
access_token: await this.jwtService.signAsync(payload),
|
23 |
};
|
24 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
}
|
|
|
2 |
import { UserService } from '../user/user.service.js';
|
3 |
import * as bcrypt from 'bcrypt';
|
4 |
import { JwtService } from '@nestjs/jwt';
|
5 |
+
import { SignUpDto } from './dto/sign-up.dto.js';
|
6 |
+
import { UserEntity } from 'src/entities/user.entity.js';
|
7 |
@Injectable()
|
8 |
export class AuthenticationService {
|
9 |
constructor(
|
|
|
24 |
access_token: await this.jwtService.signAsync(payload),
|
25 |
};
|
26 |
}
|
27 |
+
|
28 |
+
async signUp(
|
29 |
+
signUpDto: SignUpDto,
|
30 |
+
): Promise<{ access_token: string }> {
|
31 |
+
const {
|
32 |
+
full_name,
|
33 |
+
phone_number,
|
34 |
+
email,
|
35 |
+
password
|
36 |
+
} = signUpDto
|
37 |
+
const hash_password = await bcrypt.hash(password, 10)
|
38 |
+
|
39 |
+
const user = await this.usersService.create({
|
40 |
+
full_name: full_name,
|
41 |
+
phone_number: phone_number,
|
42 |
+
email: email,
|
43 |
+
password: hash_password
|
44 |
+
})
|
45 |
+
console.log(user)
|
46 |
+
await this.usersService.save(user);
|
47 |
+
|
48 |
+
const payload = { sub: user.id, username: user.id };
|
49 |
+
console.log("payload: ", payload);
|
50 |
+
const token = await this.jwtService.signAsync(payload)
|
51 |
+
console.log("token: ", token);
|
52 |
+
return {
|
53 |
+
access_token: token
|
54 |
+
};
|
55 |
+
}
|
56 |
}
|
backend/src/modules/authentication/constants.ts
DELETED
@@ -1,5 +0,0 @@
|
|
1 |
-
|
2 |
-
export const jwtConstants = {
|
3 |
-
secret: process.env.JWT_SECRET,
|
4 |
-
};
|
5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
backend/src/modules/authentication/dto/sign-in.dto.ts
CHANGED
@@ -1,9 +1,10 @@
|
|
1 |
-
import { IsString } from 'class-validator';
|
2 |
|
3 |
export class SignInDto {
|
4 |
@IsString()
|
5 |
username: string;
|
6 |
|
7 |
@IsString()
|
|
|
8 |
password: string;
|
9 |
}
|
|
|
1 |
+
import { IsString, MinLength } from 'class-validator';
|
2 |
|
3 |
export class SignInDto {
|
4 |
@IsString()
|
5 |
username: string;
|
6 |
|
7 |
@IsString()
|
8 |
+
@MinLength(8)
|
9 |
password: string;
|
10 |
}
|
backend/src/modules/authentication/dto/sign-up.dto.ts
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
IsString,
|
3 |
+
IsEmail,
|
4 |
+
IsMobilePhone,
|
5 |
+
IsOptional,
|
6 |
+
IsNumber,
|
7 |
+
MinLength
|
8 |
+
} from "class-validator";
|
9 |
+
|
10 |
+
export class SignUpDto{
|
11 |
+
@IsString()
|
12 |
+
full_name: string;
|
13 |
+
|
14 |
+
@IsMobilePhone('vi-VN')
|
15 |
+
phone_number: string;
|
16 |
+
|
17 |
+
@IsOptional()
|
18 |
+
@IsEmail()
|
19 |
+
email?: string;
|
20 |
+
|
21 |
+
@IsString()
|
22 |
+
@MinLength(8)
|
23 |
+
password: string;
|
24 |
+
}
|
backend/src/modules/user/user.service.ts
CHANGED
@@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
|
2 |
import { InjectRepository } from '@nestjs/typeorm';
|
3 |
import { Repository } from 'typeorm';
|
4 |
import { UserEntity } from '../../entities/user.entity.js';
|
|
|
5 |
|
6 |
export type User = any;
|
7 |
|
@@ -10,6 +11,19 @@ export class UserService {
|
|
10 |
constructor() {}
|
11 |
|
12 |
async findOne(username: string): Promise<UserEntity | undefined> {
|
13 |
-
return UserEntity.findOne({ where: {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
}
|
15 |
}
|
|
|
2 |
import { InjectRepository } from '@nestjs/typeorm';
|
3 |
import { Repository } from 'typeorm';
|
4 |
import { UserEntity } from '../../entities/user.entity.js';
|
5 |
+
import { SignUpDto } from '../authentication/dto/sign-up.dto.js';
|
6 |
|
7 |
export type User = any;
|
8 |
|
|
|
11 |
constructor() {}
|
12 |
|
13 |
async findOne(username: string): Promise<UserEntity | undefined> {
|
14 |
+
return UserEntity.findOne({ where: { full_name: username } });
|
15 |
+
}
|
16 |
+
|
17 |
+
async create(signUpDto: SignUpDto): Promise<UserEntity | undefined>{
|
18 |
+
return UserEntity.create({
|
19 |
+
full_name: signUpDto.full_name,
|
20 |
+
phone_number: signUpDto.phone_number,
|
21 |
+
email: signUpDto.email,
|
22 |
+
hash_password: signUpDto.password
|
23 |
+
})
|
24 |
+
}
|
25 |
+
|
26 |
+
async save(userEntity: UserEntity): Promise<UserEntity | undefined>{
|
27 |
+
return UserEntity.save(userEntity);
|
28 |
}
|
29 |
}
|