Skip to content

Commit b3222d9

Browse files
authored
Merge pull request #377 from boostcampwm2023/BE-feature/crdt-operation
Socket.io λ§ˆμΈλ“œλ§΅ μž‘μ—… λ™μž‘
2 parents 183940f + e32b012 commit b3222d9

File tree

7 files changed

+406
-51
lines changed

7 files changed

+406
-51
lines changed

β€Žnestjs-BE/server/package-lock.jsonβ€Ž

Lines changed: 40 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žnestjs-BE/server/package.jsonβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
"jest": "^29.5.0",
6363
"prettier": "^3.0.0",
6464
"prisma": "^5.6.0",
65+
"socket.io-client": "^4.8.1",
6566
"source-map-support": "^0.5.21",
6667
"supertest": "^6.3.3",
6768
"ts-jest": "^29.1.0",
Lines changed: 40 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,67 @@
1-
import { UnauthorizedException } from '@nestjs/common';
21
import { JwtService } from '@nestjs/jwt';
32
import {
43
OnGatewayConnection,
4+
OnGatewayInit,
55
SubscribeMessage,
66
WebSocketGateway,
77
WebSocketServer,
8+
WsException,
89
} from '@nestjs/websockets';
10+
import { ConfigService } from '@nestjs/config';
911
import { Server, Socket } from 'socket.io';
1012
import { BoardTreesService } from './board-trees.service';
11-
import {
12-
OperationAdd,
13-
OperationDelete,
14-
OperationMove,
15-
OperationUpdate,
16-
} from '../crdt/operation';
13+
import type { BoardOperation } from './schemas/board-operation.schema';
1714

