Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nth branch testing #92

Merged
68 changes: 68 additions & 0 deletions backend/collaboration-service/controller/collabController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const axios = require("axios");

/**
* POST /add
*
* Creates questions from form data and store in firebase
*
* Responses:
* - 500: Server error if something goes wrong while fetching data.
*/
const submitCode = async (req, res) => {
const { code, languageId } = req.body;

if (typeof code !== "string" || !languageId) {
return res.status(400).json({ error: "Invalid request data" });
}

const formData = {
language_id: languageId,
source_code: btoa(code),
};
const options = {
method: "POST",
url: process.env.REACT_APP_RAPID_API_URL,
params: { base64_encoded: "true", fields: "*" },
headers: {
"Content-Type": "application/json",
"X-RapidAPI-Host": process.env.REACT_APP_RAPID_API_HOST,
"X-RapidAPI-Key": process.env.REACT_APP_RAPID_API_KEY,
},
data: formData,
};
axios
.request(options)
.then(function (response) {
console.log("res.data", response.data);
return res.status(200).json({ submissionId: response.data.token });
})
.catch((err) => {
let error = err.response ? err.response.data : err;
console.log(error);
});
};

const getSubmissionResult = async (req, res) => {
const submissionId = req.params.submissionId;
const options = {
method: "GET",
url: process.env.REACT_APP_RAPID_API_URL + "/" + submissionId,
params: { base64_encoded: "true", fields: "*" },
headers: {
"X-RapidAPI-Host": process.env.REACT_APP_RAPID_API_HOST,
"X-RapidAPI-Key": process.env.REACT_APP_RAPID_API_KEY,
},
};
try {
let response = await axios.request(options);
return res.status(200).json(response.data);
} catch (err) {
console.error("Error fetching submission result:", err);
let error = err.response
? err.response.data
: { message: "Internal Server Error" };
return res.status(500).json(error); // Handle errors appropriately
}
};

module.exports = { submitCode, getSubmissionResult };
208 changes: 130 additions & 78 deletions backend/collaboration-service/handler/socketHandler.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,76 @@
// Author(s): Xue Ling, Xiu Jia, Calista, Andrew
const { getRandomQuestion, getComplexity } = require("../service/questionService");
// Author(s): Xue Ling, Xiu Jia, Calista
const {
getRandomQuestion,
getComplexity,
} = require("../service/questionService");

const db = require("../config/firebase");

let socketMap = {};
let intervalMap = {};

let latestContentText = {};
let latestContentCode = {};
let latestLanguage = {}
let latestLanguage = {};
let haveNewData = {};
let activeUserInRoom = {}
let activeUserInRoom = {}; // track user details in rooms
let submitStatus = {};
let activeIdInRoom = {}; //track socket ids in rooms

