diff --git a/client-side/src/components/Footer/Footer.jsx b/client-side/src/components/Footer/Footer.jsx index 6acad15..d0b74d2 100644 --- a/client-side/src/components/Footer/Footer.jsx +++ b/client-side/src/components/Footer/Footer.jsx @@ -43,9 +43,12 @@ export default function Footer() {
© {year} CodeHub. All Rights Reserved. +
{/* Contact Us */} + License + Code of Conduct

@@ -72,4 +75,3 @@ export default function Footer() { ) } - diff --git a/client-side/src/pages/DashBoard/dashboard.jsx b/client-side/src/pages/DashBoard/dashboard.jsx index e7dc058..7c12b27 100644 --- a/client-side/src/pages/DashBoard/dashboard.jsx +++ b/client-side/src/pages/DashBoard/dashboard.jsx @@ -38,8 +38,18 @@ export default function UserHome() { if (id) { cfID = id; } - console.log(cfID); - + const redirect = async (cfID) => { + try{ + const response = await axios.get(`${process.env.REACT_APP_SERVER_BASE_URL}/check/user/${cfID}`,{withCredentials: true}); + if(response.data.status === false){ + navigate("/"); + } + }catch(error){ + console.log(error); + navigate("/"); + } + + } let userData = { status: "", data: {} }; let userRating = { status: "", data: {} }; let userSubmissions = { status: "", data: {} }; @@ -100,6 +110,7 @@ export default function UserHome() { // API calls and Initialisation of Data Members try { console.log(cfID); + const userDataAPI = await axios.get("https://codeforces.com/api/user.info?handles=" + cfID); const userRatingAPI = await axios.get("https://codeforces.com/api/user.rating?handle=" + cfID); const userSubmissionsAPI = await axios.get("https://codeforces.com/api/user.status?handle=" + cfID); @@ -107,7 +118,7 @@ export default function UserHome() { userData.status = userDataAPI.data.status; userData.data = userDataAPI.data.result[0]; - + const userUpdate = await axios.post(`${process.env.REACT_APP_SERVER_BASE_URL}/updateCFData`,{username:userData.data.handle,avatar:userData.data.avatar,rating:userData.data.rating,rank:userData.data.rank},{withCredentials: true}); userRating.status = userRatingAPI.data.status; userRating.data = userRatingAPI.data.result; @@ -244,6 +255,7 @@ export default function UserHome() { } useEffect(() => { + redirect(cfID); fetchData(); }, [cfID]); diff --git a/client-side/src/pages/Leaderboard/LeaderUser.jsx b/client-side/src/pages/Leaderboard/LeaderUser.jsx new file mode 100644 index 0000000..6fb3f4c --- /dev/null +++ b/client-side/src/pages/Leaderboard/LeaderUser.jsx @@ -0,0 +1,82 @@ +import React from 'react'; +import './Leaderboard.css'; // Ensure this includes styles for user avatars, ranks, etc. +import { useNavigate } from 'react-router-dom'; +function LeaderUser(props) { + const { avatar, name, rank, rating, positionChange, title, cfID } = props; + const navigate = useNavigate(); + // Compare rating with the leaderboard + // Rank color based on Codeforces rank + const rankColor = getRankColor(title); + + // Handle user click to navigate to the user's Codeforces profile + const handleUserClick = () => { + navigate(`/get-codeforces-profile/${cfID}`); + }; + const handleCodeforcesRedirect = () => { + window.open(`https://codeforces.com/profile/${cfID}`, "_blank"); + }; + + return ( +
+
+ + {`${name} + #{rank} + {name} + + +
+ + +
+
+ {positionChange === 0 && ( + + 0 + + )} + {positionChange > 0 && ( + + +{positionChange} + + )} + {positionChange < 0 && ( + + {positionChange} + + )} +
+
+
+ ); +} + +function getRankColor(rank) { + switch(rank.toLowerCase()) { + case 'newbie': return '#A0A0A0'; // gray + case 'pupil': return '#66CDAA'; // medium sea green + case 'specialist': return '#40E0D0'; // turquoise (cyan) + case 'expert': return '#4682B4'; // steel blue (blue) + case 'candidate master': return '#8A2BE2'; // blue violet (purple) + case 'master': return '#FF7F50'; // coral (orange) + case 'international master': return '#B22222'; // firebrick (red) + case 'grandmaster': return '#8B0000'; // dark red + case 'international grandmaster': return '#8B0000'; // dark red + case 'legendary grandmaster': return '#8B0000'; // dark red + default: return '#000000'; // black + } +} + +export default LeaderUser; diff --git a/client-side/src/pages/Leaderboard/Leaderboard.css b/client-side/src/pages/Leaderboard/Leaderboard.css index 3ebdcc0..9f4b50e 100644 --- a/client-side/src/pages/Leaderboard/Leaderboard.css +++ b/client-side/src/pages/Leaderboard/Leaderboard.css @@ -1,56 +1,75 @@ .leader-heading { - font-size: xx-large; - color: #F94479; + font-size: 36px; + font-weight: bold; + color: #3f3f3f; + margin-bottom: 10px; +} + +.leader-description { + font-size: 16px; + color: #555; + line-height: 1.6; + max-width: 80%; + margin: 0 auto; +} +.leader-heading-container { text-align: center; - padding: 20px; + margin-bottom: 20px; + margin-top:20px; } + .leader-box { - padding: 10px; - background-color: white; - color: rgb(39, 8, 82); - text-align: center; - margin-left: 20px; - margin-right: 20px; - border: 0.5px solid rgb(39, 8, 82); display: flex; - flex-direction: row; justify-content: space-between; + padding: 10px; + border: 1px solid #ddd; + margin: 5px 0; + border-radius: 5px; + background-color: #f7f7f7; cursor: pointer; + transition: background-color 0.3s ease; +} + +.leader-box:hover { + background-color: #e0e0e0; +} + +.leader-info { + display: flex; + align-items: center; +} + +.user-avatar { + width: 40px; + height: 40px; + border-radius: 50%; + margin-right: 10px; } -.leader-box:hover{ - background-color: rgb(234, 227, 252); +.leader-rank { + font-size: 16px; + font-weight: bold; +} +.leader-rating { + display: flex; + align-items: center; } -.leader-title { +.leader-rating-value { + font-size: 18px; font-weight: bold; - width: 100%; - font-size: larger; - text-align: left; -} -.leader-date { - width: 100%; - text-align: left; - font-size: smaller; - margin-bottom: 5px; -} -.leader-body { - text-align: justify; - width: 100%; -} -pre { - white-space: pre-wrap; - } -.leader-footer { - text-align: center; - font-size: small; - margin-left: 20px; - margin-right: 20px; + margin-right: 10px; +} + +.rating-change { + font-size: 16px; } -@media screen and (max-width: 480px) { - .leader-reg{ - display: none; - } -} \ No newline at end of file +.positive { + color: green; +} + +.negative { + color: red; +} diff --git a/client-side/src/pages/Leaderboard/Leaderboard.jsx b/client-side/src/pages/Leaderboard/Leaderboard.jsx index a084cfe..0e215ce 100644 --- a/client-side/src/pages/Leaderboard/Leaderboard.jsx +++ b/client-side/src/pages/Leaderboard/Leaderboard.jsx @@ -5,36 +5,9 @@ import axios from 'axios'; import NavSpace from '../../components/NavSpace'; import Spinner from '../../components/Spinner/Spinner'; import Alert from '../../components/Alert/Alert'; -// import NavBarSecond from '../../components/NavBar/NavBarSecond'; import Footer from '../../components/Footer/Footer'; -import { useNavigate } from 'react-router-dom'; import { useSelector } from 'react-redux'; - -function LeaderUser(props) { - const navigate = useNavigate() - function handleUserClick(){ - navigate(`/get-codeforces-profile/${props.name}`) - } - - return ( -
-
handleUserClick()}> -
- - - #{props.rank} - - - {props.name} - -
- {/*
- {props.regNo} -
*/} -
-
- ); -} +import LeaderUser from './LeaderUser'; export default function Leaderboard() { const { user } = useSelector((state) => state.auth); @@ -42,102 +15,110 @@ export default function Leaderboard() { ); - - async function SortUsersByRating(userBoardInfo){ - //List of all the Codeforces contests - const contests = await axios.get('https://codeforces.com/api/contest.list') - const contests_data = contests.data.result; - - var latestContestId; - for(const contest of contests_data){ - if(contest.phase === "FINISHED"){ - latestContestId = contest.id; - break; - } - } - - //Users who have given latest contest - const users = await axios.get(`https://codeforces.com/api/contest.ratingChanges?contestId=${latestContestId}`) - const userRatings = []; - - - for (const userInfo of userBoardInfo) { - var user = users.data.result.find((usr)=>usr.handle === userInfo.cfID) - if(!user){ - userRatings.push({handle:userInfo.cfID, rating: "N/A"}) - } - else{ - userRatings.push({handle:user.handle, rating: user.newRating}) - } - } - - - // Sort the users by rating in decreasing order - userBoardInfo.sort((a, b) => { - const aRating = userRatings.find((user) => user.handle === a.cfID).rating; - const bRating = userRatings.find((user) => user.handle === b.cfID).rating; - if (aRating === "N/A" && bRating === "N/A") { - return 0; - } else if (aRating === "N/A") { - return 1; - } else if (bRating === "N/A") { - return -1; - } else { - return bRating - aRating; - } - }); -} const updatePageHtml = async () => { - try { - // const user = await JSON.parse(localStorage.getItem(process.env.CODETOGETHER_APP_LOCALHOST_KEY)); - const LeaderboardAPIresponse = await axios.post(process.env.REACT_APP_SERVER_BASE_URL + '/leaderboard', { cfID: user.cfID }, { withCredentials: true }); - const userBoardInfo = LeaderboardAPIresponse.data.data; - console.log(userBoardInfo) - await SortUsersByRating(userBoardInfo) - const LeaderComponent = userBoardInfo.map((userInfo, index) => ); + // Get the latest contest ID + const contests = await axios.get('https://codeforces.com/api/contest.list'); + const latestContest = contests.data.result.find((contest) => contest.phase === 'FINISHED'); + const latestContestId = latestContest.id; + const leaderboardAPIResponse = await axios.post( + `${process.env.REACT_APP_SERVER_BASE_URL}/leaderboard`, + { contestId: latestContestId } + ); + const userBoardInfo = leaderboardAPIResponse.data.data; + + // // Get ratings from the latest contest + // const ratingChanges = await axios.get(`https://codeforces.com/api/contest.ratingChanges?contestId=${latestContestId}`); + // const participants = ratingChanges.data.result; + // console.log(participants); + // // Fetch user info for usual ratings + // const userRatings = await Promise.all( + // userBoardInfo.map(async (user) => { + // const userInfo = await axios.get(`https://codeforces.com/api/user.info?handles=${user.cfID}`); + // const userDetails = userInfo.data.result[0]; + // console.log(userDetails); + // return { + // cfID: user.cfID, + // avatar:userDetails.avatar, + // rank:userDetails.rank, + // usualRating: userDetails.rating || 0, + // newRating: participants.find((p) => p.handle === user.cfID)?.newRating || 0, + // }; + // }) + // ); + + // // Sort users by new ratings and usual ratings + // userRatings.sort((a, b) => b.usualRating - a.usualRating); + // const sortedByNewRating = sortUsersByRating(userRatings, 'newRating'); + // const sortedByUsualRating = sortUsersByRating(userRatings, 'usualRating'); + + // // Calculate position changes + // const leaderComponents = sortedByNewRating.map((user, index) => { + // const oldPosition = sortedByUsualRating.findIndex((u) => u.cfID === user.cfID) + 1; + // const newPosition = index + 1; + // const positionChange = oldPosition-newPosition; + // console.log(user); + const leaderComponents = userBoardInfo.map((user,index) => { + return ( + + ); + }); setPageHtml(<> -
+
- {/* */} -
Leaderboard
-

Inactive participant's ratings are considered 0(zero).

-
- {LeaderComponent} +
+

Leaderboard

+

+ Rankings are based on the latest contest results. Ratings of inactive participants are considered 0. +

+
+ +
+ {leaderComponents}
+ ); } catch (err) { setPageHtml( <> - {/* */} -
- +
+
); } - } + }; 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;