import { auth } from './auth.js'; const SESSION_TIMEOUT_MS = Number(import.meta.env.VITE_SESSION_TIMEOUT_MS || 15 * 60 * 1000); const SESSION_WARNING_MS = Number(import.meta.env.VITE_SESSION_WARNING_MS || 60 * 1000); let inactivityTimer = null; let warningTimer = null; let warningCountdownTimer = null; let warningEndsAt = null; let warningOverlay = null; let hasInitialized = false; let fetchIsPatched = false; function isAuthenticated() { return Boolean(localStorage.getItem('token')); } function clearTimers() { if (inactivityTimer) { clearTimeout(inactivityTimer); inactivityTimer = null; } if (warningTimer) { clearTimeout(warningTimer); warningTimer = null; } if (warningCountdownTimer) { clearInterval(warningCountdownTimer); warningCountdownTimer = null; } } function removeWarningOverlay() { if (warningOverlay) { warningOverlay.remove(); warningOverlay = null; } warningEndsAt = null; if (warningCountdownTimer) { clearInterval(warningCountdownTimer); warningCountdownTimer = null; } } function getRemainingSeconds() { if (!warningEndsAt) return 0; return Math.max(0, Math.ceil((warningEndsAt - Date.now()) / 1000)); } function forceSessionExpiration(message = 'Tu sesion ha expirado. Inicia sesion de nuevo.') { removeWarningOverlay(); clearTimers(); auth.logout(); sessionStorage.setItem('session-expired-message', message); window.location.hash = '#login'; } function showWarningOverlay() { if (!isAuthenticated()) return; removeWarningOverlay(); const overlay = document.createElement('div'); overlay.className = 'session-warning-overlay'; overlay.innerHTML = ` `; const continueBtn = overlay.querySelector('.session-warning-continue'); const logoutBtn = overlay.querySelector('.session-warning-logout'); const countdownEl = overlay.querySelector('#session-warning-countdown'); continueBtn?.addEventListener('click', () => { removeWarningOverlay(); resetSessionTimers(); }); logoutBtn?.addEventListener('click', () => { forceSessionExpiration('Sesion finalizada por inactividad.'); }); warningEndsAt = Date.now() + SESSION_WARNING_MS; const updateCountdown = () => { const seconds = getRemainingSeconds(); const mins = String(Math.floor(seconds / 60)).padStart(2, '0'); const secs = String(seconds % 60).padStart(2, '0'); if (countdownEl) { countdownEl.textContent = `${mins}:${secs}`; } if (seconds <= 0) { forceSessionExpiration(); } }; updateCountdown(); warningCountdownTimer = setInterval(updateCountdown, 1000); warningOverlay = overlay; document.body.appendChild(overlay); } function resetSessionTimers() { clearTimers(); if (!isAuthenticated()) { removeWarningOverlay(); return; } const warningDelay = Math.max(SESSION_TIMEOUT_MS - SESSION_WARNING_MS, 1000); warningTimer = setTimeout(showWarningOverlay, warningDelay); inactivityTimer = setTimeout(() => forceSessionExpiration(), SESSION_TIMEOUT_MS); } function patchFetchFor401AndActivity() { if (fetchIsPatched) return; const originalFetch = window.fetch.bind(window); window.fetch = async (...args) => { const requestUrl = typeof args[0] === 'string' ? args[0] : (args[0]?.url || ''); const isLoginEndpoint = requestUrl.includes('/api/Auth/login'); if (!isLoginEndpoint && isAuthenticated()) { resetSessionTimers(); } const response = await originalFetch(...args); if (response.status === 401 && !isLoginEndpoint && isAuthenticated()) { forceSessionExpiration('Tu sesion ha expirado o ya no es valida. Inicia sesion de nuevo.'); } return response; }; fetchIsPatched = true; } function bindActivityListeners() { const events = ['mousemove', 'mousedown', 'keydown', 'touchstart', 'scroll']; const onActivity = () => { if (isAuthenticated()) { resetSessionTimers(); } }; events.forEach(eventName => { window.addEventListener(eventName, onActivity, { passive: true }); }); } function bindAuthListeners() { window.addEventListener('auth:login', () => { removeWarningOverlay(); resetSessionTimers(); }); window.addEventListener('auth:logout', () => { removeWarningOverlay(); clearTimers(); }); } export function initSessionManager() { if (hasInitialized) return; patchFetchFor401AndActivity(); bindActivityListeners(); bindAuthListeners(); resetSessionTimers(); hasInitialized = true; }