From afa36501c40c7969b7aa794e77ab5b2b42420b7e Mon Sep 17 00:00:00 2001 From: adkm12 Date: Fri, 29 Nov 2024 09:19:30 +0900 Subject: [PATCH] =?UTF-8?q?fix=20:=20=EB=A7=88=EC=9D=B8=EB=93=9C=EB=A7=B5?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20&&=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20&&=20=ED=9A=8C?= =?UTF-8?q?=EC=9D=98=EB=A1=9D,=20=EC=A0=9C=EB=AA=A9=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EA=B6=8C=ED=95=9C=20=EC=9E=84=EC=8B=9C=EB=A1=9C=20=ED=95=B4?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/modules/mindmap/mindmap.service.ts | 7 ++-- .../src/modules/node/node.service.ts | 3 +- .../src/guard/ws.jwt.auth.guard.ts | 4 ++- .../src/modules/map/map.gateway.ts | 36 ++++++++++++++++--- .../src/modules/map/map.service.ts | 10 ++++-- BE/libs/entity/src/user.mindmap.role.ts | 5 ++- 6 files changed, 53 insertions(+), 12 deletions(-) diff --git a/BE/apps/api-server/src/modules/mindmap/mindmap.service.ts b/BE/apps/api-server/src/modules/mindmap/mindmap.service.ts index e180df74..06b10520 100644 --- a/BE/apps/api-server/src/modules/mindmap/mindmap.service.ts +++ b/BE/apps/api-server/src/modules/mindmap/mindmap.service.ts @@ -60,9 +60,11 @@ export class MindmapService { const myRoles = await this.userMindmapRoleRepository.find({ where: { user: { id: userId } }, relations: ['mindmap'], + select: ['mindmap'], }); - if (!myRoles.length) { + if (!myRoles) { + this.logger.log('myRoles is empty'); return []; } @@ -83,11 +85,12 @@ export class MindmapService { } await this.mindmapRepository.softRemove({ id: mindmapId }); + await this.userMindmapRoleRepository.softDelete({ mindmap: { id: mindmapId } }); } async getDataByMindmapId(mindmapId: number) { const mindmap = await this.mindmapRepository.findOne({ where: { id: mindmapId } }); - const nodes = await this.nodeService.tableToCanvas(mindmapId); + const nodes = await this.nodeService.tableToCanvas(mindmap.id); return { title: mindmap.title, diff --git a/BE/apps/api-server/src/modules/node/node.service.ts b/BE/apps/api-server/src/modules/node/node.service.ts index 8c9978ca..46eb390e 100644 --- a/BE/apps/api-server/src/modules/node/node.service.ts +++ b/BE/apps/api-server/src/modules/node/node.service.ts @@ -24,7 +24,8 @@ export class NodeService { return {}; } - const nodeTree = await this.nodeRepository.findDescendants(rootNode); + const nodeTree = await this.nodeRepository.findDescendants(rootNode, { relations: ['children'] }); + const nodeTreeArray = nodeTree.map((node) => ({ id: node.id, keyword: node.keyword, diff --git a/BE/apps/socket-server/src/guard/ws.jwt.auth.guard.ts b/BE/apps/socket-server/src/guard/ws.jwt.auth.guard.ts index aa2eb466..d3e4e24e 100644 --- a/BE/apps/socket-server/src/guard/ws.jwt.auth.guard.ts +++ b/BE/apps/socket-server/src/guard/ws.jwt.auth.guard.ts @@ -1,16 +1,18 @@ import * as cookieParser from 'cookie-parser'; -import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; +import { CanActivate, ExecutionContext, Injectable, Logger } from '@nestjs/common'; import { JsonWebTokenError, JwtService, TokenExpiredError } from '@nestjs/jwt'; import { InvalidTokenException } from '../exceptions'; import { Socket } from 'socket.io'; @Injectable() export class WsOptionalJwtGuard implements CanActivate { + private readonly logger = new Logger(WsOptionalJwtGuard.name); constructor(private readonly jwtService: JwtService) {} canActivate(context: ExecutionContext): boolean { const client = context.switchToWs().getClient(); const token = client.handshake.auth.token; + this.logger.log('토큰 : ' + token); if (!token) { return true; diff --git a/BE/apps/socket-server/src/modules/map/map.gateway.ts b/BE/apps/socket-server/src/modules/map/map.gateway.ts index b6b67c27..7a1947d6 100644 --- a/BE/apps/socket-server/src/modules/map/map.gateway.ts +++ b/BE/apps/socket-server/src/modules/map/map.gateway.ts @@ -1,3 +1,4 @@ +import { JwtService } from '@nestjs/jwt'; import { ConnectedSocket, MessageBody, @@ -11,17 +12,15 @@ import { import { RedisService } from '@liaoliaots/nestjs-redis'; import { Redis } from 'ioredis'; import { MapService } from './map.service'; -import { UseFilters, Injectable, UseGuards, Logger } from '@nestjs/common'; +import { UseFilters, Injectable, Logger } from '@nestjs/common'; import { Socket, Server } from 'socket.io'; import { UpdateMindmapDto, CreateNodeDto, UpdateContentDto, UpdateTitleDto, AiRequestDto } from './dto'; import { MindmapValidationPipe, WsValidationPipe } from '../../pipes'; import { WsExceptionFilter } from '../../exceptionfilter/ws.exceptionFilter'; -import { WsOptionalJwtGuard } from '../../guard/ws.jwt.auth.guard'; @Injectable() @WebSocketGateway({ transports: ['websocket'] }) @UseFilters(new WsExceptionFilter()) -@UseGuards(WsOptionalJwtGuard) export class MapGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect { @WebSocketServer() server: Server; private readonly redis: Redis; @@ -30,6 +29,7 @@ export class MapGateway implements OnGatewayInit, OnGatewayConnection, OnGateway constructor( private readonly mapService: MapService, private readonly redisService: RedisService, + private readonly jwtService: JwtService, ) { this.redis = this.redisService.getOrThrow('general'); } @@ -39,7 +39,35 @@ export class MapGateway implements OnGatewayInit, OnGatewayConnection, OnGateway } async handleConnection(@ConnectedSocket() client: Socket) { - this.logger.log('사용자 연결 : ' + (client.data.user ? client.data.user.id : `guest ${client.id}`)); + const refreshToken = client.handshake.headers.cookie?.split('=')[1]; + + if (refreshToken) { + const accessToken = client.handshake.auth.token; + if (accessToken) { + try { + this.jwtService.verify(accessToken); + const payload = this.jwtService.decode(accessToken); + const user = { id: payload['id'], email: payload['email'] }; + client.data.user = user; + } catch { + try { + this.jwtService.verify(refreshToken); + const payload = this.jwtService.decode(refreshToken); + const user = { id: payload['id'], email: payload['email'] }; + const accessToken = this.jwtService.sign(user, { expiresIn: '30m' }); + client.emit('tokenRefresh', { accessToken }); + client.data.user = user; + } catch { + client.emit('tokenExpiredError', { message: '리프레시 토큰 만료' }); + } + } + } + } else { + client.data.user = null; + } + this.logger.log( + '사용자 연결 : ' + (client.data.user ? `유저 ID : ${client.data.user.id}` : `guest ID : ${client.id}`), + ); const currentData = await this.mapService.joinRoom(client); client.emit('joinRoom', currentData); } diff --git a/BE/apps/socket-server/src/modules/map/map.service.ts b/BE/apps/socket-server/src/modules/map/map.service.ts index b1b22a07..5e12b95d 100644 --- a/BE/apps/socket-server/src/modules/map/map.service.ts +++ b/BE/apps/socket-server/src/modules/map/map.service.ts @@ -75,7 +75,7 @@ export class MapService { async updateContent(client: Socket, content: string) { try { - this.checkAuth(client); + // this.checkAuth(client); await this.redis.set(`content:${client.data.connectionId}`, content); } catch (error) { if (error instanceof UnauthorizedException) throw error; @@ -85,7 +85,7 @@ export class MapService { async updateTitle(client: Socket, title: string) { try { - await this.checkAuth(client); + // await this.checkAuth(client); await this.redis.hset(client.data.connectionId, 'title', title); } catch (error) { if (error instanceof UnauthorizedException) throw error; @@ -95,7 +95,7 @@ export class MapService { async aiRequest(client: Socket, aiContent: string) { try { - await this.checkAuth(client); + // await this.checkAuth(client); const aiCount = await this.redis.hget(client.data.connectionId, 'aiCount'); if (Number(aiCount) === 0) { throw new AiRequestException('ai 요청 횟수 초과'); @@ -113,11 +113,14 @@ export class MapService { async checkAuth(client: Socket) { const type = await this.redis.hget(client.data.connectionId, 'type'); + this.logger.log('연결 type: ' + type + ' 마인드맵'); if (type === 'guest') { return; } const userId = client.data.user?.id; + this.logger.log('사용자 id: ' + userId); const ownerId = await this.redis.hget(client.data.connectionId, 'ownerId'); + this.logger.log('소유자 id: ' + ownerId); if (userId !== ownerId) { throw new UnauthorizedException(); @@ -137,6 +140,7 @@ export class MapService { client.join(connectionId); client.data.connectionId = connectionId; + this.logger.log(`클라이언트 데이터 : ${JSON.stringify(client.data)}`); const curruntData: Record = {}; const [currentState, currentContent, currentTitle, currentAiCount, ownerId, mindmapId] = await Promise.all([ diff --git a/BE/libs/entity/src/user.mindmap.role.ts b/BE/libs/entity/src/user.mindmap.role.ts index 26ddc8e5..f5e17f35 100644 --- a/BE/libs/entity/src/user.mindmap.role.ts +++ b/BE/libs/entity/src/user.mindmap.role.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; +import { Column, DeleteDateColumn, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; import { Role } from './enum/role.enum'; import { User } from './user.entity'; import { Mindmap } from './mindmap.entity'; @@ -18,4 +18,7 @@ export class UserMindmapRole { @Column({ type: 'enum', enum: Role, default: Role.OWNER }) role: Role; + + @DeleteDateColumn() + deleteAt: Date | null; }