Files
api-ping/src/lib/proxy-utils.js
2025-08-16 23:59:05 +02:00

130 líneas
3.6 KiB
JavaScript

/**
* Extrae la IP real del cliente considerando proxies como nginx
* @param {Request} request - La request de Next.js
* @returns {string} La IP del cliente
*/
export function getClientIP(request) {
// Headers que pueden contener la IP real del cliente
const forwardedFor = request.headers.get('x-forwarded-for');
const realIP = request.headers.get('x-real-ip');
const cfConnectingIP = request.headers.get('cf-connecting-ip'); // Cloudflare
const trueClientIP = request.headers.get('true-client-ip'); // Cloudflare Enterprise
const xClientIP = request.headers.get('x-client-ip');
const xForwardedHost = request.headers.get('x-forwarded-host');
// Prioridad de headers (más confiables primero)
let clientIP = null;
// 1. Cloudflare headers (muy confiables)
if (trueClientIP) {
clientIP = trueClientIP.trim();
} else if (cfConnectingIP) {
clientIP = cfConnectingIP.trim();
}
// 2. X-Real-IP (nginx real_ip_module)
else if (realIP) {
clientIP = realIP.trim();
}
// 3. X-Forwarded-For (puede contener múltiples IPs)
else if (forwardedFor) {
// X-Forwarded-For puede contener múltiples IPs separadas por comas
// La primera es la IP original del cliente
const ips = forwardedFor.split(',').map(ip => ip.trim());
clientIP = ips[0];
}
// 4. Otros headers menos comunes
else if (xClientIP) {
clientIP = xClientIP.trim();
}
// Validar que la IP obtenida sea válida
if (clientIP && isValidIP(clientIP)) {
return clientIP;
}
// Fallback: usar una IP por defecto
return '127.0.0.1';
}
/**
* Valida si una cadena es una IP válida (IPv4 o IPv6)
* @param {string} ip - La IP a validar
* @returns {boolean} True si es válida
*/
function isValidIP(ip) {
// Regex básico para IPv4
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
// Regex básico para IPv6
const ipv6Regex = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^::1$|^::$/;
if (ipv4Regex.test(ip)) {
// Validar rangos de IPv4 (0-255)
const parts = ip.split('.');
return parts.every(part => {
const num = parseInt(part, 10);
return num >= 0 && num <= 255;
});
}
if (ipv6Regex.test(ip) || ip === '::1' || ip.startsWith('::ffff:')) {
return true;
}
return false;
}
/**
* Obtiene información detallada del proxy y cliente
* @param {Request} request - La request de Next.js
* @returns {object} Información del cliente y proxy
*/
export function getProxyInfo(request) {
const headers = {};
// Recopilar todos los headers relevantes para debugging
const proxyHeaders = [
'x-forwarded-for',
'x-real-ip',
'x-forwarded-proto',
'x-forwarded-host',
'x-forwarded-port',
'cf-connecting-ip',
'true-client-ip',
'x-client-ip',
'x-cluster-client-ip',
'forwarded'
];
proxyHeaders.forEach(header => {
const value = request.headers.get(header);
if (value) {
headers[header] = value;
}
});
const clientIP = getClientIP(request);
return {
clientIP,
proxyHeaders: headers,
isProxied: Object.keys(headers).length > 0,
userAgent: request.headers.get('user-agent') || 'unknown'
};
}
/**
* Middleware helper para configurar trust proxy en desarrollo
* @param {Request} request
* @returns {object} Configuración de trust
*/
export function getTrustProxyConfig(request) {
const isProduction = process.env.NODE_ENV === 'production';
const trustProxy = process.env.TRUST_PROXY === 'true';
return {
trustProxy: isProduction || trustProxy,
clientIP: getClientIP(request),
environment: process.env.NODE_ENV || 'development'
};
}