diff --git a/backend/Input_validators/ValidateAchievement.js b/backend/Input_validators/ValidateAchievement.js new file mode 100644 index 0000000..062217b --- /dev/null +++ b/backend/Input_validators/ValidateAchievement.js @@ -0,0 +1,22 @@ +const { z } = require('zod'); +const { handleValidationError } = require('./ValidateQuestions'); + +const savedAchievementsSchema = z.object({ + unlockedAchievements: z.array(z.string(), { + required_error: "unlockedAchievements must be an array", + invalid_type_error: "unlockedAchievements must be an array of strings", + }), +}); + +const validateSavedAchievements = (req, res, next) => { + try { + savedAchievementsSchema.parse(req.body); + next(); + } catch (e) { + return handleValidationError(res, e); + } +}; + +module.exports = { + validateSavedAchievements, +}; diff --git a/backend/Input_validators/ValidateAi.js b/backend/Input_validators/ValidateAi.js new file mode 100644 index 0000000..6b36d79 --- /dev/null +++ b/backend/Input_validators/ValidateAi.js @@ -0,0 +1,56 @@ +const { z } = require("zod"); +const { handleValidationError } = require("./ValidateQuestions"); + +// Schema for interview questions request +const generateInterviewQuestionsSchema = z.object({ + role: z.string().min(1, "Role is required"), + experience: z.string().min(1, "Experience is required"), + topicsToFocus: z.array(z.string()).min(1, "At least one topic is required"), + numberOfQuestions: z.number().int().positive("Number of questions must be positive"), +}); + +// Schema for concept explanation request +const generateConceptExplanationSchema = z.object({ + question: z.string().min(1, "Question is required"), +}); + +// Schema for interview tips request +const generateInterviewTipsSchema = z.object({ + role: z.string().min(1, "Role is required"), + experience: z.string().min(1, "Experience is required"), +}); + + +const validateGenerateInterviewQuestions = (req,res,next)=>{ + + try { + generateInterviewQuestionsSchema.parse(req.body); + next(); + } catch (error) { + return handleValidationError(res, error); + } +} + +const validateGenerateConceptExplanation = (req,res,next)=>{ + + try { + generateConceptExplanationSchema.parse(req.body); + next(); + } catch (error) { + return handleValidationError(res, error); +}} + +const validateGenerateInterviewTips = (req, res, next) => { + try { + generateInterviewTipsSchema.parse(req.body); + next(); + } catch (error) { + return handleValidationError(res, error); + } +}; + +module.exports = { + validateGenerateInterviewQuestions, + validateGenerateConceptExplanation, + validateGenerateInterviewTips, +}; \ No newline at end of file diff --git a/backend/Input_validators/ValidateAuth.js b/backend/Input_validators/ValidateAuth.js new file mode 100644 index 0000000..659b5af --- /dev/null +++ b/backend/Input_validators/ValidateAuth.js @@ -0,0 +1,97 @@ +const { z } = require("zod"); +const { handleValidationError } = require("./ValidateQuestions"); + +const registerUserZod = z.object({ + name: z.string().min(4, "Length of the name should be minimum 4").trim(), + email: z.string().email("Enter a valid email").trim(), + password: z.string().trim(), + profileImageUrl: z.string().url("Enter a valid URL").trim().optional() +}); + + +const validateUserSignup = (req, res, next) => { + try { + registerUserZod.parse(req.body); + next(); + } catch (err) { + if (err instanceof z.ZodError) { + return res.status(400).json({ + success: false, + message: "Validation failed", + errors: err.issues.map(issue => ({ + field: issue.path.join("."), + message: issue.message, + })), + }); + } + // fallback for other errors + return res.status(500).json({ success: false, message: "Internal server error" }); +} +}; + +const loginUserZod = z.object({ + email : z.string().email("Enter a valid email"), + password : z.string().min(8) +}) + +const validateUserLogin = (req, res, next) => { + try { + loginUserZod.parse(req.body); + next(); + } catch (err) { + return handleValidationError(res, error); + } +}; + +const refreshTokenZod = z.object({ + refreshToken : z.string("Must be an string") +}) + +const validateRefreshToken = (req, res, next) => { + try { + refreshTokenZod.parse(req.body); + next(); + } catch (err) { + return res.status(400).json({ + success: false, + message: "Validation failed", + errors: err.errors.map(e => ({ + field: e.path.join("."), + message: e.message, + })), + }); + } +}; + +const resendVerificationZod = z.object({ + email : z.string().email() +}) + +const validateResendEmail = (req,res,next)=>{ + + try{ + + resendVerificationZod.parse(req.body); + next(); + + } + catch(err){ + + return res.status(400).json({ + success: false, + message: "Validation failed", + errors: err.errors.map(e => ({ + field: e.path.join("."), + message: e.message, + })), + }); + + } +} + +module.exports = { + validateUserLogin, + validateUserSignup, + validateRefreshToken, + validateResendEmail +} diff --git a/backend/Input_validators/ValidateQuestions.js b/backend/Input_validators/ValidateQuestions.js new file mode 100644 index 0000000..ce15de6 --- /dev/null +++ b/backend/Input_validators/ValidateQuestions.js @@ -0,0 +1,72 @@ +const { z } = require("zod"); + +// Schema for adding questions to a session +const addQuestionToSessionSchema = z.object({ + sessionId: z.string().min(1, "Session ID is required"), + questions: z.array( + z.object({ + question: z.string().min(1, "Question text is required"), + answer: z.string().min(1, "Answer text is required"), + }) + ).min(1, "At least one question is required"), +}); + +// Schema for toggling pin (params only) +const togglePinQuestionSchema = z.object({ + id: z.string().min(1, "Question ID is required"), +}); + +// Schema for updating note +const updateQuestionNoteSchema = z.object({ + note: z.string().min(1, "Note cannot be empty"), +}); + + +// Helper for consistent error responses +const handleValidationError = (res, error) => { + return res.status(400).json({ + success: false, + message: "Validation failed", + errors: error.errors.map(e => ({ + field: e.path.join("."), + message: e.message, + })), + }); +}; + +// Middleware for addQuestionToSession +const validateAddQuestionToSession = (req, res, next) => { + try { + addQuestionToSessionSchema.parse(req.body); + next(); + } catch (error) { + return handleValidationError(res, error); + } +}; + +// Middleware for togglePinQuestion (params) +const validateTogglePinQuestion = (req, res, next) => { + try { + togglePinQuestionSchema.parse(req.params); + next(); + } catch (error) { + return handleValidationError(res, error); + } +}; + +// Middleware for updateQuestionNote +const validateUpdateQuestionNote = (req, res, next) => { + try { + updateQuestionNoteSchema.parse(req.body); + next(); + } catch (error) { + return handleValidationError(res, error); + } +}; + +module.exports = { + validateAddQuestionToSession, + validateTogglePinQuestion, + validateUpdateQuestionNote, + handleValidationError +}; diff --git a/backend/Input_validators/ValidateResume.js b/backend/Input_validators/ValidateResume.js new file mode 100644 index 0000000..761d9f0 --- /dev/null +++ b/backend/Input_validators/ValidateResume.js @@ -0,0 +1,60 @@ +const { z } = require("zod"); +const { handleValidationError } = require('./ValidateQuestions') + +// Schema for compileResume request +const compileResumeSchema = z.object({ + code: z.string().min(1, "LaTeX code is required"), +}); + +// Schema for analyzeResume request +const analyzeResumeSchema = z.object({ + targetRole: z.string().min(1, "Target role is required").optional(), +}); + +// Schema for saveResume request +const saveResumeSchema = z.object({ + title: z.string().min(1, "Title is required"), + latexCode: z.string().min(1, "LaTeX code is required"), + resumeId: z.string().optional(), +}); + + +// Middleware for compileResume +const validateCompileResume = (req, res, next) => { + try { + compileResumeSchema.parse(req.body); + next(); + } catch (error) { + return handleValidationError(res, error); + } +}; + +// Middleware for analyzeResume +const validateAnalyzeResume = (req, res, next) => { + try { + analyzeResumeSchema.parse(req.body); + // also ensure file is uploaded + if (!req.file) { + return res.status(400).json({ success: false, message: "No resume file uploaded" }); + } + next(); + } catch (error) { + return handleValidationError(res, error); + } +}; + +// Middleware for saveResume +const validateSaveResume = (req, res, next) => { + try { + saveResumeSchema.parse(req.body); + next(); + } catch (error) { + return handleValidationError(res, error); + } +}; + +module.exports = { + validateCompileResume, + validateAnalyzeResume, + validateSaveResume, +}; \ No newline at end of file diff --git a/backend/Input_validators/ValidateSession.js b/backend/Input_validators/ValidateSession.js new file mode 100644 index 0000000..a0c2335 --- /dev/null +++ b/backend/Input_validators/ValidateSession.js @@ -0,0 +1,63 @@ +const { z } = require("zod"); +const { handleValidationError } = require("./ValidateQuestions"); + +// Schema for creating a session +const createSessionSchema = z.object({ + role: z.string().min(1, "Role is required"), + experience: z.string().min(1, "Experience is required"), + topicsToFocus: z.array(z.string()).min(1, "At least one topic is required"), + description: z.string().optional(), + question: z.array( + z.object({ + question: z.string().min(1, "Question text is required"), + answer: z.string().min(1, "Answer text is required"), + }) + ).optional(), +}); + +// Schema for getting a session by ID (params) +const getSessionByIdSchema = z.object({ + id: z.string().min(1, "Session ID is required"), +}); + +// Schema for deleting a session (params) +const deleteSessionSchema = z.object({ + id: z.string().min(1, "Session ID is required"), +}); + + +// Middleware for createSession +const validateCreateSession = (req, res, next) => { + try { + createSessionSchema.parse(req.body); + next(); + } catch (error) { + return handleValidationError(res, error); + } +}; + +// Middleware for getSessionById +const validateGetSessionById = (req, res, next) => { + try { + getSessionByIdSchema.parse(req.params); + next(); + } catch (error) { + return handleValidationError(res, error); + } +}; + +// Middleware for deleteSession +const validateDeleteSession = (req, res, next) => { + try { + deleteSessionSchema.parse(req.params); + next(); + } catch (error) { + return handleValidationError(res, error); + } +}; + +module.exports = { + validateCreateSession, + validateGetSessionById, + validateDeleteSession, +}; diff --git a/backend/Input_validators/ValidateUserSheetProgress.js b/backend/Input_validators/ValidateUserSheetProgress.js new file mode 100644 index 0000000..83642a0 --- /dev/null +++ b/backend/Input_validators/ValidateUserSheetProgress.js @@ -0,0 +1,42 @@ +const { z } = require("zod"); +const { handleValidationError } = require("./ValidateQuestions"); + +// Schema for saving/updating sheet progress +const saveProgressSchema = z.object({ + sheetId: z.string().min(1, "Sheet ID is required"), + followed: z.boolean().optional(), + completedTopics: z.array(z.string()).optional(), + percentage: z.number().int().min(0, "Percentage must be at least 0").max(100, "Percentage cannot exceed 100"), +}); + +// Schema for getting progress by sheetId (params) +const getProgressSchema = z.object({ + sheetId: z.string().min(1, "Sheet ID is required"), +}); + + + +// Middleware for saveProgress +const validateSaveProgress = (req, res, next) => { + try { + saveProgressSchema.parse(req.body); + next(); + } catch (error) { + return handleValidationError(res, error); + } +}; + +// Middleware for getProgress (params) +const validateGetProgress = (req, res, next) => { + try { + getProgressSchema.parse(req.params); + next(); + } catch (error) { + return handleValidationError(res, error); + } +}; + +module.exports = { + validateSaveProgress, + validateGetProgress +} \ No newline at end of file diff --git a/backend/config/db.js b/backend/config/db.js index 77e3c22..ee5dfc5 100644 --- a/backend/config/db.js +++ b/backend/config/db.js @@ -1,5 +1,6 @@ const mongoose = require("mongoose"); + const connectDB = async () => { try { await mongoose.connect(process.env.MONGO_URI, {}); diff --git a/backend/controllers/achievementController.js b/backend/controllers/achievementController.js index ba18526..dfc4c99 100644 --- a/backend/controllers/achievementController.js +++ b/backend/controllers/achievementController.js @@ -14,14 +14,6 @@ exports.getAchievements = async (req, res) => { exports.saveAchievements = async (req, res) => { const { unlockedAchievements } = req.body; - // Must be a non-empty array - if (!Array.isArray(unlockedAchievements)) { - return res.status(400).json({ - success: false, - error: 'unlockedAchievements must be an array', - }); - } - // Reject any ID not in the server-side allowlist const unknown = unlockedAchievements.filter((id) => !VALID_ACHIEVEMENTS.has(id)); if (unknown.length > 0) { diff --git a/backend/controllers/aiController.js b/backend/controllers/aiController.js index 9807743..69ac27a 100644 --- a/backend/controllers/aiController.js +++ b/backend/controllers/aiController.js @@ -39,10 +39,6 @@ const generateInterviewQuestions = async (req, res) => { try { const { role, experience, topicsToFocus, numberOfQuestions } = req.body; - if (!role || !experience || !topicsToFocus || !numberOfQuestions) { - return res.status(400).json({ message: "Missing required fields" }); - } - // Fetch questions the user has already seen for this role + topic const pastSessions = await Session.find({ user: req.user._id, @@ -161,9 +157,6 @@ const generateInterviewQuestions = async (req, res) => { const generateConceptExplanation = async (req, res) => { try { const { question } = req.body; - if (!question) { - return res.status(400).json({ message: "Missing question" }); - } const prompt = conceptExplainPrompt(question); @@ -233,10 +226,6 @@ const generateInterviewTips = async (req, res) => { try { const { role, experience } = req.body; - if (!role || !experience) { - return res.status(400).json({ message: "Missing required fields" }); - } - const prompt = interviewTipsPrompt({ role, experience }); const candidateModels = [ diff --git a/backend/controllers/questionController.js b/backend/controllers/questionController.js index 88822fa..3ecc182 100644 --- a/backend/controllers/questionController.js +++ b/backend/controllers/questionController.js @@ -23,9 +23,7 @@ const Session = require("../models/Session"); const addQuestionToSession = async (req, res) => { try { const { sessionId, questions } = req.body; - if (!sessionId || !questions || !Array.isArray(questions)) { - return res.status(400).json({ success: false, message: "Invalid or missing input data provided" }); - } + const session = await Session.findById(sessionId); if (!session) { @@ -67,11 +65,6 @@ const addQuestionToSession = async (req, res) => { const togglePinQuestion = async (req, res) => { try { const question = await Question.findById(req.params.id); - if (!question) { - return res - .status(404) - .json({ success: false, message: "Question not found" }); - } const session = await Session.findById(question.session); if (!session) { diff --git a/backend/controllers/resumeController.js b/backend/controllers/resumeController.js index 7bb9ceb..115444c 100644 --- a/backend/controllers/resumeController.js +++ b/backend/controllers/resumeController.js @@ -20,10 +20,7 @@ const { GoogleGenerativeAI } = require('@google/generative-ai'); const compileResume = async (req, res) => { try { const { code } = req.body; - if (!code) { - return res.status(400).json({ message: "No LaTeX code provided" }); - } - + // Normalize line endings to UNIX format, as texlive.net is highly sensitive to Windows \r\n const cleanCode = code.replace(/\r\n/g, '\n'); @@ -205,13 +202,9 @@ const Resume = require("../models/Resume"); */ const saveResume = async (req, res) => { try { - const { title, latexCode, resumeId } = req.body; + const userId = req.user._id || req.user.id; - if (!title || !latexCode) { - return res.status(400).json({ success: false, message: "Title and LaTeX code are required." }); - } - let resume; if (resumeId) { resume = await Resume.findOneAndUpdate( diff --git a/backend/package-lock.json b/backend/package-lock.json index 496a27b..8bd76ba 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -25,7 +25,8 @@ "node-cache": "^5.1.2", "nodemailer": "^8.0.10", "pdf-parse": "^2.4.5", - "resend": "^6.12.4" + "resend": "^6.12.4", + "zod": "^4.4.3" }, "devDependencies": { "nodemon": "^3.1.10", @@ -3793,6 +3794,15 @@ "optional": true } } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/backend/package.json b/backend/package.json index edea614..10fd0c4 100644 --- a/backend/package.json +++ b/backend/package.json @@ -28,7 +28,8 @@ "node-cache": "^5.1.2", "nodemailer": "^8.0.10", "pdf-parse": "^2.4.5", - "resend": "^6.12.4" + "resend": "^6.12.4", + "zod": "^4.4.3" }, "devDependencies": { "nodemon": "^3.1.10", diff --git a/backend/routes/achievementRoutes.js b/backend/routes/achievementRoutes.js index 04181b1..eab7cd6 100644 --- a/backend/routes/achievementRoutes.js +++ b/backend/routes/achievementRoutes.js @@ -1,9 +1,12 @@ const express = require('express'); const { getAchievements, saveAchievements } = require('../controllers/achievementController'); const { protect } = require('../middlewares/authMiddleware'); +const { validateSavedAchievements } = require('../Input_validators/ValidateAchievement'); const router = express.Router(); -router.get('/achievements', protect, getAchievements); -router.post('/achievements', protect, saveAchievements); +router.use(protect); + +router.get('/achievements',getAchievements); +router.post('/achievements', validateSavedAchievements, saveAchievements); module.exports = router; \ No newline at end of file diff --git a/backend/routes/authRoutes.js b/backend/routes/authRoutes.js index 56360ec..5b99865 100644 --- a/backend/routes/authRoutes.js +++ b/backend/routes/authRoutes.js @@ -2,23 +2,26 @@ const express = require("express"); const { registerUser, loginUser, verifyEmail, resendVerificationEmail, getUserProfile, updateUserProfile, changePassword, deleteUserAccount, refreshToken, logoutUser } = require("../controllers/authController"); const { protect } = require("../middlewares/authMiddleware"); const { upload } = require("../middlewares/uploadMiddleware"); +const { validateUserLogin, validateUserSignup, validateRefreshToken, validateResendEmail } = require("../Input_validators/ValidateAuth"); const router = express.Router(); + const { authLimiter, generalLimiter, sensitiveAuthLimiter, } = require("../middlewares/rateLimiter"); + // Auth Routes -router.post("/register", authLimiter, registerUser); -router.post("/login", authLimiter, loginUser); -router.post("/refresh", authLimiter, refreshToken); -router.post("/logout", authLimiter, logoutUser); +router.post("/register", authLimiter, validateUserSignup, registerUser); +router.post("/login", authLimiter, validateUserLogin, loginUser); +router.post("/refresh", authLimiter,validateRefreshToken, refreshToken); +router.post("/logout", authLimiter, validateRefreshToken, logoutUser); router.get("/profile", protect, generalLimiter, getUserProfile); router.put("/profile", protect, generalLimiter, updateUserProfile); router.put("/change-password", protect, sensitiveAuthLimiter, changePassword); router.delete("/delete-account", protect, sensitiveAuthLimiter, deleteUserAccount); -router.post("/resend-verification", authLimiter, resendVerificationEmail); +router.post("/resend-verification", authLimiter, validateResendEmail, resendVerificationEmail); router.get("/verify-email", verifyEmail); /** diff --git a/backend/routes/questionRoutes.js b/backend/routes/questionRoutes.js index bf1da6d..dad0cc2 100644 --- a/backend/routes/questionRoutes.js +++ b/backend/routes/questionRoutes.js @@ -3,30 +3,31 @@ const {togglePinQuestion, updateQuestionNote,addQuestionToSession} = require(".. const {protect} = require("../middlewares/authMiddleware"); const { generalLimiter } = require("../middlewares/rateLimiter"); +const { validateAddQuestionToSession, validateTogglePinQuestion, validateUpdateQuestionNote } = require('../Input_validators/ValidateQuestions'); const router = express.Router(); /** * Apply rate limiter to all question routes. */ -router.use(generalLimiter); +router.use(generalLimiter, protect); /** * Add new questions to an existing session. * @route POST /api/question/add */ -router.post('/add',protect,addQuestionToSession); +router.post('/add',validateAddQuestionToSession, addQuestionToSession); /** * Toggle pin state for a specific question. * @route POST /api/question/:id/pin */ -router.post('/:id/pin',protect,togglePinQuestion); +router.post('/:id/pin',validateTogglePinQuestion,togglePinQuestion); /** * Update the note field for a specific question. * @route POST /api/question/:id/note */ -router.post('/:id/note', protect, updateQuestionNote); +router.post('/:id/note', validateUpdateQuestionNote, updateQuestionNote); module.exports = router; \ No newline at end of file diff --git a/backend/routes/resumeRoutes.js b/backend/routes/resumeRoutes.js index f3fdf08..67cb05d 100644 --- a/backend/routes/resumeRoutes.js +++ b/backend/routes/resumeRoutes.js @@ -4,25 +4,28 @@ const { compileResume, analyzeResume, saveResume, getMyResumes } = require('../c const { protect } = require('../middlewares/authMiddleware'); const { upload, uploadResume } = require('../middlewares/uploadMiddleware'); const { aiLimiter } = require('../middlewares/rateLimiter'); +const { validateCompileResume, validateAnalyzeResume, validateSaveResume } = require('../Input_validators/ValidateResume'); + +router.use(protect); // @route POST /api/resume/compile // @desc Compile LaTeX code to PDF // @access Private — requires auth; aiLimiter caps external texlive.net calls to 20/hr per IP -router.post('/compile', protect, aiLimiter, compileResume); +router.post('/compile', aiLimiter,validateCompileResume, compileResume); // @route POST /api/resume/analyze // @desc Analyze resume using Gemini API // @access Private — requires auth; aiLimiter caps Gemini API calls to 20/hr per IP -router.post('/analyze', protect, aiLimiter, uploadResume.single("resume"), analyzeResume); +router.post('/analyze', aiLimiter, uploadResume.single("resume"), validateAnalyzeResume, analyzeResume); // @route POST /api/resume/save // @desc Save or update a resume // @access Private -router.post('/save', protect, saveResume); +router.post('/save', validateSaveResume, saveResume); // @route GET /api/resume/my-resumes // @desc Get all saved resumes for logged-in user // @access Private -router.get('/my-resumes', protect, getMyResumes); +router.get('/my-resumes', getMyResumes); module.exports = router; \ No newline at end of file diff --git a/backend/routes/sessionRoutes.js b/backend/routes/sessionRoutes.js index ff47953..c0f131d 100644 --- a/backend/routes/sessionRoutes.js +++ b/backend/routes/sessionRoutes.js @@ -6,6 +6,7 @@ const { deleteSession, } = require("../controllers/sessionController"); const { protect } = require("../middlewares/authMiddleware"); +const { validateCreateSession, validateGetSessionById, validateDeleteSession } = require("../Input_validators/ValidateSession"); const router = express.Router(); @@ -13,24 +14,26 @@ const router = express.Router(); * Create a new user session. * @route POST /api/sessions/create */ -router.post("/create", protect, createSession); + +router.use(protect); +router.post("/create", validateCreateSession, createSession); /** * Get all sessions for the authenticated user. * @route GET /api/sessions/my-sessions */ -router.get("/my-sessions", protect, getMySessions); +router.get("/my-sessions", getMySessions); /** * Get a session by its ID. * @route GET /api/sessions/:id */ -router.get("/:id", protect, getSessionById); +router.get("/:id", validateGetSessionById, getSessionById); /** * Delete a session by its ID. * @route DELETE /api/sessions/:id */ -router.delete("/:id", protect, deleteSession); +router.delete("/:id", validateDeleteSession, deleteSession); module.exports = router; diff --git a/backend/routes/userSheetProgressRoutes.js b/backend/routes/userSheetProgressRoutes.js index c542c02..c202302 100644 --- a/backend/routes/userSheetProgressRoutes.js +++ b/backend/routes/userSheetProgressRoutes.js @@ -1,6 +1,7 @@ const express = require('express'); const { saveProgress, getProgress } = require('../controllers/userSheetProgressController'); const { protect } = require('../middlewares/authMiddleware'); +const { validateSaveProgress, validateGetProgress } = require('../Input_validators/ValidateUserSheetProgress'); const router = express.Router(); @@ -8,18 +9,20 @@ const router = express.Router(); * Save or update progress for a user sheet. * @route POST /api/user/sheet-progress */ -router.post('/sheet-progress', protect, saveProgress); + +router.use(protect); +router.post('/sheet-progress', validateSaveProgress, saveProgress); /** * Get progress for a specific user sheet. * @route GET /api/user/sheet-progress/:sheetId */ -router.get('/sheet-progress/:sheetId', protect, getProgress); +router.get('/sheet-progress/:sheetId', validateGetProgress, getProgress); /** * Get all sheet progress records for the authenticated user. * @route GET /api/user/sheet-progress */ -router.get('/sheet-progress', protect, require('../controllers/userSheetProgressController').getAllProgress); +router.get('/sheet-progress', require('../controllers/userSheetProgressController').getAllProgress); module.exports = router; diff --git a/backend/server.js b/backend/server.js index 2c287c5..1397eb0 100644 --- a/backend/server.js +++ b/backend/server.js @@ -104,20 +104,26 @@ const achievementRoutes = require("./routes/achievementRoutes"); app.use("/api/user", generalLimiter, achievementRoutes); const booksRoutes = require("./routes/booksRoutes"); const { required } = require("joi"); +const { validateGenerateInterviewQuestions, validateGenerateConceptExplanation, validateGenerateInterviewTips } = require("./Input_validators/ValidateAi.js"); app.use("/api/resume", generalLimiter, resumeRoutes); + +// AI routes with Zod validation app.use( "/api/ai/generate-questions", sensitiveRouteHeaders, aiLimiter, protect, - generateInterviewQuestions, + validateGenerateInterviewQuestions, // Zod validator + generateInterviewQuestions // Controller ); + app.use( "/api/ai/generate-explanation", sensitiveRouteHeaders, aiLimiter, protect, - generateConceptExplanation, + validateGenerateConceptExplanation, // Zod validator + generateConceptExplanation // Controller ); app.use( @@ -125,9 +131,11 @@ app.use( sensitiveRouteHeaders, aiLimiter, protect, - generateInterviewTips, + validateGenerateInterviewTips, // Zod validator + generateInterviewTips // Controller ); + app.use("/api/books", generalLimiter, booksRoutes); //Serve uploads folder