From 672da179e1a9bf03ac451817623fe545226e77bf Mon Sep 17 00:00:00 2001 From: kushwahramkumar2003 Date: Mon, 13 Nov 2023 15:54:00 +0530 Subject: [PATCH 1/2] Add CORS package and delete quiz with all its questions and results --- README.md | 2 +- Server/package-lock.json | 13 ++++++++ Server/package.json | 1 + Server/src/App.js | 2 ++ Server/src/controllers/quiz.controllers.js | 29 ++++++++++------- Server/src/models/Feedback.model.js | 36 ++++++++++++++++++++++ Server/src/models/Question.model.js | 14 ++++++++- Server/src/models/Quiz.model.js | 11 +++++-- Server/src/models/Result.model.js | 2 ++ 9 files changed, 95 insertions(+), 15 deletions(-) create mode 100644 Server/src/models/Feedback.model.js diff --git a/README.md b/README.md index 088dfd6..afedb02 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# Online-Quiz \ No newline at end of file +# Online-Quiz diff --git a/Server/package-lock.json b/Server/package-lock.json index d5e3b68..87f873b 100644 --- a/Server/package-lock.json +++ b/Server/package-lock.json @@ -12,6 +12,7 @@ "bcrypt": "^5.1.1", "bcryptjs": "^2.4.3", "cookie-parser": "^1.4.6", + "cors": "^2.8.5", "dotenv": "^16.3.1", "express": "^4.18.2", "express-rate-limit": "^7.1.4", @@ -315,6 +316,18 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", diff --git a/Server/package.json b/Server/package.json index af87b09..59badcb 100644 --- a/Server/package.json +++ b/Server/package.json @@ -14,6 +14,7 @@ "bcrypt": "^5.1.1", "bcryptjs": "^2.4.3", "cookie-parser": "^1.4.6", + "cors": "^2.8.5", "dotenv": "^16.3.1", "express": "^4.18.2", "express-rate-limit": "^7.1.4", diff --git a/Server/src/App.js b/Server/src/App.js index febdcdc..213d729 100644 --- a/Server/src/App.js +++ b/Server/src/App.js @@ -2,7 +2,9 @@ const express = require("express"); const routes = require("./routes/index.routes.js"); const app = express(); const cookieParser = require("cookie-parser"); +const cors = require("cors"); +app.use(cors({ origin: "*" })); app.use(express.json()); app.use(cookieParser()); app.use(express.urlencoded({ extended: true })); diff --git a/Server/src/controllers/quiz.controllers.js b/Server/src/controllers/quiz.controllers.js index d5cd7fa..9a1223a 100644 --- a/Server/src/controllers/quiz.controllers.js +++ b/Server/src/controllers/quiz.controllers.js @@ -1,6 +1,7 @@ const Quiz = require("../models/Quiz.model.js"); const Question = require("../models/Question.model.js"); const { ObjectId } = require("bson"); +const Result = require("../models/Result.model.js"); const asyncHandler = require("./../services/asyncHandler.js"); /********************************************************************** @@ -201,19 +202,25 @@ exports.updateQuestionById = asyncHandler(async (req, res) => { }); }); -// @desc Delete a quiz by ID -// @route DELETE /api/quizzes/:id -// @access Private/Admin +/************************************************************************* + * @desc Delete a quiz by ID + * @route DELETE api/v1/quiz/:id/delete + * @access Private/Admin + *************************************************************************/ exports.deleteQuizById = asyncHandler(async (req, res) => { - const quiz = await Quiz.findById(req.params.id); + const quizId = req.params.id; - if (quiz) { - await quiz.remove(); - res.json({ message: "Quiz removed" }); - } else { - res.status(404); - throw new Error("Quiz not found"); - } + // Delete all questions corresponding to the quiz + await Question.deleteMany({ quiz: quizId }); + + // Delete all results corresponding to the quiz + await Result.deleteMany({ quiz: quizId }); + + // Delete the quiz itself + // await Quiz.findByIdAndDelete(quizId); + await Quiz.deleteOne({ _id: quizId }); + + res.status(200).json({ success: true, message: "Quiz deleted successfully" }); }); // @desc Get quiz for attempt diff --git a/Server/src/models/Feedback.model.js b/Server/src/models/Feedback.model.js new file mode 100644 index 0000000..38a2725 --- /dev/null +++ b/Server/src/models/Feedback.model.js @@ -0,0 +1,36 @@ +const mongoose = require("mongoose"); + +const feedbackSchema = new mongoose.Schema({ + name: { + type: String, + required: true, + }, + email: { + type: String, + required: true, + }, + message: { + type: String, + required: true, + }, + createdAt: { + type: Date, + default: Date.now, + }, +}); + +// Add a pre-save hook to hash the password before saving to the database +feedbackSchema.pre("save", async function (next) { + // Hash the password using bcrypt + // ... + next(); +}); + +// Add a static method to find a user by email +feedbackSchema.statics.findByEmail = async function (email) { + // ... +}; + +const Feedback = mongoose.model("Feedback", feedbackSchema); + +module.exports = Feedback; diff --git a/Server/src/models/Question.model.js b/Server/src/models/Question.model.js index 3532e26..e6fb65a 100644 --- a/Server/src/models/Question.model.js +++ b/Server/src/models/Question.model.js @@ -1,5 +1,5 @@ const mongoose = require("mongoose"); - +const Result = require("./Result.model.js"); const questionSchema = new mongoose.Schema({ text: { type: String, @@ -27,6 +27,11 @@ const questionSchema = new mongoose.Schema({ message: "Answer must be one of the options", }, }, + quiz: { + type: mongoose.Schema.Types.ObjectId, + ref: "Quiz", + required: true, + }, }); // Log the question text before saving @@ -43,6 +48,13 @@ questionSchema.pre("deleteOne", { document: true }, async function (next) { { quiz: question.quiz }, { $pull: { answers: question.answer } } ); + + // Delete all questions corresponding to the quiz + await Question.deleteMany({ quiz: this._id }); + + // Delete all results corresponding to the quiz + await Result.deleteMany({ quiz: this._id }); + next(); }); diff --git a/Server/src/models/Quiz.model.js b/Server/src/models/Quiz.model.js index 1ab4e45..273e14e 100644 --- a/Server/src/models/Quiz.model.js +++ b/Server/src/models/Quiz.model.js @@ -1,4 +1,6 @@ const mongoose = require("mongoose"); +const Result = require("./Result.model.js"); +const Question = require("./Question.model"); const quizSchema = new mongoose.Schema({ title: { type: String, @@ -54,8 +56,13 @@ quizSchema.post("save", function (doc, next) { // Delete the results of the quiz before deleting the quiz quizSchema.pre("deleteOne", { document: true }, async function (next) { const quiz = this; - await Result.deleteMany({ quiz: quiz._id }); - next(); + try { + await Result.deleteMany({ quiz: quiz._id }); + await Question.deleteMany({ quiz: quiz._id }); + next(); + } catch (err) { + console.log(err); + } }); // Check if the user's answers are correct and return the score diff --git a/Server/src/models/Result.model.js b/Server/src/models/Result.model.js index 9e7cb84..7bf9d15 100644 --- a/Server/src/models/Result.model.js +++ b/Server/src/models/Result.model.js @@ -1,4 +1,6 @@ const mongoose = require("mongoose"); +const User = require("./User.model.js"); +const Feedback = require("./Feedback.model.js"); const resultSchema = new mongoose.Schema( { user: { From 438ca8cbf9d9e51b25a3c2a02ac10b7826f5e1ce Mon Sep 17 00:00:00 2001 From: kushwahramkumar2003 Date: Fri, 17 Nov 2023 12:27:36 +0530 Subject: [PATCH 2/2] Update file permissions and configurations --- .gitignore | 0 LICENSE | 0 README.md | 0 Server/.gitignore | 0 Server/index.js | 0 Server/package-lock.json | 0 Server/package.json | 0 Server/src/.env.example | 0 Server/src/App.js | 0 Server/src/config/index.js | 4 +- Server/src/controllers/auth.controllers.js | 0 Server/src/controllers/profile.controllers.js | 0 Server/src/controllers/quiz.controllers.js | 400 ++++++++++++++---- Server/src/controllers/result.controllers.js | 0 Server/src/middlewares/auth.middlewares.js | 0 .../src/middlewares/rateLimit.middlewares.js | 0 Server/src/models/Feedback.model.js | 0 Server/src/models/Profile.model.js | 0 Server/src/models/Question.model.js | 0 Server/src/models/Quiz.model.js | 0 Server/src/models/Result.model.js | 2 + Server/src/models/User.model.js | 6 +- Server/src/routes/auth.routes.js | 0 Server/src/routes/index.routes.js | 0 Server/src/routes/profile.routes.js | 0 Server/src/routes/quiz.routes.js | 45 +- Server/src/routes/result.routes.js | 0 Server/src/services/asyncHandler.js | 0 Server/src/services/connectDB.js | 6 +- package-lock.json | 0 package.json | 0 public/favicon.ico | Bin public/index.html | 7 +- public/logo192.png | Bin public/logo512.png | Bin public/manifest.json | 0 public/robots.txt | 0 src/App.css | 0 src/App.js | 0 src/App.test.js | 0 src/Components/Images/Right-img.svg | 0 src/Components/Images/graduate.svg | 0 src/Components/Images/logo.svg | 0 src/Components/Login.css | 186 ++++++++ src/Components/SignUp.jsx | 0 src/Components/login.jsx | 148 +++++-- src/index.css | 0 src/index.js | 0 src/logo.svg | 0 src/reportWebVitals.js | 0 src/setupTests.js | 0 51 files changed, 669 insertions(+), 135 deletions(-) mode change 100644 => 100755 .gitignore mode change 100644 => 100755 LICENSE mode change 100644 => 100755 README.md mode change 100644 => 100755 Server/.gitignore mode change 100644 => 100755 Server/index.js mode change 100644 => 100755 Server/package-lock.json mode change 100644 => 100755 Server/package.json mode change 100644 => 100755 Server/src/.env.example mode change 100644 => 100755 Server/src/App.js mode change 100644 => 100755 Server/src/config/index.js mode change 100644 => 100755 Server/src/controllers/auth.controllers.js mode change 100644 => 100755 Server/src/controllers/profile.controllers.js mode change 100644 => 100755 Server/src/controllers/quiz.controllers.js mode change 100644 => 100755 Server/src/controllers/result.controllers.js mode change 100644 => 100755 Server/src/middlewares/auth.middlewares.js mode change 100644 => 100755 Server/src/middlewares/rateLimit.middlewares.js mode change 100644 => 100755 Server/src/models/Feedback.model.js mode change 100644 => 100755 Server/src/models/Profile.model.js mode change 100644 => 100755 Server/src/models/Question.model.js mode change 100644 => 100755 Server/src/models/Quiz.model.js mode change 100644 => 100755 Server/src/models/Result.model.js mode change 100644 => 100755 Server/src/models/User.model.js mode change 100644 => 100755 Server/src/routes/auth.routes.js mode change 100644 => 100755 Server/src/routes/index.routes.js mode change 100644 => 100755 Server/src/routes/profile.routes.js mode change 100644 => 100755 Server/src/routes/quiz.routes.js mode change 100644 => 100755 Server/src/routes/result.routes.js mode change 100644 => 100755 Server/src/services/asyncHandler.js mode change 100644 => 100755 Server/src/services/connectDB.js mode change 100644 => 100755 package-lock.json mode change 100644 => 100755 package.json mode change 100644 => 100755 public/favicon.ico mode change 100644 => 100755 public/index.html mode change 100644 => 100755 public/logo192.png mode change 100644 => 100755 public/logo512.png mode change 100644 => 100755 public/manifest.json mode change 100644 => 100755 public/robots.txt mode change 100644 => 100755 src/App.css mode change 100644 => 100755 src/App.js mode change 100644 => 100755 src/App.test.js mode change 100644 => 100755 src/Components/Images/Right-img.svg mode change 100644 => 100755 src/Components/Images/graduate.svg mode change 100644 => 100755 src/Components/Images/logo.svg create mode 100755 src/Components/Login.css mode change 100644 => 100755 src/Components/SignUp.jsx mode change 100644 => 100755 src/Components/login.jsx mode change 100644 => 100755 src/index.css mode change 100644 => 100755 src/index.js mode change 100644 => 100755 src/logo.svg mode change 100644 => 100755 src/reportWebVitals.js mode change 100644 => 100755 src/setupTests.js diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/Server/.gitignore b/Server/.gitignore old mode 100644 new mode 100755 diff --git a/Server/index.js b/Server/index.js old mode 100644 new mode 100755 diff --git a/Server/package-lock.json b/Server/package-lock.json old mode 100644 new mode 100755 diff --git a/Server/package.json b/Server/package.json old mode 100644 new mode 100755 diff --git a/Server/src/.env.example b/Server/src/.env.example old mode 100644 new mode 100755 diff --git a/Server/src/App.js b/Server/src/App.js old mode 100644 new mode 100755 diff --git a/Server/src/config/index.js b/Server/src/config/index.js old mode 100644 new mode 100755 index 599d4a7..8dd9993 --- a/Server/src/config/index.js +++ b/Server/src/config/index.js @@ -5,10 +5,10 @@ const config = { MONGODB_URL: process.env.MONGODB_URL || "mongodb://localhost:27017/quiz-app", PORT: process.env.PORT || 3001, - JWT_SECRET: process.env.JWT_SECRET || "thisisasecret", + JWT_SECRET: process.env.JWT_SECRET, JWT_EXPIRE: process.env.JWT_EXPIRE || "1d", - SMTP_HOST: process.env.SMTP_HOST , + SMTP_HOST: process.env.SMTP_HOST, SMTP_PORT: process.env.SMTP_PORT, SMTP_USER: process.env.SMTP_USER, SMTP_PASSWORD: process.env.SMTP_PASSWORD, diff --git a/Server/src/controllers/auth.controllers.js b/Server/src/controllers/auth.controllers.js old mode 100644 new mode 100755 diff --git a/Server/src/controllers/profile.controllers.js b/Server/src/controllers/profile.controllers.js old mode 100644 new mode 100755 diff --git a/Server/src/controllers/quiz.controllers.js b/Server/src/controllers/quiz.controllers.js old mode 100644 new mode 100755 index 9a1223a..0ee25f0 --- a/Server/src/controllers/quiz.controllers.js +++ b/Server/src/controllers/quiz.controllers.js @@ -1,7 +1,6 @@ const Quiz = require("../models/Quiz.model.js"); const Question = require("../models/Question.model.js"); -const { ObjectId } = require("bson"); -const Result = require("../models/Result.model.js"); +const QuizResult = require("../models/Result.model.js"); const asyncHandler = require("./../services/asyncHandler.js"); /********************************************************************** @@ -206,6 +205,7 @@ exports.updateQuestionById = asyncHandler(async (req, res) => { * @desc Delete a quiz by ID * @route DELETE api/v1/quiz/:id/delete * @access Private/Admin + * @kushwahramkumar2003 *************************************************************************/ exports.deleteQuizById = asyncHandler(async (req, res) => { const quizId = req.params.id; @@ -223,106 +223,326 @@ exports.deleteQuizById = asyncHandler(async (req, res) => { res.status(200).json({ success: true, message: "Quiz deleted successfully" }); }); -// @desc Get quiz for attempt -// @route GET /api/quizzes/:id/attempt -// @access Public +/************************************************************************* + * @desc Delete a question by ID + * @route DELETE api/v1/quiz/:quizId/question/:questionId/delete + * @access Private/Admin + * @kushwahramkumar2003 + *************************************************************************/ +exports.deleteQuestionById = asyncHandler(async (req, res) => { + const { quizId, questionId } = req.params; + + const quiz = await Quiz.findById(quizId); + + if (!quiz) { + return res.status(404).json({ success: false, message: "Quiz not found" }); + } + + const question = await Question.findById(questionId); + + if (!question) { + return res + .status(404) + .json({ success: false, message: "Question not found" }); + } + + await question.remove(); + + res + .status(200) + .json({ success: true, message: "Question deleted successfully" }); +}); + +/************************************************************************* + * @desc Get quiz for attempt + * @route GET /api/quizzes/:id/attempt + * @access Public + *************************************************************************/ exports.getQuizForAttempt = asyncHandler(async (req, res) => { - const quiz = await Quiz.findById(req.params.id); + const quiz = await Quiz.findById(req.params.id).populate({ + path: "questions", + select: "-correctAnswer", + }); - if (quiz) { - const { name, questions } = quiz; - const quizForAttempt = { - name, - questions: questions.map((question) => { - const { _id, text, options } = question; - return { _id, text, options }; - }), - }; - res.json(quizForAttempt); - } else { - res.status(404); - throw new Error("Quiz not found"); + if (!quiz) { + return res.status(404).json({ message: "Quiz not found" }); } + + const { questions } = quiz; + + const quizForAttempt = questions.map((question) => { + const { _id, questionText, options } = question; + return { + _id, + questionText, + options, + }; + }); + + res.status(200).json({ quiz: quizForAttempt }); }); -// @desc Submit quiz answers -// @route POST /api/quizzes/:id/submit -// @access Public +/************************************************************************* + * @desc Submit quiz answers + * @route POST /api/quizzes/:id/submit + * @access Public + *************************************************************************/ exports.submitQuizAnswers = asyncHandler(async (req, res) => { - const quiz = await Quiz.findById(req.params.id); + const quizId = req.params.id; + const { answers } = req.body; - if (quiz) { - const { answers } = req.body; - const { questions } = quiz; - const score = questions.reduce((totalScore, question, index) => { - const correctAnswer = question.options.find((option) => option.isCorrect); - const selectedAnswer = answers[index]; - if (correctAnswer._id === selectedAnswer) { - return totalScore + 1; - } else { - return totalScore; - } - }, 0); - res.json({ score }); - } else { - res.status(404); - throw new Error("Quiz not found"); + const quiz = await Quiz.findById(quizId).populate("questions"); + + if (!quiz) { + return res.status(404).json({ message: "Quiz not found" }); } + + const { questions } = quiz; + + let score = 0; + + const results = questions.map((question) => { + const { _id, correctAnswer } = question; + const userAnswer = answers[_id]; + + const isCorrect = userAnswer === correctAnswer; + + if (isCorrect) { + score++; + } + + return { + questionId: _id, + userAnswer, + isCorrect, + }; + }); + + const totalQuestions = questions.length; + const percentageScore = (score / totalQuestions) * 100; + + const quizResult = new QuizResult({ + quiz: quizId, + user: req.user._id, + score, + totalQuestions, + percentageScore, + results, + }); + + await quizResult.save(); + + res.status(200).json({ quizResult }); }); -// @desc Get quiz results -// @route GET /api/quizzes/:id/results -// @access Private/Admin +/************************************************************************* + * @desc Get quiz results + * @route GET /api/quizzes/:id/results + * @access Private/Admin + *************************************************************************/ exports.getQuizResults = asyncHandler(async (req, res) => { - const quiz = await Quiz.findById(req.params.id); + try { + const quiz = await Quiz.findById(req.params.id).populate("questions"); + if (!quiz) { + return res.status(404).json({ message: "Quiz not found" }); + } + + const results = await Result.find({ quiz: quiz._id }).populate( + "user", + "name email" + ); + res.status(200).json({ results }); + } catch (error) { + console.error(error); + res.status(500).json({ message: "Server error" }); + } +}); - if (quiz) { - const { name, questions } = quiz; - const results = await Promise.all( - questions.map(async (question) => { - const { _id, text } = question; - const correctAnswer = question.options.find( - (option) => option.isCorrect - ); - const resultsForQuestion = await Quiz.aggregate([ - { $unwind: "$attempts" }, - { - $match: { - _id: quiz._id, - "attempts.answers.questionId": question._id, - }, - }, - { - $group: { - _id: "$attempts.answers.questionId", - totalAttempts: { $sum: 1 }, - totalCorrect: { - $sum: { - $cond: [ - { - $eq: [ - "$attempts.answers.selectedOptionId", - correctAnswer._id, - ], - }, - 1, - 0, - ], - }, - }, - }, - }, - ]); - return { - _id, - text, - results: resultsForQuestion[0], - }; - }) +/************************************************************************* + * @desc Update user's answer to a question + * @route PUT /api/quizzes/:quizId/questions/:questionId/answer + * @access Public + *************************************************************************/ +exports.updateUserAnswer = asyncHandler(async (req, res) => { + try { + const { quizId, questionId } = req.params; + const { answer } = req.body; + + const quiz = await Quiz.findById(quizId); + if (!quiz) { + return res.status(404).json({ message: "Quiz not found" }); + } + + const question = await Question.findById(questionId); + if (!question) { + return res.status(404).json({ message: "Question not found" }); + } + + const userAnswer = await UserAnswer.findOneAndUpdate( + { user: req.user._id, question: questionId }, + { answer }, + { new: true, upsert: true } ); - res.json({ name, results }); - } else { - res.status(404); - throw new Error("Quiz not found"); + + res.status(200).json({ userAnswer }); + } catch (error) { + console.error(error); + res.status(500).json({ message: "Server error" }); + } +}); + +/************************************************************************* + * @desc Get remaining time for quiz + * @route GET /api/quizzes/:id/time + * @access Public + *************************************************************************/ +exports.getRemainingTime = asyncHandler(async (req, res) => { + try { + const { id } = req.params; + const quiz = await Quiz.findById(id); + if (!quiz) { + return res.status(404).json({ message: "Quiz not found" }); + } + const remainingTime = quiz.duration - (Date.now() - quiz.startTime); + res.status(200).json({ remainingTime }); + } catch (error) { + console.error(error); + res.status(500).json({ message: "Server error" }); + } +}); + +/************************************************************************* + * @desc Automatically submit quiz when time is up + * @route POST /api/quizzes/:id/submit-on-time-up + * @access Public + *************************************************************************/ +exports.submitQuizOnTimeUp = asyncHandler(async (req, res) => { + try { + const { id } = req.params; + const quiz = await Quiz.findById(id); + if (!quiz) { + return res.status(404).json({ message: "Quiz not found" }); + } + const remainingTime = quiz.duration - (Date.now() - quiz.startTime); + if (remainingTime > 0) { + return res.status(400).json({ message: "Quiz time has not expired yet" }); + } + const { answers } = req.body; + if (!answers) { + return res.status(400).json({ message: "Answers not provided" }); + } + const quizResults = new QuizResult({ + quiz: id, + user: req.user._id, + answers, + }); + await quizResults.save(); + res.status(200).json({ message: "Quiz submitted successfully" }); + } catch (error) { + console.error(error); + res.status(500).json({ message: "Server error" }); + } +}); + +/************************************************************************* + * @desc Get quiz results for a user + * @route GET /api/quizzes/:id/results/:userId + * @access Private/Admin + *************************************************************************/ +exports.getQuizResultsForUser = asyncHandler(async (req, res) => { + try { + const { id, userId } = req.params; + const quizResults = await QuizResults.findOne({ + quiz: id, + user: userId, + }).populate("quiz", "title"); + if (!quizResults) { + return res.status(404).json({ message: "Quiz results not found" }); + } + res.status(200).json({ quizResults }); + } catch (error) { + console.error(error); + res.status(500).json({ message: "Server error" }); + } +}); + +/************************************************************************* + * @desc Get all results for a quiz + * @route GET /api/quizzes/:id/all-results + * @access Private/Admin + *************************************************************************/ +exports.getAllResultsForQuiz = asyncHandler(async (req, res) => { + try { + const { id } = req.params; + const quizResults = await QuizResults.find({ quiz: id }).populate( + "user", + "name email" + ); + if (!quizResults) { + return res.status(404).json({ message: "No quiz results found" }); + } + res.status(200).json({ quizResults }); + } catch (error) { + console.error(error); + res.status(500).json({ message: "Server error" }); + } +}); + +/************************************************************************* + * @desc Get all quizzes created by a user + * @route GET /api/quizzes/user/:userId + * @access Private/Admin + *************************************************************************/ +exports.getQuizzesByUser = asyncHandler(async (req, res) => { + try { + const { userId } = req.params; + const quizzes = await Quiz.find({ user: userId }); + if (!quizzes) { + return res.status(404).json({ message: "No quizzes found" }); + } + res.status(200).json({ quizzes }); + } catch (error) { + console.error(error); + res.status(500).json({ message: "Server error" }); + } +}); + +/************************************************************************* + * @desc Delete a quiz result by ID + * @route DELETE api/v1/quiz/results/:id/delete + * @access Private/Admin + *************************************************************************/ +exports.deleteQuizResultById = asyncHandler(async (req, res) => { + try { + const { id } = req.params; + const quizResult = await QuizResult.findById(id); + if (!quizResult) { + return res.status(404).json({ message: "Quiz result not found" }); + } + await quizResult.remove(); + res.status(200).json({ message: "Quiz result deleted successfully" }); + } catch (error) { + console.error(error); + res.status(500).json({ message: "Server error" }); + } +}); + +/************************************************************************* + * @desc Delete all quiz results for a quiz + * @route DELETE api/v1/quiz/:id/results/delete + * @access Private/Admin + *************************************************************************/ +exports.deleteAllQuizResults = asyncHandler(async (req, res) => { + try { + const { id } = req.params; + const quizResults = await QuizResult.find({ quiz: id }); + if (!quizResults) { + return res.status(404).json({ message: "No quiz results found" }); + } + await QuizResult.deleteMany({ quiz: id }); + res.status(200).json({ message: "All quiz results deleted successfully" }); + } catch (error) { + console.error(error); + res.status(500).json({ message: "Server error" }); } }); diff --git a/Server/src/controllers/result.controllers.js b/Server/src/controllers/result.controllers.js old mode 100644 new mode 100755 diff --git a/Server/src/middlewares/auth.middlewares.js b/Server/src/middlewares/auth.middlewares.js old mode 100644 new mode 100755 diff --git a/Server/src/middlewares/rateLimit.middlewares.js b/Server/src/middlewares/rateLimit.middlewares.js old mode 100644 new mode 100755 diff --git a/Server/src/models/Feedback.model.js b/Server/src/models/Feedback.model.js old mode 100644 new mode 100755 diff --git a/Server/src/models/Profile.model.js b/Server/src/models/Profile.model.js old mode 100644 new mode 100755 diff --git a/Server/src/models/Question.model.js b/Server/src/models/Question.model.js old mode 100644 new mode 100755 diff --git a/Server/src/models/Quiz.model.js b/Server/src/models/Quiz.model.js old mode 100644 new mode 100755 diff --git a/Server/src/models/Result.model.js b/Server/src/models/Result.model.js old mode 100644 new mode 100755 index 7bf9d15..9050d80 --- a/Server/src/models/Result.model.js +++ b/Server/src/models/Result.model.js @@ -68,3 +68,5 @@ resultSchema.statics.findByUserAndQuiz = async function (user, quiz) { }; const Result = mongoose.model("Result", resultSchema); + +module.exports = Result; diff --git a/Server/src/models/User.model.js b/Server/src/models/User.model.js old mode 100644 new mode 100755 index 7fe31de..4947681 --- a/Server/src/models/User.model.js +++ b/Server/src/models/User.model.js @@ -111,13 +111,15 @@ userSchema.statics.sendPasswordResetEmail = async function (email) { if (!user) { throw new Error("User not found"); } - const token = jwt.sign({ _id: user._id }, "secret", { expiresIn: "1h" }); + const token = jwt.sign({ _id: user._id }, config.JWT_SECRET, { + expiresIn: "1h", + }); // Send email with password reset link containing token }; // Reset the user's password userSchema.statics.resetPassword = async function (token, newPassword) { - const decoded = jwt.verify(token, "secret"); + const decoded = jwt.verify(token, config.JWT_SECRET); const user = await User.findById(decoded._id); if (!user) { throw new Error("User not found"); diff --git a/Server/src/routes/auth.routes.js b/Server/src/routes/auth.routes.js old mode 100644 new mode 100755 diff --git a/Server/src/routes/index.routes.js b/Server/src/routes/index.routes.js old mode 100644 new mode 100755 diff --git a/Server/src/routes/profile.routes.js b/Server/src/routes/profile.routes.js old mode 100644 new mode 100755 diff --git a/Server/src/routes/quiz.routes.js b/Server/src/routes/quiz.routes.js old mode 100644 new mode 100755 index 01f23a2..29561e1 --- a/Server/src/routes/quiz.routes.js +++ b/Server/src/routes/quiz.routes.js @@ -12,6 +12,9 @@ const { getQuizResults, addQuestionToQuiz, updateQuestionById, + getRemainingTime, + getQuizResultsForUser, + deleteQuestionById, } = require("../controllers/quiz.controllers.js"); const { isAdmin, @@ -27,6 +30,7 @@ router.get("/:id", getQuizById); // POST - Create a new quiz router.post("/create", isAuthenticated, isAdmin, createQuiz); +// POST - Add a question to a quiz router.post( "/create/addQuestion/:quizId", isAuthenticated, @@ -35,21 +39,48 @@ router.post( ); // PUT - Update an existing quiz by ID -router.put("/:id/update", isAdmin, updateQuizById); +router.put("/:id/update", isAuthenticated, isAdmin, updateQuizById); // DELETE - Delete a quiz by ID -router.delete("/:id/delete", isAdmin, deleteQuizById); +router.delete("/:id/delete", isAuthenticated, isAdmin, deleteQuizById); + +// DELETE - Delete a question from a quiz by quiz ID and question ID +router.delete( + "/:quizId/question/:questionId/delete", + isAuthenticated, + isAdmin, + deleteQuestionById +); // GET - Retrieve a specific quiz for users to attempt -router.get("/:id/take", getQuizForAttempt); +router.get("/:id/attempt", isAuthenticated, getQuizForAttempt); // POST - Submit answers for a specific quiz -router.post("/:id/submit", submitQuizAnswers); +router.post("/:id/submit", isAuthenticated, submitQuizAnswers); // GET - Fetch results of a specific quiz -router.get("/:id/results", getQuizResults); +router.get("/:id/results", isAuthenticated, getQuizResults); + +// PUT - Update a question in a quiz by quiz ID and question ID +router.put( + "/:quizId/question/:questionId/update", + isAuthenticated, + isAdmin, + updateQuestionById +); + +//GET - Fetch remaining time for quiz +router.get("/:id/time", isAuthenticated, getRemainingTime); -//PUT - Update question in quiz by Quiz id and Question id -router.put("/:quizId/question/:questionId/update", updateQuestionById); +//GET - Get quiz results for a user +router.get( + "/:id/results/:userId", + isAuthenticated, + isAdmin, + getQuizResultsForUser +); + +// Exporting the router +module.exports = router; module.exports = router; diff --git a/Server/src/routes/result.routes.js b/Server/src/routes/result.routes.js old mode 100644 new mode 100755 diff --git a/Server/src/services/asyncHandler.js b/Server/src/services/asyncHandler.js old mode 100644 new mode 100755 diff --git a/Server/src/services/connectDB.js b/Server/src/services/connectDB.js old mode 100644 new mode 100755 index b20606b..c11f3eb --- a/Server/src/services/connectDB.js +++ b/Server/src/services/connectDB.js @@ -3,10 +3,8 @@ const config = require("../config/index.js"); const connect = async () => { try { - await mongoose.connect(config.MONGODB_URL, { - useNewUrlParser: true, - useUnifiedTopology: true, - }); + console.log(config.MONGODB_URL); + await mongoose.connect(config.MONGODB_URL); console.log("DB Connected!!"); } catch (error) { console.log("Error in connecting to DB ", error); diff --git a/package-lock.json b/package-lock.json old mode 100644 new mode 100755 diff --git a/package.json b/package.json old mode 100644 new mode 100755 diff --git a/public/favicon.ico b/public/favicon.ico old mode 100644 new mode 100755 diff --git a/public/index.html b/public/index.html old mode 100644 new mode 100755 index aa069f2..dc603fb --- a/public/index.html +++ b/public/index.html @@ -9,6 +9,10 @@ name="description" content="Web site created using create-react-app" /> + - React App + Online-Quiz App -