// Copyright (c) 2025 Tudor Stanciu import { Request, Response, NextFunction } from 'express'; import Joi from 'joi'; import geoIPService from '../services/geoip'; import config from '../services/config'; import { BadRequestError } from '../utils/errors'; // Validation schemas const ipSchema = Joi.string() .ip({ version: ['ipv4', 'ipv6'] }) .required(); const batchSchema = Joi.object({ ips: Joi.array() .items(Joi.string().ip({ version: ['ipv4', 'ipv6'] })) .min(1) .max(config.batchLimit) .required(), }); /** * Validates that IP query parameter exists and is valid * Adds validated IP to res.locals.validatedIp for handler use */ export const validateIpQuery = ( req: Request, res: Response, next: NextFunction ): void => { const ip = req.query.ip as string; if (!ip) { throw new BadRequestError('IP address is required'); } const { error } = ipSchema.validate(ip); if (error) { throw new BadRequestError('Invalid IP address format', { ip }); } if (!geoIPService.isValidIP(ip)) { throw new BadRequestError('Invalid IP address', { ip }); } if (geoIPService.isPrivateIP(ip)) { throw new BadRequestError('Private IP addresses are not supported', { ip }); } // Store validated IP for handler use res.locals.validatedIp = ip; next(); }; /** * Validates batch request body * Adds validated IPs to req.body.validatedIps for handler use */ export const validateBatchRequest = ( req: Request, _res: Response, next: NextFunction ): void => { const { error, value } = batchSchema.validate(req.body); if (error) { throw new BadRequestError( error.details[0]?.message || 'Invalid request body' ); } // Store validated IPs for handler use req.body.validatedIps = value.ips; next(); };