Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions BE/apps/api-server/src/modules/mindmap/mindmap.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 [];
}

Expand All @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion BE/apps/api-server/src/modules/node/node.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 3 additions & 1 deletion BE/apps/socket-server/src/guard/ws.jwt.auth.guard.ts
Original file line number Diff line number Diff line change
@@ -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<Socket>();
const token = client.handshake.auth.token;
this.logger.log('토큰 : ' + token);

if (!token) {
return true;
Expand Down
36 changes: 32 additions & 4 deletions BE/apps/socket-server/src/modules/map/map.gateway.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { JwtService } from '@nestjs/jwt';
import {
ConnectedSocket,
MessageBody,
Expand All @@ -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;
Expand All @@ -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');
}
Expand All @@ -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);
}
Expand Down
10 changes: 7 additions & 3 deletions BE/apps/socket-server/src/modules/map/map.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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 요청 횟수 초과');
Expand All @@ -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();
Expand All @@ -137,6 +140,7 @@ export class MapService {

client.join(connectionId);
client.data.connectionId = connectionId;
this.logger.log(`클라이언트 데이터 : ${JSON.stringify(client.data)}`);
const curruntData: Record<string, any> = {};

const [currentState, currentContent, currentTitle, currentAiCount, ownerId, mindmapId] = await Promise.all([
Expand Down
5 changes: 4 additions & 1 deletion BE/libs/entity/src/user.mindmap.role.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -18,4 +18,7 @@ export class UserMindmapRole {

@Column({ type: 'enum', enum: Role, default: Role.OWNER })
role: Role;

@DeleteDateColumn()
deleteAt: Date | null;
}