268 lines
11 KiB
JavaScript
268 lines
11 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.ValidationMiddleware = void 0;
|
|
const ErrorResponseService_1 = require("./ErrorResponseService");
|
|
const Logger_1 = require("./Logger");
|
|
/**
|
|
* Common validation middleware functions for request validation
|
|
*/
|
|
class ValidationMiddleware {
|
|
/**
|
|
* Validates required fields in request body
|
|
* @param requiredFields Array of required field names
|
|
*/
|
|
static validateRequiredFields(requiredFields) {
|
|
return (req, res, next) => {
|
|
const missingFields = [];
|
|
for (const field of requiredFields) {
|
|
if (!req.body || req.body[field] === undefined || req.body[field] === null || req.body[field] === '') {
|
|
missingFields.push(field);
|
|
}
|
|
}
|
|
if (missingFields.length > 0) {
|
|
(0, Logger_1.logWarning)('Validation failed - missing required fields', {
|
|
missingFields,
|
|
endpoint: req.path
|
|
}, req, res);
|
|
return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Missing required fields', { missingFields });
|
|
}
|
|
next();
|
|
};
|
|
}
|
|
/**
|
|
* Validates field types in request body
|
|
* @param fieldTypes Object mapping field names to expected types
|
|
*/
|
|
static validateFieldTypes(fieldTypes) {
|
|
return (req, res, next) => {
|
|
const typeErrors = [];
|
|
for (const [field, expectedType] of Object.entries(fieldTypes)) {
|
|
if (req.body && req.body[field] !== undefined) {
|
|
const actualType = Array.isArray(req.body[field]) ? 'array' : typeof req.body[field];
|
|
if (actualType !== expectedType) {
|
|
typeErrors.push(`Field '${field}' should be ${expectedType}, got ${actualType}`);
|
|
}
|
|
}
|
|
}
|
|
if (typeErrors.length > 0) {
|
|
(0, Logger_1.logWarning)('Validation failed - invalid field types', {
|
|
typeErrors,
|
|
endpoint: req.path
|
|
}, req, res);
|
|
return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Invalid field types', { errors: typeErrors });
|
|
}
|
|
next();
|
|
};
|
|
}
|
|
/**
|
|
* Validates string field length constraints
|
|
* @param constraints Object mapping field names to min/max length
|
|
*/
|
|
static validateStringLength(constraints) {
|
|
return (req, res, next) => {
|
|
const lengthErrors = [];
|
|
for (const [field, constraint] of Object.entries(constraints)) {
|
|
if (req.body && typeof req.body[field] === 'string') {
|
|
const value = req.body[field];
|
|
if (constraint.min !== undefined && value.length < constraint.min) {
|
|
lengthErrors.push(`Field '${field}' must be at least ${constraint.min} characters`);
|
|
}
|
|
if (constraint.max !== undefined && value.length > constraint.max) {
|
|
lengthErrors.push(`Field '${field}' must not exceed ${constraint.max} characters`);
|
|
}
|
|
}
|
|
}
|
|
if (lengthErrors.length > 0) {
|
|
(0, Logger_1.logWarning)('Validation failed - string length constraints', {
|
|
lengthErrors,
|
|
endpoint: req.path
|
|
}, req, res);
|
|
return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'String length validation failed', { errors: lengthErrors });
|
|
}
|
|
next();
|
|
};
|
|
}
|
|
/**
|
|
* Validates email format
|
|
* @param emailFields Array of field names that should contain valid emails
|
|
*/
|
|
static validateEmailFormat(emailFields) {
|
|
return (req, res, next) => {
|
|
const emailErrors = [];
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
for (const field of emailFields) {
|
|
if (req.body && req.body[field] && typeof req.body[field] === 'string') {
|
|
if (!emailRegex.test(req.body[field])) {
|
|
emailErrors.push(`Field '${field}' must contain a valid email address`);
|
|
}
|
|
}
|
|
}
|
|
if (emailErrors.length > 0) {
|
|
(0, Logger_1.logWarning)('Validation failed - invalid email format', {
|
|
emailErrors,
|
|
endpoint: req.path
|
|
}, req, res);
|
|
return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Email format validation failed', { errors: emailErrors });
|
|
}
|
|
next();
|
|
};
|
|
}
|
|
/**
|
|
* Validates UUIDs format
|
|
* @param uuidFields Array of field names that should contain valid UUIDs
|
|
*/
|
|
static validateUUIDFormat(uuidFields) {
|
|
return (req, res, next) => {
|
|
const uuidErrors = [];
|
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
for (const field of uuidFields) {
|
|
const value = field.includes('.')
|
|
? this.getNestedValue(req, field)
|
|
: req.body?.[field] || req.params?.[field] || req.query?.[field];
|
|
if (value && typeof value === 'string') {
|
|
if (!uuidRegex.test(value)) {
|
|
uuidErrors.push(`Field '${field}' must contain a valid UUID`);
|
|
}
|
|
}
|
|
}
|
|
if (uuidErrors.length > 0) {
|
|
(0, Logger_1.logWarning)('Validation failed - invalid UUID format', {
|
|
uuidErrors,
|
|
endpoint: req.path
|
|
}, req, res);
|
|
return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'UUID format validation failed', { errors: uuidErrors });
|
|
}
|
|
next();
|
|
};
|
|
}
|
|
/**
|
|
* Validates numeric constraints
|
|
* @param constraints Object mapping field names to min/max values
|
|
*/
|
|
static validateNumericConstraints(constraints) {
|
|
return (req, res, next) => {
|
|
const numericErrors = [];
|
|
for (const [field, constraint] of Object.entries(constraints)) {
|
|
if (req.body && typeof req.body[field] === 'number') {
|
|
const value = req.body[field];
|
|
if (constraint.min !== undefined && value < constraint.min) {
|
|
numericErrors.push(`Field '${field}' must be at least ${constraint.min}`);
|
|
}
|
|
if (constraint.max !== undefined && value > constraint.max) {
|
|
numericErrors.push(`Field '${field}' must not exceed ${constraint.max}`);
|
|
}
|
|
}
|
|
}
|
|
if (numericErrors.length > 0) {
|
|
(0, Logger_1.logWarning)('Validation failed - numeric constraints', {
|
|
numericErrors,
|
|
endpoint: req.path
|
|
}, req, res);
|
|
return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Numeric validation failed', { errors: numericErrors });
|
|
}
|
|
next();
|
|
};
|
|
}
|
|
/**
|
|
* Validates that arrays are not empty
|
|
* @param arrayFields Array of field names that should contain non-empty arrays
|
|
*/
|
|
static validateNonEmptyArrays(arrayFields) {
|
|
return (req, res, next) => {
|
|
const arrayErrors = [];
|
|
for (const field of arrayFields) {
|
|
if (req.body && Array.isArray(req.body[field])) {
|
|
if (req.body[field].length === 0) {
|
|
arrayErrors.push(`Field '${field}' must not be empty`);
|
|
}
|
|
}
|
|
else if (req.body && req.body[field] !== undefined) {
|
|
arrayErrors.push(`Field '${field}' must be an array`);
|
|
}
|
|
}
|
|
if (arrayErrors.length > 0) {
|
|
(0, Logger_1.logWarning)('Validation failed - empty arrays', {
|
|
arrayErrors,
|
|
endpoint: req.path
|
|
}, req, res);
|
|
return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Array validation failed', { errors: arrayErrors });
|
|
}
|
|
next();
|
|
};
|
|
}
|
|
/**
|
|
* Validates allowed values for enum-like fields
|
|
* @param allowedValues Object mapping field names to arrays of allowed values
|
|
*/
|
|
static validateAllowedValues(allowedValues) {
|
|
return (req, res, next) => {
|
|
const valueErrors = [];
|
|
for (const [field, allowed] of Object.entries(allowedValues)) {
|
|
if (req.body && req.body[field] !== undefined) {
|
|
if (!allowed.includes(req.body[field])) {
|
|
valueErrors.push(`Field '${field}' must be one of: ${allowed.join(', ')}`);
|
|
}
|
|
}
|
|
}
|
|
if (valueErrors.length > 0) {
|
|
(0, Logger_1.logWarning)('Validation failed - disallowed values', {
|
|
valueErrors,
|
|
endpoint: req.path
|
|
}, req, res);
|
|
return ErrorResponseService_1.ErrorResponseService.sendBadRequest(res, 'Value validation failed', { errors: valueErrors });
|
|
}
|
|
next();
|
|
};
|
|
}
|
|
/**
|
|
* Combines multiple validation middlewares
|
|
* @param validations Array of validation middleware functions
|
|
*/
|
|
static combine(validations) {
|
|
return async (req, res, next) => {
|
|
let currentIndex = 0;
|
|
const runNext = (error) => {
|
|
if (error) {
|
|
return next(error);
|
|
}
|
|
if (currentIndex >= validations.length) {
|
|
return next();
|
|
}
|
|
const currentValidation = validations[currentIndex++];
|
|
try {
|
|
currentValidation(req, res, (err) => {
|
|
if (res.headersSent) {
|
|
return; // Response already sent, don't continue
|
|
}
|
|
runNext(err);
|
|
});
|
|
}
|
|
catch (error) {
|
|
(0, Logger_1.logError)('Validation middleware error', error, req, res);
|
|
ErrorResponseService_1.ErrorResponseService.sendInternalServerError(res);
|
|
}
|
|
};
|
|
runNext();
|
|
};
|
|
}
|
|
/**
|
|
* Helper method to get nested values from request
|
|
* @param req Request object
|
|
* @param path Dot-notation path like 'body.user.id'
|
|
*/
|
|
static getNestedValue(req, path) {
|
|
const parts = path.split('.');
|
|
let current = req;
|
|
for (const part of parts) {
|
|
if (current && typeof current === 'object') {
|
|
current = current[part];
|
|
}
|
|
else {
|
|
return undefined;
|
|
}
|
|
}
|
|
return current;
|
|
}
|
|
}
|
|
exports.ValidationMiddleware = ValidationMiddleware;
|
|
//# sourceMappingURL=ValidationMiddleware.js.map
|