>
);
}
- }
+ };
useEffect(() => {
updatePageHtml();
}, []);
-
- return (
- <>{PageHtml}>
- );
+ return <>{PageHtml}>;
}
diff --git a/client-side/src/pages/Noticeboard/Noticeboard.jsx b/client-side/src/pages/Noticeboard/Noticeboard.jsx
index 61d7745..d65d8da 100644
--- a/client-side/src/pages/Noticeboard/Noticeboard.jsx
+++ b/client-side/src/pages/Noticeboard/Noticeboard.jsx
@@ -44,13 +44,9 @@ export default function NoticeBoard() {
const updatePageHtml = async () => {
try {
- const NoticeboardAPIresponse = await axios.get(process.env.REACT_APP_SERVER_PATH + '/noticeboard');
+ const NoticeboardAPIresponse = await axios.get(process.env.REACT_APP_SERVER_BASE_URL + '/noticeboard');
const noticeList = NoticeboardAPIresponse.data.data;
- // data is loaded.
- console.log(noticeList);
-
-
setPageHtml(<>
diff --git a/server-side/controllers/Client/auth/checkUser.js b/server-side/controllers/Client/auth/checkUser.js
new file mode 100644
index 0000000..8be8021
--- /dev/null
+++ b/server-side/controllers/Client/auth/checkUser.js
@@ -0,0 +1,19 @@
+const User = require("../../../model/userModel");
+const AsyncErrorHandler = require("../../../ErrorHandlers/async_error_handler");
+
+module.exports = AsyncErrorHandler(async (req, res, next) => {
+ const { id } = req.params;
+ try {
+ const user = await User.findOne({ cfID: id });
+ if (!user) {
+ return res.status(400).json({ status: false, msg: "User not found" });
+ }
+ if(user.emailVerified === false|| user.cfVerified === false){
+ return res.status(400).json({ status: false, msg: "Email not verified or cfID not verified" });
+ }
+ return res.status(200).json({ status: true, msg: "User found" });
+ } catch (error) {
+ next(error);
+ }
+});
+
diff --git a/server-side/controllers/Client/auth/register.js b/server-side/controllers/Client/auth/register.js
index 4692689..f701ef4 100644
--- a/server-side/controllers/Client/auth/register.js
+++ b/server-side/controllers/Client/auth/register.js
@@ -36,14 +36,40 @@ const Register = AsyncErrorHandler(async (req, res, next) => {
}
return res.status(500).json({ success: false, message: "An error occurred while verifying codeforces ID" });
}
+ const hashedPassword = await bcrypt.hash(password, 10);
//Check if the user already exists in database
const existingUser = await User.findOne({ $or: [{ email }, { cfID }, { username }] });
-
if (existingUser && !existingUser.emailVerified) {
- //Delete unverified user
- await User.deleteOne({ _id: existingUser._id });
- await VerificationToken.deleteOne({ email: existingUser.email });
+ await User.findByIdAndUpdate(existingUser._id, {
+ $set: {
+ username,
+ cfID,
+ password:hashedPassword
+ }
+ }).save();
+ await VerificationToken.deleteOne({email:email})
+ //Generate new Verification tokens
+ const verificationCode = utils.generateVerificationCode();
+ //Create a new entry in verification token model
+ const token = new VerificationToken({
+ email: email,
+ code: verificationCode
+ })
+ await token.save();
+
+ //Generate email
+ const subject = "Email Verification";
+ const text = utils.createVerificationEmail({ verificationCode, subject });
+
+ //Send email
+ await SendEmail(email, subject, text);
+ return res.status(201).json({
+ success: true,
+ message: "Now please verify your codeforces Id and Email to complete the Registration",
+ emailVerified: false
+ })
+
}
else if (existingUser && existingUser.cfVerified) {
return res.status(400).json({
@@ -52,8 +78,42 @@ const Register = AsyncErrorHandler(async (req, res, next) => {
})
}
- //Hash the password
- const hashedPassword = await bcrypt.hash(password, 10);
+
+ //saving the data in a temporary user model which will be deleted at the time of saving actual user
+ const tempCheck = await tempUser.findOne({ $or: [{ email }, { cfID }, { username }] });
+ if(tempCheck && !tempCheck.emailVerified) {
+ await tempUser.findByIdAndUpdate(tempCheck._id,{
+ $set: {
+ username,
+ cfID,
+ password: hashedPassword
+ }
+ }
+ );
+ await VerificationToken.deleteOne({email:email});
+ //Generate new Verification tokens
+ const verificationCode = utils.generateVerificationCode();
+ //Create a new entry in verification token model
+ const token = new VerificationToken({
+ email: email,
+ code: verificationCode
+ })
+ await token.save();
+
+ //Generate email
+ const subject = "Email Verification";
+ const text = utils.createVerificationEmail({ verificationCode, subject });
+
+ //Send email
+ await SendEmail(email, subject, text);
+ return res.status(201).json({
+ success: true,
+ message: "Now please verify your codeforces Id and Email to complete the Registration",
+ emailVerified: false
+ })
+
+ }
+
//Create new user
let user = {
username,
@@ -62,7 +122,6 @@ const Register = AsyncErrorHandler(async (req, res, next) => {
password: hashedPassword
};
- //saving the data in a temporary user model which will be deleted at the time of saving actual user
const newTempUser = new tempUser(user);
await newTempUser.save();
@@ -83,7 +142,7 @@ const Register = AsyncErrorHandler(async (req, res, next) => {
await SendEmail(email, subject, text);
//Send response to client
- res.status(201).json({
+ return res.status(201).json({
success: true,
message: "Now please verify your codeforces Id and Email to complete the Registration",
emailVerified: false
diff --git a/server-side/controllers/Client/auth/verifyCfID.js b/server-side/controllers/Client/auth/verifyCfID.js
index dca1a5f..c1e6a78 100644
--- a/server-side/controllers/Client/auth/verifyCfID.js
+++ b/server-side/controllers/Client/auth/verifyCfID.js
@@ -93,7 +93,7 @@ const VerifyCfID = AsyncErrorHandler(async (req, res, next) => {
await newUser.save();
//delete the temporary user
- await tempUser.deleteOne({ cfID });
+ await tempUser.findByIdAndDelete(user._id);
//Success response to client
res.status(200).json({ success: true, message: "cfID verified successfully" });
diff --git a/server-side/controllers/Client/controller.js b/server-side/controllers/Client/controller.js
index 676d93e..2bde0f1 100644
--- a/server-side/controllers/Client/controller.js
+++ b/server-side/controllers/Client/controller.js
@@ -7,6 +7,7 @@ const checkSession = require("./auth/checkSession")
const userFeedback = require("./userFeedBack");
const logout = require("./auth/logout");
const ForgetPassword = require("./auth/ForgetPassword");
+const checkUser = require("./auth/checkUser");
module.exports = {
login,
register,
@@ -16,5 +17,6 @@ module.exports = {
checkSession,
userFeedback,
logout,
- ForgetPassword
+ ForgetPassword,
+ checkUser
};
\ No newline at end of file
diff --git a/server-side/controllers/Client/leaderboard.controller.js b/server-side/controllers/Client/leaderboard.controller.js
new file mode 100644
index 0000000..2bfc544
--- /dev/null
+++ b/server-side/controllers/Client/leaderboard.controller.js
@@ -0,0 +1,197 @@
+const Leaderboard = require("../../model/leaderboard");
+const CFdata = require("../../model/CFdata"); // Schema to store user data
+const User = require("../../model/userModel");
+const axios = require("axios");
+
+const getRank = (rating) => {
+ switch (true) {
+ case rating < 1200:
+ return "newbie";
+ case rating < 1400:
+ return "pupil";
+ case rating < 1600:
+ return "specialist";
+ case rating < 1800:
+ return "expert";
+ case rating < 2000:
+ return "candidate";
+ case rating < 2200:
+ return "master";
+ case rating < 2400:
+ return "International master";
+ case rating < 2800:
+ return "grandmaster";
+ case rating < 3000:
+ return "International grandmaster";
+ case rating < 4000:
+ return "Legendary grandmaster";
+ case rating >= 4000:
+ return "Tourist";
+ default:
+ return "Unknown";
+ }
+};
+
+const getLeaderboard = async (req, res, next) => {
+ try {
+ const { contestId } = req.body;
+
+ if (!contestId) {
+ return res.status(400).json({ status: false, msg: "Contest ID is required." });
+ }
+
+ // Check if contest already exists in the database
+ let contest = await Leaderboard.findOne({ contestId });
+ if (contest) {
+ return res.status(200).json({
+ status: true,
+ msg: "Contest already in the database",
+ data: contest.Leaderboard,
+ });
+ }
+
+ // Step 1: Fetch all CF data and users
+ const [cfData, users] = await Promise.all([CFdata.find(), User.find()]);
+
+ const cfDataMap = new Map(cfData.map((user) => [user.cfusername, user]));
+ const userMap = new Map(users.map((user) => [user.cfID, user]));
+
+ // Step 2: Fetch standings from Codeforces API
+ const standingsResponse = await axios.get(
+ `https://codeforces.com/api/contest.ratingChanges?contestId=${contestId}`
+ );
+ const standings = standingsResponse.data.result;
+
+ if (!standings.length) {
+ return res.status(404).json({
+ status: false,
+ msg: "No standings data found for the contest.",
+ url: `https://codeforces.com/api/contest.ratingChanges?contestId=${contestId}`,
+ data: standings,
+ });
+ }
+
+ // Step 3: Create new leaderboard array
+ const newBoard = [];
+ const prevLeaderboard = await Leaderboard.findOne().sort({ createdAt: -1 });
+ const prevLeaderboardMap = new Map(
+ prevLeaderboard?.Leaderboard.map((entry, index) => [
+ entry.username,
+ { ...entry, index },
+ ])
+ );
+
+ await Promise.all(
+ standings.map(async (participant) => {
+ const username = participant.handle;
+ const previousEntry = prevLeaderboardMap.get(username);
+ const cfDataEntry = cfDataMap.get(username);
+ const userDataEntry = userMap.get(username);
+
+ if (userDataEntry) {
+ const position = previousEntry
+ ? previousEntry.index - newBoard.length
+ : prevLeaderboard?.Leaderboard?.length || 0;
+
+ newBoard.push({
+ username,
+ position,
+ rating: participant.newRating,
+ avatar: cfDataEntry?.avatar || "https://via.placeholder.com/100",
+ rank: getRank(participant.newRating),
+ });
+
+ // Update CFData rating
+ await CFdata.findOneAndUpdate(
+ { userId: userDataEntry._id },
+ {
+ rating: participant.newRating,
+ avatar:
+ cfDataEntry?.avatar || "https://via.placeholder.com/100",
+ rank: getRank(participant.newRating),
+ cfusername: userDataEntry.cfID,
+ },
+ { upsert: true, new: true }
+ );
+ }
+ })
+ );
+
+ // Step 4: Add users from previous leaderboard who didn’t participate
+ prevLeaderboard?.Leaderboard.forEach((entry) => {
+ if (!newBoard.some((user) => user.username === entry.username)) {
+ newBoard.push({
+ username: entry.username,
+ position: entry.position,
+ rating: entry.rating,
+ avatar: entry.avatar,
+ rank: entry.rank,
+ });
+ }
+ });
+
+ // Step 5: Save or update the leaderboard in the database
+ contest = await Leaderboard.findOneAndUpdate(
+ { contestId },
+ { contestId, Leaderboard: newBoard },
+ { upsert: true, new: true }
+ );
+
+ return res.status(200).json({
+ status: true,
+ msg: "Leaderboard updated successfully",
+ data: contest.Leaderboard,
+ });
+ } catch (error) {
+ console.error("Error updating leaderboard:", error);
+ next(error);
+ }
+};
+const updateCFData = async (req, res, next) => {
+ try {
+ const { username, avatar, rating, rank } = req.body;
+
+ if (!username) {
+ return res.status(400).json({ status: false, msg: "Username is required." });
+ }
+
+ // Fetch user by cfID
+ const user = await User.findOne({ cfID: username });
+ if (!user) {
+ return res.status(404).json({ status: false, msg: "User not found." });
+ }
+
+ // Check if a CFdata entry exists for the user
+ let cfData = await CFdata.findOne({ userId: user._id });
+
+ if (!cfData) {
+ // Create a new CFdata entry
+ cfData = new CFdata({
+ userId: user._id,
+ rating,
+ avatar,
+ rank,
+ cfusername: username,
+ });
+ await cfData.save();
+ } else {
+ // Update CFdata entry if there are changes
+ if (cfData.rating !== rating || cfData.avatar !== avatar || cfData.rank !== rank) {
+ cfData.rating = rating;
+ cfData.avatar = avatar;
+ cfData.rank = rank;
+ await cfData.save();
+ }
+ }
+ return res.status(200).json({ status: true, msg: "CF data updated successfully.", data: cfData });
+ } catch (error) {
+ console.error("Error fetching CF data:", error);
+ next(error);
+ }
+};
+
+
+module.exports = {
+ getLeaderboard,
+ updateCFData,
+};
diff --git a/server-side/controllers/clientControllers.js b/server-side/controllers/clientControllers.js
index 99b0d5b..c2dd92e 100644
--- a/server-side/controllers/clientControllers.js
+++ b/server-side/controllers/clientControllers.js
@@ -47,23 +47,23 @@ module.exports.videos = async (req, res, next) => {
next(ex);
}
};
-module.exports.leaderboard = async (req, res, next) => {
- try {
- const {decoded}= req;
- const {cookieID}= decoded;
- const session = await ClientSessions.findOne({cookieID});
- if (!session || cookieID !== session.cookieID) {
- return res.status(401).json({ status: false, msg: "Session expired or invalid" });
- }
+// module.exports.leaderboard = async (req, res, next) => {
+// try {
+// const {decoded}= req;
+// const {cookieID}= decoded;
+// const session = await ClientSessions.findOne({cookieID});
+// if (!session || cookieID !== session.cookieID) {
+// return res.status(401).json({ status: false, msg: "Session expired or invalid" });
+// }
- const cfID = await Users.find().select(["cfID"]);
- return res.json({ status: true, data: cfID });
- }
- catch (error) {
- next(error);
- }
+// const cfID = await Users.find({ emailVerified: true, cfVerified: true }).select(["cfID"]);
+// return res.json({ status: true, data: cfID });
+// }
+// catch (error) {
+// next(error);
+// }
-};
+// };
module.exports.register = async (req, res, next) => {
try {
diff --git a/server-side/model/CFdata.js b/server-side/model/CFdata.js
new file mode 100644
index 0000000..56f3ee8
--- /dev/null
+++ b/server-side/model/CFdata.js
@@ -0,0 +1,28 @@
+const { Schema } = require("mongoose");
+const mongoose = require("mongoose");
+const CFdataSchema = new Schema({
+ userId: {
+ type: String,
+ ref: "User", // Referencing the User model
+ unique: true,
+ required: true,
+ },
+ rating: {
+ type: Number,
+ required: true,
+ },
+ avatar: {
+ type: String,
+ required: true,
+ },
+ rank: {
+ type: String,
+ required: true,
+ },
+ cfusername: {
+ type: String,
+ required: true,
+ },
+}, { timestamps: true });
+
+module.exports = mongoose.model("CFdata", CFdataSchema);
diff --git a/server-side/model/leaderboard.js b/server-side/model/leaderboard.js
new file mode 100644
index 0000000..5219989
--- /dev/null
+++ b/server-side/model/leaderboard.js
@@ -0,0 +1,39 @@
+const mongoose = require("mongoose");
+const { Schema } = mongoose;
+
+// Leaderboard Schema
+const LeaderboardSchema = new Schema({
+ contestId: {
+ type: String,
+ required: true,
+ unique: true,
+ },
+ Leaderboard: {
+ type: [
+ {
+ username: {
+ type: String,
+ required: true,
+ },
+ position: {
+ type: Number, // Change to `String` if rank isn't numeric
+ },
+ rating: {
+ type: Number,
+ required: true,
+ },
+ avatar: {
+ type: String, // Add avatar if required in leaderboard
+ },
+ rank: {
+ type: String, // Add rank if required in leaderboard
+ },
+ },
+ ],
+ default: [], // Ensures Leaderboard initializes as an empty array
+ },
+}, { timestamps: true }); // Automatically add createdAt and updatedAt timestamps
+
+LeaderboardSchema.index({ createdAt: -1 }); // Index to optimize recent leaderboard queries
+
+module.exports = mongoose.model("Leaderboard", LeaderboardSchema);
diff --git a/server-side/routes/clientRoutes.js b/server-side/routes/clientRoutes.js
index 6e1bfec..71eb7a4 100644
--- a/server-side/routes/clientRoutes.js
+++ b/server-side/routes/clientRoutes.js
@@ -1,12 +1,12 @@
const {
educationCategories,
videos,
- leaderboard,
+ // leaderboard,
contactUs,
noticeboard,
- register
+ register,
} = require("../controllers/clientControllers");
-
+const leaderboard = require("../controllers/Client/leaderboard.controller");
const controller = require("../controllers/Client/controller");
const verifyCookie = require("../middleware/verifyCookie");
const verifyPasswordReq = require("../middleware/verifyPasswordReq");
@@ -15,7 +15,15 @@ const router = require("express").Router();
// Routes that require authentication
router.post("/education", verifyCookie, educationCategories);
router.post("/education/videos", verifyCookie, videos);
-router.post("/leaderboard", verifyCookie, leaderboard);
+//@route POST /leaderboard
+//@desc Get leaderboard
+//@access Private
+router.post("/leaderboard",leaderboard.getLeaderboard );
+//@route POST /updateCFData
+//@desc Update CF Data
+//@access Private
+router.post("/updateCFData", leaderboard.updateCFData);
+
router.post("/feedback", verifyCookie, controller.userFeedback);
router.post("/logout", verifyCookie, controller.logout);
router.get("/check/session", verifyCookie, controller.checkSession);
@@ -31,19 +39,24 @@ router.post("/requestCfVerification", controller.generateCfVerificationRequestTo
//For Changing Password
-// @route POST api/forgetPassword
+// @route POST /forgetPassword
// @desc Forget Password
// @access Public
router.post("/forgetPassword",controller.ForgetPassword.ForgetPassword);
-// @route POST api/verifyPasswordChangeOTP
+// @route POST /verifyPasswordChangeOTP
// @desc Confirm User
// @access Public
router.post("/verifyPasswordChangeOTP",verifyPasswordReq, controller.ForgetPassword.VerifyPasswordChangeOTP);
-// @route POST api/confirmPasswordChange
+// @route POST /confirmPasswordChange
// @desc Confirm Password Change
// @access Public
router.post("/confirmPasswordChange",verifyPasswordReq,controller.ForgetPassword.ConfirmPasswordChange);
+// @route GET /check/user/:id
+// @desc Check if user exists
+// @access Public
+router.get("/check/user/:id",verifyCookie,controller.checkUser);
+
module.exports = router;