From 8bb07009acc633b5065ec9a6665b1d81ff784f38 Mon Sep 17 00:00:00 2001 From: hoeeeeeh Date: Sat, 30 Nov 2024 21:09:23 +0900 Subject: [PATCH 01/22] =?UTF-8?q?feat:=20outgoing=20message=20=EC=97=90=20?= =?UTF-8?q?socket.id=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/chatServer/src/event/dto/IncomingMessage.dto.ts | 8 +++++++- backend/chatServer/src/event/dto/OutgoingMessage.dto.ts | 9 ++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/backend/chatServer/src/event/dto/IncomingMessage.dto.ts b/backend/chatServer/src/event/dto/IncomingMessage.dto.ts index 3dd9b237..de5d071a 100644 --- a/backend/chatServer/src/event/dto/IncomingMessage.dto.ts +++ b/backend/chatServer/src/event/dto/IncomingMessage.dto.ts @@ -21,6 +21,11 @@ class NoticeIncomingMessageDto extends DefaultIncomingMessageDto { msg: string = ''; } +class BanUserIncomingMessageDto extends DefaultIncomingMessageDto { + userId: string = ''; + socketId: string = ''; +} + export { @@ -28,5 +33,6 @@ export { QuestionIncomingMessageDto, QuestionDoneIncomingMessageDto, DefaultIncomingMessageDto, - NoticeIncomingMessageDto + NoticeIncomingMessageDto, + BanUserIncomingMessageDto }; diff --git a/backend/chatServer/src/event/dto/OutgoingMessage.dto.ts b/backend/chatServer/src/event/dto/OutgoingMessage.dto.ts index 389ebad1..8557a5ed 100644 --- a/backend/chatServer/src/event/dto/OutgoingMessage.dto.ts +++ b/backend/chatServer/src/event/dto/OutgoingMessage.dto.ts @@ -12,6 +12,7 @@ class NormalOutgoingMessageDto extends DefaultOutgoingMessageDto { msg: string = ''; msgType: OutgoingMessageType = 'normal'; owner: WhoAmI = 'user'; + socketId: string = ''; } class QuestionOutgoingMessageDto extends DefaultOutgoingMessageDto { @@ -19,9 +20,15 @@ class QuestionOutgoingMessageDto extends DefaultOutgoingMessageDto { questionId: number = -1; questionDone: boolean = false; msgType: OutgoingMessageType = 'question'; + socketId: string = ''; } -class QuestionDoneOutgoingMessageDto extends QuestionOutgoingMessageDto {} +class QuestionDoneOutgoingMessageDto extends DefaultOutgoingMessageDto { + msg: string = ''; + questionId: number = -1; + questionDone: boolean = false; + msgType: OutgoingMessageType = 'question'; +} class NoticeOutgoingMessageDto extends DefaultOutgoingMessageDto { msg: string = ''; From e94af4ca62b38f77d6ef0df372589e5d4315c204 Mon Sep 17 00:00:00 2001 From: hoeeeeeh Date: Sat, 30 Nov 2024 21:09:43 +0900 Subject: [PATCH 02/22] =?UTF-8?q?feat:=20question=20dto=20=EC=97=90=20sock?= =?UTF-8?q?et.id=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/chatServer/src/event/dto/Question.dto.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/chatServer/src/event/dto/Question.dto.ts b/backend/chatServer/src/event/dto/Question.dto.ts index 0df4b9d4..3a7c78bb 100644 --- a/backend/chatServer/src/event/dto/Question.dto.ts +++ b/backend/chatServer/src/event/dto/Question.dto.ts @@ -8,6 +8,7 @@ class QuestionDto { msgTime: string = new Date().toISOString(); msgType: OutgoingMessageType = 'question'; questionId: number = -1; + socketId: string = ''; questionDone: boolean = false; } From 299da5ec8c241ec07111f5ae90236bad86cf97cb Mon Sep 17 00:00:00 2001 From: hoeeeeeh Date: Sat, 30 Nov 2024 21:21:46 +0900 Subject: [PATCH 03/22] =?UTF-8?q?feat:=20user=20ip=20ban=20=EB=A1=9C?= =?UTF-8?q?=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 --- backend/chatServer/src/chat/chat.error.ts | 54 +++++++++++++++++++ backend/chatServer/src/chat/chat.gateway.ts | 39 ++++++++++---- backend/chatServer/src/chat/chat.guard.ts | 30 +++++++++++ backend/chatServer/src/chat/chat.module.ts | 4 +- backend/chatServer/src/event/constants.ts | 29 +--------- .../src/event/dto/OutgoingMessage.dto.ts | 1 + .../chatServer/src/event/dto/Question.dto.ts | 1 + .../chatServer/src/room/room.repository.ts | 36 ++++++++++--- backend/chatServer/src/room/room.service.ts | 39 +++++++++----- backend/chatServer/src/room/user.interface.ts | 1 + 10 files changed, 174 insertions(+), 60 deletions(-) create mode 100644 backend/chatServer/src/chat/chat.error.ts diff --git a/backend/chatServer/src/chat/chat.error.ts b/backend/chatServer/src/chat/chat.error.ts new file mode 100644 index 00000000..691b7ab2 --- /dev/null +++ b/backend/chatServer/src/chat/chat.error.ts @@ -0,0 +1,54 @@ +import { HttpStatus } from '@nestjs/common'; +import { WsException } from '@nestjs/websockets'; + +class ChatException extends WsException { + statusCode: number; + constructor({ statusCode, message } : ChatError , public roomId?: string) { + super({ statusCode, message, roomId }); + this.statusCode = statusCode; + } + + getError(): object { + return { + statusCode: this.statusCode, + msg: this.message, + roomId: this.roomId || null, + }; + } +} + +interface ChatError { + statusCode: number; + message: string; +} + +const CHATTING_SOCKET_ERROR = { + ROOM_EMPTY: { + statusCode: HttpStatus.BAD_REQUEST, + message: '유저가 참여하고 있는 채팅방이 없습니다.' + }, + + ROOM_EXISTED: { + statusCode: HttpStatus.BAD_REQUEST, + message: '이미 존재하는 방입니다.' + }, + + INVALID_USER: { + statusCode: HttpStatus.UNAUTHORIZED, + message: '유효하지 않는 유저입니다.' + }, + + QUESTION_EMPTY: { + statusCode: HttpStatus.BAD_REQUEST, + message: '유효하지 않은 질문입니다.' + }, + + BAN_USER: { + statusCode: HttpStatus.FORBIDDEN, + message: '호스트에 의해 밴 당한 유저입니다.' + } + + +}; +export { CHATTING_SOCKET_ERROR, ChatException }; + diff --git a/backend/chatServer/src/chat/chat.gateway.ts b/backend/chatServer/src/chat/chat.gateway.ts index 7966bd42..de7ad5b1 100644 --- a/backend/chatServer/src/chat/chat.gateway.ts +++ b/backend/chatServer/src/chat/chat.gateway.ts @@ -6,21 +6,21 @@ import { OnGatewayConnection, OnGatewayDisconnect, MessageBody, - ConnectedSocket, + ConnectedSocket } from '@nestjs/websockets'; import { UseGuards } from '@nestjs/common'; import { Server, Socket } from 'socket.io'; import { - CHATTING_SOCKET_DEFAULT_EVENT, - CHATTING_SOCKET_RECEIVE_EVENT, CHATTING_SOCKET_SEND_EVENT + CHATTING_SOCKET_DEFAULT_EVENT, CHATTING_SOCKET_RECEIVE_EVENT, CHATTING_SOCKET_SEND_EVENT } from '../event/constants'; import { + BanUserIncomingMessageDto, NormalIncomingMessageDto, NoticeIncomingMessageDto, QuestionDoneIncomingMessageDto, QuestionIncomingMessageDto } from '../event/dto/IncomingMessage.dto'; import { JoiningRoomDto } from '../event/dto/JoiningRoom.dto'; import { RoomService } from '../room/room.service'; import { createAdapter } from '@socket.io/redis-adapter'; -import { HostGuard, MessageGuard } from './chat.guard'; +import { BlacklistGuard, HostGuard, MessageGuard } from './chat.guard'; import { LeavingRoomDto } from '../event/dto/LeavingRoom.dto'; import { NormalOutgoingMessageDto, @@ -28,6 +28,7 @@ import { QuestionOutgoingMessageDto } from '../event/dto/OutgoingMessage.dto'; import { QuestionDto } from '../event/dto/Question.dto'; +import { ChatException, CHATTING_SOCKET_ERROR } from './chat.error'; @WebSocketGateway({ cors: true }) export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect { @@ -69,6 +70,7 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa } // 특정 방에 참여하기 위한 메서드 + @UseGuards(BlacklistGuard) @SubscribeMessage(CHATTING_SOCKET_DEFAULT_EVENT.JOIN_ROOM) async handleJoinRoom(client: Socket, payload: JoiningRoomDto) { const { roomId, userId } = payload; @@ -93,7 +95,7 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa } // 방에 NORMAL 메시지를 보내기 위한 메서드 - @UseGuards(MessageGuard) + @UseGuards(MessageGuard, BlacklistGuard) @SubscribeMessage(CHATTING_SOCKET_SEND_EVENT.NORMAL) async handleNormalMessage(@ConnectedSocket() client: Socket, @MessageBody() payload: NormalIncomingMessageDto) { const { roomId, userId, msg } = payload; @@ -103,7 +105,8 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa ...user, msg, msgTime: new Date().toISOString(), - msgType: 'normal' + msgType: 'normal', + socketId: client.id }; console.log('Normal Message Come In: ', normalOutgoingMessage); const hostId = await this.roomService.getHostOfRoom(roomId); @@ -121,7 +124,7 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa } // 방에 QUESTION 메시지를 보내기 위한 메서드 - @UseGuards(MessageGuard) + @UseGuards(MessageGuard,BlacklistGuard) @SubscribeMessage(CHATTING_SOCKET_SEND_EVENT.QUESTION) async handleQuestionMessage(@ConnectedSocket() client: Socket, @MessageBody() payload: QuestionIncomingMessageDto) { const { roomId, msg } = payload; @@ -132,7 +135,8 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa msg, msgTime: new Date().toISOString(), msgType: 'question', - questionDone: false + questionDone: false, + socketId: client.id }; const question: QuestionOutgoingMessageDto = await this.roomService.addQuestion(roomId, questionWithoutId); @@ -150,8 +154,7 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa } // 방에 NOTICE 메시지를 보내기 위한 메서드 - @UseGuards(MessageGuard) - @UseGuards(HostGuard) + @UseGuards(MessageGuard, HostGuard) @SubscribeMessage(CHATTING_SOCKET_SEND_EVENT.NOTICE) async handleNoticeMessage(@ConnectedSocket() client: Socket, @MessageBody() payload: NoticeIncomingMessageDto) { const { roomId, msg } = payload; @@ -165,4 +168,20 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa }; this.server.to(roomId).emit(CHATTING_SOCKET_RECEIVE_EVENT.NOTICE, noticeOutgoingMessage); } + + @UseGuards(HostGuard) + @SubscribeMessage(CHATTING_SOCKET_DEFAULT_EVENT.BAN_USER) + async handleBanUserMessage(@ConnectedSocket() client: Socket, @MessageBody() payload: BanUserIncomingMessageDto) { + const { roomId, socketId } = payload; + const banUser = this.server.sockets.sockets.get(socketId); + const address = banUser?.handshake.address.replaceAll('::ffff:', ''); + + if(!address) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER); + + const forwarded = banUser?.handshake.headers.forwarded ?? address; + console.log('ban:', roomId, address, forwarded); + + await this.roomService.addUserToBlacklist(roomId, address, forwarded); + console.log(await this.roomService.getUserBlacklist(roomId, address)); + } } diff --git a/backend/chatServer/src/chat/chat.guard.ts b/backend/chatServer/src/chat/chat.guard.ts index 270defc3..021baac0 100644 --- a/backend/chatServer/src/chat/chat.guard.ts +++ b/backend/chatServer/src/chat/chat.guard.ts @@ -1,5 +1,8 @@ import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { RoomService } from '../room/room.service'; +import { Socket } from 'socket.io'; + +import { ChatException, CHATTING_SOCKET_ERROR } from './chat.error'; @Injectable() export class MessageGuard implements CanActivate { @@ -22,3 +25,30 @@ export class HostGuard implements CanActivate { return hostId === userId; } } + +@Injectable() +export class BlacklistGuard implements CanActivate { + constructor(private roomService: RoomService) {}; + async canActivate(context: ExecutionContext) { + const payload = context.switchToWs().getData(); + const { roomId } = payload; + + const client: Socket = context.switchToWs().getClient(); + const address = client.handshake.address.replaceAll('::ffff:', ''); + const forwarded = client.handshake.headers.forwarded?.split(',')[0] ?? address; + + const isValidUser = await this.whenJoinRoom(roomId, address, forwarded); + + if(!isValidUser) throw new ChatException(CHATTING_SOCKET_ERROR.BAN_USER, roomId); + return true; + } + + async whenJoinRoom(roomId: string, address: string, forwarded: string) { + console.log(roomId, address, forwarded); + const blacklistInRoom = await this.roomService.getUserBlacklist(roomId, address); + console.log(blacklistInRoom); + const isInBlacklistUser = blacklistInRoom.some((blackForwarded) => blackForwarded === forwarded); + console.log('blacklistInRoom:', isInBlacklistUser); + return !isInBlacklistUser; + } +} diff --git a/backend/chatServer/src/chat/chat.module.ts b/backend/chatServer/src/chat/chat.module.ts index 51629715..bd285e1f 100644 --- a/backend/chatServer/src/chat/chat.module.ts +++ b/backend/chatServer/src/chat/chat.module.ts @@ -1,10 +1,10 @@ import { Module } from '@nestjs/common'; import { ChatGateway } from './chat.gateway'; import { RoomModule } from '../room/room.module'; -import { MessageGuard } from './chat.guard'; +import { BlacklistGuard, HostGuard, MessageGuard } from './chat.guard'; @Module({ imports: [RoomModule], - providers: [ChatGateway, MessageGuard], + providers: [ChatGateway, MessageGuard, BlacklistGuard, HostGuard], }) export class ChatModule {} diff --git a/backend/chatServer/src/event/constants.ts b/backend/chatServer/src/event/constants.ts index 2ce8d053..ce8fd0c6 100644 --- a/backend/chatServer/src/event/constants.ts +++ b/backend/chatServer/src/event/constants.ts @@ -1,8 +1,7 @@ -import { HttpStatus } from '@nestjs/common'; - const CHATTING_SOCKET_DEFAULT_EVENT = { JOIN_ROOM: 'join_room', LEAVE_ROOM: 'leave_room', + BAN_USER: 'ban_user', }; const CHATTING_SOCKET_RECEIVE_EVENT = { @@ -20,28 +19,4 @@ const CHATTING_SOCKET_SEND_EVENT = { NOTICE: 'send_notice' }; -const CHATTING_SOCKET_ERROR = { - ROOM_EMPTY : { - statusCode: HttpStatus.BAD_REQUEST, - message: '유저가 참여하고 있는 채팅방이 없습니다.' - }, - - ROOM_EXISTED: { - statusCode: HttpStatus.BAD_REQUEST, - message: '이미 존재하는 방입니다.' - }, - - INVALID_USER: { - statusCode: HttpStatus.UNAUTHORIZED, - message: '유효하지 않는 유저입니다.' - }, - - QUESTION_EMPTY: { - statusCode: HttpStatus.BAD_REQUEST, - message: '유효하지 않은 질문입니다.' - }, - - -}; - -export { CHATTING_SOCKET_DEFAULT_EVENT, CHATTING_SOCKET_SEND_EVENT, CHATTING_SOCKET_RECEIVE_EVENT, CHATTING_SOCKET_ERROR}; +export { CHATTING_SOCKET_DEFAULT_EVENT, CHATTING_SOCKET_SEND_EVENT, CHATTING_SOCKET_RECEIVE_EVENT}; diff --git a/backend/chatServer/src/event/dto/OutgoingMessage.dto.ts b/backend/chatServer/src/event/dto/OutgoingMessage.dto.ts index 8557a5ed..f77eac50 100644 --- a/backend/chatServer/src/event/dto/OutgoingMessage.dto.ts +++ b/backend/chatServer/src/event/dto/OutgoingMessage.dto.ts @@ -5,6 +5,7 @@ class DefaultOutgoingMessageDto { roomId: string = ''; nickname: string = ''; color: string = ''; + entryTime: string = ''; msgTime: string = new Date().toISOString(); } diff --git a/backend/chatServer/src/event/dto/Question.dto.ts b/backend/chatServer/src/event/dto/Question.dto.ts index 3a7c78bb..67ab8788 100644 --- a/backend/chatServer/src/event/dto/Question.dto.ts +++ b/backend/chatServer/src/event/dto/Question.dto.ts @@ -4,6 +4,7 @@ class QuestionDto { roomId: string = ''; nickname: string = ''; color: string = ''; + entryTime: string = ''; msg: string = ''; msgTime: string = new Date().toISOString(); msgType: OutgoingMessageType = 'question'; diff --git a/backend/chatServer/src/room/room.repository.ts b/backend/chatServer/src/room/room.repository.ts index 6bb90b68..3289d2d0 100644 --- a/backend/chatServer/src/room/room.repository.ts +++ b/backend/chatServer/src/room/room.repository.ts @@ -1,8 +1,9 @@ import { Injectable } from '@nestjs/common'; import { Cluster } from 'ioredis'; -import { CHATTING_SOCKET_ERROR } from '../event/constants'; -import { WsException } from '@nestjs/websockets'; import { QuestionDto } from '../event/dto/Question.dto'; +import { ChatException, CHATTING_SOCKET_ERROR } from '../chat/chat.error'; + +type FORWARDED = string; @Injectable() export class RoomRepository { @@ -10,6 +11,7 @@ export class RoomRepository { roomIdPrefix = 'room:'; questionPrefix = 'question'; questionIdPrefix = 'id'; + blacklistPrefix = 'blacklist'; injectClient(redisClient: Cluster){ this.redisClient = redisClient; @@ -27,6 +29,14 @@ export class RoomRepository { return `${this.getRoomStringWithPrefix(roomId)}-${this.questionPrefix}-${this.questionIdPrefix}`; } + private getUserBlacklistStringWithPrefix(address:string){ + return `${this.blacklistPrefix}:${address}`; + } + + private getUserBlacklistInRoomWithPrefix(roomId:string, address:string){ + return `${this.getRoomStringWithPrefix(roomId)}-${this.getUserBlacklistStringWithPrefix(address)}`; + } + private async lindex(key: string, index: number){ const result = await this.redisClient.lindex(key, index); if(result) return JSON.parse(result) as T; @@ -35,12 +45,12 @@ export class RoomRepository { private async lrange(key: string, start: number, end: number){ const result = await this.redisClient.lrange(key, start, end); - return result.map((r) => JSON.parse(r)) as T; + return result as T; } private async getData(key: string) { const result = await this.redisClient.get(key); - if(result) return JSON.parse(result) as T; + if(result) return typeof result === 'string' ? result as T : JSON.parse(result) as T; return undefined; } @@ -50,7 +60,7 @@ export class RoomRepository { async getHost(roomId: string) { const hostId = await this.redisClient.get(this.getRoomStringWithPrefix(roomId)); - if(!hostId) throw new WsException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if(!hostId) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); return hostId; } @@ -72,7 +82,7 @@ export class RoomRepository { async markQuestionAsDone(roomId: string, questionId: number): Promise { const question = await this.getQuestion(roomId, questionId); - if(!question) throw new WsException(CHATTING_SOCKET_ERROR.QUESTION_EMPTY); + if(!question) throw new ChatException(CHATTING_SOCKET_ERROR.QUESTION_EMPTY); question.questionDone = true; this.redisClient.lset(this.getQuestionStringWithPrefix(roomId), questionId, JSON.stringify(question)); return question; @@ -88,7 +98,7 @@ export class RoomRepository { async getQuestion(roomId: string, questionId: number): Promise { const question = await this.lindex>(this.getQuestionStringWithPrefix(roomId), questionId); if(question) return {...question, questionId }; - throw new WsException(CHATTING_SOCKET_ERROR.QUESTION_EMPTY); + throw new ChatException(CHATTING_SOCKET_ERROR.QUESTION_EMPTY); } async getQuestionId(roomId: string) { @@ -102,4 +112,16 @@ export class RoomRepository { } + async getUserBlacklist(roomId: string, address: string): Promise { + const userBlacklist = await this.lrange(this.getUserBlacklistInRoomWithPrefix(roomId, address), 0, -1); + console.log('blacklist', userBlacklist); + if (!userBlacklist) return []; + return userBlacklist; + } + + async addUserBlacklistToRoom(roomId: string, address: string, forwarded: string){ + console.log(roomId, address, forwarded); + console.log(this.getUserBlacklistInRoomWithPrefix(roomId, address)); + return this.redisClient.rpush(this.getUserBlacklistInRoomWithPrefix(roomId, address), forwarded); + } } diff --git a/backend/chatServer/src/room/room.service.ts b/backend/chatServer/src/room/room.service.ts index 3246d45e..e72126ec 100644 --- a/backend/chatServer/src/room/room.service.ts +++ b/backend/chatServer/src/room/room.service.ts @@ -1,8 +1,6 @@ import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; import { Cluster, Redis } from 'ioredis'; import { createAdapter } from '@socket.io/redis-adapter'; -import { WsException } from '@nestjs/websockets'; -import { CHATTING_SOCKET_ERROR } from '../event/constants'; import { User } from './user.interface'; import { getRandomAdjective, getRandomBrightColor, getRandomNoun } from '../utils/random'; import { RoomRepository } from './room.repository'; @@ -12,6 +10,7 @@ import dotenv from 'dotenv'; import path from 'path'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; +import { ChatException, CHATTING_SOCKET_ERROR } from '../chat/chat.error'; // 현재 파일의 URL을 파일 경로로 변환 const __filename = fileURLToPath(import.meta.url); @@ -28,7 +27,8 @@ function createRandomNickname(){ function createRandomUserInstance(): User { return { nickname: createRandomNickname(), - color: getRandomBrightColor() + color: getRandomBrightColor(), + entryTime: new Date().toISOString() }; } @@ -85,13 +85,13 @@ export class RoomService implements OnModuleInit, OnModuleDestroy { // 방 삭제 async deleteRoom(roomId: string) { const roomExists = await this.redisRepository.isRoomExisted(roomId); - if (!roomExists) throw new WsException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); await this.redisRepository.deleteRoom(roomId); } async addQuestion(roomId: string, question: Omit){ const roomExists = await this.redisRepository.isRoomExisted(roomId); - if (!roomExists) throw new WsException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); return await this.redisRepository.addQuestionToRoom(roomId, question); } @@ -102,7 +102,7 @@ export class RoomService implements OnModuleInit, OnModuleDestroy { question: Omit, ): Promise { const roomExists = await this.redisRepository.isRoomExisted(roomId); - if (!roomExists) throw new WsException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); return await this.redisRepository.addQuestionToRoom(roomId, question); } @@ -110,24 +110,24 @@ export class RoomService implements OnModuleInit, OnModuleDestroy { // 특정 질문 완료 처리 async markQuestionAsDone(roomId: string, questionId: number) { const roomExists = await this.redisRepository.isRoomExisted(roomId); - if (!roomExists) throw new WsException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); const markedQuestion = await this.redisRepository.markQuestionAsDone(roomId, questionId); - if (!markedQuestion) throw new WsException(CHATTING_SOCKET_ERROR.QUESTION_EMPTY); + if (!markedQuestion) throw new ChatException(CHATTING_SOCKET_ERROR.QUESTION_EMPTY); return markedQuestion; } // 방에 속한 모든 질문 조회 async getQuestions(roomId: string): Promise { const roomExists = await this.redisRepository.isRoomExisted(roomId); - if (!roomExists) throw new WsException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); return this.redisRepository.getQuestionsAll(roomId); } async getQuestionsNotDone(roomId: string): Promise { const roomExists = await this.redisRepository.isRoomExisted(roomId); - if (!roomExists) throw new WsException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); return this.redisRepository.getQuestionsUnmarked(roomId); } @@ -135,7 +135,7 @@ export class RoomService implements OnModuleInit, OnModuleDestroy { // 특정 질문 조회 async getQuestion(roomId: string, questionId: number): Promise { const roomExists = await this.redisRepository.isRoomExisted(roomId); - if (!roomExists) throw new WsException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); return this.redisRepository.getQuestion(roomId, questionId); } @@ -149,7 +149,7 @@ export class RoomService implements OnModuleInit, OnModuleDestroy { // 유저 삭제 async deleteUser(clientId: string) { const user = this.users.get(clientId); - if (!user) throw new WsException(CHATTING_SOCKET_ERROR.INVALID_USER); + if (!user) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER); this.users.delete(clientId); return user; } @@ -157,13 +157,24 @@ export class RoomService implements OnModuleInit, OnModuleDestroy { // 특정 유저 조회 async getUserByClientId(clientId: string) { const user = this.users.get(clientId); - if (!user) throw new WsException(CHATTING_SOCKET_ERROR.INVALID_USER); + if (!user) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER); return user; } async getHostOfRoom(roomId: string) { const roomExists = await this.redisRepository.isRoomExisted(roomId); - if (!roomExists) throw new WsException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); return await this.redisRepository.getHost(roomId); } + + async getUserBlacklist(roomId: string, address: string) { + const roomExists = await this.redisRepository.isRoomExisted(roomId); + if (!roomExists) return []; + + return await this.redisRepository.getUserBlacklist(roomId, address); + } + + async addUserToBlacklist(roomId: string, address: string, forwarded: string){ + return await this.redisRepository.addUserBlacklistToRoom(roomId, address, forwarded); + } } diff --git a/backend/chatServer/src/room/user.interface.ts b/backend/chatServer/src/room/user.interface.ts index a37d58a8..cba40228 100644 --- a/backend/chatServer/src/room/user.interface.ts +++ b/backend/chatServer/src/room/user.interface.ts @@ -1,4 +1,5 @@ interface User { + entryTime: string; nickname: string; color: string; } From 86e9327b847b8370da5d2f5d9e909c80eeac51cb Mon Sep 17 00:00:00 2001 From: i3kae Date: Sat, 30 Nov 2024 21:28:16 +0900 Subject: [PATCH 04/22] =?UTF-8?q?feat:=20=EB=B0=A9=EC=86=A1/=EB=8B=A4?= =?UTF-8?q?=EC=8B=9C=EB=B3=B4=EA=B8=B0=20=EC=A1=B4=EC=9E=AC=20=EC=97=AC?= =?UTF-8?q?=EB=B6=80=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=B2=B4=ED=81=AC=20?= =?UTF-8?q?api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mainServer/src/replay/replay.controller.ts | 16 ++++++++++++++++ .../src/streams/streams.controller.ts | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/backend/mainServer/src/replay/replay.controller.ts b/backend/mainServer/src/replay/replay.controller.ts index 76288cad..cf11f904 100644 --- a/backend/mainServer/src/replay/replay.controller.ts +++ b/backend/mainServer/src/replay/replay.controller.ts @@ -58,4 +58,20 @@ export class ReplayController { } } + @Get('/existence') + @ApiOperation({summary: 'Get replay exited', description: '다시보기에 대한 존재 여부를 반환 받습니다.'}) + async getExistence(@Query('videoId') videoId: string, @Res() res: Response) { + try { + const replaySessions = this.memoryDBService.findAll().filter((info) => info.replay); + if (replaySessions.some((info) => info.sessionKey === videoId)) { + res.status(HttpStatus.OK).json({exited: true}); + } + else { + res.status(HttpStatus.OK).json({exited: false}); + } + } catch (err) { + console.log(err); + res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(); + } + } } diff --git a/backend/mainServer/src/streams/streams.controller.ts b/backend/mainServer/src/streams/streams.controller.ts index 2a84a162..eeb65f53 100644 --- a/backend/mainServer/src/streams/streams.controller.ts +++ b/backend/mainServer/src/streams/streams.controller.ts @@ -99,4 +99,21 @@ export class StreamsController { } } } + + @Get('/existence') + @ApiOperation({summary: 'Get Session exited', description: '방송 세션에 대한 존재 여부를 반환 받습니다.'}) + async getExistence(@Query('sessionKey') sessionKey: string, @Res() res: Response) { + try { + const liveSessions = this.memoryDBService.findAll().filter((info) => info.state); + if (liveSessions.some((info) => info.sessionKey === sessionKey)) { + res.status(HttpStatus.OK).json({exited: true}); + } + else { + res.status(HttpStatus.OK).json({exited: false}); + } + } catch (err) { + console.log(err); + res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(); + } + } } \ No newline at end of file From 73c706e387aa23909a7252a66e976dbfcc7075f8 Mon Sep 17 00:00:00 2001 From: hoeeeeeh Date: Mon, 2 Dec 2024 10:28:12 +0900 Subject: [PATCH 05/22] =?UTF-8?q?fix:=20init=20data=20=ED=98=95=EC=8B=9D?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/chatServer/src/room/room.repository.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/backend/chatServer/src/room/room.repository.ts b/backend/chatServer/src/room/room.repository.ts index 3289d2d0..5549272a 100644 --- a/backend/chatServer/src/room/room.repository.ts +++ b/backend/chatServer/src/room/room.repository.ts @@ -39,19 +39,24 @@ export class RoomRepository { private async lindex(key: string, index: number){ const result = await this.redisClient.lindex(key, index); - if(result) return JSON.parse(result) as T; - return undefined; + if(!result) return undefined; + + const l = typeof result === 'string' ? result : JSON.parse(result); + return l as T; } private async lrange(key: string, start: number, end: number){ const result = await this.redisClient.lrange(key, start, end); - return result as T; + if(!result) return undefined; + const arrayT = result.map((r) => typeof r === 'string' ? r : JSON.parse(r)); + return arrayT as T; } private async getData(key: string) { const result = await this.redisClient.get(key); - if(result) return typeof result === 'string' ? result as T : JSON.parse(result) as T; - return undefined; + if(!result) return undefined; + const data = result === 'string' ? result : JSON.parse(result); + return data as T; } async isRoomExisted(roomId: string) { From 905c03a835c9624ba63b5c2bca6b89866837f067 Mon Sep 17 00:00:00 2001 From: hoeeeeeh Date: Mon, 2 Dec 2024 10:42:20 +0900 Subject: [PATCH 06/22] =?UTF-8?q?fix:=20init=20data=20=ED=98=95=EC=8B=9D?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chatServer/src/room/room.repository.ts | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/backend/chatServer/src/room/room.repository.ts b/backend/chatServer/src/room/room.repository.ts index 5549272a..d3908103 100644 --- a/backend/chatServer/src/room/room.repository.ts +++ b/backend/chatServer/src/room/room.repository.ts @@ -40,23 +40,34 @@ export class RoomRepository { private async lindex(key: string, index: number){ const result = await this.redisClient.lindex(key, index); if(!result) return undefined; - - const l = typeof result === 'string' ? result : JSON.parse(result); - return l as T; + try { + return JSON.parse(result) as T; + } catch { + return result as T; + } } private async lrange(key: string, start: number, end: number){ const result = await this.redisClient.lrange(key, start, end); if(!result) return undefined; - const arrayT = result.map((r) => typeof r === 'string' ? r : JSON.parse(r)); + const arrayT = result.map((r) => { + try { + return JSON.parse(r); + } catch { + return r; + } + }); return arrayT as T; } private async getData(key: string) { const result = await this.redisClient.get(key); if(!result) return undefined; - const data = result === 'string' ? result : JSON.parse(result); - return data as T; + try { + return JSON.parse(result) as T; + } catch { + return result as T; + } } async isRoomExisted(roomId: string) { From 8175734162dda61ed02a6f64ba77620f87a54084 Mon Sep 17 00:00:00 2001 From: hoeeeeeh Date: Mon, 2 Dec 2024 12:08:38 +0900 Subject: [PATCH 07/22] =?UTF-8?q?refactor:=20ChatException=20throw=20?= =?UTF-8?q?=EC=8B=9C,=20roomId=20=EB=A5=BC=20=EB=8B=B4=EC=95=84=EC=84=9C?= =?UTF-8?q?=20throw?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/chatServer/src/chat/chat.gateway.ts | 2 +- backend/chatServer/src/room/room.repository.ts | 6 +++--- backend/chatServer/src/room/room.service.ts | 18 +++++++++--------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/backend/chatServer/src/chat/chat.gateway.ts b/backend/chatServer/src/chat/chat.gateway.ts index de7ad5b1..6c4b52a2 100644 --- a/backend/chatServer/src/chat/chat.gateway.ts +++ b/backend/chatServer/src/chat/chat.gateway.ts @@ -176,7 +176,7 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa const banUser = this.server.sockets.sockets.get(socketId); const address = banUser?.handshake.address.replaceAll('::ffff:', ''); - if(!address) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER); + if(!address) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER, roomId); const forwarded = banUser?.handshake.headers.forwarded ?? address; console.log('ban:', roomId, address, forwarded); diff --git a/backend/chatServer/src/room/room.repository.ts b/backend/chatServer/src/room/room.repository.ts index d3908103..18fc4d51 100644 --- a/backend/chatServer/src/room/room.repository.ts +++ b/backend/chatServer/src/room/room.repository.ts @@ -76,7 +76,7 @@ export class RoomRepository { async getHost(roomId: string) { const hostId = await this.redisClient.get(this.getRoomStringWithPrefix(roomId)); - if(!hostId) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if(!hostId) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY, roomId); return hostId; } @@ -98,7 +98,7 @@ export class RoomRepository { async markQuestionAsDone(roomId: string, questionId: number): Promise { const question = await this.getQuestion(roomId, questionId); - if(!question) throw new ChatException(CHATTING_SOCKET_ERROR.QUESTION_EMPTY); + if(!question) throw new ChatException(CHATTING_SOCKET_ERROR.QUESTION_EMPTY, roomId); question.questionDone = true; this.redisClient.lset(this.getQuestionStringWithPrefix(roomId), questionId, JSON.stringify(question)); return question; @@ -114,7 +114,7 @@ export class RoomRepository { async getQuestion(roomId: string, questionId: number): Promise { const question = await this.lindex>(this.getQuestionStringWithPrefix(roomId), questionId); if(question) return {...question, questionId }; - throw new ChatException(CHATTING_SOCKET_ERROR.QUESTION_EMPTY); + throw new ChatException(CHATTING_SOCKET_ERROR.QUESTION_EMPTY, roomId); } async getQuestionId(roomId: string) { diff --git a/backend/chatServer/src/room/room.service.ts b/backend/chatServer/src/room/room.service.ts index e72126ec..f2e16a89 100644 --- a/backend/chatServer/src/room/room.service.ts +++ b/backend/chatServer/src/room/room.service.ts @@ -85,13 +85,13 @@ export class RoomService implements OnModuleInit, OnModuleDestroy { // 방 삭제 async deleteRoom(roomId: string) { const roomExists = await this.redisRepository.isRoomExisted(roomId); - if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY, roomId); await this.redisRepository.deleteRoom(roomId); } async addQuestion(roomId: string, question: Omit){ const roomExists = await this.redisRepository.isRoomExisted(roomId); - if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY, roomId); return await this.redisRepository.addQuestionToRoom(roomId, question); } @@ -102,7 +102,7 @@ export class RoomService implements OnModuleInit, OnModuleDestroy { question: Omit, ): Promise { const roomExists = await this.redisRepository.isRoomExisted(roomId); - if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY, roomId); return await this.redisRepository.addQuestionToRoom(roomId, question); } @@ -110,24 +110,24 @@ export class RoomService implements OnModuleInit, OnModuleDestroy { // 특정 질문 완료 처리 async markQuestionAsDone(roomId: string, questionId: number) { const roomExists = await this.redisRepository.isRoomExisted(roomId); - if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY, roomId); const markedQuestion = await this.redisRepository.markQuestionAsDone(roomId, questionId); - if (!markedQuestion) throw new ChatException(CHATTING_SOCKET_ERROR.QUESTION_EMPTY); + if (!markedQuestion) throw new ChatException(CHATTING_SOCKET_ERROR.QUESTION_EMPTY, roomId); return markedQuestion; } // 방에 속한 모든 질문 조회 async getQuestions(roomId: string): Promise { const roomExists = await this.redisRepository.isRoomExisted(roomId); - if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY, roomId); return this.redisRepository.getQuestionsAll(roomId); } async getQuestionsNotDone(roomId: string): Promise { const roomExists = await this.redisRepository.isRoomExisted(roomId); - if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY, roomId); return this.redisRepository.getQuestionsUnmarked(roomId); } @@ -135,7 +135,7 @@ export class RoomService implements OnModuleInit, OnModuleDestroy { // 특정 질문 조회 async getQuestion(roomId: string, questionId: number): Promise { const roomExists = await this.redisRepository.isRoomExisted(roomId); - if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY, roomId); return this.redisRepository.getQuestion(roomId, questionId); } @@ -163,7 +163,7 @@ export class RoomService implements OnModuleInit, OnModuleDestroy { async getHostOfRoom(roomId: string) { const roomExists = await this.redisRepository.isRoomExisted(roomId); - if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY); + if (!roomExists) throw new ChatException(CHATTING_SOCKET_ERROR.ROOM_EMPTY, roomId); return await this.redisRepository.getHost(roomId); } From 8fb89c668bdfcf5f5983c082005d57b83a0ce888 Mon Sep 17 00:00:00 2001 From: hoeeeeeh Date: Mon, 2 Dec 2024 12:54:29 +0900 Subject: [PATCH 08/22] =?UTF-8?q?feat:=20Chat=20Exception=20=EC=84=B8?= =?UTF-8?q?=EB=B6=84=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/chatServer/src/chat/chat.error.ts | 12 +++++++++++- backend/chatServer/src/chat/chat.guard.ts | 4 +++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/backend/chatServer/src/chat/chat.error.ts b/backend/chatServer/src/chat/chat.error.ts index 691b7ab2..3ce07cb5 100644 --- a/backend/chatServer/src/chat/chat.error.ts +++ b/backend/chatServer/src/chat/chat.error.ts @@ -8,7 +8,7 @@ class ChatException extends WsException { this.statusCode = statusCode; } - getError(): object { + getError() { return { statusCode: this.statusCode, msg: this.message, @@ -38,6 +38,11 @@ const CHATTING_SOCKET_ERROR = { message: '유효하지 않는 유저입니다.' }, + UNAUTHORIZED: { + statusCode: HttpStatus.UNAUTHORIZED, + message: '해당 명령에 대한 권한이 없습니다.' + }, + QUESTION_EMPTY: { statusCode: HttpStatus.BAD_REQUEST, message: '유효하지 않은 질문입니다.' @@ -46,6 +51,11 @@ const CHATTING_SOCKET_ERROR = { BAN_USER: { statusCode: HttpStatus.FORBIDDEN, message: '호스트에 의해 밴 당한 유저입니다.' + }, + + MSG_TOO_LONG:{ + statusCode: HttpStatus.NOT_ACCEPTABLE, + message: '메세지의 내용이 없거나, 길이가 150자를 초과했습니다.' } diff --git a/backend/chatServer/src/chat/chat.guard.ts b/backend/chatServer/src/chat/chat.guard.ts index 021baac0..575ecb83 100644 --- a/backend/chatServer/src/chat/chat.guard.ts +++ b/backend/chatServer/src/chat/chat.guard.ts @@ -10,7 +10,8 @@ export class MessageGuard implements CanActivate { canActivate(context: ExecutionContext): boolean { const payload = context.switchToWs().getData(); const { msg } = payload; - return !!msg && msg.length <= 150; + if(!!msg && msg.length <= 150) return true; + throw new ChatException(CHATTING_SOCKET_ERROR.MSG_TOO_LONG); } } @@ -22,6 +23,7 @@ export class HostGuard implements CanActivate { const { roomId, userId } = payload; const hostId = await this.roomService.getHostOfRoom(roomId); console.log('hostGuard:', hostId, userId); + if (hostId !== userId) throw new ChatException(CHATTING_SOCKET_ERROR.UNAUTHORIZED, roomId); return hostId === userId; } } From a5068ab6a61dd4dd37bdd3d6793ffcd2a5a5ee7d Mon Sep 17 00:00:00 2001 From: hoeeeeeh Date: Mon, 2 Dec 2024 12:55:16 +0900 Subject: [PATCH 09/22] =?UTF-8?q?feat:=20Chat=20Exception=20=EC=84=B8?= =?UTF-8?q?=EB=B6=84=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/chatServer/src/chat/chat.guard.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/chatServer/src/chat/chat.guard.ts b/backend/chatServer/src/chat/chat.guard.ts index 575ecb83..3246cfaf 100644 --- a/backend/chatServer/src/chat/chat.guard.ts +++ b/backend/chatServer/src/chat/chat.guard.ts @@ -23,8 +23,8 @@ export class HostGuard implements CanActivate { const { roomId, userId } = payload; const hostId = await this.roomService.getHostOfRoom(roomId); console.log('hostGuard:', hostId, userId); - if (hostId !== userId) throw new ChatException(CHATTING_SOCKET_ERROR.UNAUTHORIZED, roomId); - return hostId === userId; + if (hostId === userId) return true; + throw new ChatException(CHATTING_SOCKET_ERROR.UNAUTHORIZED, roomId); } } From b47e98c131d8fa79ea4c118859646aa0ee33b566 Mon Sep 17 00:00:00 2001 From: hoeeeeeh Date: Mon, 2 Dec 2024 16:15:33 +0900 Subject: [PATCH 10/22] =?UTF-8?q?refactor=20:=20user=20ban=20=EC=9D=84=20x?= =?UTF-8?q?-forwarded=20=EC=97=90=EC=84=9C=20userAgent=20=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/chatServer/src/chat/chat.gateway.ts | 6 +++--- backend/chatServer/src/chat/chat.guard.ts | 11 ++++++----- backend/chatServer/src/room/room.repository.ts | 12 ++++++------ backend/chatServer/src/room/room.service.ts | 4 ++-- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/backend/chatServer/src/chat/chat.gateway.ts b/backend/chatServer/src/chat/chat.gateway.ts index 6c4b52a2..93211dfd 100644 --- a/backend/chatServer/src/chat/chat.gateway.ts +++ b/backend/chatServer/src/chat/chat.gateway.ts @@ -178,10 +178,10 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa if(!address) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER, roomId); - const forwarded = banUser?.handshake.headers.forwarded ?? address; - console.log('ban:', roomId, address, forwarded); + const userAgent = banUser?.handshake.headers['user-agent']; + if(!userAgent) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER, roomId); - await this.roomService.addUserToBlacklist(roomId, address, forwarded); + await this.roomService.addUserToBlacklist(roomId, address, userAgent); console.log(await this.roomService.getUserBlacklist(roomId, address)); } } diff --git a/backend/chatServer/src/chat/chat.guard.ts b/backend/chatServer/src/chat/chat.guard.ts index 3246cfaf..0a66e366 100644 --- a/backend/chatServer/src/chat/chat.guard.ts +++ b/backend/chatServer/src/chat/chat.guard.ts @@ -37,19 +37,20 @@ export class BlacklistGuard implements CanActivate { const client: Socket = context.switchToWs().getClient(); const address = client.handshake.address.replaceAll('::ffff:', ''); - const forwarded = client.handshake.headers.forwarded?.split(',')[0] ?? address; + const userAgent = client.handshake.headers['user-agent']; - const isValidUser = await this.whenJoinRoom(roomId, address, forwarded); + if(!userAgent) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER, roomId); + const isValidUser = await this.whenJoinRoom(roomId, address, userAgent); if(!isValidUser) throw new ChatException(CHATTING_SOCKET_ERROR.BAN_USER, roomId); return true; } - async whenJoinRoom(roomId: string, address: string, forwarded: string) { - console.log(roomId, address, forwarded); + async whenJoinRoom(roomId: string, address: string, userAgent: string) { + console.log(roomId, address, userAgent); const blacklistInRoom = await this.roomService.getUserBlacklist(roomId, address); console.log(blacklistInRoom); - const isInBlacklistUser = blacklistInRoom.some((blackForwarded) => blackForwarded === forwarded); + const isInBlacklistUser = blacklistInRoom.some((bannedUserAgent) => bannedUserAgent === userAgent); console.log('blacklistInRoom:', isInBlacklistUser); return !isInBlacklistUser; } diff --git a/backend/chatServer/src/room/room.repository.ts b/backend/chatServer/src/room/room.repository.ts index 18fc4d51..16cf757a 100644 --- a/backend/chatServer/src/room/room.repository.ts +++ b/backend/chatServer/src/room/room.repository.ts @@ -3,7 +3,7 @@ import { Cluster } from 'ioredis'; import { QuestionDto } from '../event/dto/Question.dto'; import { ChatException, CHATTING_SOCKET_ERROR } from '../chat/chat.error'; -type FORWARDED = string; +type USER_AGENT = string; @Injectable() export class RoomRepository { @@ -128,16 +128,16 @@ export class RoomRepository { } - async getUserBlacklist(roomId: string, address: string): Promise { - const userBlacklist = await this.lrange(this.getUserBlacklistInRoomWithPrefix(roomId, address), 0, -1); + async getUserBlacklist(roomId: string, address: string): Promise { + const userBlacklist = await this.lrange(this.getUserBlacklistInRoomWithPrefix(roomId, address), 0, -1); console.log('blacklist', userBlacklist); if (!userBlacklist) return []; return userBlacklist; } - async addUserBlacklistToRoom(roomId: string, address: string, forwarded: string){ - console.log(roomId, address, forwarded); + async addUserBlacklistToRoom(roomId: string, address: string, userAgent: string){ + console.log(roomId, address, userAgent); console.log(this.getUserBlacklistInRoomWithPrefix(roomId, address)); - return this.redisClient.rpush(this.getUserBlacklistInRoomWithPrefix(roomId, address), forwarded); + return this.redisClient.rpush(this.getUserBlacklistInRoomWithPrefix(roomId, address), userAgent); } } diff --git a/backend/chatServer/src/room/room.service.ts b/backend/chatServer/src/room/room.service.ts index f2e16a89..cfa9caf6 100644 --- a/backend/chatServer/src/room/room.service.ts +++ b/backend/chatServer/src/room/room.service.ts @@ -174,7 +174,7 @@ export class RoomService implements OnModuleInit, OnModuleDestroy { return await this.redisRepository.getUserBlacklist(roomId, address); } - async addUserToBlacklist(roomId: string, address: string, forwarded: string){ - return await this.redisRepository.addUserBlacklistToRoom(roomId, address, forwarded); + async addUserToBlacklist(roomId: string, address: string, userAgent: string){ + return await this.redisRepository.addUserBlacklistToRoom(roomId, address, userAgent); } } From 976ccff6960271351386d868290bd2f7142a3351 Mon Sep 17 00:00:00 2001 From: I3KAE <45356754+i3kae@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:40:14 +0900 Subject: [PATCH 11/22] =?UTF-8?q?chore:=20API=20reponse=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=B0=8F=20=EA=B8=B0=EB=B3=B8=20=EA=B3=B5=EC=A7=80?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/mainServer/src/common/constants.ts | 2 +- backend/mainServer/src/replay/replay.controller.ts | 4 ++-- backend/mainServer/src/streams/streams.controller.ts | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/mainServer/src/common/constants.ts b/backend/mainServer/src/common/constants.ts index a920a9c5..cede7f87 100644 --- a/backend/mainServer/src/common/constants.ts +++ b/backend/mainServer/src/common/constants.ts @@ -1,5 +1,5 @@ export const DEFAULT_VALUE = { THUMBNAIL_IMG_URL : 'https://kr.object.ncloudstorage.com/web22/static/liboo_default_thumbnail.png', - NOTICE : '쾌적한 컨퍼런스 환경을 위해 상대방을 존중하는 언어를 사용해 주시길 바랍니다. 모두가 배움과 소통을 즐길 수 있는 문화를 함께 만들기에 동참해주세요.', + NOTICE : '쾌적한 컨퍼런스 환경을 위해 상대방을 존중하는 언어를 사용해 주시길 바랍니다.\n모두가 배움과 소통을 즐길 수 있는 문화를 함께 만들기에 동참해주세요.', HOST_NAME : '방송 진행자', }; \ No newline at end of file diff --git a/backend/mainServer/src/replay/replay.controller.ts b/backend/mainServer/src/replay/replay.controller.ts index cf11f904..0430e60b 100644 --- a/backend/mainServer/src/replay/replay.controller.ts +++ b/backend/mainServer/src/replay/replay.controller.ts @@ -64,10 +64,10 @@ export class ReplayController { try { const replaySessions = this.memoryDBService.findAll().filter((info) => info.replay); if (replaySessions.some((info) => info.sessionKey === videoId)) { - res.status(HttpStatus.OK).json({exited: true}); + res.status(HttpStatus.OK).json({existed: true}); } else { - res.status(HttpStatus.OK).json({exited: false}); + res.status(HttpStatus.OK).json({existed: false}); } } catch (err) { console.log(err); diff --git a/backend/mainServer/src/streams/streams.controller.ts b/backend/mainServer/src/streams/streams.controller.ts index eeb65f53..b845224b 100644 --- a/backend/mainServer/src/streams/streams.controller.ts +++ b/backend/mainServer/src/streams/streams.controller.ts @@ -85,7 +85,7 @@ export class StreamsController { if (!sessionInfo) { throw new HttpException('Bad Request', HttpStatus.BAD_REQUEST); } - res.status(HttpStatus.OK).json({notice: sessionInfo.notice}); + res.status(HttpStatus.OK).json({notice: sessionInfo.notice, channelName: sessionInfo.channel.channelName}); } catch (error) { if ((error as { status: number }).status === 400) { res.status(HttpStatus.BAD_REQUEST).json({ @@ -106,10 +106,10 @@ export class StreamsController { try { const liveSessions = this.memoryDBService.findAll().filter((info) => info.state); if (liveSessions.some((info) => info.sessionKey === sessionKey)) { - res.status(HttpStatus.OK).json({exited: true}); + res.status(HttpStatus.OK).json({existed: true}); } else { - res.status(HttpStatus.OK).json({exited: false}); + res.status(HttpStatus.OK).json({existed: false}); } } catch (err) { console.log(err); From 583db3031bcdf68f8892e41db2e9b17bda75f2e2 Mon Sep 17 00:00:00 2001 From: I3KAE <45356754+i3kae@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:47:15 +0900 Subject: [PATCH 12/22] =?UTF-8?q?fix:=20=EC=B9=98=EC=A7=80=EC=A7=81=20mock?= =?UTF-8?q?=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EC=97=90=20=EB=A7=9E=EA=B2=8C=20?= =?UTF-8?q?=EC=84=B8=EC=85=98=20=ED=99=95=EC=9D=B8=20api=EA=B0=80=20?= =?UTF-8?q?=EB=8F=99=EC=9E=91=ED=95=98=EB=8F=84=EB=A1=9D=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 --- backend/mainServer/src/streams/streams.controller.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/mainServer/src/streams/streams.controller.ts b/backend/mainServer/src/streams/streams.controller.ts index b845224b..47cb3c5a 100644 --- a/backend/mainServer/src/streams/streams.controller.ts +++ b/backend/mainServer/src/streams/streams.controller.ts @@ -104,6 +104,10 @@ export class StreamsController { @ApiOperation({summary: 'Get Session exited', description: '방송 세션에 대한 존재 여부를 반환 받습니다.'}) async getExistence(@Query('sessionKey') sessionKey: string, @Res() res: Response) { try { + if (this.memoryDBService.chzzkSwitch && sessionKey in this.memoryDBService.chzzkDb) { + res.status(HttpStatus.OK).json({existed: true}); + return; + } const liveSessions = this.memoryDBService.findAll().filter((info) => info.state); if (liveSessions.some((info) => info.sessionKey === sessionKey)) { res.status(HttpStatus.OK).json({existed: true}); From 5ea5dedc192e423c15e41440ee676042b4e86174 Mon Sep 17 00:00:00 2001 From: I3KAE <45356754+i3kae@users.noreply.github.com> Date: Tue, 3 Dec 2024 09:29:07 +0900 Subject: [PATCH 13/22] =?UTF-8?q?feat:=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20url=EC=9D=84=20=EB=B0=A9=EC=86=A1?= =?UTF-8?q?=20=EC=8B=9C=EC=9E=91=20=EC=8B=9C=20=EC=82=BD=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/mainServer/src/host/host.controller.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/mainServer/src/host/host.controller.ts b/backend/mainServer/src/host/host.controller.ts index af27a0e8..0e85cb62 100644 --- a/backend/mainServer/src/host/host.controller.ts +++ b/backend/mainServer/src/host/host.controller.ts @@ -34,6 +34,7 @@ export class HostController { sessionInfo.replay = false; sessionInfo.startDate = new Date(); sessionInfo.streamUrl = `https://kr.object.ncloudstorage.com/web22/live/${sessionInfo.sessionKey}/index.m3u8`; + sessionInfo.liveImageUrl = `https://kr.object.ncloudstorage.com/web22/live/${sessionInfo.sessionKey}/thumbnail.png`; this.memoryDBService.updateBySessionKey(streamKey, sessionInfo); res.status(HttpStatus.OK).json({ 'session-key': sessionInfo.sessionKey }); } catch (error) { From ea72aa7bdc8d9553adb1ec76d9c8fc9259cc20fc Mon Sep 17 00:00:00 2001 From: I3KAE <45356754+i3kae@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:47:35 +0900 Subject: [PATCH 14/22] =?UTF-8?q?chore:=20=EB=8B=A4=EC=8B=9C=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0=20=EC=98=81=EC=83=81=EC=9D=B4=20=EB=93=B1=EB=A1=9D=20?= =?UTF-8?q?=EC=88=9C=EC=9C=BC=EB=A1=9C=20=EC=A0=95=EB=A0=AC=EB=90=98?= =?UTF-8?q?=EB=8F=84=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 --- backend/mainServer/src/memory-db/memory-db.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/mainServer/src/memory-db/memory-db.service.ts b/backend/mainServer/src/memory-db/memory-db.service.ts index 6b015f2e..4a58af1c 100644 --- a/backend/mainServer/src/memory-db/memory-db.service.ts +++ b/backend/mainServer/src/memory-db/memory-db.service.ts @@ -68,7 +68,7 @@ export class MemoryDBService { } getBroadcastInfo(size: number, dtoTransformer: (info: MemoryDbDto) => T, checker: (item: MemoryDbDto) => boolean, appender: number = 0) { - const findSession = this.db.filter(item => checker(item)); + const findSession = this.db.filter(item => checker(item)).sort((a: MemoryDbDto, b: MemoryDbDto) => a.endDate.getTime() - b.endDate.getTime()); if (findSession.length < size) { const findSessionRev = findSession.reverse().map((info) => dtoTransformer(info)); return [[...findSessionRev], []]; From d173cc7b7909dc6200dbf2b670f5175e54d97818 Mon Sep 17 00:00:00 2001 From: I3KAE <45356754+i3kae@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:00:32 +0900 Subject: [PATCH 15/22] =?UTF-8?q?refactor:=20node-media-server=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/rtmpServer/package.json | 2 +- yarn.lock | 28 +++++++++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/backend/rtmpServer/package.json b/backend/rtmpServer/package.json index 2dc36d0f..95b8fa84 100644 --- a/backend/rtmpServer/package.json +++ b/backend/rtmpServer/package.json @@ -3,7 +3,7 @@ "private": true, "packageManager": "yarn@4.5.1", "dependencies": { - "@hoeeeeeh/node-media-server": "3.0.2", + "@hoeeeeeh/node-media-server": "3.0.7", "@types/node": "^22.9.0", "dotenv": "^16.4.5", "path": "0.12.7" diff --git a/yarn.lock b/yarn.lock index 80f0bebe..1093b219 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2689,16 +2689,18 @@ __metadata: languageName: node linkType: hard -"@hoeeeeeh/node-media-server@npm:3.0.2": - version: 3.0.2 - resolution: "@hoeeeeeh/node-media-server@npm:3.0.2" +"@hoeeeeeh/node-media-server@npm:3.0.7": + version: 3.0.7 + resolution: "@hoeeeeeh/node-media-server@npm:3.0.7" dependencies: "@aws-sdk/client-s3": "npm:^3.688.0" "@types/node": "npm:^22.9.0" basic-auth-connect: "npm:^1.0.0" body-parser: "npm:1.20.3" chalk: "npm:^4.1.0" + chokidar: "npm:^4.0.1" dateformat: "npm:^4.6.3" + dotenv: "npm:^16.4.5" express: "npm:^4.19.2" http2-express-bridge: "npm:^1.0.7" lodash: "npm:^4.17.21" @@ -2707,7 +2709,7 @@ __metadata: ws: "npm:^8.13.0" bin: node-media-server: bin/app.js - checksum: 10c0/45b6c240e825e1e72a1100347c99b29bdb1462fb556a8af4337f6d134a5ab99eabb9e722056b314880e1f679d8609abfd866516f2f7bd7b7838880ce0ae0da22 + checksum: 10c0/54a26fddd0bef9bfc8f15c6a22d7019b379070501b793dd5a8482d434c3a9fc93e6e04428a83e1ec48a468102d7f360dcd5a371f946ea5ae4e4d6f129413bd31 languageName: node linkType: hard @@ -6236,6 +6238,15 @@ __metadata: languageName: node linkType: hard +"chokidar@npm:^4.0.1": + version: 4.0.1 + resolution: "chokidar@npm:4.0.1" + dependencies: + readdirp: "npm:^4.0.1" + checksum: 10c0/4bb7a3adc304059810bb6c420c43261a15bb44f610d77c35547addc84faa0374265c3adc67f25d06f363d9a4571962b02679268c40de07676d260de1986efea9 + languageName: node + linkType: hard + "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -11447,6 +11458,13 @@ __metadata: languageName: node linkType: hard +"readdirp@npm:^4.0.1": + version: 4.0.2 + resolution: "readdirp@npm:4.0.2" + checksum: 10c0/a16ecd8ef3286dcd90648c3b103e3826db2b766cdb4a988752c43a83f683d01c7059158d623cbcd8bdfb39e65d302d285be2d208e7d9f34d022d912b929217dd + languageName: node + linkType: hard + "readdirp@npm:~3.6.0": version: 3.6.0 resolution: "readdirp@npm:3.6.0" @@ -11831,7 +11849,7 @@ __metadata: resolution: "rtmpServer@workspace:backend/rtmpServer" dependencies: "@eslint/js": "npm:^9.13.0" - "@hoeeeeeh/node-media-server": "npm:3.0.2" + "@hoeeeeeh/node-media-server": "npm:3.0.7" "@types/node": "npm:^22.9.0" "@typescript-eslint/eslint-plugin": "npm:^8.14.0" "@typescript-eslint/parser": "npm:^8.14.0" From 4ab309e6f39949d6d8795294733754fc1d9a9760 Mon Sep 17 00:00:00 2001 From: I3KAE <45356754+i3kae@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:49:40 +0900 Subject: [PATCH 16/22] =?UTF-8?q?refactor:=20node=20media=20server?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EC=B6=B0=20=ED=83=80=EC=9E=85=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/memory-db/memory-db.service.ts | 6 +- backend/rtmpServer/src/types/index.d.ts | 104 ++++++++++++++++++ backend/rtmpServer/tsconfig.json | 3 +- 3 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 backend/rtmpServer/src/types/index.d.ts diff --git a/backend/mainServer/src/memory-db/memory-db.service.ts b/backend/mainServer/src/memory-db/memory-db.service.ts index 4a58af1c..942a01fc 100644 --- a/backend/mainServer/src/memory-db/memory-db.service.ts +++ b/backend/mainServer/src/memory-db/memory-db.service.ts @@ -68,7 +68,11 @@ export class MemoryDBService { } getBroadcastInfo(size: number, dtoTransformer: (info: MemoryDbDto) => T, checker: (item: MemoryDbDto) => boolean, appender: number = 0) { - const findSession = this.db.filter(item => checker(item)).sort((a: MemoryDbDto, b: MemoryDbDto) => a.endDate.getTime() - b.endDate.getTime()); + const findSession = this.db.filter(item => checker(item)).sort((a: MemoryDbDto, b: MemoryDbDto) => { + const aDate = a.endDate ? a.endDate.getTime() : 0; + const bDate = b.endDate ? b.endDate.getTime() : 0; + return aDate - bDate; + }); if (findSession.length < size) { const findSessionRev = findSession.reverse().map((info) => dtoTransformer(info)); return [[...findSessionRev], []]; diff --git a/backend/rtmpServer/src/types/index.d.ts b/backend/rtmpServer/src/types/index.d.ts new file mode 100644 index 00000000..9ed9ec9d --- /dev/null +++ b/backend/rtmpServer/src/types/index.d.ts @@ -0,0 +1,104 @@ +interface Config { + logType?: number; + rtmp?: RtmpConfig; + http?: HttpConfig; + https?: SslConfig; + trans?: TransConfig; + relay?: RelayConfig; + fission?: FissionConfig; + auth?: AuthConfig; +} + +interface RtmpConfig { + port?: number; + ssl?: SslConfig; + chunk_size?: number; + gop_cache?: boolean; + ping?: number; + ping_timeout?: number; +} + +interface SslConfig { + key: string; + cert: string; + port?: number; +} + +interface HttpConfig { + mediaroot: string; + port?: number; + allow_origin?: string; +} + +interface AuthConfig { + play?: boolean; + publish?: boolean; + secret?: string; + api?: boolean; + api_user?: string; + api_pass?: string; +} + +interface TransConfig { + ffmpeg: string; + tasks: TransTaskConfig[]; +} + +interface RelayConfig { + tasks: RelayTaskConfig[]; + ffmpeg: string; +} + +interface FissionConfig { + ffmpeg: string; + tasks: FissionTaskConfig[]; +} + +interface TransTaskConfig { + app: string; + hls?: boolean; + hlsFlags?: string; + dash?: boolean; + dashFlags?: string; + vc?: string; + vcParam?: string[]; + ac?: string; + acParam?: string[]; + rtmp?: boolean; + rtmpApp?: string; + mp4?: boolean; + mp4Flags?: string; +} + +interface RelayTaskConfig { + app: string; + name?: string; + mode: string; + edge: string; + rtsp_transport?: string; + appendName?: boolean; +} + +interface FissionTaskConfig { + rule: string; + model: FissionTaskModel[]; +} + +interface FissionTaskModel { + ab: string; + vb: string; + vs: string; + vf: string; +} + +declare class NodeMediaServer { + constructor(config: Config); + run(): void; + on(eventName: string, listener: (id: string, StreamPath: string, args: object) => void): void; + stop(): void; + getSession(id: string): Map; +} + +declare module '@hoeeeeeh/node-media-server' { + export default NodeMediaServer; +} \ No newline at end of file diff --git a/backend/rtmpServer/tsconfig.json b/backend/rtmpServer/tsconfig.json index 5bc14d4b..a6594915 100644 --- a/backend/rtmpServer/tsconfig.json +++ b/backend/rtmpServer/tsconfig.json @@ -3,5 +3,6 @@ "compilerOptions": { "baseUrl": ".", "outDir": "./.dist" - } + }, + "typeRoots": ["./src/types"] } From 4321e2af80ead711d70e6f9272c8c8d7874e16b9 Mon Sep 17 00:00:00 2001 From: I3KAE <45356754+i3kae@users.noreply.github.com> Date: Tue, 3 Dec 2024 19:25:04 +0900 Subject: [PATCH 17/22] =?UTF-8?q?feat:=20=EA=B0=9C=EB=B0=9C=EC=9E=90?= =?UTF-8?q?=EC=9A=A9=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/mainServer/src/dto/memoryDbDto.ts | 4 ++-- backend/mainServer/src/memory-db/memory-db.service.ts | 8 ++++++++ .../mainServer/src/mock-data/mock-data.controller.ts | 8 ++++++++ backend/mainServer/src/mock-data/mock-data.service.ts | 10 ++++++++++ backend/rtmpServer/package.json | 2 +- yarn.lock | 10 +++++----- 6 files changed, 34 insertions(+), 8 deletions(-) diff --git a/backend/mainServer/src/dto/memoryDbDto.ts b/backend/mainServer/src/dto/memoryDbDto.ts index 313c18cd..3083a6b0 100644 --- a/backend/mainServer/src/dto/memoryDbDto.ts +++ b/backend/mainServer/src/dto/memoryDbDto.ts @@ -147,10 +147,10 @@ export class MemoryDbDto { readCount: number = 0; @ApiProperty({ - description: '라이브 우선 순위', + description: '라이브 당시 시청횟수', example: 0, }) - livePr: number = 0; + livePr: number = Math.floor(Math.random() * 1000) + 100; constructor(data?: Partial) { if (data) { diff --git a/backend/mainServer/src/memory-db/memory-db.service.ts b/backend/mainServer/src/memory-db/memory-db.service.ts index 942a01fc..116e19ed 100644 --- a/backend/mainServer/src/memory-db/memory-db.service.ts +++ b/backend/mainServer/src/memory-db/memory-db.service.ts @@ -103,6 +103,14 @@ export class MemoryDBService { return true; } + updateById(id: number, updatedItem: Partial): boolean { + const index = this.db.findIndex(item => Number(item.id) === Number(id)); + if (index === -1) return false; + console.log(this.db[index]); + this.db[index] = new MemoryDbDto({ ...this.db[index], ...updatedItem }); + return true; + } + updateBySessionKey(sessionKey: string, updatedItem: Partial): boolean { const index = this.db.findIndex(item => item.sessionKey === sessionKey); if (index === -1) return false; diff --git a/backend/mainServer/src/mock-data/mock-data.controller.ts b/backend/mainServer/src/mock-data/mock-data.controller.ts index f95e3331..286bbf21 100644 --- a/backend/mainServer/src/mock-data/mock-data.controller.ts +++ b/backend/mainServer/src/mock-data/mock-data.controller.ts @@ -45,6 +45,14 @@ export class MockDataController { this.memoryDbService.create(newData); } + @Post('/update') + @ApiOperation({ summary: 'Delete Session Info', description: '방송 정보를 삭제합니다. (start만 적으면 단일, start, end 범위를 적으면 범위 삭제)' }) + async updateMemodyData(@Query('id') id: number, @Body() newData: MemoryDbDto) { + console.log(id); + console.log(newData); + this.memoryDbService.updateById(id, newData); + } + @Get('/chzzk/switch') @ApiOperation({summary: 'Change Curation Data', description: '메인 랜덤 영상을 치지직 영상으로 대체합니다. (true: mode On, false: mode off 전환 시 기존 치지직 데이터 초기화)'}) async changeCurationData(@Res() res: Response) { diff --git a/backend/mainServer/src/mock-data/mock-data.service.ts b/backend/mainServer/src/mock-data/mock-data.service.ts index 5d53132c..9f1d4e2e 100644 --- a/backend/mainServer/src/mock-data/mock-data.service.ts +++ b/backend/mainServer/src/mock-data/mock-data.service.ts @@ -13,6 +13,7 @@ export class MockDataService implements OnModuleInit { liveTitle: 'Tech Conference 2024', category: 'Technology', defaultThumbnailImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test1_thumbnail.png', + liveImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test1-live-image.png', tags: ['Conference', 'Tech', '2024'], startDate: new Date('2024-11-21T09:00:00'), endDate: new Date('2024-11-21T11:00:00'), @@ -30,6 +31,7 @@ export class MockDataService implements OnModuleInit { liveTitle: 'DAN24', category: 'Art', defaultThumbnailImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test2_thumbnail.png', + liveImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test2-live-image.png', tags: ['Dan', 'Showcase', 'Art'], startDate: new Date('2024-11-21T12:00:00'), endDate: new Date('2024-11-21T14:00:00'), @@ -47,6 +49,7 @@ export class MockDataService implements OnModuleInit { liveTitle: 'Gaming Tournament Finals', category: 'Gaming', defaultThumbnailImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test3_thumbnail.png', + liveImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test3-live-image.png', tags: ['Gaming', 'Esports', 'Finals'], startDate: new Date('2024-11-21T15:00:00'), endDate: new Date('2024-11-21T18:00:00'), @@ -64,6 +67,7 @@ export class MockDataService implements OnModuleInit { liveTitle: 'Music Live Show', category: 'Music', defaultThumbnailImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test4_thumbnail.png', + liveImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test4-live-image.png', tags: ['Music', 'Live', 'Concert'], startDate: new Date('2024-11-21T19:00:00'), endDate: new Date('2024-11-21T21:00:00'), @@ -81,6 +85,7 @@ export class MockDataService implements OnModuleInit { liveTitle: 'Cooking with Pros', category: 'Food', defaultThumbnailImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test5_thumbnail.png', + liveImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test5-live-image.png', tags: ['Cooking', 'Food', 'Recipes'], startDate: new Date('2024-11-22T12:00:00'), endDate: new Date('2024-11-22T14:00:00'), @@ -98,6 +103,7 @@ export class MockDataService implements OnModuleInit { liveTitle: 'Tech Conference 2024', category: 'Technology', defaultThumbnailImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test6_thumbnail.png', + liveImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test6-live-image.png', tags: ['Tech', 'Conference', 'Innovation'], startDate: new Date('2024-11-22T15:00:00'), endDate: new Date('2024-11-22T18:00:00'), @@ -115,6 +121,7 @@ export class MockDataService implements OnModuleInit { liveTitle: 'Art Masterclass', category: 'Art', defaultThumbnailImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test7_thumbnail.png', + liveImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test7-live-image.png', tags: ['Art', 'Painting', 'Creative'], startDate: new Date('2024-11-23T10:00:00'), endDate: new Date('2024-11-23T12:00:00'), @@ -132,6 +139,7 @@ export class MockDataService implements OnModuleInit { liveTitle: 'Fitness Live Session', category: 'Health', defaultThumbnailImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test8_thumbnail.png', + liveImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test8-live-image.png', tags: ['Fitness', 'Health', 'Workout'], startDate: new Date('2024-11-23T16:00:00'), endDate: new Date('2024-11-23T17:00:00'), @@ -149,6 +157,7 @@ export class MockDataService implements OnModuleInit { liveTitle: 'Travel Vlog Live', category: 'Travel', defaultThumbnailImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test9_thumbnail.png', + liveImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test9-live-image.png', tags: ['Travel', 'Adventure', 'Vlog'], startDate: new Date('2024-11-24T09:00:00'), endDate: new Date('2024-11-24T11:00:00'), @@ -166,6 +175,7 @@ export class MockDataService implements OnModuleInit { liveTitle: 'Replay Title', category: 'Replay Category', defaultThumbnailImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test10_thumbnail.png', + liveImageUrl: 'https://kr.object.ncloudstorage.com/web22/static/test10-live-image.png', tags: ['replay', '다시보기'], startDate: new Date(Date.now() - 60 * 60 * 1000), // 1 hour ago endDate: new Date(Date.now() - 30 * 60 * 1000), // 30 minutes ago diff --git a/backend/rtmpServer/package.json b/backend/rtmpServer/package.json index 95b8fa84..b47533ed 100644 --- a/backend/rtmpServer/package.json +++ b/backend/rtmpServer/package.json @@ -3,7 +3,7 @@ "private": true, "packageManager": "yarn@4.5.1", "dependencies": { - "@hoeeeeeh/node-media-server": "3.0.7", + "@hoeeeeeh/node-media-server": "3.0.9", "@types/node": "^22.9.0", "dotenv": "^16.4.5", "path": "0.12.7" diff --git a/yarn.lock b/yarn.lock index 1093b219..f4148e5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2689,9 +2689,9 @@ __metadata: languageName: node linkType: hard -"@hoeeeeeh/node-media-server@npm:3.0.7": - version: 3.0.7 - resolution: "@hoeeeeeh/node-media-server@npm:3.0.7" +"@hoeeeeeh/node-media-server@npm:3.0.9": + version: 3.0.9 + resolution: "@hoeeeeeh/node-media-server@npm:3.0.9" dependencies: "@aws-sdk/client-s3": "npm:^3.688.0" "@types/node": "npm:^22.9.0" @@ -2709,7 +2709,7 @@ __metadata: ws: "npm:^8.13.0" bin: node-media-server: bin/app.js - checksum: 10c0/54a26fddd0bef9bfc8f15c6a22d7019b379070501b793dd5a8482d434c3a9fc93e6e04428a83e1ec48a468102d7f360dcd5a371f946ea5ae4e4d6f129413bd31 + checksum: 10c0/cfb1a07295d85c9d7088cddfdd9208e4311f25d905baa97d4a80d3d53844309362e322ff79d7774b3ac372425bc232689764c451590c829f6022920ebf2d1b7d languageName: node linkType: hard @@ -11849,7 +11849,7 @@ __metadata: resolution: "rtmpServer@workspace:backend/rtmpServer" dependencies: "@eslint/js": "npm:^9.13.0" - "@hoeeeeeh/node-media-server": "npm:3.0.7" + "@hoeeeeeh/node-media-server": "npm:3.0.9" "@types/node": "npm:^22.9.0" "@typescript-eslint/eslint-plugin": "npm:^8.14.0" "@typescript-eslint/parser": "npm:^8.14.0" From a410a7a2c182ec3bb991dc3a470cdf51bd260581 Mon Sep 17 00:00:00 2001 From: I3KAE <45356754+i3kae@users.noreply.github.com> Date: Tue, 3 Dec 2024 19:53:29 +0900 Subject: [PATCH 18/22] =?UTF-8?q?fix:=20rtmp=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20-=20=EB=B3=80=EC=88=98=20=EB=B9=A0?= =?UTF-8?q?=EC=A0=B8=EC=9E=88=EC=97=88=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/rtmpServer/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/rtmpServer/package.json b/backend/rtmpServer/package.json index b47533ed..c9864822 100644 --- a/backend/rtmpServer/package.json +++ b/backend/rtmpServer/package.json @@ -3,7 +3,7 @@ "private": true, "packageManager": "yarn@4.5.1", "dependencies": { - "@hoeeeeeh/node-media-server": "3.0.9", + "@hoeeeeeh/node-media-server": "3.0.10", "@types/node": "^22.9.0", "dotenv": "^16.4.5", "path": "0.12.7" diff --git a/yarn.lock b/yarn.lock index f4148e5a..622f2c6a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2689,9 +2689,9 @@ __metadata: languageName: node linkType: hard -"@hoeeeeeh/node-media-server@npm:3.0.9": - version: 3.0.9 - resolution: "@hoeeeeeh/node-media-server@npm:3.0.9" +"@hoeeeeeh/node-media-server@npm:3.0.10": + version: 3.0.10 + resolution: "@hoeeeeeh/node-media-server@npm:3.0.10" dependencies: "@aws-sdk/client-s3": "npm:^3.688.0" "@types/node": "npm:^22.9.0" @@ -2709,7 +2709,7 @@ __metadata: ws: "npm:^8.13.0" bin: node-media-server: bin/app.js - checksum: 10c0/cfb1a07295d85c9d7088cddfdd9208e4311f25d905baa97d4a80d3d53844309362e322ff79d7774b3ac372425bc232689764c451590c829f6022920ebf2d1b7d + checksum: 10c0/ac5a520a8465d735dc6dcd7c9152da33f4a21d9db18447939df410c5797666f2b14c84675f53b75f7c22841063c205f52d24092b25a69c062e0537e41a37e4d4 languageName: node linkType: hard @@ -11849,7 +11849,7 @@ __metadata: resolution: "rtmpServer@workspace:backend/rtmpServer" dependencies: "@eslint/js": "npm:^9.13.0" - "@hoeeeeeh/node-media-server": "npm:3.0.9" + "@hoeeeeeh/node-media-server": "npm:3.0.10" "@types/node": "npm:^22.9.0" "@typescript-eslint/eslint-plugin": "npm:^8.14.0" "@typescript-eslint/parser": "npm:^8.14.0" From 835058b5ba8495710a2938b538974827fa16be9d Mon Sep 17 00:00:00 2001 From: I3KAE <45356754+i3kae@users.noreply.github.com> Date: Tue, 3 Dec 2024 20:12:30 +0900 Subject: [PATCH 19/22] =?UTF-8?q?refactor:=20=EB=9D=BC=EC=9D=B4=EB=B8=8C?= =?UTF-8?q?=20/=20=EB=8B=A4=EC=8B=9C=EB=B3=B4=EA=B8=B0=EA=B0=80=20?= =?UTF-8?q?=EA=B0=81=EA=B0=81=20=EC=8B=9C=EC=9E=91=20=EB=82=A0=EC=A7=9C,?= =?UTF-8?q?=20=EC=A2=85=EB=A3=8C=20=EB=82=A0=EC=A7=9C=20=EC=88=9C=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=95=EB=A0=AC=EB=90=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 --- backend/mainServer/src/memory-db/memory-db.service.ts | 8 ++------ backend/mainServer/src/replay/replay.controller.ts | 7 ++++++- backend/mainServer/src/streams/streams.controller.ts | 7 ++++++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/backend/mainServer/src/memory-db/memory-db.service.ts b/backend/mainServer/src/memory-db/memory-db.service.ts index 116e19ed..33a45cdc 100644 --- a/backend/mainServer/src/memory-db/memory-db.service.ts +++ b/backend/mainServer/src/memory-db/memory-db.service.ts @@ -67,12 +67,8 @@ export class MemoryDBService { return getRandomElementsFromArray(liveSession, count); } - getBroadcastInfo(size: number, dtoTransformer: (info: MemoryDbDto) => T, checker: (item: MemoryDbDto) => boolean, appender: number = 0) { - const findSession = this.db.filter(item => checker(item)).sort((a: MemoryDbDto, b: MemoryDbDto) => { - const aDate = a.endDate ? a.endDate.getTime() : 0; - const bDate = b.endDate ? b.endDate.getTime() : 0; - return aDate - bDate; - }); + getBroadcastInfo(size: number, dtoTransformer: (info: MemoryDbDto) => T, checker: (item: MemoryDbDto) => boolean, compare: (a: MemoryDbDto, b: MemoryDbDto) => number, appender: number = 0) { + const findSession = this.db.filter(item => checker(item)).sort((a: MemoryDbDto, b: MemoryDbDto) => compare(a, b)); if (findSession.length < size) { const findSessionRev = findSession.reverse().map((info) => dtoTransformer(info)); return [[...findSessionRev], []]; diff --git a/backend/mainServer/src/replay/replay.controller.ts b/backend/mainServer/src/replay/replay.controller.ts index 0430e60b..59d004d7 100644 --- a/backend/mainServer/src/replay/replay.controller.ts +++ b/backend/mainServer/src/replay/replay.controller.ts @@ -17,7 +17,12 @@ export class ReplayController { async getLatestReplay(@Res() res: Response) { try { const replayChecker = (item: MemoryDbDto) => { return item.replay && !item.state; }; - const [serchedData, appendData] = this.memoryDBService.getBroadcastInfo(8, memoryDbDtoToReplayVideoDto, replayChecker, 8); + const compare = (a: MemoryDbDto, b: MemoryDbDto) => { + const aTime = a.startDate ? a.startDate.getTime() : 0; + const bTime = b.startDate ? b.startDate.getTime() : 0; + return aTime - bTime; + }; + const [serchedData, appendData] = this.memoryDBService.getBroadcastInfo(8, memoryDbDtoToReplayVideoDto, replayChecker, compare, 8); res.status(HttpStatus.OK).json({info: serchedData, appendInfo: appendData}); } catch (error) { if ((error as { status: number }).status === 400) { diff --git a/backend/mainServer/src/streams/streams.controller.ts b/backend/mainServer/src/streams/streams.controller.ts index 47cb3c5a..ed043f59 100644 --- a/backend/mainServer/src/streams/streams.controller.ts +++ b/backend/mainServer/src/streams/streams.controller.ts @@ -37,7 +37,12 @@ export class StreamsController { async getLatestSession(@Res() res: Response) { try { const streamChecker = (item: MemoryDbDto) => item.state; - const [serchedData, appendData] = this.memoryDBService.getBroadcastInfo(8, fromLiveSessionDto, streamChecker, 8); + const compare = (a: MemoryDbDto, b: MemoryDbDto) => { + const aTime = a.startDate ? a.startDate.getTime() : 0; + const bTime = b.startDate ? b.startDate.getTime() : 0; + return aTime - bTime; + }; + const [serchedData, appendData] = this.memoryDBService.getBroadcastInfo(8, fromLiveSessionDto, streamChecker, compare, 8); res.status(HttpStatus.OK).json({info: serchedData, appendInfo: appendData}); } catch (error) { if ((error as { status: number }).status === 400) { From ffc215652bfb45cb290f0bd295356e33751e5ae3 Mon Sep 17 00:00:00 2001 From: hoeeeeeh Date: Wed, 4 Dec 2024 12:00:53 +0900 Subject: [PATCH 20/22] =?UTF-8?q?refactor:=20chat=20server=20=EC=9D=98=20U?= =?UTF-8?q?ser=20=EB=A5=BC=20redis=20=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chatServer/src/room/room.repository.ts | 20 +++++++++++++++++++ backend/chatServer/src/room/room.service.ts | 12 ++++------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/backend/chatServer/src/room/room.repository.ts b/backend/chatServer/src/room/room.repository.ts index 16cf757a..61c78e65 100644 --- a/backend/chatServer/src/room/room.repository.ts +++ b/backend/chatServer/src/room/room.repository.ts @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'; import { Cluster } from 'ioredis'; import { QuestionDto } from '../event/dto/Question.dto'; import { ChatException, CHATTING_SOCKET_ERROR } from '../chat/chat.error'; +import { User } from './user.interface'; type USER_AGENT = string; @@ -12,11 +13,16 @@ export class RoomRepository { questionPrefix = 'question'; questionIdPrefix = 'id'; blacklistPrefix = 'blacklist'; + userPrefix = 'user'; injectClient(redisClient: Cluster){ this.redisClient = redisClient; } + private getUserStringWithPrefix(clientId: string){ + return `${this.userPrefix}:${clientId}`; + } + private getRoomStringWithPrefix(roomId: string) { return `${this.roomIdPrefix}${roomId}`; } @@ -70,6 +76,20 @@ export class RoomRepository { } } + async createUser(clientId: string, user: User){ + return !! await this.redisClient.set(this.getUserStringWithPrefix(clientId), JSON.stringify(user)); + } + + async getUser(clientId: string) { + const user = await this.getData(this.getUserStringWithPrefix(clientId)); + if(!user) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER); + return user; + } + + async deleteUser(clientId: string){ + return !! await this.redisClient.del(this.getUserStringWithPrefix(clientId)); + } + async isRoomExisted(roomId: string) { return !! await this.redisClient.exists(this.getRoomStringWithPrefix(roomId)); } diff --git a/backend/chatServer/src/room/room.service.ts b/backend/chatServer/src/room/room.service.ts index cfa9caf6..b546d4e5 100644 --- a/backend/chatServer/src/room/room.service.ts +++ b/backend/chatServer/src/room/room.service.ts @@ -36,7 +36,6 @@ function createRandomUserInstance(): User { export class RoomService implements OnModuleInit, OnModuleDestroy { redisAdapter: ReturnType; redisClient: Cluster; - users: Map = new Map(); constructor(private redisRepository: RoomRepository) { this.redisClient = new Redis.Cluster(REDIS_CONFIG); @@ -142,22 +141,19 @@ export class RoomService implements OnModuleInit, OnModuleDestroy { // 유저 생성 async createUser(clientId: string) { const newUser = createRandomUserInstance(); - this.users.set(clientId, newUser); + const isCreatedDone = await this.redisRepository.createUser(clientId, newUser); + if(!isCreatedDone) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER); return newUser; } // 유저 삭제 async deleteUser(clientId: string) { - const user = this.users.get(clientId); - if (!user) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER); - this.users.delete(clientId); - return user; + return await this.redisRepository.deleteUser(clientId); } // 특정 유저 조회 async getUserByClientId(clientId: string) { - const user = this.users.get(clientId); - if (!user) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER); + const user = this.redisRepository.getUser(clientId); return user; } From 307832d125929c25646da7805ab5b53dcd040a12 Mon Sep 17 00:00:00 2001 From: hoeeeeeh Date: Wed, 4 Dec 2024 13:20:12 +0900 Subject: [PATCH 21/22] =?UTF-8?q?refactor:=20chat=20server=20blacklist=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95=20refactor:=20chat=20se?= =?UTF-8?q?rver=20=EC=9D=98=20blacklist=20user=20=EB=A5=BC=20=EA=B0=80?= =?UTF-8?q?=EC=A0=B8=EC=98=AC=20=EB=95=8C,=20server.handshake=20=EB=A5=BC?= =?UTF-8?q?=20=EC=B0=B8=EC=A1=B0=ED=95=98=EB=8A=94=20=EA=B2=83=EC=97=90?= =?UTF-8?q?=EC=84=9C=20user=20=EC=97=90=20=EB=AF=B8=EB=A6=AC=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=ED=95=B4=EB=86=93=EA=B3=A0=20user=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/chatServer/src/chat/chat.gateway.ts | 24 +++++++++++-------- backend/chatServer/src/room/room.service.ts | 16 ++++++++++--- backend/chatServer/src/room/user.interface.ts | 2 ++ 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/backend/chatServer/src/chat/chat.gateway.ts b/backend/chatServer/src/chat/chat.gateway.ts index 93211dfd..e13c9dae 100644 --- a/backend/chatServer/src/chat/chat.gateway.ts +++ b/backend/chatServer/src/chat/chat.gateway.ts @@ -47,7 +47,7 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa async handleConnection(client: Socket) { console.log(`Client connected: ${client.id}`); - const user = await this.roomService.createUser(client.id); + const user = await this.roomService.createUser(client); console.log(user); /* @@ -102,7 +102,9 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa const user = await this.roomService.getUserByClientId(client.id); const normalOutgoingMessage: Omit = { roomId, - ...user, + nickname: user.nickname, + color: user.color, + entryTime: user.entryTime, msg, msgTime: new Date().toISOString(), msgType: 'normal', @@ -131,7 +133,9 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa const user = await this.roomService.getUserByClientId(client.id); const questionWithoutId: Omit = { roomId, - ...user, + nickname: user.nickname, + color: user.color, + entryTime: user.entryTime, msg, msgTime: new Date().toISOString(), msgType: 'question', @@ -161,7 +165,9 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa const user = await this.roomService.getUserByClientId(client.id); const noticeOutgoingMessage: NoticeOutgoingMessageDto = { roomId, - ...user, + nickname: user.nickname, + color: user.color, + entryTime: user.entryTime, msg, msgTime: new Date().toISOString(), msgType: 'notice' @@ -173,12 +179,10 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa @SubscribeMessage(CHATTING_SOCKET_DEFAULT_EVENT.BAN_USER) async handleBanUserMessage(@ConnectedSocket() client: Socket, @MessageBody() payload: BanUserIncomingMessageDto) { const { roomId, socketId } = payload; - const banUser = this.server.sockets.sockets.get(socketId); - const address = banUser?.handshake.address.replaceAll('::ffff:', ''); - - if(!address) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER, roomId); - - const userAgent = banUser?.handshake.headers['user-agent']; + const banUser = await this.roomService.getUserByClientId(socketId); + console.log('banUSer = ', banUser); + if(!banUser) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER, roomId); + const { address, userAgent } = banUser; if(!userAgent) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER, roomId); await this.roomService.addUserToBlacklist(roomId, address, userAgent); diff --git a/backend/chatServer/src/room/room.service.ts b/backend/chatServer/src/room/room.service.ts index b546d4e5..8b0a0553 100644 --- a/backend/chatServer/src/room/room.service.ts +++ b/backend/chatServer/src/room/room.service.ts @@ -11,6 +11,7 @@ import path from 'path'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; import { ChatException, CHATTING_SOCKET_ERROR } from '../chat/chat.error'; +import { Socket } from 'socket.io'; // 현재 파일의 URL을 파일 경로로 변환 const __filename = fileURLToPath(import.meta.url); @@ -24,8 +25,10 @@ function createRandomNickname(){ return `${getRandomAdjective()} ${getRandomNoun()}`; } -function createRandomUserInstance(): User { +function createRandomUserInstance(address: string, userAgent: string): User { return { + address, + userAgent, nickname: createRandomNickname(), color: getRandomBrightColor(), entryTime: new Date().toISOString() @@ -139,10 +142,17 @@ export class RoomService implements OnModuleInit, OnModuleDestroy { } // 유저 생성 - async createUser(clientId: string) { - const newUser = createRandomUserInstance(); + async createUser(socket: Socket) { + const clientId = socket.id; + const address = socket.handshake.address.replaceAll('::ffff:', ''); + const userAgent = socket.handshake.headers['user-agent']; + + if(!address || !userAgent) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER); + + const newUser = createRandomUserInstance(address, userAgent); const isCreatedDone = await this.redisRepository.createUser(clientId, newUser); if(!isCreatedDone) throw new ChatException(CHATTING_SOCKET_ERROR.INVALID_USER); + console.log(newUser); return newUser; } diff --git a/backend/chatServer/src/room/user.interface.ts b/backend/chatServer/src/room/user.interface.ts index cba40228..ad6f82cb 100644 --- a/backend/chatServer/src/room/user.interface.ts +++ b/backend/chatServer/src/room/user.interface.ts @@ -1,4 +1,6 @@ interface User { + address: string; + userAgent: string; entryTime: string; nickname: string; color: string; From c1e14b40734b2d624f23db36e62b81dae6e5f118 Mon Sep 17 00:00:00 2001 From: hoeeeeeh Date: Wed, 4 Dec 2024 16:59:27 +0900 Subject: [PATCH 22/22] feat: check heartbeat --- backend/chatServer/src/chat/chat.gateway.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/chatServer/src/chat/chat.gateway.ts b/backend/chatServer/src/chat/chat.gateway.ts index e13c9dae..ceaadb69 100644 --- a/backend/chatServer/src/chat/chat.gateway.ts +++ b/backend/chatServer/src/chat/chat.gateway.ts @@ -30,7 +30,11 @@ import { import { QuestionDto } from '../event/dto/Question.dto'; import { ChatException, CHATTING_SOCKET_ERROR } from './chat.error'; -@WebSocketGateway({ cors: true }) +@WebSocketGateway({ + cors: true, + pingInterval: 30000, + pingTimeout: 10000, +}) export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect { constructor(private roomService: RoomService) {};