1815
@WebSocketGateway({ namespace: 'board' })
19-
export class BoardTreesGateway implements OnGatewayConnection {
16+
export class BoardTreesGateway implements OnGatewayInit, OnGatewayConnection {
2017
constructor(
2118
private boardTreesService: BoardTreesService,
2219
private jwtService: JwtService,
20+
private configService: ConfigService,
2321
) {}
2422

2523
@WebSocketServer()
2624
server: Server;
2725

28-
handleConnection(client: Socket, token: string) {
29-
if (!token) {
30-
client.disconnect();
31-
throw new UnauthorizedException();
32-
}
33-
try {
34-
this.jwtService.verify(token);
35-
} catch (error) {
36-
client.disconnect();
37-
throw new UnauthorizedException();
38-
}
26+
afterInit(server: Server) {
27+
server.use((socket, next) => {
28+
const token = socket.handshake.auth.token;
29+
if (!token) {
30+
next(new WsException('access token required'));
31+
}
32+
try {
33+
this.jwtService.verify(token, {
34+
secret: this.configService.get<string>('JWT_ACCESS_SECRET'),
35+
});
36+
next();
37+
} catch (error) {
38+
next(new WsException('token is invalid'));
39+
}
40+
});
3941
}
4042

41-
@SubscribeMessage('joinBoard')
42-
async handleJoinBoard(client: Socket, payload: string) {
43-
const payloadObject = JSON.parse(payload);
44-
if (!this.boardTreesService.hasTree(payloadObject.boardId)) {
45-
await this.boardTreesService.initBoardTree(
46-
payloadObject.boardId,
47-
payloadObject.boardName,
48-
);
43+
handleConnection(client: Socket) {
44+
const query = client.handshake.query;
45+
const boardId = query.boardId;
46+
47+
if (!boardId) {
48+
client.emit('board_id_required', new WsException('board id required'));
49+
client.disconnect();
4950
}
50-
client.join(payloadObject.boardId);
51-
client.emit(
52-
'initTree',
53-
this.boardTreesService.getTreeData(payloadObject.boardId),
54-
);
51+
client.join(boardId);
52+
client.emit('board_joined', boardId);
5553
}
5654

57-
@SubscribeMessage('updateMindmap')
58-
handleUpdateMindmap(client: Socket, payload: string) {
59-
const payloadObject = JSON.parse(payload);
60-
const { boardId, operation: serializedOperation } = payloadObject;
61-
62-
const operationTypeMap = {
63-
add: OperationAdd.parse,
64-
delete: OperationDelete.parse,
65-
move: OperationMove.parse,
66-
update: OperationUpdate.parse,
67-
};
68-
69-
const operation =
70-
operationTypeMap[serializedOperation.operationType](serializedOperation);
71-
this.boardTreesService.applyOperation(boardId, operation);
72-
this.boardTreesService.updateTreeData(boardId);
55+
@SubscribeMessage('createOperation')
56+
async handleCreateOperation(client: Socket, operation: BoardOperation) {
57+
await this.boardTreesService.createOperationLog(operation);
58+
client.broadcast.to(operation.boardId).emit('operation', operation);
59+
client.emit('operationCreated');
60+
}
7361

74-
client.broadcast
75-
.to(boardId)
76-
.emit('operationFromServer', serializedOperation);
62+
@SubscribeMessage('getOperations')
63+
async handleGetOperations(client: Socket, boardId: string) {
64+
const operations = await this.boardTreesService.getOperationLogs(boardId);
65+
client.emit('getOperations', operations);
7766
}
7867
}

β€Žnestjs-BE/server/src/board-trees/board-trees.module.tsβ€Ž

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@ import { MongooseModule } from '@nestjs/mongoose';
44
import { BoardTreesService } from './board-trees.service';
55
import { BoardTreesGateway } from './board-trees.gateway';
66
import { BoardTree, BoardTreeSchema } from './schemas/board-tree.schema';
7+
import {
8+
BoardOperation,
9+
BoardOperationSchema,
10+
} from './schemas/board-operation.schema';
711

812
@Module({
913
imports: [
1014
MongooseModule.forFeature([
1115
{ name: BoardTree.name, schema: BoardTreeSchema },
16+
{ name: BoardOperation.name, schema: BoardOperationSchema },
1217
]),
1318
JwtModule,
1419
],

β€Žnestjs-BE/server/src/board-trees/board-trees.service.tsβ€Ž

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ import { Injectable } from '@nestjs/common';
22
import { InjectModel } from '@nestjs/mongoose';
33
import { Model } from 'mongoose';
44
import { BoardTree } from './schemas/board-tree.schema';
5+
import { BoardOperation } from './schemas/board-operation.schema';
56
import { CrdtTree } from '../crdt/crdt-tree';
67
import { Operation } from '../crdt/operation';
78

89
@Injectable()
910
export class BoardTreesService {
1011
constructor(
1112
@InjectModel(BoardTree.name) private boardTreeModel: Model<BoardTree>,
13+
@InjectModel(BoardOperation.name)
14+
private boardOperationModel: Model<BoardOperation>,
1215
) {}
1316

1417
private boardTrees = new Map<string, CrdtTree>();
@@ -57,4 +60,15 @@ export class BoardTreesService {
5760
.updateOne({ boardId }, { tree: JSON.stringify(tree) })
5861
.exec();
5962
}
63+
64+
async createOperationLog(operation: BoardOperation) {
65+
return this.boardOperationModel.create(operation);
66+
}
67+
68+
async getOperationLogs(boardId: string) {
69+
return this.boardOperationModel
70+
.find({ boardId })
71+
.select('-_id -__v')
72+
.lean();
73+
}
6074
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
2+
import { HydratedDocument } from 'mongoose';
3+
4+
export type BoardOperationDocument = HydratedDocument<BoardOperation>;
5+
6+
@Schema()
7+
export class BoardOperation {
8+
@Prop({ required: true })
9+
boardId: string;
10+
11+
@Prop({ required: true })
12+
type: string;
13+
14+
@Prop()
15+
parentId: string;
16+
17+
@Prop()
18+
oldParentId: string;
19+
20+
@Prop()
21+
content: string;
22+
23+
@Prop()
24+
oldContent: string;
25+
}
26+
27+
export const BoardOperationSchema =
28+
SchemaFactory.createForClass(BoardOperation);

0 commit comments

Comments
Β (0)