const handleSocketIO = (io) => {
io.on("connection", (socket) => {
console.log(`A user connected with socket ID: ${socket.id}`);


socket.on("createSocketRoom", async ({ data, id, currentUser }) => {
// Store the socket id for the user
socketMap[currentUser] = socket.id;
socket.roomId = id;

socket.join(id);
console.log(`User with socket ID ${socket.id} joined room with ID ${id}`);
if (activeUserInRoom[id]) {
activeUserInRoom[id] = activeUserInRoom[id] + 1
} else {
activeUserInRoom[id] = 1

if (!activeIdInRoom[id]) {
activeIdInRoom[id] = []; // Initialize as an empty array
}
console.log(`UactiveUserInRoom[id]: ${activeUserInRoom[id]}`);
activeIdInRoom[id].push(socket.id);

console.log(`UactiveUserInRoom[id]: ${activeIdInRoom[id].length}`);

const room = io.sockets.adapter.rooms.get(id);

if (room && room.size === 2) {
const { user1, user2 } = data;

activeUserInRoom[id] = { user1, user2 };

const complexity = getComplexity(user1, user2);

const questionData = await getRandomQuestion(user1.category, complexity);
const questionData = await getRandomQuestion(
user1.category,
complexity
);

activeUserInRoom[id] = {
user1: user1,
user2: user2,
questionData: questionData,
length: 2,
};

io.in(id).emit("readyForCollab", {
id: id,
user1,
user2,
questionData
questionData,
});

console.log(`Room ${id} is ready. Collaboration question sent: ${questionData}`);
if (!submitStatus[id]) {
submitStatus[id] = { user1: false, user2: false };
}

console.log(
`Room ${id} is ready. Collaboration question sent: ${questionData}`
);



Expand Down Expand Up @@ -105,45 +128,9 @@ const handleSocketIO = (io) => {

// a timer to backup the current collab data
const interval = setInterval(async () => {
const currentTime = new Date().toISOString();
const currentContentText = latestContentText[id];
const currentContentCode = latestContentCode[id];
const currentLanguage = latestLanguage[id] || null;
const periodicData = {
user1,
user2,
questionData,
currentLanguage,
currentContentText,
currentContentCode,
timestamp: currentTime
};

try {
const collabRef = db.collection("collabs").doc(id);
const doc = await collabRef.get();

if (doc.exists) {
if (haveNewData[id]) {
haveNewData[id] = false;
await collabRef.update(periodicData);
console.log(`Collab Data for roomid ${id} updated to Firebase at ${currentTime}`);
}
} else {

await collabRef.set({
roomId: id,
...periodicData
});
console.log(`New Collab page for roomid ${id} recorded to Firebase at ${currentTime}`);
}

} catch (error) {
console.error("Fail to save to database: ", error);
}
updateCollabData(id);
}, 5000);


intervalMap[id] = interval;
}
});
Expand Down Expand Up @@ -173,41 +160,106 @@ const handleSocketIO = (io) => {
socket.to(id).emit("languageChange", { language });
});

// Handle disconnection
socket.on("disconnect", () => {
// Delete the
activeUserInRoom[socket.roomId] = activeUserInRoom[socket.roomId] - 1;

if(activeUserInRoom[socket.roomId] == 0) {
console.log(`All users in roomId ${socket.roomId} disconnected, deleting room data`);
delete activeUserInRoom[socket.roomId];

clearInterval(intervalMap[socket.roomId]);
delete intervalMap[socket.roomId];
delete latestContentText[socket.roomId];
delete latestContentCode[socket.roomId];
delete latestLanguage[socket.roomId];
delete haveNewData[socket.roomId];
// Handle submission
socket.on("endSession", () => {
if (socket.id === activeIdInRoom[socket.roomId][0]) {
submitStatus[socket.roomId].user1 = true;
} else if (socket.id === activeIdInRoom[socket.roomId][1]) {
submitStatus[socket.roomId].user2 = true;
}

for (let user in socketMap) {
if (socketMap[user] === socket.id) {
delete socketMap[user];
break;
}
console.log(submitStatus);
if (submitStatus[socket.roomId].user1 && submitStatus[socket.roomId].user2) {
console.log("Both users have submitted in room:", socket.roomId);
updateCollabData(socket.roomId);
activeUserInRoom[socket.roomId].length = 0;
const roomId = socket.roomId;
const userEmails = activeUserInRoom[roomId];
io.to(socket.roomId).emit('sessionEnded', {user1Email: userEmails.user1.email, user2Email: userEmails.user2.email, roomId: roomId});
socket.disconnect();
}
});

if (socket.roomId) {
socket.leave(socket.roomId);
console.log(`User with socket ID ${socket.id} disconnected, leaving ${socket.roomId}`);
// Handle disconnection
socket.on("disconnect", () => {
console.log("disconnecting...");
const activeUsers = activeUserInRoom[socket.roomId];
if (!activeUsers || activeUsers.length === 0) {
console.log(`No active users in room: ${socket.roomId}`);
} else {
console.log(`User with socket ID ${socket.id} disconnected`);
activeUserInRoom[socket.roomId].length =
Math.max(0, activeUserInRoom[socket.roomId].length - 1);;

if (activeUserInRoom[socket.roomId].length == 0) {
console.log(
`All users in roomId ${socket.roomId} disconnected, deleting room data`
);
delete activeUserInRoom[socket.roomId];
delete activeIdInRoom[socket.roomId];

clearInterval(intervalMap[socket.roomId]);
delete intervalMap[socket.roomId];
delete latestContentText[socket.roomId];
delete latestContentCode[socket.roomId];
delete latestLanguage[socket.roomId];
delete haveNewData[socket.roomId];
}

for (let user in socketMap) {
if (socketMap[user] === socket.id) {
delete socketMap[user];
break;
}
}
console.log(
`User with socket ID ${socket.id} disconnected, leaving ${socket.roomId}`
);
}
});


});
};

async function updateCollabData(id) {
const currentTime = new Date().toISOString();
const currentContentText = latestContentText[id];
const currentContentCode = latestContentCode[id];
const currentLanguage = latestLanguage[id] || null;
const user1 = activeUserInRoom[id].user1;
const user2 = activeUserInRoom[id].user2;
const questionData = activeUserInRoom[id].questionData;
const periodicData = {
user1,
user2,
questionData,
currentLanguage,
currentContentText,
currentContentCode,
timestamp: currentTime,
};

try {
const collabRef = db.collection("collabs").doc(id);
const doc = await collabRef.get();

if (doc.exists) {
if (haveNewData[id]) {
haveNewData[id] = false;
await collabRef.update(periodicData);
console.log(
`Collab Data for roomid ${id} updated to Firebase at ${currentTime}`
);
}
} else {
await collabRef.set({
roomId: id,
...periodicData,
});
console.log(
`New Collab page for roomid ${id} recorded to Firebase at ${currentTime}`
);
}
} catch (error) {
console.error("Fail to save to database: ", error);
}
}
// Export user functions
module.exports = { handleSocketIO };
module.exports = { handleSocketIO };
21 changes: 20 additions & 1 deletion backend/collaboration-service/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const http = require("http");
const { Server } = require("socket.io");
const port = process.env.PORT || 5003;

const collaborationRoute = require("./routes/collaborationRoute");

// Import the socket handler
const { handleSocketIO } = require("./handler/socketHandler.js");

Expand All @@ -25,4 +27,21 @@ handleSocketIO(io); // This calls the function to set up the socket listeners
// Start the server
server.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
});

const express = require("express");
const cors = require("cors");
const app = express();

app.use(cors());
app.use(express.json());

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Routes
app.use("/collaboration", collaborationRoute);

// app.listen(5004, () => {
// console.log(`Example app listening on port ${5004}`);
// });
Loading