265 lines
7.7 KiB
JavaScript
265 lines
7.7 KiB
JavaScript
/**
|
|
* Multi-User Session Handler
|
|
* Menangani kasus ketika multiple user login dan session conflict
|
|
*/
|
|
class MultiUserSessionHandler {
|
|
constructor() {
|
|
this.checkInterval = null;
|
|
this.lastCheckTime = 0;
|
|
this.isChecking = false;
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
console.log("Multi-User Session Handler initialized");
|
|
|
|
// Check session setiap 10 detik
|
|
this.startPeriodicCheck();
|
|
|
|
// Check session ketika tab menjadi aktif
|
|
document.addEventListener("visibilitychange", () => {
|
|
if (!document.hidden) {
|
|
this.checkSession();
|
|
}
|
|
});
|
|
|
|
// Check session ketika window focus
|
|
window.addEventListener("focus", () => {
|
|
this.checkSession();
|
|
});
|
|
|
|
// Check session ketika user melakukan interaksi
|
|
document.addEventListener("click", () => {
|
|
this.checkSession();
|
|
});
|
|
|
|
// Check session ketika ada AJAX request
|
|
this.interceptAjaxRequests();
|
|
}
|
|
|
|
startPeriodicCheck() {
|
|
this.checkInterval = setInterval(() => {
|
|
this.checkSession();
|
|
}, 10000); // 10 detik
|
|
}
|
|
|
|
stopPeriodicCheck() {
|
|
if (this.checkInterval) {
|
|
clearInterval(this.checkInterval);
|
|
this.checkInterval = null;
|
|
}
|
|
}
|
|
|
|
async checkSession() {
|
|
// Prevent multiple simultaneous checks
|
|
if (this.isChecking) {
|
|
return;
|
|
}
|
|
|
|
// Prevent checking too frequently
|
|
const now = Date.now();
|
|
if (now - this.lastCheckTime < 5000) {
|
|
// 5 detik minimum interval
|
|
return;
|
|
}
|
|
|
|
this.isChecking = true;
|
|
this.lastCheckTime = now;
|
|
|
|
try {
|
|
const response = await fetch("/api/check-session", {
|
|
method: "GET",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"X-Requested-With": "XMLHttpRequest",
|
|
"Cache-Control": "no-cache",
|
|
},
|
|
credentials: "include",
|
|
});
|
|
|
|
if (response.status === 401) {
|
|
console.log("Session invalid detected, logging out...");
|
|
this.handleSessionInvalid();
|
|
} else if (response.status === 200) {
|
|
const data = await response.json();
|
|
if (!data.valid) {
|
|
console.log("Session validation failed, logging out...");
|
|
this.handleSessionInvalid();
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("Session check failed:", error);
|
|
} finally {
|
|
this.isChecking = false;
|
|
}
|
|
}
|
|
|
|
interceptAjaxRequests() {
|
|
// Intercept fetch requests
|
|
const originalFetch = window.fetch;
|
|
window.fetch = async (...args) => {
|
|
try {
|
|
const response = await originalFetch(...args);
|
|
|
|
// Check if response is 401
|
|
if (response.status === 401) {
|
|
console.log(
|
|
"401 detected in fetch request, checking session..."
|
|
);
|
|
this.checkSession();
|
|
}
|
|
|
|
return response;
|
|
} catch (error) {
|
|
console.error("Fetch request failed:", error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
// Intercept XMLHttpRequest
|
|
const originalXHROpen = XMLHttpRequest.prototype.open;
|
|
const originalXHRSend = XMLHttpRequest.prototype.send;
|
|
|
|
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) {
|
|
console.log(
|
|
"401 detected in XHR request, checking session..."
|
|
);
|
|
window.multiUserSessionHandler.checkSession();
|
|
}
|
|
|
|
if (originalOnReadyStateChange) {
|
|
originalOnReadyStateChange.apply(xhr, arguments);
|
|
}
|
|
};
|
|
|
|
return originalXHRSend.apply(this, args);
|
|
};
|
|
}
|
|
|
|
handleSessionInvalid() {
|
|
this.stopPeriodicCheck();
|
|
|
|
// Show notification
|
|
this.showNotification(
|
|
"Session Anda telah berakhir karena user lain login. Silakan login ulang.",
|
|
"warning"
|
|
);
|
|
|
|
// Clear any stored data
|
|
this.clearStoredData();
|
|
|
|
// Redirect to login after 2 seconds
|
|
setTimeout(() => {
|
|
window.location.href = "/login";
|
|
}, 2000);
|
|
}
|
|
|
|
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,
|
|
});
|
|
} else if (typeof alert !== "undefined") {
|
|
alert(message);
|
|
} 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);
|
|
`;
|
|
notification.textContent = message;
|
|
|
|
document.body.appendChild(notification);
|
|
|
|
// Remove after 5 seconds
|
|
setTimeout(() => {
|
|
if (notification.parentNode) {
|
|
notification.parentNode.removeChild(notification);
|
|
}
|
|
}, 5000);
|
|
}
|
|
|
|
clearStoredData() {
|
|
// Clear localStorage
|
|
try {
|
|
localStorage.clear();
|
|
} catch (e) {
|
|
console.warn("Could not clear localStorage:", e);
|
|
}
|
|
|
|
// Clear sessionStorage
|
|
try {
|
|
sessionStorage.clear();
|
|
} catch (e) {
|
|
console.warn("Could not clear sessionStorage:", e);
|
|
}
|
|
}
|
|
|
|
// Method untuk manual logout
|
|
logout() {
|
|
this.stopPeriodicCheck();
|
|
|
|
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(() => {
|
|
this.clearStoredData();
|
|
window.location.href = "/login";
|
|
})
|
|
.catch(() => {
|
|
this.clearStoredData();
|
|
window.location.href = "/login";
|
|
});
|
|
}
|
|
}
|
|
|
|
// Initialize when DOM is ready
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
window.multiUserSessionHandler = new MultiUserSessionHandler();
|
|
});
|
|
|
|
// Export for module usage
|
|
if (typeof module !== "undefined" && module.exports) {
|
|
module.exports = MultiUserSessionHandler;
|
|
}
|