171 lines
5.6 KiB
JavaScript
171 lines
5.6 KiB
JavaScript
const Joi = require('joi');
|
|
|
|
// User validation schemas
|
|
const registerSchema = Joi.object({
|
|
email: Joi.string().email().required(),
|
|
password: Joi.string().min(8).pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&#+])[A-Za-z\d@$!%*?&#+]/).required()
|
|
.messages({
|
|
'string.pattern.base': 'Password must contain at least one lowercase letter, one uppercase letter, one number, and one special character'
|
|
}),
|
|
firstName: Joi.string().max(100).required(),
|
|
lastName: Joi.string().max(100).required()
|
|
});
|
|
|
|
const loginSchema = Joi.object({
|
|
email: Joi.string().email().required(),
|
|
password: Joi.string().required()
|
|
});
|
|
|
|
const updateUserSchema = Joi.object({
|
|
firstName: Joi.string().max(100),
|
|
lastName: Joi.string().max(100),
|
|
email: Joi.string().email()
|
|
});
|
|
|
|
const changePasswordSchema = Joi.object({
|
|
currentPassword: Joi.string().required(),
|
|
newPassword: Joi.string().min(8).pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&#+])[A-Za-z\d@$!%*?&#+]/).required()
|
|
.messages({
|
|
'string.pattern.base': 'Password must contain at least one lowercase letter, one uppercase letter, one number, and one special character'
|
|
})
|
|
});
|
|
|
|
// Property validation schemas
|
|
const propertySchema = Joi.object({
|
|
name: Joi.string().max(255).required(),
|
|
address: Joi.string().max(500),
|
|
latitude: Joi.number().min(-90).max(90),
|
|
longitude: Joi.number().min(-180).max(180),
|
|
totalArea: Joi.number().positive()
|
|
});
|
|
|
|
const lawnSectionSchema = Joi.object({
|
|
name: Joi.string().max(255).required(),
|
|
area: Joi.number().positive().required(),
|
|
polygonData: Joi.object(),
|
|
grassType: Joi.string().max(100).allow(null, '').optional(),
|
|
soilType: Joi.string().max(100).allow(null, '').optional()
|
|
});
|
|
|
|
// Equipment validation schemas
|
|
const equipmentSchema = Joi.object({
|
|
equipmentTypeId: Joi.number().integer().positive().required(),
|
|
customName: Joi.string().max(255),
|
|
tankSize: Joi.number().positive(),
|
|
pumpGpm: Joi.number().positive(),
|
|
nozzleGpm: Joi.number().positive(),
|
|
nozzleCount: Joi.number().integer().positive(),
|
|
spreaderWidth: Joi.number().positive()
|
|
});
|
|
|
|
// Product validation schemas
|
|
const productSchema = Joi.object({
|
|
name: Joi.string().max(255).required(),
|
|
brand: Joi.string().max(100),
|
|
categoryId: Joi.number().integer().positive().required(),
|
|
productType: Joi.string().valid('granular', 'liquid').required(),
|
|
activeIngredients: Joi.string(),
|
|
description: Joi.string()
|
|
});
|
|
|
|
const productRateSchema = Joi.object({
|
|
applicationType: Joi.string().max(100).required(),
|
|
rateAmount: Joi.number().positive().required(),
|
|
rateUnit: Joi.string().max(50).required(),
|
|
notes: Joi.string()
|
|
});
|
|
|
|
const userProductSchema = Joi.object({
|
|
productId: Joi.number().integer().positive().allow(null).optional(),
|
|
customName: Joi.string().max(255).allow(null).optional(),
|
|
customRateAmount: Joi.number().positive().allow(null).optional(),
|
|
customRateUnit: Joi.string().max(50).allow(null).optional(),
|
|
notes: Joi.string().allow(null, '').optional(),
|
|
// Additional fields for advanced editing
|
|
brand: Joi.string().max(100).allow(null).optional(),
|
|
categoryId: Joi.number().integer().positive().allow(null).optional(),
|
|
productType: Joi.string().valid('granular', 'liquid', 'seed', 'powder').allow(null).optional(),
|
|
activeIngredients: Joi.string().allow(null).optional(),
|
|
description: Joi.string().allow(null).optional(),
|
|
isAdvancedEdit: Joi.boolean().optional()
|
|
});
|
|
|
|
// Application validation schemas
|
|
const applicationPlanSchema = Joi.object({
|
|
lawnSectionId: Joi.number().integer().positive().required(),
|
|
equipmentId: Joi.number().integer().positive().required(),
|
|
plannedDate: Joi.date().required(),
|
|
notes: Joi.string().allow('').optional(),
|
|
products: Joi.array().items(Joi.object({
|
|
productId: Joi.number().integer().positive().optional(),
|
|
userProductId: Joi.number().integer().positive().optional(),
|
|
rateAmount: Joi.number().positive().required(),
|
|
rateUnit: Joi.string().max(50).required()
|
|
})).min(1).required()
|
|
});
|
|
|
|
const applicationLogSchema = Joi.object({
|
|
planId: Joi.number().integer().positive(),
|
|
lawnSectionId: Joi.number().integer().positive().required(),
|
|
equipmentId: Joi.number().integer().positive().required(),
|
|
weatherConditions: Joi.object(),
|
|
gpsTrack: Joi.object(),
|
|
averageSpeed: Joi.number().positive(),
|
|
areaCovered: Joi.number().positive(),
|
|
notes: Joi.string(),
|
|
products: Joi.array().items(Joi.object({
|
|
productId: Joi.number().integer().positive(),
|
|
userProductId: Joi.number().integer().positive(),
|
|
rateAmount: Joi.number().positive().required(),
|
|
rateUnit: Joi.string().max(50).required(),
|
|
actualProductAmount: Joi.number().positive(),
|
|
actualWaterAmount: Joi.number().positive(),
|
|
actualSpeedMph: Joi.number().positive()
|
|
})).min(1).required()
|
|
});
|
|
|
|
// Validation middleware
|
|
const validateRequest = (schema) => {
|
|
return (req, res, next) => {
|
|
const { error } = schema.validate(req.body, { abortEarly: false });
|
|
if (error) {
|
|
return next(error);
|
|
}
|
|
next();
|
|
};
|
|
};
|
|
|
|
const validateParams = (schema) => {
|
|
return (req, res, next) => {
|
|
const { error } = schema.validate(req.params, { abortEarly: false });
|
|
if (error) {
|
|
return next(error);
|
|
}
|
|
next();
|
|
};
|
|
};
|
|
|
|
const idParamSchema = Joi.object({
|
|
id: Joi.number().integer().positive().required()
|
|
});
|
|
|
|
module.exports = {
|
|
// Schemas
|
|
registerSchema,
|
|
loginSchema,
|
|
updateUserSchema,
|
|
changePasswordSchema,
|
|
propertySchema,
|
|
lawnSectionSchema,
|
|
equipmentSchema,
|
|
productSchema,
|
|
productRateSchema,
|
|
userProductSchema,
|
|
applicationPlanSchema,
|
|
applicationLogSchema,
|
|
idParamSchema,
|
|
|
|
// Middleware
|
|
validateRequest,
|
|
validateParams
|
|
}; |