Skip to content

Commit 458ee97

Browse files
committed
Add Collab space routing
1 parent d88dd8a commit 458ee97

File tree

11 files changed

+135
-9
lines changed

11 files changed

+135
-9
lines changed

Backend/CollabService/app.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const express = require('express');
2+
const http = require('http');
3+
const { setupWebSocket } = require('./websocket/collabSocket');
4+
5+
const app = express();
6+
const server = http.createServer(app);
7+
8+
// Set up WebSocket server and attach to HTTP server
9+
setupWebSocket(server);
10+
11+
// Basic route to test the server
12+
app.get('/', (req, res) => {
13+
res.send('CollabService is running');
14+
});
15+
16+
module.exports = { app, server };

Backend/CollabService/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const app = require('./app')
2+
3+
// Change the port number to listen to a different port but remember to change the port number in frontend too!
4+
app.listen(3004, () => {
5+
console.log("Collab Servicce is Running")
6+
})
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const rooms = {}; // { roomId: [sockets] }
2+
3+
function manageRoom(ws, roomId) {
4+
if (!rooms[roomId]) {
5+
rooms[roomId] = [];
6+
}
7+
8+
// Add the user to the room
9+
rooms[roomId].push(ws);
10+
ws.roomId = roomId;
11+
12+
console.log(`User joined room ${roomId}`);
13+
}
14+
15+
module.exports = { manageRoom };
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const WebSocket = require('ws');
2+
const { manageRoom } = require('../utils/roomManager');
3+
4+
function setupWebSocket(server) {
5+
const wss = new WebSocket.Server({ server });
6+
7+
wss.on('connection', (ws) => {
8+
console.log('User connected to the collaboration space');
9+
10+
ws.on('message', (message) => {
11+
const data = JSON.parse(message);
12+
13+
switch (data.type) {
14+
case 'joinRoom':
15+
manageRoom(ws, data.roomId);
16+
break;
17+
case 'codeUpdate':
18+
broadcastToRoom(data.roomId, data.code);
19+
break;
20+
default:
21+
console.error('Unknown message type');
22+
}
23+
});
24+
25+
ws.on('close', () => {
26+
console.log('User disconnected from collaboration space');
27+
});
28+
});
29+
30+
function broadcastToRoom(roomId, code) {
31+
wss.clients.forEach((client) => {
32+
if (client.readyState === WebSocket.OPEN && client.roomId === roomId) {
33+
client.send(JSON.stringify({ type: 'codeUpdate', code }));
34+
}
35+
});
36+
}
37+
}
38+
39+
module.exports = { setupWebSocket };

Backend/MatchingService/package-lock.json

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

