Skip to content

Commit

Permalink
WIP: add some channel socket logics
Browse files Browse the repository at this point in the history
  • Loading branch information
cjho0316 committed Dec 13, 2023
1 parent 67def88 commit 41dae03
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 41 deletions.
2 changes: 2 additions & 0 deletions src/channels/channel-invitation.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,7 @@ export class ChannelInvitationRepository extends Repository<ChannelInvitation> {
const result = await this.save(channelInvitation);
if (!result)
throw DBUpdateFailureException('create channel invitation failed');

return result;
}
}
23 changes: 4 additions & 19 deletions src/channels/channels.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Body,
Controller,
Get,
Logger,
Param,
ParseIntPipe,
Patch,
Expand Down Expand Up @@ -69,14 +70,6 @@ export class ChannelsController {
const createChannelResponseDto =
await this.channelsService.createChannel(user.id, channelInfo);

// channel room에 join
if (user.channelSocketId) {
this.channelsGateway.joinChannelRoom(
createChannelResponseDto.channelId.toString(),
user.channelSocketId,
);
}

return createChannelResponseDto;
}

Expand Down Expand Up @@ -139,15 +132,8 @@ export class ChannelsController {

const channelUsersResponseDto =
await this.channelsService.createChannelUser(channelUserParamDto);

if (user.channelSocketId) {
this.channelsGateway.joinChannelRoom(
channelId.toString(),
user.channelSocketId,
);
}

return channelUsersResponseDto;

return channelUsersResponseDto ;
}

