From 0661820e2e2b1cff280c344f0adb66cfa7e1e1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ti=E1=BA=BFn=20Nguy=E1=BB=85n?= Date: Mon, 11 May 2026 00:09:16 +1000 Subject: [PATCH] Harden authenticated user data boundaries --- controller/accountController.js | 17 ++++++++-- controller/appointmentController.js | 39 +++++++++++++++++----- controller/chatbotController.js | 27 +++++++++++---- controller/recipeController.js | 21 +++++++++--- controller/shoppingListController.js | 37 +++++++++++--------- model/appointmentModel.js | 9 +++-- routes/account.js | 5 +-- routes/appointment.js | 13 ++++---- routes/chatbot.js | 7 ++-- routes/recipe.js | 9 ++--- routes/shoppingList.js | 15 +++++---- routes/waterIntake.js | 21 +++++++++--- services/shoppingListService.js | 50 ++++++++++++++++++++++++++-- validators/appointmentValidators.js | 4 +-- validators/recipeValidator.js | 6 ++-- validators/shoppingListValidator.js | 9 ++--- 16 files changed, 211 insertions(+), 78 deletions(-) diff --git a/controller/accountController.js b/controller/accountController.js index d95cda41..b9deee99 100644 --- a/controller/accountController.js +++ b/controller/accountController.js @@ -1,8 +1,21 @@ const getMealPlanByUserIdAndDate = require('../model/getMealPlanByUserIdAndDate.js'); +function resolveAccountUserId(req) { + const requestUserId = req.query?.user_id; + const currentUserId = req.user?.userId; + const role = String(req.user?.role || '').toLowerCase(); + + if ((role === 'admin' || role === 'nutritionist') && requestUserId) { + return requestUserId; + } + + return currentUserId; +} + const getAllAccount = async (req, res) => { try { - const { user_id, created_at } = req.query; + const { created_at } = req.query; + const user_id = resolveAccountUserId(req); const mealPlans = await getMealPlanByUserIdAndDate(user_id, created_at); @@ -19,4 +32,4 @@ const getAllAccount = async (req, res) => { module.exports = { getAllAccount -}; \ No newline at end of file +}; diff --git a/controller/appointmentController.js b/controller/appointmentController.js index 7100a454..d61900df 100644 --- a/controller/appointmentController.js +++ b/controller/appointmentController.js @@ -13,6 +13,22 @@ function validationFailure(res, errors) { return res.status(400).json({ errors: errors.array() }); } +function resolveAppointmentUserId(req) { + const requestUserId = + req.body?.userId || + req.body?.user_id || + req.query?.userId || + req.query?.user_id; + const currentUserId = req.user?.userId; + const role = String(req.user?.role || '').toLowerCase(); + + if (role === 'admin' || role === 'nutritionist') { + return requestUserId || currentUserId; + } + + return currentUserId; +} + function internalFailure(res, label, error, context = {}) { logger.error(label, { error: error.message, ...context }); return res.status(500).json({ error: 'Internal server error' }); @@ -23,7 +39,8 @@ const saveAppointment = async (req, res) => { if (!errors.isEmpty()) { return validationFailure(res, errors); } - const { userId, date, time, description } = req.body; + const userId = resolveAppointmentUserId(req); + const { date, time, description } = req.body; try { await addAppointment(userId, date, time, description); res.status(201).json({ message: 'Appointment saved successfully' }); @@ -37,7 +54,8 @@ const saveAppointmentV2 = async (req, res) => { if (!errors.isEmpty()) { return validationFailure(res, errors); } - const { userId, title, doctor, type, date, time, location, address, phone, notes, reminder } = req.body; + const userId = resolveAppointmentUserId(req); + const { title, doctor, type, date, time, location, address, phone, notes, reminder } = req.body; try { const appointment = await addAppointmentModelV2({ userId, title, doctor, type, date, time, location, address, phone, notes, reminder, @@ -54,32 +72,37 @@ const updateAppointment = async (req, res) => { return validationFailure(res, errors); } const { id } = req.params; + const userId = resolveAppointmentUserId(req); const { title, doctor, type, date, time, location, address, phone, notes, reminder } = req.body; try { - const updatedAppointment = await updateAppointmentModel(id, { + const updatedAppointment = await updateAppointmentModel(id, userId, { title, doctor, type, date, time, location, address, phone, notes, reminder, }); + if (!updatedAppointment) { + return res.status(404).json({ message: 'Appointment not found' }); + } res.status(200).json({ message: 'Appointment updated successfully', appointment: updatedAppointment }); } catch (error) { - return internalFailure(res, 'Error updating appointment', error, { appointmentId: id }); + return internalFailure(res, 'Error updating appointment', error, { appointmentId: id, userId }); } }; const delAppointment = async (req, res) => { const { id } = req.params; + const userId = resolveAppointmentUserId(req); try { - const deleted = await deleteAppointmentById(id); + const deleted = await deleteAppointmentById(id, userId); if (!deleted) { return res.status(404).json({ message: 'Appointment not found' }); } res.status(200).json({ message: 'Appointment deleted successfully' }); } catch (error) { - return internalFailure(res, 'Error deleting appointment', error, { appointmentId: id }); + return internalFailure(res, 'Error deleting appointment', error, { appointmentId: id, userId }); } }; const getAppointments = async (req, res) => { - const userId = req.query.userId || req.user?.id || req.user?.user_id; + const userId = resolveAppointmentUserId(req); try { const appointments = await getAppointmentsByUserId(userId); res.status(200).json(appointments); @@ -93,7 +116,7 @@ const getAppointmentsV2 = async (req, res) => { const page = parseInt(req.query.page, 10) || 1; const pageSize = parseInt(req.query.pageSize, 10) || 10; const search = req.query.search || ''; - const userId = req.query.userId || req.user?.id || req.user?.user_id; + const userId = resolveAppointmentUserId(req); const from = (page - 1) * pageSize; const to = from + pageSize - 1; diff --git a/controller/chatbotController.js b/controller/chatbotController.js index fb6396e2..957a46c6 100644 --- a/controller/chatbotController.js +++ b/controller/chatbotController.js @@ -25,10 +25,23 @@ function handleUnexpectedError(res, label, error, context = {}) { ); } +function resolveChatbotUserId(req) { + const requestUserId = req.body?.user_id || req.query?.user_id; + const currentUserId = req.user?.userId; + const role = String(req.user?.role || '').toLowerCase(); + + if ((role === 'admin' || role === 'nutritionist') && requestUserId) { + return requestUserId; + } + + return currentUserId; +} + async function getChatResponse(req, res) { try { + const userId = resolveChatbotUserId(req); const result = await chatbotService.getChatResponse({ - userId: req.body.user_id, + userId, userInput: req.body.user_input }); return res.status(result.statusCode).json(result.body); @@ -38,7 +51,7 @@ async function getChatResponse(req, res) { } return handleUnexpectedError(res, 'Error in chatbot response', error, { - userId: req.body.user_id + userId: resolveChatbotUserId(req) }); } } @@ -73,7 +86,8 @@ async function addPDF(req, res) { async function getChatHistory(req, res) { try { - const result = await chatbotService.getChatHistory(req.body.user_id); + const userId = resolveChatbotUserId(req); + const result = await chatbotService.getChatHistory(userId); return res.status(result.statusCode).json(result.body); } catch (error) { if (isServiceError(error)) { @@ -81,14 +95,15 @@ async function getChatHistory(req, res) { } return handleUnexpectedError(res, 'Error retrieving chat history', error, { - userId: req.body.user_id + userId: resolveChatbotUserId(req) }); } } async function clearChatHistory(req, res) { try { - const result = await chatbotService.clearChatHistory(req.body.user_id); + const userId = resolveChatbotUserId(req); + const result = await chatbotService.clearChatHistory(userId); return res.status(result.statusCode).json(result.body); } catch (error) { if (isServiceError(error)) { @@ -96,7 +111,7 @@ async function clearChatHistory(req, res) { } return handleUnexpectedError(res, 'Error clearing chat history', error, { - userId: req.body.user_id + userId: resolveChatbotUserId(req) }); } } diff --git a/controller/recipeController.js b/controller/recipeController.js index 0ad869bf..681495f8 100644 --- a/controller/recipeController.js +++ b/controller/recipeController.js @@ -14,6 +14,18 @@ const normalizeId = (id) => { return id; }; +const resolveRecipeUserId = (req) => { + const requestUserId = req.body?.user_id || req.query?.user_id || req.params?.user_id; + const currentUserId = req.user?.userId; + const role = String(req.user?.role || '').toLowerCase(); + + if ((role === 'admin' || role === 'nutritionist') && requestUserId) { + return normalizeId(requestUserId); + } + + return normalizeId(currentUserId); +}; + async function enrichRecipeRow(recipe) { if (!recipe) return null; @@ -60,7 +72,6 @@ async function getRecipeDetailRow(recipeId) { const createAndSaveRecipe = async (req, res) => { const { - user_id, ingredient_id, ingredient_quantity, recipe_name, @@ -78,6 +89,7 @@ const createAndSaveRecipe = async (req, res) => { return res.status(400).json({ errors: errors.array() }); } + const user_id = resolveRecipeUserId(req); const recipe = await createRecipe.createRecipe( user_id, ingredient_id, @@ -124,7 +136,7 @@ const createAndSaveRecipe = async (req, res) => { }; const getRecipes = async (req, res) => { - const user_id = req.body.user_id; + const user_id = resolveRecipeUserId(req); try { if (!user_id) { @@ -228,7 +240,7 @@ const getRecipes = async (req, res) => { const getUserRecipes = async (req, res) => { try { - let userId = req.params.user_id || req.query.user_id; + let userId = resolveRecipeUserId(req); if (!userId) { return res.status(400).json({ success: false, error: 'user_id is required' }); } @@ -291,7 +303,8 @@ const getRecipeNutrition = async (req, res) => { }; const deleteRecipe = async (req, res) => { - const { user_id, recipe_id } = req.body; + const user_id = resolveRecipeUserId(req); + const { recipe_id } = req.body; try { if (!user_id || !recipe_id) { diff --git a/controller/shoppingListController.js b/controller/shoppingListController.js index d0dd96c3..759fb56f 100644 --- a/controller/shoppingListController.js +++ b/controller/shoppingListController.js @@ -29,6 +29,22 @@ function sendWrapped(res, statusCode, body) { }); } +function resolveShoppingListUserId(req) { + const requestUserId = + req.body?.user_id || + req.body?.userId || + req.query?.user_id || + req.query?.userId; + const currentUserId = req.user?.userId; + const role = String(req.user?.role || '').toLowerCase(); + + if (role === 'admin' || role === 'nutritionist') { + return requestUserId ? normalizeId(requestUserId) : currentUserId; + } + + return currentUserId; +} + async function getIngredientOptions(req, res) { try { const name = req.query?.name || ''; @@ -41,10 +57,7 @@ async function getIngredientOptions(req, res) { async function generateFromMealPlan(req, res) { try { - if (!req.body || !req.body.user_id) { - return res.status(400).json({ success: false, error: 'user_id required' }); - } - const userId = normalizeId(req.body.user_id); + const userId = resolveShoppingListUserId(req); const mealPlanIds = Array.isArray(req.body.meal_plan_ids) ? req.body.meal_plan_ids : []; const result = await shoppingListService.generateFromMealPlan({ userId, mealPlanIds }); return sendWrapped(res, result.statusCode || 200, result.body || result); @@ -55,10 +68,7 @@ async function generateFromMealPlan(req, res) { async function createShoppingList(req, res) { try { - if (!req.body || !req.body.user_id) { - return res.status(400).json({ success: false, error: 'user_id required' }); - } - const userId = normalizeId(req.body.user_id); + const userId = resolveShoppingListUserId(req); const result = await shoppingListService.createShoppingList({ userId, name: req.body.name, @@ -73,11 +83,7 @@ async function createShoppingList(req, res) { async function getShoppingList(req, res) { try { - const rawUserId = req.query?.user_id || req.query?.userId; - if (!rawUserId) { - return res.status(400).json({ success: false, error: 'user_id required' }); - } - const userId = normalizeId(rawUserId); + const userId = resolveShoppingListUserId(req); const result = await shoppingListService.getShoppingList(userId); return sendWrapped(res, result.statusCode || 200, result.body || result); } catch (error) { @@ -92,7 +98,7 @@ async function updateShoppingListItem(req, res) { return res.status(400).json({ success: false, error: 'id param required' }); } const id = normalizeId(rawId); - const result = await shoppingListService.updateShoppingListItem(id, { + const result = await shoppingListService.updateShoppingListItem(id, req.user?.userId, { purchased: req.body?.purchased, quantity: req.body?.quantity, notes: req.body?.notes @@ -106,6 +112,7 @@ async function updateShoppingListItem(req, res) { async function addShoppingListItem(req, res) { try { const result = await shoppingListService.addShoppingListItem({ + userId: req.user?.userId, shoppingListId: req.body?.shopping_list_id ? normalizeId(req.body.shopping_list_id) : undefined, ingredientName: req.body?.ingredient_name, category: req.body?.category, @@ -129,7 +136,7 @@ async function deleteShoppingListItem(req, res) { return res.status(400).json({ success: false, error: 'id param required' }); } const id = normalizeId(rawId); - const result = await shoppingListService.deleteShoppingListItem(id); + const result = await shoppingListService.deleteShoppingListItem(id, req.user?.userId); return sendWrapped(res, result.statusCode || 200, result.body || result); } catch (error) { return handleError(res, error, 'deleteShoppingListItem'); diff --git a/model/appointmentModel.js b/model/appointmentModel.js index 0f4c2c23..a286237b 100644 --- a/model/appointmentModel.js +++ b/model/appointmentModel.js @@ -66,6 +66,7 @@ async function addAppointmentModelV2({ async function updateAppointmentModel( id, + userId, { title, doctor, @@ -95,26 +96,28 @@ async function updateAppointmentModel( reminder, }) .eq("id", id) + .eq("user_id", userId) .select() .single(); - if (error) throw error; + if (error && error.code !== "PGRST116") throw error; return data; } catch (err) { throw err; } } -async function deleteAppointmentById(id) { +async function deleteAppointmentById(id, userId) { try { const { data, error } = await supabase .from("appointments") .delete() .eq("id", id) + .eq("user_id", userId) .select() .single(); - if (error) throw error; + if (error && error.code !== "PGRST116") throw error; return data; } catch (err) { throw err; diff --git a/routes/account.js b/routes/account.js index 9b9e28a5..cc31effc 100644 --- a/routes/account.js +++ b/routes/account.js @@ -1,7 +1,8 @@ const express = require('express'); const router = express.Router(); const controller = require("../controller/accountController"); +const { authenticateToken } = require('../middleware/authenticateToken'); -router.route('/').get(controller.getAllAccount); +router.route('/').get(authenticateToken, controller.getAllAccount); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/routes/appointment.js b/routes/appointment.js index 600eddc3..a3607040 100644 --- a/routes/appointment.js +++ b/routes/appointment.js @@ -2,6 +2,7 @@ const express = require('express'); const router = express.Router(); const { coreApp } = require('../controller'); +const { authenticateToken } = require('../middleware/authenticateToken'); const { createAppointment: appointmentValidator } = require('../validators/appointmentValidators.js'); const validate = require('../middleware/validateRequest.js'); @@ -9,16 +10,16 @@ const { appointments: appointmentController } = coreApp; // Legacy appointment routes router.route('/') - .post(appointmentValidator, validate, appointmentController.saveAppointment) - .get(appointmentController.getAppointments); + .post(authenticateToken, appointmentValidator, validate, appointmentController.saveAppointment) + .get(authenticateToken, appointmentController.getAppointments); // Structured appointment routes used by newer clients router.route('/v2') - .post(appointmentValidator, validate, appointmentController.saveAppointmentV2) - .get(appointmentController.getAppointmentsV2); + .post(authenticateToken, appointmentValidator, validate, appointmentController.saveAppointmentV2) + .get(authenticateToken, appointmentController.getAppointmentsV2); router.route('/v2/:id') - .put(appointmentValidator, validate, appointmentController.updateAppointment) - .delete(appointmentController.delAppointment); + .put(authenticateToken, appointmentValidator, validate, appointmentController.updateAppointment) + .delete(authenticateToken, appointmentController.delAppointment); module.exports = router; diff --git a/routes/chatbot.js b/routes/chatbot.js index 74c14787..2e7d487b 100644 --- a/routes/chatbot.js +++ b/routes/chatbot.js @@ -1,16 +1,17 @@ const express = require('express'); const router = express.Router(); const { aiAndMedical } = require('../controller'); +const { authenticateToken } = require('../middleware/authenticateToken'); const { chatbot: chatbotController } = aiAndMedical; -router.route('/query').post(chatbotController.getChatResponse); +router.route('/query').post(authenticateToken, chatbotController.getChatResponse); // router.route('/chat').post(chatbotController.getChatResponse); router.route('/add_urls').post(chatbotController.addURL); router.route('/add_pdfs').post(chatbotController.addPDF); -router.route('/history').post(chatbotController.getChatHistory); -router.route('/history').delete(chatbotController.clearChatHistory); +router.route('/history').post(authenticateToken, chatbotController.getChatHistory); +router.route('/history').delete(authenticateToken, chatbotController.clearChatHistory); module.exports = router; diff --git a/routes/recipe.js b/routes/recipe.js index 8518ece7..e329625d 100644 --- a/routes/recipe.js +++ b/routes/recipe.js @@ -1,18 +1,19 @@ const express = require('express'); const router = express.Router(); const recipeController = require('../controller/recipeController.js'); +const { authenticateToken } = require('../middleware/authenticateToken'); const { validateRecipe } = require('../validators/recipeValidator.js'); const validateRequest = require('../middleware/validateRequest.js'); -router.post('/createRecipe', validateRecipe, validateRequest, recipeController.createAndSaveRecipe); +router.post('/createRecipe', authenticateToken, validateRecipe, validateRequest, recipeController.createAndSaveRecipe); -router.get('/user/:user_id', (req, res, next) => { +router.get('/user/:user_id', authenticateToken, (req, res, next) => { req.query.user_id = req.params.user_id; next(); }, recipeController.getUserRecipes); router.get('/:id', recipeController.getRecipeById); -router.post('/', recipeController.getRecipes); -router.delete('/', recipeController.deleteRecipe); +router.post('/', authenticateToken, recipeController.getRecipes); +router.delete('/', authenticateToken, recipeController.deleteRecipe); module.exports = router; diff --git a/routes/shoppingList.js b/routes/shoppingList.js index 7d06b476..138f3939 100644 --- a/routes/shoppingList.js +++ b/routes/shoppingList.js @@ -1,6 +1,7 @@ const express = require('express'); const router = express.Router(); const { coreApp } = require('../controller'); +const { authenticateToken } = require('../middleware/authenticateToken'); const { getIngredientOptionsValidation, generateFromMealPlanValidation, @@ -15,19 +16,19 @@ const validate = require('../middleware/validateRequest.js'); const controller = coreApp.shoppingList; // Planning helpers -router.get('/ingredient-options', getIngredientOptionsValidation, validate, controller.getIngredientOptions); -router.post('/from-meal-plan', generateFromMealPlanValidation, validate, controller.generateFromMealPlan); +router.get('/ingredient-options', authenticateToken, getIngredientOptionsValidation, validate, controller.getIngredientOptions); +router.post('/from-meal-plan', authenticateToken, generateFromMealPlanValidation, validate, controller.generateFromMealPlan); // Shopping list collection router.route('/') - .post(createShoppingListValidation, validate, controller.createShoppingList) - .get(getShoppingListValidation, validate, controller.getShoppingList); + .post(authenticateToken, createShoppingListValidation, validate, controller.createShoppingList) + .get(authenticateToken, getShoppingListValidation, validate, controller.getShoppingList); // Shopping list items -router.post('/items', addShoppingListItemValidation, validate, controller.addShoppingListItem); +router.post('/items', authenticateToken, addShoppingListItemValidation, validate, controller.addShoppingListItem); router.route('/items/:id') - .patch(updateShoppingListItemValidation, validate, controller.updateShoppingListItem) - .delete(deleteShoppingListItemValidation, validate, controller.deleteShoppingListItem); + .patch(authenticateToken, updateShoppingListItemValidation, validate, controller.updateShoppingListItem) + .delete(authenticateToken, deleteShoppingListItemValidation, validate, controller.deleteShoppingListItem); module.exports = router; diff --git a/routes/waterIntake.js b/routes/waterIntake.js index b900066d..b45a5521 100644 --- a/routes/waterIntake.js +++ b/routes/waterIntake.js @@ -1,11 +1,24 @@ const express = require('express'); const router = express.Router(); const supabase = require('../dbConnection'); +const { authenticateToken } = require('../middleware/authenticateToken'); const normalizeId = require('../utils/normalizeId'); -router.get('/', async (req, res) => { +function resolveWaterIntakeUserId(req) { + const requestUserId = req.query.user_id || req.body?.user_id; + const currentUserId = req.user?.userId; + const role = String(req.user?.role || '').toLowerCase(); + + if ((role === 'admin' || role === 'nutritionist') && requestUserId) { + return normalizeId(requestUserId); + } + + return currentUserId; +} + +router.get('/', authenticateToken, async (req, res) => { try { - const userId = normalizeId(req.query.user_id); + const userId = resolveWaterIntakeUserId(req); const date = req.query.date || new Date().toISOString().split('T')[0]; const { data, error } = await supabase @@ -21,9 +34,9 @@ router.get('/', async (req, res) => { } }); -router.post('/', async (req, res) => { +router.post('/', authenticateToken, async (req, res) => { try { - const userId = normalizeId(req.body.user_id); + const userId = resolveWaterIntakeUserId(req); const { amount_ml, date } = req.body; const { data, error } = await supabase diff --git a/services/shoppingListService.js b/services/shoppingListService.js index acd69572..437896e6 100644 --- a/services/shoppingListService.js +++ b/services/shoppingListService.js @@ -14,6 +14,44 @@ function successResponse(statusCode, data) { } class ShoppingListService { + async assertShoppingListOwnership(shoppingListId, userId) { + const { data, error } = await supabase + .from('shopping_lists') + .select('id') + .eq('id', shoppingListId) + .eq('user_id', userId) + .maybeSingle(); + + if (error) { + throw new ServiceError(500, 'Failed to verify shopping list ownership'); + } + + if (!data) { + throw new ServiceError(404, 'Shopping list not found'); + } + + return data; + } + + async assertShoppingListItemOwnership(itemId, userId) { + const { data: item, error: itemError } = await supabase + .from('shopping_list_items') + .select('id, shopping_list_id') + .eq('id', itemId) + .maybeSingle(); + + if (itemError) { + throw new ServiceError(500, 'Failed to verify shopping list item ownership'); + } + + if (!item) { + throw new ServiceError(404, 'Shopping list item not found'); + } + + await this.assertShoppingListOwnership(item.shopping_list_id, userId); + return item; + } + async getIngredientOptions(name) { if (!name) { throw new ServiceError(400, 'Ingredient name parameter is required'); @@ -248,7 +286,9 @@ class ShoppingListService { return successResponse(200, result); } - async updateShoppingListItem(id, updates) { + async updateShoppingListItem(id, userId, updates) { + await this.assertShoppingListItemOwnership(id, userId); + const updateData = {}; if (updates.purchased !== undefined) updateData.purchased = updates.purchased; if (updates.quantity !== undefined) updateData.quantity = updates.quantity; @@ -269,10 +309,12 @@ class ShoppingListService { } async addShoppingListItem(item) { - if (!item.shoppingListId || !item.ingredientName) { + if (!item.userId || !item.shoppingListId || !item.ingredientName) { throw new ServiceError(400, 'Shopping list ID and ingredient name are required'); } + await this.assertShoppingListOwnership(item.shoppingListId, item.userId); + const itemData = { shopping_list_id: item.shoppingListId, ingredient_name: item.ingredientName, @@ -299,7 +341,9 @@ class ShoppingListService { return successResponse(201, data); } - async deleteShoppingListItem(id) { + async deleteShoppingListItem(id, userId) { + await this.assertShoppingListItemOwnership(id, userId); + const { error } = await supabase .from('shopping_list_items') .delete() diff --git a/validators/appointmentValidators.js b/validators/appointmentValidators.js index 84ae8bdd..19b22f7a 100644 --- a/validators/appointmentValidators.js +++ b/validators/appointmentValidators.js @@ -5,8 +5,8 @@ const { body, param, query } = require('express-validator'); const createAppointment = [ - body('user_id').exists().withMessage('user_id is required'), - body('user_id').isInt().withMessage('user_id must be numeric').toInt(), + body('user_id').optional().isInt().withMessage('user_id must be numeric').toInt(), + body('userId').optional().isInt().withMessage('userId must be numeric').toInt(), body('date').optional().isISO8601().withMessage('date must be ISO8601 (YYYY-MM-DD)'), body('time').optional().isString(), body('location').optional().isString(), diff --git a/validators/recipeValidator.js b/validators/recipeValidator.js index d8c93a8a..bc8c78aa 100644 --- a/validators/recipeValidator.js +++ b/validators/recipeValidator.js @@ -2,7 +2,7 @@ const Joi = require('joi'); const { body } = require('express-validator'); const recipeSchema = Joi.object({ - user_id: Joi.alternatives().try(Joi.number(), Joi.string()).required(), + user_id: Joi.alternatives().try(Joi.number(), Joi.string()).optional(), recipe_name: Joi.string().required(), cuisine_id: Joi.number().optional(), total_servings: Joi.number().min(1).required(), @@ -15,11 +15,11 @@ const recipeSchema = Joi.object({ }); const getRecipesSchema = Joi.object({ - user_id: Joi.alternatives().try(Joi.number(), Joi.string()).required() + user_id: Joi.alternatives().try(Joi.number(), Joi.string()).optional() }); const validateRecipe = [ - body('user_id').notEmpty().withMessage('user_id is required'), + body('user_id').optional(), body('recipe_name').notEmpty().withMessage('recipe_name is required'), body('total_servings').isInt({ min: 1 }).withMessage('total_servings must be at least 1'), body('preparation_time').isInt({ min: 1 }).withMessage('preparation_time must be at least 1'), diff --git a/validators/shoppingListValidator.js b/validators/shoppingListValidator.js index 4fb2d3d9..7cb861ce 100644 --- a/validators/shoppingListValidator.js +++ b/validators/shoppingListValidator.js @@ -12,8 +12,7 @@ const getIngredientOptionsValidation = [ // Validation for generating shopping list from meal plan const generateFromMealPlanValidation = [ body('user_id') - .notEmpty() - .withMessage('User ID cannot be empty') + .optional() .isInt({ min: 1 }) .withMessage('User ID must be a positive integer'), body('meal_plan_ids') @@ -28,8 +27,7 @@ const generateFromMealPlanValidation = [ // Validation for creating shopping list const createShoppingListValidation = [ body('user_id') - .notEmpty() - .withMessage('User ID cannot be empty') + .optional() .isInt({ min: 1 }) .withMessage('User ID must be a positive integer'), body('name') @@ -59,8 +57,7 @@ const createShoppingListValidation = [ // Validation for getting shopping list const getShoppingListValidation = [ query('user_id') - .notEmpty() - .withMessage('User ID cannot be empty') + .optional() .isInt({ min: 1 }) .withMessage('User ID must be a positive integer') ];