bitip/src/backend/middleware/validators.ts

76 lines
1.8 KiB
TypeScript

// 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();
};