fixes: favoritos y mas cosas

Signed-off-by: Omar Sánchez Pizarro <omar.sanchez@pistacero.net>
This commit is contained in:
Omar Sánchez Pizarro
2026-01-20 20:06:28 +01:00
parent d5f0ba4e03
commit 05f0455744
20 changed files with 1691 additions and 350 deletions

View File

@@ -0,0 +1,165 @@
import crypto from 'crypto';
/**
* Genera un fingerprint del dispositivo basado en headers HTTP
* @param {Object} req - Request object de Express
* @returns {Object} Objeto con fingerprint hash y metadata del dispositivo
*/
export function generateDeviceFingerprint(req) {
// Extraer información del dispositivo desde headers
const userAgent = req.headers['user-agent'] || '';
const acceptLanguage = req.headers['accept-language'] || '';
const acceptEncoding = req.headers['accept-encoding'] || '';
const accept = req.headers['accept'] || '';
const connection = req.headers['connection'] || '';
const upgradeInsecureRequests = req.headers['upgrade-insecure-requests'] || '';
// IP del cliente (considerando proxies)
const ip = req.ip ||
req.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
req.headers['x-real-ip'] ||
req.connection?.remoteAddress ||
'unknown';
// Crear string combinado para el hash
const fingerprintString = [
userAgent,
acceptLanguage,
acceptEncoding,
accept,
connection,
upgradeInsecureRequests,
ip
].join('|');
// Generar hash SHA-256
const fingerprintHash = crypto
.createHash('sha256')
.update(fingerprintString)
.digest('hex');
// Extraer información legible del User-Agent
const deviceInfo = parseUserAgent(userAgent);
return {
fingerprint: fingerprintHash,
deviceInfo: {
...deviceInfo,
ip: ip,
userAgent: userAgent.substring(0, 200), // Limitar longitud
}
};
}
/**
* Parsea el User-Agent para extraer información legible
* @param {string} userAgent - User-Agent string
* @returns {Object} Información del dispositivo
*/
function parseUserAgent(userAgent) {
if (!userAgent) {
return {
browser: 'Unknown',
browserVersion: '',
os: 'Unknown',
osVersion: '',
device: 'Unknown',
};
}
const ua = userAgent.toLowerCase();
// Detectar navegador
let browser = 'Unknown';
let browserVersion = '';
if (ua.includes('chrome') && !ua.includes('edg') && !ua.includes('opr')) {
browser = 'Chrome';
const match = ua.match(/chrome\/([\d.]+)/);
browserVersion = match ? match[1] : '';
} else if (ua.includes('firefox')) {
browser = 'Firefox';
const match = ua.match(/firefox\/([\d.]+)/);
browserVersion = match ? match[1] : '';
} else if (ua.includes('safari') && !ua.includes('chrome')) {
browser = 'Safari';
const match = ua.match(/version\/([\d.]+)/);
browserVersion = match ? match[1] : '';
} else if (ua.includes('edg')) {
browser = 'Edge';
const match = ua.match(/edg\/([\d.]+)/);
browserVersion = match ? match[1] : '';
} else if (ua.includes('opr')) {
browser = 'Opera';
const match = ua.match(/opr\/([\d.]+)/);
browserVersion = match ? match[1] : '';
}
// Detectar sistema operativo
let os = 'Unknown';
let osVersion = '';
if (ua.includes('windows')) {
os = 'Windows';
if (ua.includes('windows nt 10')) osVersion = '10/11';
else if (ua.includes('windows nt 6.3')) osVersion = '8.1';
else if (ua.includes('windows nt 6.2')) osVersion = '8';
else if (ua.includes('windows nt 6.1')) osVersion = '7';
} else if (ua.includes('mac os x') || ua.includes('macintosh')) {
os = 'macOS';
const match = ua.match(/mac os x ([\d_]+)/);
osVersion = match ? match[1].replace(/_/g, '.') : '';
} else if (ua.includes('linux')) {
os = 'Linux';
} else if (ua.includes('android')) {
os = 'Android';
const match = ua.match(/android ([\d.]+)/);
osVersion = match ? match[1] : '';
} else if (ua.includes('ios') || ua.includes('iphone') || ua.includes('ipad')) {
os = 'iOS';
const match = ua.match(/os ([\d_]+)/);
osVersion = match ? match[1].replace(/_/g, '.') : '';
}
// Detectar tipo de dispositivo
let device = 'Desktop';
if (ua.includes('mobile') || ua.includes('android') || ua.includes('iphone')) {
device = 'Mobile';
} else if (ua.includes('tablet') || ua.includes('ipad')) {
device = 'Tablet';
}
return {
browser,
browserVersion,
os,
osVersion,
device,
};
}
/**
* Genera un fingerprint desde el frontend (cuando se envía desde el cliente)
* @param {string} fingerprintHash - Hash del fingerprint generado en el cliente
* @param {Object} deviceInfo - Información del dispositivo del cliente
* @param {Object} req - Request object de Express
* @returns {Object} Objeto con fingerprint y metadata combinada
*/
export function combineFingerprint(fingerprintHash, deviceInfo, req) {
const serverFingerprint = generateDeviceFingerprint(req);
// Si el cliente envió un fingerprint, combinarlo con el del servidor
// Esto permite usar librerías avanzadas del cliente pero validar con servidor
const combinedFingerprint = fingerprintHash
? crypto.createHash('sha256').update(fingerprintHash + serverFingerprint.fingerprint).digest('hex')
: serverFingerprint.fingerprint;
return {
fingerprint: combinedFingerprint,
deviceInfo: {
...serverFingerprint.deviceInfo,
...deviceInfo, // El del cliente tiene prioridad si existe
}
};
}