From f3ba1698bdcb618de8fcfcaedf2740a462fa57d4 Mon Sep 17 00:00:00 2001 From: 2swo Date: Wed, 8 Nov 2023 15:46:45 +0900 Subject: [PATCH 01/69] =?UTF-8?q?ADD(#95)=20=EB=8C=80=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EA=B3=BC=20=EC=88=98=EC=A0=95,=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/boards/entities/board.entity.ts | 12 +- src/comments/comment.module.ts | 8 +- .../controllers/comments.controller.ts | 44 ++++++- src/comments/dto/create-recomment-dto.ts | 6 + src/comments/dto/update-recomment-dto.ts | 6 + src/comments/entities/comment-like.entity.ts | 6 +- src/comments/entities/recomment.entity.ts | 12 +- .../repository/comments.repository.ts | 1 - .../repository/recomments.repository.ts | 48 ++++++++ src/comments/services/recomments.services.ts | 58 +++++++++ .../add-recomment-decorators.ts | 103 ++++++++++++++++ .../delete-recomment-decoratro.ts | 85 ++++++++++++++ ...atros.ts => get-all-comment-decorators.ts} | 0 ...oratros.ts => patch-comment-decorators.ts} | 37 ++---- .../patch-recomment-decorator.ts | 111 ++++++++++++++++++ .../notice/entities/board-notice.entity.ts | 2 +- .../notice/entities/comment-notice.entity.ts | 30 ----- src/config/swagger.ts | 12 +- src/config/typeorm.config.ts | 13 +- 19 files changed, 512 insertions(+), 82 deletions(-) create mode 100644 src/comments/dto/create-recomment-dto.ts create mode 100644 src/comments/dto/update-recomment-dto.ts create mode 100644 src/comments/repository/recomments.repository.ts create mode 100644 src/comments/services/recomments.services.ts create mode 100644 src/comments/swagger-decoratros/add-recomment-decorators.ts create mode 100644 src/comments/swagger-decoratros/delete-recomment-decoratro.ts rename src/comments/swagger-decoratros/{get-all-comment-decoratros.ts => get-all-comment-decorators.ts} (100%) rename src/comments/swagger-decoratros/{patch-comment-decoratros.ts => patch-comment-decorators.ts} (64%) create mode 100644 src/comments/swagger-decoratros/patch-recomment-decorator.ts delete mode 100644 src/common/notice/entities/comment-notice.entity.ts diff --git a/src/boards/entities/board.entity.ts b/src/boards/entities/board.entity.ts index fb6bc4c..9a76c24 100644 --- a/src/boards/entities/board.entity.ts +++ b/src/boards/entities/board.entity.ts @@ -12,6 +12,7 @@ import { UpdateDateColumn, } from 'typeorm'; import { BoardLike } from './board-like.entity'; +import { BoardNotification } from 'src/common/notice/entities/board-notice.entity'; @Entity({ name: 'board', @@ -37,6 +38,15 @@ export class Board { }) boardLike: BoardLike; + @OneToMany( + () => BoardNotification, + (BoardNotification) => BoardNotification.board, + { + onDelete: 'CASCADE', + }, + ) + boardNotification: BoardNotification; + @Index({ fulltext: true }) @Column('varchar') head: string; @@ -56,4 +66,4 @@ export class Board { @UpdateDateColumn({ name: 'updated_at' }) updateAt: Date; -} \ No newline at end of file +} diff --git a/src/comments/comment.module.ts b/src/comments/comment.module.ts index eb68558..c7ce4b4 100644 --- a/src/comments/comment.module.ts +++ b/src/comments/comment.module.ts @@ -1,19 +1,23 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { Recomment } from './entities/recomment.entity'; +import { ReComment } from './entities/recomment.entity'; import { Comment } from './entities/comment.entity'; import { CommentsController } from './controllers/comments.controller'; import { CommentsService } from './services/comments.services'; import { TokenService } from 'src/auth/services/token.service'; import { CommentsRepository } from './repository/comments.repository'; import { TokenRepository } from 'src/auth/repositories/token.repository'; +import { ReCommentsService } from './services/recomments.services'; +import { ReCommentsRepository } from './repository/recomments.repository'; @Module({ - imports: [TypeOrmModule.forFeature([Comment, Recomment])], + imports: [TypeOrmModule.forFeature([Comment, ReComment])], controllers: [CommentsController], providers: [ CommentsService, CommentsRepository, + ReCommentsService, + ReCommentsRepository, TokenService, TokenRepository, ], diff --git a/src/comments/controllers/comments.controller.ts b/src/comments/controllers/comments.controller.ts index 639c052..44edd8f 100644 --- a/src/comments/controllers/comments.controller.ts +++ b/src/comments/controllers/comments.controller.ts @@ -14,16 +14,22 @@ import { CommentsService } from '../services/comments.services'; import { TokenService } from 'src/auth/services/token.service'; import { CreateCommentDto } from '../dto/create-comment-dto'; import { ApiAddComment } from '../swagger-decoratros/add-comment-decorators'; -import { ApiGetAllComment } from '../swagger-decoratros/get-all-comment-decoratros'; +import { ApiGetAllComment } from '../swagger-decoratros/get-all-comment-decorators'; import { commentResponseDTO } from '../dto/get-all-comment-dto'; -import { ApiUpdateComment } from '../swagger-decoratros/patch-comment-decoratros'; +import { ApiUpdateComment } from '../swagger-decoratros/patch-comment-decorators'; import { ApiDeleteComment } from '../swagger-decoratros/delete-comment-decorator'; +import { ReCommentsService } from '../services/recomments.services'; +import { CreateReCommentDto } from '../dto/create-recomment-dto'; +import { ReComment } from '../entities/recomment.entity'; +import { ApiAddReComment } from '../swagger-decoratros/add-recomment-decorators'; +import { ApiUpdateReComment } from '../swagger-decoratros/patch-recomment-decorator'; @Controller('comments') @ApiTags('Comment API') export class CommentsController { constructor( private readonly commentsService: CommentsService, + private readonly recommentsService: ReCommentsService, private tokenService: TokenService, ) {} @@ -38,6 +44,21 @@ export class CommentsController { return await this.commentsService.create(createCommentDto, userId, boardId); } + @Post('/Re') + @ApiAddReComment() + async createReComment( + @Headers('access_token') accessToken: string, + @Query('commentId') commentId: number, + @Body() createReCommentDto: CreateReCommentDto, + ): Promise { + const userId = await this.tokenService.decodeToken(accessToken); + return await this.recommentsService.create( + createReCommentDto, + userId, + commentId, + ); + } + @Get('') @ApiGetAllComment() async getComment( @@ -57,6 +78,15 @@ export class CommentsController { return this.commentsService.updateComment(commentId, commentData); } + @Patch('/Re') + @ApiUpdateReComment() + async updateReComment( + @Query('reCommentId') reCommentId: number, + @Body() recommentData: Partial, + ): Promise { + return this.recommentsService.updateReComment(reCommentId, recommentData); + } + @Delete('') @ApiDeleteComment() async deleteComment( @@ -66,4 +96,14 @@ export class CommentsController { const userId = await this.tokenService.decodeToken(accessToken); await this.commentsService.deleteComment(commentId, userId); } + + @Delete('/Re') + @ApiDeleteComment() + async deleteReComment( + @Query('reCommentId') reCommentId: number, + @Headers('access_token') accessToken: string, + ) { + const userId = await this.tokenService.decodeToken(accessToken); + await this.recommentsService.deleteReComment(reCommentId, userId); + } } diff --git a/src/comments/dto/create-recomment-dto.ts b/src/comments/dto/create-recomment-dto.ts new file mode 100644 index 0000000..d8f9658 --- /dev/null +++ b/src/comments/dto/create-recomment-dto.ts @@ -0,0 +1,6 @@ +import { IsString } from 'class-validator'; + +export class CreateReCommentDto { + @IsString() + content: string; +} diff --git a/src/comments/dto/update-recomment-dto.ts b/src/comments/dto/update-recomment-dto.ts new file mode 100644 index 0000000..f678732 --- /dev/null +++ b/src/comments/dto/update-recomment-dto.ts @@ -0,0 +1,6 @@ +import { IsString } from 'class-validator'; + +export class UpdateReCommentDto { + @IsString() + content: string; +} diff --git a/src/comments/entities/comment-like.entity.ts b/src/comments/entities/comment-like.entity.ts index 57cc379..e2f9547 100644 --- a/src/comments/entities/comment-like.entity.ts +++ b/src/comments/entities/comment-like.entity.ts @@ -1,7 +1,7 @@ import { User } from 'src/users/entities/user.entity'; import { Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; import { Comment } from './comment.entity'; -import { Recomment } from './recomment.entity'; +import { ReComment } from './recomment.entity'; @Entity({ name: 'comment_like' }) export class CommentLike { @@ -16,7 +16,7 @@ export class CommentLike { @JoinColumn({ name: 'comment_id' }) commentId: Comment; - @ManyToOne(() => Recomment) + @ManyToOne(() => ReComment) @JoinColumn({ name: 'recomment_id' }) - recommentId: Recomment; + recommentId: ReComment; } diff --git a/src/comments/entities/recomment.entity.ts b/src/comments/entities/recomment.entity.ts index b8b55a0..3b9a2b3 100644 --- a/src/comments/entities/recomment.entity.ts +++ b/src/comments/entities/recomment.entity.ts @@ -12,17 +12,23 @@ import { @Entity({ name: 'recomment', }) -export class Recomment { +export class ReComment { @PrimaryGeneratedColumn() id: number; @ManyToOne(() => User) @JoinColumn({ name: 'user_id' }) - userId: User; + user: User; + + @Column({ name: 'user_id' }) + userId: number; @ManyToOne(() => Comment) @JoinColumn({ name: 'comment_id' }) - commentId: Comment; + comment: Comment; + + @Column({ name: 'comment_id' }) + commentId: number; @Column() content: string; diff --git a/src/comments/repository/comments.repository.ts b/src/comments/repository/comments.repository.ts index e7399bf..9ec2e9c 100644 --- a/src/comments/repository/comments.repository.ts +++ b/src/comments/repository/comments.repository.ts @@ -24,7 +24,6 @@ export class CommentsRepository { .innerJoinAndSelect('comment.user', 'user') .innerJoinAndSelect('user.userImage', 'userImage') .where('comment.boardId = :boardId', { boardId }); - return query.getMany(); } diff --git a/src/comments/repository/recomments.repository.ts b/src/comments/repository/recomments.repository.ts new file mode 100644 index 0000000..821f7be --- /dev/null +++ b/src/comments/repository/recomments.repository.ts @@ -0,0 +1,48 @@ +import { Injectable } from '@nestjs/common'; +import { EntityManager } from 'typeorm'; +import { ReComment } from '../entities/recomment.entity'; +import { CreateReCommentDto } from '../dto/create-recomment-dto'; + +@Injectable() +export class ReCommentsRepository { + constructor(private readonly entityManager: EntityManager) {} + async createReComment( + recommentData: CreateReCommentDto, + userId: number, + commentId: number, + ): Promise { + const reComment = new ReComment(); + reComment.content = recommentData.content; + reComment.userId = userId; + reComment.commentId = commentId; + return await this.entityManager.save(ReComment, reComment); + } + + async findOneReComment(id: number): Promise { + return this.entityManager.findOne(ReComment, { + relations: ['user', 'user.userImage'], + where: { id }, + }); + } + + async updateReComment( + id: number, + commentData: Partial, + ): Promise { + const existingReComment = await this.entityManager.findOne(ReComment, { + relations: ['user', 'user.userImage'], + where: { id }, + }); + for (const key in commentData) { + if (commentData.hasOwnProperty(key)) { + existingReComment[key] = commentData[key]; + } + } + await this.entityManager.save(ReComment, existingReComment); + return existingReComment; + } + + async deleteReComment(reComment: ReComment): Promise { + await this.entityManager.remove(ReComment, reComment); + } +} diff --git a/src/comments/services/recomments.services.ts b/src/comments/services/recomments.services.ts new file mode 100644 index 0000000..9c5df8b --- /dev/null +++ b/src/comments/services/recomments.services.ts @@ -0,0 +1,58 @@ +import { Injectable } from '@nestjs/common'; +import { CreateReCommentDto } from '../dto/create-recomment-dto'; + +import { ReComment } from '../entities/recomment.entity'; +import { ReCommentsRepository } from '../repository/recomments.repository'; +import { UpdateReCommentDto } from '../dto/update-recomment-dto'; + +@Injectable() +export class ReCommentsService { + constructor(private reCommentRepository: ReCommentsRepository) {} + async create( + recommentData: CreateReCommentDto, + userId: number, + commentId: number, + ): Promise { + try { + return await this.reCommentRepository.createReComment( + recommentData, + userId, + commentId, + ); + } catch (error) { + console.log(error); + } + } + + async updateReComment( + reCommentId: number, + reCommentData: Partial, + ): Promise { + const existingComment = + await this.reCommentRepository.findOneReComment(reCommentId); + for (const key in reCommentData) { + if (reCommentData.hasOwnProperty(key)) { + existingComment[key] = reCommentData[key]; + } + } + const updatedReComment = await this.reCommentRepository.updateReComment( + reCommentId, + existingComment, + ); + return updatedReComment; + } + + async deleteReComment(reCommentId: number, userId: number): Promise { + const reComment = + await this.reCommentRepository.findOneReComment(reCommentId); + + if (!reComment) { + throw new Error('존재하지 않는 댓글입니다.'); + } + + if (reComment.userId !== userId) { + throw new Error('작성한 댓글이 아닙니다.'); + } + await this.reCommentRepository.deleteReComment(reComment); + } +} diff --git a/src/comments/swagger-decoratros/add-recomment-decorators.ts b/src/comments/swagger-decoratros/add-recomment-decorators.ts new file mode 100644 index 0000000..bb82c15 --- /dev/null +++ b/src/comments/swagger-decoratros/add-recomment-decorators.ts @@ -0,0 +1,103 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiBody, + ApiHeaders, + ApiOperation, + ApiParam, + ApiResponse, +} from '@nestjs/swagger'; + +export function ApiAddReComment() { + return applyDecorators( + ApiOperation({ + summary: '대댓글을 생성하는 API', + description: '대댓글을 생성하는 API', + }), + ApiResponse({ + status: 200, + description: '성공적으로 대댓글을 생성한 경우', + content: { + JSON: { + example: { + content: '대댓글 1차시도', + userId: '작성한 userId가 넘어옵니다', + commentId: '작성한 보드의 commentId가 넘어옵니다', + id: '대댓글 고유 id', + createAt: '작성한 시간이 넘어옵니다', + }, + }, + }, + }), + ApiResponse({ + status: 401, + description: '우리 서비스의 액세스 토큰이 아닌 경우', + content: { + JSON: { + example: { statusCode: 401, message: '유효하지 않은 토큰입니다.' }, + }, + }, + }), + ApiResponse({ + status: 403, + description: '만료된 액세스 토큰인 경우', + content: { + JSON: { + example: { statusCode: 403, message: '만료된 토큰입니다.' }, + }, + }, + }), + ApiResponse({ + status: 404, + description: 'DB에서 보드를 찾을 수 없는 경우', + content: { + JSON: { + example: { statusCode: 404, message: '보드를 찾을 수 없습니다.' }, + }, + }, + }), + ApiResponse({ + status: 411, + description: '액세스 토큰이 제공되지 않은 경우', + content: { + JSON: { + example: { statusCode: 411, message: '토큰이 제공되지 않았습니다.' }, + }, + }, + }), + ApiResponse({ + status: 500, + description: '대댓글을 생성하는 중 오류가 발생한 경우', + content: { + JSON: { + example: { + statusCode: 500, + message: '대댓글을 생성하는 중 오류가 발생했습니다.', + }, + }, + }, + }), + ApiHeaders([ + { + name: 'access_token', + description: '액세스 토큰', + required: true, + example: '여기에 액세스 토큰', + }, + ]), + ApiParam({ + name: 'commentId', + description: '대댓글을 추가할 댓글의 ID', + }), + ApiBody({ + schema: { + type: 'object', + properties: { + content: { type: 'string' }, + }, + example: { + content: '추가할 대댓글 입력입니다.', + }, + }, + }), + ); +} diff --git a/src/comments/swagger-decoratros/delete-recomment-decoratro.ts b/src/comments/swagger-decoratros/delete-recomment-decoratro.ts new file mode 100644 index 0000000..543f300 --- /dev/null +++ b/src/comments/swagger-decoratros/delete-recomment-decoratro.ts @@ -0,0 +1,85 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiHeaders, + ApiOperation, + ApiParam, + ApiResponse, +} from '@nestjs/swagger'; + +export function ApiDeleteReComment() { + return applyDecorators( + ApiOperation({ + summary: '대댓글을 삭제하는 API', + description: '대댓글을 삭제하는 API', + }), + ApiResponse({ + status: 200, + description: '성공적으로 대댓글을 삭제한 경우', + content: { + JSON: { + example: {}, + }, + }, + }), + ApiResponse({ + status: 401, + description: '우리 서비스의 액세스 토큰이 아닌 경우', + content: { + JSON: { + example: { statusCode: 401, message: '유효하지 않은 토큰입니다.' }, + }, + }, + }), + ApiResponse({ + status: 403, + description: '만료된 액세스 토큰인 경우', + content: { + JSON: { + example: { statusCode: 403, message: '만료된 토큰입니다.' }, + }, + }, + }), + ApiResponse({ + status: 404, + description: 'DB에서 댓글을 찾을 수 없는 경우', + content: { + JSON: { + example: { statusCode: 404, message: '댓글을 찾을 수 없습니다.' }, + }, + }, + }), + ApiResponse({ + status: 411, + description: '액세스 토큰이 제공되지 않은 경우', + content: { + JSON: { + example: { statusCode: 411, message: '토큰이 제공되지 않았습니다.' }, + }, + }, + }), + ApiResponse({ + status: 500, + description: '대댓글을 삭제하는 오류가 발생한 경우', + content: { + JSON: { + example: { + statusCode: 500, + message: '대댓글을 삭제하는 중 오류가 발생했습니다.', + }, + }, + }, + }), + ApiHeaders([ + { + name: 'access_token', + description: '액세스 토큰', + required: true, + example: '여기에 액세스 토큰', + }, + ]), + ApiParam({ + name: 'reCommentId', + description: '삭제할 대댓글의 ID', + }), + ); +} diff --git a/src/comments/swagger-decoratros/get-all-comment-decoratros.ts b/src/comments/swagger-decoratros/get-all-comment-decorators.ts similarity index 100% rename from src/comments/swagger-decoratros/get-all-comment-decoratros.ts rename to src/comments/swagger-decoratros/get-all-comment-decorators.ts diff --git a/src/comments/swagger-decoratros/patch-comment-decoratros.ts b/src/comments/swagger-decoratros/patch-comment-decorators.ts similarity index 64% rename from src/comments/swagger-decoratros/patch-comment-decoratros.ts rename to src/comments/swagger-decoratros/patch-comment-decorators.ts index fc02913..e5d9c1f 100644 --- a/src/comments/swagger-decoratros/patch-comment-decoratros.ts +++ b/src/comments/swagger-decoratros/patch-comment-decorators.ts @@ -19,40 +19,19 @@ export function ApiUpdateComment() { content: { JSON: { example: { - id: '보드의 id를 받아옵니다.', - userId: '유저의 id를 받아옵니다.', - head: '수정한 게시물 제목입니다.', - body: '수정한 게시물 본문입니다.', - main_category: '수정한 메인 카테고리입니다.', - sub_category: '수정한 서브 카테고리입니다.', - createAt: '2023-10-29T17:07:53.964Z', - updateAt: '수정한 시간이 됩니다.', + id: '댓글 고유 id', + userId: '댓글 작성한 유저 id', + boardId: '댓글을 작성한 보드의 id', + content: '수정수정수정수정수정수정수정', + createAt: '2023-11-06T20:51:35.573Z', user: { name: '이승우', userImage: { - id: '유저이미지 고유 id가 number로 들어옵니다', - userId: '유저의 고유 id가 number로 들어옵니다', - imageUrl: - '유저의 고유 프로필 사진 URL이 string으로 들어옵니다.', + id: '유저이미지 고유 id', + userId: '유저 고유 id', + imageUrl: '프로필 이미지 url', }, }, - boardImages: [ - { - id: '고유 보드 이미지 id', - boardId: '수정한 보드의 id', - imageUrl: 's3에 저장된 보드 이미지 URL', - }, - { - id: '고유 보드 이미지 id', - boardId: '수정한 보드의 id', - imageUrl: 's3에 저장된 보드 이미지 URL', - }, - { - id: '고유 보드 이미지 id', - boardId: '수정한 보드의 id', - imageUrl: 's3에 저장된 보드 이미지 URL', - }, - ], }, }, }, diff --git a/src/comments/swagger-decoratros/patch-recomment-decorator.ts b/src/comments/swagger-decoratros/patch-recomment-decorator.ts new file mode 100644 index 0000000..ec22c5f --- /dev/null +++ b/src/comments/swagger-decoratros/patch-recomment-decorator.ts @@ -0,0 +1,111 @@ +import { applyDecorators } from '@nestjs/common'; +import { + ApiBody, + ApiHeaders, + ApiOperation, + ApiParam, + ApiResponse, +} from '@nestjs/swagger'; + +export function ApiUpdateReComment() { + return applyDecorators( + ApiOperation({ + summary: '대댓글을 수정하는 API', + description: '대댓글을 수정하는 API', + }), + ApiResponse({ + status: 200, + description: '대댓글의 내용을 성공적으로 수정한 경우', + content: { + JSON: { + example: { + id: '대댓글 고유 id', + userId: '대댓글 작성한 유저 id', + commentId: '대댓글을 작성한 댓글의 id', + content: '수정수정수정수정수정수정수정', + createAt: '2023-11-06T20:51:35.573Z', + user: { + name: '이승우', + userImage: { + id: '유저이미지 고유 id', + userId: '유저 고유 id', + imageUrl: '프로필 이미지 url', + }, + }, + }, + }, + }, + }), + ApiResponse({ + status: 401, + description: '우리 서비스의 액세스 토큰이 아닌 경우', + content: { + JSON: { + example: { statusCode: 401, message: '유효하지 않은 토큰입니다.' }, + }, + }, + }), + ApiResponse({ + status: 403, + description: '만료된 액세스 토큰인 경우', + content: { + JSON: { + example: { statusCode: 403, message: '만료된 토큰입니다.' }, + }, + }, + }), + ApiResponse({ + status: 404, + description: 'DB에서 사용자를 찾을 수 없는 경우', + content: { + JSON: { + example: { statusCode: 404, message: '사용자를 찾을 수 없습니다.' }, + }, + }, + }), + ApiResponse({ + status: 411, + description: '액세스 토큰이 제공되지 않은 경우', + content: { + JSON: { + example: { statusCode: 411, message: '토큰이 제공되지 않았습니다.' }, + }, + }, + }), + ApiResponse({ + status: 500, + description: '대댓글 수정중 오류가 발생했습니다', + content: { + JSON: { + example: { + statusCode: 500, + message: '대댓글 수정 중 오류가 발생했습니다.', + }, + }, + }, + }), + ApiHeaders([ + { + name: 'access_token', + description: '액세스 토큰', + required: true, + example: '여기에 액세스 토큰', + }, + ]), + ApiParam({ + name: 'reCommentId', + description: '수정할 대댓글의 ID', + }), + ApiBody({ + schema: { + type: 'object', + properties: { + content: { type: 'string' }, + }, + example: { + content: '수정수정수정수정수정수정수정', + }, + }, + }), + ); +} diff --git a/src/common/notice/entities/board-notice.entity.ts b/src/common/notice/entities/board-notice.entity.ts index bee577c..441d08a 100644 --- a/src/common/notice/entities/board-notice.entity.ts +++ b/src/common/notice/entities/board-notice.entity.ts @@ -24,7 +24,7 @@ export class BoardNotification { @Column({ name: 'board_id' }) boardId: number; - @ManyToOne(() => Board) + @ManyToOne(() => Board, (board) => board.boardNotification) @JoinColumn({ name: 'board_id' }) board: Board; diff --git a/src/common/notice/entities/comment-notice.entity.ts b/src/common/notice/entities/comment-notice.entity.ts deleted file mode 100644 index a6de409..0000000 --- a/src/common/notice/entities/comment-notice.entity.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Comment } from 'src/comments/entities/comment.entity'; -import { User } from 'src/users/entities/user.entity'; -import { - CreateDateColumn, - Entity, - JoinColumn, - ManyToOne, - PrimaryGeneratedColumn, -} from 'typeorm'; - -@Entity({ name: 'comment_notification' }) -export class CommentNotification { - @PrimaryGeneratedColumn() - id: number; - - @ManyToOne(() => Comment) - @JoinColumn({ name: 'comment_id' }) - commentId: Comment; - - @ManyToOne(() => User) - @JoinColumn({ name: 'sender_id' }) - senderId: User; - - @ManyToOne(() => User) - @JoinColumn({ name: 'receiver_id' }) - receiverId: User; - - @CreateDateColumn({ name: 'create_at' }) - createAt: Date; -} \ No newline at end of file diff --git a/src/config/swagger.ts b/src/config/swagger.ts index efbb92f..4ae03dc 100644 --- a/src/config/swagger.ts +++ b/src/config/swagger.ts @@ -7,7 +7,17 @@ export function setupSwagger(app: INestApplication): void { .setDescription('모던애자일 6기 메인프로젝트 API 문서') .setVersion('1.0') .addCookieAuth('connect.sid') - .addBearerAuth({ type: 'http', scheme: 'bearer', bearerFormat: 'JWT', name: 'JWT', description: '여기에 토큰 입력', in: 'header' }, 'access-token') + .addBearerAuth( + { + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + name: 'JWT', + description: '여기에 토큰 입력', + in: 'header', + }, + 'access-token', + ) .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('swagger', app, document); diff --git a/src/config/typeorm.config.ts b/src/config/typeorm.config.ts index 877dde2..09030f3 100644 --- a/src/config/typeorm.config.ts +++ b/src/config/typeorm.config.ts @@ -5,17 +5,14 @@ import { Token } from 'src/auth/entities/token.entity'; import { BoardImage } from 'src/boards/entities/board-image.entity'; import { BoardLike } from 'src/boards/entities/board-like.entity'; import { Board } from 'src/boards/entities/board.entity'; -// import { BoardRepository } from 'src/boards/repository/boards.repository'; import { CommentLike } from 'src/comments/entities/comment-like.entity'; import { Comment } from 'src/comments/entities/comment.entity'; -import { Recomment } from 'src/comments/entities/recomment.entity'; +import { ReComment } from 'src/comments/entities/recomment.entity'; import { Friend } from 'src/friends/entities/friends.entity'; import { BoardNotification } from 'src/common/notice/entities/board-notice.entity'; -import { CommentNotification } from 'src/common/notice/entities/comment-notice.entity'; import { UserImage } from 'src/users/entities/user-image.entity'; import { User } from 'src/users/entities/user.entity'; -// .env 파일 로드 dotenv.config(); export const TypeORMconfig: TypeOrmModuleOptions = { @@ -34,11 +31,9 @@ export const TypeORMconfig: TypeOrmModuleOptions = { BoardLike, Comment, CommentLike, - Recomment, + ReComment, Friend, BoardNotification, - CommentNotification, - // BoardRepository, - ], // 여기에 엔티티들을 추가해야 합니다. - synchronize: process.env.NODE_ENV === 'false', + ], + synchronize: process.env.NODE_ENV === 'true', }; From 5139e7572d826e1cde5563166ad116ac298cd1a0 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Wed, 8 Nov 2023 23:57:52 +0900 Subject: [PATCH 02/69] =?UTF-8?q?modify(#60):=20=EC=86=8C=EC=BC=93=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=9A=A9=20client.js=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client.js | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 src/client.js diff --git a/src/client.js b/src/client.js deleted file mode 100644 index 1bf440f..0000000 --- a/src/client.js +++ /dev/null @@ -1,36 +0,0 @@ -const namespaceURL = 'http://localhost:3000/ch-653383a4468680bc4e9f8491'; -const socket = io(namespaceURL); - -socket.on('connect', () => { - console.log('Connected to WebSocket server'); - - // 예제로 'login' 이벤트를 보내는 방법 - socket.emit('login', { - userId: 1, - rooms: ['653383a4468680bc4e9f8491'], - }); - // socket.emit('messages', { - // roomId: '650bde3798dd4c34439c30dc', - // message: 'asdf', - // }); - // console.log(socket); - - // 해당 네임스페이스의 이벤트를 수신 -}); - -socket.on('hello', (data) => { - console.log('Received hello message:', data); -}); - -socket.on('msgNoti', (data) => { - console.log('Received message notification:', data); -}); - -socket.on('disconnect', () => { - console.log('Disconnected from WebSocket server'); -}); - -socket.on('message', (data) => { - console.log('Received message:', data); - // 여기에서 메시지를 처리하거나 화면에 표시하는 로직을 추가하세요.. -}); From f24b23e47ee40548c05a7746b61e58cbfcf78478 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Thu, 9 Nov 2023 11:06:27 +0900 Subject: [PATCH 03/69] =?UTF-8?q?refactor(#46):=20controller=EC=97=90=20Ap?= =?UTF-8?q?iTags=EC=B6=94=EA=B0=80,=20=EA=B2=80=EC=83=89=20=EC=8B=9C=20lim?= =?UTF-8?q?it=EA=B0=92=2030=EC=9C=BC=EB=A1=9C=20=EA=B3=A0=EC=A0=95,=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=EA=B9=8C=EC=A7=80=20?= =?UTF-8?q?=EA=B3=A0=EB=A0=A4=ED=95=98=EC=97=AC=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/search/controllers/search.controller.ts | 15 +++++---- src/search/repositories/search.repository.ts | 16 ++++++++-- src/search/services/search.service.ts | 32 +++++++++++++++---- .../search-boards-by-body.decorator.ts | 4 +-- .../search-boards-by-head.decorator.ts | 6 ++-- 5 files changed, 53 insertions(+), 20 deletions(-) diff --git a/src/search/controllers/search.controller.ts b/src/search/controllers/search.controller.ts index 5e2fe91..bb158fe 100644 --- a/src/search/controllers/search.controller.ts +++ b/src/search/controllers/search.controller.ts @@ -1,6 +1,7 @@ import { Controller, Get, + Param, ParseIntPipe, Query, UsePipes, @@ -9,30 +10,32 @@ import { import { SearchService } from '../services/search.service'; import { ApiSearchBoardsByHead } from '../swagger-decorators/search-boards-by-head.decorator'; import { ApiSearchBoardsByBody } from '../swagger-decorators/search-boards-by-body.decorator'; +import { ApiTags } from '@nestjs/swagger'; +@ApiTags('SEARCH') @UsePipes(ValidationPipe) @Controller('search') export class SearchController { constructor(private searchService: SearchService) {} @ApiSearchBoardsByHead() - @Get('boards/head') + @Get('boards/:category/head') async searchBoardsByHead( + @Param('category') category: string, @Query('searchQuery') searchQuery: string, @Query('page', ParseIntPipe) page: number, - @Query('limit', ParseIntPipe) limit: number, ) { - return this.searchService.searchBoardsByHead(searchQuery, page, limit); + return this.searchService.searchBoardsByHead(category, searchQuery, page); } @ApiSearchBoardsByBody() - @Get('boards/body') + @Get('boards/:category/body') async searchBoardsByBody( + @Param('category') category: string, @Query('searchQuery') searchQuery: string, @Query('page', ParseIntPipe) page: number, - @Query('limit', ParseIntPipe) limit: number, ) { - return this.searchService.searchBoardsByBody(searchQuery, page, limit); + return this.searchService.searchBoardsByBody(category, searchQuery, page); } @Get('users') diff --git a/src/search/repositories/search.repository.ts b/src/search/repositories/search.repository.ts index 5c6d94c..05fecb3 100644 --- a/src/search/repositories/search.repository.ts +++ b/src/search/repositories/search.repository.ts @@ -6,7 +6,12 @@ import { EntityManager } from 'typeorm'; @Injectable() export class SearchRepository { constructor(private entityManager: EntityManager) {} - async searchBoardsByHead(searchQuery: string, skip: number, take: number) { + async searchBoardsByHead( + category: string, + searchQuery: string, + skip: number, + take: number, + ) { const boardRepository = this.entityManager.getRepository(Board); return boardRepository @@ -18,12 +23,18 @@ export class SearchRepository { .where(`MATCH(head) AGAINST (:searchQuery IN BOOLEAN MODE)`, { searchQuery, }) + .andWhere('board.main_category = :category', { category }) .skip(skip) .take(take) .getManyAndCount(); } - async searchBoardsByBody(searchQuery: string, skip: number, take: number) { + async searchBoardsByBody( + category: string, + searchQuery: string, + skip: number, + take: number, + ) { const boardRepository = this.entityManager.getRepository(Board); return boardRepository @@ -35,6 +46,7 @@ export class SearchRepository { .where(`MATCH(body) AGAINST (:searchQuery IN BOOLEAN MODE)`, { searchQuery, }) + .andWhere('board.main_category = :category', { category }) .skip(skip) .take(take) .getManyAndCount(); diff --git a/src/search/services/search.service.ts b/src/search/services/search.service.ts index c3df90a..fcaaa42 100644 --- a/src/search/services/search.service.ts +++ b/src/search/services/search.service.ts @@ -9,12 +9,21 @@ export class SearchService { private searchRepository: SearchRepository, private boardLikesRepository: BoardsLikeRepository, ) {} - async searchBoardsByHead(searchQuery: string, page: number, limit: number) { - const take = limit; + async searchBoardsByHead( + category: string, + searchQuery: string, + page: number, + ) { + const take = 30; const skip = page <= 0 ? (page = 0) : (page - 1) * take; const [returnedBoards, total] = - await this.searchRepository.searchBoardsByHead(searchQuery, skip, take); + await this.searchRepository.searchBoardsByHead( + category, + searchQuery, + skip, + take, + ); const last_page = Math.ceil(total / take); @@ -58,14 +67,23 @@ export class SearchService { } } - async searchBoardsByBody(searchQuery: string, page: number, limit: number) { - const take = limit; + async searchBoardsByBody( + category: string, + searchQuery: string, + page: number, + ) { + const take = 30; const skip = page <= 0 ? (page = 0) : (page - 1) * take; const [returnedBoards, total] = - await this.searchRepository.searchBoardsByBody(searchQuery, skip, take); + await this.searchRepository.searchBoardsByBody( + category, + searchQuery, + skip, + take, + ); - const last_page = Math.ceil(total / limit); + const last_page = Math.ceil(total / take); const boardResponse: BoardResponseDTO[] = await Promise.all( returnedBoards.map(async (board) => { diff --git a/src/search/swagger-decorators/search-boards-by-body.decorator.ts b/src/search/swagger-decorators/search-boards-by-body.decorator.ts index c087539..f63576b 100644 --- a/src/search/swagger-decorators/search-boards-by-body.decorator.ts +++ b/src/search/swagger-decorators/search-boards-by-body.decorator.ts @@ -5,8 +5,8 @@ export function ApiSearchBoardsByBody() { return applyDecorators( ApiOperation({ summary: '게시글 검색 API', - description: `Query String으로 입력된 값을 토대로 게시글의 본문에 일치하는 값을 조회합니다. - ex)'흑돼지고기' 검색 - '흑돼', '돼지', '지고', '고기'로 검색 (정확성 순으로 정렬됨)`, + description: `Query String의 내용과 Param으로 입력된 카테고리를 토대로 게시글의 본문에 일치하는 값을 조회합니다. + ex)'흑돼지 고기' 검색 - '흑돼지', '고기' 라는 단어 단위로 검색. (정확성 순으로 정렬됨)`, }), ApiResponse({ status: 200, diff --git a/src/search/swagger-decorators/search-boards-by-head.decorator.ts b/src/search/swagger-decorators/search-boards-by-head.decorator.ts index 488bee1..f82fcbe 100644 --- a/src/search/swagger-decorators/search-boards-by-head.decorator.ts +++ b/src/search/swagger-decorators/search-boards-by-head.decorator.ts @@ -5,12 +5,12 @@ export function ApiSearchBoardsByHead() { return applyDecorators( ApiOperation({ summary: '게시글 검색 API', - description: `Query String으로 입력된 값을 토대로 게시글의 제목에 일치하는 값을 조회합니다. - ex)'흑돼지고기' 검색 - '흑돼', '돼지', '지고', '고기'로 검색 (정확성 순으로 정렬됨)`, + description: `Query String의 내용과 Param으로 입력된 카테고리를 토대로 게시글의 제목에 일치하는 값을 조회합니다. + ex)'흑돼지 고기' 검색 - '흑돼지', '고기' 라는 단어 단위로 검색. (정확성 순으로 정렬됨)`, }), ApiResponse({ status: 200, - description: '성공적으로 검색한 게시글(제목 기준) 조회', + description: '성공적으로 검색한 게시글(제목, 카테고리 기준) 조회', content: { JSON: { example: { From 425f7881f826c86b82df50721dd74a238945ac01 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Thu, 9 Nov 2023 11:06:51 +0900 Subject: [PATCH 04/69] =?UTF-8?q?modify(#46):=20take=EA=B0=92=2016?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=B4=EC=84=9C=20?= =?UTF-8?q?=EA=B3=A0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/search/services/search.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search/services/search.service.ts b/src/search/services/search.service.ts index fcaaa42..7dd8e13 100644 --- a/src/search/services/search.service.ts +++ b/src/search/services/search.service.ts @@ -14,7 +14,7 @@ export class SearchService { searchQuery: string, page: number, ) { - const take = 30; + const take = 16; const skip = page <= 0 ? (page = 0) : (page - 1) * take; const [returnedBoards, total] = @@ -72,7 +72,7 @@ export class SearchService { searchQuery: string, page: number, ) { - const take = 30; + const take = 16; const skip = page <= 0 ? (page = 0) : (page - 1) * take; const [returnedBoards, total] = From 02de1f16af4faefa792d0964c41a23c453b84ea8 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Thu, 9 Nov 2023 11:50:38 +0900 Subject: [PATCH 05/69] =?UTF-8?q?modify(#46):=20=EC=86=8C=EC=BC=93=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=9A=A9=20client.js=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client.js | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 src/client.js diff --git a/src/client.js b/src/client.js deleted file mode 100644 index 1bf440f..0000000 --- a/src/client.js +++ /dev/null @@ -1,36 +0,0 @@ -const namespaceURL = 'http://localhost:3000/ch-653383a4468680bc4e9f8491'; -const socket = io(namespaceURL); - -socket.on('connect', () => { - console.log('Connected to WebSocket server'); - - // 예제로 'login' 이벤트를 보내는 방법 - socket.emit('login', { - userId: 1, - rooms: ['653383a4468680bc4e9f8491'], - }); - // socket.emit('messages', { - // roomId: '650bde3798dd4c34439c30dc', - // message: 'asdf', - // }); - // console.log(socket); - - // 해당 네임스페이스의 이벤트를 수신 -}); - -socket.on('hello', (data) => { - console.log('Received hello message:', data); -}); - -socket.on('msgNoti', (data) => { - console.log('Received message notification:', data); -}); - -socket.on('disconnect', () => { - console.log('Disconnected from WebSocket server'); -}); - -socket.on('message', (data) => { - console.log('Received message:', data); - // 여기에서 메시지를 처리하거나 화면에 표시하는 로직을 추가하세요.. -}); From 44b9c55c86abba6e3596e84fc8738b8d16c75f91 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Thu, 9 Nov 2023 12:01:19 +0900 Subject: [PATCH 06/69] =?UTF-8?q?feat(#46):=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=EA=B2=8C=EC=8B=9C=ED=8C=90=20=EA=B2=80=EC=83=89=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/search/repositories/search.repository.ts | 29 +++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/search/repositories/search.repository.ts b/src/search/repositories/search.repository.ts index 05fecb3..7807ef3 100644 --- a/src/search/repositories/search.repository.ts +++ b/src/search/repositories/search.repository.ts @@ -13,7 +13,20 @@ export class SearchRepository { take: number, ) { const boardRepository = this.entityManager.getRepository(Board); - + if (category === '전체') { + return boardRepository + .createQueryBuilder('board') + .select() + .leftJoinAndSelect('board.user', 'user') + .leftJoinAndSelect('user.userImage', 'userImage') + .leftJoinAndSelect('board.boardImages', 'boardImages') + .where(`MATCH(head) AGAINST (:searchQuery IN BOOLEAN MODE)`, { + searchQuery, + }) + .skip(skip) + .take(take) + .getManyAndCount(); + } return boardRepository .createQueryBuilder('board') .select() @@ -37,6 +50,20 @@ export class SearchRepository { ) { const boardRepository = this.entityManager.getRepository(Board); + if (category === '전체') { + return boardRepository + .createQueryBuilder('board') + .select() + .leftJoinAndSelect('board.user', 'user') + .leftJoinAndSelect('user.userImage', 'userImage') + .leftJoinAndSelect('board.boardImages', 'boardImages') + .where(`MATCH(body) AGAINST (:searchQuery IN BOOLEAN MODE)`, { + searchQuery, + }) + .skip(skip) + .take(take) + .getManyAndCount(); + } return boardRepository .createQueryBuilder('board') .select() From bbe3665b4b7536e48d52e7354ee8cd278a3f6890 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Thu, 9 Nov 2023 13:17:14 +0900 Subject: [PATCH 07/69] =?UTF-8?q?modify(#46):=20swagger=20=EB=AC=B8?= =?UTF-8?q?=EC=84=9C=20=EB=82=B4=EC=9A=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../swagger-decorators/search-boards-by-body.decorator.ts | 2 +- .../swagger-decorators/search-boards-by-head.decorator.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search/swagger-decorators/search-boards-by-body.decorator.ts b/src/search/swagger-decorators/search-boards-by-body.decorator.ts index f63576b..9e14feb 100644 --- a/src/search/swagger-decorators/search-boards-by-body.decorator.ts +++ b/src/search/swagger-decorators/search-boards-by-body.decorator.ts @@ -5,7 +5,7 @@ export function ApiSearchBoardsByBody() { return applyDecorators( ApiOperation({ summary: '게시글 검색 API', - description: `Query String의 내용과 Param으로 입력된 카테고리를 토대로 게시글의 본문에 일치하는 값을 조회합니다. + description: `Query String의 내용과 Param으로 입력된 카테고리를 토대로 게시글의 본문에 일치하는 값을 조회합니다(카테고리에 "전체" 입력 시 전체 게시판 검색). ex)'흑돼지 고기' 검색 - '흑돼지', '고기' 라는 단어 단위로 검색. (정확성 순으로 정렬됨)`, }), ApiResponse({ diff --git a/src/search/swagger-decorators/search-boards-by-head.decorator.ts b/src/search/swagger-decorators/search-boards-by-head.decorator.ts index f82fcbe..762ec06 100644 --- a/src/search/swagger-decorators/search-boards-by-head.decorator.ts +++ b/src/search/swagger-decorators/search-boards-by-head.decorator.ts @@ -5,7 +5,7 @@ export function ApiSearchBoardsByHead() { return applyDecorators( ApiOperation({ summary: '게시글 검색 API', - description: `Query String의 내용과 Param으로 입력된 카테고리를 토대로 게시글의 제목에 일치하는 값을 조회합니다. + description: `Query String의 내용과 Param으로 입력된 카테고리를 토대로 게시글의 제목에 일치하는 값을 조회합니다(카테고리에 "전체" 입력 시 전체 게시판 검색). ex)'흑돼지 고기' 검색 - '흑돼지', '고기' 라는 단어 단위로 검색. (정확성 순으로 정렬됨)`, }), ApiResponse({ From 313be0421abff176e5ed6984066c886cf578b79c Mon Sep 17 00:00:00 2001 From: NicoDora Date: Thu, 9 Nov 2023 13:41:10 +0900 Subject: [PATCH 08/69] =?UTF-8?q?refactor(#11):=20=EC=95=88=EC=93=B0?= =?UTF-8?q?=EB=8A=94=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 197 ++++------------------------------------------ package.json | 8 -- 2 files changed, 14 insertions(+), 191 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9585851..5f6a457 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,14 +15,12 @@ "@nestjs/core": "^10.0.0", "@nestjs/jwt": "^10.1.1", "@nestjs/mongoose": "^10.0.1", - "@nestjs/passport": "^10.0.2", "@nestjs/platform-express": "^10.0.0", "@nestjs/platform-socket.io": "^10.2.5", "@nestjs/schedule": "^4.0.0", "@nestjs/swagger": "^7.1.10", "@nestjs/typeorm": "^10.0.0", "@nestjs/websockets": "^10.2.5", - "@types/passport-jwt": "^3.0.12", "aws-sdk": "^2.1443.0", "axios": "^1.5.1", "chalk": "^5.3.0", @@ -34,11 +32,6 @@ "mysql2": "^3.6.0", "nest-aws-sdk": "^3.0.1", "nestjs-asyncapi": "^1.2.1", - "passport": "^0.6.0", - "passport-google-oauth20": "^2.0.0", - "passport-jwt": "^4.0.1", - "passport-kakao": "^1.0.1", - "passport-naver-v2": "^2.0.8", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "socket.io-client": "^4.7.2", @@ -54,7 +47,6 @@ "@types/jest": "^29.5.2", "@types/multer": "^1.4.9", "@types/node": "^20.3.1", - "@types/passport-naver": "^1.0.1", "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", @@ -4481,15 +4473,6 @@ "rxjs": "^7.0.0" } }, - "node_modules/@nestjs/passport": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.2.tgz", - "integrity": "sha512-od31vfB2z3y05IDB5dWSbCGE2+pAf2k2WCBinNuTTOxN0O0+wtO1L3kawj/aCW3YR9uxsTOVbTDwtwgpNNsnjQ==", - "peerDependencies": { - "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", - "passport": "^0.4.0 || ^0.5.0 || ^0.6.0" - } - }, "node_modules/@nestjs/platform-express": { "version": "10.2.7", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.2.7.tgz", @@ -6148,6 +6131,7 @@ "version": "1.19.4", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.4.tgz", "integrity": "sha512-N7UDG0/xiPQa2D/XrVJXjkWbpqHCd2sBaB32ggRF2l83RhPfamgKGF8gwwqyksS95qUS5ZYF9aF+lLPRlwI2UA==", + "dev": true, "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -6163,6 +6147,7 @@ "version": "3.4.37", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.37.tgz", "integrity": "sha512-zBUSRqkfZ59OcwXon4HVxhx5oWCJmc0OtBTK05M+p0dYjgN6iTwIL2T/WbsQZrEsdnwaF9cWQ+azOnpPvIqY3Q==", + "dev": true, "dependencies": { "@types/node": "*" } @@ -6231,6 +6216,7 @@ "version": "4.17.20", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.20.tgz", "integrity": "sha512-rOaqlkgEvOW495xErXMsmyX3WKBInbhG5eqojXYi3cGUaLoRDlXa5d52fkfWZT963AZ3v2eZ4MbKE6WpDAGVsw==", + "dev": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -6242,6 +6228,7 @@ "version": "4.17.39", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.39.tgz", "integrity": "sha512-BiEUfAiGCOllomsRAZOiMFP7LAnrifHpt56pc4Z7l9K6ACyN06Ns1JLMBxwkfLOjJRlSf06NwWsT7yzfpaVpyQ==", + "dev": true, "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -6261,7 +6248,8 @@ "node_modules/@types/http-errors": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.3.tgz", - "integrity": "sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA==" + "integrity": "sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA==", + "dev": true }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.5", @@ -6318,7 +6306,8 @@ "node_modules/@types/mime": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.4.tgz", - "integrity": "sha512-1Gjee59G25MrQGk8bsNvC6fxNiRgUlGn2wlhGf95a59DrprnnHk80FIMMFG9XHMdrfsuA119ht06QPDXA1Z7tw==" + "integrity": "sha512-1Gjee59G25MrQGk8bsNvC6fxNiRgUlGn2wlhGf95a59DrprnnHk80FIMMFG9XHMdrfsuA119ht06QPDXA1Z7tw==", + "dev": true }, "node_modules/@types/multer": { "version": "1.4.9", @@ -6343,43 +6332,6 @@ "integrity": "sha512-3YmXzzPAdOTVljVMkTMBdBEvlOLg2cDQaDhnnhT3nT9uDbnJzjWhKlzb+desT12Y7tGqaN6d+AbozcKzyL36Ng==", "dev": true }, - "node_modules/@types/passport": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.14.tgz", - "integrity": "sha512-D6p2ygR2S7Cq5PO7iUaEIQu/5WrM0tONu6Lxgk0C9r3lafQIlVpWCo3V/KI9To3OqHBxcfQaOeK+8AvwW5RYmw==", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/passport-jwt": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.12.tgz", - "integrity": "sha512-nXCd1lu20rw//nZ5AnK1FnlVZdSC4R5xksquev9oAJlXwJw0irMdZ7dRAE4KDlalptKObiaoam6BQ8lpujeZog==", - "dependencies": { - "@types/express": "*", - "@types/jsonwebtoken": "*", - "@types/passport-strategy": "*" - } - }, - "node_modules/@types/passport-naver": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/passport-naver/-/passport-naver-1.0.3.tgz", - "integrity": "sha512-n3OK5KfkKXEN42QVfyW5kPQXyt/ca9DZ+gAeMKR9UBiBEDk7Pahv+OxadwyHbJKSyOjmaJD2YDDEigkPZGJBzQ==", - "dev": true, - "dependencies": { - "@types/express": "*", - "@types/passport": "*" - } - }, - "node_modules/@types/passport-strategy": { - "version": "0.2.37", - "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.37.tgz", - "integrity": "sha512-ltgwLnwHVfpjK7/66lpv43hiz90nIVb36JmeB0iF3FAZoHX6+LbkY5Ey97Bm8Jr0uGhQyDFEsSOOfejp5PJehg==", - "dependencies": { - "@types/express": "*", - "@types/passport": "*" - } - }, "node_modules/@types/protocol-buffers-schema": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/@types/protocol-buffers-schema/-/protocol-buffers-schema-3.4.2.tgz", @@ -6391,12 +6343,14 @@ "node_modules/@types/qs": { "version": "6.9.9", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.9.tgz", - "integrity": "sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg==" + "integrity": "sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg==", + "dev": true }, "node_modules/@types/range-parser": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.6.tgz", - "integrity": "sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA==" + "integrity": "sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA==", + "dev": true }, "node_modules/@types/semver": { "version": "7.5.4", @@ -6408,6 +6362,7 @@ "version": "0.17.3", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.3.tgz", "integrity": "sha512-/7fKxvKUoETxjFUsuFlPB9YndePpxxRAOfGC/yJdc9kTjTeP5kRCTzfnE8kPUKCeyiyIZu0YQ76s50hCedI1ug==", + "dev": true, "dependencies": { "@types/mime": "^1", "@types/node": "*" @@ -6417,6 +6372,7 @@ "version": "1.15.4", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.4.tgz", "integrity": "sha512-aqqNfs1XTF0HDrFdlY//+SGUxmdSUbjeRXb5iaZc3x0/vMbYmdw9qvOgHWOyyLFxSSRnUuP5+724zBgfw8/WAw==", + "dev": true, "dependencies": { "@types/http-errors": "*", "@types/mime": "*", @@ -7532,14 +7488,6 @@ "node": "^4.5.0 || >= 5.9" } }, - "node_modules/base64url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", - "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -14677,11 +14625,6 @@ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==" }, - "node_modules/oauth": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", - "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" - }, "node_modules/oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -15101,100 +15044,6 @@ "node": ">= 0.8" } }, - "node_modules/passport": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", - "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", - "dependencies": { - "passport-strategy": "1.x.x", - "pause": "0.0.1", - "utils-merge": "^1.0.1" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-google-oauth20": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", - "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", - "dependencies": { - "passport-oauth2": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/passport-jwt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", - "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", - "dependencies": { - "jsonwebtoken": "^9.0.0", - "passport-strategy": "^1.0.0" - } - }, - "node_modules/passport-kakao": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz", - "integrity": "sha512-uItaYRVrTHL6iGPMnMZvPa/O1GrAdh/V6EMjOHcFlQcVroZ9wgG7BZ5PonMNJCxfHQ3L2QVNRnzhKWUzSsumbw==", - "dependencies": { - "passport-oauth2": "~1.1.2", - "pkginfo": "~0.3.0" - } - }, - "node_modules/passport-kakao/node_modules/passport-oauth2": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz", - "integrity": "sha512-wpsGtJDHHQUjyc9WcV9FFB0bphFExpmKtzkQrxpH1vnSr6RcWa3ZEGHx/zGKAh2PN7Po9TKYB1fJeOiIBspNPA==", - "dependencies": { - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "uid2": "0.0.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/passport-naver-v2": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/passport-naver-v2/-/passport-naver-v2-2.0.8.tgz", - "integrity": "sha512-CA0u+aA4K4Zf5e3dSd47agOS69ULOdBGei7CZY2BN1cEbLnhnc6OalFPvnXLuEKT8I4IuGwvh3EBZCST2FoI+A==", - "dependencies": { - "passport-oauth2": "^1.5.0" - } - }, - "node_modules/passport-oauth2": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz", - "integrity": "sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==", - "dependencies": { - "base64url": "3.x.x", - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "uid2": "0.0.x", - "utils-merge": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -15264,11 +15113,6 @@ "node": ">=8" } }, - "node_modules/pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" - }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -15363,14 +15207,6 @@ "node": ">=8" } }, - "node_modules/pkginfo": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", - "integrity": "sha512-yO5feByMzAp96LtP58wvPKSbaKAi/1C4kV9XpTctr6EepnP6F33RBNOiVrdz9BrPA98U2BMFsTNHo44TWcbQ2A==", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/pluralize": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", @@ -18239,11 +18075,6 @@ "node": ">=8" } }, - "node_modules/uid2": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", - "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" - }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", diff --git a/package.json b/package.json index 4f54025..85254e8 100644 --- a/package.json +++ b/package.json @@ -26,14 +26,12 @@ "@nestjs/core": "^10.0.0", "@nestjs/jwt": "^10.1.1", "@nestjs/mongoose": "^10.0.1", - "@nestjs/passport": "^10.0.2", "@nestjs/platform-express": "^10.0.0", "@nestjs/platform-socket.io": "^10.2.5", "@nestjs/schedule": "^4.0.0", "@nestjs/swagger": "^7.1.10", "@nestjs/typeorm": "^10.0.0", "@nestjs/websockets": "^10.2.5", - "@types/passport-jwt": "^3.0.12", "aws-sdk": "^2.1443.0", "axios": "^1.5.1", "chalk": "^5.3.0", @@ -45,11 +43,6 @@ "mysql2": "^3.6.0", "nest-aws-sdk": "^3.0.1", "nestjs-asyncapi": "^1.2.1", - "passport": "^0.6.0", - "passport-google-oauth20": "^2.0.0", - "passport-jwt": "^4.0.1", - "passport-kakao": "^1.0.1", - "passport-naver-v2": "^2.0.8", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "socket.io-client": "^4.7.2", @@ -65,7 +58,6 @@ "@types/jest": "^29.5.2", "@types/multer": "^1.4.9", "@types/node": "^20.3.1", - "@types/passport-naver": "^1.0.1", "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", From 9252a1d96c2b3b9a8e040ae278e35757a82625bf Mon Sep 17 00:00:00 2001 From: NicoDora Date: Thu, 9 Nov 2023 16:14:11 +0900 Subject: [PATCH 09/69] =?UTF-8?q?refactor(#11):=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=ED=95=A0=20=EB=95=8C=20=EB=A6=AC=ED=94=84=EB=A0=88?= =?UTF-8?q?=EC=8B=9C=20=ED=86=A0=ED=81=B0=20=EC=BF=A0=ED=82=A4=EC=97=90=20?= =?UTF-8?q?=EB=8B=B4=EC=95=84=20=EB=B3=B4=EB=83=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 31 +++++++++++++++++++ package.json | 2 ++ src/auth/controllers/auth.controller.ts | 23 +++++++++++--- .../kakao-login.decorator.ts | 2 +- .../naver-login.decorator.ts | 10 ++++-- .../new-access-token.decorator.ts | 10 +----- src/config/guards/jwt-refresh-token.guard.ts | 12 +++---- src/config/swagger.ts | 23 ++++++++++++-- src/main.ts | 2 ++ 9 files changed, 88 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5f6a457..27be6fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "config": "^3.3.9", + "cookie-parser": "^1.4.6", "dotenv": "^16.3.1", "mongoose": "^7.5.0", "mysql2": "^3.6.0", @@ -43,6 +44,7 @@ "@nestjs/schematics": "^10.0.2", "@nestjs/testing": "^10.0.0", "@types/config": "^3.3.2", + "@types/cookie-parser": "^1.4.6", "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/multer": "^1.4.9", @@ -6157,6 +6159,15 @@ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" }, + "node_modules/@types/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-KoooCrD56qlLskXPLGUiJxOMnv5l/8m7cQD2OxJ73NPMhuSz9PmvwRD6EpjDyKBVrdJDdQ4bQK7JFNHnNmax0w==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/cookiejar": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.3.tgz", @@ -8483,6 +8494,26 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", diff --git a/package.json b/package.json index 85254e8..3185174 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "config": "^3.3.9", + "cookie-parser": "^1.4.6", "dotenv": "^16.3.1", "mongoose": "^7.5.0", "mysql2": "^3.6.0", @@ -54,6 +55,7 @@ "@nestjs/schematics": "^10.0.2", "@nestjs/testing": "^10.0.0", "@types/config": "^3.3.2", + "@types/cookie-parser": "^1.4.6", "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/multer": "^1.4.9", diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 6a648a8..d6e522b 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -11,7 +11,7 @@ import { } from '@nestjs/common'; import { S3Service } from 'src/common/s3/s3.service'; import { TokenService } from '../services/token.service'; -import { ApiTags } from '@nestjs/swagger'; +import { ApiCookieAuth, ApiTags } from '@nestjs/swagger'; import { ApiNaverLogin } from '../swagger-decorators/naver-login.decorator'; import { ApiKakaoLogin } from '../swagger-decorators/kakao-login.decorator'; import { ApiNewAccessToken } from '../swagger-decorators/new-access-token.decorator'; @@ -29,8 +29,8 @@ import { GetUserId } from 'src/common/decorators/get-userId.decorator'; export class AuthController { constructor( private readonly authService: AuthService, - private tokenService: TokenService, - private s3Service: S3Service, + private readonly tokenService: TokenService, + private readonly s3Service: S3Service, ) {} @ApiNaverLogin() @@ -52,7 +52,13 @@ export class AuthController { naverRefreshToken, ); - return res.json({ accessToken, refreshToken }); + res.cookie('refresh_token', refreshToken, { + httpOnly: true, + secure: true, + maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 + }); + + return res.json({ accessToken }); } @ApiKakaoLogin() @@ -74,9 +80,16 @@ export class AuthController { kakaoRefreshToken, ); - return res.json({ accessToken, refreshToken }); + res.cookie('refresh_token', refreshToken, { + httpOnly: true, + secure: true, + maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 + }); + + return res.json({ accessToken }); } + @ApiCookieAuth('refresh-token') @ApiNewAccessToken() @UseGuards(JwtRefreshTokenGuard) @Get('new-access-token') diff --git a/src/auth/swagger-decorators/kakao-login.decorator.ts b/src/auth/swagger-decorators/kakao-login.decorator.ts index 780e64a..1595a7b 100644 --- a/src/auth/swagger-decorators/kakao-login.decorator.ts +++ b/src/auth/swagger-decorators/kakao-login.decorator.ts @@ -9,7 +9,7 @@ export function ApiKakaoLogin() { }), ApiResponse({ status: 200, - description: '성공적으로 로그인 된 경우', + description: '성공적으로 로그인 된 경우 (refresh_token은 쿠키로 전달됨)', content: { JSON: { example: { diff --git a/src/auth/swagger-decorators/naver-login.decorator.ts b/src/auth/swagger-decorators/naver-login.decorator.ts index a841c78..77a894b 100644 --- a/src/auth/swagger-decorators/naver-login.decorator.ts +++ b/src/auth/swagger-decorators/naver-login.decorator.ts @@ -1,5 +1,10 @@ import { applyDecorators } from '@nestjs/common'; -import { ApiOperation, ApiQuery, ApiResponse } from '@nestjs/swagger'; +import { + ApiHeader, + ApiOperation, + ApiQuery, + ApiResponse, +} from '@nestjs/swagger'; export function ApiNaverLogin() { return applyDecorators( @@ -9,12 +14,11 @@ export function ApiNaverLogin() { }), ApiResponse({ status: 200, - description: '성공적으로 로그인 된 경우', + description: '성공적으로 로그인 된 경우 (refresh_token은 쿠키로 전달됨)', content: { JSON: { example: { accessToken: '여기에 액세스 토큰', - refreshToken: '여기에 리프레시 토큰', }, }, }, diff --git a/src/auth/swagger-decorators/new-access-token.decorator.ts b/src/auth/swagger-decorators/new-access-token.decorator.ts index 033f859..7f0681e 100644 --- a/src/auth/swagger-decorators/new-access-token.decorator.ts +++ b/src/auth/swagger-decorators/new-access-token.decorator.ts @@ -1,5 +1,5 @@ import { applyDecorators } from '@nestjs/common'; -import { ApiHeaders, ApiOperation, ApiResponse } from '@nestjs/swagger'; +import { ApiOperation, ApiResponse } from '@nestjs/swagger'; export function ApiNewAccessToken() { return applyDecorators( @@ -49,13 +49,5 @@ export function ApiNewAccessToken() { }, }, }), - ApiHeaders([ - { - name: 'refresh_token', - description: '리프레시 토큰', - required: true, - example: '여기에 리프레시 토큰', - }, - ]), ); } diff --git a/src/config/guards/jwt-refresh-token.guard.ts b/src/config/guards/jwt-refresh-token.guard.ts index 111d7e0..161097e 100644 --- a/src/config/guards/jwt-refresh-token.guard.ts +++ b/src/config/guards/jwt-refresh-token.guard.ts @@ -1,15 +1,13 @@ -import { ExecutionContext, Injectable } from "@nestjs/common"; -import { TokenService } from "src/auth/services/token.service"; +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { TokenService } from 'src/auth/services/token.service'; @Injectable() export class JwtRefreshTokenGuard { - constructor( - private tokenService: TokenService, - ) {} + constructor(private tokenService: TokenService) {} async canActivate(context: ExecutionContext) { const request = context.switchToHttp().getRequest(); - const refreshToken = request.headers['refresh_token']; + const refreshToken = request.cookies['refresh_token']; if (!refreshToken) { return false; @@ -20,4 +18,4 @@ export class JwtRefreshTokenGuard { return true; } -} \ No newline at end of file +} diff --git a/src/config/swagger.ts b/src/config/swagger.ts index efbb92f..f83f038 100644 --- a/src/config/swagger.ts +++ b/src/config/swagger.ts @@ -6,8 +6,27 @@ export function setupSwagger(app: INestApplication): void { .setTitle('ma6-main API') .setDescription('모던애자일 6기 메인프로젝트 API 문서') .setVersion('1.0') - .addCookieAuth('connect.sid') - .addBearerAuth({ type: 'http', scheme: 'bearer', bearerFormat: 'JWT', name: 'JWT', description: '여기에 토큰 입력', in: 'header' }, 'access-token') + .addBearerAuth( + { + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + name: 'JWT', + description: '여기에 토큰 입력', + in: 'header', + }, + 'access-token', + ) + .addCookieAuth( + 'refreshToken-cookie', + { + type: 'http', + in: 'Header', + scheme: 'Bearer', + description: '여기에 토큰 입력', + }, + 'refresh-token', + ) .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('swagger', app, document); diff --git a/src/main.ts b/src/main.ts index 3424eca..fdcfd18 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,7 @@ import { AppModule } from './app.module'; import { Logger, ValidationPipe } from '@nestjs/common'; import { setupSwagger } from './config/swagger'; import { AsyncApiDocumentBuilder, AsyncApiModule } from 'nestjs-asyncapi'; +import * as cookieParser from 'cookie-parser'; async function bootstrap() { const app = await NestFactory.create(AppModule, { cors: true }); @@ -10,6 +11,7 @@ async function bootstrap() { app.useGlobalPipes(new ValidationPipe()); // app.useGlobalFilters(new HttpExceptionFilter()); app.enableCors(); + app.use(cookieParser()); setupSwagger(app); const asyncApiOptions = new AsyncApiDocumentBuilder() .setTitle('ma6-main-asyncapi') From 7a2d21d9e56be025b875a55cdf99e78511b83a29 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Thu, 9 Nov 2023 17:06:26 +0900 Subject: [PATCH 10/69] =?UTF-8?q?feat(#46):=20=EC=BF=BC=EB=A6=AC=EB=B9=8C?= =?UTF-8?q?=EB=8D=94=20=EC=84=B1=EB=8A=A5=20=EC=B5=9C=EC=A0=81=ED=99=94=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B7=B8=EC=97=90=20=EB=A7=9E=EC=B6=B0=20map?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=95=BD=EA=B0=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/boards/entities/board.entity.ts | 2 +- src/config/typeorm.config.ts | 3 +- src/search/repositories/search.repository.ts | 120 ++++++++++++++++--- src/search/services/search.service.ts | 8 +- 4 files changed, 111 insertions(+), 22 deletions(-) diff --git a/src/boards/entities/board.entity.ts b/src/boards/entities/board.entity.ts index 9a76c24..1a6cca8 100644 --- a/src/boards/entities/board.entity.ts +++ b/src/boards/entities/board.entity.ts @@ -28,7 +28,7 @@ export class Board { @JoinColumn({ name: 'user_id' }) user: User; - @OneToMany(() => BoardImage, (boardImage) => boardImage.board, { + @OneToMany(() => BoardImage, (boardImages) => boardImages.board, { onDelete: 'CASCADE', }) boardImages: BoardImage[]; diff --git a/src/config/typeorm.config.ts b/src/config/typeorm.config.ts index 865deda..098655e 100644 --- a/src/config/typeorm.config.ts +++ b/src/config/typeorm.config.ts @@ -38,5 +38,6 @@ export const TypeORMconfig: TypeOrmModuleOptions = { BoardNotification, // BoardRepository, ], // 여기에 엔티티들을 추가해야 합니다. - synchronize: process.env.NODE_ENV === 'false', + synchronize: process.env.NODE_ENV === 'true', + logging: true, }; diff --git a/src/search/repositories/search.repository.ts b/src/search/repositories/search.repository.ts index 7807ef3..bc5e8ed 100644 --- a/src/search/repositories/search.repository.ts +++ b/src/search/repositories/search.repository.ts @@ -1,4 +1,5 @@ import { Injectable } from '@nestjs/common'; +import { BoardImage } from 'src/boards/entities/board-image.entity'; import { Board } from 'src/boards/entities/board.entity'; import { User } from 'src/users/entities/user.entity'; import { EntityManager } from 'typeorm'; @@ -13,30 +14,74 @@ export class SearchRepository { take: number, ) { const boardRepository = this.entityManager.getRepository(Board); + if (category === '전체') { return boardRepository .createQueryBuilder('board') - .select() - .leftJoinAndSelect('board.user', 'user') - .leftJoinAndSelect('user.userImage', 'userImage') - .leftJoinAndSelect('board.boardImages', 'boardImages') .where(`MATCH(head) AGAINST (:searchQuery IN BOOLEAN MODE)`, { searchQuery, }) + .leftJoinAndMapMany( + 'board.user', + User, + 'user', + 'user.id = board.userId', + ) + .leftJoinAndSelect('user.userImage', 'userImage') + .leftJoinAndMapMany( + 'board.boardImages', + BoardImage, + 'boardImages', + 'boardImages.boardId = board.id', + ) + .select([ + 'board.id', + 'board.head', + 'board.body', + 'board.main_category', + 'board.sub_category', + 'board.createAt', + 'board.updateAt', + 'user.name', + 'userImage.id', + 'userImage.userId', + 'userImage.imageUrl', + 'boardImages.id', + 'boardImages.imageUrl', + ]) .skip(skip) .take(take) .getManyAndCount(); } return boardRepository .createQueryBuilder('board') - .select() - .leftJoinAndSelect('board.user', 'user') - .leftJoinAndSelect('user.userImage', 'userImage') - .leftJoinAndSelect('board.boardImages', 'boardImages') .where(`MATCH(head) AGAINST (:searchQuery IN BOOLEAN MODE)`, { searchQuery, }) .andWhere('board.main_category = :category', { category }) + .leftJoinAndMapMany('board.user', User, 'user', 'user.id = board.userId') + .leftJoinAndSelect('user.userImage', 'userImage') + .leftJoinAndMapMany( + 'board.boardImages', + BoardImage, + 'boardImages', + 'boardImages.boardId = board.id', + ) + .select([ + 'board.id', + 'board.head', + 'board.body', + 'board.main_category', + 'board.sub_category', + 'board.createAt', + 'board.updateAt', + 'user.name', + 'userImage.id', + 'userImage.userId', + 'userImage.imageUrl', + 'boardImages.id', + 'boardImages.imageUrl', + ]) .skip(skip) .take(take) .getManyAndCount(); @@ -53,27 +98,70 @@ export class SearchRepository { if (category === '전체') { return boardRepository .createQueryBuilder('board') - .select() - .leftJoinAndSelect('board.user', 'user') - .leftJoinAndSelect('user.userImage', 'userImage') - .leftJoinAndSelect('board.boardImages', 'boardImages') .where(`MATCH(body) AGAINST (:searchQuery IN BOOLEAN MODE)`, { searchQuery, }) + .leftJoinAndMapMany( + 'board.user', + User, + 'user', + 'user.id = board.userId', + ) + .leftJoinAndSelect('user.userImage', 'userImage') + .leftJoinAndMapMany( + 'board.boardImages', + BoardImage, + 'boardImages', + 'boardImages.boardId = board.id', + ) + .select([ + 'board.id', + 'board.head', + 'board.body', + 'board.main_category', + 'board.sub_category', + 'board.createAt', + 'board.updateAt', + 'user.name', + 'userImage.id', + 'userImage.userId', + 'userImage.imageUrl', + 'boardImages.id', + 'boardImages.imageUrl', + ]) .skip(skip) .take(take) .getManyAndCount(); } return boardRepository .createQueryBuilder('board') - .select() - .leftJoinAndSelect('board.user', 'user') - .leftJoinAndSelect('user.userImage', 'userImage') - .leftJoinAndSelect('board.boardImages', 'boardImages') .where(`MATCH(body) AGAINST (:searchQuery IN BOOLEAN MODE)`, { searchQuery, }) .andWhere('board.main_category = :category', { category }) + .leftJoinAndMapMany('board.user', User, 'user', 'user.id = board.userId') + .leftJoinAndSelect('user.userImage', 'userImage') + .leftJoinAndMapMany( + 'board.boardImages', + BoardImage, + 'boardImages', + 'boardImages.boardId = board.id', + ) + .select([ + 'board.id', + 'board.head', + 'board.body', + 'board.main_category', + 'board.sub_category', + 'board.createAt', + 'board.updateAt', + 'user.name', + 'userImage.id', + 'userImage.userId', + 'userImage.imageUrl', + 'boardImages.id', + 'boardImages.imageUrl', + ]) .skip(skip) .take(take) .getManyAndCount(); diff --git a/src/search/services/search.service.ts b/src/search/services/search.service.ts index 7dd8e13..e193e5d 100644 --- a/src/search/services/search.service.ts +++ b/src/search/services/search.service.ts @@ -42,8 +42,8 @@ export class SearchService { createAt: board.createAt, updateAt: board.updateAt, userId: { - name: board.user.name, - userImage: board.user.userImage ? board.user.userImage : [], + name: board.user[0].name, + userImage: board.user[0].userImage ? board.user[0].userImage : [], }, boardLike: like, boardImages: board.boardImages.map((image) => ({ @@ -100,8 +100,8 @@ export class SearchService { createAt: board.createAt, updateAt: board.updateAt, userId: { - name: board.user.name, - userImage: board.user.userImage ? board.user.userImage : [], + name: board.user[0].name, + userImage: board.user[0].userImage ? board.user[0].userImage : [], }, boardLike: like, boardImages: board.boardImages.map((image) => ({ From 6b9f30158dab146e8f7ab06987f70d20bf206863 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Thu, 9 Nov 2023 17:06:54 +0900 Subject: [PATCH 11/69] =?UTF-8?q?feat(#46):=20limit=20=ED=81=B4=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EC=96=B8=ED=8A=B8=20=EC=B8=A1=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=88=98=EC=B9=98=20=EC=A0=95=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 2 +- src/config/typeorm.config.ts | 3 +-- src/search/controllers/search.controller.ts | 16 ++++++++++++++-- src/search/services/search.service.ts | 6 ++++-- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/app.module.ts b/src/app.module.ts index 1e59f1e..b3b3b5d 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -25,7 +25,7 @@ import { SearchModule } from './search/search.module'; UserModule, TypeOrmModule.forRoot({ ...TypeORMconfig, // TypeORM 설정 객체 확장 - synchronize: true, // DB 동기화 여부 설정 + synchronize: false, // DB 동기화 여부 설정 }), // TypeOrmModule.forFeature([Image]), ConfigModule.forRoot({ diff --git a/src/config/typeorm.config.ts b/src/config/typeorm.config.ts index 098655e..865deda 100644 --- a/src/config/typeorm.config.ts +++ b/src/config/typeorm.config.ts @@ -38,6 +38,5 @@ export const TypeORMconfig: TypeOrmModuleOptions = { BoardNotification, // BoardRepository, ], // 여기에 엔티티들을 추가해야 합니다. - synchronize: process.env.NODE_ENV === 'true', - logging: true, + synchronize: process.env.NODE_ENV === 'false', }; diff --git a/src/search/controllers/search.controller.ts b/src/search/controllers/search.controller.ts index bb158fe..acad5b4 100644 --- a/src/search/controllers/search.controller.ts +++ b/src/search/controllers/search.controller.ts @@ -24,8 +24,14 @@ export class SearchController { @Param('category') category: string, @Query('searchQuery') searchQuery: string, @Query('page', ParseIntPipe) page: number, + @Query('limit', ParseIntPipe) limit: number, ) { - return this.searchService.searchBoardsByHead(category, searchQuery, page); + return this.searchService.searchBoardsByHead( + category, + searchQuery, + page, + limit, + ); } @ApiSearchBoardsByBody() @@ -34,8 +40,14 @@ export class SearchController { @Param('category') category: string, @Query('searchQuery') searchQuery: string, @Query('page', ParseIntPipe) page: number, + @Query('limit', ParseIntPipe) limit: number, ) { - return this.searchService.searchBoardsByBody(category, searchQuery, page); + return this.searchService.searchBoardsByBody( + category, + searchQuery, + page, + limit, + ); } @Get('users') diff --git a/src/search/services/search.service.ts b/src/search/services/search.service.ts index e193e5d..84e3797 100644 --- a/src/search/services/search.service.ts +++ b/src/search/services/search.service.ts @@ -13,8 +13,9 @@ export class SearchService { category: string, searchQuery: string, page: number, + limit: number, ) { - const take = 16; + const take = limit; const skip = page <= 0 ? (page = 0) : (page - 1) * take; const [returnedBoards, total] = @@ -71,8 +72,9 @@ export class SearchService { category: string, searchQuery: string, page: number, + limit: number, ) { - const take = 16; + const take = limit; const skip = page <= 0 ? (page = 0) : (page - 1) * take; const [returnedBoards, total] = From b0b1bf9d1377f84a70fbdc7af7555af456931fed Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Thu, 9 Nov 2023 18:00:36 +0900 Subject: [PATCH 12/69] =?UTF-8?q?feat(#46):=20=EC=9E=91=EC=84=B1=EC=9E=90?= =?UTF-8?q?=20=EA=B8=B0=EB=B0=98=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/search/controllers/search.controller.ts | 16 +++++ src/search/repositories/search.repository.ts | 57 ++++++++++++++++++ src/search/services/search.service.ts | 61 ++++++++++++++++++++ 3 files changed, 134 insertions(+) diff --git a/src/search/controllers/search.controller.ts b/src/search/controllers/search.controller.ts index acad5b4..976bdc5 100644 --- a/src/search/controllers/search.controller.ts +++ b/src/search/controllers/search.controller.ts @@ -50,6 +50,22 @@ export class SearchController { ); } + // @ApiSearchBoardsByBody() + @Get('boards/:category/user') + async searchBoardsByUserName( + @Param('category') category: string, + @Query('searchQuery') searchQuery: string, + @Query('page', ParseIntPipe) page: number, + @Query('limit', ParseIntPipe) limit: number, + ) { + return this.searchService.searchBoardsByUserName( + category, + searchQuery, + page, + limit, + ); + } + @Get('users') async searchUsersByName(@Query('searchQuery') searchQuery: string) { return this.searchService.searchUsersByName(searchQuery); diff --git a/src/search/repositories/search.repository.ts b/src/search/repositories/search.repository.ts index bc5e8ed..b287540 100644 --- a/src/search/repositories/search.repository.ts +++ b/src/search/repositories/search.repository.ts @@ -167,6 +167,63 @@ export class SearchRepository { .getManyAndCount(); } + async searchBoardsByUserName( + category: string, + searchQuery: string, + skip: number, + take: number, + ) { + const userRepository = this.entityManager.getRepository(User); + + if (category === '전체') { + const returnedUsers = await userRepository + .createQueryBuilder('user') + .where(`MATCH(name) AGAINST (:searchQuery IN BOOLEAN MODE)`, { + searchQuery, + }) + .select(['user.id']) + .getMany(); + console.log(returnedUsers); + returnedUsers.forEach((id) => { + this.entityManager.findOne(Board, { where: { userId: id } }); + }); + + return [returnedUsers]; + } + // return userRepository + // .createQueryBuilder('board') + // .where(`MATCH(body) AGAINST (:searchQuery IN BOOLEAN MODE)`, { + // searchQuery, + // }) + // .andWhere('board.main_category = :category', { category }) + // .leftJoinAndMapMany('board.user', User, 'user', 'user.id = board.userId') + // .leftJoinAndSelect('user.userImage', 'userImage') + // .leftJoinAndMapMany( + // 'board.boardImages', + // BoardImage, + // 'boardImages', + // 'boardImages.boardId = board.id', + // ) + // .select([ + // 'board.id', + // 'board.head', + // 'board.body', + // 'board.main_category', + // 'board.sub_category', + // 'board.createAt', + // 'board.updateAt', + // 'user.name', + // 'userImage.id', + // 'userImage.userId', + // 'userImage.imageUrl', + // 'boardImages.id', + // 'boardImages.imageUrl', + // ]) + // .skip(skip) + // .take(take) + // .getManyAndCount(); + } + async searchUsersByName(searchQuery: string) { const userRepository = this.entityManager.getRepository(User); diff --git a/src/search/services/search.service.ts b/src/search/services/search.service.ts index 84e3797..ed15268 100644 --- a/src/search/services/search.service.ts +++ b/src/search/services/search.service.ts @@ -127,6 +127,67 @@ export class SearchService { } } + async searchBoardsByUserName( + category: string, + searchQuery: string, + page: number, + limit: number, + ) { + const take = limit; + const skip = page <= 0 ? (page = 0) : (page - 1) * take; + + const [returnedBoards, total] = + await this.searchRepository.searchBoardsByUserName( + category, + searchQuery, + skip, + take, + ); + + return [returnedBoards, total]; + + // const last_page = Math.ceil(total / take); + + // const boardResponse: BoardResponseDTO[] = await Promise.all( + // returnedBoards.map(async (board) => { + // const like = await this.boardLikesRepository.getBoardLikesCount( + // board.id, + // ); + + // return { + // id: board.id, + // head: board.head, + // body: board.body.substring(0, 30), + // main_category: board.main_category, + // sub_category: board.sub_category, + // createAt: board.createAt, + // updateAt: board.updateAt, + // userId: { + // name: board.user[0].name, + // userImage: board.user[0].userImage ? board.user[0].userImage : [], + // }, + // boardLike: like, + // boardImages: board.boardImages.map((image) => ({ + // id: image.id, + // imageUrl: image.imageUrl, + // })), + // }; + // }), + // ); + // if (last_page >= page) { + // return { + // data: boardResponse, + // meta: { + // total, + // page: page <= 0 ? (page = 1) : page, + // last_page, + // }, + // }; + // } else { + // throw new NotFoundException('해당 페이지는 존재하지 않습니다.'); + // } + } + async searchUsersByName(searchQuery: string) { return this.searchRepository.searchUsersByName(searchQuery); } From 2dd7d622c6dd1a5be984657a05df3a5aaa2bea6b Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Thu, 9 Nov 2023 20:49:43 +0900 Subject: [PATCH 13/69] =?UTF-8?q?feat(#46):=20userName=EA=B8=B0=EB=B0=98?= =?UTF-8?q?=20=EA=B2=8C=EC=8B=9C=EB=AC=BC=20=EA=B2=80=EC=83=89=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84(=EB=AF=B8=EC=99=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/typeorm.config.ts | 1 + src/search/repositories/search.repository.ts | 113 ++++++++++++++++++- src/search/services/search.service.ts | 15 ++- 3 files changed, 115 insertions(+), 14 deletions(-) diff --git a/src/config/typeorm.config.ts b/src/config/typeorm.config.ts index 865deda..73d604e 100644 --- a/src/config/typeorm.config.ts +++ b/src/config/typeorm.config.ts @@ -39,4 +39,5 @@ export const TypeORMconfig: TypeOrmModuleOptions = { // BoardRepository, ], // 여기에 엔티티들을 추가해야 합니다. synchronize: process.env.NODE_ENV === 'false', + logging: true, }; diff --git a/src/search/repositories/search.repository.ts b/src/search/repositories/search.repository.ts index b287540..bfee989 100644 --- a/src/search/repositories/search.repository.ts +++ b/src/search/repositories/search.repository.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { BoardImage } from 'src/boards/entities/board-image.entity'; import { Board } from 'src/boards/entities/board.entity'; import { User } from 'src/users/entities/user.entity'; -import { EntityManager } from 'typeorm'; +import { Brackets, EntityManager } from 'typeorm'; @Injectable() export class SearchRepository { @@ -183,12 +183,113 @@ export class SearchRepository { }) .select(['user.id']) .getMany(); - console.log(returnedUsers); - returnedUsers.forEach((id) => { - this.entityManager.findOne(Board, { where: { userId: id } }); - }); - return [returnedUsers]; + // const returnedBoards = {}; + // returnedUsers.forEach(async (user, index) => { + // let newBoards = {}; + + // newBoards = await this.entityManager.find(Board, { + // where: { userId: user.id }, + // }); + // returnedBoards[user.id] = { ...newBoards }; + const returnedBoards = []; + + for (const user of returnedUsers) { + let index = 0; + index++; + const parameterName = 'token' + index; + const [newBoards, total] = await this.entityManager + .createQueryBuilder(Board, 'board') + .leftJoinAndMapMany( + 'board.user', + User, + 'user', + 'user.id = board.userId', + ) + .leftJoinAndSelect('user.userImage', 'userImage') + .leftJoinAndMapMany( + 'board.boardImages', + BoardImage, + 'boardImages', + 'boardImages.boardId = board.id', + ) + .select([ + 'board.id', + 'board.head', + 'board.body', + 'board.main_category', + 'board.sub_category', + 'board.createAt', + 'board.updateAt', + 'user.name', + 'userImage.id', + 'userImage.userId', + 'userImage.imageUrl', + 'boardImages.id', + 'boardImages.imageUrl', + ]) + .addSelect('SUBSTRING(board.body, 0, 30)', 'board.body') + .where( + new Brackets((qb) => { + qb.where('board.userId = :' + parameterName); + }), + ) + .setParameter(parameterName, user.id) + .skip(skip) + .take(take) + .getManyAndCount(); + // console.log(newBoards); + + returnedBoards.push({ ...newBoards, total }); + } + + console.log(returnedBoards); + return returnedBoards; + // const parameterName = 'token' + index; + // returnedBoards[user.id] = await this.entityManager + // .createQueryBuilder(Board, 'board') + + // // .setParameter(parameterName, user) + // .leftJoinAndMapMany( + // 'board.user', + // User, + // 'user', + // 'user.id = board.userId', + // ) + // .leftJoinAndSelect('user.userImage', 'userImage') + // .leftJoinAndMapMany( + // 'board.boardImages', + // BoardImage, + // 'boardImages', + // 'boardImages.boardId = board.id', + // ) + // .select([ + // 'board.id', + // 'board.head', + // 'board.body', + // 'board.main_category', + // 'board.sub_category', + // 'board.createAt', + // 'board.updateAt', + // 'user.id', + // 'user.name', + // 'userImage.id', + // 'userImage.userId', + // 'userImage.imageUrl', + // 'boardImages.id', + // 'boardImages.imageUrl', + // ]) + // .where('MATCH(userId) AGAINST (:usersId IN BOOLEAN MODE)', { + // usersId: user.id, + // }) + // .skip(skip) + // .take(take) + // .getManyAndCount(); + // console.log(returnedBoards[0]); + // console.log(returnedBoards); + // }); + console.log(returnedBoards); + return [returnedBoards]; } // return userRepository // .createQueryBuilder('board') diff --git a/src/search/services/search.service.ts b/src/search/services/search.service.ts index ed15268..7b6a87c 100644 --- a/src/search/services/search.service.ts +++ b/src/search/services/search.service.ts @@ -136,15 +136,14 @@ export class SearchService { const take = limit; const skip = page <= 0 ? (page = 0) : (page - 1) * take; - const [returnedBoards, total] = - await this.searchRepository.searchBoardsByUserName( - category, - searchQuery, - skip, - take, - ); + const [returnedBoards] = await this.searchRepository.searchBoardsByUserName( + category, + searchQuery, + skip, + take, + ); - return [returnedBoards, total]; + return [returnedBoards]; // const last_page = Math.ceil(total / take); From 2dd5c7443a6cd4d496a8d81b73b167b91d392c25 Mon Sep 17 00:00:00 2001 From: 2swo Date: Fri, 10 Nov 2023 10:28:17 +0900 Subject: [PATCH 14/69] =?UTF-8?q?ADD(#95)=20=EB=8C=93=EA=B8=80=EA=B3=BC=20?= =?UTF-8?q?=EB=8C=80=EB=8C=93=EA=B8=80=20=EB=A7=A4=ED=95=91=ED=95=B4?= =?UTF-8?q?=EC=84=9C=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/comments/dto/get-all-comment-dto.ts | 13 +++++++++-- src/comments/dto/get-all-recomment-dto.ts | 11 ++++++++++ src/comments/entities/comment.entity.ts | 7 ++++++ src/comments/entities/recomment.entity.ts | 2 +- .../repository/comments.repository.ts | 8 +++++-- .../repository/recomments.repository.ts | 9 ++++++++ src/comments/services/comments.services.ts | 14 ++++++++++-- src/comments/services/recomments.services.ts | 22 ++++++++++++++++++- .../get-all-comment-decorators.ts | 5 +++++ 9 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 src/comments/dto/get-all-recomment-dto.ts diff --git a/src/comments/dto/get-all-comment-dto.ts b/src/comments/dto/get-all-comment-dto.ts index 02a720d..0170476 100644 --- a/src/comments/dto/get-all-comment-dto.ts +++ b/src/comments/dto/get-all-comment-dto.ts @@ -3,9 +3,18 @@ import { UserImage } from 'src/users/entities/user-image.entity'; export class commentResponseDTO { id: number; content: string; - commentowner: true | false; - userId: { + commentowner: boolean; + user: { name: string; userImage: UserImage | UserImage[]; }; + reComment: { + id: number; + content: string; + reCommentowner: boolean; + user: { + name: string; + userImage: UserImage | UserImage[]; + }; + }[]; } diff --git a/src/comments/dto/get-all-recomment-dto.ts b/src/comments/dto/get-all-recomment-dto.ts new file mode 100644 index 0000000..b43eada --- /dev/null +++ b/src/comments/dto/get-all-recomment-dto.ts @@ -0,0 +1,11 @@ +import { UserImage } from 'src/users/entities/user-image.entity'; + +export class reCommentResponseDTO { + id: number; + content: string; + reCommentowner: boolean; + user: { + name: string; + userImage: UserImage | UserImage[]; + }; +} diff --git a/src/comments/entities/comment.entity.ts b/src/comments/entities/comment.entity.ts index ec9e988..5a6b086 100644 --- a/src/comments/entities/comment.entity.ts +++ b/src/comments/entities/comment.entity.ts @@ -1,11 +1,13 @@ import { Board } from 'src/boards/entities/board.entity'; import { User } from 'src/users/entities/user.entity'; +import { ReComment } from './recomment.entity'; import { Column, CreateDateColumn, Entity, JoinColumn, ManyToOne, + OneToMany, PrimaryGeneratedColumn, } from 'typeorm'; @@ -23,6 +25,11 @@ export class Comment { @Column({ name: 'user_id' }) userId: number; + @OneToMany(() => ReComment, (reComment) => reComment.comment, { + onDelete: 'CASCADE', + }) + reComment: ReComment[]; + @ManyToOne(() => Board) @JoinColumn({ name: 'board_id' }) board: Board; diff --git a/src/comments/entities/recomment.entity.ts b/src/comments/entities/recomment.entity.ts index 3b9a2b3..dfce06e 100644 --- a/src/comments/entities/recomment.entity.ts +++ b/src/comments/entities/recomment.entity.ts @@ -23,7 +23,7 @@ export class ReComment { @Column({ name: 'user_id' }) userId: number; - @ManyToOne(() => Comment) + @ManyToOne(() => Comment, (comment) => comment.reComment) @JoinColumn({ name: 'comment_id' }) comment: Comment; diff --git a/src/comments/repository/comments.repository.ts b/src/comments/repository/comments.repository.ts index 9ec2e9c..65d6013 100644 --- a/src/comments/repository/comments.repository.ts +++ b/src/comments/repository/comments.repository.ts @@ -22,8 +22,12 @@ export class CommentsRepository { const query = this.entityManager .createQueryBuilder(Comment, 'comment') .innerJoinAndSelect('comment.user', 'user') - .innerJoinAndSelect('user.userImage', 'userImage') - .where('comment.boardId = :boardId', { boardId }); + .leftJoinAndSelect('comment.reComment', 'reComment') + .leftJoinAndSelect('reComment.user', 'reCommentUser') + .leftJoinAndSelect('reCommentUser.userImage', 'reCommentUserImage') + .leftJoinAndSelect('user.userImage', 'userImage') + .where('comment.boardId = :boardId', { boardId }) + .groupBy('comment.id'); return query.getMany(); } diff --git a/src/comments/repository/recomments.repository.ts b/src/comments/repository/recomments.repository.ts index 821f7be..b559852 100644 --- a/src/comments/repository/recomments.repository.ts +++ b/src/comments/repository/recomments.repository.ts @@ -18,6 +18,15 @@ export class ReCommentsRepository { return await this.entityManager.save(ReComment, reComment); } + async findReCommentByCommentId(commentId: number): Promise { + const query = this.entityManager + .createQueryBuilder(ReComment, 'reComment') + .innerJoinAndSelect('reComment.user', 'user') + .innerJoinAndSelect('user.userImage', 'userImage') + .where('reComment.commentId = :commentId', { commentId }); + return query.getMany(); + } + async findOneReComment(id: number): Promise { return this.entityManager.findOne(ReComment, { relations: ['user', 'user.userImage'], diff --git a/src/comments/services/comments.services.ts b/src/comments/services/comments.services.ts index df14668..046b464 100644 --- a/src/comments/services/comments.services.ts +++ b/src/comments/services/comments.services.ts @@ -31,16 +31,26 @@ export class CommentsService { const comments = await this.CommentRepository.findCommentsByBoardId(boardId); if (!comments) { - throw new Error('댓글을 찾을 수 없습니다.'); + return []; // 에러 말고 리턴으로 빈 배열 } return comments.map((comment) => ({ id: comment.id, content: comment.content, commentowner: comment.userId === userId, - userId: { + user: { name: comment.user.name, userImage: comment.user.userImage ? comment.user.userImage : [], }, + reComment: (comment.reComment || []).map((reComment) => ({ + //예외처리 , reComment가 없을경우 빈 배열 + id: reComment.id, + content: reComment.content, + reCommentowner: reComment.userId === userId, + user: { + name: reComment.user.name, + userImage: reComment.user.userImage ? reComment.user.userImage : [], + }, + })), })); } diff --git a/src/comments/services/recomments.services.ts b/src/comments/services/recomments.services.ts index 9c5df8b..2ada3a0 100644 --- a/src/comments/services/recomments.services.ts +++ b/src/comments/services/recomments.services.ts @@ -1,9 +1,9 @@ import { Injectable } from '@nestjs/common'; import { CreateReCommentDto } from '../dto/create-recomment-dto'; - import { ReComment } from '../entities/recomment.entity'; import { ReCommentsRepository } from '../repository/recomments.repository'; import { UpdateReCommentDto } from '../dto/update-recomment-dto'; +import { reCommentResponseDTO } from '../dto/get-all-recomment-dto'; @Injectable() export class ReCommentsService { @@ -42,6 +42,26 @@ export class ReCommentsService { return updatedReComment; } + async findAllReComments( + commentId: number, + userId: number, + ): Promise { + const reComments = + await this.reCommentRepository.findReCommentByCommentId(commentId); + if (!reComments) { + return []; + } + return reComments.map((reComment) => ({ + id: reComment.id, + content: reComment.content, + reCommentowner: reComment.userId === userId, + user: { + name: reComment.user.name, + userImage: reComment.user.userImage ? reComment.user.userImage : [], + }, + })); + } + async deleteReComment(reCommentId: number, userId: number): Promise { const reComment = await this.reCommentRepository.findOneReComment(reCommentId); diff --git a/src/comments/swagger-decoratros/get-all-comment-decorators.ts b/src/comments/swagger-decoratros/get-all-comment-decorators.ts index 3e1dbb8..9984c5b 100644 --- a/src/comments/swagger-decoratros/get-all-comment-decorators.ts +++ b/src/comments/swagger-decoratros/get-all-comment-decorators.ts @@ -31,6 +31,7 @@ export function ApiGetAllComment() { 'http://k.kakaocdn.net/dn/dpk9l1/btqmGhA2lKL/Oz0wDuJn1YV2DIn92f6DVK/img_640x640.jpg', }, }, + reComment: ['없는 경우 빈 배열'], }, { id: 2, @@ -45,6 +46,7 @@ export function ApiGetAllComment() { 'http://k.kakaocdn.net/dn/dpk9l1/btqmGhA2lKL/Oz0wDuJn1YV2DIn92f6DVK/img_640x640.jpg', }, }, + reComment: ['없는 경우 빈 배열'], }, { id: 3, @@ -59,6 +61,7 @@ export function ApiGetAllComment() { 'http://k.kakaocdn.net/dn/dpk9l1/btqmGhA2lKL/Oz0wDuJn1YV2DIn92f6DVK/img_640x640.jpg', }, }, + reComment: ['없는 경우 빈 배열'], }, { id: 4, @@ -73,6 +76,7 @@ export function ApiGetAllComment() { 'http://k.kakaocdn.net/dn/dpk9l1/btqmGhA2lKL/Oz0wDuJn1YV2DIn92f6DVK/img_640x640.jpg', }, }, + reComment: ['없는 경우 빈 배열'], }, { id: 5, @@ -87,6 +91,7 @@ export function ApiGetAllComment() { 'http://k.kakaocdn.net/dn/dpk9l1/btqmGhA2lKL/Oz0wDuJn1YV2DIn92f6DVK/img_640x640.jpg', }, }, + reComment: ['없는 경우 빈 배열'], }, ], }, From 9bf6959c78213c809541cb3b36ebb91f31e1eb39 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Fri, 10 Nov 2023 11:40:35 +0900 Subject: [PATCH 15/69] =?UTF-8?q?feat(#46):=20userName=EC=9D=84=20?= =?UTF-8?q?=ED=86=A0=EB=8C=80=EB=A1=9C=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/search/repositories/search.repository.ts | 221 +++++++------------ src/search/services/search.service.ts | 4 +- 2 files changed, 78 insertions(+), 147 deletions(-) diff --git a/src/search/repositories/search.repository.ts b/src/search/repositories/search.repository.ts index bfee989..0b9a7d7 100644 --- a/src/search/repositories/search.repository.ts +++ b/src/search/repositories/search.repository.ts @@ -175,154 +175,85 @@ export class SearchRepository { ) { const userRepository = this.entityManager.getRepository(User); + const returnedUsers = await userRepository + .createQueryBuilder('user') + .where(`MATCH(name) AGAINST (:searchQuery IN BOOLEAN MODE)`, { + searchQuery, + }) + .select(['user.id']) + .getMany(); + if (category === '전체') { - const returnedUsers = await userRepository - .createQueryBuilder('user') - .where(`MATCH(name) AGAINST (:searchQuery IN BOOLEAN MODE)`, { - searchQuery, + return this.entityManager + .createQueryBuilder(Board, 'board') + .leftJoinAndMapMany( + 'board.user', + User, + 'user', + 'user.id = board.userId', + ) + .leftJoinAndSelect('user.userImage', 'userImage') + .leftJoinAndMapMany( + 'board.boardImages', + BoardImage, + 'boardImages', + 'boardImages.boardId = board.id', + ) + .select([ + 'board.id', + 'board.head', + 'board.body', + 'board.main_category', + 'board.sub_category', + 'board.createAt', + 'board.updateAt', + 'user.name', + 'userImage.id', + 'userImage.userId', + 'userImage.imageUrl', + 'boardImages.id', + 'boardImages.imageUrl', + ]) + .where('board.userId IN (:...userId)', { + userId: returnedUsers.map((user) => user.id), }) - .select(['user.id']) - .getMany(); - - // const returnedBoards = {}; - // returnedUsers.forEach(async (user, index) => { - // let newBoards = {}; - - // newBoards = await this.entityManager.find(Board, { - // where: { userId: user.id }, - // }); - // returnedBoards[user.id] = { ...newBoards }; - const returnedBoards = []; - - for (const user of returnedUsers) { - let index = 0; - index++; - const parameterName = 'token' + index; - const [newBoards, total] = await this.entityManager - .createQueryBuilder(Board, 'board') - .leftJoinAndMapMany( - 'board.user', - User, - 'user', - 'user.id = board.userId', - ) - .leftJoinAndSelect('user.userImage', 'userImage') - .leftJoinAndMapMany( - 'board.boardImages', - BoardImage, - 'boardImages', - 'boardImages.boardId = board.id', - ) - .select([ - 'board.id', - 'board.head', - 'board.body', - 'board.main_category', - 'board.sub_category', - 'board.createAt', - 'board.updateAt', - 'user.name', - 'userImage.id', - 'userImage.userId', - 'userImage.imageUrl', - 'boardImages.id', - 'boardImages.imageUrl', - ]) - .addSelect('SUBSTRING(board.body, 0, 30)', 'board.body') - .where( - new Brackets((qb) => { - qb.where('board.userId = :' + parameterName); - }), - ) - .setParameter(parameterName, user.id) - .skip(skip) - .take(take) - .getManyAndCount(); - // console.log(newBoards); - - returnedBoards.push({ ...newBoards, total }); - } - - console.log(returnedBoards); - return returnedBoards; - // const parameterName = 'token' + index; - // returnedBoards[user.id] = await this.entityManager - // .createQueryBuilder(Board, 'board') - - // // .setParameter(parameterName, user) - // .leftJoinAndMapMany( - // 'board.user', - // User, - // 'user', - // 'user.id = board.userId', - // ) - // .leftJoinAndSelect('user.userImage', 'userImage') - // .leftJoinAndMapMany( - // 'board.boardImages', - // BoardImage, - // 'boardImages', - // 'boardImages.boardId = board.id', - // ) - // .select([ - // 'board.id', - // 'board.head', - // 'board.body', - // 'board.main_category', - // 'board.sub_category', - // 'board.createAt', - // 'board.updateAt', - // 'user.id', - // 'user.name', - // 'userImage.id', - // 'userImage.userId', - // 'userImage.imageUrl', - // 'boardImages.id', - // 'boardImages.imageUrl', - // ]) - // .where('MATCH(userId) AGAINST (:usersId IN BOOLEAN MODE)', { - // usersId: user.id, - // }) - // .skip(skip) - // .take(take) - // .getManyAndCount(); - // console.log(returnedBoards[0]); - // console.log(returnedBoards); - // }); - console.log(returnedBoards); - return [returnedBoards]; + .skip(skip) + .take(take) + .getManyAndCount(); } - // return userRepository - // .createQueryBuilder('board') - // .where(`MATCH(body) AGAINST (:searchQuery IN BOOLEAN MODE)`, { - // searchQuery, - // }) - // .andWhere('board.main_category = :category', { category }) - // .leftJoinAndMapMany('board.user', User, 'user', 'user.id = board.userId') - // .leftJoinAndSelect('user.userImage', 'userImage') - // .leftJoinAndMapMany( - // 'board.boardImages', - // BoardImage, - // 'boardImages', - // 'boardImages.boardId = board.id', - // ) - // .select([ - // 'board.id', - // 'board.head', - // 'board.body', - // 'board.main_category', - // 'board.sub_category', - // 'board.createAt', - // 'board.updateAt', - // 'user.name', - // 'userImage.id', - // 'userImage.userId', - // 'userImage.imageUrl', - // 'boardImages.id', - // 'boardImages.imageUrl', - // ]) - // .skip(skip) - // .take(take) - // .getManyAndCount(); + + return this.entityManager + .createQueryBuilder(Board, 'board') + .leftJoinAndMapMany('board.user', User, 'user', 'user.id = board.userId') + .leftJoinAndSelect('user.userImage', 'userImage') + .leftJoinAndMapMany( + 'board.boardImages', + BoardImage, + 'boardImages', + 'boardImages.boardId = board.id', + ) + .select([ + 'board.id', + 'board.head', + 'board.body', + 'board.main_category', + 'board.sub_category', + 'board.createAt', + 'board.updateAt', + 'user.name', + 'userImage.id', + 'userImage.userId', + 'userImage.imageUrl', + 'boardImages.id', + 'boardImages.imageUrl', + ]) + .where('board.userId IN (:...userId)', { + userId: returnedUsers.map((user) => user.id), + }) + .andWhere('board.main_category = :category', { category }) + .skip(skip) + .take(take) + .getManyAndCount(); } async searchUsersByName(searchQuery: string) { diff --git a/src/search/services/search.service.ts b/src/search/services/search.service.ts index 7b6a87c..89a2b84 100644 --- a/src/search/services/search.service.ts +++ b/src/search/services/search.service.ts @@ -136,14 +136,14 @@ export class SearchService { const take = limit; const skip = page <= 0 ? (page = 0) : (page - 1) * take; - const [returnedBoards] = await this.searchRepository.searchBoardsByUserName( + const returnedBoards = await this.searchRepository.searchBoardsByUserName( category, searchQuery, skip, take, ); - return [returnedBoards]; + return returnedBoards; // const last_page = Math.ceil(total / take); From 85f3852c5b73262d42d91c9327b9d2f5bd43bd67 Mon Sep 17 00:00:00 2001 From: NicoDora Date: Fri, 10 Nov 2023 13:03:47 +0900 Subject: [PATCH 16/69] =?UTF-8?q?refactor(#11):=20=EB=B0=B0=ED=8F=AC?= =?UTF-8?q?=ED=9B=84=20=EC=BF=A0=ED=82=A4=EC=97=90=20=ED=86=A0=ED=81=B0?= =?UTF-8?q?=EC=9D=B4=20=EB=8B=B4=EA=B8=B0=EC=A7=80=20=EC=95=8A=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/controllers/auth.controller.ts | 2 -- src/config/guards/jwt-access-token.guard.ts | 10 ++++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index d6e522b..acd8574 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -54,7 +54,6 @@ export class AuthController { res.cookie('refresh_token', refreshToken, { httpOnly: true, - secure: true, maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); @@ -82,7 +81,6 @@ export class AuthController { res.cookie('refresh_token', refreshToken, { httpOnly: true, - secure: true, maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); diff --git a/src/config/guards/jwt-access-token.guard.ts b/src/config/guards/jwt-access-token.guard.ts index aa88ba3..9c7451d 100644 --- a/src/config/guards/jwt-access-token.guard.ts +++ b/src/config/guards/jwt-access-token.guard.ts @@ -1,11 +1,9 @@ -import { ExecutionContext, Injectable } from "@nestjs/common"; -import { TokenService } from "src/auth/services/token.service"; +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { TokenService } from 'src/auth/services/token.service'; @Injectable() export class JwtAccessTokenGuard { - constructor( - private tokenService: TokenService, - ) {} + constructor(private tokenService: TokenService) {} async canActivate(context: ExecutionContext) { const request = context.switchToHttp().getRequest(); @@ -20,4 +18,4 @@ export class JwtAccessTokenGuard { return true; } -} \ No newline at end of file +} From 07910060d9f73ab63b2a6bade618106720ab231f Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Fri, 10 Nov 2023 13:10:58 +0900 Subject: [PATCH 17/69] =?UTF-8?q?feat(#46):=20=EA=B0=9D=EC=B2=B4=EC=97=90?= =?UTF-8?q?=20=EC=A2=8B=EC=95=84=EC=9A=94=20=EA=B0=9C=EC=88=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EA=B3=A0=20=ED=98=95=EC=8B=9D=EC=97=90=20?= =?UTF-8?q?=EB=A7=9E=EC=B6=B0=20=EA=B0=90=EC=8B=B8=EB=8A=94=20map=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/search/repositories/search.repository.ts | 8 +- src/search/services/search.service.ts | 92 ++++++++++---------- 2 files changed, 48 insertions(+), 52 deletions(-) diff --git a/src/search/repositories/search.repository.ts b/src/search/repositories/search.repository.ts index 0b9a7d7..88e2fa1 100644 --- a/src/search/repositories/search.repository.ts +++ b/src/search/repositories/search.repository.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { BoardImage } from 'src/boards/entities/board-image.entity'; import { Board } from 'src/boards/entities/board.entity'; import { User } from 'src/users/entities/user.entity'; -import { Brackets, EntityManager } from 'typeorm'; +import { EntityManager } from 'typeorm'; @Injectable() export class SearchRepository { @@ -173,10 +173,8 @@ export class SearchRepository { skip: number, take: number, ) { - const userRepository = this.entityManager.getRepository(User); - - const returnedUsers = await userRepository - .createQueryBuilder('user') + const returnedUsers = await this.entityManager + .createQueryBuilder(User, 'user') .where(`MATCH(name) AGAINST (:searchQuery IN BOOLEAN MODE)`, { searchQuery, }) diff --git a/src/search/services/search.service.ts b/src/search/services/search.service.ts index 89a2b84..2584efb 100644 --- a/src/search/services/search.service.ts +++ b/src/search/services/search.service.ts @@ -136,55 +136,53 @@ export class SearchService { const take = limit; const skip = page <= 0 ? (page = 0) : (page - 1) * take; - const returnedBoards = await this.searchRepository.searchBoardsByUserName( - category, - searchQuery, - skip, - take, - ); - - return returnedBoards; - - // const last_page = Math.ceil(total / take); + const [returnedBoards, total] = + await this.searchRepository.searchBoardsByUserName( + category, + searchQuery, + skip, + take, + ); + const last_page = Math.ceil(total / take); + const boardResponse: BoardResponseDTO[] = await Promise.all( + returnedBoards.map(async (board) => { + const like = await this.boardLikesRepository.getBoardLikesCount( + board.id, + ); - // const boardResponse: BoardResponseDTO[] = await Promise.all( - // returnedBoards.map(async (board) => { - // const like = await this.boardLikesRepository.getBoardLikesCount( - // board.id, - // ); + return { + id: board.id, + head: board.head, + body: board.body.substring(0, 30), + main_category: board.main_category, + sub_category: board.sub_category, + createAt: board.createAt, + updateAt: board.updateAt, + userId: { + name: board.user[0].name, + userImage: board.user[0].userImage ? board.user[0].userImage : [], + }, + boardLike: like, + boardImages: board.boardImages.map((image) => ({ + id: image.id, + imageUrl: image.imageUrl, + })), + }; + }), + ); - // return { - // id: board.id, - // head: board.head, - // body: board.body.substring(0, 30), - // main_category: board.main_category, - // sub_category: board.sub_category, - // createAt: board.createAt, - // updateAt: board.updateAt, - // userId: { - // name: board.user[0].name, - // userImage: board.user[0].userImage ? board.user[0].userImage : [], - // }, - // boardLike: like, - // boardImages: board.boardImages.map((image) => ({ - // id: image.id, - // imageUrl: image.imageUrl, - // })), - // }; - // }), - // ); - // if (last_page >= page) { - // return { - // data: boardResponse, - // meta: { - // total, - // page: page <= 0 ? (page = 1) : page, - // last_page, - // }, - // }; - // } else { - // throw new NotFoundException('해당 페이지는 존재하지 않습니다.'); - // } + if (last_page >= page) { + return { + data: boardResponse, + meta: { + total, + page: page <= 0 ? (page = 1) : page, + last_page, + }, + }; + } else { + throw new NotFoundException('해당 페이지는 존재하지 않습니다.'); + } } async searchUsersByName(searchQuery: string) { From 3647e0c8f11145a07207d77150392b5bab3e693a Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Fri, 10 Nov 2023 13:15:36 +0900 Subject: [PATCH 18/69] =?UTF-8?q?feat(#46):=20=EA=B2=8C=EC=8B=9C=EA=B8=80?= =?UTF-8?q?=20=EC=9C=A0=EC=A0=80=20=EC=9D=B4=EB=A6=84=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20swagger=20=EB=8D=B0=EC=BD=94=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EB=AC=B8=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/search/controllers/search.controller.ts | 3 +- .../search-boards-by-userName.decorator.ts | 302 ++++++++++++++++++ 2 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 src/search/swagger-decorators/search-boards-by-userName.decorator.ts diff --git a/src/search/controllers/search.controller.ts b/src/search/controllers/search.controller.ts index 976bdc5..5a0df7e 100644 --- a/src/search/controllers/search.controller.ts +++ b/src/search/controllers/search.controller.ts @@ -11,6 +11,7 @@ import { SearchService } from '../services/search.service'; import { ApiSearchBoardsByHead } from '../swagger-decorators/search-boards-by-head.decorator'; import { ApiSearchBoardsByBody } from '../swagger-decorators/search-boards-by-body.decorator'; import { ApiTags } from '@nestjs/swagger'; +import { ApiSearchBoardsByUserName } from '../swagger-decorators/search-boards-by-userName.decorator'; @ApiTags('SEARCH') @UsePipes(ValidationPipe) @@ -50,7 +51,7 @@ export class SearchController { ); } - // @ApiSearchBoardsByBody() + @ApiSearchBoardsByUserName() @Get('boards/:category/user') async searchBoardsByUserName( @Param('category') category: string, diff --git a/src/search/swagger-decorators/search-boards-by-userName.decorator.ts b/src/search/swagger-decorators/search-boards-by-userName.decorator.ts new file mode 100644 index 0000000..77ab0ff --- /dev/null +++ b/src/search/swagger-decorators/search-boards-by-userName.decorator.ts @@ -0,0 +1,302 @@ +import { applyDecorators } from '@nestjs/common'; +import { ApiOperation, ApiResponse } from '@nestjs/swagger'; + +export function ApiSearchBoardsByUserName() { + return applyDecorators( + ApiOperation({ + summary: '게시글 검색 API', + description: `Query String의 내용과 Param으로 입력된 카테고리를 토대로 게시글의 작성자에 일치하는 값을 조회합니다(카테고리에 "전체" 입력 시 전체 게시판 검색). + ex)'이재진' 검색 '이재진' 라는 단어 단위로 검색.('이재'라고 검색하면 '이재'가 포함된 단어를 찾음.('이재진', '이재명')) (정확성 순으로 정렬됨)`, + }), + ApiResponse({ + status: 200, + description: '성공적으로 검색한 게시글(userName 기준) 조회', + content: { + JSON: { + example: { + data: [ + { + id: 286, + head: '흑돼지고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:33:59.713Z', + updateAt: '2023-11-01T21:33:59.713Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 293, + head: '흑돼지고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:25.623Z', + updateAt: '2023-11-01T21:34:25.623Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 292, + head: '백돼지고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:21.670Z', + updateAt: '2023-11-01T21:34:21.670Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 287, + head: '흑돼지 고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:01.707Z', + updateAt: '2023-11-01T21:34:01.707Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 294, + head: '흑돼지 고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:32.500Z', + updateAt: '2023-11-01T21:34:32.500Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 289, + head: '양 고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:10.379Z', + updateAt: '2023-11-01T21:34:10.379Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 290, + head: '백 고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:16.132Z', + updateAt: '2023-11-01T21:34:16.132Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 291, + head: '백고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:17.954Z', + updateAt: '2023-11-01T21:34:17.954Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 295, + head: '기린고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:36.688Z', + updateAt: '2023-11-01T21:34:36.688Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 296, + head: '소고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:40.449Z', + updateAt: '2023-11-01T21:34:40.449Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 297, + head: '소 고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:42.169Z', + updateAt: '2023-11-01T21:34:42.169Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 298, + head: '소 고기', + body: '흑돼지', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:39:22.442Z', + updateAt: '2023-11-01T21:39:22.442Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 300, + head: '소 고기', + body: '백돼지기', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:39:30.715Z', + updateAt: '2023-11-01T21:39:30.715Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + ], + meta: { + total: 13, + page: 1, + last_page: 1, + }, + }, + }, + }, + }), + ApiResponse({ + status: 404, + description: '페이지 조회 실패.', + content: { + JSON: { + example: { + message: '해당 페이지는 존재하지 않습니다.', + error: 'Not Found', + statusCode: 404, + }, + }, + }, + }), + ); +} From 5361e8accb5247dd5d4395bd6e237321b796321d Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Fri, 10 Nov 2023 13:59:46 +0900 Subject: [PATCH 19/69] =?UTF-8?q?refactor(#46):=20=ED=95=98=EB=82=98?= =?UTF-8?q?=EC=9D=98=20api=EB=A1=9C=20=ED=86=B5=ED=95=A9=ED=95=98=EA=B3=A0?= =?UTF-8?q?=20queryString=EC=97=90=20=EB=B3=80=EC=88=98=EB=A1=9C=20head=20?= =?UTF-8?q?/=20body=20/=20userName=EC=9C=BC=EB=A1=9C=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=EC=9D=84=20=EC=A7=80=EC=A0=95=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95.=20swagger=EB=AC=B8?= =?UTF-8?q?=EC=84=9C=EB=8F=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/search/controllers/search.controller.ts | 48 +-- src/search/services/search.service.ts | 278 ++++++++---------- ...by-head-or-body-or-user-name.decorator.ts} | 11 +- 3 files changed, 146 insertions(+), 191 deletions(-) rename src/search/swagger-decorators/{search-boards-by-head.decorator.ts => search-boards-by-head-or-body-or-user-name.decorator.ts} (90%) diff --git a/src/search/controllers/search.controller.ts b/src/search/controllers/search.controller.ts index 5a0df7e..61f4744 100644 --- a/src/search/controllers/search.controller.ts +++ b/src/search/controllers/search.controller.ts @@ -8,10 +8,8 @@ import { ValidationPipe, } from '@nestjs/common'; import { SearchService } from '../services/search.service'; -import { ApiSearchBoardsByHead } from '../swagger-decorators/search-boards-by-head.decorator'; -import { ApiSearchBoardsByBody } from '../swagger-decorators/search-boards-by-body.decorator'; +import { ApiSearchBoardsByHeadOrBodyOrUserName } from '../swagger-decorators/search-boards-by-head-or-body-or-user-name.decorator'; import { ApiTags } from '@nestjs/swagger'; -import { ApiSearchBoardsByUserName } from '../swagger-decorators/search-boards-by-userName.decorator'; @ApiTags('SEARCH') @UsePipes(ValidationPipe) @@ -19,49 +17,21 @@ import { ApiSearchBoardsByUserName } from '../swagger-decorators/search-boards-b export class SearchController { constructor(private searchService: SearchService) {} - @ApiSearchBoardsByHead() - @Get('boards/:category/head') + @ApiSearchBoardsByHeadOrBodyOrUserName() + @Get('board/:category') async searchBoardsByHead( @Param('category') category: string, - @Query('searchQuery') searchQuery: string, + @Query('head') head: string, + @Query('body') body: string, + @Query('userName') userName: string, @Query('page', ParseIntPipe) page: number, @Query('limit', ParseIntPipe) limit: number, ) { return this.searchService.searchBoardsByHead( + head, + body, + userName, category, - searchQuery, - page, - limit, - ); - } - - @ApiSearchBoardsByBody() - @Get('boards/:category/body') - async searchBoardsByBody( - @Param('category') category: string, - @Query('searchQuery') searchQuery: string, - @Query('page', ParseIntPipe) page: number, - @Query('limit', ParseIntPipe) limit: number, - ) { - return this.searchService.searchBoardsByBody( - category, - searchQuery, - page, - limit, - ); - } - - @ApiSearchBoardsByUserName() - @Get('boards/:category/user') - async searchBoardsByUserName( - @Param('category') category: string, - @Query('searchQuery') searchQuery: string, - @Query('page', ParseIntPipe) page: number, - @Query('limit', ParseIntPipe) limit: number, - ) { - return this.searchService.searchBoardsByUserName( - category, - searchQuery, page, limit, ); diff --git a/src/search/services/search.service.ts b/src/search/services/search.service.ts index 2584efb..ac80bb8 100644 --- a/src/search/services/search.service.ts +++ b/src/search/services/search.service.ts @@ -10,178 +10,160 @@ export class SearchService { private boardLikesRepository: BoardsLikeRepository, ) {} async searchBoardsByHead( + head: string, + body: string, + userName: string, category: string, - searchQuery: string, page: number, limit: number, ) { const take = limit; const skip = page <= 0 ? (page = 0) : (page - 1) * take; - const [returnedBoards, total] = - await this.searchRepository.searchBoardsByHead( - category, - searchQuery, - skip, - take, - ); - - const last_page = Math.ceil(total / take); - - const boardResponse: BoardResponseDTO[] = await Promise.all( - returnedBoards.map(async (board) => { - const like = await this.boardLikesRepository.getBoardLikesCount( - board.id, + if (head) { + const [returnedBoards, total] = + await this.searchRepository.searchBoardsByHead( + category, + head, + skip, + take, ); + const last_page = Math.ceil(total / take); + + const boardResponse: BoardResponseDTO[] = await Promise.all( + returnedBoards.map(async (board) => { + const like = await this.boardLikesRepository.getBoardLikesCount( + board.id, + ); + return { + id: board.id, + head: board.head, + body: board.body.substring(0, 30), + main_category: board.main_category, + sub_category: board.sub_category, + createAt: board.createAt, + updateAt: board.updateAt, + userId: { + name: board.user[0].name, + userImage: board.user[0].userImage ? board.user[0].userImage : [], + }, + boardLike: like, + boardImages: board.boardImages.map((image) => ({ + id: image.id, + imageUrl: image.imageUrl, + })), + }; + }), + ); + if (last_page >= page) { return { - id: board.id, - head: board.head, - body: board.body.substring(0, 30), - main_category: board.main_category, - sub_category: board.sub_category, - createAt: board.createAt, - updateAt: board.updateAt, - userId: { - name: board.user[0].name, - userImage: board.user[0].userImage ? board.user[0].userImage : [], + data: boardResponse, + meta: { + total, + page: page <= 0 ? (page = 1) : page, + last_page, }, - boardLike: like, - boardImages: board.boardImages.map((image) => ({ - id: image.id, - imageUrl: image.imageUrl, - })), }; - }), - ); - if (last_page >= page) { - return { - data: boardResponse, - meta: { - total, - page: page <= 0 ? (page = 1) : page, - last_page, - }, - }; - } else { - throw new NotFoundException('해당 페이지는 존재하지 않습니다'); - } - } + } else { + throw new NotFoundException('해당 페이지는 존재하지 않습니다'); + } + } else if (body) { + const [returnedBoards, total] = + await this.searchRepository.searchBoardsByBody( + category, + body, + skip, + take, + ); + const last_page = Math.ceil(total / take); - async searchBoardsByBody( - category: string, - searchQuery: string, - page: number, - limit: number, - ) { - const take = limit; - const skip = page <= 0 ? (page = 0) : (page - 1) * take; + const boardResponse: BoardResponseDTO[] = await Promise.all( + returnedBoards.map(async (board) => { + const like = await this.boardLikesRepository.getBoardLikesCount( + board.id, + ); - const [returnedBoards, total] = - await this.searchRepository.searchBoardsByBody( - category, - searchQuery, - skip, - take, + return { + id: board.id, + head: board.head, + body: board.body.substring(0, 30), + main_category: board.main_category, + sub_category: board.sub_category, + createAt: board.createAt, + updateAt: board.updateAt, + userId: { + name: board.user[0].name, + userImage: board.user[0].userImage ? board.user[0].userImage : [], + }, + boardLike: like, + boardImages: board.boardImages.map((image) => ({ + id: image.id, + imageUrl: image.imageUrl, + })), + }; + }), ); - - const last_page = Math.ceil(total / take); - - const boardResponse: BoardResponseDTO[] = await Promise.all( - returnedBoards.map(async (board) => { - const like = await this.boardLikesRepository.getBoardLikesCount( - board.id, - ); - + if (last_page >= page) { return { - id: board.id, - head: board.head, - body: board.body.substring(0, 30), - main_category: board.main_category, - sub_category: board.sub_category, - createAt: board.createAt, - updateAt: board.updateAt, - userId: { - name: board.user[0].name, - userImage: board.user[0].userImage ? board.user[0].userImage : [], + data: boardResponse, + meta: { + total, + page: page <= 0 ? (page = 1) : page, + last_page, }, - boardLike: like, - boardImages: board.boardImages.map((image) => ({ - id: image.id, - imageUrl: image.imageUrl, - })), }; - }), - ); - if (last_page >= page) { - return { - data: boardResponse, - meta: { - total, - page: page <= 0 ? (page = 1) : page, - last_page, - }, - }; - } else { - throw new NotFoundException('해당 페이지는 존재하지 않습니다.'); - } - } + } else { + throw new NotFoundException('해당 페이지는 존재하지 않습니다'); + } + } else if (userName) { + const [returnedBoards, total] = + await this.searchRepository.searchBoardsByUserName( + category, + userName, + skip, + take, + ); + const last_page = Math.ceil(total / take); - async searchBoardsByUserName( - category: string, - searchQuery: string, - page: number, - limit: number, - ) { - const take = limit; - const skip = page <= 0 ? (page = 0) : (page - 1) * take; + const boardResponse: BoardResponseDTO[] = await Promise.all( + returnedBoards.map(async (board) => { + const like = await this.boardLikesRepository.getBoardLikesCount( + board.id, + ); - const [returnedBoards, total] = - await this.searchRepository.searchBoardsByUserName( - category, - searchQuery, - skip, - take, + return { + id: board.id, + head: board.head, + body: board.body.substring(0, 30), + main_category: board.main_category, + sub_category: board.sub_category, + createAt: board.createAt, + updateAt: board.updateAt, + userId: { + name: board.user[0].name, + userImage: board.user[0].userImage ? board.user[0].userImage : [], + }, + boardLike: like, + boardImages: board.boardImages.map((image) => ({ + id: image.id, + imageUrl: image.imageUrl, + })), + }; + }), ); - const last_page = Math.ceil(total / take); - const boardResponse: BoardResponseDTO[] = await Promise.all( - returnedBoards.map(async (board) => { - const like = await this.boardLikesRepository.getBoardLikesCount( - board.id, - ); - + if (last_page >= page) { return { - id: board.id, - head: board.head, - body: board.body.substring(0, 30), - main_category: board.main_category, - sub_category: board.sub_category, - createAt: board.createAt, - updateAt: board.updateAt, - userId: { - name: board.user[0].name, - userImage: board.user[0].userImage ? board.user[0].userImage : [], + data: boardResponse, + meta: { + total, + page: page <= 0 ? (page = 1) : page, + last_page, }, - boardLike: like, - boardImages: board.boardImages.map((image) => ({ - id: image.id, - imageUrl: image.imageUrl, - })), }; - }), - ); - - if (last_page >= page) { - return { - data: boardResponse, - meta: { - total, - page: page <= 0 ? (page = 1) : page, - last_page, - }, - }; - } else { - throw new NotFoundException('해당 페이지는 존재하지 않습니다.'); + } else { + throw new NotFoundException('해당 페이지는 존재하지 않습니다'); + } } } diff --git a/src/search/swagger-decorators/search-boards-by-head.decorator.ts b/src/search/swagger-decorators/search-boards-by-head-or-body-or-user-name.decorator.ts similarity index 90% rename from src/search/swagger-decorators/search-boards-by-head.decorator.ts rename to src/search/swagger-decorators/search-boards-by-head-or-body-or-user-name.decorator.ts index 762ec06..428374f 100644 --- a/src/search/swagger-decorators/search-boards-by-head.decorator.ts +++ b/src/search/swagger-decorators/search-boards-by-head-or-body-or-user-name.decorator.ts @@ -1,16 +1,19 @@ import { applyDecorators } from '@nestjs/common'; import { ApiOperation, ApiResponse } from '@nestjs/swagger'; -export function ApiSearchBoardsByHead() { +export function ApiSearchBoardsByHeadOrBodyOrUserName() { return applyDecorators( ApiOperation({ summary: '게시글 검색 API', - description: `Query String의 내용과 Param으로 입력된 카테고리를 토대로 게시글의 제목에 일치하는 값을 조회합니다(카테고리에 "전체" 입력 시 전체 게시판 검색). - ex)'흑돼지 고기' 검색 - '흑돼지', '고기' 라는 단어 단위로 검색. (정확성 순으로 정렬됨)`, + description: `Query String의 내용과 Param으로 입력된 카테고리를 토대로 게시글의 제목/본문/유저이름에 일치하는 값을 조회합니다(카테고리에 "전체" 입력 시 전체 게시판 검색). + ex)'흑돼지 고기' 검색 - '흑돼지', '고기' 라는 단어 단위로 검색. (정확성 순으로 정렬됨) + 제목 검색 시 head, 본문 검색시 body, 작성자 검색시 userName + ex) http://localhost:3000/search/board/전체?head=흑돼지&page=1&limit=16`, }), ApiResponse({ status: 200, - description: '성공적으로 검색한 게시글(제목, 카테고리 기준) 조회', + description: + '성공적으로 검색한 게시글(제목/본문/작성자, 카테고리 기준) 조회', content: { JSON: { example: { From 27adbc097e931b96161f9d4a167db44ed462e136 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Fri, 10 Nov 2023 14:01:47 +0900 Subject: [PATCH 20/69] modify(#46): typeorm logging false --- src/config/typeorm.config.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/config/typeorm.config.ts b/src/config/typeorm.config.ts index 73d604e..865deda 100644 --- a/src/config/typeorm.config.ts +++ b/src/config/typeorm.config.ts @@ -39,5 +39,4 @@ export const TypeORMconfig: TypeOrmModuleOptions = { // BoardRepository, ], // 여기에 엔티티들을 추가해야 합니다. synchronize: process.env.NODE_ENV === 'false', - logging: true, }; From 1abe895d4c8498a31934b017b42d39d6c0ccd048 Mon Sep 17 00:00:00 2001 From: 2swo Date: Fri, 10 Nov 2023 16:10:22 +0900 Subject: [PATCH 21/69] =?UTF-8?q?Refactor(#95)=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20onDelete=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/boards/entities/board-image.entity.ts | 4 +++- src/boards/entities/board.entity.ts | 15 +++++---------- src/comments/entities/comment-like.entity.ts | 12 +++++++++--- src/comments/entities/comment.entity.ts | 12 +++++++----- src/comments/entities/recomment.entity.ts | 8 ++++++-- src/common/notice/entities/board-notice.entity.ts | 10 +++++++--- 6 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/boards/entities/board-image.entity.ts b/src/boards/entities/board-image.entity.ts index 6d34134..916256d 100644 --- a/src/boards/entities/board-image.entity.ts +++ b/src/boards/entities/board-image.entity.ts @@ -12,7 +12,9 @@ export class BoardImage { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => Board, (board) => board.boardImages) + @ManyToOne(() => Board, (board) => board.boardImages, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'board_id' }) board: Board; diff --git a/src/boards/entities/board.entity.ts b/src/boards/entities/board.entity.ts index 9a76c24..6eb21e5 100644 --- a/src/boards/entities/board.entity.ts +++ b/src/boards/entities/board.entity.ts @@ -24,26 +24,21 @@ export class Board { @Column({ name: 'user_id' }) userId: number; - @ManyToOne(() => User, (user) => user.board) + @ManyToOne(() => User, (user) => user.board, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'user_id' }) user: User; - @OneToMany(() => BoardImage, (boardImage) => boardImage.board, { - onDelete: 'CASCADE', - }) + @OneToMany(() => BoardImage, (boardImage) => boardImage.board) boardImages: BoardImage[]; - @OneToMany(() => BoardLike, (boardLike) => boardLike.boardId, { - onDelete: 'CASCADE', - }) + @OneToMany(() => BoardLike, (boardLike) => boardLike.boardId) boardLike: BoardLike; @OneToMany( () => BoardNotification, (BoardNotification) => BoardNotification.board, - { - onDelete: 'CASCADE', - }, ) boardNotification: BoardNotification; diff --git a/src/comments/entities/comment-like.entity.ts b/src/comments/entities/comment-like.entity.ts index e2f9547..9ddc780 100644 --- a/src/comments/entities/comment-like.entity.ts +++ b/src/comments/entities/comment-like.entity.ts @@ -8,15 +8,21 @@ export class CommentLike { @PrimaryGeneratedColumn() id: string; - @ManyToOne(() => User) + @ManyToOne(() => User, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'user_id' }) userId: User; - @ManyToOne(() => Comment) + @ManyToOne(() => Comment, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'comment_id' }) commentId: Comment; - @ManyToOne(() => ReComment) + @ManyToOne(() => ReComment, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'recomment_id' }) recommentId: ReComment; } diff --git a/src/comments/entities/comment.entity.ts b/src/comments/entities/comment.entity.ts index 5a6b086..56cf661 100644 --- a/src/comments/entities/comment.entity.ts +++ b/src/comments/entities/comment.entity.ts @@ -18,19 +18,21 @@ export class Comment { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => User) + @ManyToOne(() => User, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'user_id' }) user: User; @Column({ name: 'user_id' }) userId: number; - @OneToMany(() => ReComment, (reComment) => reComment.comment, { - onDelete: 'CASCADE', - }) + @OneToMany(() => ReComment, (reComment) => reComment.comment) reComment: ReComment[]; - @ManyToOne(() => Board) + @ManyToOne(() => Board, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'board_id' }) board: Board; diff --git a/src/comments/entities/recomment.entity.ts b/src/comments/entities/recomment.entity.ts index dfce06e..52c1980 100644 --- a/src/comments/entities/recomment.entity.ts +++ b/src/comments/entities/recomment.entity.ts @@ -16,14 +16,18 @@ export class ReComment { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => User) + @ManyToOne(() => User, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'user_id' }) user: User; @Column({ name: 'user_id' }) userId: number; - @ManyToOne(() => Comment, (comment) => comment.reComment) + @ManyToOne(() => Comment, (comment) => comment.reComment, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'comment_id' }) comment: Comment; diff --git a/src/common/notice/entities/board-notice.entity.ts b/src/common/notice/entities/board-notice.entity.ts index 441d08a..97c7bd6 100644 --- a/src/common/notice/entities/board-notice.entity.ts +++ b/src/common/notice/entities/board-notice.entity.ts @@ -24,21 +24,25 @@ export class BoardNotification { @Column({ name: 'board_id' }) boardId: number; - @ManyToOne(() => Board, (board) => board.boardNotification) + @ManyToOne(() => Board, (board) => board.boardNotification, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'board_id' }) board: Board; @Column({ name: 'sender_id' }) senderId: number; - @ManyToOne(() => User) + @ManyToOne(() => User, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'sender_id' }) sender: User; @Column({ name: 'receiver_id' }) receiverId: number; - @ManyToOne(() => User) + @ManyToOne(() => User, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'receiver_id' }) receiver: User; From cbc2c59aed575be5c29a391d21b97cfc7e256597 Mon Sep 17 00:00:00 2001 From: NicoDora Date: Fri, 10 Nov 2023 16:25:30 +0900 Subject: [PATCH 22/69] =?UTF-8?q?refactor(#11):=20httponly=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/controllers/auth.controller.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index acd8574..87e119a 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -53,7 +53,6 @@ export class AuthController { ); res.cookie('refresh_token', refreshToken, { - httpOnly: true, maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); @@ -80,7 +79,6 @@ export class AuthController { ); res.cookie('refresh_token', refreshToken, { - httpOnly: true, maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); From 6ed9a6d822bd80da59f1c4530cd09b4bad8aaea2 Mon Sep 17 00:00:00 2001 From: NicoDora Date: Fri, 10 Nov 2023 16:39:59 +0900 Subject: [PATCH 23/69] =?UTF-8?q?refactor(#11):=20=EC=BF=A0=ED=82=A4cors?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index fdcfd18..c59b8bb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,7 +10,11 @@ async function bootstrap() { const logger = new Logger(); app.useGlobalPipes(new ValidationPipe()); // app.useGlobalFilters(new HttpExceptionFilter()); - app.enableCors(); + app.enableCors({ + origin: true, // 또는 특정 도메인을 설정 + methods: 'GET ,HEAD, PUT, PATCH, POST, DELETE', + credentials: true, // 이 옵션을 true로 설정하여 쿠키 전송을 허용 + }); app.use(cookieParser()); setupSwagger(app); const asyncApiOptions = new AsyncApiDocumentBuilder() From 635cf5e66f58ac303366666ca9062a4a7df5c12c Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Fri, 10 Nov 2023 16:45:34 +0900 Subject: [PATCH 24/69] =?UTF-8?q?modify(#46):=20=EC=9B=90=EB=9E=98=20?= =?UTF-8?q?=EC=9E=88=EB=8D=98=20=EB=A1=9C=EC=A7=81=EB=8F=84=20=EA=B7=B8?= =?UTF-8?q?=EB=8C=80=EB=A1=9C=20=EC=B6=94=EA=B0=80=20=ED=96=88=EC=8A=B5?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.=20=ED=94=84=EB=A1=A0=ED=8A=B8=20=EB=8B=A8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=ED=98=84=EC=9E=AC=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=EC=9D=B4=20=ED=9E=98=EB=93=A4=20=EA=B2=83=20=EA=B0=99=EC=95=84?= =?UTF-8?q?=EC=84=9C=20=EB=91=98=20=EB=8B=A4=20=EC=83=9D=EC=84=B1.=20swagg?= =?UTF-8?q?er=EB=AC=B8=EC=84=9C=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/boards/entities/board-image.entity.ts | 4 +- src/boards/entities/board.entity.ts | 15 +- .../notice/entities/board-notice.entity.ts | 12 +- src/config/typeorm.config.ts | 7 +- src/search/controllers/search.controller.ts | 57 +++- src/search/repositories/search.repository.ts | 16 +- src/search/services/search.service.ts | 189 ++++++++++- ...-by-head-or-body-or-user-name.decorator.ts | 2 +- .../search-boards-by-head.decorator.ts | 302 ++++++++++++++++++ ...> search-boards-by-user-name.decorator.ts} | 0 .../search-users-by-user-name.decorato.ts | 29 ++ 11 files changed, 605 insertions(+), 28 deletions(-) create mode 100644 src/search/swagger-decorators/search-boards-by-head.decorator.ts rename src/search/swagger-decorators/{search-boards-by-userName.decorator.ts => search-boards-by-user-name.decorator.ts} (100%) create mode 100644 src/search/swagger-decorators/search-users-by-user-name.decorato.ts diff --git a/src/boards/entities/board-image.entity.ts b/src/boards/entities/board-image.entity.ts index 6d34134..916256d 100644 --- a/src/boards/entities/board-image.entity.ts +++ b/src/boards/entities/board-image.entity.ts @@ -12,7 +12,9 @@ export class BoardImage { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => Board, (board) => board.boardImages) + @ManyToOne(() => Board, (board) => board.boardImages, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'board_id' }) board: Board; diff --git a/src/boards/entities/board.entity.ts b/src/boards/entities/board.entity.ts index 1a6cca8..312efd4 100644 --- a/src/boards/entities/board.entity.ts +++ b/src/boards/entities/board.entity.ts @@ -24,26 +24,21 @@ export class Board { @Column({ name: 'user_id' }) userId: number; - @ManyToOne(() => User, (user) => user.board) + @ManyToOne(() => User, (user) => user.board, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'user_id' }) user: User; - @OneToMany(() => BoardImage, (boardImages) => boardImages.board, { - onDelete: 'CASCADE', - }) + @OneToMany(() => BoardImage, (boardImages) => boardImages.board) boardImages: BoardImage[]; - @OneToMany(() => BoardLike, (boardLike) => boardLike.boardId, { - onDelete: 'CASCADE', - }) + @OneToMany(() => BoardLike, (boardLike) => boardLike.boardId) boardLike: BoardLike; @OneToMany( () => BoardNotification, (BoardNotification) => BoardNotification.board, - { - onDelete: 'CASCADE', - }, ) boardNotification: BoardNotification; diff --git a/src/common/notice/entities/board-notice.entity.ts b/src/common/notice/entities/board-notice.entity.ts index 441d08a..d63887f 100644 --- a/src/common/notice/entities/board-notice.entity.ts +++ b/src/common/notice/entities/board-notice.entity.ts @@ -24,21 +24,27 @@ export class BoardNotification { @Column({ name: 'board_id' }) boardId: number; - @ManyToOne(() => Board, (board) => board.boardNotification) + @ManyToOne(() => Board, (board) => board.boardNotification, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'board_id' }) board: Board; @Column({ name: 'sender_id' }) senderId: number; - @ManyToOne(() => User) + @ManyToOne(() => User, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'sender_id' }) sender: User; @Column({ name: 'receiver_id' }) receiverId: number; - @ManyToOne(() => User) + @ManyToOne(() => User, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'receiver_id' }) receiver: User; diff --git a/src/config/typeorm.config.ts b/src/config/typeorm.config.ts index ae0fdca..ba5f9ee 100644 --- a/src/config/typeorm.config.ts +++ b/src/config/typeorm.config.ts @@ -5,6 +5,7 @@ import { Token } from 'src/auth/entities/token.entity'; import { BoardImage } from 'src/boards/entities/board-image.entity'; import { BoardLike } from 'src/boards/entities/board-like.entity'; import { Board } from 'src/boards/entities/board.entity'; +// import { BoardRepository } from 'src/boards/repository/boards.repository'; import { CommentLike } from 'src/comments/entities/comment-like.entity'; import { Comment } from 'src/comments/entities/comment.entity'; import { ReComment } from 'src/comments/entities/recomment.entity'; @@ -13,6 +14,7 @@ import { BoardNotification } from 'src/common/notice/entities/board-notice.entit import { UserImage } from 'src/users/entities/user-image.entity'; import { User } from 'src/users/entities/user.entity'; +// .env 파일 로드 dotenv.config(); export const TypeORMconfig: TypeOrmModuleOptions = { @@ -34,6 +36,7 @@ export const TypeORMconfig: TypeOrmModuleOptions = { ReComment, Friend, BoardNotification, - ], - synchronize: process.env.NODE_ENV === 'false', + // BoardRepository, + ], // 여기에 엔티티들을 추가해야 합니다. + synchronize: process.env.NODE_ENV === 'true', }; diff --git a/src/search/controllers/search.controller.ts b/src/search/controllers/search.controller.ts index 61f4744..30199b9 100644 --- a/src/search/controllers/search.controller.ts +++ b/src/search/controllers/search.controller.ts @@ -10,6 +10,9 @@ import { import { SearchService } from '../services/search.service'; import { ApiSearchBoardsByHeadOrBodyOrUserName } from '../swagger-decorators/search-boards-by-head-or-body-or-user-name.decorator'; import { ApiTags } from '@nestjs/swagger'; +import { ApiSearchBoardsByBody } from '../swagger-decorators/search-boards-by-body.decorator'; +import { ApiSearchBoardsByHead } from '../swagger-decorators/search-boards-by-head.decorator'; +import { ApiSearchBoardsByUserName } from '../swagger-decorators/search-boards-by-user-name.decorator'; @ApiTags('SEARCH') @UsePipes(ValidationPipe) @@ -18,8 +21,8 @@ export class SearchController { constructor(private searchService: SearchService) {} @ApiSearchBoardsByHeadOrBodyOrUserName() - @Get('board/:category') - async searchBoardsByHead( + @Get('boards/:category') + async searchBoardsByHeadOrUserOrName( @Param('category') category: string, @Query('head') head: string, @Query('body') body: string, @@ -27,7 +30,7 @@ export class SearchController { @Query('page', ParseIntPipe) page: number, @Query('limit', ParseIntPipe) limit: number, ) { - return this.searchService.searchBoardsByHead( + return this.searchService.searchBoardsByHeadOrBodyOrUserName( head, body, userName, @@ -37,6 +40,54 @@ export class SearchController { ); } + @ApiSearchBoardsByHead() + @Get('boards/:category/head') + async searchBoardsByHead( + @Param('category') category: string, + @Query('searchQuery') searchQuery: string, + @Query('page', ParseIntPipe) page: number, + @Query('limit', ParseIntPipe) limit: number, + ) { + return this.searchService.searchBoardsByHead( + category, + searchQuery, + page, + limit, + ); + } + + @ApiSearchBoardsByBody() + @Get('boards/:category/body') + async searchBoardsByBody( + @Param('category') category: string, + @Query('searchQuery') searchQuery: string, + @Query('page', ParseIntPipe) page: number, + @Query('limit', ParseIntPipe) limit: number, + ) { + return this.searchService.searchBoardsByBody( + category, + searchQuery, + page, + limit, + ); + } + + @ApiSearchBoardsByUserName() + @Get('boards/:category/userName') + async searchBoardsByUserName( + @Param('category') category: string, + @Query('searchQuery') searchQuery: string, + @Query('page', ParseIntPipe) page: number, + @Query('limit', ParseIntPipe) limit: number, + ) { + return this.searchService.searchBoardsByUserName( + category, + searchQuery, + page, + limit, + ); + } + @Get('users') async searchUsersByName(@Query('searchQuery') searchQuery: string) { return this.searchService.searchUsersByName(searchQuery); diff --git a/src/search/repositories/search.repository.ts b/src/search/repositories/search.repository.ts index 88e2fa1..c547000 100644 --- a/src/search/repositories/search.repository.ts +++ b/src/search/repositories/search.repository.ts @@ -167,12 +167,7 @@ export class SearchRepository { .getManyAndCount(); } - async searchBoardsByUserName( - category: string, - searchQuery: string, - skip: number, - take: number, - ) { + async findUserId(searchQuery: string) { const returnedUsers = await this.entityManager .createQueryBuilder(User, 'user') .where(`MATCH(name) AGAINST (:searchQuery IN BOOLEAN MODE)`, { @@ -181,6 +176,15 @@ export class SearchRepository { .select(['user.id']) .getMany(); + if (returnedUsers.length) return returnedUsers; + } + + async searchBoardsByUserName( + category: string, + returnedUsers: User[], + skip: number, + take: number, + ) { if (category === '전체') { return this.entityManager .createQueryBuilder(Board, 'board') diff --git a/src/search/services/search.service.ts b/src/search/services/search.service.ts index ac80bb8..c653ea9 100644 --- a/src/search/services/search.service.ts +++ b/src/search/services/search.service.ts @@ -9,7 +9,7 @@ export class SearchService { private searchRepository: SearchRepository, private boardLikesRepository: BoardsLikeRepository, ) {} - async searchBoardsByHead( + async searchBoardsByHeadOrBodyOrUserName( head: string, body: string, userName: string, @@ -117,13 +117,23 @@ export class SearchService { throw new NotFoundException('해당 페이지는 존재하지 않습니다'); } } else if (userName) { + const returnedUsers = await this.searchRepository.findUserId(userName); + if (!returnedUsers.length) return returnedUsers; + + const take = limit; + const skip = page <= 0 ? (page = 0) : (page - 1) * take; const [returnedBoards, total] = await this.searchRepository.searchBoardsByUserName( category, - userName, + returnedUsers, skip, take, ); + + if (returnedBoards.length) { + return returnedBoards; + } + const last_page = Math.ceil(total / take); const boardResponse: BoardResponseDTO[] = await Promise.all( @@ -167,6 +177,181 @@ export class SearchService { } } + async searchBoardsByHead( + category: string, + serachQuery: string, + page: number, + limit: number, + ) { + const take = limit; + const skip = page <= 0 ? (page = 0) : (page - 1) * take; + const [returnedBoards, total] = + await this.searchRepository.searchBoardsByHead( + category, + serachQuery, + skip, + take, + ); + const last_page = Math.ceil(total / take); + + const boardResponse: BoardResponseDTO[] = await Promise.all( + returnedBoards.map(async (board) => { + const like = await this.boardLikesRepository.getBoardLikesCount( + board.id, + ); + + return { + id: board.id, + head: board.head, + body: board.body.substring(0, 30), + main_category: board.main_category, + sub_category: board.sub_category, + createAt: board.createAt, + updateAt: board.updateAt, + userId: { + name: board.user[0].name, + userImage: board.user[0].userImage ? board.user[0].userImage : [], + }, + boardLike: like, + boardImages: board.boardImages.map((image) => ({ + id: image.id, + imageUrl: image.imageUrl, + })), + }; + }), + ); + if (last_page >= page) { + return { + data: boardResponse, + meta: { + total, + page: page <= 0 ? (page = 1) : page, + last_page, + }, + }; + } else { + throw new NotFoundException('해당 페이지는 존재하지 않습니다'); + } + } + + async searchBoardsByBody( + category: string, + serachQuery: string, + page: number, + limit: number, + ) { + const take = limit; + const skip = page <= 0 ? (page = 0) : (page - 1) * take; + const [returnedBoards, total] = + await this.searchRepository.searchBoardsByBody( + category, + serachQuery, + skip, + take, + ); + const last_page = Math.ceil(total / take); + + const boardResponse: BoardResponseDTO[] = await Promise.all( + returnedBoards.map(async (board) => { + const like = await this.boardLikesRepository.getBoardLikesCount( + board.id, + ); + + return { + id: board.id, + head: board.head, + body: board.body.substring(0, 30), + main_category: board.main_category, + sub_category: board.sub_category, + createAt: board.createAt, + updateAt: board.updateAt, + userId: { + name: board.user[0].name, + userImage: board.user[0].userImage ? board.user[0].userImage : [], + }, + boardLike: like, + boardImages: board.boardImages.map((image) => ({ + id: image.id, + imageUrl: image.imageUrl, + })), + }; + }), + ); + if (last_page >= page) { + return { + data: boardResponse, + meta: { + total, + page: page <= 0 ? (page = 1) : page, + last_page, + }, + }; + } else { + throw new NotFoundException('해당 페이지는 존재하지 않습니다'); + } + } + + async searchBoardsByUserName( + category: string, + serachQuery: string, + page: number, + limit: number, + ) { + const returnedUsers = await this.searchRepository.findUserId(serachQuery); + if (!returnedUsers) return []; + + const take = limit; + const skip = page <= 0 ? (page = 0) : (page - 1) * take; + const [returnedBoards, total] = + await this.searchRepository.searchBoardsByUserName( + category, + returnedUsers, + skip, + take, + ); + + const last_page = Math.ceil(total / take); + + const boardResponse: BoardResponseDTO[] = await Promise.all( + returnedBoards.map(async (board) => { + const like = await this.boardLikesRepository.getBoardLikesCount( + board.id, + ); + + return { + id: board.id, + head: board.head, + body: board.body.substring(0, 30), + main_category: board.main_category, + sub_category: board.sub_category, + createAt: board.createAt, + updateAt: board.updateAt, + userId: { + name: board.user[0].name, + userImage: board.user[0].userImage ? board.user[0].userImage : [], + }, + boardLike: like, + boardImages: board.boardImages.map((image) => ({ + id: image.id, + imageUrl: image.imageUrl, + })), + }; + }), + ); + if (last_page >= page) { + return { + data: boardResponse, + meta: { + total, + page: page <= 0 ? (page = 1) : page, + last_page, + }, + }; + } else { + throw new NotFoundException('해당 페이지는 존재하지 않습니다'); + } + } + async searchUsersByName(searchQuery: string) { return this.searchRepository.searchUsersByName(searchQuery); } diff --git a/src/search/swagger-decorators/search-boards-by-head-or-body-or-user-name.decorator.ts b/src/search/swagger-decorators/search-boards-by-head-or-body-or-user-name.decorator.ts index 428374f..58ad33c 100644 --- a/src/search/swagger-decorators/search-boards-by-head-or-body-or-user-name.decorator.ts +++ b/src/search/swagger-decorators/search-boards-by-head-or-body-or-user-name.decorator.ts @@ -8,7 +8,7 @@ export function ApiSearchBoardsByHeadOrBodyOrUserName() { description: `Query String의 내용과 Param으로 입력된 카테고리를 토대로 게시글의 제목/본문/유저이름에 일치하는 값을 조회합니다(카테고리에 "전체" 입력 시 전체 게시판 검색). ex)'흑돼지 고기' 검색 - '흑돼지', '고기' 라는 단어 단위로 검색. (정확성 순으로 정렬됨) 제목 검색 시 head, 본문 검색시 body, 작성자 검색시 userName - ex) http://localhost:3000/search/board/전체?head=흑돼지&page=1&limit=16`, + ex) http://localhost:3000/search/boards/전체?head=흑돼지&page=1&limit=16`, }), ApiResponse({ status: 200, diff --git a/src/search/swagger-decorators/search-boards-by-head.decorator.ts b/src/search/swagger-decorators/search-boards-by-head.decorator.ts new file mode 100644 index 0000000..0cec99e --- /dev/null +++ b/src/search/swagger-decorators/search-boards-by-head.decorator.ts @@ -0,0 +1,302 @@ +import { applyDecorators } from '@nestjs/common'; +import { ApiOperation, ApiResponse } from '@nestjs/swagger'; + +export function ApiSearchBoardsByHead() { + return applyDecorators( + ApiOperation({ + summary: '게시글 검색 API', + description: `Query String의 내용과 Param으로 입력된 카테고리를 토대로 게시글의 제목에 일치하는 값을 조회합니다(카테고리에 "전체" 입력 시 전체 게시판 검색). + ex)'흑돼지 고기' 검색 - '흑돼지', '고기' 라는 단어 단위로 검색. (정확성 순으로 정렬됨)`, + }), + ApiResponse({ + status: 200, + description: '성공적으로 검색한 게시글(제목 기준) 조회', + content: { + JSON: { + example: { + data: [ + { + id: 286, + head: '흑돼지고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:33:59.713Z', + updateAt: '2023-11-01T21:33:59.713Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 293, + head: '흑돼지고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:25.623Z', + updateAt: '2023-11-01T21:34:25.623Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 292, + head: '백돼지고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:21.670Z', + updateAt: '2023-11-01T21:34:21.670Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 287, + head: '흑돼지 고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:01.707Z', + updateAt: '2023-11-01T21:34:01.707Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 294, + head: '흑돼지 고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:32.500Z', + updateAt: '2023-11-01T21:34:32.500Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 289, + head: '양 고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:10.379Z', + updateAt: '2023-11-01T21:34:10.379Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 290, + head: '백 고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:16.132Z', + updateAt: '2023-11-01T21:34:16.132Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 291, + head: '백고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:17.954Z', + updateAt: '2023-11-01T21:34:17.954Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 295, + head: '기린고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:36.688Z', + updateAt: '2023-11-01T21:34:36.688Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 296, + head: '소고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:40.449Z', + updateAt: '2023-11-01T21:34:40.449Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 297, + head: '소 고기', + body: '바 보', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:34:42.169Z', + updateAt: '2023-11-01T21:34:42.169Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 298, + head: '소 고기', + body: '흑돼지', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:39:22.442Z', + updateAt: '2023-11-01T21:39:22.442Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + { + id: 300, + head: '소 고기', + body: '백돼지기', + main_category: '만남', + sub_category: '친구', + createAt: '2023-11-01T21:39:30.715Z', + updateAt: '2023-11-01T21:39:30.715Z', + userId: { + name: '이재진', + userImage: { + id: 16, + userId: 66, + imageUrl: + 'http://k.kakaocdn.net/dn/prL5M/btsjYsDuxVB/zzswnuvKP610YUBwvRuSb1/img_640x640.jpg', + }, + }, + boardLike: 0, + boardImages: [], + }, + ], + meta: { + total: 13, + page: 1, + last_page: 1, + }, + }, + }, + }, + }), + ApiResponse({ + status: 404, + description: '페이지 조회 실패.', + content: { + JSON: { + example: { + message: '해당 페이지는 존재하지 않습니다.', + error: 'Not Found', + statusCode: 404, + }, + }, + }, + }), + ); +} diff --git a/src/search/swagger-decorators/search-boards-by-userName.decorator.ts b/src/search/swagger-decorators/search-boards-by-user-name.decorator.ts similarity index 100% rename from src/search/swagger-decorators/search-boards-by-userName.decorator.ts rename to src/search/swagger-decorators/search-boards-by-user-name.decorator.ts diff --git a/src/search/swagger-decorators/search-users-by-user-name.decorato.ts b/src/search/swagger-decorators/search-users-by-user-name.decorato.ts new file mode 100644 index 0000000..50b6a11 --- /dev/null +++ b/src/search/swagger-decorators/search-users-by-user-name.decorato.ts @@ -0,0 +1,29 @@ +import { applyDecorators } from '@nestjs/common'; +import { ApiOperation, ApiResponse } from '@nestjs/swagger'; + +export function ApiSearchUsersByUserName() { + return applyDecorators( + ApiOperation({ + summary: '유저 검색 API', + description: `Query String의 내용에 유저의 닉네임을 입력해서 검색`, + }), + ApiResponse({ + status: 200, + description: '성공적으로 검색한 유저(userName 기준) 조회', + content: { + JSON: { + example: [ + { + id: 69, + provider: 'kakao', + name: '정비호', + email: 'jjb2643@nate.com', + gender: 'M', + admin: false, + }, + ], + }, + }, + }), + ); +} From 44467981818ab11440d9f29792483b7abb241199 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Fri, 10 Nov 2023 16:48:10 +0900 Subject: [PATCH 25/69] =?UTF-8?q?conflict(#46):=20=EC=B6=A9=EB=8F=8C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B02?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/boards/entities/board-image.entity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boards/entities/board-image.entity.ts b/src/boards/entities/board-image.entity.ts index 916256d..4e02ef9 100644 --- a/src/boards/entities/board-image.entity.ts +++ b/src/boards/entities/board-image.entity.ts @@ -12,7 +12,7 @@ export class BoardImage { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => Board, (board) => board.boardImages, { + @ManyToOne(() => Board, (board) => board.boardImage, { onDelete: 'CASCADE', }) @JoinColumn({ name: 'board_id' }) From 910c73d2c93dc24965d39378687622d36c604c12 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Fri, 10 Nov 2023 16:57:42 +0900 Subject: [PATCH 26/69] =?UTF-8?q?conflict(#46):=20=EC=B6=A9=EB=8F=8C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B03?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/boards/entities/board-image.entity.ts | 2 +- src/boards/entities/board.entity.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/boards/entities/board-image.entity.ts b/src/boards/entities/board-image.entity.ts index 4e02ef9..916256d 100644 --- a/src/boards/entities/board-image.entity.ts +++ b/src/boards/entities/board-image.entity.ts @@ -12,7 +12,7 @@ export class BoardImage { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => Board, (board) => board.boardImage, { + @ManyToOne(() => Board, (board) => board.boardImages, { onDelete: 'CASCADE', }) @JoinColumn({ name: 'board_id' }) diff --git a/src/boards/entities/board.entity.ts b/src/boards/entities/board.entity.ts index eb3851d..312efd4 100644 --- a/src/boards/entities/board.entity.ts +++ b/src/boards/entities/board.entity.ts @@ -30,8 +30,8 @@ export class Board { @JoinColumn({ name: 'user_id' }) user: User; - @OneToMany(() => BoardImage, (boardImage) => boardImage.board) - boardImage: BoardImage[]; + @OneToMany(() => BoardImage, (boardImages) => boardImages.board) + boardImages: BoardImage[]; @OneToMany(() => BoardLike, (boardLike) => boardLike.boardId) boardLike: BoardLike; From e1200bca793724b0d37e28ca7fc9013163daa902 Mon Sep 17 00:00:00 2001 From: NicoDora Date: Fri, 10 Nov 2023 17:22:45 +0900 Subject: [PATCH 27/69] =?UTF-8?q?refactor(#11):=20sameSite=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/controllers/auth.controller.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 87e119a..9f4c591 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -53,6 +53,7 @@ export class AuthController { ); res.cookie('refresh_token', refreshToken, { + sameSite: 'lax', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); @@ -79,6 +80,7 @@ export class AuthController { ); res.cookie('refresh_token', refreshToken, { + sameSite: 'lax', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); From 7b68003133362939a86b64244c45de03ffa84339 Mon Sep 17 00:00:00 2001 From: 2swo Date: Mon, 13 Nov 2023 13:57:06 +0900 Subject: [PATCH 28/69] =?UTF-8?q?Refactor(#117)=20Board=20controller=20?= =?UTF-8?q?=EC=BB=A4=EC=8A=A4=ED=85=80=EB=8D=B0=EC=BD=94=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/boards/controllers/Boards.controller.ts | 26 ++++++++++----------- src/friends/entities/friends.entity.ts | 6 ++++- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/boards/controllers/Boards.controller.ts b/src/boards/controllers/Boards.controller.ts index 5952a3e..56ee070 100644 --- a/src/boards/controllers/Boards.controller.ts +++ b/src/boards/controllers/Boards.controller.ts @@ -7,8 +7,8 @@ import { Delete, UseInterceptors, Query, - Headers, UploadedFiles, + UseGuards, } from '@nestjs/common'; import { BoardsService } from '../services/Boards.service'; import { Board } from '../entities/board.entity'; @@ -26,6 +26,8 @@ import { ApiUpdateBoard } from '../swagger-decorators/patch-board-decorators'; import { ApiTags } from '@nestjs/swagger'; import { ApiDeleteBoard } from '../swagger-decorators/delete-board-decorators'; import { ApiUpdateBoardImage } from '../swagger-decorators/patch-board-images-decorators'; +import { JwtAccessTokenGuard } from 'src/config/guards/jwt-access-token.guard'; +import { GetUserId } from 'src/common/decorators/get-userId.decorator'; @Controller('boards') @ApiTags('board API') @@ -37,26 +39,24 @@ export class BoardsController { ) {} @Post('') + @UseGuards(JwtAccessTokenGuard) @ApiAddBoard() async create( - @Headers('access_token') accessToken: string, + @GetUserId() userId: number, @Body() createBoardDto: CreateBoardDto, ): Promise { - const userId = await this.tokenService.decodeToken(accessToken); return await this.boardsService.create(createBoardDto, userId); } @Post('/images') + @UseGuards(JwtAccessTokenGuard) @UseInterceptors(FilesInterceptor('files', 3)) @ApiUploadBoardImages() async uploadImage( - @Headers('access_token') accesstoken: string, + @GetUserId() userId: number, @Query('boardId') boardId: number, @UploadedFiles() files: Express.Multer.File[], ): Promise { - const userId = await this.tokenService.decodeToken(accesstoken); - console.log(files); - return await this.boardImagesService.createBoardImages( boardId, files, @@ -74,13 +74,13 @@ export class BoardsController { } @Get('/unit') + @UseGuards(JwtAccessTokenGuard) @ApiGetOneBoard() async findOne( @Query('boardId') boardId: number, - @Headers('access_token') accesstoken: string, + @GetUserId() userId: number, ): Promise { ``; - const userId = await this.tokenService.decodeToken(accesstoken); return await this.boardsService.findOneBoard(boardId, userId); } @@ -94,15 +94,15 @@ export class BoardsController { } @Patch('/images') + @UseGuards(JwtAccessTokenGuard) @ApiUpdateBoardImage() @UseInterceptors(FilesInterceptor('files', 3)) async editBoardImages( - @Headers('access_token') accessToken: string, + @GetUserId() userId: number, @Query('boardId') boardId: number, @Query('deleteImageUrl') deleteImageUrl: string[], @UploadedFiles() files: Express.Multer.File[], ) { - const userId = await this.tokenService.decodeToken(accessToken); return await this.boardImagesService.updateBoardImages( boardId, files, @@ -112,12 +112,12 @@ export class BoardsController { } @Delete('') + @UseGuards(JwtAccessTokenGuard) @ApiDeleteBoard() async deleteBoard( @Query('boardId') boardId: number, - @Headers('access_token') accessToken: string, + @GetUserId() userId: number, ) { - const userId = await this.tokenService.decodeToken(accessToken); await this.boardsService.deleteBoard(boardId, userId); } } diff --git a/src/friends/entities/friends.entity.ts b/src/friends/entities/friends.entity.ts index c523dbf..86cb366 100644 --- a/src/friends/entities/friends.entity.ts +++ b/src/friends/entities/friends.entity.ts @@ -40,6 +40,10 @@ export class Friend { @Column({ type: 'enum', enum: Status, default: Status.PENDING }) status!: Status; - @Column({ name: 'created_at', type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' }) + @Column({ + name: 'created_at', + type: 'timestamp', + default: () => 'CURRENT_TIMESTAMP', + }) createdAt: Date; } From 1e4740a21b63e12c6bfe44c2c4e76cbd2a39769d Mon Sep 17 00:00:00 2001 From: 2swo Date: Mon, 13 Nov 2023 14:00:31 +0900 Subject: [PATCH 29/69] =?UTF-8?q?Refactor(#117)=20Board-like=20controller?= =?UTF-8?q?=20=EC=BB=A4=EC=8A=A4=ED=85=80=EB=8D=B0=EC=BD=94=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/boards-like.controller.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/boards/controllers/boards-like.controller.ts b/src/boards/controllers/boards-like.controller.ts index e66f34b..9a433ee 100644 --- a/src/boards/controllers/boards-like.controller.ts +++ b/src/boards/controllers/boards-like.controller.ts @@ -8,7 +8,7 @@ import { UsePipes, ValidationPipe, Query, - Headers, + UseGuards, } from '@nestjs/common'; import { BoardsLikeService } from '../services/boards-like.service'; import { ApiTags } from '@nestjs/swagger'; @@ -16,6 +16,8 @@ import { ApiAddBoardLike } from '../swagger-decorators/add-board-like.decorator' import { ApiGetBoardLikeCount } from '../swagger-decorators/get-board-like-count.decorator'; import { ApiDeleteBoardLike } from '../swagger-decorators/delete-board-like.decorator'; import { TokenService } from 'src/auth/services/token.service'; +import { GetUserId } from 'src/common/decorators/get-userId.decorator'; +import { JwtAccessTokenGuard } from 'src/config/guards/jwt-access-token.guard'; @ApiTags('BOARDS-LIKE') @UsePipes(ValidationPipe) @@ -28,23 +30,22 @@ export class BoardsLikeController { @ApiAddBoardLike() @Post('like/:boardId') + @UseGuards(JwtAccessTokenGuard) async addBoardLike( - @Headers('access_token') accessToken: string, + @GetUserId() userId: number, @Param('boardId', ParseIntPipe) boardId: number, ) { - const userId = await this.tokenService.decodeToken(accessToken); return this.boardsLikeService.addBoardLike(boardId, userId); } @ApiGetBoardLikeCount() @Get('like') + @UseGuards(JwtAccessTokenGuard) async getBoardsLike( - @Headers('access_token') accessToken: string, + @GetUserId() userId: number, @Query('boardId', ParseIntPipe) boardId: number, ) { try { - const userId = await this.tokenService.decodeToken(accessToken); - return this.boardsLikeService.getBoardLikesAndIsLike(boardId, userId); } catch (error) { if ( @@ -65,12 +66,12 @@ export class BoardsLikeController { @ApiDeleteBoardLike() @Delete('like/:boardId') + @UseGuards(JwtAccessTokenGuard) async deleteBoardLike( - @Headers('access_token') accessToken: string, + @GetUserId() userId: number, @Param('boardId', ParseIntPipe) boardId: number, ) { - const userId = await this.tokenService.decodeToken(accessToken); return this.boardsLikeService.deleteBoardLike(boardId, userId); } } From 955540401d90465899991c8202a780b9d0832cbf Mon Sep 17 00:00:00 2001 From: NicoDora Date: Mon, 13 Nov 2023 17:07:33 +0900 Subject: [PATCH 30/69] =?UTF-8?q?refactor(#11):=20sameSite=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/controllers/auth.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 9f4c591..ec2e65f 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -53,7 +53,7 @@ export class AuthController { ); res.cookie('refresh_token', refreshToken, { - sameSite: 'lax', + sameSite: 'None', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); @@ -80,7 +80,7 @@ export class AuthController { ); res.cookie('refresh_token', refreshToken, { - sameSite: 'lax', + sameSite: 'None', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); From 98ca9d46506d45731b407e8279af49e8d5d15b8b Mon Sep 17 00:00:00 2001 From: NicoDora Date: Mon, 13 Nov 2023 17:21:48 +0900 Subject: [PATCH 31/69] =?UTF-8?q?refactor(#11):=20sameSite=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/controllers/auth.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index ec2e65f..9f4c591 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -53,7 +53,7 @@ export class AuthController { ); res.cookie('refresh_token', refreshToken, { - sameSite: 'None', + sameSite: 'lax', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); @@ -80,7 +80,7 @@ export class AuthController { ); res.cookie('refresh_token', refreshToken, { - sameSite: 'None', + sameSite: 'lax', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); From da8c7c25ea2dc497bd5931766615e7fa81acaa44 Mon Sep 17 00:00:00 2001 From: NicoDora Date: Mon, 13 Nov 2023 20:44:10 +0900 Subject: [PATCH 32/69] =?UTF-8?q?refactor(#11):=20httpOnly=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/controllers/auth.controller.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 9f4c591..6ced774 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -53,6 +53,7 @@ export class AuthController { ); res.cookie('refresh_token', refreshToken, { + httpOnly: true, sameSite: 'lax', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); @@ -80,6 +81,7 @@ export class AuthController { ); res.cookie('refresh_token', refreshToken, { + httpOnly: true, sameSite: 'lax', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); From 857b690370a73330ccc19d3955e14b0445afd7ab Mon Sep 17 00:00:00 2001 From: NicoDora Date: Tue, 14 Nov 2023 14:20:27 +0900 Subject: [PATCH 33/69] =?UTF-8?q?refactor(#11):=20cors=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index c59b8bb..fbeede7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,7 +11,7 @@ async function bootstrap() { app.useGlobalPipes(new ValidationPipe()); // app.useGlobalFilters(new HttpExceptionFilter()); app.enableCors({ - origin: true, // 또는 특정 도메인을 설정 + origin: '*', // 허용할 도메인 methods: 'GET ,HEAD, PUT, PATCH, POST, DELETE', credentials: true, // 이 옵션을 true로 설정하여 쿠키 전송을 허용 }); From be4a6dd3b8762964642148d4759be3dfdd062900 Mon Sep 17 00:00:00 2001 From: NicoDora Date: Tue, 14 Nov 2023 14:35:28 +0900 Subject: [PATCH 34/69] =?UTF-8?q?refactor(#11):=20cookie=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/controllers/auth.controller.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 6ced774..366219b 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -55,6 +55,7 @@ export class AuthController { res.cookie('refresh_token', refreshToken, { httpOnly: true, sameSite: 'lax', + path: '/', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); @@ -83,6 +84,7 @@ export class AuthController { res.cookie('refresh_token', refreshToken, { httpOnly: true, sameSite: 'lax', + path: '/', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); From 7bd4d04cf0060797275aaf55f07d7cb7128319a0 Mon Sep 17 00:00:00 2001 From: NicoDora Date: Tue, 14 Nov 2023 15:40:34 +0900 Subject: [PATCH 35/69] =?UTF-8?q?refactor(#11):=20cookie=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/controllers/auth.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 366219b..4c63e5f 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -55,7 +55,7 @@ export class AuthController { res.cookie('refresh_token', refreshToken, { httpOnly: true, sameSite: 'lax', - path: '/', + domain: 'localhost', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); @@ -84,7 +84,7 @@ export class AuthController { res.cookie('refresh_token', refreshToken, { httpOnly: true, sameSite: 'lax', - path: '/', + domain: 'localhost', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); From 73dcdd97a51483ecac797bcc72a5aefc7d6e2ca7 Mon Sep 17 00:00:00 2001 From: NicoDora Date: Tue, 14 Nov 2023 16:34:30 +0900 Subject: [PATCH 36/69] =?UTF-8?q?refactor(#11):=20cookie=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index fbeede7..b9f9503 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,7 +11,7 @@ async function bootstrap() { app.useGlobalPipes(new ValidationPipe()); // app.useGlobalFilters(new HttpExceptionFilter()); app.enableCors({ - origin: '*', // 허용할 도메인 + origin: 'http://localhost:3000', // 허용되는 Origin URL methods: 'GET ,HEAD, PUT, PATCH, POST, DELETE', credentials: true, // 이 옵션을 true로 설정하여 쿠키 전송을 허용 }); From eb942d062beed02d2dc088fc389a868910f1e638 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Tue, 14 Nov 2023 16:49:23 +0900 Subject: [PATCH 37/69] =?UTF-8?q?modify(#11):=20cors=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index c59b8bb..1e4bdd1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,7 +6,7 @@ import { AsyncApiDocumentBuilder, AsyncApiModule } from 'nestjs-asyncapi'; import * as cookieParser from 'cookie-parser'; async function bootstrap() { - const app = await NestFactory.create(AppModule, { cors: true }); + const app = await NestFactory.create(AppModule); const logger = new Logger(); app.useGlobalPipes(new ValidationPipe()); // app.useGlobalFilters(new HttpExceptionFilter()); From 65ba2d5c47e548b0e6891975ef87d644a2ed0ae8 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Tue, 14 Nov 2023 17:49:31 +0900 Subject: [PATCH 38/69] =?UTF-8?q?modify(#11):=20cors=20origin=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 6d2c256..1e4bdd1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,7 +11,7 @@ async function bootstrap() { app.useGlobalPipes(new ValidationPipe()); // app.useGlobalFilters(new HttpExceptionFilter()); app.enableCors({ - origin: 'http://localhost:3000', // 허용되는 Origin URL + origin: true, // 또는 특정 도메인을 설정 methods: 'GET ,HEAD, PUT, PATCH, POST, DELETE', credentials: true, // 이 옵션을 true로 설정하여 쿠키 전송을 허용 }); From 9f4ac71a7d7c99a10d8764be1c86bfcdd69e2dde Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Tue, 14 Nov 2023 17:52:05 +0900 Subject: [PATCH 39/69] =?UTF-8?q?modify(#11):=20sameSite=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/controllers/auth.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 4c63e5f..930ac96 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -54,7 +54,7 @@ export class AuthController { res.cookie('refresh_token', refreshToken, { httpOnly: true, - sameSite: 'lax', + sameSite: 'Lax', domain: 'localhost', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); @@ -83,7 +83,7 @@ export class AuthController { res.cookie('refresh_token', refreshToken, { httpOnly: true, - sameSite: 'lax', + sameSite: 'Lax', domain: 'localhost', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); From 1fe05051b2487fb2f231e9bcc599f94a2afc68d3 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Wed, 15 Nov 2023 00:31:38 +0900 Subject: [PATCH 40/69] =?UTF-8?q?test(#11):=20sameSite=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/controllers/auth.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 930ac96..4116c8c 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -54,7 +54,7 @@ export class AuthController { res.cookie('refresh_token', refreshToken, { httpOnly: true, - sameSite: 'Lax', + sameSite: 'None', domain: 'localhost', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); @@ -83,7 +83,7 @@ export class AuthController { res.cookie('refresh_token', refreshToken, { httpOnly: true, - sameSite: 'Lax', + sameSite: 'None', domain: 'localhost', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); From 59129771beecd8b0a89218c44a301a256b2cc263 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Wed, 15 Nov 2023 00:52:53 +0900 Subject: [PATCH 41/69] =?UTF-8?q?test(#11):=20sameSite=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/controllers/auth.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 4116c8c..930ac96 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -54,7 +54,7 @@ export class AuthController { res.cookie('refresh_token', refreshToken, { httpOnly: true, - sameSite: 'None', + sameSite: 'Lax', domain: 'localhost', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); @@ -83,7 +83,7 @@ export class AuthController { res.cookie('refresh_token', refreshToken, { httpOnly: true, - sameSite: 'None', + sameSite: 'Lax', domain: 'localhost', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); From e97e9c24868499ae683904e2eb6b088c60c1a299 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Wed, 15 Nov 2023 01:04:06 +0900 Subject: [PATCH 42/69] =?UTF-8?q?test(#11):=20sameSite=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/controllers/auth.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 930ac96..4116c8c 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -54,7 +54,7 @@ export class AuthController { res.cookie('refresh_token', refreshToken, { httpOnly: true, - sameSite: 'Lax', + sameSite: 'None', domain: 'localhost', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); @@ -83,7 +83,7 @@ export class AuthController { res.cookie('refresh_token', refreshToken, { httpOnly: true, - sameSite: 'Lax', + sameSite: 'None', domain: 'localhost', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); From 3723c9815ac8533b445dbde6226160b6d625b15e Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Wed, 15 Nov 2023 01:14:41 +0900 Subject: [PATCH 43/69] =?UTF-8?q?test(#11):=20key=EA=B0=92=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/controllers/auth.controller.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 4116c8c..461dc98 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -81,13 +81,14 @@ export class AuthController { kakaoRefreshToken, ); - res.cookie('refresh_token', refreshToken, { + res.cookie('refreshToken', refreshToken, { httpOnly: true, sameSite: 'None', domain: 'localhost', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); + console.log(accessToken); return res.json({ accessToken }); } From 0c8692da04823122f6b26edbf468ea977619ce89 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Wed, 15 Nov 2023 01:22:52 +0900 Subject: [PATCH 44/69] =?UTF-8?q?test(#11):=20return=EA=B0=92=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/controllers/auth.controller.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 461dc98..5bfc2e7 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -73,7 +73,7 @@ export class AuthController { await this.authService.kakaoLogin(code); const accessToken = await this.tokenService.createAccessToken(userId); const refreshToken = await this.tokenService.createRefreshToken(userId); - + console.log(refreshToken); await this.tokenService.saveTokens( userId, refreshToken, @@ -88,8 +88,10 @@ export class AuthController { maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); + console.log(res); console.log(accessToken); - return res.json({ accessToken }); + console.log(refreshToken); + return res.json({ accessToken, refreshToken }); } @ApiCookieAuth('refresh-token') From 7a7bbfd919ee802b3d2ea5fa0d12d8c31bd29132 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Wed, 15 Nov 2023 01:41:04 +0900 Subject: [PATCH 45/69] =?UTF-8?q?test(#11):=20passThrough=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/controllers/auth.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 5bfc2e7..11fd71c 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -64,7 +64,7 @@ export class AuthController { @ApiKakaoLogin() @Get('kakao/login') - async kakaoLogin(@Query() { code }, @Res() res) { + async kakaoLogin(@Query() { code }, @Res({ passthrough: true }) res) { if (!code) { throw new BadRequestException('인가코드가 없습니다.'); } From 97d7836a0cc987a26b66a2f1b3f111512a258d72 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Wed, 15 Nov 2023 01:48:51 +0900 Subject: [PATCH 46/69] =?UTF-8?q?test(#11):=20=EB=A1=A4=EB=B0=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/controllers/auth.controller.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 11fd71c..e99922f 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -54,7 +54,7 @@ export class AuthController { res.cookie('refresh_token', refreshToken, { httpOnly: true, - sameSite: 'None', + sameSite: 'Lax', domain: 'localhost', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); @@ -64,7 +64,7 @@ export class AuthController { @ApiKakaoLogin() @Get('kakao/login') - async kakaoLogin(@Query() { code }, @Res({ passthrough: true }) res) { + async kakaoLogin(@Query() { code }, @Res() res) { if (!code) { throw new BadRequestException('인가코드가 없습니다.'); } @@ -73,7 +73,7 @@ export class AuthController { await this.authService.kakaoLogin(code); const accessToken = await this.tokenService.createAccessToken(userId); const refreshToken = await this.tokenService.createRefreshToken(userId); - console.log(refreshToken); + await this.tokenService.saveTokens( userId, refreshToken, @@ -81,17 +81,14 @@ export class AuthController { kakaoRefreshToken, ); - res.cookie('refreshToken', refreshToken, { + res.cookie('refresh_Token', refreshToken, { httpOnly: true, - sameSite: 'None', + sameSite: 'Lax', domain: 'localhost', maxAge: 1000 * 60 * 60 * 24 * 7, // 7일 }); - console.log(res); - console.log(accessToken); - console.log(refreshToken); - return res.json({ accessToken, refreshToken }); + return res.json({ accessToken }); } @ApiCookieAuth('refresh-token') From c1b209165f96cc9e5d5dbfce0353122ad2e4c18a Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Wed, 15 Nov 2023 11:03:57 +0900 Subject: [PATCH 47/69] =?UTF-8?q?modify(#99):=20reComment=201=EA=B0=9C?= =?UTF-8?q?=EB=A7=8C=20=EB=B6=88=EB=9F=AC=EC=98=A4=EB=8A=94=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EB=8B=A4=20=EB=B6=88=EB=9F=AC=EC=98=A4=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/comments/repository/comments.repository.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/comments/repository/comments.repository.ts b/src/comments/repository/comments.repository.ts index 65d6013..e286937 100644 --- a/src/comments/repository/comments.repository.ts +++ b/src/comments/repository/comments.repository.ts @@ -26,8 +26,7 @@ export class CommentsRepository { .leftJoinAndSelect('reComment.user', 'reCommentUser') .leftJoinAndSelect('reCommentUser.userImage', 'reCommentUserImage') .leftJoinAndSelect('user.userImage', 'userImage') - .where('comment.boardId = :boardId', { boardId }) - .groupBy('comment.id'); + .where('comment.boardId = :boardId', { boardId }); return query.getMany(); } From f207b68ce506c0e839aedd2ff09d152074e01aa5 Mon Sep 17 00:00:00 2001 From: NicoDora Date: Wed, 15 Nov 2023 17:36:04 +0900 Subject: [PATCH 48/69] =?UTF-8?q?fix(#40):=20=EC=B9=9C=EA=B5=AC=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EC=98=81=EA=B5=AC=EA=B1=B0=EC=A0=88=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/friends/repositories/friends.repository.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/friends/repositories/friends.repository.ts b/src/friends/repositories/friends.repository.ts index a39313b..bd60eaa 100644 --- a/src/friends/repositories/friends.repository.ts +++ b/src/friends/repositories/friends.repository.ts @@ -159,7 +159,7 @@ export class FriendsRepository { if (!friend) { return null; } - friend.status = Status.REJECT; + friend.status = Status.PERMANENT; return await this.entityManager.save(Friend, friend); } From 75b44a96faa46cef92d1f1bcde29a21fb03e4690 Mon Sep 17 00:00:00 2001 From: 2swo Date: Wed, 15 Nov 2023 18:18:07 +0900 Subject: [PATCH 49/69] =?UTF-8?q?Refactor(#117)=20Comment=20controller=20?= =?UTF-8?q?=EC=BB=A4=EC=8A=A4=ED=85=80=EB=8D=B0=EC=BD=94=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/comments.controller.ts | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/comments/controllers/comments.controller.ts b/src/comments/controllers/comments.controller.ts index 44edd8f..babc95b 100644 --- a/src/comments/controllers/comments.controller.ts +++ b/src/comments/controllers/comments.controller.ts @@ -2,16 +2,15 @@ import { Controller, Post, Body, - Headers, Query, Get, Patch, Delete, + UseGuards, } from '@nestjs/common'; import { Comment } from '../entities/comment.entity'; import { ApiTags } from '@nestjs/swagger'; import { CommentsService } from '../services/comments.services'; -import { TokenService } from 'src/auth/services/token.service'; import { CreateCommentDto } from '../dto/create-comment-dto'; import { ApiAddComment } from '../swagger-decoratros/add-comment-decorators'; import { ApiGetAllComment } from '../swagger-decoratros/get-all-comment-decorators'; @@ -23,6 +22,8 @@ import { CreateReCommentDto } from '../dto/create-recomment-dto'; import { ReComment } from '../entities/recomment.entity'; import { ApiAddReComment } from '../swagger-decoratros/add-recomment-decorators'; import { ApiUpdateReComment } from '../swagger-decoratros/patch-recomment-decorator'; +import { JwtAccessTokenGuard } from 'src/config/guards/jwt-access-token.guard'; +import { GetUserId } from 'src/common/decorators/get-userId.decorator'; @Controller('comments') @ApiTags('Comment API') @@ -30,28 +31,27 @@ export class CommentsController { constructor( private readonly commentsService: CommentsService, private readonly recommentsService: ReCommentsService, - private tokenService: TokenService, ) {} @Post('') + @UseGuards(JwtAccessTokenGuard) @ApiAddComment() async createComment( - @Headers('access_token') accessToken: string, + @GetUserId() userId: number, @Query('boardId') boardId: number, @Body() createCommentDto: CreateCommentDto, ): Promise { - const userId = await this.tokenService.decodeToken(accessToken); return await this.commentsService.create(createCommentDto, userId, boardId); } @Post('/Re') + @UseGuards(JwtAccessTokenGuard) @ApiAddReComment() async createReComment( - @Headers('access_token') accessToken: string, + @GetUserId() userId: number, @Query('commentId') commentId: number, @Body() createReCommentDto: CreateReCommentDto, ): Promise { - const userId = await this.tokenService.decodeToken(accessToken); return await this.recommentsService.create( createReCommentDto, userId, @@ -60,12 +60,12 @@ export class CommentsController { } @Get('') + @UseGuards(JwtAccessTokenGuard) @ApiGetAllComment() async getComment( - @Headers('access_token') accessToken: string, + @GetUserId() userId: number, @Query('boardId') boardId: number, ): Promise { - const userId = await this.tokenService.decodeToken(accessToken); return this.commentsService.findAllComments(boardId, userId); } @@ -88,22 +88,22 @@ export class CommentsController { } @Delete('') + @UseGuards(JwtAccessTokenGuard) @ApiDeleteComment() async deleteComment( @Query('commentId') commentId: number, - @Headers('access_token') accessToken: string, + @GetUserId() userId: number, ) { - const userId = await this.tokenService.decodeToken(accessToken); await this.commentsService.deleteComment(commentId, userId); } @Delete('/Re') + @UseGuards(JwtAccessTokenGuard) @ApiDeleteComment() async deleteReComment( @Query('reCommentId') reCommentId: number, - @Headers('access_token') accessToken: string, + @GetUserId() userId: number, ) { - const userId = await this.tokenService.decodeToken(accessToken); await this.recommentsService.deleteReComment(reCommentId, userId); } } From cce3af111511fd8617765e18ffc4f77d4633c8f1 Mon Sep 17 00:00:00 2001 From: NicoDora Date: Thu, 16 Nov 2023 13:46:14 +0900 Subject: [PATCH 50/69] =?UTF-8?q?modify(#40):=20=EC=B9=9C=EA=B5=AC=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/friends/controllers/friends.controller.ts | 16 ++++++++-------- src/friends/entities/friends.entity.ts | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/friends/controllers/friends.controller.ts b/src/friends/controllers/friends.controller.ts index 96eb595..fae5f34 100644 --- a/src/friends/controllers/friends.controller.ts +++ b/src/friends/controllers/friends.controller.ts @@ -79,25 +79,25 @@ export class FriendsController { return await this.friendsService.friendResponseReject(userId, friendId); } - @ApiDeleteRejectPermanentCancel() - @Delete('responses/reject/permanent/:friend_id') - async friendResponseRejectPermanentCancel( + @ApiFriendResponseRejectPermanent() + @Patch('responses/reject/permanent/:friend_id') + async friendResponseRejectPermanent( @GetUserId() userId: number, @Param('friend_id') friendId: number, ) { - return await this.friendsService.friendResponseRejectPermanentCancel( + return await this.friendsService.friendResponseRejectPermanent( userId, friendId, ); } - @ApiFriendResponseRejectPermanent() - @Patch('responses/reject/permanent/:friend_id') - async friendResponseRejectPermanent( + @ApiDeleteRejectPermanentCancel() + @Delete('responses/reject/permanent/:friend_id') + async friendResponseRejectPermanentCancel( @GetUserId() userId: number, @Param('friend_id') friendId: number, ) { - return await this.friendsService.friendResponseRejectPermanent( + return await this.friendsService.friendResponseRejectPermanentCancel( userId, friendId, ); diff --git a/src/friends/entities/friends.entity.ts b/src/friends/entities/friends.entity.ts index 86cb366..0680cf5 100644 --- a/src/friends/entities/friends.entity.ts +++ b/src/friends/entities/friends.entity.ts @@ -12,6 +12,7 @@ export enum Status { ACCEPT = '친구 수락', REJECT = '친구 거절', PERMANENT = '영구 거절', + BLOCK = '차단', } @Entity({ name: 'friend' }) From fc365761e64b379e1f1170d7432387c97bcb4fbb Mon Sep 17 00:00:00 2001 From: NicoDora Date: Thu, 16 Nov 2023 14:06:19 +0900 Subject: [PATCH 51/69] =?UTF-8?q?modify(#40):=20=EC=B9=9C=EA=B5=AC?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=20=EC=B7=A8=EC=86=8C=20API=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/friends/controllers/friends.controller.ts | 10 +++ .../repositories/friends.repository.ts | 19 +++++ src/friends/services/friends.service.ts | 26 +++++++ .../delete-request.decorator.ts | 77 +++++++++++++++++++ 4 files changed, 132 insertions(+) create mode 100644 src/friends/swagger-decorators/delete-request.decorator.ts diff --git a/src/friends/controllers/friends.controller.ts b/src/friends/controllers/friends.controller.ts index fae5f34..0a27e22 100644 --- a/src/friends/controllers/friends.controller.ts +++ b/src/friends/controllers/friends.controller.ts @@ -21,6 +21,7 @@ import { ApiGetRejectPermanent } from '../swagger-decorators/get-reject-permanen import { ApiDeleteRejectPermanentCancel } from '../swagger-decorators/delete-reject-permanent-cancel.decorator'; import { JwtAccessTokenGuard } from 'src/config/guards/jwt-access-token.guard'; import { GetUserId } from 'src/common/decorators/get-userId.decorator'; +import { ApiDeleteRequest } from '../swagger-decorators/delete-request.decorator'; @UseGuards(JwtAccessTokenGuard) @Controller('friends') @@ -91,6 +92,15 @@ export class FriendsController { ); } + @ApiDeleteRequest() + @Delete('requests/cancel/:friend_id') + async friendRequestCancel( + @GetUserId() userId: number, + @Param('friend_id') friendId: number, + ) { + return await this.friendsService.friendRequestCancel(userId, friendId); + } + @ApiDeleteRejectPermanentCancel() @Delete('responses/reject/permanent/:friend_id') async friendResponseRejectPermanentCancel( diff --git a/src/friends/repositories/friends.repository.ts b/src/friends/repositories/friends.repository.ts index bd60eaa..63d62f1 100644 --- a/src/friends/repositories/friends.repository.ts +++ b/src/friends/repositories/friends.repository.ts @@ -105,6 +105,25 @@ export class FriendsRepository { return await this.entityManager.save(friend); } + async friendRequestCancel( + userId: number, + friendId: number, + ): Promise { + const friend = await this.entityManager.findOne(Friend, { + where: { + requesterId: userId, + respondentId: friendId, + status: Status.PENDING, + }, + }); + + if (!friend) { + return null; + } + + return await this.entityManager.delete(Friend, friend); + } + async friendResponseReject( userId: number, friendId: number, diff --git a/src/friends/services/friends.service.ts b/src/friends/services/friends.service.ts index b7dd254..dfee4ec 100644 --- a/src/friends/services/friends.service.ts +++ b/src/friends/services/friends.service.ts @@ -126,6 +126,32 @@ export class FriendsService { } } + async friendRequestCancel(userId: number, friendId: number) { + try { + const cancel = await this.friendsRepository.friendRequestCancel( + userId, + friendId, + ); + if (!cancel) { + throw new HttpException( + '친구 요청을 찾을 수 없습니다.', + HttpStatus.NOT_FOUND, + ); + } + return { message: '친구 요청을 취소했습니다.' }; + } catch (error) { + if (error.getStatus() === HttpStatus.NOT_FOUND) { + throw error; + } else { + console.log(error); + throw new HttpException( + '친구 요청 취소에 실패했습니다.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + } + async friendResponseReject(userId: number, friendId: number) { try { const reject = await this.friendsRepository.friendResponseReject( diff --git a/src/friends/swagger-decorators/delete-request.decorator.ts b/src/friends/swagger-decorators/delete-request.decorator.ts new file mode 100644 index 0000000..97c228c --- /dev/null +++ b/src/friends/swagger-decorators/delete-request.decorator.ts @@ -0,0 +1,77 @@ +import { applyDecorators } from '@nestjs/common'; +import { ApiHeaders, ApiOperation, ApiResponse } from '@nestjs/swagger'; + +export function ApiDeleteRequest() { + return applyDecorators( + ApiOperation({ + summary: '친구 요청 취소 API', + description: '친구 요청을 취소합니다.', + }), + ApiResponse({ + status: 200, + description: '성공적으로 친구 요청을 취소한 경우', + content: { + JSON: { example: { message: '친구 요청을 취소했습니다.' } }, + }, + }), + ApiResponse({ + status: 401, + description: '우리 서비스의 액세스 토큰이 아닌 경우', + content: { + JSON: { + example: { statusCode: 401, message: '유효하지 않은 토큰입니다.' }, + }, + }, + }), + ApiResponse({ + status: 403, + description: '만료된 액세스 토큰인 경우', + content: { + JSON: { + example: { statusCode: 403, message: '만료된 토큰입니다.' }, + }, + }, + }), + ApiResponse({ + status: 404, + description: '친구 요청을 찾을 수 없는 경우', + content: { + JSON: { + example: { + statusCode: 404, + message: '친구 요청을 찾을 수 없습니다.', + }, + }, + }, + }), + ApiResponse({ + status: 411, + description: '액세스 토큰이 제공되지 않은 경우', + content: { + JSON: { + example: { statusCode: 411, message: '토큰이 제공되지 않았습니다.' }, + }, + }, + }), + ApiResponse({ + status: 500, + description: '친구 요청 취소에 실패한 경우', + content: { + JSON: { + example: { + statusCode: 500, + message: '친구 요청 취소에 실패했습니다.', + }, + }, + }, + }), + ApiHeaders([ + { + name: 'access_token', + description: '액세스 토큰', + required: true, + example: '여기에 액세스 토큰', + }, + ]), + ); +} From 231b82a7f6bd98f3e68a24be0c5abe0d65b9bd75 Mon Sep 17 00:00:00 2001 From: NicoDora Date: Thu, 16 Nov 2023 14:20:57 +0900 Subject: [PATCH 52/69] =?UTF-8?q?modify(#40):=20=EC=B9=9C=EA=B5=AC?= =?UTF-8?q?=EC=B0=A8=EB=8B=A8=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 2 +- src/friends/controllers/friends.controller.ts | 10 +++ .../repositories/friends.repository.ts | 24 +++++++ src/friends/services/friends.service.ts | 23 ++++++ .../friend-block.decorator.ts | 71 +++++++++++++++++++ 5 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 src/friends/swagger-decorators/friend-block.decorator.ts diff --git a/src/app.module.ts b/src/app.module.ts index b3b3b5d..1e59f1e 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -25,7 +25,7 @@ import { SearchModule } from './search/search.module'; UserModule, TypeOrmModule.forRoot({ ...TypeORMconfig, // TypeORM 설정 객체 확장 - synchronize: false, // DB 동기화 여부 설정 + synchronize: true, // DB 동기화 여부 설정 }), // TypeOrmModule.forFeature([Image]), ConfigModule.forRoot({ diff --git a/src/friends/controllers/friends.controller.ts b/src/friends/controllers/friends.controller.ts index 0a27e22..296d7b2 100644 --- a/src/friends/controllers/friends.controller.ts +++ b/src/friends/controllers/friends.controller.ts @@ -22,6 +22,7 @@ import { ApiDeleteRejectPermanentCancel } from '../swagger-decorators/delete-rej import { JwtAccessTokenGuard } from 'src/config/guards/jwt-access-token.guard'; import { GetUserId } from 'src/common/decorators/get-userId.decorator'; import { ApiDeleteRequest } from '../swagger-decorators/delete-request.decorator'; +import { ApiFriendBlock } from '../swagger-decorators/friend-block.decorator'; @UseGuards(JwtAccessTokenGuard) @Controller('friends') @@ -80,6 +81,15 @@ export class FriendsController { return await this.friendsService.friendResponseReject(userId, friendId); } + @ApiFriendBlock() + @Patch('block/:friend_id') + async friendBlock( + @GetUserId() userId: number, + @Param('friend_id') friendId: number, + ) { + return await this.friendsService.friendBlock(userId, friendId); + } + @ApiFriendResponseRejectPermanent() @Patch('responses/reject/permanent/:friend_id') async friendResponseRejectPermanent( diff --git a/src/friends/repositories/friends.repository.ts b/src/friends/repositories/friends.repository.ts index 63d62f1..5f4e494 100644 --- a/src/friends/repositories/friends.repository.ts +++ b/src/friends/repositories/friends.repository.ts @@ -105,6 +105,30 @@ export class FriendsRepository { return await this.entityManager.save(friend); } + async friendBlock(userId: number, friendId: number): Promise { + const friend = await this.entityManager.findOne(Friend, { + where: [ + { + requesterId: userId, + respondentId: friendId, + status: Status.ACCEPT, + }, + { + requesterId: friendId, + respondentId: userId, + status: Status.ACCEPT, + }, + ], + }); + + if (!friend) { + return null; + } + + friend.status = Status.BLOCK; + return await this.entityManager.save(friend); + } + async friendRequestCancel( userId: number, friendId: number, diff --git a/src/friends/services/friends.service.ts b/src/friends/services/friends.service.ts index dfee4ec..4b020cb 100644 --- a/src/friends/services/friends.service.ts +++ b/src/friends/services/friends.service.ts @@ -126,6 +126,29 @@ export class FriendsService { } } + async friendBlock(userId: number, friendId: number) { + try { + const block = await this.friendsRepository.friendBlock(userId, friendId); + if (!block) { + throw new HttpException( + '친구를 찾을 수 없습니다.', + HttpStatus.NOT_FOUND, + ); + } + return { message: '친구를 차단했습니다.' }; + } catch (error) { + if (error.getStatus() === HttpStatus.NOT_FOUND) { + throw error; + } else { + console.log(error); + throw new HttpException( + '친구 차단에 실패했습니다.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + } + async friendRequestCancel(userId: number, friendId: number) { try { const cancel = await this.friendsRepository.friendRequestCancel( diff --git a/src/friends/swagger-decorators/friend-block.decorator.ts b/src/friends/swagger-decorators/friend-block.decorator.ts new file mode 100644 index 0000000..904a44c --- /dev/null +++ b/src/friends/swagger-decorators/friend-block.decorator.ts @@ -0,0 +1,71 @@ +import { applyDecorators } from '@nestjs/common'; +import { ApiHeaders, ApiOperation, ApiResponse } from '@nestjs/swagger'; + +export function ApiFriendBlock() { + return applyDecorators( + ApiOperation({ + summary: '친구 차단 API', + description: '친구를 차단합니다.', + }), + ApiResponse({ + status: 200, + description: '성공적으로 친구를 차단한 경우', + content: { + JSON: { example: { message: '친구를 차단했습니다.' } }, + }, + }), + ApiResponse({ + status: 401, + description: '우리 서비스의 액세스 토큰이 아닌 경우', + content: { + JSON: { + example: { statusCode: 401, message: '유효하지 않은 토큰입니다.' }, + }, + }, + }), + ApiResponse({ + status: 403, + description: '만료된 액세스 토큰인 경우', + content: { + JSON: { + example: { statusCode: 403, message: '만료된 토큰입니다.' }, + }, + }, + }), + ApiResponse({ + status: 404, + description: '친구를 찾을 수 없는 경우', + content: { + JSON: { + example: { statusCode: 404, message: '친구를 찾을 수 없습니다.' }, + }, + }, + }), + ApiResponse({ + status: 411, + description: '액세스 토큰이 제공되지 않은 경우', + content: { + JSON: { + example: { statusCode: 411, message: '토큰이 제공되지 않았습니다.' }, + }, + }, + }), + ApiResponse({ + status: 500, + description: '친구 차단에 실패한 경우', + content: { + JSON: { + example: { statusCode: 500, message: '친구 차단에 실패했습니다.' }, + }, + }, + }), + ApiHeaders([ + { + name: 'access_token', + description: '액세스 토큰', + required: true, + example: '여기에 액세스 토큰', + }, + ]), + ); +} From 5c3ee594dd98b4e444a0272dcc755304f872aa02 Mon Sep 17 00:00:00 2001 From: NicoDora Date: Thu, 16 Nov 2023 14:26:07 +0900 Subject: [PATCH 53/69] =?UTF-8?q?modify(#40):=20=EC=B9=9C=EA=B5=AC?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=EC=8B=9C=20=EC=B0=A8=EB=8B=A8=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repositories/friends.repository.ts | 19 ++++++++++++ src/friends/services/friends.service.ts | 30 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/friends/repositories/friends.repository.ts b/src/friends/repositories/friends.repository.ts index 5f4e494..15f1340 100644 --- a/src/friends/repositories/friends.repository.ts +++ b/src/friends/repositories/friends.repository.ts @@ -244,6 +244,25 @@ export class FriendsRepository { return check; } + async checkBlock(userId: number, friendId: number): Promise { + const check = await this.entityManager.findOne(Friend, { + where: [ + { + requesterId: userId, + respondentId: friendId, + status: Status.BLOCK, + }, + { + requesterId: friendId, + respondentId: userId, + status: Status.BLOCK, + }, + ], + }); + + return check; + } + async cleanupRejectedFriends() { const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000); diff --git a/src/friends/services/friends.service.ts b/src/friends/services/friends.service.ts index 4b020cb..1420d09 100644 --- a/src/friends/services/friends.service.ts +++ b/src/friends/services/friends.service.ts @@ -36,6 +36,17 @@ export class FriendsService { ); } + const checkBlock = await this.friendsRepository.checkBlock( + userId, + friendId, + ); + if (checkBlock) { + throw new HttpException( + '상대방이 친구를 차단했습니다.', + HttpStatus.GONE, + ); + } + const checkRejectTime = await this.friendsRepository.checkRejectTime( userId, friendId, @@ -297,6 +308,25 @@ export class FriendsService { } } + async checkBlock(userId: number, friendId: number) { + try { + const checkBlock = await this.friendsRepository.checkBlock( + userId, + friendId, + ); + if (!checkBlock) { + return false; + } + return true; + } catch (error) { + console.log(error); + throw new HttpException( + '차단 체크에 실패했습니다.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) async cleanupRejectedFriends() { try { From 036ae1a6ebbe8f24da9cc2316cc5daecd156762c Mon Sep 17 00:00:00 2001 From: NicoDora Date: Thu, 16 Nov 2023 14:48:06 +0900 Subject: [PATCH 54/69] =?UTF-8?q?modify(#40):=20=EC=B9=9C=EA=B5=AC=20?= =?UTF-8?q?=EC=B0=A8=EB=8B=A8=20=EC=B7=A8=EC=86=8C=20API=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/friends/controllers/friends.controller.ts | 10 +++ .../repositories/friends.repository.ts | 26 +++++++ src/friends/services/friends.service.ts | 26 +++++++ .../delete-block.decorator.ts | 77 +++++++++++++++++++ 4 files changed, 139 insertions(+) create mode 100644 src/friends/swagger-decorators/delete-block.decorator.ts diff --git a/src/friends/controllers/friends.controller.ts b/src/friends/controllers/friends.controller.ts index 296d7b2..112eeed 100644 --- a/src/friends/controllers/friends.controller.ts +++ b/src/friends/controllers/friends.controller.ts @@ -23,6 +23,7 @@ import { JwtAccessTokenGuard } from 'src/config/guards/jwt-access-token.guard'; import { GetUserId } from 'src/common/decorators/get-userId.decorator'; import { ApiDeleteRequest } from '../swagger-decorators/delete-request.decorator'; import { ApiFriendBlock } from '../swagger-decorators/friend-block.decorator'; +import { ApiDeleteBlock } from '../swagger-decorators/delete-block.decorator'; @UseGuards(JwtAccessTokenGuard) @Controller('friends') @@ -111,6 +112,15 @@ export class FriendsController { return await this.friendsService.friendRequestCancel(userId, friendId); } + @ApiDeleteBlock() + @Delete('block/cancel/:friend_id') + async friendBlockCancel( + @GetUserId() userId: number, + @Param('friend_id') friendId: number, + ) { + return await this.friendsService.friendBlockCancel(userId, friendId); + } + @ApiDeleteRejectPermanentCancel() @Delete('responses/reject/permanent/:friend_id') async friendResponseRejectPermanentCancel( diff --git a/src/friends/repositories/friends.repository.ts b/src/friends/repositories/friends.repository.ts index 15f1340..17f0bcb 100644 --- a/src/friends/repositories/friends.repository.ts +++ b/src/friends/repositories/friends.repository.ts @@ -187,6 +187,32 @@ export class FriendsRepository { return await this.entityManager.delete(Friend, friend); } + async friendBlockCancel( + userId: number, + friendId: number, + ): Promise { + const friend = await this.entityManager.findOne(Friend, { + where: [ + { + requesterId: userId, + respondentId: friendId, + status: Status.BLOCK, + }, + { + requesterId: friendId, + respondentId: userId, + status: Status.BLOCK, + }, + ], + }); + + if (!friend) { + return null; + } + + return await this.entityManager.delete(Friend, friend); + } + async friendResponseRejectPermanent( userId: number, friendId: number, diff --git a/src/friends/services/friends.service.ts b/src/friends/services/friends.service.ts index 1420d09..ab1b4b1 100644 --- a/src/friends/services/friends.service.ts +++ b/src/friends/services/friends.service.ts @@ -238,6 +238,32 @@ export class FriendsService { } } + async friendBlockCancel(userId: number, friendId: number) { + try { + const blockCancel = await this.friendsRepository.friendBlockCancel( + userId, + friendId, + ); + if (!blockCancel) { + throw new HttpException( + '친구 차단을 찾을 수 없습니다.', + HttpStatus.NOT_FOUND, + ); + } + return { message: '친구 차단을 취소했습니다.' }; + } catch (error) { + if (error.getStatus() === HttpStatus.NOT_FOUND) { + throw error; + } else { + console.log(error); + throw new HttpException( + '친구 차단 취소에 실패했습니다.', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + } + async friendResponseRejectPermanent(userId: number, friendId: number) { try { const rejectPermanent = diff --git a/src/friends/swagger-decorators/delete-block.decorator.ts b/src/friends/swagger-decorators/delete-block.decorator.ts new file mode 100644 index 0000000..189e395 --- /dev/null +++ b/src/friends/swagger-decorators/delete-block.decorator.ts @@ -0,0 +1,77 @@ +import { applyDecorators } from '@nestjs/common'; +import { ApiHeaders, ApiOperation, ApiResponse } from '@nestjs/swagger'; + +export function ApiDeleteBlock() { + return applyDecorators( + ApiOperation({ + summary: '친구 차단 취소 API', + description: '친구 차단을 취소 합니다.', + }), + ApiResponse({ + status: 200, + description: '성공적으로 친구 차단을 취소한 경우', + content: { + JSON: { example: { message: '친구 차단을 취소했습니다.' } }, + }, + }), + ApiResponse({ + status: 401, + description: '우리 서비스의 액세스 토큰이 아닌 경우', + content: { + JSON: { + example: { statusCode: 401, message: '유효하지 않은 토큰입니다.' }, + }, + }, + }), + ApiResponse({ + status: 403, + description: '만료된 액세스 토큰인 경우', + content: { + JSON: { + example: { statusCode: 403, message: '만료된 토큰입니다.' }, + }, + }, + }), + ApiResponse({ + status: 404, + description: '친구 차단을 찾을 수 없는 경우', + content: { + JSON: { + example: { + statusCode: 404, + message: '친구 차단을 찾을 수 없습니다.', + }, + }, + }, + }), + ApiResponse({ + status: 411, + description: '액세스 토큰이 제공되지 않은 경우', + content: { + JSON: { + example: { statusCode: 411, message: '토큰이 제공되지 않았습니다.' }, + }, + }, + }), + ApiResponse({ + status: 500, + description: '친구 차단 취소에 실패한 경우', + content: { + JSON: { + example: { + statusCode: 500, + message: '친구 차단 취소에 실패했습니다.', + }, + }, + }, + }), + ApiHeaders([ + { + name: 'access_token', + description: '액세스 토큰', + required: true, + example: '여기에 액세스 토큰', + }, + ]), + ); +} From 4b94f489dad5a69079e467f06e2d3d7b58922c7b Mon Sep 17 00:00:00 2001 From: NicoDora Date: Thu, 16 Nov 2023 15:06:15 +0900 Subject: [PATCH 55/69] Update app.module.ts --- src/app.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app.module.ts b/src/app.module.ts index 1e59f1e..5bbda3d 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -25,7 +25,7 @@ import { SearchModule } from './search/search.module'; UserModule, TypeOrmModule.forRoot({ ...TypeORMconfig, // TypeORM 설정 객체 확장 - synchronize: true, // DB 동기화 여부 설정 + synchronize: false, // DB 동기화 여부 설정 }), // TypeOrmModule.forFeature([Image]), ConfigModule.forRoot({ From dd88038ab5fb4c27338408bdb9c6778a60e1ec58 Mon Sep 17 00:00:00 2001 From: NicoDora Date: Thu, 16 Nov 2023 15:07:08 +0900 Subject: [PATCH 56/69] Update app.module.ts --- src/app.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app.module.ts b/src/app.module.ts index 5bbda3d..b3b3b5d 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -25,7 +25,7 @@ import { SearchModule } from './search/search.module'; UserModule, TypeOrmModule.forRoot({ ...TypeORMconfig, // TypeORM 설정 객체 확장 - synchronize: false, // DB 동기화 여부 설정 + synchronize: false, // DB 동기화 여부 설정 }), // TypeOrmModule.forFeature([Image]), ConfigModule.forRoot({ From 9ff98191071a311fe5447b43dda1a4e2e93720c9 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Thu, 16 Nov 2023 15:16:59 +0900 Subject: [PATCH 57/69] =?UTF-8?q?modify(#60):=20guard=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/controllers/chat.controller.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/chat/controllers/chat.controller.ts b/src/chat/controllers/chat.controller.ts index 0b3d2b6..e6c7551 100644 --- a/src/chat/controllers/chat.controller.ts +++ b/src/chat/controllers/chat.controller.ts @@ -33,7 +33,6 @@ import { ApiGetChatNotifications } from '../swagger-decorators/get-chat-notifica import { ApiCreateChatImage } from '../swagger-decorators/create-chat-image.decorators'; @ApiTags('CHAT') -@UseGuards(JwtAccessTokenGuard) @UsePipes(ValidationPipe) @Controller('chat-room') export class ChatController { @@ -48,12 +47,14 @@ export class ChatController { return this.chatService.notificationListener(); } + @UseGuards(JwtAccessTokenGuard) @ApiGetChatRooms() @Get() async getChatRooms(@GetUserId() userId: number) { return this.chatService.getChatRooms(userId); } + @UseGuards(JwtAccessTokenGuard) @ApiGetOneChatRoom() @Get(':roomId') async getOneChatRoom( @@ -63,6 +64,7 @@ export class ChatController { return this.chatService.getOneChatRoom(userId, roomId); } + @UseGuards(JwtAccessTokenGuard) @ApiCreateChatRoom() @Post() async createChatRoom( @@ -72,6 +74,7 @@ export class ChatController { return this.chatService.createChatRoom(userId, body.receiverId); } + @UseGuards(JwtAccessTokenGuard) @ApiDeleteChatRoom() @Delete(':roomId') async deleteChatRoom( @@ -81,6 +84,7 @@ export class ChatController { return this.chatService.deleteChatRoom(userId, roomId); } + @UseGuards(JwtAccessTokenGuard) @ApiGetChats() @Get(':roomId/chat') async getChats( @@ -90,6 +94,7 @@ export class ChatController { return this.chatService.getChats(userId, roomId); } + @UseGuards(JwtAccessTokenGuard) @ApiCreateChatImage() @Post(':roomId/chat/image') @UseInterceptors(FileInterceptor('file')) @@ -107,6 +112,7 @@ export class ChatController { ); } + @UseGuards(JwtAccessTokenGuard) @ApiGetChatNotifications() @Get('chat/notice') async getChatNotifications( From bd40269929cf77c879f227b456299837b6112709 Mon Sep 17 00:00:00 2001 From: NicoDora Date: Thu, 16 Nov 2023 15:19:10 +0900 Subject: [PATCH 58/69] =?UTF-8?q?modify(#40):=20=EC=B9=9C=EA=B5=AC=20?= =?UTF-8?q?=EC=B0=A8=EB=8B=A8=20=EB=AA=A9=EB=A1=9D=20get=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/friends/controllers/friends.controller.ts | 7 ++ .../repositories/friends.repository.ts | 16 ++++ src/friends/services/friends.service.ts | 4 + .../get-friends-block.decorator.ts | 78 +++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 src/friends/swagger-decorators/get-friends-block.decorator.ts diff --git a/src/friends/controllers/friends.controller.ts b/src/friends/controllers/friends.controller.ts index 112eeed..eb4e515 100644 --- a/src/friends/controllers/friends.controller.ts +++ b/src/friends/controllers/friends.controller.ts @@ -24,6 +24,7 @@ import { GetUserId } from 'src/common/decorators/get-userId.decorator'; import { ApiDeleteRequest } from '../swagger-decorators/delete-request.decorator'; import { ApiFriendBlock } from '../swagger-decorators/friend-block.decorator'; import { ApiDeleteBlock } from '../swagger-decorators/delete-block.decorator'; +import { ApiGetFriendsBlock } from '../swagger-decorators/get-friends-block.decorator'; @UseGuards(JwtAccessTokenGuard) @Controller('friends') @@ -49,6 +50,12 @@ export class FriendsController { return await this.friendsService.getFriends(userId); } + @ApiGetFriendsBlock() + @Get('block') + async getBlock(@GetUserId() userId: number) { + return await this.friendsService.getBlock(userId); + } + @ApiGetRejectPermanent() @Get('responses/reject/permanent') async getRejectPermanent(@GetUserId() userId: number) { diff --git a/src/friends/repositories/friends.repository.ts b/src/friends/repositories/friends.repository.ts index 17f0bcb..7a28044 100644 --- a/src/friends/repositories/friends.repository.ts +++ b/src/friends/repositories/friends.repository.ts @@ -49,6 +49,22 @@ export class FriendsRepository { .getMany(); } + async getBlock(userId: number): Promise { + return this.entityManager + .createQueryBuilder(Friend, 'friend') + .where('friend.requesterId = :userId', { userId }) + .andWhere('friend.status = :status', { status: Status.BLOCK }) + .orWhere('friend.respondentId = :userId', { userId }) + .andWhere('friend.status = :status', { status: Status.BLOCK }) + .leftJoin('friend.requester', 'user') + .leftJoin('friend.respondent', 'user2') + .leftJoin('user.userImage', 'userImage') + .leftJoin('user2.userImage', 'userImage2') + .addSelect(['user.name', 'userImage.imageUrl']) + .addSelect(['user2.name', 'userImage2.imageUrl']) + .getMany(); + } + async getRejectPermanent(userId: number): Promise { return this.entityManager .createQueryBuilder(Friend, 'friend') diff --git a/src/friends/services/friends.service.ts b/src/friends/services/friends.service.ts index ab1b4b1..13bd07a 100644 --- a/src/friends/services/friends.service.ts +++ b/src/friends/services/friends.service.ts @@ -19,6 +19,10 @@ export class FriendsService { return await this.friendsRepository.getFriends(userId); } + async getBlock(userId: number) { + return await this.friendsRepository.getBlock(userId); + } + async getRejectPermanent(userId: number) { return await this.friendsRepository.getRejectPermanent(userId); } diff --git a/src/friends/swagger-decorators/get-friends-block.decorator.ts b/src/friends/swagger-decorators/get-friends-block.decorator.ts new file mode 100644 index 0000000..b47e44d --- /dev/null +++ b/src/friends/swagger-decorators/get-friends-block.decorator.ts @@ -0,0 +1,78 @@ +import { applyDecorators } from '@nestjs/common'; +import { ApiHeaders, ApiOperation, ApiResponse } from '@nestjs/swagger'; + +export function ApiGetFriendsBlock() { + return applyDecorators( + ApiOperation({ + summary: '내가 차단한 친구 목록 조회 API', + description: '내가 차단한 친구 목록을 조회합니다.', + }), + ApiResponse({ + status: 200, + description: + '성공적으로 친구 목록을 조회한 경우 (배열 형태) , 내가 차단한 친구가 없는 경우 빈 배열을 반환합니다.', + content: { + Array: { + example: [ + { + id: 53, + requesterId: 62, + respondentId: 1, + status: '차단', + createdAt: '2023-11-15T21:13:13.000Z', + requester: { + name: '박준혁', + userImage: { + imageUrl: + 'http://k.kakaocdn.net/dn/bgfjbT/btrNZpdv3sK/AMb1oWdaF6WxMEXkuKRkR0/img_640x640.jpg', + }, + }, + respondent: { + name: 'test', + userImage: { + imageUrl: + 'http://k.kakaocdn.net/dn/dpk9l1/btqmGhA2lKL/Oz0wDuJn1YV2DIn92f6DVK/img_640x640.jpg', + }, + }, + }, + ], + }, + }, + }), + ApiResponse({ + status: 401, + description: '우리 서비스의 액세스 토큰이 아닌 경우', + content: { + JSON: { + example: { statusCode: 401, message: '유효하지 않은 토큰입니다.' }, + }, + }, + }), + ApiResponse({ + status: 403, + description: '만료된 액세스 토큰인 경우', + content: { + JSON: { + example: { statusCode: 403, message: '만료된 토큰입니다.' }, + }, + }, + }), + ApiResponse({ + status: 411, + description: '액세스 토큰이 제공되지 않은 경우', + content: { + JSON: { + example: { statusCode: 411, message: '토큰이 제공되지 않았습니다.' }, + }, + }, + }), + ApiHeaders([ + { + name: 'access_token', + description: '액세스 토큰', + required: true, + example: '여기에 액세스 토큰', + }, + ]), + ); +} From 1fb7982cd0cdbdc8dabac7bcaf606103318fd34e Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Thu, 16 Nov 2023 15:25:37 +0900 Subject: [PATCH 59/69] =?UTF-8?q?modify(#60):=20rxjs=20=EB=AC=B8=EB=B2=95?= =?UTF-8?q?=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/services/chat.service.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/chat/services/chat.service.ts b/src/chat/services/chat.service.ts index c538c2e..4c0c8d1 100644 --- a/src/chat/services/chat.service.ts +++ b/src/chat/services/chat.service.ts @@ -32,16 +32,12 @@ export class ChatService { ) {} notificationListener() { - return ( - this.subject - .asObservable() - .pipe( - map((notification: Notification) => JSON.stringify(notification)), - ), + return this.subject.asObservable().pipe( + map((notification: Notification) => JSON.stringify(notification)), catchError((err) => { this.logger.error('notificationListener : ' + err.message); throw new HttpException(err.message, HttpStatus.INTERNAL_SERVER_ERROR); - }) + }), ); } async getChatRooms(myId: number) { From a7d9331a7d6871bcf46b2bf3fb694ac2c22294a5 Mon Sep 17 00:00:00 2001 From: 2swo Date: Mon, 20 Nov 2023 12:40:16 +0900 Subject: [PATCH 60/69] =?UTF-8?q?refactor(#117)=20BoardGuard=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/boards/controllers/Boards.controller.ts | 9 ++++--- src/boards/services/Boards.service.ts | 3 ++- .../decorators/board-owner.decorator.ts | 9 +++++++ src/common/decorators/get-userId.decorator.ts | 2 +- src/config/guards/board-owner.guard.ts | 24 +++++++++++++++++++ 5 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 src/common/decorators/board-owner.decorator.ts create mode 100644 src/config/guards/board-owner.guard.ts diff --git a/src/boards/controllers/Boards.controller.ts b/src/boards/controllers/Boards.controller.ts index 56ee070..0e02247 100644 --- a/src/boards/controllers/Boards.controller.ts +++ b/src/boards/controllers/Boards.controller.ts @@ -28,6 +28,8 @@ import { ApiDeleteBoard } from '../swagger-decorators/delete-board-decorators'; import { ApiUpdateBoardImage } from '../swagger-decorators/patch-board-images-decorators'; import { JwtAccessTokenGuard } from 'src/config/guards/jwt-access-token.guard'; import { GetUserId } from 'src/common/decorators/get-userId.decorator'; +import { BoardOwnerGuard } from 'src/config/guards/board-Owner.guard'; +import { BoardOwner } from 'src/common/decorators/board-owner.decorator'; @Controller('boards') @ApiTags('board API') @@ -74,14 +76,15 @@ export class BoardsController { } @Get('/unit') - @UseGuards(JwtAccessTokenGuard) + @UseGuards(BoardOwnerGuard) @ApiGetOneBoard() async findOne( @Query('boardId') boardId: number, + @BoardOwner() unitOnwer: boolean, @GetUserId() userId: number, ): Promise { ``; - return await this.boardsService.findOneBoard(boardId, userId); + return await this.boardsService.findOneBoard(boardId, userId, unitOnwer); } @Patch('') @@ -94,7 +97,7 @@ export class BoardsController { } @Patch('/images') - @UseGuards(JwtAccessTokenGuard) + @UseGuards() @ApiUpdateBoardImage() @UseInterceptors(FilesInterceptor('files', 3)) async editBoardImages( diff --git a/src/boards/services/Boards.service.ts b/src/boards/services/Boards.service.ts index 365752f..c8edcef 100644 --- a/src/boards/services/Boards.service.ts +++ b/src/boards/services/Boards.service.ts @@ -62,9 +62,10 @@ export class BoardsService { async findOneBoard( boardId: number, userId: number, + unitOnwer: boolean, ): Promise { const board = await this.boardRepository.findBoardById(boardId); - const unitowner = board.userId === userId; + const unitowner = unitOnwer; if (!board) { throw new Error('게시물을 찾을 수 없습니다.'); } diff --git a/src/common/decorators/board-owner.decorator.ts b/src/common/decorators/board-owner.decorator.ts new file mode 100644 index 0000000..48e23d2 --- /dev/null +++ b/src/common/decorators/board-owner.decorator.ts @@ -0,0 +1,9 @@ +import { ExecutionContext, createParamDecorator } from '@nestjs/common'; + +export const BoardOwner = createParamDecorator( + (data, ctx: ExecutionContext): number => { + const req = ctx.switchToHttp().getRequest(); + + return req.unitowner; + }, +); diff --git a/src/common/decorators/get-userId.decorator.ts b/src/common/decorators/get-userId.decorator.ts index c8aec4a..59ca9ad 100644 --- a/src/common/decorators/get-userId.decorator.ts +++ b/src/common/decorators/get-userId.decorator.ts @@ -6,4 +6,4 @@ export const GetUserId = createParamDecorator( return req.user.userId; }, -); \ No newline at end of file +); diff --git a/src/config/guards/board-owner.guard.ts b/src/config/guards/board-owner.guard.ts new file mode 100644 index 0000000..aac9808 --- /dev/null +++ b/src/config/guards/board-owner.guard.ts @@ -0,0 +1,24 @@ +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { TokenService } from 'src/auth/services/token.service'; +import { BoardRepository } from 'src/boards/repository/boards.repository'; + +@Injectable() +export class BoardOwnerGuard { + constructor( + private tokenService: TokenService, + private boardRepository: BoardRepository, + ) {} + + async canActivate(context: ExecutionContext) { + const request = context.switchToHttp().getRequest(); + const accessToken = request.headers['access_token']; + const boardId = request.query['boardId']; + const userId = await this.tokenService.decodeToken(accessToken); + const board = await this.boardRepository.findBoardById(boardId); + const unitowner = board.userId === userId; + + request.unitowner = unitowner; + request.user = { userId }; + return true; + } +} From 4114542135771bfac5661052c8ab0ac3a994570e Mon Sep 17 00:00:00 2001 From: 2swo Date: Mon, 20 Nov 2023 12:55:15 +0900 Subject: [PATCH 61/69] =?UTF-8?q?refactor(#117)=20BoardGuard=EC=88=98?= =?UTF-8?q?=EC=A0=95(=ED=86=A0=ED=81=B0=20=EC=97=86=EC=9D=84=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/boards/controllers/Boards.controller.ts | 2 +- src/config/guards/board-owner.guard.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/boards/controllers/Boards.controller.ts b/src/boards/controllers/Boards.controller.ts index 0e02247..cbc962f 100644 --- a/src/boards/controllers/Boards.controller.ts +++ b/src/boards/controllers/Boards.controller.ts @@ -28,7 +28,7 @@ import { ApiDeleteBoard } from '../swagger-decorators/delete-board-decorators'; import { ApiUpdateBoardImage } from '../swagger-decorators/patch-board-images-decorators'; import { JwtAccessTokenGuard } from 'src/config/guards/jwt-access-token.guard'; import { GetUserId } from 'src/common/decorators/get-userId.decorator'; -import { BoardOwnerGuard } from 'src/config/guards/board-Owner.guard'; +import { BoardOwnerGuard } from 'src/config/guards/board-owner.guard'; import { BoardOwner } from 'src/common/decorators/board-owner.decorator'; @Controller('boards') diff --git a/src/config/guards/board-owner.guard.ts b/src/config/guards/board-owner.guard.ts index aac9808..305de4c 100644 --- a/src/config/guards/board-owner.guard.ts +++ b/src/config/guards/board-owner.guard.ts @@ -13,6 +13,11 @@ export class BoardOwnerGuard { const request = context.switchToHttp().getRequest(); const accessToken = request.headers['access_token']; const boardId = request.query['boardId']; + if (!accessToken) { + request.unitowner = false; + request.user = false; + return true; + } const userId = await this.tokenService.decodeToken(accessToken); const board = await this.boardRepository.findBoardById(boardId); const unitowner = board.userId === userId; From 1d2ce84c066aa5064b0298cf918964784f80aea2 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Mon, 20 Nov 2023 13:01:30 +0900 Subject: [PATCH 62/69] =?UTF-8?q?feat(#117):=20=EB=B9=84=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=8B=9C=20=EC=A2=8B=EC=95=84=EC=9A=94=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20guard?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=B2=98=EB=A6=AC=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/boards-like.controller.ts | 23 +++++-------------- src/common/decorators/get-userId.decorator.ts | 2 +- src/config/guards/jwt-board-like.guard.ts | 23 +++++++++++++++++++ 3 files changed, 30 insertions(+), 18 deletions(-) create mode 100644 src/config/guards/jwt-board-like.guard.ts diff --git a/src/boards/controllers/boards-like.controller.ts b/src/boards/controllers/boards-like.controller.ts index 9a433ee..890c294 100644 --- a/src/boards/controllers/boards-like.controller.ts +++ b/src/boards/controllers/boards-like.controller.ts @@ -18,6 +18,7 @@ import { ApiDeleteBoardLike } from '../swagger-decorators/delete-board-like.deco import { TokenService } from 'src/auth/services/token.service'; import { GetUserId } from 'src/common/decorators/get-userId.decorator'; import { JwtAccessTokenGuard } from 'src/config/guards/jwt-access-token.guard'; +import { JwtBoardLikeGuard } from 'src/config/guards/jwt-board-like.guard'; @ApiTags('BOARDS-LIKE') @UsePipes(ValidationPipe) @@ -40,28 +41,16 @@ export class BoardsLikeController { @ApiGetBoardLikeCount() @Get('like') - @UseGuards(JwtAccessTokenGuard) + @UseGuards(JwtBoardLikeGuard) async getBoardsLike( @GetUserId() userId: number, @Query('boardId', ParseIntPipe) boardId: number, ) { - try { - return this.boardsLikeService.getBoardLikesAndIsLike(boardId, userId); - } catch (error) { - if ( - (error.status === 401 && - error.message === '유효하지 않은 토큰입니다.') || - (error.status === 403 && error.message === '만료된 토큰입니다.') || - (error.status === 404 && - error.message === '사용자를 찾을 수 없습니다.') || - (error.status === 411 && - error.message === '토큰이 제공되지 않았습니다.') - ) { - return this.boardsLikeService.getBoardLikes(boardId); - } - console.error(error); - throw error; + if (userId === undefined) { + return this.boardsLikeService.getBoardLikes(boardId); } + + return this.boardsLikeService.getBoardLikesAndIsLike(boardId, userId); } @ApiDeleteBoardLike() diff --git a/src/common/decorators/get-userId.decorator.ts b/src/common/decorators/get-userId.decorator.ts index c8aec4a..59ca9ad 100644 --- a/src/common/decorators/get-userId.decorator.ts +++ b/src/common/decorators/get-userId.decorator.ts @@ -6,4 +6,4 @@ export const GetUserId = createParamDecorator( return req.user.userId; }, -); \ No newline at end of file +); diff --git a/src/config/guards/jwt-board-like.guard.ts b/src/config/guards/jwt-board-like.guard.ts new file mode 100644 index 0000000..63a49b4 --- /dev/null +++ b/src/config/guards/jwt-board-like.guard.ts @@ -0,0 +1,23 @@ +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { TokenService } from 'src/auth/services/token.service'; + +@Injectable() +export class JwtBoardLikeGuard { + constructor(private tokenService: TokenService) {} + + async canActivate(context: ExecutionContext) { + const request = context.switchToHttp().getRequest(); + const accessToken = request.headers['access_token']; + if (!accessToken) { + const userId = undefined; + request.user = { userId }; + return true; + } + + const userId = await this.tokenService.decodeToken(accessToken); + + request.user = { userId }; + + return true; + } +} From 9e682f8e48e0ee6b0e98fdd463767a03c2dbd96f Mon Sep 17 00:00:00 2001 From: 2swo Date: Mon, 20 Nov 2023 13:07:43 +0900 Subject: [PATCH 63/69] =?UTF-8?q?refactor(#117)=20BoardGuard=EC=88=98?= =?UTF-8?q?=EC=A0=95(=ED=86=A0=ED=81=B0=20=EC=97=86=EC=9D=84=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/boards/controllers/Boards.controller.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/boards/controllers/Boards.controller.ts b/src/boards/controllers/Boards.controller.ts index cbc962f..09d026e 100644 --- a/src/boards/controllers/Boards.controller.ts +++ b/src/boards/controllers/Boards.controller.ts @@ -83,7 +83,6 @@ export class BoardsController { @BoardOwner() unitOnwer: boolean, @GetUserId() userId: number, ): Promise { - ``; return await this.boardsService.findOneBoard(boardId, userId, unitOnwer); } From 202fda8127b564e9be62d6e5a1730835997c3933 Mon Sep 17 00:00:00 2001 From: 2swo Date: Mon, 20 Nov 2023 13:08:44 +0900 Subject: [PATCH 64/69] =?UTF-8?q?refactor(#117)=20BoardGuard=EC=88=98?= =?UTF-8?q?=EC=A0=95(=ED=86=A0=ED=81=B0=20=EC=97=86=EC=9D=84=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/boards/controllers/Boards.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boards/controllers/Boards.controller.ts b/src/boards/controllers/Boards.controller.ts index 09d026e..06b4109 100644 --- a/src/boards/controllers/Boards.controller.ts +++ b/src/boards/controllers/Boards.controller.ts @@ -96,7 +96,7 @@ export class BoardsController { } @Patch('/images') - @UseGuards() + @UseGuards(JwtAccessTokenGuard) @ApiUpdateBoardImage() @UseInterceptors(FilesInterceptor('files', 3)) async editBoardImages( From 78522a9545e606c1f4acd71f0229a6b126edc2c7 Mon Sep 17 00:00:00 2001 From: 2swo Date: Mon, 20 Nov 2023 13:11:39 +0900 Subject: [PATCH 65/69] =?UTF-8?q?refactor(#117)=20BoardGuard=EC=88=98?= =?UTF-8?q?=EC=A0=95(=ED=86=A0=ED=81=B0=20=EC=97=86=EC=9D=84=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/boards/controllers/Boards.controller.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/boards/controllers/Boards.controller.ts b/src/boards/controllers/Boards.controller.ts index 06b4109..7ddccf8 100644 --- a/src/boards/controllers/Boards.controller.ts +++ b/src/boards/controllers/Boards.controller.ts @@ -17,7 +17,6 @@ import { BoardImagesService } from '../services/BoardImage.service'; import { FilesInterceptor } from '@nestjs/platform-express'; import { BoardResponseDTO } from '../dto/boards.response.dto'; import { CreateBoardImageDto } from '../dto/create.board-image.dto'; -import { TokenService } from 'src/auth/services/token.service'; import { ApiUploadBoardImages } from '../swagger-decorators/upload-baord-images-decorator'; import { ApiAddBoard } from '../swagger-decorators/add-board-decorators'; import { ApiGetPageBoards } from '../swagger-decorators/get-page-boards-decorators'; @@ -37,7 +36,6 @@ export class BoardsController { constructor( private readonly boardsService: BoardsService, private readonly boardImagesService: BoardImagesService, - private tokenService: TokenService, ) {} @Post('') From 7aa34250d07a72137b235a98aa6978a778f42908 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Mon, 20 Nov 2023 17:48:42 +0900 Subject: [PATCH 66/69] refactor(#117): change guard's name --- src/boards/controllers/boards-like.controller.ts | 4 ++-- .../guards/{jwt-board-like.guard.ts => jwt-optional.guard.ts} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/config/guards/{jwt-board-like.guard.ts => jwt-optional.guard.ts} (94%) diff --git a/src/boards/controllers/boards-like.controller.ts b/src/boards/controllers/boards-like.controller.ts index 890c294..7ad7c43 100644 --- a/src/boards/controllers/boards-like.controller.ts +++ b/src/boards/controllers/boards-like.controller.ts @@ -18,7 +18,7 @@ import { ApiDeleteBoardLike } from '../swagger-decorators/delete-board-like.deco import { TokenService } from 'src/auth/services/token.service'; import { GetUserId } from 'src/common/decorators/get-userId.decorator'; import { JwtAccessTokenGuard } from 'src/config/guards/jwt-access-token.guard'; -import { JwtBoardLikeGuard } from 'src/config/guards/jwt-board-like.guard'; +import { JwtOptionalGuard } from 'src/config/guards/jwt-optional.guard'; @ApiTags('BOARDS-LIKE') @UsePipes(ValidationPipe) @@ -41,7 +41,7 @@ export class BoardsLikeController { @ApiGetBoardLikeCount() @Get('like') - @UseGuards(JwtBoardLikeGuard) + @UseGuards(JwtOptionalGuard) async getBoardsLike( @GetUserId() userId: number, @Query('boardId', ParseIntPipe) boardId: number, diff --git a/src/config/guards/jwt-board-like.guard.ts b/src/config/guards/jwt-optional.guard.ts similarity index 94% rename from src/config/guards/jwt-board-like.guard.ts rename to src/config/guards/jwt-optional.guard.ts index 63a49b4..008bb25 100644 --- a/src/config/guards/jwt-board-like.guard.ts +++ b/src/config/guards/jwt-optional.guard.ts @@ -2,7 +2,7 @@ import { ExecutionContext, Injectable } from '@nestjs/common'; import { TokenService } from 'src/auth/services/token.service'; @Injectable() -export class JwtBoardLikeGuard { +export class JwtOptionalGuard { constructor(private tokenService: TokenService) {} async canActivate(context: ExecutionContext) { From f19b80c6b61fd9a41e6727eab0206d02c45ff257 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Tue, 21 Nov 2023 16:08:58 +0900 Subject: [PATCH 67/69] feat(#60): add interceptors for set success response type in chat-controller and socket-gateway --- src/chat/controllers/chat.controller.ts | 2 ++ src/chat/events/events.gateway.ts | 9 ++++++-- src/chat/services/chat.service.ts | 4 ++-- .../success-response.interceptor.ts | 21 ++++++++++++++++++ src/common/s3/s3.service.ts | 1 + .../web-socket-jwt-access-token.guard.ts | 22 +++++++++++++++++++ 6 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 src/common/interceptors/success-response.interceptor.ts create mode 100644 src/config/guards/web-socket-jwt-access-token.guard.ts diff --git a/src/chat/controllers/chat.controller.ts b/src/chat/controllers/chat.controller.ts index e6c7551..fef9acd 100644 --- a/src/chat/controllers/chat.controller.ts +++ b/src/chat/controllers/chat.controller.ts @@ -31,9 +31,11 @@ import { JwtAccessTokenGuard } from 'src/config/guards/jwt-access-token.guard'; import { GetNotificationsResponseDto } from '../dto/get-notifications-response.dto'; import { ApiGetChatNotifications } from '../swagger-decorators/get-chat-notifications.decorator'; import { ApiCreateChatImage } from '../swagger-decorators/create-chat-image.decorators'; +import { SuccessResponseInterceptor } from 'src/common/interceptors/success-response.interceptor'; @ApiTags('CHAT') @UsePipes(ValidationPipe) +@UseInterceptors(SuccessResponseInterceptor) @Controller('chat-room') export class ChatController { constructor( diff --git a/src/chat/events/events.gateway.ts b/src/chat/events/events.gateway.ts index 031b6bb..22de1fc 100644 --- a/src/chat/events/events.gateway.ts +++ b/src/chat/events/events.gateway.ts @@ -15,12 +15,14 @@ import { AsyncApiSub } from 'nestjs-asyncapi'; import { BadRequestException, UseFilters, + UseGuards, UsePipes, ValidationPipe, } from '@nestjs/common'; import { LoginChatRoomDto } from '../dto/login-chat-room.dto'; import { WebSocketExceptionFilter } from '../exceptions/websocket-exception.filter'; import mongoose from 'mongoose'; +import { WebSocketJwtAccessTokenGuard } from 'src/config/guards/web-socket-jwt-access-token.guard'; @WebSocketGateway({ namespace: /\/ch-.+/, cors: true }) @UsePipes(ValidationPipe) @@ -78,6 +80,7 @@ export class EventsGateway payload: PostChatDto, }, }) + @UseGuards(WebSocketJwtAccessTokenGuard) @SubscribeMessage('message') async handleMessage( @MessageBody() postChatDto: PostChatDto, @@ -85,10 +88,12 @@ export class EventsGateway ) { if (postChatDto.hasOwnProperty('content')) { const returnedChat = await this.chatService.createChat(postChatDto); - socket.to(postChatDto.roomId.toString()).emit('message', returnedChat); + const data = returnedChat; + socket.to(postChatDto.roomId.toString()).emit('message', { data }); } else { const returnedChat = await this.chatService.findChatImage(postChatDto); - socket.to(postChatDto.roomId.toString()).emit('message', returnedChat); + const data = returnedChat; + socket.to(postChatDto.roomId.toString()).emit('message', { data }); } } diff --git a/src/chat/services/chat.service.ts b/src/chat/services/chat.service.ts index 4c0c8d1..b07ca73 100644 --- a/src/chat/services/chat.service.ts +++ b/src/chat/services/chat.service.ts @@ -139,7 +139,7 @@ export class ChatService { receiverId, ); - const chat = { + const data = { content: returnedChat.content, sender: returnedChat.sender, receiver: returnedChat.receiver, @@ -147,7 +147,7 @@ export class ChatService { if (returnedChat) this.subject.next(returnedChat); - return chat; + return data; } async createChatImage( diff --git a/src/common/interceptors/success-response.interceptor.ts b/src/common/interceptors/success-response.interceptor.ts new file mode 100644 index 0000000..776bad4 --- /dev/null +++ b/src/common/interceptors/success-response.interceptor.ts @@ -0,0 +1,21 @@ +import { + Injectable, + NestInterceptor, + ExecutionContext, + CallHandler, +} from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +@Injectable() +export class SuccessResponseInterceptor implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler): Observable { + const statusCode = context.getArgByIndex(1).statusCode; + return next.handle().pipe( + map((data) => ({ + statusCode, + data, + })), + ); + } +} diff --git a/src/common/s3/s3.service.ts b/src/common/s3/s3.service.ts index 220992f..3a35a7f 100644 --- a/src/common/s3/s3.service.ts +++ b/src/common/s3/s3.service.ts @@ -44,6 +44,7 @@ export class S3Service { return { url: fileUrl, key: filename }; } catch (error) { + console.error(error); throw new Error('S3 업로드 오류'); } } diff --git a/src/config/guards/web-socket-jwt-access-token.guard.ts b/src/config/guards/web-socket-jwt-access-token.guard.ts new file mode 100644 index 0000000..88f49e2 --- /dev/null +++ b/src/config/guards/web-socket-jwt-access-token.guard.ts @@ -0,0 +1,22 @@ +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { TokenService } from 'src/auth/services/token.service'; + +@Injectable() +export class WebSocketJwtAccessTokenGuard { + constructor(private tokenService: TokenService) {} + + async canActivate(context: ExecutionContext) { + const request = context.switchToWs().getClient().handshake; + const accessToken = request.headers['access_token']; + + if (!accessToken) { + return false; + } + + const userId = await this.tokenService.decodeToken(accessToken); + request.user = { userId }; + console.log(request.user); + + return true; + } +} From 28ba6ddde53a760a2984eda05c2cae3a7cab6318 Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Tue, 21 Nov 2023 16:18:19 +0900 Subject: [PATCH 68/69] feat(#60): add ws-guard(not working) --- src/chat/events/events.gateway.ts | 4 -- .../web-socket-jwt-access-token.guard.ts | 41 ++++++++++++------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/chat/events/events.gateway.ts b/src/chat/events/events.gateway.ts index 22de1fc..271a33a 100644 --- a/src/chat/events/events.gateway.ts +++ b/src/chat/events/events.gateway.ts @@ -15,15 +15,12 @@ import { AsyncApiSub } from 'nestjs-asyncapi'; import { BadRequestException, UseFilters, - UseGuards, UsePipes, ValidationPipe, } from '@nestjs/common'; import { LoginChatRoomDto } from '../dto/login-chat-room.dto'; import { WebSocketExceptionFilter } from '../exceptions/websocket-exception.filter'; import mongoose from 'mongoose'; -import { WebSocketJwtAccessTokenGuard } from 'src/config/guards/web-socket-jwt-access-token.guard'; - @WebSocketGateway({ namespace: /\/ch-.+/, cors: true }) @UsePipes(ValidationPipe) @UseFilters(WebSocketExceptionFilter) @@ -80,7 +77,6 @@ export class EventsGateway payload: PostChatDto, }, }) - @UseGuards(WebSocketJwtAccessTokenGuard) @SubscribeMessage('message') async handleMessage( @MessageBody() postChatDto: PostChatDto, diff --git a/src/config/guards/web-socket-jwt-access-token.guard.ts b/src/config/guards/web-socket-jwt-access-token.guard.ts index 88f49e2..1ddd98f 100644 --- a/src/config/guards/web-socket-jwt-access-token.guard.ts +++ b/src/config/guards/web-socket-jwt-access-token.guard.ts @@ -1,22 +1,35 @@ -import { ExecutionContext, Injectable } from '@nestjs/common'; -import { TokenService } from 'src/auth/services/token.service'; +// import { ExecutionContext, Injectable } from '@nestjs/common'; +// import { TokenService } from 'src/auth/services/token.service'; -@Injectable() -export class WebSocketJwtAccessTokenGuard { - constructor(private tokenService: TokenService) {} +// @Injectable() +// export class WebSocketJwtAccessTokenGuard { +// constructor(private tokenService: TokenService) {} + +// async canActivate(context: ExecutionContext) { +// const request = context.switchToWs().getClient().handshake; +// const accessToken = request.headers['access_token']; - async canActivate(context: ExecutionContext) { - const request = context.switchToWs().getClient().handshake; - const accessToken = request.headers['access_token']; +// if (!accessToken) { +// return false; +// } - if (!accessToken) { - return false; - } +// const userId = await this.tokenService.decodeToken(accessToken); +// request.user = { userId }; +// console.log(request.user); - const userId = await this.tokenService.decodeToken(accessToken); - request.user = { userId }; - console.log(request.user); +// return true; +// } +// } +import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; +import { Observable } from 'rxjs'; + +@Injectable() +export class AuthGuard implements CanActivate { + canActivate( + context: ExecutionContext, + ): boolean | Promise | Observable { + const request = context.switchToHttp().getRequest(); return true; } } From 0b32ab32b82927a17a2f5e3416c69d9ccbf3086f Mon Sep 17 00:00:00 2001 From: hobiJeong Date: Tue, 21 Nov 2023 16:56:54 +0900 Subject: [PATCH 69/69] refactor(#60): change return variable's name --- src/chat/services/chat.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chat/services/chat.service.ts b/src/chat/services/chat.service.ts index b07ca73..4c0c8d1 100644 --- a/src/chat/services/chat.service.ts +++ b/src/chat/services/chat.service.ts @@ -139,7 +139,7 @@ export class ChatService { receiverId, ); - const data = { + const chat = { content: returnedChat.content, sender: returnedChat.sender, receiver: returnedChat.receiver, @@ -147,7 +147,7 @@ export class ChatService { if (returnedChat) this.subject.next(returnedChat); - return data; + return chat; } async createChatImage(