import FingerprintJS from '@fingerprintjs/fingerprintjs'; let fpPromise = null; let cachedFingerprint = null; let cachedDeviceInfo = null; /** * Inicializa FingerprintJS (solo una vez) */ function initFingerprintJS() { if (!fpPromise) { fpPromise = FingerprintJS.load(); } return fpPromise; } /** * Obtiene el fingerprint del dispositivo * @returns {Promise<{fingerprint: string, deviceInfo: Object}>} */ export async function getDeviceFingerprint() { // Si ya tenemos el fingerprint en caché, devolverlo if (cachedFingerprint && cachedDeviceInfo) { return { fingerprint: cachedFingerprint, deviceInfo: cachedDeviceInfo, }; } try { const fp = await initFingerprintJS(); const result = await fp.get(); // Extraer información del dispositivo desde los componentes const deviceInfo = extractDeviceInfo(result.components); cachedFingerprint = result.visitorId; cachedDeviceInfo = deviceInfo; return { fingerprint: result.visitorId, deviceInfo: deviceInfo, }; } catch (error) { console.error('Error obteniendo fingerprint:', error); // Fallback: generar un fingerprint básico return { fingerprint: generateFallbackFingerprint(), deviceInfo: { browser: navigator.userAgent.includes('Chrome') ? 'Chrome' : navigator.userAgent.includes('Firefox') ? 'Firefox' : navigator.userAgent.includes('Safari') ? 'Safari' : 'Unknown', os: navigator.platform, device: 'Unknown', }, }; } } /** * Extrae información legible del dispositivo desde los componentes de FingerprintJS * @param {Object} components - Componentes de FingerprintJS * @returns {Object} Información del dispositivo */ function extractDeviceInfo(components) { const info = { browser: 'Unknown', browserVersion: '', os: 'Unknown', osVersion: '', device: 'Unknown', screenResolution: '', timezone: '', language: navigator.language || '', }; // Información del navegador if (components.browserName) { info.browser = components.browserName.value || 'Unknown'; } if (components.browserVersion) { info.browserVersion = components.browserVersion.value || ''; } // Información del sistema operativo if (components.os) { info.os = components.os.value || 'Unknown'; } if (components.osVersion) { info.osVersion = components.osVersion.value || ''; } // Información del dispositivo if (components.deviceMemory) { info.device = components.deviceMemory.value ? 'Desktop' : 'Mobile'; } if (components.platform) { const platform = components.platform.value?.toLowerCase() || ''; if (platform.includes('mobile') || platform.includes('android') || platform.includes('iphone')) { info.device = 'Mobile'; } else if (platform.includes('tablet') || platform.includes('ipad')) { info.device = 'Tablet'; } else { info.device = 'Desktop'; } } // Resolución de pantalla if (components.screenResolution) { const res = components.screenResolution.value; if (res && res.length >= 2) { info.screenResolution = `${res[0]}x${res[1]}`; } } // Zona horaria if (components.timezone) { info.timezone = components.timezone.value || ''; } return info; } /** * Genera un fingerprint básico como fallback * @returns {string} Hash del fingerprint */ function generateFallbackFingerprint() { const data = [ navigator.userAgent, navigator.language, navigator.platform, screen.width + 'x' + screen.height, new Date().getTimezoneOffset(), ].join('|'); // Simple hash (no usar en producción, solo como fallback) let hash = 0; for (let i = 0; i < data.length; i++) { const char = data.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32bit integer } return Math.abs(hash).toString(36); } /** * Limpia el caché del fingerprint (útil para testing) */ export function clearFingerprintCache() { cachedFingerprint = null; cachedDeviceInfo = null; }