Skip to content

Commit 23e71e9

Browse files
committed
Add checks for user entering collab space
1 parent 93d8401 commit 23e71e9

File tree

3 files changed

+148
-61
lines changed

3 files changed

+148
-61
lines changed

Backend/CollabService/utils/roomManager.js

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,48 @@ const WebSocket = require('ws');
22
const rooms = {}; // { roomId: [sockets] }
33

44
function manageRoom(ws, roomId, userId, type) {
5+
console.log(`manageRoom function: ${type} ${userId}`)
56

67
switch (type) {
78
case "join":
9+
10+
// userIds -> track users in room
11+
// matchedUserIds -> check authorized users
812
if (!rooms[roomId]) {
9-
rooms[roomId] = { sockets: [], userIds: []};
13+
rooms[roomId] = { sockets: [], userIds: [], matchedUserIds: []};
1014
}
1115

12-
// add user to room
13-
rooms[roomId].sockets.push(ws);
14-
rooms[roomId].userIds.push(userId);
16+
console.log(`BEFORE room Info: userIds[${rooms[roomId].userIds}] || matchedusers[${rooms[roomId].matchedUserIds}]`)
17+
18+
const numOfMatchedUsers = rooms[roomId].matchedUserIds.length
19+
20+
// max 2 authorized users per room
21+
if (numOfMatchedUsers < 2) {
22+
rooms[roomId].sockets.push(ws);
23+
rooms[roomId].userIds.push(userId);
24+
rooms[roomId].matchedUserIds.push(userId);
25+
}
26+
27+
if (numOfMatchedUsers === 2){
28+
if (!rooms[roomId].matchedUserIds.includes(userId)){
29+
console.log(`User ${userId} is denied access to room ${roomId}`);
30+
ws.send(JSON.stringify({ type: 'accessDenied', message: 'You are not allowed to access this room.' }));
31+
ws.close();
32+
return;
33+
} else {
34+
// case: matched user rejoins an un-deleted room
35+
rooms[roomId].sockets.push(ws);
36+
rooms[roomId].userIds.push(userId);
37+
}
38+
}
39+
40+
1541
// set websocket roomId
1642
ws.roomId = roomId;
1743

18-
console.log(`User ${userId} joined room ${roomId}`);
44+
console.log(`[ROOM MANAGER] User ${userId} joined room ${roomId}`);
45+
console.log(`AFTER room Info: userIds[${rooms[roomId].userIds}] || matchedusers[${rooms[roomId].matchedUserIds}]`)
46+
1947

2048
// notify users of roomId of updated user list to display in frontend
2149
broadcastUserListUpdate(roomId);
@@ -73,4 +101,9 @@ function manageRoom(ws, roomId, userId, type) {
73101
}
74102
}
75103

76-
module.exports = { manageRoom };
104+
function getUsersInRoom(roomId){
105+
return rooms[roomId]?.userIds || [];
106+
}
107+
108+
module.exports = { manageRoom, getUsersInRoom };
109+

Backend/CollabService/websocket/roomManagerSocket.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const WebSocket = require('ws');
2-
const { manageRoom } = require('../utils/roomManager');
2+
const { manageRoom, getUsersInRoom} = require('../utils/roomManager');
33

44
function setupWebSocket(server) {
55
const wss = new WebSocket.Server({ server });
@@ -19,6 +19,13 @@ function setupWebSocket(server) {
1919
// remove user from room with roomId
2020
manageRoom(ws, data.roomId, data.userId, "leave");
2121
break;
22+
case 'requestUserList':
23+
const users = getUsersInRoom(data.roomId);
24+
ws.send(JSON.stringify({
25+
type: 'usersListUpdate',
26+
users
27+
}));
28+
break;
2229
default:
2330
console.error('Unknown message type');
2431
}

Frontend/src/components/collab/CollaborationSpace.jsx

Lines changed: 101 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import CollabNavigationBar from './CollabNavigationBar';
99
import CodeSpace from './CodeSpace';
1010
import { Container, Row, Col } from 'react-bootstrap';
1111
import collabService from '../../services/collab';
12+
import Toast from 'react-bootstrap/Toast';
13+
import ToastContainer from 'react-bootstrap/ToastContainer';
14+
import Spinner from 'react-bootstrap/Spinner';
1215

1316
const CollaborationSpace = () => {
1417
const navigate = useNavigate();
@@ -22,14 +25,27 @@ const CollaborationSpace = () => {
2225
const [language, setLanguage] = useState("python") // set default language to python
2326
const [output, setOutput] = useState("")
2427

25-
// use https://emkc.org/api/v2/piston/runtimes to GET other languages
2628
const LANGUAGEVERSIONS = {
2729
"python" : "3.10.0",
2830
"java" : "15.0.2",
2931
"c++": "10.2.0"
30-
}
32+
};
33+
34+
const [showAccessDeniedToast, setShowAccessDeniedToast] = useState(
35+
JSON.parse(sessionStorage.getItem('showAccessDeniedToast')) || false
36+
);
37+
const [toastMessage, setToastMessage] = useState('');
38+
const [loading, setLoading] = useState(
39+
JSON.parse(sessionStorage.getItem('loading')) !== false
40+
);
41+
42+
const handleCloseToast = () => {
43+
setShowAccessDeniedToast(false);
44+
sessionStorage.setItem('showAccessDeniedToast', false);
45+
navigate("/home");
46+
};
3147

32-
{/* Set up websockets for room management on client side, and collaboration for Yjs */}
48+
// Set up websockets for room management on client side, and collaboration for Yjs
3349
useEffect(() => {
3450
const fetchUser = async () => {
3551
const user = await getUserFromToken();
@@ -42,25 +58,40 @@ const CollaborationSpace = () => {
4258
};
4359

4460
fetchUser();
45-
}, [])
61+
// Sync states with sessionStorage
62+
return () => {
63+
sessionStorage.setItem('loading', loading);
64+
sessionStorage.setItem('showAccessDeniedToast', showAccessDeniedToast);
65+
};
66+
}, [loading, showAccessDeniedToast]);
4667

4768
const initiateWebSocket = (userId) => {
48-
49-
// create websocket server for room management
5069
const websocket = new WebSocket("ws://localhost:3004");
5170
setWebsocket(websocket);
5271

5372
websocket.onopen = () => {
54-
// notify the server user has joined
5573
websocket.send(JSON.stringify( {type: 'joinRoom', roomId, userId: userId}));
56-
}
74+
75+
// Request userIds when matched user rejoins
76+
websocket.send(JSON.stringify({ type: 'requestUserList', roomId }));
77+
};
5778

58-
// on getting a reply from server
5979
websocket.onmessage = (event) => {
6080
const data = JSON.parse(event.data);
81+
const stringData = JSON.stringify(data);
82+
console.log(`[FRONTEND] data message is ${stringData}`);
6183
switch (data.type) {
6284
case 'usersListUpdate':
63-
setUsers(data.users);
85+
setUsers(data.users); // Update the user list
86+
setShowAccessDeniedToast(false);
87+
setLoading(false); // Access allowed; stop loading
88+
sessionStorage.setItem('loading', false);
89+
break;
90+
case 'accessDenied':
91+
setToastMessage(data.message);
92+
setShowAccessDeniedToast(true);
93+
setLoading(false);
94+
sessionStorage.setItem('showAccessDeniedToast', true);
6495
break;
6596
default:
6697
console.log("No messages received from room management server");
@@ -88,69 +119,85 @@ const CollaborationSpace = () => {
88119
// clean up for room management
89120
wsProvider.destroy();
90121
doc.destroy();
91-
}
92-
}
93-
94-
{/* Functions to handle interaction with UI elements */}
122+
};
123+
};
95124

96125
const handleExit = () => {
97-
// Notify server 3004 user is leaving
98126
websocket.send(JSON.stringify({ type: 'leaveRoom', roomId, userId}));
99-
100-
// Clean up Yjs document and provider before going back to home
101-
if (provider) {
102-
provider.destroy();
103-
}
104-
105-
if (yDoc) {
106-
yDoc.destroy();
107-
}
108-
109-
navigate("/home")
127+
if (provider) provider.destroy();
128+
if (yDoc) yDoc.destroy();
129+
navigate("/home");
110130
};
111131

112132
const handleCodeRun = () => {
113133
const code_message = {
114134
"language": language,
115-
"files": [
116-
{
117-
"content": code
118-
}
119-
],
135+
"files": [{ "content": code }],
120136
"version": LANGUAGEVERSIONS[language]
121-
}
137+
};
122138

123139
collabService.getCodeOutput(code_message)
124-
.then(result => {
125-
console.log(result.data.run.output)
126-
setOutput(result.data.run.output)
127-
})
140+
.then(result => setOutput(result.data.run.output))
128141
.catch(err => console.log(err));
129-
130-
}
142+
};
131143

