From 01e9bf581572203dd52ef6c41b224225edeca127 Mon Sep 17 00:00:00 2001 From: maydayv7 Date: Sat, 28 Jun 2025 23:39:34 +0530 Subject: [PATCH 01/17] Add notification for new user registration NOTIFICATION_URL must be updated to "$URL/api/v1/notifications" --- server/src/users/controller.js | 44 +++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/server/src/users/controller.js b/server/src/users/controller.js index f7b7298..87fca48 100644 --- a/server/src/users/controller.js +++ b/server/src/users/controller.js @@ -15,6 +15,7 @@ export const createUser = async (data) => { console.log("data is: ", data); let user = {}; + let notification = null; if (typeOfUser === roles.STUDENT) { user = await Student.create({ @@ -23,6 +24,13 @@ export const createUser = async (data) => { createdAt: new Date(), updatedAt: new Date(), }); + + notification = { + title: "Welcome Student", + message: "Welcome to RIP", + link: "Test", + userIds: [user._id], + }; } else if (typeOfUser === roles.RECRUITER) { user = await Recruiter.create({ name, @@ -30,21 +38,27 @@ export const createUser = async (data) => { createdAt: new Date(), updatedAt: new Date(), }); + + notification = { + title: "Welcome Recruiter", + message: "Welcome to RIP", + link: "Test", + userIds: [user._id], + }; } else if (typeOfUser === roles.ADMIN) { // console.log(adminList.includes(email)); - if(!adminList.includes(email)){ + if (!adminList.includes(email)) { throw new Error("You are not authorized to create an admin account"); } // if(adminList.includes(email)){ - user = await Admin.create({ - name, - email, - createdAt: new Date(), - updatedAt: new Date(), - }); - + user = await Admin.create({ + name, + email, + createdAt: new Date(), + updatedAt: new Date(), + }); } const appUser = await User.create({ @@ -54,6 +68,20 @@ export const createUser = async (data) => { connection_id: user._id, }); logger.info(`an app user of type ${typeOfUser} is created succesfully`); + + if (notification) { + const notificationResponse = await axios.post( + `${process.env.NOTIFICATION_URL}/createOne`, + notification, + { + headers: { + "Content-Type": "application/json", + }, + } + ); + logger.info("Notification sent successfully:", notificationResponse.data); + } + return appUser; } catch (error) { throw error; From 780b5d3d073c4d3b89f0a10709bd5f11c867e920 Mon Sep 17 00:00:00 2001 From: maydayv7 Date: Sat, 28 Jun 2025 23:54:27 +0530 Subject: [PATCH 02/17] Send notification when new job is created Currently doesn't work because recruiter_data is null --- server/src/recruiter/controllers/jobs.js | 51 ++++++++++++++---------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/server/src/recruiter/controllers/jobs.js b/server/src/recruiter/controllers/jobs.js index a440758..1aef001 100644 --- a/server/src/recruiter/controllers/jobs.js +++ b/server/src/recruiter/controllers/jobs.js @@ -3,7 +3,7 @@ import Student from "../../students/models/student.js"; import logger from "../../utils/logger.js"; import recruiter from "../models/recruiter.js"; import axios from "axios"; -import mongoose from "mongoose"; +import mongoose from "mongoose"; const createJob = async (req, res) => { try { @@ -18,15 +18,22 @@ const createJob = async (req, res) => { // }); // } const job = await Jobs.create(data); - // const response = await axios.post( - // `${process.env.NOTIFICATION_URL}/create-students`, - // { - // title: "New Job", - // message: `A new internship opportunity has been posted by ${recruiter_data.name}.\nClick on "View More" to know more about the internship.`, - // link: `/internships/internship/${job._id}`, - // } - // ); - // console.log(response.data); + + const notificationResponse = await axios.post( + `${process.env.NOTIFICATION_URL}/create-students`, + { + title: "New Job", + message: `A new internship opportunity has been posted by ${recruiter_data.name}.\nClick on "View More" to know more about the internship.`, + link: `/internships/internship/${job._id}`, + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ); + logger.info("Notification sent successfully:", notificationResponse); + return res.status(201).json({ message: "Job created successfully", data: job, @@ -41,9 +48,6 @@ const createJob = async (req, res) => { } }; - - - const getAllJobsOfRecruiter = async (req, res) => { try { const { recruiter_id } = req.params; @@ -195,6 +199,7 @@ const updateJob = async (req, res) => { } const recruiter_data = await recruiter.findById(job.recruiter); console.log(recruiter_data); + // await axios.post(`${process.env.NOTIFICATION_URL}/create-students`, { // title: "Changes in Application Criteria", // message: `${recruiter_data.name} has changed the application criteria for the internship.\nClick on "View More" to know more about the internship.`, @@ -238,15 +243,14 @@ const updateJob = async (req, res) => { // ); // } // console.log('applicationsData=',applicantsData); - + // return res.status(200).json({ // message: "Job retrieved successfully", // data: applicantsData, // status: "success", // }); // } - - + // catch (error) { // logger.error(error); // console.log(error); @@ -284,10 +288,16 @@ const getAllStudentsOfJob = async (req, res) => { let applicantId; // 🧠 Decide format: Object or direct ID - if (typeof applicantEntry === 'string' || typeof applicantEntry === 'object' && applicantEntry.toString) { + if ( + typeof applicantEntry === "string" || + (typeof applicantEntry === "object" && applicantEntry.toString) + ) { // Case: applicantEntry is a direct ID applicantId = applicantEntry; - } else if (typeof applicantEntry === 'object' && applicantEntry.applicant) { + } else if ( + typeof applicantEntry === "object" && + applicantEntry.applicant + ) { // Case: applicantEntry is { applicant: 'studentId' } applicantId = applicantEntry.applicant; } else { @@ -313,10 +323,9 @@ const getAllStudentsOfJob = async (req, res) => { return res.status(200).json({ message: "Job retrieved successfully", - data: applicantsData.filter(Boolean), + data: applicantsData.filter(Boolean), status: "success", }); - } catch (error) { console.error("Server Error in getAllStudentsOfJob:", error); return res.status(500).json({ @@ -327,7 +336,6 @@ const getAllStudentsOfJob = async (req, res) => { } }; - const getJobByfilter = async (req, res) => { try { const data = req.body; @@ -377,7 +385,6 @@ const applyForJob = async (req, res) => { } }; - export { getJob, getJobById, From 536bfc8b8674f05a9a344ccdb6ffac1fc7de904a Mon Sep 17 00:00:00 2001 From: maydayv7 Date: Sun, 29 Jun 2025 00:01:30 +0530 Subject: [PATCH 03/17] Send notification when job is created, or when student is selected/rejected --- server/src/recruiter/controllers/jobs.js | 19 ++++++--- server/src/recruiter/controllers/recruiter.js | 40 ++++++++++++++----- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/server/src/recruiter/controllers/jobs.js b/server/src/recruiter/controllers/jobs.js index 1aef001..0154f99 100644 --- a/server/src/recruiter/controllers/jobs.js +++ b/server/src/recruiter/controllers/jobs.js @@ -200,11 +200,20 @@ const updateJob = async (req, res) => { const recruiter_data = await recruiter.findById(job.recruiter); console.log(recruiter_data); - // await axios.post(`${process.env.NOTIFICATION_URL}/create-students`, { - // title: "Changes in Application Criteria", - // message: `${recruiter_data.name} has changed the application criteria for the internship.\nClick on "View More" to know more about the internship.`, - // link: `/internships/internship/${job._id}`, - // }); + const notificationResponse = await axios.post( + `${process.env.NOTIFICATION_URL}/create-students`, + { + title: "Changes in Application Criteria", + message: `${recruiter_data.name} has changed the application criteria for the internship.\nClick on "View More" to know more about the internship.`, + link: `/internships/internship/${job._id}`, + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ); + logger.info("Notification sent successfully:", notificationResponse); return res.status(200).json({ message: "Job updated successfully", diff --git a/server/src/recruiter/controllers/recruiter.js b/server/src/recruiter/controllers/recruiter.js index affc6c4..85228c6 100644 --- a/server/src/recruiter/controllers/recruiter.js +++ b/server/src/recruiter/controllers/recruiter.js @@ -201,7 +201,7 @@ const deleteRecruiter = async (req, res) => { }; const acceptStudentForJob = async (req, res) => { - console.log('hi job is hit') + console.log("hi job is hit"); try { const { job_id, student_id } = req.body; const job = await jobs.findById(job_id); @@ -221,11 +221,20 @@ const acceptStudentForJob = async (req, res) => { job.selected_student.push(student_id); job.save(); - // await axios.post(`${process.env.NOTIFICATION_URL}/createOne`, { - // title: "Selected for Internship", - // message: `Congratulations! You have been selected for the internship posted by ${recruiter_data.name}. An email has been sent to your Outlook email with the details of the further procedure.`, - // userIds: [student_id], - // }); + const notificationResponse = await axios.post( + `${process.env.NOTIFICATION_URL}/createOne`, + { + title: "Selected for Internship", + message: `Congratulations! You have been selected for the internship posted by ${recruiter_data.name}. An email has been sent to your Outlook email with the details of the further procedure.`, + userIds: [student_id], + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ); + logger.info("Notification sent successfully:", notificationResponse); // await axios.post(`${process.env.EMAIL_URL}/send-email`, { // emails: [student_data.email], @@ -268,11 +277,20 @@ const rejectStudentForJob = async (req, res) => { job.rejected_student.push(student_id); job.save(); - // await axios.post(`${process.env.NOTIFICATION_URL}/createOne`, { - // title: "Application Rejected", - // message: `Your application for the internship created by ${recruiter_data.name} has been rejected.`, - // userIds: [student_id], - // }); + const notificationResponse = await axios.post( + `${process.env.NOTIFICATION_URL}/createOne`, + { + title: "Application Rejected", + message: `Your application for the internship created by ${recruiter_data.name} has been rejected.`, + userIds: [student_id], + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ); + logger.info("Notification sent successfully:", notificationResponse); // await axios.post(`${process.env.EMAIL_URL}/send-email`, { // emails: [student_data.email], From 288e92cc727d2d0479013e590244181f7cc23f09 Mon Sep 17 00:00:00 2001 From: maydayv7 Date: Sun, 29 Jun 2025 00:05:05 +0530 Subject: [PATCH 04/17] Change logic for notification sent when user is created Move to auth.js --- server/src/auth/controllers/auth.js | 48 +++++++++++++++++------------ server/src/users/controller.js | 29 ----------------- 2 files changed, 29 insertions(+), 48 deletions(-) diff --git a/server/src/auth/controllers/auth.js b/server/src/auth/controllers/auth.js index 9e33280..826643d 100644 --- a/server/src/auth/controllers/auth.js +++ b/server/src/auth/controllers/auth.js @@ -132,25 +132,35 @@ export const onedriveRedirect = async (req, res) => { typeOfUser: createdUser.typeOfUser, }; - // if (newUserCookie) { - // try { - // await axios.post(`${process.env.NOTIFICATION_URL}/createOne`, { - // title: "Welcome to Research Intern Portal, IIT Guwahati", - // message: - // "We are glad to have you on board! Feel free to explore the platform and reach out to us in case of any queries. We recommend you to complete your profile to get the best experience.", - // link: `/profile`, - // userIds: [createdUser.connection_id], - // }); - // await axios.post(`${process.env.EMAIL_URL}/send-email`, { - // emails: [createdUser.email], - // subject: "Welcome to Research Intern Portal, IIT Guwahati", - // message: `We are glad to have you on board! Feel free to explore the platform and reach out to us in case of any queries. We recommend you to complete your profile to get the best experience.`, - // }); - // } catch (err) { - // console.log(err); - // logger.error(err); - // } - // } + if (newUserCookie) { + try { + const notificationResponse = await axios.post( + `${process.env.NOTIFICATION_URL}/createOne`, + { + title: "Welcome to Research Intern Portal, IIT Guwahati", + message: + "We are glad to have you on board! Feel free to explore the platform and reach out to us in case of any queries. We recommend you to complete your profile to get the best experience.", + link: `/profile`, + userIds: [createdUser.connection_id], + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ); + logger.info("Notification sent successfully:", notificationResponse); + + // await axios.post(`${process.env.EMAIL_URL}/send-email`, { + // emails: [createdUser.email], + // subject: "Welcome to Research Intern Portal, IIT Guwahati", + // message: `We are glad to have you on board! Feel free to explore the platform and reach out to us in case of any queries. We recommend you to complete your profile to get the best experience.`, + // }); + } catch (err) { + console.log(err); + logger.error(err); + } + } const stringify = JSON.stringify(newUserCookie); diff --git a/server/src/users/controller.js b/server/src/users/controller.js index 87fca48..966e007 100644 --- a/server/src/users/controller.js +++ b/server/src/users/controller.js @@ -15,7 +15,6 @@ export const createUser = async (data) => { console.log("data is: ", data); let user = {}; - let notification = null; if (typeOfUser === roles.STUDENT) { user = await Student.create({ @@ -24,13 +23,6 @@ export const createUser = async (data) => { createdAt: new Date(), updatedAt: new Date(), }); - - notification = { - title: "Welcome Student", - message: "Welcome to RIP", - link: "Test", - userIds: [user._id], - }; } else if (typeOfUser === roles.RECRUITER) { user = await Recruiter.create({ name, @@ -38,13 +30,6 @@ export const createUser = async (data) => { createdAt: new Date(), updatedAt: new Date(), }); - - notification = { - title: "Welcome Recruiter", - message: "Welcome to RIP", - link: "Test", - userIds: [user._id], - }; } else if (typeOfUser === roles.ADMIN) { // console.log(adminList.includes(email)); @@ -68,20 +53,6 @@ export const createUser = async (data) => { connection_id: user._id, }); logger.info(`an app user of type ${typeOfUser} is created succesfully`); - - if (notification) { - const notificationResponse = await axios.post( - `${process.env.NOTIFICATION_URL}/createOne`, - notification, - { - headers: { - "Content-Type": "application/json", - }, - } - ); - logger.info("Notification sent successfully:", notificationResponse.data); - } - return appUser; } catch (error) { throw error; From 921e9af647c4e61dbf0b67b4b317e083c0a13bff Mon Sep 17 00:00:00 2001 From: maydayv7 Date: Sun, 29 Jun 2025 00:14:53 +0530 Subject: [PATCH 05/17] Send notification for successful job application --- server/src/recruiter/controllers/jobs.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/server/src/recruiter/controllers/jobs.js b/server/src/recruiter/controllers/jobs.js index 0154f99..c29de44 100644 --- a/server/src/recruiter/controllers/jobs.js +++ b/server/src/recruiter/controllers/jobs.js @@ -380,6 +380,23 @@ const applyForJob = async (req, res) => { if (!apply) { return res.status(404).json({ message: "Something went wrong" }); } + + const notificationResponse = await axios.post( + `${process.env.NOTIFICATION_URL}/createOne`, + { + title: "Job Application Success", + message: `Successfully applied for job: ${job.title}`, + link: `/internship/${job._id}`, + userIds: [user_id], + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ); + logger.info("Notification sent successfully:", notificationResponse); + return res .status(200) .json({ message: "Successfully applied for the job" }); From 6da03faebbe12420f646424a893aa97cc5bb9798 Mon Sep 17 00:00:00 2001 From: yashraghav25 Date: Sun, 29 Jun 2025 21:43:39 +0530 Subject: [PATCH 06/17] implemented notifications --- server/src/admin/controller/updates.js | 7 ++- server/src/auth/controllers/auth.js | 38 ++++++------- server/src/recruiter/controllers/jobs.js | 56 ++++++++++++++----- server/src/recruiter/controllers/recruiter.js | 22 ++++---- 4 files changed, 77 insertions(+), 46 deletions(-) diff --git a/server/src/admin/controller/updates.js b/server/src/admin/controller/updates.js index 88ef3c0..83e8e43 100644 --- a/server/src/admin/controller/updates.js +++ b/server/src/admin/controller/updates.js @@ -1,10 +1,15 @@ import logger from "../../utils/logger.js"; import Updates from "../models/updates.js"; - +import axios from "axios"; const createUpdate = async (req, res) => { try { const { title, description, link } = req.body; const update = await Updates.create({ title, description, link }); + await axios.post(`${process.env.NOTIFICATION_URL}/create`, + { + title: title, + message: `A new internship opportunity has been posted by ${recruiter_data.name}.\nClick on "View More" to know more about the internship.`, + link: link }); logger.info(`Update created with ID: ${update._id}, title: ${title}`); return res.status(201).json({ status: "success", diff --git a/server/src/auth/controllers/auth.js b/server/src/auth/controllers/auth.js index 9e33280..2f3ae70 100644 --- a/server/src/auth/controllers/auth.js +++ b/server/src/auth/controllers/auth.js @@ -132,25 +132,25 @@ export const onedriveRedirect = async (req, res) => { typeOfUser: createdUser.typeOfUser, }; - // if (newUserCookie) { - // try { - // await axios.post(`${process.env.NOTIFICATION_URL}/createOne`, { - // title: "Welcome to Research Intern Portal, IIT Guwahati", - // message: - // "We are glad to have you on board! Feel free to explore the platform and reach out to us in case of any queries. We recommend you to complete your profile to get the best experience.", - // link: `/profile`, - // userIds: [createdUser.connection_id], - // }); - // await axios.post(`${process.env.EMAIL_URL}/send-email`, { - // emails: [createdUser.email], - // subject: "Welcome to Research Intern Portal, IIT Guwahati", - // message: `We are glad to have you on board! Feel free to explore the platform and reach out to us in case of any queries. We recommend you to complete your profile to get the best experience.`, - // }); - // } catch (err) { - // console.log(err); - // logger.error(err); - // } - // } + if (newUserCookie) { + try { + await axios.post(`${process.env.NOTIFICATION_URL}/createOne`, { + title: "Welcome to Research Intern Portal, IIT Guwahati", + message: + "We are glad to have you on board! Feel free to explore the platform and reach out to us in case of any queries. We recommend you to complete your profile to get the best experience.", + link: `/profile`, + userIds: [createdUser.connection_id], + }); + // await axios.post(`${process.env.EMAIL_URL}/send-email`, { + // emails: [createdUser.email], + // subject: "Welcome to Research Intern Portal, IIT Guwahati", + // message: `We are glad to have you on board! Feel free to explore the platform and reach out to us in case of any queries. We recommend you to complete your profile to get the best experience.`, + // }); + } catch (err) { + console.log(err); + logger.error(err); + } + } const stringify = JSON.stringify(newUserCookie); diff --git a/server/src/recruiter/controllers/jobs.js b/server/src/recruiter/controllers/jobs.js index a440758..02d2f9a 100644 --- a/server/src/recruiter/controllers/jobs.js +++ b/server/src/recruiter/controllers/jobs.js @@ -8,7 +8,8 @@ import mongoose from "mongoose"; const createJob = async (req, res) => { try { const data = req.body; - const recruiter_data = await recruiter.findById(req?.user?.connection_id); + // console.log(data); + const recruiter_data = await recruiter.findById(data?.recruiter); // if (recruiter_data.isVerified === false) { // return res.status(400).json({ @@ -18,15 +19,14 @@ const createJob = async (req, res) => { // }); // } const job = await Jobs.create(data); - // const response = await axios.post( - // `${process.env.NOTIFICATION_URL}/create-students`, - // { - // title: "New Job", - // message: `A new internship opportunity has been posted by ${recruiter_data.name}.\nClick on "View More" to know more about the internship.`, - // link: `/internships/internship/${job._id}`, - // } - // ); - // console.log(response.data); + await axios.post( + `${process.env.NOTIFICATION_URL}/create-students`, + { + title: "New Job", + message: `A new internship opportunity has been posted by ${recruiter_data.name}.\nClick on "View More" to know more about the internship.`, + link: `/internships/internship/${job._id}`, + } + ); return res.status(201).json({ message: "Job created successfully", data: job, @@ -177,6 +177,7 @@ const reopenApplications = async (req, res) => { }); } catch (error) { logger.error(error); + return res .status(500) .json({ message: "Server Error", data: null, status: "error" }); @@ -195,11 +196,11 @@ const updateJob = async (req, res) => { } const recruiter_data = await recruiter.findById(job.recruiter); console.log(recruiter_data); - // await axios.post(`${process.env.NOTIFICATION_URL}/create-students`, { - // title: "Changes in Application Criteria", - // message: `${recruiter_data.name} has changed the application criteria for the internship.\nClick on "View More" to know more about the internship.`, - // link: `/internships/internship/${job._id}`, - // }); + await axios.post(`${process.env.NOTIFICATION_URL}/create-students`, { + title: "Changes in Application Criteria", + message: `${recruiter_data.name} has changed the application criteria for the internship.\nClick on "View More" to know more about the internship.`, + link: `/internships/internship/${job._id}`, + }); return res.status(200).json({ message: "Job updated successfully", @@ -363,6 +364,31 @@ const applyForJob = async (req, res) => { if (!apply) { return res.status(404).json({ message: "Something went wrong" }); } + const recruiter_data = await recruiter.findById(job.recruiter); + // Send notification to the student about successful application + try { + await axios.post(`${process.env.NOTIFICATION_URL}/createOne`, { + title: "✅ Application Submitted Successfully!", + message: `Your application for the internship "${job.title}" by ${ + recruiter_data?.name || "the recruiter" + } has been submitted successfully. You will be notified about the status of your application.`, + link: `/internships/internship/${job._id}`, + userIds: [user_id], + }); + + logger.info( + `Application success notification sent to student ${user_id} for job ${job_id}` + ); + } catch (notificationError) { + console.error( + "Failed to send application success notification:", + notificationError + ); + logger.error( + `Application notification failed for student ${user_id}: ${notificationError.message}` + ); + // Don't fail the application if notification fails + } return res .status(200) .json({ message: "Successfully applied for the job" }); diff --git a/server/src/recruiter/controllers/recruiter.js b/server/src/recruiter/controllers/recruiter.js index affc6c4..71bbd39 100644 --- a/server/src/recruiter/controllers/recruiter.js +++ b/server/src/recruiter/controllers/recruiter.js @@ -6,7 +6,7 @@ import logger from "../../utils/logger.js"; import { User } from "../../users/model.js"; import jobs from "../models/jobs.js"; import student from "../../students/models/student.js"; - +import axios from "axios"; const createRecuiter = async (req, res) => { try { const { name, email } = req.body; @@ -221,11 +221,11 @@ const acceptStudentForJob = async (req, res) => { job.selected_student.push(student_id); job.save(); - // await axios.post(`${process.env.NOTIFICATION_URL}/createOne`, { - // title: "Selected for Internship", - // message: `Congratulations! You have been selected for the internship posted by ${recruiter_data.name}. An email has been sent to your Outlook email with the details of the further procedure.`, - // userIds: [student_id], - // }); + await axios.post(`${process.env.NOTIFICATION_URL}/createOne`, { + title: "Selected for Internship", + message: `Congratulations! You have been selected for the internship posted by ${recruiter_data.name}. An email has been sent to your Outlook email with the details of the further procedure.`, + userIds: [student_id], + }); // await axios.post(`${process.env.EMAIL_URL}/send-email`, { // emails: [student_data.email], @@ -268,11 +268,11 @@ const rejectStudentForJob = async (req, res) => { job.rejected_student.push(student_id); job.save(); - // await axios.post(`${process.env.NOTIFICATION_URL}/createOne`, { - // title: "Application Rejected", - // message: `Your application for the internship created by ${recruiter_data.name} has been rejected.`, - // userIds: [student_id], - // }); + await axios.post(`${process.env.NOTIFICATION_URL}/createOne`, { + title: "Application Rejected", + message: `Your application for the internship created by ${recruiter_data.name} has been rejected.`, + userIds: [student_id], + }); // await axios.post(`${process.env.EMAIL_URL}/send-email`, { // emails: [student_data.email], From 13728a4625dad76529dc9a62b3b1d9fdf71a1ea7 Mon Sep 17 00:00:00 2001 From: maydayv7 Date: Sun, 29 Jun 2025 22:46:10 +0530 Subject: [PATCH 07/17] Update notification of job reopening --- server/src/recruiter/controllers/jobs.js | 27 +++++++++++++++++------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/server/src/recruiter/controllers/jobs.js b/server/src/recruiter/controllers/jobs.js index c29de44..019b9e0 100644 --- a/server/src/recruiter/controllers/jobs.js +++ b/server/src/recruiter/controllers/jobs.js @@ -169,11 +169,22 @@ const reopenApplications = async (req, res) => { .json({ message: "Job not found", data: null, status: "error" }); } const recruiter_data = await recruiter.findById(job.recruiter); - await axios.post(`${process.env.NOTIFICATION_URL}/create-students`, { - title: "Application Reopened", - message: `The application period of internship created by ${recruiter_data.name} has been reopened.\nClick on "View More" to know more about the internship.`, - link: `/internships/internship/${job._id}`, - }); + + const notificationResponse = await axios.post( + `${process.env.NOTIFICATION_URL}/createOne`, + { + title: "Application Reopened", + message: `The application period of internship created by ${recruiter_data.name} has been reopened.\nClick on "View More" to know more about the internship.`, + link: `/internships/internship/${job._id}`, + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ); + logger.info("Notification sent successfully:", notificationResponse); + return res.status(200).json({ message: "Job applications reopened", data: job, @@ -384,9 +395,9 @@ const applyForJob = async (req, res) => { const notificationResponse = await axios.post( `${process.env.NOTIFICATION_URL}/createOne`, { - title: "Job Application Success", - message: `Successfully applied for job: ${job.title}`, - link: `/internship/${job._id}`, + title: "Internship Application Success", + message: `Successfully applied for "${job.title}"`, + link: `/internships/internship/${job._id}`, userIds: [user_id], }, { From 9151a734b8cb0c75ec09d0c4c42ae2dce9db23bd Mon Sep 17 00:00:00 2001 From: maydayv7 Date: Sun, 29 Jun 2025 23:03:07 +0530 Subject: [PATCH 08/17] Update notification sent for admin announcements --- server/src/admin/controller/updates.js | 39 +++++++++++++++--------- server/src/recruiter/controllers/jobs.js | 7 +++-- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/server/src/admin/controller/updates.js b/server/src/admin/controller/updates.js index 83e8e43..ce008ec 100644 --- a/server/src/admin/controller/updates.js +++ b/server/src/admin/controller/updates.js @@ -5,12 +5,23 @@ const createUpdate = async (req, res) => { try { const { title, description, link } = req.body; const update = await Updates.create({ title, description, link }); - await axios.post(`${process.env.NOTIFICATION_URL}/create`, - { - title: title, - message: `A new internship opportunity has been posted by ${recruiter_data.name}.\nClick on "View More" to know more about the internship.`, - link: link }); logger.info(`Update created with ID: ${update._id}, title: ${title}`); + + const notificationResponse = await axios.post( + `${process.env.NOTIFICATION_URL}/create`, + { + title, + message: description, + link, + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ); + logger.info("Notification sent successfully:", notificationResponse); + return res.status(201).json({ status: "success", message: "Update created successfully", @@ -50,7 +61,7 @@ const getUpdateById = async (req, res) => { try { const update = await Updates.findById(id); if (!update) { - logger.warn(`Update with ID ${id} not found`); + logger.warn(`Update with ID ${id} not found`); return res.status(404).json({ status: "error", message: "Update not found", @@ -86,14 +97,14 @@ const editUpdate = async (req, res) => { data: null, }); } - + return res.status(200).json({ status: "success", message: "Update edited successfully", data: update, }); } catch (err) { -logger.error(`Error editing update with ID ${id}: ${err.message}`); + logger.error(`Error editing update with ID ${id}: ${err.message}`); res.status(500).json({ status: "error", message: "Internal server error", @@ -108,12 +119,12 @@ const deleteUpdate = async (req, res) => { const { id } = req.params; const update = await Updates.findByIdAndDelete(id); if (!update) { - logger.warn(`update not found!`); - return res.status(404).json({ - status: "error", - message: "Update not found", - data: null, - }); + logger.warn(`update not found!`); + return res.status(404).json({ + status: "error", + message: "Update not found", + data: null, + }); } logger.info(`Update with ID ${id} was deleted`); return res.status(200).json({ diff --git a/server/src/recruiter/controllers/jobs.js b/server/src/recruiter/controllers/jobs.js index 21b9939..3c3c98d 100644 --- a/server/src/recruiter/controllers/jobs.js +++ b/server/src/recruiter/controllers/jobs.js @@ -394,11 +394,12 @@ const applyForJob = async (req, res) => { return res.status(404).json({ message: "Something went wrong" }); } + const recruiter_data = await recruiter.findById(job.recruiter); const notificationResponse = await axios.post( `${process.env.NOTIFICATION_URL}/createOne`, { - title: "Internship Application Success", - message: `Successfully applied for "${job.title}"`, + title: "Application Submitted Successfully!", + message: `Your application for the internship "${job.title}" by ${recruiter_data.name} has been submitted successfully.`, link: `/internships/internship/${job._id}`, userIds: [user_id], }, @@ -414,7 +415,7 @@ const applyForJob = async (req, res) => { .status(200) .json({ message: "Successfully applied for the job" }); } else { - return res.status(404).json({ message: "Requirements did't match" }); + return res.status(404).json({ message: "Requirements didn't match" }); } } catch (error) { logger.error(error); From 5eb5b96eafd508495601138e8715281c61b46db7 Mon Sep 17 00:00:00 2001 From: maydayv7 Date: Sun, 29 Jun 2025 23:09:34 +0530 Subject: [PATCH 09/17] Fix logging --- server/src/admin/controller/updates.js | 2 +- server/src/auth/controllers/auth.js | 5 ++++- server/src/recruiter/controllers/jobs.js | 8 ++++---- server/src/recruiter/controllers/recruiter.js | 4 ++-- server/src/recruiter/models/jobs.js | 6 +++--- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/server/src/admin/controller/updates.js b/server/src/admin/controller/updates.js index ce008ec..28f96e2 100644 --- a/server/src/admin/controller/updates.js +++ b/server/src/admin/controller/updates.js @@ -20,7 +20,7 @@ const createUpdate = async (req, res) => { }, } ); - logger.info("Notification sent successfully:", notificationResponse); + logger.info("Notification sent successfully:", notificationResponse.data); return res.status(201).json({ status: "success", diff --git a/server/src/auth/controllers/auth.js b/server/src/auth/controllers/auth.js index 826643d..977fa78 100644 --- a/server/src/auth/controllers/auth.js +++ b/server/src/auth/controllers/auth.js @@ -149,7 +149,10 @@ export const onedriveRedirect = async (req, res) => { }, } ); - logger.info("Notification sent successfully:", notificationResponse); + logger.info( + "Notification sent successfully:", + notificationResponse.data + ); // await axios.post(`${process.env.EMAIL_URL}/send-email`, { // emails: [createdUser.email], diff --git a/server/src/recruiter/controllers/jobs.js b/server/src/recruiter/controllers/jobs.js index 3c3c98d..813e1dd 100644 --- a/server/src/recruiter/controllers/jobs.js +++ b/server/src/recruiter/controllers/jobs.js @@ -33,7 +33,7 @@ const createJob = async (req, res) => { }, } ); - logger.info("Notification sent successfully:", notificationResponse); + logger.info("Notification sent successfully:", notificationResponse.data); return res.status(201).json({ message: "Job created successfully", @@ -184,7 +184,7 @@ const reopenApplications = async (req, res) => { }, } ); - logger.info("Notification sent successfully:", notificationResponse); + logger.info("Notification sent successfully:", notificationResponse.data); return res.status(200).json({ message: "Job applications reopened", @@ -226,7 +226,7 @@ const updateJob = async (req, res) => { }, } ); - logger.info("Notification sent successfully:", notificationResponse); + logger.info("Notification sent successfully:", notificationResponse.data); return res.status(200).json({ message: "Job updated successfully", @@ -409,7 +409,7 @@ const applyForJob = async (req, res) => { }, } ); - logger.info("Notification sent successfully:", notificationResponse); + logger.info("Notification sent successfully:", notificationResponse.data); return res .status(200) diff --git a/server/src/recruiter/controllers/recruiter.js b/server/src/recruiter/controllers/recruiter.js index a9a92b4..d9bbcd2 100644 --- a/server/src/recruiter/controllers/recruiter.js +++ b/server/src/recruiter/controllers/recruiter.js @@ -234,7 +234,7 @@ const acceptStudentForJob = async (req, res) => { }, } ); - logger.info("Notification sent successfully:", notificationResponse); + logger.info("Notification sent successfully:", notificationResponse.data); // await axios.post(`${process.env.EMAIL_URL}/send-email`, { // emails: [student_data.email], @@ -290,7 +290,7 @@ const rejectStudentForJob = async (req, res) => { }, } ); - logger.info("Notification sent successfully:", notificationResponse); + logger.info("Notification sent successfully:", notificationResponse.data); // await axios.post(`${process.env.EMAIL_URL}/send-email`, { // emails: [student_data.email], diff --git a/server/src/recruiter/models/jobs.js b/server/src/recruiter/models/jobs.js index 2e5627f..28954e6 100644 --- a/server/src/recruiter/models/jobs.js +++ b/server/src/recruiter/models/jobs.js @@ -7,7 +7,7 @@ const requirementSchema = new mongoose.Schema({ }, department: { type: [String], - default:"Computer Science", + default: "Computer Science", // required: true, }, study_year: { @@ -60,8 +60,8 @@ const JobSchema = new mongoose.Schema({ type: Number, required: true, }, - applicants:{ - type: [String] + applicants: { + type: [String], }, selected_student: { From 831e03da8acf935976d1a44d46b233299a8b0763 Mon Sep 17 00:00:00 2001 From: maydayv7 Date: Sun, 29 Jun 2025 23:14:53 +0530 Subject: [PATCH 10/17] Send notification 24hrs before job expiry --- server/package.json | 1 + server/src/index.js | 2 ++ server/src/recruiter/notifier.js | 51 ++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 server/src/recruiter/notifier.js diff --git a/server/package.json b/server/package.json index adf023a..a095dad 100644 --- a/server/package.json +++ b/server/package.json @@ -26,6 +26,7 @@ "marked": "^12.0.2", "mongoose": "^8.12.1", "multer": "^1.4.5-lts.1", + "node-cron": "^4.1.1", "node-fetch": "^3.3.2", "querystring": "^0.2.1", "sanitize-html": "^2.13.0", diff --git a/server/src/index.js b/server/src/index.js index 01fe2ee..583e100 100644 --- a/server/src/index.js +++ b/server/src/index.js @@ -19,6 +19,7 @@ import adminUpdateRoutes from "./admin/routes/updates.js"; import studentRoutes from "./students/routes/student.js"; import jobRoutes from "./recruiter/routes/jobs.js"; import recruiterRoutes from "./recruiter/routes/recruiter.js"; +import jobExpiryNotifier from "./recruiter/notifier.js"; const app = express(); @@ -65,5 +66,6 @@ app.get("/ping", (req, res) => { app.use(errorHandler); app.listen(data.PORT, async () => { await connectToDb(); + jobExpiryNotifier(); logger.info(`Server is running on ${data.PORT}`); }); diff --git a/server/src/recruiter/notifier.js b/server/src/recruiter/notifier.js new file mode 100644 index 0000000..209af00 --- /dev/null +++ b/server/src/recruiter/notifier.js @@ -0,0 +1,51 @@ +import cron from "node-cron"; +import axios from "axios"; +import Jobs from "./models/jobs.js"; +import recruiter from "./models/recruiter.js"; + +export default function jobExpiryNotifier() { + // Runs every 24 hours + cron.schedule("0 0 * * *", async () => { + logger.info("Checking for jobs expiring within 24 hours..."); + + const now = new Date(); + const after23hrs = new Date(now.getTime() + 23 * 60 * 60 * 1000); + const after24hrs = new Date(now.getTime() + 24 * 60 * 60 * 1000); + + try { + const expiringJobs = await Jobs.find({ + accepting: true, + last_date: { + $gte: after23hrs, + $lte: after24hrs, + }, + }); + + for (const job of expiringJobs) { + const recruiter_data = await recruiter.findById(job.recruiter); + const notificationResponse = await axios.post( + `${process.env.NOTIFICATION_URL}/create-students`, + { + title: `"${job.title}" is closing soon!`, + message: `Applications for "${job.title}" by ${recruiter_data.name} will close within 24 hours.\nClick on "View More" to know more about the internship.`, + link: `/internships/internship/${job._id}`, + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ); + logger.info( + "Notification sent successfully:", + notificationResponse.data + ); + } + } catch (err) { + logger.error( + "jobExpiryNotifier error:", + err?.response?.data || err.message + ); + } + }); +} From e84258a1404b1857e6e72afe4c46c176ed11f56f Mon Sep 17 00:00:00 2001 From: maydayv7 Date: Mon, 30 Jun 2025 20:32:52 +0530 Subject: [PATCH 11/17] Fix notification links --- server/src/auth/controllers/auth.js | 2 +- server/src/recruiter/controllers/jobs.js | 10 +++++----- server/src/recruiter/notifier.js | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/server/src/auth/controllers/auth.js b/server/src/auth/controllers/auth.js index 977fa78..b7220c3 100644 --- a/server/src/auth/controllers/auth.js +++ b/server/src/auth/controllers/auth.js @@ -140,7 +140,7 @@ export const onedriveRedirect = async (req, res) => { title: "Welcome to Research Intern Portal, IIT Guwahati", message: "We are glad to have you on board! Feel free to explore the platform and reach out to us in case of any queries. We recommend you to complete your profile to get the best experience.", - link: `/profile`, + link: `/profile/overview`, userIds: [createdUser.connection_id], }, { diff --git a/server/src/recruiter/controllers/jobs.js b/server/src/recruiter/controllers/jobs.js index 813e1dd..dc73184 100644 --- a/server/src/recruiter/controllers/jobs.js +++ b/server/src/recruiter/controllers/jobs.js @@ -25,7 +25,7 @@ const createJob = async (req, res) => { { title: "New Job", message: `A new internship opportunity has been posted by ${recruiter_data.name}.\nClick on "View More" to know more about the internship.`, - link: `/internships/internship/${job._id}`, + link: `/student/internships/internship/${job._id}`, }, { headers: { @@ -143,7 +143,7 @@ const stopAcceptingApplications = async (req, res) => { await axios.post(`${process.env.NOTIFICATION_URL}/create-students`, { title: "Changes in Application Criteria", message: `${recruiter_data.name} has stopped accepting applications for the internship.`, - link: `/internships/internship/${job._id}`, + link: `/student/internships/internship/${job._id}`, }); return res.status(200).json({ @@ -176,7 +176,7 @@ const reopenApplications = async (req, res) => { { title: "Application Reopened", message: `The application period of internship created by ${recruiter_data.name} has been reopened.\nClick on "View More" to know more about the internship.`, - link: `/internships/internship/${job._id}`, + link: `/student/internships/internship/${job._id}`, }, { headers: { @@ -218,7 +218,7 @@ const updateJob = async (req, res) => { { title: "Changes in Application Criteria", message: `${recruiter_data.name} has changed the application criteria for the internship.\nClick on "View More" to know more about the internship.`, - link: `/internships/internship/${job._id}`, + link: `/student/internships/internship/${job._id}`, }, { headers: { @@ -400,7 +400,7 @@ const applyForJob = async (req, res) => { { title: "Application Submitted Successfully!", message: `Your application for the internship "${job.title}" by ${recruiter_data.name} has been submitted successfully.`, - link: `/internships/internship/${job._id}`, + link: `/student/internships/internship/${job._id}`, userIds: [user_id], }, { diff --git a/server/src/recruiter/notifier.js b/server/src/recruiter/notifier.js index 209af00..e27074a 100644 --- a/server/src/recruiter/notifier.js +++ b/server/src/recruiter/notifier.js @@ -28,7 +28,7 @@ export default function jobExpiryNotifier() { { title: `"${job.title}" is closing soon!`, message: `Applications for "${job.title}" by ${recruiter_data.name} will close within 24 hours.\nClick on "View More" to know more about the internship.`, - link: `/internships/internship/${job._id}`, + link: `/student/internships/internship/${job._id}`, }, { headers: { From b86457b1851669deb4ded690c3d513f05f447e86 Mon Sep 17 00:00:00 2001 From: yashraghav25 Date: Mon, 30 Jun 2025 20:50:43 +0530 Subject: [PATCH 12/17] added redirection --- server/src/auth/controllers/auth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/auth/controllers/auth.js b/server/src/auth/controllers/auth.js index b7220c3..5084685 100644 --- a/server/src/auth/controllers/auth.js +++ b/server/src/auth/controllers/auth.js @@ -140,7 +140,7 @@ export const onedriveRedirect = async (req, res) => { title: "Welcome to Research Intern Portal, IIT Guwahati", message: "We are glad to have you on board! Feel free to explore the platform and reach out to us in case of any queries. We recommend you to complete your profile to get the best experience.", - link: `/profile/overview`, + link: `/${createdUser.typeOfUser}/profile/overview`, userIds: [createdUser.connection_id], }, { From 2fa487ae87d71e8c6153af0b9f3c5e4b37f847c9 Mon Sep 17 00:00:00 2001 From: maydayv7 Date: Mon, 30 Jun 2025 21:43:16 +0530 Subject: [PATCH 13/17] Fix jobExpiryNotifier --- server/src/recruiter/notifier.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/server/src/recruiter/notifier.js b/server/src/recruiter/notifier.js index e27074a..d0466e1 100644 --- a/server/src/recruiter/notifier.js +++ b/server/src/recruiter/notifier.js @@ -2,21 +2,23 @@ import cron from "node-cron"; import axios from "axios"; import Jobs from "./models/jobs.js"; import recruiter from "./models/recruiter.js"; +import logger from "../utils/logger.js"; export default function jobExpiryNotifier() { + logger.info("jobExpiryNotifier started"); + // Runs every 24 hours cron.schedule("0 0 * * *", async () => { logger.info("Checking for jobs expiring within 24 hours..."); const now = new Date(); - const after23hrs = new Date(now.getTime() + 23 * 60 * 60 * 1000); const after24hrs = new Date(now.getTime() + 24 * 60 * 60 * 1000); try { const expiringJobs = await Jobs.find({ accepting: true, last_date: { - $gte: after23hrs, + $gte: now, $lte: after24hrs, }, }); @@ -26,7 +28,7 @@ export default function jobExpiryNotifier() { const notificationResponse = await axios.post( `${process.env.NOTIFICATION_URL}/create-students`, { - title: `"${job.title}" is closing soon!`, + title: `Internship is closing soon!`, message: `Applications for "${job.title}" by ${recruiter_data.name} will close within 24 hours.\nClick on "View More" to know more about the internship.`, link: `/student/internships/internship/${job._id}`, }, From e8e51e1686948296a37ebfa447b914585be4f43b Mon Sep 17 00:00:00 2001 From: maydayv7 Date: Mon, 30 Jun 2025 22:04:58 +0530 Subject: [PATCH 14/17] Change jobExpiryNotifier schedule to prevent conflict --- server/src/recruiter/notifier.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/recruiter/notifier.js b/server/src/recruiter/notifier.js index d0466e1..e26932c 100644 --- a/server/src/recruiter/notifier.js +++ b/server/src/recruiter/notifier.js @@ -7,8 +7,8 @@ import logger from "../utils/logger.js"; export default function jobExpiryNotifier() { logger.info("jobExpiryNotifier started"); - // Runs every 24 hours - cron.schedule("0 0 * * *", async () => { + // Runs everyday at 12 noon + cron.schedule("0 12 * * *", async () => { logger.info("Checking for jobs expiring within 24 hours..."); const now = new Date(); From 28b697b65466e209e84560bbf05f6a31a4c0f058 Mon Sep 17 00:00:00 2001 From: yashraghav25 Date: Tue, 1 Jul 2025 18:56:22 +0530 Subject: [PATCH 15/17] email verification feature added --- server/src/auth/controllers/auth.js | 30 +++++++++++++++++------ server/src/index.js | 4 ++-- server/src/users/controller.js | 37 +++++++++++++++++++++++++++++ server/src/users/model.js | 4 ++++ server/src/users/routers.js | 20 ++++++++++++++-- 5 files changed, 84 insertions(+), 11 deletions(-) diff --git a/server/src/auth/controllers/auth.js b/server/src/auth/controllers/auth.js index 5084685..dbcfd5a 100644 --- a/server/src/auth/controllers/auth.js +++ b/server/src/auth/controllers/auth.js @@ -2,7 +2,11 @@ import dotenv from "dotenv"; import querystring from "querystring"; import jwt from "jsonwebtoken"; import { roles } from "../../utils/roles.js"; -import { createUser, getUserFromToken } from "../../users/controller.js"; +import { + createUser, + getUserFromToken, + generateEmailVerificationToken, +} from "../../users/controller.js"; import { User } from "../../users/model.js"; import logger from "../../utils/logger.js"; import { frontendUrl } from "../../../frontend-url.js"; @@ -97,6 +101,7 @@ export const onedriveRedirect = async (req, res) => { user_id: findUser._id, connection_id: findUser.connection_id, typeOfUser: findUser.typeOfUser, + isVerified: findUser.isVerified, }; const stringify = JSON.stringify(userCookie); @@ -123,13 +128,30 @@ export const onedriveRedirect = async (req, res) => { name: userDataFromGraphApi.name, email: userDataFromGraphApi.email, typeOfUser: state, + isVerified: false, }); + // Generate email verification token and send verification email + const verificationToken = generateEmailVerificationToken(createdUser); + const verificationLink = `${ + process.env.EMAIL_VERIFICATION_URL || frontendUrl + }/api/users/verify?token=${verificationToken}`; + try { + await axios.post(`${process.env.EMAIL_URL}/send-email`, { + emails: [createdUser.email], + subject: "Verify your email for Research Intern Portal, IIT Guwahati", + message: `Welcome! Please verify your email by clicking the following link: ${verificationLink}\n\nIf you did not request this, please ignore this email.`, + }); + } catch (err) { + logger.error("Failed to send verification email", err); + } + const newUserCookie = { name: createdUser.name, user_id: createdUser._id, connection_id: createdUser.connection_id, typeOfUser: createdUser.typeOfUser, + isVerified: createdUser.isVerified, }; if (newUserCookie) { @@ -153,12 +175,6 @@ export const onedriveRedirect = async (req, res) => { "Notification sent successfully:", notificationResponse.data ); - - // await axios.post(`${process.env.EMAIL_URL}/send-email`, { - // emails: [createdUser.email], - // subject: "Welcome to Research Intern Portal, IIT Guwahati", - // message: `We are glad to have you on board! Feel free to explore the platform and reach out to us in case of any queries. We recommend you to complete your profile to get the best experience.`, - // }); } catch (err) { console.log(err); logger.error(err); diff --git a/server/src/index.js b/server/src/index.js index 583e100..4b5e0e7 100644 --- a/server/src/index.js +++ b/server/src/index.js @@ -20,7 +20,7 @@ import studentRoutes from "./students/routes/student.js"; import jobRoutes from "./recruiter/routes/jobs.js"; import recruiterRoutes from "./recruiter/routes/recruiter.js"; import jobExpiryNotifier from "./recruiter/notifier.js"; - +import userRoutes from "./users/routers.js"; const app = express(); setupSwagger(app); @@ -54,7 +54,7 @@ app.use("/api/v1/students", studentRoutes); // app.use("/api/v1/recruiters", verifyJWT, recruiterGuard, recruiterRoutes); app.use("/api/v1/recruiters", recruiterRoutes); app.use("/api/v1/job", jobRoutes); - +app.use("/api/v1/users", userRoutes); app.use("/api/v1/admin/bugs", bugRoutes); // test route diff --git a/server/src/users/controller.js b/server/src/users/controller.js index 966e007..5fa3e1d 100644 --- a/server/src/users/controller.js +++ b/server/src/users/controller.js @@ -7,8 +7,13 @@ import logger from "../utils/logger.js"; import Admin from "../admin/models/admin.js"; import axios from "axios"; import { adminList } from "./admin-list.js"; +import jwt from "jsonwebtoken"; // import Admin from "../admin/models/updates.js" +const EMAIL_VERIFICATION_SECRET = + process.env.EMAIL_VERIFICATION_SECRET || "email_verification_secret"; +const EMAIL_VERIFICATION_EXPIRY = "1h"; // 1 hour + export const createUser = async (data) => { try { const { name, email, typeOfUser } = data; @@ -113,3 +118,35 @@ export const getSavedJobs = async (req, res) => { return res.status(500).json({ message: "Internal server error" }); } }; + +export const generateEmailVerificationToken = (user) => { + const token = jwt.sign( + { userId: user._id, email: user.email }, + EMAIL_VERIFICATION_SECRET, + { expiresIn: EMAIL_VERIFICATION_EXPIRY } + ); + // console.log(`the token is: ${token}`); + return token; +}; + +export const verifyEmail = async (req, res) => { + const { token } = req.query; + if (!token) { + return res.status(400).json({ message: "Verification token is required" }); + } + try { + const decoded = jwt.verify(token, EMAIL_VERIFICATION_SECRET); + const user = await User.findById(decoded.userId); + if (!user) { + return res.status(404).json({ message: "User not found" }); + } + if (user.isVerified) { + return res.status(200).json({ message: "Email already verified" }); + } + user.isVerified = true; + await user.save(); + return res.status(200).json({ message: "Email verified successfully" }); + } catch (err) { + return res.status(400).json({ message: "Invalid or expired token" }); + } +}; diff --git a/server/src/users/model.js b/server/src/users/model.js index 925a960..3fc389f 100644 --- a/server/src/users/model.js +++ b/server/src/users/model.js @@ -41,6 +41,10 @@ const UserSchema = new mongoose.Schema({ type: mongoose.Schema.Types.ObjectId, ref: "Jobs", // Assuming you have a 'Job' model }], + isVerified: { + type: Boolean, + default: false, + }, }); export const User = mongoose.model("User", UserSchema); diff --git a/server/src/users/routers.js b/server/src/users/routers.js index 8e893d1..b213019 100644 --- a/server/src/users/routers.js +++ b/server/src/users/routers.js @@ -1,5 +1,5 @@ import { Router } from "express"; -import { createUser, getSavedJobs } from "./controller"; +import { createUser, getSavedJobs, verifyEmail } from "./controller.js"; const router = Router(); /** @@ -37,7 +37,23 @@ const router = Router(); * description: Internal server error */ router.post("/", createUser); - +/** + * @swagger + * /users/verify: + * get: + * tags: + * - Users + * summary: Verify email + * description: Verify the email of a user + * responses: + * 200: + * description: Email verified successfully + * 400: + * description: Invalid request parameters + * 500: + * description: Internal server error + */ +router.get("/verify", verifyEmail); /** * @swagger * /users/{userId}/saved-jobs: From dae5291c6e2ee7f36f60c909db2b0bb8812f039e Mon Sep 17 00:00:00 2001 From: yashraghav25 Date: Tue, 1 Jul 2025 19:06:32 +0530 Subject: [PATCH 16/17] removed user routes used for testing --- server/src/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/src/index.js b/server/src/index.js index 4b5e0e7..e0b35b4 100644 --- a/server/src/index.js +++ b/server/src/index.js @@ -20,7 +20,6 @@ import studentRoutes from "./students/routes/student.js"; import jobRoutes from "./recruiter/routes/jobs.js"; import recruiterRoutes from "./recruiter/routes/recruiter.js"; import jobExpiryNotifier from "./recruiter/notifier.js"; -import userRoutes from "./users/routers.js"; const app = express(); setupSwagger(app); @@ -54,7 +53,6 @@ app.use("/api/v1/students", studentRoutes); // app.use("/api/v1/recruiters", verifyJWT, recruiterGuard, recruiterRoutes); app.use("/api/v1/recruiters", recruiterRoutes); app.use("/api/v1/job", jobRoutes); -app.use("/api/v1/users", userRoutes); app.use("/api/v1/admin/bugs", bugRoutes); // test route From d9c4c1919f865c4729a69e7079966f7297d6a6bf Mon Sep 17 00:00:00 2001 From: maydayv7 Date: Thu, 3 Jul 2025 14:26:22 +0530 Subject: [PATCH 17/17] Merge duplicate controllers and make job applications work --- client/src/apis/student.js | 31 ++--- .../pages/internships/InternshipCard.jsx | 16 +-- .../pages/internships/InternshipDetail.jsx | 6 +- server/src/recruiter/controllers/jobs.js | 109 ++++++++++++------ server/src/students/controllers/student.js | 59 ---------- server/src/students/routes/student.js | 48 +------- 6 files changed, 99 insertions(+), 170 deletions(-) diff --git a/client/src/apis/student.js b/client/src/apis/student.js index c948446..a09446d 100644 --- a/client/src/apis/student.js +++ b/client/src/apis/student.js @@ -4,9 +4,9 @@ import { message } from "antd"; export const getStudent = async (id, navigate) => { try { - console.log("get student", backendURL) + console.log("get student", backendURL); const response = await axios.get(`${backendURL}/api/v1/students/${id}`, { - withCredentials: true, + withCredentials: true }); console.log(response.data); return response.data; @@ -20,21 +20,20 @@ export const updateStudent = async (id, data) => { try { console.log(data); const response = await axios.put(`${backendURL}/api/v1/students/${id}`, data, { - withCredentials: true, + withCredentials: true }); return response.data; } catch (error) { console.log(error); return error?.response?.data || error; - } }; export const getAppliedJobsByStudents = async (id, navigate) => { try { const response = await axios.get(`${backendURL}/api/v1/students/${id}/intern-applied`, { - withCredentials: true, + withCredentials: true }); console.log(response); return response.data; @@ -46,17 +45,23 @@ export const getAppliedJobsByStudents = async (id, navigate) => { export const applyToJobs = async (id, internId, navigate) => { try { - const response = await axios.post(`${backendURL}/api/v1/students/${id}/intern-apply/${internId}`, { - withCredentials: true, - }); + const response = await axios.post( + `${backendURL}/api/v1/job/apply`, + { + job_id: internId, + user_id: id + }, + { + headers: { + "Content-Type": "application/json" + }, + withCredentials: true + } + ); // console.log(response); return response.data; } catch (error) { console.log(error); - if (error?.response?.status === 400) { - message.error("Already Applied"); - return error?.response?.data; - } // navigate("/500"); return error?.response?.data || error; } @@ -65,7 +70,7 @@ export const applyToJobs = async (id, internId, navigate) => { export const getAllStudents = async (navigate) => { try { const response = await axios.get(`${backendURL}/api/v1/students`, { - withCredentials: true, + withCredentials: true }); return response.data; } catch (error) { diff --git a/client/src/student/pages/internships/InternshipCard.jsx b/client/src/student/pages/internships/InternshipCard.jsx index 6e859af..a0951ee 100644 --- a/client/src/student/pages/internships/InternshipCard.jsx +++ b/client/src/student/pages/internships/InternshipCard.jsx @@ -21,25 +21,21 @@ function InternshipCard({ arr }) { message.success("Applied Successfully"); navigate(`/student/applied`); } else { - // console.log(res); - if (res.message === "Already applied") { - return; - } - // message.error("Failed to apply"); + message.error(res.message || "Failed to apply"); } }; useEffect(() => { if (arr.applicants) { console.log(`applied ${arr}`); - const appliedOrNot = arr.applicants.find( - (person) => {if(person === user.connection_id){ + const appliedOrNot = arr.applicants.find((person) => { + if (person === user.connection_id) { console.log(person); console.log(user.connection_id); return true; - }} - ); - console.log(appliedOrNot) + } + }); + console.log(appliedOrNot); if (appliedOrNot) { setApplied(true); } diff --git a/client/src/student/pages/internships/InternshipDetail.jsx b/client/src/student/pages/internships/InternshipDetail.jsx index fbf36c0..3ab8835 100644 --- a/client/src/student/pages/internships/InternshipDetail.jsx +++ b/client/src/student/pages/internships/InternshipDetail.jsx @@ -52,11 +52,7 @@ export default function DriveDetail() { message.success("Applied Successfully"); navigate(`/student/applied`); } else { - // console.log(res); - if (res.message === "Already applied") { - return; - } - // message.error("Failed to apply"); + message.error(res.message || "Failed to apply"); } }; diff --git a/server/src/recruiter/controllers/jobs.js b/server/src/recruiter/controllers/jobs.js index dc73184..9cbd206 100644 --- a/server/src/recruiter/controllers/jobs.js +++ b/server/src/recruiter/controllers/jobs.js @@ -378,50 +378,83 @@ const getJobByfilter = async (req, res) => { const applyForJob = async (req, res) => { try { const { job_id, user_id } = req.body; - let user = await Student.findById(user_id); - let job = await Jobs.findById(job_id); - let jobRequirement = job.requirements; - - if ( - jobRequirement.cpi <= user.cpi && - jobRequirement.department == user.department && - new Date(job.last_date) >= new Date() - ) { - const apply = await Jobs.findByIdAndUpdate(job_id, { - $push: { applicants: user_id }, + const [student, job] = await Promise.all([ + Student.findById(user_id), + Jobs.findById(job_id), + ]); + + if (!student) { + return res.status(404).json({ + status: "error", + message: "Invalid Student ID", + data: null, }); - if (!apply) { - return res.status(404).json({ message: "Something went wrong" }); - } + } + if (!job) { + return res.status(404).json({ + status: "error", + message: "Invalid Job ID", + data: null, + }); + } - const recruiter_data = await recruiter.findById(job.recruiter); - const notificationResponse = await axios.post( - `${process.env.NOTIFICATION_URL}/createOne`, - { - title: "Application Submitted Successfully!", - message: `Your application for the internship "${job.title}" by ${recruiter_data.name} has been submitted successfully.`, - link: `/student/internships/internship/${job._id}`, - userIds: [user_id], - }, - { - headers: { - "Content-Type": "application/json", - }, - } - ); - logger.info("Notification sent successfully:", notificationResponse.data); + if (student.applications.includes(job_id)) { + return res.status(400).json({ + status: "error", + message: "Already applied", + data: null, + }); + } - return res - .status(200) - .json({ message: "Successfully applied for the job" }); - } else { - return res.status(404).json({ message: "Requirements didn't match" }); + const jobReq = job.requirements; + const deadline = new Date(job.last_date) < new Date(); + const cpiReq = jobReq.cpi > student.cpi; + const deptReq = !jobReq.department.includes(student.department); + if (cpiReq || deptReq || deadline) { + console.log(deadline, cpiReq, deptReq); + return res.status(400).json({ + status: "error", + message: "Requirements didn't match or application deadline passed", + data: null, + }); } + + student.applications.push(job._id); + job.applicants.push(student._id); + await Promise.all([ + student.save({ validateBeforeSave: false }), + job.save({ validateBeforeSave: false }), + ]); + + const recruiterData = await recruiter.findById(job.recruiter); + const notificationResponse = await axios.post( + `${process.env.NOTIFICATION_URL}/createOne`, + { + title: "Application Submitted Successfully!", + message: `Your application for the internship "${job.title}" by ${recruiterData.name} has been submitted successfully.`, + link: `/student/internships/internship/${job._id}`, + userIds: [user_id], + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ); + logger.info("Notification sent successfully:", notificationResponse.data); + + return res.status(200).json({ + status: "success", + message: "Successfully applied for the internship", + data: null, + }); } catch (error) { logger.error(error); - return res - .status(500) - .json({ message: "Server Error", error: error.message }); + return res.status(500).json({ + status: "error", + message: "Internal Server Error", + data: error.message, + }); } }; diff --git a/server/src/students/controllers/student.js b/server/src/students/controllers/student.js index 683b572..0c42bd9 100644 --- a/server/src/students/controllers/student.js +++ b/server/src/students/controllers/student.js @@ -104,7 +104,6 @@ const updateStudent = async (req, res) => { } }; - // const deleteStudent = async (req, res) => { // try { // const id = req.params.id; @@ -295,62 +294,6 @@ const getStudentsApplicationById = async (req, res) => { } }; -const addStudentsApplications = async (req, res) => { - try { - console.log(req.params); - const { id, internId } = req.params; - //assumed for now that the id for the document of intern post is sent is params - - //checking if the id's sent are true or not - const [student, intern] = await Promise.all([ - Student.findById(id), - Jobs.findById(internId), - ]); - - if (!student) { - return res.status(404).json({ - status: "error", - message: "Invalid Student Id", - data: null, - }); - } - if (!intern) { - return res.status(404).json({ - status: "error", - message: "Invalid Intern Id", - data: null, - }); - } - - const applicationsList = student.applications; - if (applicationsList.includes(internId)) { - return res.status(400).json({ - status: "error", - message: "Already applied", - data: null, - }); - } - - student.applications.push(intern._id); - // intern.applicants.push({ applicant: id, enum: "pending" }); - intern.applicants.push(id); - await student.save({ validateBeforeSave: false }); - await intern.save({ validateBeforeSave: false }); - return res.status(200).json({ - status: "success", - message: "Intern Applied", - data: null, - }); - } catch (error) { - logger.error(error); - return res.status(500).json({ - status: "error", - message: "Internal Server Error Occurred", - data: null, - }); - } -}; - const logoutStudent = async (req, res) => { try { const options = { @@ -384,7 +327,5 @@ export { getStudentByInterests, getStudentsByFilter, getStudentsApplicationById, - addStudentsApplications, logoutStudent, }; - \ No newline at end of file diff --git a/server/src/students/routes/student.js b/server/src/students/routes/student.js index 289efba..631ca86 100644 --- a/server/src/students/routes/student.js +++ b/server/src/students/routes/student.js @@ -9,8 +9,7 @@ import { updateStudent, createStudent, getStudentsApplicationById, - addStudentsApplications, - logoutStudent + logoutStudent, } from "../controllers/student.js"; import { upload, uploadFile } from "../upload/onedrive.upload.js"; import verifyJWT from "../../middlewares/token-verify.js"; @@ -54,7 +53,7 @@ const router = express.Router(); * description: Internal server error */ router.get("/", getStudents); -router.post("/upload", upload.single("file"),verifyJWT, uploadFile); +router.post("/upload", upload.single("file"), verifyJWT, uploadFile); /** * @swagger @@ -219,47 +218,6 @@ router.get("/search-interest", getStudentByInterests); */ router.get("/:id/intern-applied", getStudentsApplicationById); -/** - * @swagger - * /api/v1/students/{id}/intern-apply/{internId}: - * post: - * summary: Apply for an internship - * tags: [Students] - * parameters: - * - in: path - * name: id - * required: true - * description: The ID of the student - * schema: - * type: string - * - in: path - * name: internId - * required: true - * description: The ID of the internship - * schema: - * type: string - * responses: - * 200: - * description: Successfully applied for the internship - * content: - * application/json: - * schema: - * type: object - * properties: - * status: - * type: string - * example: success - * message: - * type: string - * example: Intern Applied - * 400: - * description: Already applied or invalid IDs - * 500: - * description: Internal server error - */ - -router.post("/:id/intern-apply/:internId", addStudentsApplications); - /** * @swagger * /api/v1/students/{id}: @@ -338,7 +296,7 @@ router.post("/:id/intern-apply/:internId", addStudentsApplications); */ router.put("/:id", updateStudent); router.post("/create", createStudent); -router.get("/logout/:id" , logoutStudent) +router.get("/logout/:id", logoutStudent); //router.delete('/:id', deleteStudent); export default router;