-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
21 changed files
with
306 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 2 additions & 2 deletions
4
...ckend/src/auth/googleAuth.service.spec.ts → ...rc/auth/google/googleAuth.service.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...es/backend/src/auth/googleAuth.service.ts → ...end/src/auth/google/googleAuth.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,4 +12,4 @@ export class GoogleAuthGuard extends AuthGuard('google') { | |
await super.logIn(request); | ||
return isActivate; | ||
} | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
...kend/src/auth/passport/google.strategy.ts → ...c/auth/google/strategy/google.strategy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { MemoryStore } from 'express-session'; | ||
|
||
export const MEMORY_STORE = 'memoryStore'; | ||
|
||
@Module({ | ||
providers: [ | ||
{ | ||
provide: MEMORY_STORE, | ||
useFactory: () => { | ||
return new MemoryStore(); | ||
}, | ||
}, | ||
], | ||
exports: [MEMORY_STORE], | ||
}) | ||
export class SessionModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import * as crypto from 'node:crypto'; | ||
import { WsException } from '@nestjs/websockets'; | ||
import * as cookie from 'cookie'; | ||
import { Socket } from 'socket.io'; | ||
import { sessionConfig } from '@/configs/session.config'; | ||
|
||
export const websocketCookieParse = (socket: Socket) => { | ||
if (!socket.request.headers.cookie) { | ||
throw new WsException('not found cookie'); | ||
} | ||
const cookies = cookie.parse(socket.request.headers.cookie); | ||
const sid = cookies['connect.sid']; | ||
return getSessionIdFromCookie(sid); | ||
}; | ||
|
||
const getSessionIdFromCookie = (cookieValue: string) => { | ||
if (cookieValue.startsWith('s:')) { | ||
const [id, signature] = cookieValue.slice(2).split('.'); | ||
const expectedSignature = crypto | ||
.createHmac('sha256', sessionConfig.secret) | ||
.update(id) | ||
.digest('base64') | ||
.replace(/=+$/, ''); | ||
|
||
if (expectedSignature === signature) { | ||
return id; | ||
} | ||
throw new WsException('Invalid cookie signature'); | ||
} | ||
throw new WsException('Invalid cookie format'); | ||
}; |
File renamed without changes.
45 changes: 45 additions & 0 deletions
45
packages/backend/src/auth/session/webSocketSession.guard..ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { | ||
CanActivate, | ||
ExecutionContext, | ||
Inject, | ||
Injectable, | ||
} from '@nestjs/common'; | ||
import { WsException } from '@nestjs/websockets'; | ||
import { MemoryStore, SessionData } from 'express-session'; | ||
import { Socket } from 'socket.io'; | ||
import { websocketCookieParse } from '@/auth/session/cookieParser'; | ||
import { MEMORY_STORE } from '@/auth/session.module'; | ||
import { User } from '@/user/domain/user.entity'; | ||
|
||
export interface SessionSocket extends Socket { | ||
session?: User; | ||
} | ||
|
||
interface PassportSession extends SessionData { | ||
passport: { user: User }; | ||
} | ||
|
||
@Injectable() | ||
export class WebSocketSessionGuard implements CanActivate { | ||
constructor( | ||
@Inject(MEMORY_STORE) private readonly sessionStore: MemoryStore, | ||
) {} | ||
async canActivate(context: ExecutionContext): Promise<boolean> { | ||
const socket: SessionSocket = context.switchToHttp().getRequest(); | ||
const cookieValue = websocketCookieParse(socket); | ||
const session = await this.getSession(cookieValue); | ||
socket.session = session.passport.user; | ||
return true; | ||
} | ||
|
||
private getSession(cookieValue: string) { | ||
return new Promise<PassportSession>((resolve, reject) => { | ||
this.sessionStore.get(cookieValue, (err: Error, session) => { | ||
if (err || !session) { | ||
reject(new WsException('forbidden chat')); | ||
} | ||
resolve(session as PassportSession); | ||
}); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { Inject, UseFilters, UseGuards } from '@nestjs/common'; | ||
import { | ||
ConnectedSocket, | ||
MessageBody, | ||
OnGatewayConnection, | ||
SubscribeMessage, | ||
WebSocketGateway, | ||
WebSocketServer, | ||
} from '@nestjs/websockets'; | ||
import { Server, Socket } from 'socket.io'; | ||
import { Logger } from 'winston'; | ||
import { | ||
SessionSocket, | ||
WebSocketSessionGuard, | ||
} from '@/auth/session/webSocketSession.guard.'; | ||
import { WebSocketExceptionFilter } from '@/middlewares/filter/webSocketException.filter'; | ||
import { StockService } from '@/stock/stock.service'; | ||
import { ChatService } from '@/chat/chat.service'; | ||
import { Chat } from '@/chat/domain/chat.entity'; | ||
|
||
interface chatMessage { | ||
room: string; | ||
content: string; | ||
} | ||
|
||
interface chatResponse { | ||
likeCount: number; | ||
message: string; | ||
type: string; | ||
createdAt: Date; | ||
} | ||
|
||
@WebSocketGateway({ namespace: 'chat' }) | ||
@UseFilters(WebSocketExceptionFilter) | ||
export class ChatGateway implements OnGatewayConnection { | ||
@WebSocketServer() | ||
server: Server; | ||
constructor( | ||
@Inject('winston') private readonly logger: Logger, | ||
private readonly stockService: StockService, | ||
private readonly chatService: ChatService, | ||
) {} | ||
|
||
@UseGuards(WebSocketSessionGuard) | ||
@SubscribeMessage('chat') | ||
async handleConnectStock( | ||
@MessageBody() message: chatMessage, | ||
@ConnectedSocket() client: SessionSocket, | ||
) { | ||
const { room, content } = message; | ||
if (!client.rooms.has(room)) { | ||
client.emit('error', 'You are not in the room'); | ||
this.logger.warn(`client is not in the room ${room}`); | ||
return; | ||
} | ||
if (!client.session || !client.session.id) { | ||
client.emit('error', 'Invalid session'); | ||
this.logger.warn('client session is invalid'); | ||
return; | ||
} | ||
const savedChat = await this.chatService.saveChat(client.session.id, { | ||
stockId: room, | ||
message: content, | ||
}); | ||
this.server.to(room).emit('chat', this.toResponse(savedChat)); | ||
} | ||
|
||
async handleConnection(client: Socket) { | ||
const room = client.handshake.query.stockId; | ||
if (!room || !(await this.stockService.checkStockExist(room as string))) { | ||
client.emit('error', 'Invalid stockId'); | ||
this.logger.warn(`client connected with invalid stockId: ${room}`); | ||
client.disconnect(); | ||
return; | ||
} | ||
if (room) { | ||
client.join(room); | ||
this.logger.info(`client joined room ${room}`); | ||
} | ||
} | ||
|
||
private toResponse(chat: Chat): chatResponse { | ||
return { | ||
likeCount: chat.likeCount, | ||
message: chat.message, | ||
type: chat.type, | ||
createdAt: chat.date?.createdAt || new Date(), | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { TypeOrmModule } from '@nestjs/typeorm'; | ||
import { SessionModule } from '@/auth/session.module'; | ||
import { ChatGateway } from '@/chat/chat.gateway'; | ||
import { Chat } from '@/chat/domain/chat.entity'; | ||
import { StockModule } from '@/stock/stock.module'; | ||
import { ChatService } from '@/chat/chat.service'; | ||
|
||
@Module({ | ||
imports: [TypeOrmModule.forFeature([Chat]), StockModule, SessionModule], | ||
providers: [ChatGateway, ChatService], | ||
}) | ||
export class ChatModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { DataSource } from 'typeorm'; | ||
import { Chat } from '@/chat/domain/chat.entity'; | ||
|
||
interface ChatMessage { | ||
message: string; | ||
stockId: string; | ||
} | ||
|
||
@Injectable() | ||
export class ChatService { | ||
constructor(private readonly dataSource: DataSource) {} | ||
|
||
async saveChat(userId: number, chatMessage: ChatMessage) { | ||
return this.dataSource.manager.save(Chat, { | ||
user: { id: userId }, | ||
stock: { id: chatMessage.stockId }, | ||
message: chatMessage.message, | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; | ||
import { ChatType } from '@/chat/domain/chatType.enum'; | ||
import { DateEmbedded } from '@/common/dateEmbedded.entity'; | ||
import { Stock } from '@/stock/domain/stock.entity'; | ||
import { User } from '@/user/domain/user.entity'; | ||
|
||
@Entity() | ||
export class Chat { | ||
@PrimaryGeneratedColumn() | ||
id: number; | ||
|
||
@ManyToOne(() => User, (user) => user.id) | ||
user: User; | ||
|
||
@ManyToOne(() => Stock, (stock) => stock.id) | ||
stock: Stock; | ||
|
||
@Column() | ||
message: string; | ||
|
||
@Column({ type: 'enum', enum: ChatType, default: ChatType.NORMAL }) | ||
type: ChatType = ChatType.NORMAL; | ||
|
||
@Column({ name: 'like_count', default: 0 }) | ||
likeCount: number = 0; | ||
|
||
@Column(() => DateEmbedded, { prefix: '' }) | ||
date?: DateEmbedded; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export const ChatType = { | ||
NORMAL: 'NORMAL', | ||
BROADCAST: 'BROADCAST', | ||
}; | ||
|
||
export type ChatType = (typeof ChatType)[keyof typeof ChatType]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.