Skip to content

Commit

Permalink
feat: 세션 통신 개선
Browse files Browse the repository at this point in the history
- Intercepter 도입
- 이미 쿠키에 세션이 있었거나, 쿠키에 설정할 세션이 있으면 응답 종류 후에만 업데이트

[#224]
  • Loading branch information
LuizyHub authored and flydog98 committed Dec 4, 2023
1 parent 05a3d85 commit 55233f3
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 14 deletions.
3 changes: 3 additions & 0 deletions packages/backend/src/quizzes/quizzes.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
Delete,
UseGuards,
HttpCode,
UseInterceptors,
} from '@nestjs/common';
import {
ApiTags,
Expand Down Expand Up @@ -39,9 +40,11 @@ import { QuizWizardService } from '../quiz-wizard/quiz-wizard.service';
import { Fail, SubmitDto, Success } from './dto/submit.dto';
import { preview } from '../common/util';
import { QuizGuard } from './quiz.guard';
import { SessionUpdateInterceptor } from '../session/session-save.intercepter';

@ApiTags('quizzes')
@Controller('api/v1/quizzes')
@UseInterceptors(SessionUpdateInterceptor)
export class QuizzesController {
constructor(
private readonly quizService: QuizzesService,
Expand Down
35 changes: 35 additions & 0 deletions packages/backend/src/session/session-save.intercepter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { tap } from 'rxjs/operators';
import { Observable } from 'rxjs';
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { SessionService } from './session.service';
import { Response } from 'express';

@Injectable()
export class SessionUpdateInterceptor implements NestInterceptor {
constructor(private sessionService: SessionService) {}

intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const response: Response = context.switchToHttp().getResponse();
let sessionId = request.cookies.sessionId; // 세션 ID 추출

return next.handle().pipe(
tap(() => {
sessionId =
sessionId || this.extractSessionId(response.getHeader('Set-Cookie')); // 세션 ID가 없으면 쿠키에서 추출
// 세션 업데이트 로직
this.sessionService.saveSession(sessionId);
}),
);
}

private extractSessionId(cookieStr) {
const sessionIdMatch = /sessionId=([^;]+)/.exec(cookieStr);
return sessionIdMatch ? sessionIdMatch[1] : null;
}
}
4 changes: 3 additions & 1 deletion packages/backend/src/session/session.controller.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Controller, Delete, Res } from '@nestjs/common';
import { Controller, Delete, Res, UseInterceptors } from '@nestjs/common';
import { SessionService } from './session.service';
import { SessionId } from './session.decorator';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { Response } from 'express';
import { SessionUpdateInterceptor } from './session-save.intercepter';

@ApiTags('session')
@Controller('api/v1/session')
@UseInterceptors(SessionUpdateInterceptor)
export class SessionController {
constructor(private readonly sessionService: SessionService) {}

Expand Down
36 changes: 23 additions & 13 deletions packages/backend/src/session/session.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,21 @@ import { ObjectId } from 'typeorm';

@Injectable()
export class SessionService {
private readonly sessionMap: Map<string, Session> = new Map();
constructor(
@InjectModel(Session.name) private sessionModel: Model<Session>,
@Inject('winston') private readonly logger: Logger,
) {
const testSession = new this.sessionModel({
problems: {},
});
testSession.save();
}
) {}

async createSession(): Promise<string> {
const session = new this.sessionModel({
problems: {},
});
return await session.save().then((session) => {
this.logger.log('info', `session ${session._id as ObjectId} created`);
return (session._id as ObjectId).toString('hex');
const sessionId = (session._id as ObjectId).toString('hex');
this.sessionMap.set(sessionId, session);
return sessionId;
});
}

Expand All @@ -44,7 +42,6 @@ export class SessionService {
'info',
`session's new quizId: ${problemId}, document created`,
);
await session.save();
} else {
this.logger.log(
'info',
Expand All @@ -68,7 +65,6 @@ export class SessionService {
`setting ${sessionId}'s containerId as ${containerId}`,
);
session.problems.get(problemId).containerId = containerId;
session.save();
}

async getRecentLog(
Expand All @@ -95,7 +91,6 @@ export class SessionService {
throw new Error('problem not found');
}
session.problems.get(problemId).logs.push(log);
session.save();
}

async deleteCommandHistory(
Expand All @@ -108,18 +103,33 @@ export class SessionService {
}
session.problems.get(problemId).logs = [];
session.problems.get(problemId).containerId = '';
session.save();
}

private async getSessionById(id: string): Promise<Session> {
return await this.sessionModel.findById(id);
let session = this.sessionMap.get(id);
if (!session) {
session = await this.sessionModel.findById(id);
if (!session) {
throw new Error('session not found');
}
this.sessionMap.set(id, session);
}
return session;
}

async saveSession(sessionId: string): Promise<void> {
// 세션 조회 및 저장 로직
const session = this.sessionMap.get(sessionId);
if (session) {
this.sessionMap.delete(sessionId);
await session.save();
}
}

async deleteSession(sessionId: string): Promise<void> {
//soft delete
const session = await this.getSessionById(sessionId);
session.deletedAt = new Date();
session.save();
this.logger.log('info', `session ${session._id as ObjectId} deleted`);
}

Expand Down

0 comments on commit 55233f3

Please sign in to comment.