sanket3280/code-execution
0
1/**2 * Validation Middleware3 * Provides reusable validation rules for common fields4 */5 6const { check, validationResult } = require('express-validator');7const { errorResponse } = require('../utils/response');8 9/**10 * Validation error handler middleware11 */12const handleValidationErrors = (req, res, next) => {13 const errors = validationResult(req);14 if (!errors.isEmpty()) {15 return errorResponse(res, 'Validation failed', 400, errors.array());16 }17 next();18};19 20/**21 * Common validation rules22 */23const validationRules = {24 // User validations25 username: check('username')26 .trim()27 .isLength({ min: 3, max: 30 })28 .withMessage('Username must be between 3 and 30 characters')29 .matches(/^[a-zA-Z0-9_-]+$/)30 .withMessage('Username can only contain letters, numbers, underscores, and hyphens'),31 32 email: check('email')33 .trim()34 .isEmail()35 .withMessage('Please provide a valid email')36 .normalizeEmail(),37 38 password: check('password')39 .isLength({ min: 8 })40 .withMessage('Password must be at least 8 characters')41 .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)42 .withMessage('Password must contain at least one uppercase letter, one lowercase letter, and one number'),43 44 // MongoDB ObjectId validation45 mongoId: (field = 'id') => check(field)46 .isMongoId()47 .withMessage(`Invalid ${field} format`),48 49 // Pagination validations50 page: check('page')51 .optional()52 .isInt({ min: 1 })53 .withMessage('Page must be a positive integer')54 .toInt(),55 56 limit: check('limit')57 .optional()58 .isInt({ min: 1, max: 100 })59 .withMessage('Limit must be between 1 and 100')60 .toInt(),61 62 // Problem validations63 difficulty: check('difficulty')64 .optional()65 .isIn(['easy', 'medium', 'hard'])66 .withMessage('Difficulty must be easy, medium, or hard'),67 68 category: check('category')69 .optional()70 .isString()71 .trim()72 .withMessage('Category must be a string'),73 74 // Challenge validations75 title: check('title')76 .trim()77 .isLength({ min: 3, max: 100 })78 .withMessage('Title must be between 3 and 100 characters'),79 80 description: check('description')81 .trim()82 .isLength({ min: 10, max: 5000 })83 .withMessage('Description must be between 10 and 5000 characters'),84 85 // Date validations86 startDate: check('startDate')87 .isISO8601()88 .withMessage('Start date must be a valid ISO 8601 date')89 .toDate(),90 91 endDate: check('endDate')92 .isISO8601()93 .withMessage('End date must be a valid ISO 8601 date')94 .toDate()95 .custom((value, { req }) => {96 if (req.body.startDate && new Date(value) <= new Date(req.body.startDate)) {97 throw new Error('End date must be after start date');98 }99 return true;100 }),101 102 // OTP validation103 otp: check('otp')104 .isLength({ min: 6, max: 6 })105 .withMessage('OTP must be 6 digits')106 .isNumeric()107 .withMessage('OTP must contain only numbers'),108};109 110/**111 * Validation rule sets for common operations112 */113const validationSets = {114 register: [115 validationRules.username,116 validationRules.email,117 validationRules.password,118 handleValidationErrors,119 ],120 121 login: [122 check('emailOrUsername')123 .trim()124 .notEmpty()125 .withMessage('Email or username is required'),126 check('password')127 .notEmpty()128 .withMessage('Password is required'),129 handleValidationErrors,130 ],131 132 updateProfile: [133 validationRules.username.optional(),134 check('avatar')135 .optional()136 .isURL()137 .withMessage('Avatar must be a valid URL'),138 handleValidationErrors,139 ],140 141 changePassword: [142 check('currentPassword')143 .notEmpty()144 .withMessage('Current password is required'),145 validationRules.password,146 handleValidationErrors,147 ],148 149 createProblem: [150 validationRules.title,151 validationRules.description,152 validationRules.difficulty,153 validationRules.category,154 handleValidationErrors,155 ],156 157 createChallenge: [158 validationRules.title,159 validationRules.description,160 validationRules.startDate,161 validationRules.endDate,162 check('problems')163 .isArray({ min: 1 })164 .withMessage('At least one problem is required'),165 handleValidationErrors,166 ],167 168 pagination: [169 validationRules.page,170 validationRules.limit,171 handleValidationErrors,172 ],173 174 forgotPassword: [175 validationRules.email,176 handleValidationErrors,177 ],178 179 verifyOTP: [180 validationRules.email,181 validationRules.otp,182 handleValidationErrors,183 ],184 185 resetPassword: [186 validationRules.email,187 validationRules.otp,188 validationRules.password,189 handleValidationErrors,190 ],191};192 193module.exports = {194 validationRules,195 validationSets,196 handleValidationErrors,197};198 