132144
const handleEditorChange = (value) => {
133145
const yText = yDoc.getText('monacoEditor');
134-
yText.delete(0, yText.length); // Clear existing content
135-
yText.insert(0, value); // Insert new content
146+
yText.delete(0, yText.length);
147+
yText.insert(0, value);
148+
};
149+
150+
if (loading) {
151+
return (
152+
<div style={{ textAlign: 'center', marginTop: '50px' }}>
153+
<Spinner animation="border" variant="primary" role="status">
154+
<span className="visually-hidden">Loading...</span>
155+
</Spinner>
156+
<p>Loading collaboration space...</p>
157+
</div>
158+
);
136159
}
137160

138161
return (
139-
<div>
140-
<CollabNavigationBar handleExit={handleExit} handleCodeRun={handleCodeRun} users={users} setLanguage={setLanguage} language={language}/>
141-
<Container fluid style={{ marginTop: '20px' }}>
142-
<Row>
143-
<Col md={8}>
144-
<CodeSpace handleEditorChange={handleEditorChange} code={code} language={language} output={output}/>
145-
</Col>
146-
<Col md={4}>
147-
<QuestionDisplay/>
148-
<Chat/>
149-
</Col>
150-
</Row>
151-
</Container>
162+
<div style={{ textAlign: 'center', marginTop: '50px' }}>
163+
{showAccessDeniedToast ? (
164+
<ToastContainer
165+
className="p-3"
166+
position="top-center"
167+
style={{ zIndex: 1 }}
168+
>
169+
<Toast
170+
onClose={handleCloseToast}
171+
show={showAccessDeniedToast}
172+
delay={3000}
173+
autohide
174+
bg="danger"
175+
>
176+
<Toast.Body className='text-white'>
177+
<strong>{toastMessage}</strong>
178+
</Toast.Body>
179+
</Toast>
180+
</ToastContainer>
181+
) : (
182+
<>
183+
<div>
184+
<CollabNavigationBar handleExit={handleExit} handleCodeRun={handleCodeRun} users={users} setLanguage={setLanguage} language={language}/>
185+
<Container fluid style={{ marginTop: '20px' }}>
186+
<Row>
187+
<Col md={8}>
188+
<CodeSpace handleEditorChange={handleEditorChange} code={code} language={language} output={output}/>
189+
</Col>
190+
<Col md={4}>
191+
<QuestionDisplay/>
192+
<Chat/>
193+
</Col>
194+
</Row>
195+
</Container>
196+
</div>
197+
</>
198+
)}
152199
</div>
153200
);
154201
};
155202

156-
export default CollaborationSpace;
203+
export default CollaborationSpace;

0 commit comments

Comments
 (0)