/** * 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' }; }