import { exec } from './db.mjs'; import { config } from './config.mjs'; /** * Registra una acción en la bitácora. Nunca lanza: si el registro falla, * la petición del usuario no se cae por eso. */ export async function registrar(req, { accion, entidad = '', entidad_id = '', resultado = 'ok', detalle = '' }) { const u = req.usuario ?? null; try { await exec( `INSERT INTO bitacora (usuario_id, actor, rol, accion, entidad, entidad_id, resultado, detalle, ip_origen) VALUES (?,?,?,?,?,?,?,?,?)`, [ u?.id ?? null, u?.nombre ?? 'anónimo', u?.rol_clave ?? '', accion, String(entidad).slice(0, 48), String(entidad_id).slice(0, 48), resultado, String(detalle).slice(0, 500), ip(req), ], ); } catch (err) { console.error('[bitacora] no se pudo registrar:', err.message); } } /** * Origen de la petición. X-Forwarded-For lo pone quien llama, así que solo se * cree cuando se declara explícitamente que hay un proxy de confianza delante * (CONFIAR_PROXY=true). Si no, la bitácora registraría la IP que el cliente * quiera y dejaría de servir para auditar. */ export function ip(req) { let cruda = req.socket?.remoteAddress || ''; if (config.confiarProxy) { const xff = req.headers['x-forwarded-for']; const primera = (Array.isArray(xff) ? xff[0] : xff)?.split(',')[0]?.trim(); if (primera) cruda = primera; } return String(cruda).replace('::ffff:', '').slice(0, 64); }