Backend/MatchingService/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"cors": "^2.8.5",
55
"dotenv": "^16.4.5",
66
"express": "^4.21.1",
7+
"uuid": "^10.0.0",
78
"ws": "^8.18.0"
89
},
910
"scripts": {

Backend/MatchingService/rabbitmq/subscriber.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const amqp = require('amqplib');
22
const { queueNames } = require('./setup.js');
33
// const { matchUsers } = require('../services/matchingService.js');
44
const { notifyUsers } = require('../websocket/websocket');
5+
const { v4: uuidv4 } = require('uuid');
56

67
// TODO: Subscribe and acknowledge messages with user info when timeout/user matched
78

@@ -31,9 +32,14 @@ function matchUsers(channel, msg, userId, language, difficulty) {
3132
if (waitingUsers[criteriaKey].length >= 2) {
3233
const matchedUsers = waitingUsers[criteriaKey].splice(0, 2); // Match the first two users
3334
console.log(`Matched users: ${matchedUsers.map(user => user.userId)}`);
35+
36+
// Create a unique collaboration room ID
37+
const roomId = uuidv4();
3438

3539
// Notify users of the match
36-
notifyUsers(matchedUsers.map(user => user.userId), 'Match found!', 'match');
40+
notifyUsers(matchedUsers.map(user => user.userId), 'Match found!', 'match', {
41+
collaborationUrl: `/collaboration/${roomId}`
42+
});
3743

3844
// Acknowledge the messages for both matched users
3945
matchedUsers.forEach(({ msg, channel }) => {

Backend/MatchingService/websocket/websocket.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ wss.on('connection', (ws) => {
2222
console.log('Message published to RabbitMQ');
2323

2424
// Notify the user that their message has been processed successfully
25-
ws.send(JSON.stringify({ status: 'su9ccess', message: 'Match request sent!' }));
25+
ws.send(JSON.stringify({ status: 'success', message: 'Match request sent!' }));
2626
} else if (action === 'cancel') {
2727
await publishCancelRequest({ userId });
2828
console.log('Cancel request published to RabbitMQ');
@@ -50,8 +50,9 @@ wss.on('connection', (ws) => {
5050
* @param {string|array} userId - User ID or an array of user IDs to notify.
5151
* @param {string} message - The message to send.
5252
* @param {string} type - The type of message (e.g., 'match' or 'rejection').
53+
* @param {object} [data] - Additional data (e.g., collaboration URL).
5354
*/
54-
function notifyUsers(userId, message, type) {
55+
function notifyUsers(userId, message, type, data = {}) {
5556
console.log(`Notifying user(s): ${userId}, Message: ${message}, Type: ${type}`);
5657

5758
const userIds = Array.isArray(userId) ? userId : [userId]; // Convert to array if single user
@@ -60,12 +61,14 @@ function notifyUsers(userId, message, type) {
6061
if (client.readyState === WebSocket.OPEN && userIds.includes(client.userId)) {
6162
console.log(`Notifying client: ${client.userId}`);
6263
client.send(JSON.stringify({
63-
userId: client.userId,
64+
userId: client.userId,
6465
message,
65-
type
66+
type,
67+
...data // Include additional data such as collaboration URL
6668
}));
6769
}
6870
});
6971
}
7072

73+
7174
module.exports = { notifyUsers };

Frontend/src/App.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'bootstrap/dist/css/bootstrap.min.css';
44
import Home from './components/Home';
55
import Login from './components/auth/Login';
66
import SignUp from './components/auth/SignUp';
7+
import CollaborationSpace from './components/collab/CollaborationSpace';
78
import EditProfile from "./components/user/EditProfile";
89
import ProtectedRoute from './components/routes/ProtectedRoute';
910

@@ -26,6 +27,9 @@ function App() {
2627

2728
{/* Edit Profile page route */}
2829
<Route path='/profile/:id' element={<ProtectedRoute><EditProfile /></ProtectedRoute>} />
30+
31+
{/* Collaboration page route */}
32+
<Route path="/collaboration/:roomId" element={<ProtectedRoute><CollaborationSpace /></ProtectedRoute>} />
2933
</Routes>
3034
</BrowserRouter>
3135
</div>

Frontend/src/components/Sidebar.jsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import Matching from "./matching/Matching";
88
import SuccessfulMatch from "./matching/SuccessfulMatch";
99
import UnsuccessfulMatch from "./matching/UnsuccessfulMatch";
1010
import CriteriaDisplay from './matching/CriteriaDisplay';
11+
import { useNavigate } from 'react-router-dom';
1112
const { getUserFromToken } = require('./user/userAvatarBox');
1213

14+
1315
function Sidebar() {
1416
const [ws, setWs] = useState(null);
1517
const [difficulty, setDifficulty] = useState('');
@@ -26,6 +28,8 @@ function Sidebar() {
2628
const handleCloseSuccessfulMatch = () => setShowSuccessfulMatch(false);
2729
const handleCloseUnsuccessfulMatch = () => setShowUnsuccessfulMatch(false);
2830

31+
const navigate = useNavigate();
32+
2933
useEffect(() => {
3034
const fetchUser = async () => {
3135
const user = await getUserFromToken();
@@ -41,13 +45,16 @@ function Sidebar() {
4145
const websocket = new WebSocket('ws://localhost:8080');
4246

4347
websocket.onmessage = (event) => {
44-
const { message, type } = JSON.parse(event.data);
45-
console.log(`Notification from server: ${message}`);
48+
const data = JSON.parse(event.data);
49+
console.log(`Notification from server: ${data.message}`);
4650

47-
if (type === 'match') {
51+
if (data.type === 'match' && data.collaborationUrl) {
4852
setShowMatching(false);
4953
setShowSuccessfulMatch(true);
50-
} else if (type === 'rejection') {
54+
setTimeout(() => {
55+
navigate(data.collaborationUrl); // Programmatically navigate to collaboration space
56+
}, 3000);
57+
} else if (data.type === 'rejection') {
5158
setShowMatching(false);
5259
setShowUnsuccessfulMatch(true);
5360
}

0 commit comments

Comments
 (0)