@Patch('/exit')
Expand All @@ -168,8 +154,7 @@ export class ChannelsController {
@ApiOperation({ summary: '유저 초대', description: '유저 초대' })
async createChannelInvitation(
@GetUser() user: User,
@Body()
createInvitationRequestDto: CreateInvitationRequestDto,
@Body() createInvitationRequestDto: CreateInvitationRequestDto,
) {
const invitingUserId = user.id;
const channelId = createInvitationRequestDto.channelId;
Expand Down
71 changes: 53 additions & 18 deletions src/channels/channels.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { UsersRepository } from 'src/users/users.repository';
import { ChannelUsersRepository } from './channel-users.repository';
import { EventMessageOnDto } from './dto/event-message-on.dto';
import { GatewayCreateInvitationParamDto } from '../game/dto/gateway-create-invitation-param.dto';
import { CreateInvitationParamDto } from './dto/create-invitation-param.dto';

@WebSocketGateway({ namespace: 'channels' })
@UseFilters(WsExceptionFilter)
Expand Down Expand Up @@ -51,6 +52,7 @@ export class ChannelsGateway

async handleConnection(@ConnectedSocket() client: Socket, ...args: any[]) {
// Socket으로부터 user 정보를 가져온다.

const user = await this.authService.getUserFromSocket(client);
if (!user || !client.id || user.channelSocketId) {
return client.disconnect();
Expand Down Expand Up @@ -129,16 +131,14 @@ export class ChannelsGateway
@ConnectedSocket() client: Socket,
@MessageBody() data: EventMessageOnDto,
) {
this.logger.log(`handleMessage: ${JSON.stringify(data)}`); // TODO: test code. 추후 삭제

// Socket으로부터 user 정보를 가져온다.
const user = await this.authService.getUserFromSocket(client);

if (!user || user.channelSocketId !== client.id) {
throw WSBadRequestException('유저 정보가 일치하지 않습니다.'); // TODO: exception 발생해도 서버 죽지 않는지 확인
}

const { channelId, message } = data;
const { channelId, message, notice } = data;

// 채널유저 유효성 검사
const channelUser = await this.channelUsersRepository.findOne({
Expand All @@ -151,18 +151,18 @@ export class ChannelsGateway
const eventMessageEmitDto = {
nickname: user.nickname,
message,
channelId,
notice,
};
// mute된 유저의 socketId List를 가져온다.
const muteRedisKey = `mute:${channelId}:${user.id}`;
// console.log('muteRedisKey:', muteRedisKey);
const isMutedChannelUser = await this.redis.exists(muteRedisKey);

if (isMutedChannelUser) {
throw WSBadRequestException('채널에서 음소거 되었습니다.');
}
// console.log('muteRedisKey:', muteRedisKey);

// 채널에 메시지를 emit한다.
// 어떤 user가 보내는지 찾아주기 위해 emit한다. (nickname, message, channelId, notice)
this.server
.to(channelId.toString())
.emit('message', eventMessageEmitDto);
Expand All @@ -177,25 +177,13 @@ export class ChannelsGateway
const socket = (await this.server.fetchSockets()).find(
(s) => s.id === channelSocketId,
);

if (!socket) {
return WSBadRequestException('socket이 존재하지 않습니다.');
}

this.logger.log(`socket.id: `, socket.id);
socket.join(channelRoomName);
}

@SubscribeMessage('ClientToServer') // TODO: test용 코드. 추후 삭제
handleMessageTest(
@ConnectedSocket() client: Socket,
@MessageBody() data: string,
) {
this.logger.log(`client TEST: `, client);
this.logger.log('ClientToServer: ', data);
this.server.emit('ServerToClient', 'Hello Client!');
}

async inviteGame(
gatewayInvitationParamDto: GatewayCreateInvitationParamDto,
) {
Expand Down Expand Up @@ -230,4 +218,51 @@ export class ChannelsGateway
gameType: gatewayInvitationParamDto.gameType,
});
}

//소켓 연결 해제 시, 채널 룸에서 leave하는 메서드
@SubscribeMessage('leaveChannelRoom')
async handleleaveChannelRoom(
@ConnectedSocket() client: Socket,
@MessageBody() data: { channelId: number },
) {
const user = await this.authService.getUserFromSocket(client);
if (!user || !client.id || user.channelSocketId !== client.id) {
return WSBadRequestException('유저 정보가 일치하지 않습니다.');
}

const { channelId } = data;

// 채널유저 유효성 검사
const channelUser = await this.channelUsersRepository.findOne({
where: { userId: user.id, channelId, isBanned: false },
});
if (!channelUser) {
throw WSBadRequestException('채널에 속해있지 않습니다.');
}
// 채널에 leave한다.
client.leave(channelId.toString());

this.server
.to(channelId.toString())
.emit('message', `${user.nickname}님이 퇴장하셨습니다.`);

// 어느 채널에 퇴장했는지 알려주기 위해 front에 emit한다.
client
.emit('leaveChannelRoom', channelId.toString());
}

// 알람 구현을 위한 메소드(한명에게만 알람)
async PrivateAlert(
data: { invitedUserId: number, invitationId: number }
) {
const invitedUser = await this.usersRepository.findOne({
where: { id: data.invitedUserId },
});
const invitationId = data.invitationId;

if (!invitedUser || invitedUser.status === UserStatus.OFFLINE || !invitedUser.channelSocketId) {
throw WSBadRequestException('유저가 유효하지 않습니다.');
}
this.server.to(invitedUser.channelSocketId).emit('privateAlert', invitationId);
}
}
26 changes: 22 additions & 4 deletions src/channels/channels.service.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ChannelsGateway } from './channels.gateway';
import { InjectRedis } from '@liaoliaots/nestjs-redis';
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
import * as bycrypt from 'bcrypt';
Expand All @@ -20,6 +21,7 @@ import { CreateInvitationParamDto } from './dto/create-invitation-param.dto';
import { DmChannelListResponseDto } from './dto/dmchannel-list-response.dto';
import { DmChannelListReturnDto } from './dto/dmchannel-list-return.dto';
import { UpdateChannelPwdParamDto } from './dto/update-channel-pwd-param.dto';
import { GatewayCreateChannelInvitationParamDto } from 'src/game/dto/gateway-create-channelInvitation-param-dto';

@Injectable()
export class ChannelsService {
Expand All @@ -28,6 +30,7 @@ export class ChannelsService {
private readonly channelUsersRepository: ChannelUsersRepository,
private readonly channelInvitationRepository: ChannelInvitationRepository,
private readonly usersRepository: UsersRepository,
private readonly ChannelsGateway: ChannelsGateway,
@InjectRedis() private readonly redis: Redis,
) {}

Expand Down Expand Up @@ -99,8 +102,20 @@ export class ChannelsService {
userId,
channel.id,
);
// // 채널에 소환된 유저에게 알람 전송. DM의 경우에만 해당
// if (channel.channelType === ChannelType.DM) {
// console.log(`channelInfo.userId: ${channelInfo.userId}`)
// const targetUser = await this.usersRepository.findOne({
// where: { id: channelInfo.userId },
// });
// if (targetUser?.channelSocketId) {
// this.ChannelsGateway.sendChannelAlert(
// channel.id,
// [targetUser.channelSocketId],
// );
// }
// }

// TODO: 채널에 소환된 유저에게 알림 전송. DM의 경우에만 해당
// TODO: cache에 user count 저장
this.logger.log(
`channel ${channel.id} is created. user count: ${userCount}`,
Expand Down Expand Up @@ -142,7 +157,6 @@ export class ChannelsService {
userId,
channelId,
);

return {
channelUsers: channelUserInfoList,
myChannelUserType: myChannelUserInfo.channelUserType,
Expand Down Expand Up @@ -289,11 +303,15 @@ export class ChannelsService {
);
}

await this.channelInvitationRepository.createChannelInvitation(
const channelInvitation = await this.channelInvitationRepository.createChannelInvitation(
createInvitationParamDto,
);

// TODO: 알림 보내기
const gatewayInvitationParamDto: GatewayCreateChannelInvitationParamDto = {
invitationId: channelInvitation.id,
invitedUserId: invitedUserId,
};
await this.ChannelsGateway.PrivateAlert(gatewayInvitationParamDto);
}

async updateChannelUser(userId: number, channelId: number) {
Expand Down
5 changes: 5 additions & 0 deletions src/game/dto/gateway-create-channelInvitation-param-dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export class GatewayCreateChannelInvitationParamDto {
invitationId: number;

invitedUserId: number;
}

0 comments on commit 41dae03

Please sign in to comment.