From de3b1028f831de8cf3cb4ab9976261bc2504be97 Mon Sep 17 00:00:00 2001 From: ssojungg Date: Thu, 23 Nov 2023 03:08:21 +0900 Subject: [PATCH 01/18] =?UTF-8?q?feat:=20swagger=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 6 ++++-- src/main.ts | 20 +++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/app.module.ts b/src/app.module.ts index 8309d36..c1a13ca 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -3,6 +3,8 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { ConfigModule } from '@nestjs/config'; import { addTransactionalDataSource } from 'typeorm-transactional'; import { DataSource } from 'typeorm'; +import { PhoneController } from './phone/phone-controller/phone-controller.controller'; +import {PhoneService} from './phone/phone-service/phone-service.service'; @Module({ imports: [ @@ -29,7 +31,7 @@ import { DataSource } from 'typeorm'; }, }), ], - controllers: [], - providers: [], + controllers: [PhoneController], + providers: [PhoneService], }) export class AppModule {} diff --git a/src/main.ts b/src/main.ts index 620bdfe..fe74845 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,15 +1,21 @@ import { NestFactory } from '@nestjs/core'; +import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { AppModule } from './app.module'; -import { initializeTransactionalContext } from 'typeorm-transactional'; async function bootstrap() { - initializeTransactionalContext(); - const app = await NestFactory.create(AppModule); - // TODO: 프로그램 구현 - await app.listen(process.env.PORT || 8000); - console.log(`Application is running on: ${await app.getUrl()}`); -} + const config = new DocumentBuilder() + .setTitle('Phone Verification API') + .setDescription('The phone verification API description') + .setVersion('1.0') + .addTag('phone-verification') + .build(); + + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('api', app, document); + await app.listen(3001); +} bootstrap(); + From 602de7078a9130003bc1f01ad1b0c652ee4ff408 Mon Sep 17 00:00:00 2001 From: ssojungg Date: Thu, 23 Nov 2023 03:11:52 +0900 Subject: [PATCH 02/18] =?UTF-8?q?feat:=20=EC=A0=84=ED=99=94=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=9E=85=EB=A0=A5=ED=95=98=EB=8A=94=20dto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/phone/dto/SendCodeDto.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/phone/dto/SendCodeDto.ts diff --git a/src/phone/dto/SendCodeDto.ts b/src/phone/dto/SendCodeDto.ts new file mode 100644 index 0000000..1ae0e02 --- /dev/null +++ b/src/phone/dto/SendCodeDto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsNotEmpty, IsString, Matches } from 'class-validator'; + +export class SendCodeDto { + @IsNotEmpty() + @IsString() + @ApiProperty({ example: '010-1234-5678', description: '전화번호' }) + @Matches(/^010-\d{4}-\d{4}$/, { message: '잘못된 전화번호 형식입니다' }) + phoneNumber: string; +} From a5b1bfe7a42a8f54ae28b30d9ea3f385028a5105 Mon Sep 17 00:00:00 2001 From: ssojungg Date: Thu, 23 Nov 2023 03:16:59 +0900 Subject: [PATCH 03/18] =?UTF-8?q?feat:=20=EC=A0=84=ED=99=94=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=ED=95=B4?= =?UTF-8?q?=EC=A4=84=20=EB=95=8C=20=EC=93=B0=EC=9D=B4=EB=8A=94=20dto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/phone/dto/VerifyCodeDto.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/phone/dto/VerifyCodeDto.ts diff --git a/src/phone/dto/VerifyCodeDto.ts b/src/phone/dto/VerifyCodeDto.ts new file mode 100644 index 0000000..f400340 --- /dev/null +++ b/src/phone/dto/VerifyCodeDto.ts @@ -0,0 +1,16 @@ +import { IsNotEmpty, IsString, Length, Matches } from 'class-validator'; +import { ApiProperty } from "@nestjs/swagger"; + +export class VerifyCodeDto { + @IsNotEmpty() + @IsString() + @ApiProperty({ example: '010-1234-5678', description: '전화번호' }) + @Matches(/^010-\d{4}-\d{4}$/, { message: '잘못된 전화번호 형식입니다' }) + phoneNumber: string; + + @IsNotEmpty() + @IsString() + @Length(6, 6, { message: '코드는 6자리 숫자여야 합니다' }) + @ApiProperty({ example: '123456', description: '코드' }) + code: string; +} From 4b78e4034fb277be329a67ecbfa6287b20f12df8 Mon Sep 17 00:00:00 2001 From: ssojungg Date: Thu, 23 Nov 2023 03:17:29 +0900 Subject: [PATCH 04/18] =?UTF-8?q?feat:=20=EB=AA=A8=EB=93=A0=20=EC=A0=84?= =?UTF-8?q?=ED=99=94=EB=B2=88=ED=98=B8=20=EB=B0=8F=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=B3=B4=EC=97=AC=EC=A3=BC=EB=8A=94=20dto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/phone/dto/GetAllVerificationsDto.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/phone/dto/GetAllVerificationsDto.ts diff --git a/src/phone/dto/GetAllVerificationsDto.ts b/src/phone/dto/GetAllVerificationsDto.ts new file mode 100644 index 0000000..a0b117a --- /dev/null +++ b/src/phone/dto/GetAllVerificationsDto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { VerifyCodeDto } from './VerifyCodeDto'; + +export class GetAllVerificationsDto { + @ApiProperty({ + type: [VerifyCodeDto], + description: '저장된 모든 전화번호와 그 인증 코드 목록' + }) + verifications: VerifyCodeDto[]; +} From 55af998609b872bb560a4be204f9fb58ccff91b1 Mon Sep 17 00:00:00 2001 From: ssojungg Date: Thu, 23 Nov 2023 03:19:30 +0900 Subject: [PATCH 05/18] =?UTF-8?q?feat:=20=EC=9D=B8=EC=A6=9D=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=ED=99=95=EC=9D=B8=20dto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/notificaiton/dto/notificaiton-response.dto.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/notificaiton/dto/notificaiton-response.dto.ts diff --git a/src/notificaiton/dto/notificaiton-response.dto.ts b/src/notificaiton/dto/notificaiton-response.dto.ts new file mode 100644 index 0000000..9653723 --- /dev/null +++ b/src/notificaiton/dto/notificaiton-response.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; + +import { IsNotEmpty, IsString, Length, Matches } from "class-validator"; + +export class CheckCodeDto { + @IsNotEmpty() + @IsString() + @Length(6, 6) + @ApiProperty({ example: '123456', description: 'Verification code' }) + code: string; +} From cb8a9f25a0bf17753ee1bdbd8d1c29791d7854e3 Mon Sep 17 00:00:00 2001 From: ssojungg Date: Thu, 23 Nov 2023 03:19:54 +0900 Subject: [PATCH 06/18] =?UTF-8?q?feat:=20Entity=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/phone/entity/PhoneVerification.ts | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/phone/entity/PhoneVerification.ts diff --git a/src/phone/entity/PhoneVerification.ts b/src/phone/entity/PhoneVerification.ts new file mode 100644 index 0000000..4fc8dbb --- /dev/null +++ b/src/phone/entity/PhoneVerification.ts @@ -0,0 +1,5 @@ +export class PhoneVerification { + phoneNumber: string; + code: string; + timestamp: Date; +} From 428a4062fcfd2cf1041638d71893e19c0c6c28de Mon Sep 17 00:00:00 2001 From: ssojungg Date: Thu, 23 Nov 2023 03:21:02 +0900 Subject: [PATCH 07/18] =?UTF-8?q?feat:=20dto=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/notificaiton/dto/notificaiton-response.dto.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/notificaiton/dto/notificaiton-response.dto.ts b/src/notificaiton/dto/notificaiton-response.dto.ts index 9653723..00650cd 100644 --- a/src/notificaiton/dto/notificaiton-response.dto.ts +++ b/src/notificaiton/dto/notificaiton-response.dto.ts @@ -1,11 +1,10 @@ import { ApiProperty } from '@nestjs/swagger'; - -import { IsNotEmpty, IsString, Length, Matches } from "class-validator"; +import { IsNotEmpty, IsString, Length } from "class-validator"; export class CheckCodeDto { @IsNotEmpty() @IsString() @Length(6, 6) - @ApiProperty({ example: '123456', description: 'Verification code' }) + @ApiProperty({ example: '123456', description: '인증 코드' }) code: string; } From 2a47477b2967220649b2d269cccb5c31d892c616 Mon Sep 17 00:00:00 2001 From: ssojungg Date: Thu, 23 Nov 2023 03:21:39 +0900 Subject: [PATCH 08/18] =?UTF-8?q?feat:=20contorller=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=20=EB=B0=8F=20LongPolling=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../phone-controller.controller.spec.ts | 18 +++++++ .../phone-controller.controller.ts | 51 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 src/phone/phone-controller/phone-controller.controller.spec.ts create mode 100644 src/phone/phone-controller/phone-controller.controller.ts diff --git a/src/phone/phone-controller/phone-controller.controller.spec.ts b/src/phone/phone-controller/phone-controller.controller.spec.ts new file mode 100644 index 0000000..c959265 --- /dev/null +++ b/src/phone/phone-controller/phone-controller.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { PhoneController } from './phone-controller.controller'; + +describe('PhoneController', () => { + let controller: PhoneController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [PhoneController], + }).compile(); + + controller = module.get(PhoneController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/phone/phone-controller/phone-controller.controller.ts b/src/phone/phone-controller/phone-controller.controller.ts new file mode 100644 index 0000000..7613e2d --- /dev/null +++ b/src/phone/phone-controller/phone-controller.controller.ts @@ -0,0 +1,51 @@ +import { Body, Controller, Post, HttpCode, HttpStatus, Get, Query, Param } from "@nestjs/common"; +import { ApiOperation, ApiBody, ApiResponse, ApiQuery, ApiParam } from "@nestjs/swagger"; +import { VerifyCodeDto } from '../dto/VerifyCodeDto'; +import {PhoneService} from "../phone-service/phone-service.service"; +import {SendCodeDto} from "../dto/SendCodeDto"; +import { GetAllVerificationsDto } from "../dto/GetAllVerificationsDto"; +import { CheckCodeDto } from "../../notificaiton/dto/notificaiton-response.dto"; + +@Controller('PhoneController') +export class PhoneController { + constructor(private readonly phoneVerificationService: PhoneService) {} + + + @Post('/send-code') + @HttpCode(HttpStatus.OK) + @ApiOperation({ summary: 'Send Verification Code', description: 'Sends a verification code to the given phone number.' }) + @ApiBody({ type: SendCodeDto }) + @ApiResponse({ status: 200, description: 'Verification code sent', type: String }) + sendCode(@Body() sendCodeDto: SendCodeDto) { + return this.phoneVerificationService.sendCode(sendCodeDto); + } + + @Get('/start-verification/:phoneNumber') + @ApiOperation({ summary: '휴대전화 인증 시작', description: '주어진 휴대전화 번호에 대한 인증 절차를 시작합니다.' }) + @ApiParam({ name: 'phoneNumber', type: String, description: '인증을 시작할 휴대전화 번호' }) + @ApiResponse({ status: 200, description: '인증 절차 시작됨', type: Boolean }) + startVerification(@Param('phoneNumber') phoneNumber: string): Promise { + return this.phoneVerificationService.startVerification(phoneNumber); + } + + @Post('/check-code') + @ApiOperation({ summary: '인증 코드 확인', description: '제공된 인증 코드의 유효성을 검증합니다.' }) + @ApiBody({ type: CheckCodeDto }) // CheckCodeDto는 인증 코드만 포함 + @ApiResponse({ status: 200, description: '인증 코드 검증 결과', type: Boolean }) + checkCode(@Body() checkCodeDto: CheckCodeDto): boolean { + return this.phoneVerificationService.checkCode(checkCodeDto.code); + } + + + + @Get('get-all-verifications') + @ApiOperation({ summary: 'Get all stored phone numbers and verification codes' }) + @ApiResponse({ + status: 200, + description: 'List of all stored phone numbers and verification codes', + type: [VerifyCodeDto] + }) + getAllVerifications(): VerifyCodeDto[] { + return this.phoneVerificationService.getAllVerifications(); + } +} From d321db37f5399d20ffdcaa0b8b3d64a5f0acc854 Mon Sep 17 00:00:00 2001 From: ssojungg Date: Thu, 23 Nov 2023 03:22:06 +0900 Subject: [PATCH 09/18] =?UTF-8?q?feat:=20service=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EB=B0=8F=20Long=20Polling=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../phone-service.service.spec.ts | 18 +++++ .../phone-service/phone-service.service.ts | 81 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 src/phone/phone-service/phone-service.service.spec.ts create mode 100644 src/phone/phone-service/phone-service.service.ts diff --git a/src/phone/phone-service/phone-service.service.spec.ts b/src/phone/phone-service/phone-service.service.spec.ts new file mode 100644 index 0000000..0ed32d4 --- /dev/null +++ b/src/phone/phone-service/phone-service.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import {PhoneService} from './phone-service.service'; + +describe('PhoneServiceService', () => { + let service: PhoneService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [PhoneService], + }).compile(); + + service = module.get(PhoneService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/phone/phone-service/phone-service.service.ts b/src/phone/phone-service/phone-service.service.ts new file mode 100644 index 0000000..617dd4b --- /dev/null +++ b/src/phone/phone-service/phone-service.service.ts @@ -0,0 +1,81 @@ +import { Injectable } from '@nestjs/common'; +import { PhoneVerification } from '../entity/PhoneVerification'; +import { VerifyCodeDto } from '../dto/VerifyCodeDto'; +import {SendCodeDto} from "../dto/SendCodeDto"; +import { CheckCodeDto } from "../../notificaiton/dto/notificaiton-response.dto"; + +@Injectable() +export class PhoneService { + private readonly verifications: PhoneVerification[] = []; + private readonly waitingClients: Map void> = new Map(); + sendCode(sendCodeDto: SendCodeDto): any { + const verificationCode = Math.floor(100000 + Math.random() * 900000); // 6자리 랜덤 숫자 생성 + const expirationTime = new Date(); + expirationTime.setMinutes(expirationTime.getMinutes() + 5); // 현재 시간으로부터 5분 후 + + const verification: PhoneVerification = { + phoneNumber: sendCodeDto.phoneNumber, + code: verificationCode.toString(), + timestamp: expirationTime, // 여기를 수정 + }; + + this.verifications.push(verification); + return { code: verificationCode }; + } + + + + verifyCode(phoneNumber: string, code: string): Promise { + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + resolve(false); // 5분 후 false 반환 + }, 300000); // 5분 + + this.waitForCodeVerification(phoneNumber, code, resolve, timer); + }); + } + + private waitForCodeVerification(phoneNumber: string, code: string, resolve: (value: boolean) => void, timer: NodeJS.Timeout) { + const checkVerification = () => { + const verification = this.verifications.find(v => v.phoneNumber === phoneNumber && v.code === code); + if (verification) { + clearTimeout(timer); + resolve(true); + } else { + setTimeout(checkVerification, 5000); // 5초마다 확인 + } + }; + checkVerification(); + } + + getAllVerifications(): VerifyCodeDto[] { + return this.verifications.map(verification => ({ + phoneNumber: verification.phoneNumber, + code: verification.code + })); + } + + startVerification(phoneNumber: string): Promise { + return new Promise((resolve) => { + this.waitingClients.set(phoneNumber, resolve); + }); + } + + checkCode(code: string): boolean { + let isValid = false; + this.verifications.forEach((verification) => { + if (verification.code === code) { + const resolve = this.waitingClients.get(verification.phoneNumber); + if (resolve) { + resolve(true); // 인증 코드가 유효하면 true로 Long Polling 완료 + isValid = true; + } + this.waitingClients.delete(verification.phoneNumber); + } + }); + return isValid; // 코드 검증 결과 반환 + } + + + +} \ No newline at end of file From f460de5b9f7acac7612dbdc5ccd7fb6b3903e4b3 Mon Sep 17 00:00:00 2001 From: ssojungg Date: Thu, 23 Nov 2023 03:23:18 +0900 Subject: [PATCH 10/18] =?UTF-8?q?fix:=20.env=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 .env.example diff --git a/.env.example b/.env.example deleted file mode 100644 index e084950..0000000 --- a/.env.example +++ /dev/null @@ -1,11 +0,0 @@ -# 서버 애플리케이션 포트 관련 변수입니다. -PORT=8000 - -# DB 세팅 환경 변수입니다 -DB_HOST=localhost -DB_PORT=3306 -DB_USERNAME=user -DB_PASSWORD=password -DB_DATABASE=nest_db - -DB_SYNC=true \ No newline at end of file From 1df8c62744352e9759e2983c1805fb8302e0a0cf Mon Sep 17 00:00:00 2001 From: ssojungg Date: Thu, 23 Nov 2023 04:01:51 +0900 Subject: [PATCH 11/18] =?UTF-8?q?fix:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EA=B0=9D=EC=B2=B4=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/phone/entity/PhoneVerification.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/phone/entity/PhoneVerification.ts b/src/phone/entity/PhoneVerification.ts index 4fc8dbb..bbcf596 100644 --- a/src/phone/entity/PhoneVerification.ts +++ b/src/phone/entity/PhoneVerification.ts @@ -1,5 +1,4 @@ export class PhoneVerification { phoneNumber: string; code: string; - timestamp: Date; } From 1146abbf86e4693b2926b6365f70370f19541f5c Mon Sep 17 00:00:00 2001 From: ssojungg Date: Thu, 23 Nov 2023 04:02:05 +0900 Subject: [PATCH 12/18] =?UTF-8?q?feat:=20=EC=97=90=EB=9F=AC=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/phone/exception/TitleNullException.ts | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/phone/exception/TitleNullException.ts diff --git a/src/phone/exception/TitleNullException.ts b/src/phone/exception/TitleNullException.ts new file mode 100644 index 0000000..d36fc49 --- /dev/null +++ b/src/phone/exception/TitleNullException.ts @@ -0,0 +1,7 @@ +import { HttpException, HttpStatus } from "@nestjs/common"; + +export class TitleNullException extends HttpException { + constructor() { + super('전화번호를 입력해주세요', HttpStatus.BAD_REQUEST); + } +} \ No newline at end of file From 9068e7bda470a0528a296b57535d561fdc70c9e6 Mon Sep 17 00:00:00 2001 From: ssojungg Date: Thu, 23 Nov 2023 04:02:33 +0900 Subject: [PATCH 13/18] =?UTF-8?q?fix:=20swagger=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/phone/phone-controller/phone-controller.controller.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/phone/phone-controller/phone-controller.controller.ts b/src/phone/phone-controller/phone-controller.controller.ts index 7613e2d..bf72bea 100644 --- a/src/phone/phone-controller/phone-controller.controller.ts +++ b/src/phone/phone-controller/phone-controller.controller.ts @@ -1,12 +1,11 @@ import { Body, Controller, Post, HttpCode, HttpStatus, Get, Query, Param } from "@nestjs/common"; -import { ApiOperation, ApiBody, ApiResponse, ApiQuery, ApiParam } from "@nestjs/swagger"; +import { ApiOperation, ApiBody, ApiResponse, ApiQuery, ApiParam, ApiTags } from "@nestjs/swagger"; import { VerifyCodeDto } from '../dto/VerifyCodeDto'; import {PhoneService} from "../phone-service/phone-service.service"; import {SendCodeDto} from "../dto/SendCodeDto"; -import { GetAllVerificationsDto } from "../dto/GetAllVerificationsDto"; import { CheckCodeDto } from "../../notificaiton/dto/notificaiton-response.dto"; - -@Controller('PhoneController') +@ApiTags('Phone') +@Controller('api/v1/Phone') export class PhoneController { constructor(private readonly phoneVerificationService: PhoneService) {} From dceba5162e4bc181fd0a823a81d09d9522e03e10 Mon Sep 17 00:00:00 2001 From: ssojungg Date: Thu, 23 Nov 2023 04:03:11 +0900 Subject: [PATCH 14/18] =?UTF-8?q?fix:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../phone-service/phone-service.service.ts | 58 ++++++++----------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/src/phone/phone-service/phone-service.service.ts b/src/phone/phone-service/phone-service.service.ts index 617dd4b..5a088be 100644 --- a/src/phone/phone-service/phone-service.service.ts +++ b/src/phone/phone-service/phone-service.service.ts @@ -2,52 +2,32 @@ import { Injectable } from '@nestjs/common'; import { PhoneVerification } from '../entity/PhoneVerification'; import { VerifyCodeDto } from '../dto/VerifyCodeDto'; import {SendCodeDto} from "../dto/SendCodeDto"; -import { CheckCodeDto } from "../../notificaiton/dto/notificaiton-response.dto"; +import { TitleNullException } from "../exception/TitleNullException"; @Injectable() export class PhoneService { private readonly verifications: PhoneVerification[] = []; private readonly waitingClients: Map void> = new Map(); + + //인증 코드 생성 sendCode(sendCodeDto: SendCodeDto): any { + + //만약 전화번호 작성하지 않았으면 + if (!sendCodeDto.phoneNumber) { + throw new TitleNullException(); + } const verificationCode = Math.floor(100000 + Math.random() * 900000); // 6자리 랜덤 숫자 생성 - const expirationTime = new Date(); - expirationTime.setMinutes(expirationTime.getMinutes() + 5); // 현재 시간으로부터 5분 후 - const verification: PhoneVerification = { + const verification: PhoneVerification = { //매핑 phoneNumber: sendCodeDto.phoneNumber, code: verificationCode.toString(), - timestamp: expirationTime, // 여기를 수정 }; this.verifications.push(verification); return { code: verificationCode }; } - - - verifyCode(phoneNumber: string, code: string): Promise { - return new Promise((resolve, reject) => { - const timer = setTimeout(() => { - resolve(false); // 5분 후 false 반환 - }, 300000); // 5분 - - this.waitForCodeVerification(phoneNumber, code, resolve, timer); - }); - } - - private waitForCodeVerification(phoneNumber: string, code: string, resolve: (value: boolean) => void, timer: NodeJS.Timeout) { - const checkVerification = () => { - const verification = this.verifications.find(v => v.phoneNumber === phoneNumber && v.code === code); - if (verification) { - clearTimeout(timer); - resolve(true); - } else { - setTimeout(checkVerification, 5000); // 5초마다 확인 - } - }; - checkVerification(); - } - + //모든 전화번호와 코드 확인 getAllVerifications(): VerifyCodeDto[] { return this.verifications.map(verification => ({ phoneNumber: verification.phoneNumber, @@ -55,12 +35,23 @@ export class PhoneService { })); } + //휴대전화 인증 시작 startVerification(phoneNumber: string): Promise { - return new Promise((resolve) => { - this.waitingClients.set(phoneNumber, resolve); + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + this.waitingClients.delete(phoneNumber); + reject(new Error('Verification timed out')); // 5분 후 타임아웃 에러 반환 + }, 300000); // 5분 + + this.waitingClients.set(phoneNumber, (isValid) => { + clearTimeout(timer); + resolve(isValid); + }); }); } + + //인증 코드 확인 checkCode(code: string): boolean { let isValid = false; this.verifications.forEach((verification) => { @@ -75,7 +66,4 @@ export class PhoneService { }); return isValid; // 코드 검증 결과 반환 } - - - } \ No newline at end of file From 62b339fe7d8204d7424a89ce65eb152790da585a Mon Sep 17 00:00:00 2001 From: ssojungg Date: Fri, 24 Nov 2023 00:22:06 +0900 Subject: [PATCH 15/18] =?UTF-8?q?feat:=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/phone/exception/CodeNotFound.ts | 7 +++++++ src/phone/exception/TimeOver.ts | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 src/phone/exception/CodeNotFound.ts create mode 100644 src/phone/exception/TimeOver.ts diff --git a/src/phone/exception/CodeNotFound.ts b/src/phone/exception/CodeNotFound.ts new file mode 100644 index 0000000..3367531 --- /dev/null +++ b/src/phone/exception/CodeNotFound.ts @@ -0,0 +1,7 @@ +import { HttpException, HttpStatus } from "@nestjs/common"; + +export class CodeNotFound extends HttpException { + constructor() { + super('인증번호가 맞지 않습니다', HttpStatus.BAD_REQUEST); + } +} \ No newline at end of file diff --git a/src/phone/exception/TimeOver.ts b/src/phone/exception/TimeOver.ts new file mode 100644 index 0000000..4d4d7bd --- /dev/null +++ b/src/phone/exception/TimeOver.ts @@ -0,0 +1,7 @@ +import { HttpException, HttpStatus } from "@nestjs/common"; + +export class TimeOver extends HttpException { + constructor() { + super('인증번호 유효시간이 지났습니다', HttpStatus.BAD_REQUEST); + } +} \ No newline at end of file From dd749b4c5c039d51c2b2d74a31a696026f603306 Mon Sep 17 00:00:00 2001 From: ssojungg Date: Fri, 24 Nov 2023 00:27:06 +0900 Subject: [PATCH 16/18] =?UTF-8?q?fix:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../phone-controller.controller.spec.ts | 35 ++++++++- .../phone-service.service.spec.ts | 72 +++++++++++++++++-- 2 files changed, 101 insertions(+), 6 deletions(-) diff --git a/src/phone/phone-controller/phone-controller.controller.spec.ts b/src/phone/phone-controller/phone-controller.controller.spec.ts index c959265..009cd6a 100644 --- a/src/phone/phone-controller/phone-controller.controller.spec.ts +++ b/src/phone/phone-controller/phone-controller.controller.spec.ts @@ -1,18 +1,49 @@ import { Test, TestingModule } from '@nestjs/testing'; import { PhoneController } from './phone-controller.controller'; +import { PhoneService } from '../phone-service/phone-service.service'; +import { SendCodeDto } from '../dto/SendCodeDto'; +import { CheckCodeDto } from '../../notificaiton/dto/notificaiton-response.dto'; describe('PhoneController', () => { let controller: PhoneController; + let mockPhoneService: PhoneService; beforeEach(async () => { + mockPhoneService = { + sendCode: jest.fn(), + checkCode: jest.fn(), + getAllVerifications: jest.fn() + } as any; + const module: TestingModule = await Test.createTestingModule({ controllers: [PhoneController], + providers: [{ provide: PhoneService, useValue: mockPhoneService }], }).compile(); controller = module.get(PhoneController); }); - it('should be defined', () => { - expect(controller).toBeDefined(); + describe('sendCode', () => { + it('PhoneService의 sendCode 메서드를 호출해야 함', async () => { + const sendCodeDto: SendCodeDto = { phoneNumber: '010-1234-5678' }; + await controller.sendCode(sendCodeDto); + expect(mockPhoneService.sendCode).toHaveBeenCalledWith(sendCodeDto); + }); + }); + + describe('checkCode', () => { + it('정확한 매개변수와 함께 PhoneService의 checkCode 메서드를 호출해야 함', async () => { + const phoneNumber = '010-1234-5678'; + const checkCodeDto: CheckCodeDto = { code: '123456' }; + await controller.checkCode(phoneNumber, checkCodeDto); + expect(mockPhoneService.checkCode).toHaveBeenCalledWith(phoneNumber, checkCodeDto.code); + }); + }); + + describe('getAllVerifications', () => { + it('PhoneService의 getAllVerifications 메서드를 호출해야 함', async () => { + await controller.getAllVerifications(); + expect(mockPhoneService.getAllVerifications).toHaveBeenCalled(); + }); }); }); diff --git a/src/phone/phone-service/phone-service.service.spec.ts b/src/phone/phone-service/phone-service.service.spec.ts index 0ed32d4..5f5832d 100644 --- a/src/phone/phone-service/phone-service.service.spec.ts +++ b/src/phone/phone-service/phone-service.service.spec.ts @@ -1,18 +1,82 @@ import { Test, TestingModule } from '@nestjs/testing'; import {PhoneService} from './phone-service.service'; +import { SendCodeDto } from "../dto/SendCodeDto"; +import { TitleNullException } from "../exception/TitleNullException"; +import { exhaustiveTypeException } from "tsconfig-paths/lib/try-path"; +import { CodeNotFound } from "../exception/CodeNotFound"; +import { TimeOver } from "../exception/TimeOver"; describe('PhoneServiceService', () => { let service: PhoneService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [PhoneService], - }).compile(); + exports: [PhoneService], + providers: [ + { + provide: PhoneService, + useValue: { + sendCode: jest.fn(), + getAllVerifications:jest.fn(), + checkCode:jest.fn(), + }, + }, + ], + }).compile(); service = module.get(PhoneService); }); - it('should be defined', () => { - expect(service).toBeDefined(); + it('전화번호가 주어지면, 인증 코드를 생성하고 반환해야 함', async () => { + const mockSendCodeResult = { code: '123456' }; + jest.spyOn(service, 'sendCode').mockResolvedValue(mockSendCodeResult); + + const mockPhoneNumber = '010-1234-5678'; + const sendCodeDto: SendCodeDto = { phoneNumber: mockPhoneNumber }; + + const result = await service.sendCode(sendCodeDto); + + expect(result).toEqual(mockSendCodeResult); + }); + + + it('전화번호가 주어지지 않으면, TitleNullException을 발생시켜야 함', async () => { + const sendCodeDto: SendCodeDto = { phoneNumber: '' }; + + jest.spyOn(service, 'sendCode').mockRejectedValue(new TitleNullException()); + + await expect(service.sendCode(sendCodeDto)).rejects.toThrow(TitleNullException); + }); + + + describe('getAllVerifications',() =>{ + service.sendCode({phoneNumber:'010-1234-1234'}); + service.sendCode({phoneNumber:'010-1234-1111'}); + + const result = service.getAllVerifications(); + expect(result).toHaveLength(2); + }); + + describe('checkCode',()=>{ + it('올바른 전화번호와 코드로 인증 성공해야 함',()=>{ + service.sendCode({phoneNumber:'010-1234-1234'}); + expect(()=> service.checkCode('010-1234-1234','wrongcode')).toThrow(CodeNotFound); + }); + + it('코드 유효 시간이 지나면 TimeOver 예외를 발생시켜야 함', () => { + jest.useFakeTimers(); + + const sendCodeDto: SendCodeDto = { phoneNumber: '010-1234-1234' }; + const sentCode = service.sendCode(sendCodeDto); // 변수 이름 수정 + + const futureTime = new Date(); + futureTime.setMinutes(futureTime.getMinutes() + 5); // 오타 수정 + jest.setSystemTime(futureTime); + + // 코드 검증 시도 + expect(() => service.checkCode(sendCodeDto.phoneNumber, sentCode.code)).toThrow(TimeOver); // 변수 이름 수정 + + jest.useRealTimers(); // 테스트 후 실제 타이머로 다시 설정 + }); }); }); From ee6392beafa149b45fa720413b886962849ac317 Mon Sep 17 00:00:00 2001 From: ssojungg Date: Fri, 24 Nov 2023 00:27:30 +0900 Subject: [PATCH 17/18] =?UTF-8?q?fix:=20LongPolling=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/phone/entity/PhoneVerification.ts | 10 +++++ .../phone-controller.controller.ts | 19 +++----- .../phone-service/phone-service.service.ts | 44 ++++++++----------- 3 files changed, 35 insertions(+), 38 deletions(-) diff --git a/src/phone/entity/PhoneVerification.ts b/src/phone/entity/PhoneVerification.ts index bbcf596..cf45fa1 100644 --- a/src/phone/entity/PhoneVerification.ts +++ b/src/phone/entity/PhoneVerification.ts @@ -1,4 +1,14 @@ +import { Entity, Column } from "typeorm"; + +@Entity() export class PhoneVerification { + @Column("전화번호") phoneNumber: string; + + @Column("코드") code: string; + + @Column("시간") + timestamp: Date; } + diff --git a/src/phone/phone-controller/phone-controller.controller.ts b/src/phone/phone-controller/phone-controller.controller.ts index bf72bea..716054e 100644 --- a/src/phone/phone-controller/phone-controller.controller.ts +++ b/src/phone/phone-controller/phone-controller.controller.ts @@ -19,20 +19,13 @@ export class PhoneController { return this.phoneVerificationService.sendCode(sendCodeDto); } - @Get('/start-verification/:phoneNumber') - @ApiOperation({ summary: '휴대전화 인증 시작', description: '주어진 휴대전화 번호에 대한 인증 절차를 시작합니다.' }) - @ApiParam({ name: 'phoneNumber', type: String, description: '인증을 시작할 휴대전화 번호' }) - @ApiResponse({ status: 200, description: '인증 절차 시작됨', type: Boolean }) - startVerification(@Param('phoneNumber') phoneNumber: string): Promise { - return this.phoneVerificationService.startVerification(phoneNumber); - } - - @Post('/check-code') - @ApiOperation({ summary: '인증 코드 확인', description: '제공된 인증 코드의 유효성을 검증합니다.' }) - @ApiBody({ type: CheckCodeDto }) // CheckCodeDto는 인증 코드만 포함 + @Post('/check-code/:phoneNumber') + @ApiOperation({ summary: '인증 코드 확인', description: '제공된 전화번호에 대한 인증 코드의 유효성을 검증합니다.' }) + @ApiBody({ type: CheckCodeDto }) + @ApiParam({ name: 'phoneNumber', required: true, type: String, description: '전화번호' }) @ApiResponse({ status: 200, description: '인증 코드 검증 결과', type: Boolean }) - checkCode(@Body() checkCodeDto: CheckCodeDto): boolean { - return this.phoneVerificationService.checkCode(checkCodeDto.code); + checkCode(@Param('phoneNumber') phoneNumber: string, @Body() checkCodeDto: CheckCodeDto): { success: boolean, message: string } { + return this.phoneVerificationService.checkCode(phoneNumber, checkCodeDto.code); } diff --git a/src/phone/phone-service/phone-service.service.ts b/src/phone/phone-service/phone-service.service.ts index 5a088be..dbe9ab5 100644 --- a/src/phone/phone-service/phone-service.service.ts +++ b/src/phone/phone-service/phone-service.service.ts @@ -3,6 +3,8 @@ import { PhoneVerification } from '../entity/PhoneVerification'; import { VerifyCodeDto } from '../dto/VerifyCodeDto'; import {SendCodeDto} from "../dto/SendCodeDto"; import { TitleNullException } from "../exception/TitleNullException"; +import { CodeNotFound } from "../exception/CodeNotFound"; +import { TimeOver } from "../exception/TimeOver"; @Injectable() export class PhoneService { @@ -21,6 +23,7 @@ export class PhoneService { const verification: PhoneVerification = { //매핑 phoneNumber: sendCodeDto.phoneNumber, code: verificationCode.toString(), + timestamp: new Date() }; this.verifications.push(verification); @@ -35,35 +38,26 @@ export class PhoneService { })); } - //휴대전화 인증 시작 - startVerification(phoneNumber: string): Promise { - return new Promise((resolve, reject) => { - const timer = setTimeout(() => { - this.waitingClients.delete(phoneNumber); - reject(new Error('Verification timed out')); // 5분 후 타임아웃 에러 반환 - }, 300000); // 5분 - this.waitingClients.set(phoneNumber, (isValid) => { - clearTimeout(timer); - resolve(isValid); - }); - }); - } + // 인증 코드 확인 + checkCode(phoneNumber: string, code: string): { success: boolean, message: string } { + const currentTime = new Date(); // 현재 시간 + for (const verification of this.verifications) { + if (verification.phoneNumber === phoneNumber && verification.code === code) { + // 인증번호의 생성 시간과 현재 시간의 차이가 5분 이내인지 확인 + const timeDiff = (currentTime.getTime() - verification.timestamp.getTime()) / 60000; // 분 단위로 변환 - //인증 코드 확인 - checkCode(code: string): boolean { - let isValid = false; - this.verifications.forEach((verification) => { - if (verification.code === code) { - const resolve = this.waitingClients.get(verification.phoneNumber); - if (resolve) { - resolve(true); // 인증 코드가 유효하면 true로 Long Polling 완료 - isValid = true; + if (timeDiff <= 5) { // 5분 이내이면 유효 + this.waitingClients.delete(verification.phoneNumber); + return { success: true, message: "인증 성공" }; // 인증 성공 + } else { + // 5분 초과 시 + throw new TimeOver();// 코드 유효시간이 지나면 TimeOver 예외 발생 } - this.waitingClients.delete(verification.phoneNumber); } - }); - return isValid; // 코드 검증 결과 반환 + } + // 일치하는 코드가 없으면 CodeNotFound 예외 발생 + throw new CodeNotFound(); } } \ No newline at end of file From 14813442ab3b09b35bd12db1689e0968d1653de9 Mon Sep 17 00:00:00 2001 From: ssojungg Date: Fri, 24 Nov 2023 00:28:50 +0900 Subject: [PATCH 18/18] =?UTF-8?q?feat:=20=EC=B4=88=EA=B8=B0=EC=84=B8?= =?UTF-8?q?=ED=8C=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 73 ++++++++++++++++++++++++++++++++--------------- package.json | 7 +++-- 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index a31e1a7..8d05ed4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "attendance-api", + "name": "nest-phone-verify-1", "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "attendance-api", + "name": "nest-phone-verify-1", "version": "0.0.1", "license": "UNLICENSED", "dependencies": { @@ -13,14 +13,15 @@ "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", - "@nestjs/swagger": "^7.1.13", - "@nestjs/typeorm": "^10.0.0", + "@nestjs/swagger": "^7.1.16", + "@nestjs/typeorm": "^10.0.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "dotenv": "^16.3.1", - "mysql2": "^3.6.1", + "mysql2": "^3.6.4", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", + "swagger-ui-express": "^5.0.0", "ts-mockito": "^2.6.1", "typeorm": "^0.3.17", "typeorm-transactional": "^0.4.1" @@ -1578,9 +1579,9 @@ } }, "node_modules/@nestjs/mapped-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.2.tgz", - "integrity": "sha512-V0izw6tWs6fTp9+KiiPUbGHWALy563Frn8X6Bm87ANLRuE46iuBMD5acKBDP5lKL/75QFvrzSJT7HkCbB0jTpg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.3.tgz", + "integrity": "sha512-40Zdqg98lqoF0+7ThWIZFStxgzisK6GG22+1ABO4kZiGF/Tu2FE+DYLw+Q9D94vcFWizJ+MSjNN4ns9r6hIGxw==", "peerDependencies": { "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", "class-transformer": "^0.4.0 || ^0.5.0", @@ -1689,15 +1690,15 @@ } }, "node_modules/@nestjs/swagger": { - "version": "7.1.13", - "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.1.13.tgz", - "integrity": "sha512-aHfW0rDZZKTuPVSkxutBCB16lBy5vrsHVoRF5RvPtH7U2cm4Vf+OnfhxKKuG2g2Xocn9sDL+JAyVlY2VN3ytTw==", + "version": "7.1.16", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.1.16.tgz", + "integrity": "sha512-f9KBk/BX9MUKPTj7tQNYJ124wV/jP5W2lwWHLGwe/4qQXixuDOo39zP55HIJ44LE7S04B7BOeUOo9GBJD/vRcw==", "dependencies": { - "@nestjs/mapped-types": "2.0.2", + "@nestjs/mapped-types": "2.0.3", "js-yaml": "4.1.0", "lodash": "4.17.21", "path-to-regexp": "3.2.0", - "swagger-ui-dist": "5.9.0" + "swagger-ui-dist": "5.9.1" }, "peerDependencies": { "@fastify/static": "^6.0.0", @@ -1747,11 +1748,11 @@ } }, "node_modules/@nestjs/typeorm": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-10.0.0.tgz", - "integrity": "sha512-WQU4HCDTz4UavsFzvGUKDHqi0MO5K47yFoPXdmh+Z/hCNO7SHCMmV9jLiLukM8n5nKUqJ3jDqiljkWBcZPdCtA==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-10.0.1.tgz", + "integrity": "sha512-YVFYL7D25VAVp5/G+KLXIgsRfYomA+VaFZBpm2rtwrrBOmkXNrxr7kuI2bBBO/Xy4kKBDe6wbvIVVFeEA7/ngA==", "dependencies": { - "uuid": "9.0.0" + "uuid": "9.0.1" }, "peerDependencies": { "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", @@ -1761,6 +1762,18 @@ "typeorm": "^0.3.0" } }, + "node_modules/@nestjs/typeorm/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -6480,9 +6493,9 @@ "dev": true }, "node_modules/mysql2": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.6.1.tgz", - "integrity": "sha512-O7FXjLtNkjcMBpLURwkXIhyVbX9i4lq4nNRCykPNOXfceq94kJ0miagmTEGCZieuO8JtwtXaZ41U6KT4eF9y3g==", + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.6.4.tgz", + "integrity": "sha512-x27dKYnowULCLFIdrtYKKNtUIIdfWSx5JR+UE94rJT09XPPnh4w4lJLHo97HAxdZ+3zlklRZOQN68WyLOeXu7w==", "dependencies": { "denque": "^2.1.0", "generate-function": "^2.3.1", @@ -8015,9 +8028,23 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.9.0.tgz", - "integrity": "sha512-NUHSYoe5XRTk/Are8jPJ6phzBh3l9l33nEyXosM17QInoV95/jng8+PuSGtbD407QoPf93MH3Bkh773OgesJpA==" + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.9.1.tgz", + "integrity": "sha512-5zAx+hUwJb9T3EAntc7TqYkV716CMqG6sZpNlAAMOMWkNXRYxGkN8ADIvD55dQZ10LxN90ZM/TQmN7y1gpICnw==" + }, + "node_modules/swagger-ui-express": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.0.tgz", + "integrity": "sha512-tsU9tODVvhyfkNSvf03E6FAk+z+5cU3lXAzMy6Pv4av2Gt2xA0++fogwC4qo19XuFf6hdxevPuVCSKFuMHJhFA==", + "dependencies": { + "swagger-ui-dist": ">=5.0.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0 || >=5.0.0-beta" + } }, "node_modules/symbol-observable": { "version": "4.0.0", diff --git a/package.json b/package.json index f6f92b7..60fa162 100644 --- a/package.json +++ b/package.json @@ -24,14 +24,15 @@ "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", - "@nestjs/swagger": "^7.1.13", - "@nestjs/typeorm": "^10.0.0", + "@nestjs/swagger": "^7.1.16", + "@nestjs/typeorm": "^10.0.1", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "dotenv": "^16.3.1", - "mysql2": "^3.6.1", + "mysql2": "^3.6.4", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", + "swagger-ui-express": "^5.0.0", "ts-mockito": "^2.6.1", "typeorm": "^0.3.17", "typeorm-transactional": "^0.4.1"