/** * Simple Session Validator * Menangani validasi session tanpa periodic checking * Hanya respond pada 401 errors dari API requests */ class SimpleSessionValidator { constructor() { this.isLoggingOut = false; this.consecutiveErrors = 0; this.maxConsecutiveErrors = 2; this.init(); } init() { console.log("Simple Session Validator initialized"); // Intercept all AJAX requests untuk detect 401 this.interceptAjaxRequests(); // Listen untuk page visibility changes document.addEventListener("visibilitychange", () => { if (!document.hidden && this.consecutiveErrors > 0) { // Reset errors ketika user kembali ke tab this.consecutiveErrors = 0; console.log("Page visible, reset error counter"); } }); } interceptAjaxRequests() { const validator = this; // Intercept fetch requests const originalFetch = window.fetch; window.fetch = async function (...args) { try { const response = await originalFetch(...args); // Check if response is 401 dan URL mengandung /api/ if (response.status === 401) { const url = args[0]; if (typeof url === "string" && url.includes("/api/")) { console.log("401 detected in API fetch request:", url); validator.handleApiError401(url); } } return response; } catch (error) { console.error("Fetch request failed:", error); throw error; } }; // Intercept XMLHttpRequest const originalXHRSend = XMLHttpRequest.prototype.send; const originalXHROpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function (...args) { this._url = args[1]; return originalXHROpen.apply(this, args); }; XMLHttpRequest.prototype.send = function (...args) { const xhr = this; const originalOnReadyStateChange = xhr.onreadystatechange; xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 401) { if (xhr._url && xhr._url.includes("/api/")) { console.log( "401 detected in API XHR request:", xhr._url ); validator.handleApiError401(xhr._url); } } if (originalOnReadyStateChange) { originalOnReadyStateChange.apply(xhr, arguments); } }; return originalXHRSend.apply(this, args); }; // Intercept jQuery AJAX jika tersedia if (typeof $ !== "undefined" && $.ajaxSetup) { $(document).ajaxError(function (event, xhr, settings, thrownError) { if ( xhr.status === 401 && settings.url && settings.url.includes("/api/") ) { console.log( "401 detected in jQuery AJAX request:", settings.url ); validator.handleApiError401(settings.url); } }); } } handleApiError401(url) { if (this.isLoggingOut) { return; } console.log(`API 401 Error detected on ${url}`); // Increment consecutive errors this.consecutiveErrors++; // Jika sudah 2x error berturut-turut, logout if (this.consecutiveErrors >= this.maxConsecutiveErrors) { this.handleSessionInvalid( "Token API tidak valid. User lain telah login." ); } } handleSessionInvalid(message) { if (this.isLoggingOut) { return; } this.isLoggingOut = true; console.log("Handling session invalid:", message); // Show notification this.showNotification(message, "warning"); // Redirect to login after 3 seconds setTimeout(() => { this.forceLogout(); }, 3000); } showNotification(message, type = "info") { // Try different notification libraries if (typeof toastr !== "undefined") { toastr[type](message); } else if (typeof Swal !== "undefined") { Swal.fire({ title: "Peringatan Session", text: message, icon: type, confirmButtonText: "OK", allowOutsideClick: false, timer: 5000, timerProgressBar: true, }); } else { // Create custom notification this.createCustomNotification(message, type); } } createCustomNotification(message, type) { const notification = document.createElement("div"); notification.style.cssText = ` position: fixed; top: 20px; right: 20px; background: ${type === "warning" ? "#ffc107" : "#007bff"}; color: white; padding: 15px 20px; border-radius: 5px; z-index: 9999; max-width: 400px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); font-family: Arial, sans-serif; font-size: 14px; `; notification.innerHTML = ` Peringatan!
${message}
Anda akan diarahkan ke halaman login... `; document.body.appendChild(notification); // Remove after 8 seconds setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 8000); } forceLogout() { console.log("Forcing logout..."); // Try to logout via API first fetch("/logout", { method: "POST", headers: { "Content-Type": "application/json", "X-CSRF-TOKEN": document .querySelector('meta[name="csrf-token"]') ?.getAttribute("content") || "", "X-Requested-With": "XMLHttpRequest", }, credentials: "include", }) .then(() => { window.location.href = "/login"; }) .catch(() => { // Force redirect even if logout fails window.location.href = "/login"; }); } // Method untuk manual reset reset() { this.consecutiveErrors = 0; this.isLoggingOut = false; console.log("Session validator reset"); } } // Initialize when DOM is ready document.addEventListener("DOMContentLoaded", () => { window.simpleSessionValidator = new SimpleSessionValidator(); }); // Export for module usage if (typeof module !== "undefined" && module.exports) { module.exports = SimpleSessionValidator; }