${key} : ${data[key]}
`; 24 | })}` 25 | ); 26 | if (info) { 27 | return res.status(200).send({ 28 | success: true, 29 | message: "Your message has been sent successfully", 30 | }); 31 | } else { 32 | return res.status(403).send({ 33 | success: false, 34 | message: "Something went wrong", 35 | }); 36 | } 37 | } catch (error) { 38 | return res.status(403).send({ 39 | success: false, 40 | message: "Something went wrong", 41 | }); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /server/controllers/RatingAndReviews.js: -------------------------------------------------------------------------------- 1 | const RatingAndReview=require("../models/RatingAndReview") 2 | const Course = require("../models/Course"); 3 | const { default: mongoose } = require("mongoose"); 4 | 5 | 6 | exports.createRating = async (req,res)=>{ 7 | try { 8 | const userId=req.user.id; 9 | const {rating, review,courseId} = req.body; 10 | const courseDetails= await Course.find({_id: courseId, 11 | studentsEnrolled: {$elemMatch:{$eq:userId}}}); 12 | 13 | if(!courseDetails){ 14 | return res.status(404).json({success:false,emessage: "Student not enrolled in course"}); 15 | }; 16 | const alreadyReviewed =await RatingAndReview.findOne({user:userId, 17 | course:courseId}); 18 | 19 | if(alreadyReviewed){ 20 | return res.status(404).json({success: false,message: "Already reviewed"}); 21 | } 22 | const ratingReview= await RatingAndReview.create({rating, 23 | review, 24 | course:courseId, 25 | user:userId}); 26 | 27 | 28 | await Course.findByIdAndUpdate({_id:courseId}, 29 | { 30 | $push:{ 31 | ratingAndReviews: ratingReview._id 32 | }}); 33 | 34 | 35 | res.status(200).json({ 36 | success: true, 37 | message: "Rating added successfully", 38 | ratingReview}); 39 | 40 | } catch (error) { 41 | console.log(error); 42 | res.status(500).json({message: error.message}); 43 | } 44 | } 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | exports.getAverageRating = async (res,req)=>{ 54 | try { 55 | const courseId=req.body.courseId; 56 | const result= await RatingAndReview.aggregate([ 57 | { 58 | $match:{ 59 | course:new mongoose.Types.ObjectId(courseId), 60 | } 61 | }, 62 | { 63 | $group:{ 64 | _id:null, 65 | averageRating: {$avg:"$rating"} 66 | } 67 | } 68 | ]); 69 | 70 | if(result.length > 0) { 71 | return res.status(200).json({averageRating: result[0].averageRating}); 72 | } 73 | else{ 74 | return res.status(200).json({message: "Average rating is 0", 75 | averageRating:0}); 76 | } 77 | 78 | } catch (error) { 79 | console.log(error); 80 | res.status(500).json({message: error.message}); 81 | } 82 | }; 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | exports.getAllRating = async (req,res) => { 93 | //get sorted by rating 94 | try { 95 | const allReviews = await RatingAndReview.find( 96 | ).sort({rating: -1}) 97 | .populate({path: "user", 98 | select: "firstName lastName email image"}) 99 | .populate({path: "course", 100 | select: "courseName"}) 101 | .exec(); 102 | 103 | return res.status(200).json({ 104 | success: true, 105 | message:"all reviews fetched successfully", 106 | data:allReviews, 107 | }); 108 | } catch (error) { 109 | console.log(error); 110 | res.status(500).json({message: error.message}); 111 | } 112 | } -------------------------------------------------------------------------------- /server/controllers/ResetPassword.js: -------------------------------------------------------------------------------- 1 | const User = require("../models/User"); 2 | const mailSender = require("../utils/mailSender"); 3 | const bcrypt = require("bcrypt"); 4 | const crypto = require("crypto"); 5 | 6 | exports.resetPasswordToken = async (req, res) => { 7 | try { 8 | const email = req.body.email; 9 | const user = await User.findOne({ email: email }); 10 | if (!user) { 11 | return res.json({ 12 | success: false, 13 | message: `This Email: ${email} is not Registered With Us Enter a Valid Email `, 14 | }); 15 | } 16 | const token = crypto.randomBytes(20).toString("hex"); 17 | 18 | const updatedDetails = await User.findOneAndUpdate( 19 | { email: email }, 20 | { 21 | token: token, 22 | resetPasswordExpires: Date.now() + 3600000, 23 | }, 24 | { new: true } 25 | ); 26 | console.log("DETAILS", updatedDetails); 27 | 28 | const url = `https://studynotion.fun/update-password/${token}`; 29 | 30 | await mailSender( 31 | email, 32 | "Password Reset", 33 | `Your Link for email verification is ${url}. Please click this url to reset your password.` 34 | ); 35 | 36 | res.json({ 37 | success: true, 38 | message: 39 | "Email Sent Successfully, Please Check Your Email to Continue Further", 40 | }); 41 | } catch (error) { 42 | return res.json({ 43 | error: error.message, 44 | success: false, 45 | message: `Some Error in Sending the Reset Message`, 46 | }); 47 | } 48 | }; 49 | 50 | exports.resetPassword = async (req, res) => { 51 | try { 52 | const { password, confirmPassword, token } = req.body; 53 | 54 | if (confirmPassword !== password) { 55 | return res.json({ 56 | success: false, 57 | message: "Password and Confirm Password Does not Match", 58 | }); 59 | } 60 | const userDetails = await User.findOne({ token: token }); 61 | if (!userDetails) { 62 | return res.json({ 63 | success: false, 64 | message: "Token is Invalid", 65 | }); 66 | } 67 | if (!(userDetails.resetPasswordExpires > Date.now())) { 68 | return res.status(403).json({ 69 | success: false, 70 | message: `Token is Expired, Please Regenerate Your Token`, 71 | }); 72 | } 73 | const encryptedPassword = await bcrypt.hash(password, 10); 74 | await User.findOneAndUpdate( 75 | { token: token }, 76 | { password: encryptedPassword }, 77 | { new: true } 78 | ); 79 | res.json({ 80 | success: true, 81 | message: `Password Reset Successful`, 82 | }); 83 | } catch (error) { 84 | return res.json({ 85 | error: error.message, 86 | success: false, 87 | message: `Some Error in Updating the Password`, 88 | }); 89 | } 90 | }; -------------------------------------------------------------------------------- /server/controllers/Section.js: -------------------------------------------------------------------------------- 1 | const Section = require("../models/Section"); 2 | const Course = require("../models/Course"); 3 | // CREATE a new section 4 | exports.createSection = async (req, res) => { 5 | try { 6 | // Extract the required properties from the request body 7 | const { sectionName, courseId } = req.body; 8 | 9 | // Validate the input 10 | if (!sectionName || !courseId) { 11 | return res.status(400).json({ 12 | success: false, 13 | message: "Missing required properties", 14 | }); 15 | } 16 | 17 | const ifcourse= await Course.findById(courseId); 18 | if (!ifcourse) { 19 | return res.status(404).json({ 20 | success: false, 21 | message: "Course not found", 22 | }); 23 | } 24 | 25 | // Create a new section with the given name 26 | const newSection = await Section.create({ sectionName }); 27 | 28 | // Add the new section to the course's content array 29 | const updatedCourse = await Course.findByIdAndUpdate( 30 | courseId, 31 | { 32 | $push: { 33 | courseContent: newSection._id, 34 | }, 35 | }, 36 | { new: true } 37 | ) 38 | .populate({ 39 | path: "courseContent", 40 | populate: { 41 | path: "subSection", 42 | }, 43 | }) 44 | .exec(); 45 | 46 | // Return the updated course object in the response 47 | res.status(200).json({ 48 | success: true, 49 | message: "Section created successfully", 50 | updatedCourse, 51 | }); 52 | } catch (error) { 53 | // Handle errors 54 | res.status(500).json({ 55 | success: false, 56 | message: "Internal server error", 57 | error: error.message, 58 | }); 59 | } 60 | }; 61 | 62 | // UPDATE a section 63 | exports.updateSection = async (req, res) => { 64 | try { 65 | const { sectionName, sectionId,courseId } = req.body; 66 | console.log(sectionName, sectionId); 67 | const section = await Section.findByIdAndUpdate( 68 | sectionId, 69 | { sectionName }, 70 | { new: true } 71 | ); 72 | const updatedCourse = await Course.findById(courseId).populate({ path: "courseContent", populate: { path: "subSection" } }).exec(); 73 | res.status(200).json({ 74 | success: true, 75 | message: "Section updated successfully", 76 | updatedCourse, 77 | 78 | }); 79 | } catch (error) { 80 | console.error("Error updating section:", error); 81 | res.status(500).json({ 82 | success: false, 83 | message: "Internal server error", 84 | }); 85 | } 86 | }; 87 | 88 | // DELETE a section 89 | exports.deleteSection = async (req, res) => { 90 | try { 91 | const { sectionId,courseId } = req.body; 92 | await Section.findByIdAndDelete(sectionId); 93 | const updatedCourse = await Course.findById(courseId).populate({ path: "courseContent", populate: { path: "subSection" } }).exec(); 94 | res.status(200).json({ 95 | success: true, 96 | message: "Section deleted", 97 | updatedCourse, 98 | }); 99 | } catch (error) { 100 | console.error("Error deleting section:", error); 101 | res.status(500).json({ 102 | success: false, 103 | message: "Internal server error", 104 | }); 105 | } 106 | }; -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | 3 | const app = express(); 4 | 5 | const userRoutes = require("./routes/User"); 6 | const paymentRoutes = require("./routes/Payments"); 7 | const profileRoutes = require("./routes/Profile"); 8 | const CourseRoutes = require("./routes/Course"); 9 | 10 | const database = require("./config/database"); 11 | const cookieParser = require("cookie-parser"); 12 | 13 | const cors = require("cors"); 14 | const fileUpload = require("express-fileupload"); 15 | const { cloudnairyconnect } = require("./config/cloudinary"); 16 | 17 | const dotenv = require("dotenv"); 18 | dotenv.config(); 19 | 20 | const PORT = process.env.PORT || 5000; 21 | database.connect(); 22 | 23 | app.use(express.json()); 24 | app.use(cookieParser()); 25 | 26 | const whitelist = process.env.CORS_ORIGIN 27 | ? JSON.parse(process.env.CORS_ORIGIN) 28 | : ["*"]; 29 | 30 | app.use( 31 | cors({ 32 | origin: whitelist, 33 | credentials: true, 34 | maxAge: 14400, 35 | }) 36 | ); 37 | 38 | app.use( 39 | fileUpload({ 40 | useTempFiles: true, 41 | tempFileDir: "/tmp", 42 | }) 43 | ); 44 | 45 | cloudnairyconnect(); 46 | 47 | app.use("/api/v1/auth", userRoutes); 48 | 49 | app.use("/api/v1/payment", paymentRoutes); 50 | 51 | app.use("/api/v1/profile", profileRoutes); 52 | 53 | app.use("/api/v1/course", CourseRoutes); 54 | 55 | app.use("/api/v1/contact", require("./routes/ContactUs")); 56 | 57 | app.get("/", (req, res) => { 58 | res.status(200).json({ 59 | message: "Welcome to the API", 60 | }); 61 | }); 62 | 63 | app.listen(PORT, () => { 64 | console.log(`Server is running on port ${PORT}`); 65 | }); 66 | -------------------------------------------------------------------------------- /server/mail/templates/courseEnrollmentEmail.js: -------------------------------------------------------------------------------- 1 | exports.courseEnrollmentEmail = (courseName, name) => { 2 | return ` 3 | 4 | 5 | 6 | 7 |Dear ${name},
75 |You have successfully registered for the course "${courseName}". We 76 | are excited to have you as a participant!
77 |Please log in to your learning dashboard to access the course materials and start your learning journey. 78 |
79 | Go to Dashboard 80 |Dear User,
74 |Thank you for registering with StudyNotion. To complete your registration, please use the following OTP 75 | (One-Time Password) to verify your account:
76 |This OTP is valid for 5 minutes. If you did not request this verification, please disregard this email. 78 | Once your account is verified, you will have access to our platform and its features.
79 |Hey ${name},
63 |Your password has been successfully updated for the email ${email}. 64 |
65 |If you did not request this password change, please contact us immediately to secure your account.
66 |Dear ${name} ${lastname},
77 |78 | Thank you for purchasing the course. Your payment of ₹${amount} has been successfully received. 79 |
80 |81 | Your payment ID is ${paymentId} and your order ID is ${orderId}. 83 |
84 |9 | {modalData.text1} 10 |
11 |12 | {modalData.text2} 13 |
14 |11 | We'd love to here for you, Please fill out this form. 12 |
13 |73 | {card.description} 74 |
75 |87 | {card.description} 88 |
89 |22 | {description1}{" "} 23 | 24 | {description2} 25 | 26 |
27 | {formType === "signup" ?{course?.courseName}
30 |By {course?.instructor?.firstName} {course?.instructor?.lastName}
31 |Rs. {course?.price}
37 |{item.title}
54 |⚡ Course Upload Tips
14 |{course?.courseName}
26 | 27 | 28 |{course?.category?.name}
29 | 30 | 31 |₹ {course?.price}
50 | 56 |Total:
32 |₹ {total}
33 | 34 |{totalItems} Courses in Cart
16 | 17 | {total > 0 18 | ? (Your Cart is Empty
)} 23 |Loading...
:( 39 |Publish Settings
72 | 84 |1
41 |2
42 |3
43 |4
44 |5
45 |6
46 |7
47 |8
48 |9
49 |10
50 |11
51 |40 | Learn to build anything you can imagine 41 |
42 | 43 |27 | Instructors from around the world teach millions of students on StudyNotion. We provide the tools and skills to teach what you love. 28 |
29 | 30 |{element.Description}
50 |10
68 |Years of Experience
69 |250
73 |TYpe of Courses
74 |92 | {review?.course?.courseName} 93 |
94 |Add Review
52 | 55 |{user?.firstName} {user?.lastName}
61 |Posting Publicly
62 |Error-404
7 |29 | { 30 | !emailSent?("Have no fear. We'll email you instructions to reset your password. If you dont have access to your email we can try account recovery"):(`We have sent the reset email to ${email}`) 31 | } 32 |
33 | 45 |Back To Login
48 | 49 |Take a Demo
A verification code has been sent to you. Enter the code below
45 | 61 | 62 | 63 |