Skip to content

Commit d95ffd5

Browse files
committed
Set up handlers for match, queue and websocket
1 parent 242b222 commit d95ffd5

File tree

17 files changed

+399
-242
lines changed

17 files changed

+399
-242
lines changed

backend/matching-service/app.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import matchingRoutes from "./src/routes/matchingRoutes.ts";
99

1010
dotenv.config();
1111

12-
const allowedOrigins = process.env.ORIGINS
12+
export const allowedOrigins = process.env.ORIGINS
1313
? process.env.ORIGINS.split(",")
1414
: ["http://localhost:5173", "http://127.0.0.1:5173"];
1515

backend/matching-service/package-lock.json

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

backend/matching-service/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"express": "^4.21.1",
1919
"socket.io": "^4.8.0",
2020
"swagger-ui-express": "^5.0.1",
21+
"uuid": "^10.0.0",
2122
"yaml": "^2.5.1"
2223
},
2324
"devDependencies": {
@@ -29,6 +30,7 @@
2930
"@types/socket.io": "^3.0.2",
3031
"@types/supertest": "^6.0.2",
3132
"@types/swagger-ui-express": "^4.1.6",
33+
"@types/uuid": "^10.0.0",
3234
"cross-env": "^7.0.3",
3335
"eslint": "^9.12.0",
3436
"globals": "^15.11.0",

backend/matching-service/server.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
import http from "http";
2-
import app from "./app.ts";
3-
import Websocket from "./src/websocket/websocket.ts";
4-
import { handleMatchRequest } from "./src/websocket/websocketHandlers.ts";
2+
import app, { allowedOrigins } from "./app.ts";
3+
import { handleWebsocketMatchEvents } from "./src/handlers/websocketHandler.ts";
4+
import { Server } from "socket.io";
55

66
const server = http.createServer(app);
7-
const io = Websocket.getInstance(server);
8-
io.initSocketHandlers([{ path: "/matching", handler: handleMatchRequest }]);
7+
export const io = new Server(server, {
8+
cors: {
9+
origin: allowedOrigins,
10+
methods: ["GET", "POST"],
11+
},
12+
connectionStateRecovery: {},
13+
});
14+
15+
io.on("connection", (socket) => {
16+
handleWebsocketMatchEvents(socket);
17+
});
918

1019
const PORT = process.env.PORT || 3002;
1120

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { io } from "../../server";
2+
import { Match, MatchItem, MatchRequest } from "../types/matchTypes";
3+
import { v4 as uuidv4 } from "uuid";
4+
import {
5+
MATCH_ACCEPTANCE_TIMEOUT,
6+
MATCH_FOUND,
7+
MATCH_SUCCESSFUL,
8+
MATCH_TIMEOUT,
9+
MATCH_UNSUCCESSFUL,
10+
} from "../utils/constants";
11+
import { appendToMatchQueue } from "./queueHandler";
12+
import { Socket } from "socket.io";
13+
14+
const matches: Match = {};
15+
16+
export const createMatchItem = (socket: Socket, matchRequest: MatchRequest) => {
17+
const { user, complexities, categories, languages, timeout } = matchRequest;
18+
19+
const matchTimeout = setTimeout(() => {
20+
socket.emit(MATCH_TIMEOUT);
21+
}, timeout * 1000);
22+
23+
const matchQueueItem: MatchItem = {
24+
socket: socket,
25+
user: user,
26+
complexities: complexities,
27+
categories: categories,
28+
languages: languages,
29+
timeout: matchTimeout,
30+
acceptedMatch: false,
31+
};
32+
33+
appendToMatchQueue(matchQueueItem);
34+
};
35+
36+
export const createMatch = (matchItems: MatchItem[]) => {
37+
const matchItem1 = matchItems[0];
38+
const matchItem2 = matchItems[1];
39+
40+
clearTimeout(matchItem1.timeout);
41+
clearTimeout(matchItem2.timeout);
42+
43+
const matchId = uuidv4();
44+
matches[matchId] = {
45+
item1: matchItem1,
46+
item2: matchItem2,
47+
timeout: null,
48+
accepted: false,
49+
};
50+
51+
matchItem1.socket.join(matchId);
52+
matchItem2.socket.join(matchId);
53+
io.to(matchId).emit(MATCH_FOUND, {
54+
matchId: matchId,
55+
user1: matchItem1.user,
56+
user2: matchItem2.user,
57+
});
58+
};
59+
60+
export const setMatchTimeout = (matchId: string) => {
61+
const match = matches[matchId];
62+
if (!match) {
63+
return;
64+
}
65+
66+
const timeout = setTimeout(() => {
67+
io.to(matchId).emit(MATCH_UNSUCCESSFUL);
68+
delete matches[matchId];
69+
}, MATCH_ACCEPTANCE_TIMEOUT);
70+
71+
match.timeout = timeout;
72+
};
73+
74+
export const handleMatchAcceptance = (matchId: string) => {
75+
const match = matches[matchId];
76+
if (!match) {
77+
return;
78+
}
79+
80+
if (match.accepted) {
81+
clearTimeout(match.timeout!);
82+
io.to(matchId).emit(MATCH_SUCCESSFUL);
83+
delete matches[matchId];
84+
} else {
85+
match.accepted = true;
86+
}
87+
};
88+
89+
export const handleMatchDecline = (matchId: string) => {
90+
const match = matches[matchId];
91+
if (!match) {
92+
return;
93+
}
94+
95+
clearTimeout(match.timeout!);
96+
io.to(matchId).emit(MATCH_UNSUCCESSFUL);
97+
delete matches[matchId];
98+
};
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { MatchItem } from "../types/matchTypes";
2+
import { createMatch } from "./matchHandler";
3+
4+
/* Basic queue set-up for websocket testing (feel free to replace with the actual queueing mechanism) */
5+
6+
const matchQueue: MatchItem[] = [];
7+
8+
setInterval(() => {
9+
findMatch();
10+
}, 5000);
11+
12+
const findMatch = () => {
13+
if (matchQueue.length < 2) {
14+
return;
15+
}
16+
17+
const matchedItems = [];
18+
while (matchedItems.length < 2 && matchQueue.length > 0) {
19+
const matchItem = matchQueue.shift()!;
20+
if (matchItem.socket.connected) {
21+
matchedItems.push(matchItem);
22+
}
23+
}
24+
25+
if (matchedItems.length === 2) {
26+
createMatch(matchedItems);
27+
} else {
28+
matchedItems.reverse().forEach((item) => matchQueue.unshift(item));
29+
}
30+
};
31+
32+
export const appendToMatchQueue = (item: MatchItem) => {
33+
if (!matchQueue.find((queueItem) => queueItem.user.id === item.user.id)) {
34+
matchQueue.push(item);
35+
}
36+
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Socket } from "socket.io";
2+
import {
3+
MATCH_ACCEPTED,
4+
MATCH_DECLINED,
5+
MATCH_RECEIVED,
6+
MATCH_REQUEST,
7+
} from "../utils/constants";
8+
import { MatchRequest } from "../types/matchTypes";
9+
import {
10+
createMatchItem,
11+
handleMatchAcceptance,
12+
handleMatchDecline,
13+
setMatchTimeout,
14+
} from "./matchHandler";
15+
16+
export const handleWebsocketMatchEvents = (socket: Socket) => {
17+
socket.on(MATCH_REQUEST, (matchRequest: MatchRequest) => {
18+
createMatchItem(socket, matchRequest);
19+
});
20+
21+
socket.on(MATCH_RECEIVED, (matchId: string) => {
22+
setMatchTimeout(matchId);
23+
});
24+
25+
socket.on(MATCH_ACCEPTED, (matchId: string) => {
26+
handleMatchAcceptance(matchId);
27+
});
28+
29+
socket.on(MATCH_DECLINED, (matchId: string) => {
30+
handleMatchDecline(matchId);
31+
});
32+
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Socket } from "socket.io";
2+
3+
export interface MatchUser {
4+
id: string;
5+
username: string;
6+
profile: string;
7+
}
8+
9+
export interface MatchRequest {
10+
user: MatchUser;
11+
complexities: string[];
12+
categories: string[];
13+
languages: string[];
14+
timeout: number;
15+
}
16+
17+
export interface MatchItem {
18+
socket: Socket;
19+
user: MatchUser;
20+
complexities: string[];
21+
categories: string[];
22+
languages: string[];
23+
timeout: NodeJS.Timeout;
24+
acceptedMatch: boolean;
25+
}
26+
27+
export interface Match {
28+
[id: string]: {
29+
item1: MatchItem;
30+
item2: MatchItem;
31+
timeout: NodeJS.Timeout | null;
32+
accepted: boolean;
33+
};
34+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* Websocket Match Events */
2+
export const MATCH_REQUEST = "match_request";
3+
export const MATCH_TIMEOUT = "match_timeout";
4+
export const MATCH_FOUND = "match_found";
5+
export const MATCH_RECEIVED = "match_received";
6+
export const MATCH_ACCEPTED = "match_accepted";
7+
export const MATCH_DECLINED = "match_declined";
8+
export const MATCH_SUCCESSFUL = "match_successful";
9+
export const MATCH_UNSUCCESSFUL = "match_unsuccessful";
10+
11+
export const MATCH_ACCEPTANCE_TIMEOUT = 10000;

backend/matching-service/src/websocket/websocket.ts

Lines changed: 0 additions & 44 deletions
This file was deleted.

0 commit comments

Comments
 (0)