Files
SkyArtShop/website/admin/js/auth.js
Local Server e4b3de4a46 Updatweb
2025-12-19 20:44:46 -06:00

363 lines
11 KiB
JavaScript

// Shared Authentication Utility for Admin Panel
// Include this file in all admin pages to handle authentication
// Global authentication state
window.adminAuth = {
user: null,
isAuthenticated: false,
};
// Check authentication and redirect if needed - attach to window
window.checkAuth = async function () {
try {
const response = await fetch("/api/admin/session", {
credentials: "include",
headers: {
Accept: "application/json",
},
});
if (!response.ok) {
window.redirectToLogin();
return false;
}
const data = await response.json();
if (!data.authenticated) {
window.redirectToLogin();
return false;
}
// Store user data
window.adminAuth.user = data.user;
window.adminAuth.isAuthenticated = true;
// Initialize mobile menu after auth check
window.initMobileMenu();
return true;
} catch (error) {
// Only log in development
if (window.location.hostname === "localhost") {
console.error("Authentication check failed:", error);
}
window.redirectToLogin();
return false;
}
};
// Redirect to login page
window.redirectToLogin = function () {
if (window.location.pathname !== "/admin/login.html") {
window.location.href = "/admin/login.html";
}
};
// Initialize mobile menu toggle
window.initMobileMenu = function () {
// Check if mobile menu button exists
let menuToggle = document.getElementById("mobileMenuToggle");
if (!menuToggle && window.innerWidth <= 768) {
// Create mobile menu button
menuToggle = document.createElement("button");
menuToggle.id = "mobileMenuToggle";
menuToggle.className = "mobile-menu-toggle";
menuToggle.setAttribute("aria-label", "Toggle navigation menu");
menuToggle.setAttribute("aria-expanded", "false");
menuToggle.innerHTML = '<i class="bi bi-list"></i>';
document.body.appendChild(menuToggle);
}
if (menuToggle) {
menuToggle.addEventListener("click", function () {
const sidebar = document.querySelector(".sidebar");
if (sidebar) {
const isActive = sidebar.classList.toggle("active");
this.setAttribute("aria-expanded", isActive ? "true" : "false");
this.innerHTML = isActive
? '<i class="bi bi-x"></i>'
: '<i class="bi bi-list"></i>';
}
});
// Close sidebar when clicking outside on mobile
document.addEventListener("click", function (event) {
const sidebar = document.querySelector(".sidebar");
const menuToggle = document.getElementById("mobileMenuToggle");
if (sidebar && menuToggle && window.innerWidth <= 768) {
if (
!sidebar.contains(event.target) &&
event.target !== menuToggle &&
!menuToggle.contains(event.target)
) {
if (sidebar.classList.contains("active")) {
sidebar.classList.remove("active");
menuToggle.setAttribute("aria-expanded", "false");
menuToggle.innerHTML = '<i class="bi bi-list"></i>';
}
}
}
});
// Close menu on link click (mobile)
const sidebarLinks = document.querySelectorAll(".sidebar-menu a");
sidebarLinks.forEach((link) => {
link.addEventListener("click", function () {
if (window.innerWidth <= 768) {
const sidebar = document.querySelector(".sidebar");
if (sidebar && sidebar.classList.contains("active")) {
sidebar.classList.remove("active");
if (menuToggle) {
menuToggle.setAttribute("aria-expanded", "false");
menuToggle.innerHTML = '<i class="bi bi-list"></i>';
}
}
}
});
});
}
// Handle window resize
let resizeTimer;
window.addEventListener("resize", function () {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function () {
if (window.innerWidth > 768) {
const sidebar = document.querySelector(".sidebar");
if (sidebar) {
sidebar.classList.remove("active");
}
if (menuToggle) {
menuToggle.setAttribute("aria-expanded", "false");
menuToggle.innerHTML = '<i class="bi bi-list"></i>';
}
}
}, 250);
});
};
// Custom logout confirmation modal
window.showLogoutConfirm = function (onConfirm) {
// Create modal backdrop
const backdrop = document.createElement("div");
backdrop.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
animation: fadeIn 0.2s ease;
`;
// Create modal
const modal = document.createElement("div");
modal.style.cssText = `
background: white;
border-radius: 12px;
padding: 30px;
max-width: 400px;
width: 90%;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
animation: slideIn 0.3s ease;
`;
modal.innerHTML = `
<style>
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideIn {
from { transform: translateY(-20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
</style>
<div style="text-align: center;">
<div style="font-size: 48px; margin-bottom: 15px;">
<i class="bi bi-box-arrow-right" style="color: #dc3545;"></i>
</div>
<h3 style="margin: 0 0 10px 0; color: #2c3e50; font-weight: 600;">Confirm Logout</h3>
<p style="color: #6c757d; margin: 0 0 25px 0;">Are you sure you want to logout?</p>
<div style="display: flex; gap: 10px; justify-content: center;">
<button id="cancelLogout" style="
padding: 10px 24px;
border: 2px solid #6c757d;
background: white;
color: #6c757d;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: all 0.2s;
">Cancel</button>
<button id="confirmLogout" style="
padding: 10px 24px;
border: none;
background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);
color: white;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: all 0.2s;
box-shadow: 0 2px 8px rgba(220, 53, 69, 0.3);
">Logout</button>
</div>
</div>
`;
backdrop.appendChild(modal);
document.body.appendChild(backdrop);
// Add hover effects
const cancelBtn = modal.querySelector("#cancelLogout");
const confirmBtn = modal.querySelector("#confirmLogout");
cancelBtn.addEventListener("mouseenter", function () {
this.style.background = "#6c757d";
this.style.color = "white";
});
cancelBtn.addEventListener("mouseleave", function () {
this.style.background = "white";
this.style.color = "#6c757d";
});
confirmBtn.addEventListener("mouseenter", function () {
this.style.transform = "translateY(-2px)";
this.style.boxShadow = "0 4px 12px rgba(220, 53, 69, 0.4)";
});
confirmBtn.addEventListener("mouseleave", function () {
this.style.transform = "translateY(0)";
this.style.boxShadow = "0 2px 8px rgba(220, 53, 69, 0.3)";
});
// Handle buttons
const closeModal = () => {
backdrop.style.animation = "fadeIn 0.2s ease reverse";
setTimeout(() => backdrop.remove(), 200);
};
cancelBtn.addEventListener("click", closeModal);
backdrop.addEventListener("click", function (e) {
if (e.target === backdrop) closeModal();
});
confirmBtn.addEventListener("click", function () {
closeModal();
onConfirm();
});
// ESC key to close
const escHandler = (e) => {
if (e.key === "Escape") {
closeModal();
document.removeEventListener("keydown", escHandler);
}
};
document.addEventListener("keydown", escHandler);
};
// Logout function - explicitly attach to window for onclick handlers
window.logout = async function (skipConfirm = false) {
if (!skipConfirm) {
window.showLogoutConfirm(async () => {
await performLogout();
});
return;
}
await performLogout();
};
// CRITICAL: Global function for inline onclick="logout()" handlers
// This must be at global scope so inline onclick can find it
function logout(skipConfirm = false) {
window.logout(skipConfirm);
}
// Actual logout logic
async function performLogout() {
try {
const response = await fetch("/api/admin/logout", {
method: "POST",
credentials: "include",
});
if (response.ok) {
window.adminAuth.user = null;
window.adminAuth.isAuthenticated = false;
window.location.href = "/admin/login.html";
} else {
console.error("Logout failed with status:", response.status);
// Still redirect to login even if logout fails
window.location.href = "/admin/login.html";
}
} catch (error) {
console.error("Logout error:", error);
// Still redirect to login even if logout fails
window.location.href = "/admin/login.html";
}
}
// Show success notification
window.showSuccess = function (message) {
const alert = document.createElement("div");
alert.className =
"alert alert-success alert-dismissible fade show position-fixed";
alert.style.cssText =
"top: 20px; right: 20px; z-index: 9999; min-width: 300px;";
alert.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(alert);
setTimeout(() => alert.remove(), 5000);
};
// Show error notification
window.showError = function (message) {
const alert = document.createElement("div");
alert.className =
"alert alert-danger alert-dismissible fade show position-fixed";
alert.style.cssText =
"top: 20px; right: 20px; z-index: 9999; min-width: 300px;";
alert.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(alert);
setTimeout(() => alert.remove(), 5000);
};
// Auto-check authentication when this script loads
// Only run if we're not on the login page
if (window.location.pathname !== "/admin/login.html") {
document.addEventListener("DOMContentLoaded", function () {
window.checkAuth();
// Attach logout event listeners to all logout buttons
const logoutButtons = document.querySelectorAll(
'.btn-logout, [data-logout], [onclick*="logout"]'
);
logoutButtons.forEach((button) => {
// Remove inline onclick if it exists
button.removeAttribute("onclick");
// Add proper event listener
button.addEventListener("click", function (e) {
e.preventDefault();
e.stopPropagation();
window.logout();
});
});
});
}