Files
SerpentRace/SerpentRace_Backend/dist/Application/Services/ValidationMiddleware.js
T

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