Skip to content

Commit

Permalink
feat(be): add session termination (#214)
Browse files Browse the repository at this point in the history
feat: add session termination
  • Loading branch information
wlgh1553 authored Nov 28, 2024
1 parent 7cb91da commit c7ff25a
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class SessionTokenValidationGuard implements CanActivate {

async canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
const sessionId = request.body?.sessionId || request.query?.sessionId;
const sessionId = request.body?.sessionId || request.query?.sessionId || request.params?.sessionId;
const token = request.body?.token || request.query?.token;

if (!sessionId || !token) {
Expand Down
13 changes: 13 additions & 0 deletions apps/server/src/sessions/dto/terminate-session.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator';

export class TerminateSessionDto {
@ApiProperty({
example: 'user_token_123',
description: '사용자의 토큰',
required: true,
})
@IsString()
@IsNotEmpty({ message: '사용자 토큰은 필수입니다.' })
token: string;
}
24 changes: 21 additions & 3 deletions apps/server/src/sessions/sessions.controller.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import { Body, Controller, Get, Post, Req, UseGuards, UseInterceptors } from '@nestjs/common';
import { Body, Controller, Get, HttpCode, Param, Post, Req, UseGuards, UseInterceptors } from '@nestjs/common';
import { ApiBody, ApiTags } from '@nestjs/swagger';

import { CreateSessionDto } from './dto/create-session.dto';
import { TerminateSessionDto } from './dto/terminate-session.dto';
import { SessionsService } from './sessions.service';
import { CreateSessionSwagger } from './swagger/create-session.swagger';
import { GetSessionSwagger } from './swagger/get-session.swagger';
import { TerminateSessionSwagger } from './swagger/terminate-session.swagger';

import { JwtAuthGuard } from '@auth/jwt-auth.guard';
import { SessionTokenValidationGuard } from '@common/guards/session-token-validation.guard';
import { TransformInterceptor } from '@common/interceptors/transform.interceptor';
import { SocketGateway } from '@socket/socket.gateway';

@ApiTags('Sessions')
@UseInterceptors(TransformInterceptor)
@UseGuards(JwtAuthGuard)
@Controller('sessions')
export class SessionsController {
constructor(private readonly sessionsService: SessionsService) {}
constructor(
private readonly sessionsService: SessionsService,
private readonly socketGateway: SocketGateway,
) {}

@Post()
@UseGuards(JwtAuthGuard)
@CreateSessionSwagger()
@ApiBody({ type: CreateSessionDto })
async create(@Body() createSessionDto: CreateSessionDto, @Req() request: Request) {
Expand All @@ -26,10 +33,21 @@ export class SessionsController {
}

@Get()
@UseGuards(JwtAuthGuard)
@GetSessionSwagger()
async getSessionsById(@Req() request: Request) {
const userId = request['user'].userId;
const sessionData = await this.sessionsService.getSessionsById(userId);
return { sessionData };
}

@Post(':sessionId/terminate')
@HttpCode(200)
@TerminateSessionSwagger()
@UseGuards(SessionTokenValidationGuard)
async terminateSession(@Param('sessionId') sessionId: string, @Body() { token }: TerminateSessionDto) {
const result = await this.sessionsService.terminateSession(sessionId, token);
this.socketGateway.broadcastSessionEnd(sessionId, token, result);
return result;
}
}
5 changes: 4 additions & 1 deletion apps/server/src/sessions/sessions.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import { SessionsRepository } from './sessions.repository';
import { SessionsService } from './sessions.service';

import { AuthModule } from '@auth/auth.module';
import { SessionTokenModule } from '@common/guards/session-token.module';
import { PrismaModule } from '@prisma-alias/prisma.module';
import { SessionsAuthRepository } from '@sessions-auth/sessions-auth.repository';
import { SocketModule } from '@socket/socket.module';

@Module({
imports: [PrismaModule, JwtModule.register({}), AuthModule],
imports: [PrismaModule, JwtModule.register({}), AuthModule, SessionTokenModule, SocketModule],
controllers: [SessionsController],
providers: [SessionsService, SessionsRepository, SessionsAuthRepository],
exports: [SessionsService],
Expand Down
13 changes: 13 additions & 0 deletions apps/server/src/sessions/sessions.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,17 @@ export class SessionsRepository {
throw DatabaseException.read('UserSessionToken');
}
}

async updateSessionExpiredAt(sessionId: string, expireTime: Date) {
try {
await this.prisma.session.update({
where: {
sessionId,
},
data: { expiredAt: expireTime },
});
} catch (error) {
throw DatabaseException.update('session');
}
}
}
16 changes: 15 additions & 1 deletion apps/server/src/sessions/sessions.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Injectable } from '@nestjs/common';
import { ForbiddenException, Injectable } from '@nestjs/common';

import { CreateSessionDto } from './dto/create-session.dto';
import { SessionCreateData } from './interface/session-create-data.interface';
Expand Down Expand Up @@ -52,4 +52,18 @@ export class SessionsService {
});
return transformedSessions;
}

async terminateSession(sessionId: string, token: string) {
const [{ createUserId }, { userId }] = await Promise.all([
this.sessionRepository.findById(sessionId),
this.sessionsAuthRepository.findByToken(token),
]);

if (createUserId !== userId) {
throw new ForbiddenException('세션 생성자만이 이 작업을 수행할 수 있습니다.');
}
const expireTime = new Date();
await this.sessionRepository.updateSessionExpiredAt(sessionId, expireTime);
return { expired: true };
}
}
25 changes: 25 additions & 0 deletions apps/server/src/sessions/swagger/terminate-session.swagger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { applyDecorators } from '@nestjs/common';
import { ApiOperation, ApiResponse } from '@nestjs/swagger';

export const TerminateSessionSwagger = () =>
applyDecorators(
ApiOperation({ summary: '세션 만료' }),
ApiResponse({
status: 200,
description: '세션 만료작업 성공',
schema: {
example: {
expired: true,
},
},
}),
ApiResponse({
status: 403,
description: '세션 만료 작업 실패',
schema: {
example: {
message: '세션 생성자만이 이 작업을 수행할 수 있습니다.',
},
},
}),
);
3 changes: 3 additions & 0 deletions apps/server/src/socket/socket.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const SOCKET_EVENTS = {
PARTICIPANT_COUNT_UPDATED: 'participantCountUpdated',

HOST_CHANGED: 'hostChanged',
SESSION_ENDED: 'sessionEnded',
} as const;

interface Client {
Expand Down Expand Up @@ -162,4 +163,6 @@ export class SocketGateway implements OnGatewayConnection, OnGatewayDisconnect {
broadcastReplyLike = this.createEventBroadcaster(SOCKET_EVENTS.REPLY_LIKED);

broadcastHostChange = this.createEventBroadcaster(SOCKET_EVENTS.HOST_CHANGED);

broadcastSessionEnd = this.createEventBroadcaster(SOCKET_EVENTS.SESSION_ENDED);
}

0 comments on commit c7ff25a

Please sign in to comment.