Skip to content

Commit

Permalink
Merge pull request #48 from PBTP/test/unit
Browse files Browse the repository at this point in the history
Test/unit
  • Loading branch information
emibgo2 authored Nov 11, 2024
2 parents 1973505 + f20ac25 commit 0082216
Show file tree
Hide file tree
Showing 80 changed files with 1,950 additions and 393 deletions.
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test": "jest --verbose",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
Expand Down Expand Up @@ -86,8 +86,10 @@
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"roots": [
"<rootDir>/src",
"<rootDir>/test"
], "testRegex": ".*\\.(spec|test)\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
Expand Down
83 changes: 43 additions & 40 deletions src/auth/application/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Injectable, Logger } from '@nestjs/common';
import { Inject, Injectable, Logger } from '@nestjs/common';
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { CacheService } from '../../common/cache/cache.service';
import { CACHE_SERVICE, ICacheService } from '../../common/cache/cache.service';
import { UnauthorizedException } from '@nestjs/common/exceptions';
import { UserService } from './user.service';
import { UserDto } from '../presentation/user.dto';
import { AuthDto } from '../presentation/auth.dto';
import { Builder } from 'builder-pattern';

@Injectable()
export class AuthService {
Expand All @@ -17,7 +18,8 @@ export class AuthService {
constructor(
private readonly jwtService: JwtService,
private readonly configService: ConfigService,
private readonly cacheService: CacheService,
@Inject(CACHE_SERVICE)
private readonly cacheService: ICacheService,
private readonly userService: UserService,
) {
this.accessTokenOption = {
Expand All @@ -36,8 +38,12 @@ export class AuthService {
}

async login(dto: UserDto): Promise<AuthDto> {
let user: UserDto = await this.userService.findOne(dto);
user = user ?? (await this.userService.create(dto));
const user: UserDto = await this.userService
.findOne(dto)
.then(async (user) => {
return user ?? (await this.userService.create(dto));
});

user.userId = user.customerId ?? user.driverId ?? user.businessId;

user.userType = dto.userType;
Expand Down Expand Up @@ -75,36 +81,30 @@ export class AuthService {
refreshToken: refreshToken,
});

return {
uuid: user.uuid,
name: user.name,
userId: user.userId,
userType: user.userType ?? dto.userType,
phoneNumber: user.phoneNumber,
authProvider: user.authProvider,
accessToken: accessToken,
refreshToken: refreshToken,
};
return Builder(AuthDto)
.uuid(user.uuid)
.name(dto.name)
.userId(user.userId)
.userType(user.userType)
.phoneNumber(user.phoneNumber)
.authProvider(user.authProvider)
.accessToken(accessToken)
.refreshToken(refreshToken)
.build();
}

async tokenRefresh(
request: Request & { headers: { authorization?: string } },
): Promise<AuthDto> {
const token = request.headers.authorization?.replace('Bearer ', '');

async tokenRefresh(token: string): Promise<AuthDto> {
if (!token) {
throw new UnauthorizedException('Authorization 헤더에 토큰이 없습니다.');
throw new UnauthorizedException('토큰이 없습니다.');
}

const payload = this.jwtService.decode(token);

console.log('payload', payload);

if (!payload) {
throw new UnauthorizedException('토큰이 유효하지 않습니다.');
}

const user: UserDto = await this.userService.findOne({
const user: UserDto = await this.userService.getOne({
userType: payload.userType,
userId: payload.subject,
});
Expand Down Expand Up @@ -150,21 +150,19 @@ export class AuthService {
}

const key = `${user.userType}:${user.userId}:accessToken`;
const uniqueStrategy = this.accessTokenStrategy?.toLowerCase() === 'unique';

if (this.accessTokenStrategy?.toLowerCase() === 'unique') {
this.cacheService.get(key).then((v) => {
if (v) {
this.cacheService.del(v);
}
});

await this.cacheService.set(
key,
accessToken,
(this.accessTokenOption.expiresIn as number) / 1000,
);
if (uniqueStrategy) {
const existingToken = await this.cacheService.get(key);
if (existingToken) await this.cacheService.del(existingToken);
}

await this.cacheService.set(
key,
accessToken,
(this.accessTokenOption.expiresIn as number) / 1000,
);

await this.cacheService.set(
accessToken,
JSON.stringify({
Expand All @@ -173,10 +171,15 @@ export class AuthService {
}),
(this.accessTokenOption.expiresIn as number) / 1000,
);
}

async decode(token: string): Promise<any> {
return await this.jwtService.decode(token);
await this.cacheService.set(
accessToken,
JSON.stringify({
...user,
refreshToken: undefined,
}),
(this.accessTokenOption.expiresIn as number) / 1000,
);
}

async getUser(token: string): Promise<any> {
Expand All @@ -185,7 +188,7 @@ export class AuthService {
throw new UnauthorizedException('토큰이 유효하지 않습니다.');
}

return await this.userService.findOne({
return await this.userService.getOne({
userType: payload.userType,
userId: payload.subject,
});
Expand Down
5 changes: 3 additions & 2 deletions src/auth/application/jwt-access.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ import {
BadRequestException,
UnauthorizedException,
} from '@nestjs/common/exceptions';
import { Injectable } from '@nestjs/common';
import { Inject, Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { CacheService } from '../../common/cache/cache.service';
import { CACHE_SERVICE, CacheService } from '../../common/cache/cache.service';
import { UserDto } from '../presentation/user.dto';

@Injectable()
export class JwtAccessStrategy extends PassportStrategy(Strategy, 'access') {
constructor(
@Inject(CACHE_SERVICE)
private readonly cacheService: CacheService,
private readonly configService: ConfigService,
) {
Expand Down
2 changes: 1 addition & 1 deletion src/auth/application/jwt-refresh.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class JwtRefreshStrategy extends PassportStrategy(Strategy, 'refresh') {
throw new BadRequestException();
}

return await this.userServices[payload.userType].findOne({
return await this.userServices[payload.userType].getOne({
userId: payload.subject,
});
}
Expand Down
7 changes: 7 additions & 0 deletions src/auth/application/security.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ import { Injectable } from '@nestjs/common';
import * as crypto from 'crypto';
import { ConfigService } from '@nestjs/config';

export const SECURITY_SERVICE = 'SECURITY_SERVICE';

export interface ISecurityService {
encrypt(text: string): string | undefined;
decrypt(hash: string): string | undefined;
}

@Injectable()
export class SecurityService {
private readonly algorithm: string;
Expand Down
11 changes: 9 additions & 2 deletions src/auth/application/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,20 @@ export class UserService {
this.userServices.business = businessService;
}

async findOne(dto: UserDto): Promise<any> {
const user = await this.userServices[dto.userType!].findOne(dto);
async getOne(dto: UserDto): Promise<UserDto> {
const user = await this.userServices[dto.userType!].getOne(dto);

user && (user.userType = dto.userType);
return user;
}

async findOne(dto: UserDto): Promise<UserDto | null> {
const user = await this.userServices[dto.userType!].findOne(dto);

user && (user.userType = dto.userType);
return user ?? null;
}

async create(dto: UserDto) {
return this.userServices[dto.userType!].create(dto);
}
Expand Down
14 changes: 7 additions & 7 deletions src/auth/decorator/auth.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,26 @@ import {
HttpStatus,
UseGuards,
} from '@nestjs/common';
import { Customer } from '../../schemas/customer.entity';
import { CustomerEntity } from '../../schemas/customer.entity';
import { ApiBearerAuth, ApiUnauthorizedResponse } from '@nestjs/swagger';
import { AuthGuard } from '@nestjs/passport';
import { Business } from '../../schemas/business.entity';
import { Driver } from '../../schemas/drivers.entity';
import { BusinessEntity } from '../../schemas/business.entity';
import { DriverEntity } from '../../schemas/drivers.entity';

export const CurrentCustomer = createParamDecorator(
(data: unknown, context: ExecutionContext) => {
const req: { user?: Customer } = context.switchToHttp().getRequest();
const req: { user?: CustomerEntity } = context.switchToHttp().getRequest();

if (!req.user?.customerId) {
throw new ForbiddenException('해당 계정은 고객 계정이 아닙니다.');
}
return req.user;
return CustomerEntity.toModel(req.user);
},
);

export const CurrentBusiness = createParamDecorator(
(data: unknown, context: ExecutionContext) => {
const req: { user?: Business } = context.switchToHttp().getRequest();
const req: { user?: BusinessEntity } = context.switchToHttp().getRequest();

if (!req.user?.businessId) {
throw new ForbiddenException('해당 계정은 업체 계정이 아닙니다.');
Expand All @@ -37,7 +37,7 @@ export const CurrentBusiness = createParamDecorator(

export const CurrentDriver = createParamDecorator(
(data: unknown, context: ExecutionContext) => {
const req: { user?: Driver } = context.switchToHttp().getRequest();
const req: { user?: DriverEntity } = context.switchToHttp().getRequest();

if (!req.user?.driverId) {
throw new ForbiddenException('해당 계정은 기사 계정이 아닙니다.');
Expand Down
10 changes: 9 additions & 1 deletion src/auth/presentation/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { Auth } from '../decorator/auth.decorator';
import { UserDto, UserGroup } from './user.dto';
import { GroupValidation } from '../../common/validation/validation.decorator';
import { UnauthorizedException } from '@nestjs/common/exceptions';

@ApiTags('인증 관련 API')
@Controller('/v1/auth')
Expand Down Expand Up @@ -45,6 +46,13 @@ export class AuthController {
@Auth(HttpStatus.CREATED, 'refresh')
@Post('/refresh')
async refresh(@Req() req: Request): Promise<AuthDto> {
return await this.authService.tokenRefresh(req);
const token = req.headers.get('Authorization')?.replace('Bearer ', '');
if (!token) {
throw new UnauthorizedException(
'Authorization 헤더에 Bearer Token이 없습니다..',
);
}

return await this.authService.tokenRefresh(token);
}
}
6 changes: 3 additions & 3 deletions src/auth/presentation/auth.dto.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsOptional } from "class-validator";
import { UserDto } from "./user.dto";
import { ApiProperty } from '@nestjs/swagger';
import { IsOptional } from 'class-validator';
import { UserDto } from './user.dto';

export class AuthDto extends UserDto {
@ApiProperty({
Expand Down
12 changes: 12 additions & 0 deletions src/auth/presentation/user.dto.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { IsIn, IsNotEmpty, IsOptional } from 'class-validator';
import { CrudGroup } from '../../common/validation/validation.data';
import { ApiProperty } from '@nestjs/swagger';
import { Customer } from '../../customer/customer.domain';
import { Builder } from 'builder-pattern';

// 고객, 업체, 기사 공통 사용 DTO
export type UserType = 'customer' | 'driver' | 'business';
Expand Down Expand Up @@ -63,4 +65,14 @@ export class UserDto {
customerId?: number;
driverId?: number;
businessId?: number;

static from(customer: Customer): UserDto {
return Builder(UserDto)
.userId(customer.customerId)
.name(customer.customerName)
.phoneNumber(customer.customerPhoneNumber)
.authProvider(customer.authProvider)
.uuid(customer.uuid)
.build();
}
}
4 changes: 3 additions & 1 deletion src/auth/user.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { AuthDto } from './presentation/auth.dto';
export interface IUserService {
readonly userType: UserType;

findOne(dto: Partial<AuthDto>): Promise<UserDto>;
findOne(dto: Partial<AuthDto>): Promise<UserDto | null>;

getOne(dto: Partial<AuthDto>): Promise<UserDto>;

create(dto: UserDto): Promise<UserDto>;

Expand Down
Loading

0 comments on commit 0082216

Please sign in to comment.