fixed auth redirection bug

This commit is contained in:
Russel Yasol
2024-10-03 08:33:32 +08:00
parent 7d36748dce
commit badfb5e182
4 changed files with 381 additions and 180 deletions

View File

@@ -160,3 +160,104 @@ html.theme-light {
animation:s7 1s infinite; animation:s7 1s infinite;
} }
@keyframes s7 {to{transform: rotate(.5turn)}} @keyframes s7 {to{transform: rotate(.5turn)}}
/* table responsiveness */
/* Custom table styles */
.cell-table {
width: 100%;
border-collapse: collapse;
}
.cell-table th,
.cell-table td {
padding: 0.75rem;
}
/* Mobile styles */
@media screen and (max-width: 768px) {
.cell-table thead {
display: none;
}
.cell-table tbody {
display: block;
}
.cell-carousel {
position: relative;
overflow: hidden;
padding: 1rem 0;
}
.cell-carousel__container {
display: flex;
transition: transform 0.3s ease;
min-height: 300px; /* Adjust based on your content */
}
.cell-carousel__slide {
flex: 0 0 100%;
padding: 1rem;
box-sizing: border-box;
}
.cell-card {
border: 1px solid #dbdbdb;
border-radius: 4px;
padding: 1rem;
height: 100%;
}
.cell-card__item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem 0;
border-bottom: 1px solid #dbdbdb;
}
.cell-card__item:last-child {
border-bottom: none;
}
.cell-card__label {
font-weight: bold;
margin-right: 1rem;
}
/* Navigation buttons */
.cell-carousel__nav {
display: flex;
justify-content: center;
margin-top: 1rem;
}
/* .cell-carousel__btn {
background: #3273dc;
color: white;
border: none;
padding: 0.5rem 1rem;
margin: 0 0.5rem;
border-radius: 4px;
cursor: pointer;
} */
.cell-carousel__indicators {
display: flex;
justify-content: center;
margin-top: 0.5rem;
}
.cell-carousel__dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #dbdbdb;
margin: 0 4px;
cursor: pointer;
}
.cell-carousel__dot--active {
background: #3273dc;
}
}

View File

@@ -407,20 +407,20 @@
</div> </div>
</div> </div>
</div> </div>
<table class="table is-fullwidth" id="bandTable"> <table class="cell-table" id="bandTable">
<thead> <thead>
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>EARFCN</th> <th>EARFCN</th>
<th>Bandwidth</th> <th>Bandwidth</th>
<th>Physical ID</th> <th>Physical ID</th>
<th class="is-hidden-mobile">RSRP</th> <th>RSRP</th>
<th class="is-hidden-mobile">RSRQ</th> <th>RSRQ</th>
<th class="is-hidden-mobile">SINR</th> <th>SINR</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<!-- Automatically Generated Here --> <!-- Rows will be added dynamically -->
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@@ -1,50 +1,15 @@
class AuthManager { document.addEventListener("DOMContentLoaded", () => {
constructor() { const SESSION_DURATION = 30 * 60 * 1000; // 30 minutes in milliseconds
this.protectedPages = new Set([
'/home.html', function generateAuthToken(length = 32) {
'/advance-settings.html', const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
'/bandlock.html',
'/cell-locking.html',
'/cell-scanner.html',
'/cell-settings.html',
'/cell-sms.html',
'/about.html'
]);
// Session timeout in milliseconds (e.g., 30 minutes)
this.sessionTimeout = 30 * 60 * 1000;
this.init();
}
init() {
// Initially hide the body to prevent content flashing
document.body.style.display = 'none';
// Check authentication state
this.checkAuthState();
// Set up event listeners
this.setupEventListeners();
// Show body after auth check
document.body.style.display = '';
}
generateAuthToken(length = 32) {
const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
return Array.from(crypto.getRandomValues(new Uint8Array(length))) return Array.from(crypto.getRandomValues(new Uint8Array(length)))
.map(x => charset[x % charset.length]) .map(x => charset[x % charset.length])
.join(''); .join('');
} }
isProtectedPage(path) { function getSessionData() {
return this.protectedPages.has(path) || const sessionStr = localStorage.getItem("session");
Array.from(this.protectedPages).some(page => path.includes(page));
}
getSessionData() {
const sessionStr = localStorage.getItem('session');
if (!sessionStr) return null; if (!sessionStr) return null;
try { try {
@@ -53,153 +18,133 @@ class AuthManager {
return null; return null;
} }
} }
setSessionData(token) { function setSessionData(token) {
const session = { const session = {
token, token,
lastActivity: Date.now(), lastActivity: Date.now(),
expiresAt: Date.now() + this.sessionTimeout expiresAt: Date.now() + SESSION_DURATION
}; };
localStorage.setItem('session', JSON.stringify(session)); localStorage.setItem("session", JSON.stringify(session));
} }
isSessionValid() { function isSessionValid() {
const session = this.getSessionData(); const session = getSessionData();
if (!session) return false; if (!session) return false;
const now = Date.now(); const now = Date.now();
// Check if session has expired // Check if session has expired
if (now > session.expiresAt) { if (now > session.expiresAt) {
this.logout(); logout();
return false; return false;
} }
// Update last activity and extend session if needed // Extend session if it's been more than 5 minutes since last activity
if (now - session.lastActivity > 5 * 60 * 1000) { // Update every 5 minutes if (now - session.lastActivity > 5 * 60 * 1000) {
this.setSessionData(session.token); setSessionData(session.token);
} }
return true; return true;
} }
checkAuthState() { function logout() {
const currentPath = window.location.pathname; localStorage.removeItem("session");
const isAuthenticated = this.isSessionValid(); window.location.href = "index.html";
// Redirect logic
if (!isAuthenticated && this.isProtectedPage(currentPath)) {
window.location.href = '/index.html';
return false;
}
if (isAuthenticated && currentPath.includes('index.html')) {
window.location.href = '/home.html';
return false;
}
return true;
} }
async login(username, password) { // Initially hide the body to prevent content from flashing
try { document.body.style.display = "none";
const formData = new URLSearchParams();
formData.append('username', username);
formData.append('password', encodeURIComponent(password));
const response = await fetch('/cgi-bin/auth.sh', { // Define which pages should be protected
method: 'POST', const protectedPages = [
body: formData, "/home.html",
headers: { "/advance-settings.html",
'Content-Type': 'application/x-www-form-urlencoded' "/bandlock.html",
} "/cell-locking.html",
}); "/cell-scanner.html",
"/cell-settings.html",
"/cell-sms.html",
"/about.html",
];
const result = await response.json(); const currentPage = window.location.pathname;
if (result.state === 'success') { // Authentication check
const token = this.generateAuthToken(); const isAuthenticated = isSessionValid();
this.setSessionData(token);
window.location.href = '/home.html'; // Redirect logic
return true; if (!isAuthenticated && protectedPages.includes(currentPage)) {
} window.location.href = "index.html";
return;
return false;
} catch (error) {
console.error('Login error:', error);
throw new Error('An error occurred during login');
}
} }
logout() { if (isAuthenticated && currentPage.includes("index.html")) {
localStorage.removeItem('session'); window.location.href = "home.html";
window.location.href = '/index.html'; return;
} }
// Show the page if authentication check is complete
document.body.style.display = "";
setupEventListeners() { // Login form logic
// Handle login form const loginForm = document.getElementById("loginForm");
const loginForm = document.getElementById('loginForm'); if (loginForm) {
if (loginForm) { loginForm.addEventListener("submit", async (e) => {
loginForm.addEventListener('submit', async (e) => { e.preventDefault();
e.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
const errorElement = document.getElementById('error');
try { const username = document.getElementById("username").value;
const success = await this.login(username, password); const password = document.getElementById("password").value;
if (!success) { const errorElement = document.getElementById("error");
errorElement.textContent = 'Invalid username or password';
}
} catch (error) {
errorElement.textContent = error.message;
}
});
}
// Handle component loading try {
window.addEventListener('componentLoaded', (event) => { const formData = new URLSearchParams();
if (event.detail.componentId === 'nav-placeholder') { formData.append("username", username);
this.setupNavbarHandlers(); formData.append("password", encodeURIComponent(password));
}
});
// Set up periodic session check const response = await fetch("/cgi-bin/auth.sh", {
setInterval(() => { method: "POST",
if (this.isProtectedPage(window.location.pathname)) { body: formData,
this.isSessionValid(); headers: {
} "Content-Type": "application/x-www-form-urlencoded",
}, 60000); // Check every minute },
}
setupNavbarHandlers() {
// Handle logout button
const logoutButton = document.getElementById('logoutButton');
if (logoutButton) {
logoutButton.addEventListener('click', (e) => {
e.preventDefault();
this.logout();
});
}
// Handle home navigation
const homeLinks = document.querySelectorAll('.navbar-item');
homeLinks.forEach(link => {
if (link.textContent.trim() === 'Home') {
link.addEventListener('click', (e) => {
e.preventDefault();
if (this.isSessionValid()) {
window.location.href = '/home.html';
} else {
window.location.href = '/index.html';
}
}); });
const result = await response.json();
if (result.state === "success") {
const newToken = generateAuthToken();
setSessionData(newToken);
window.location.href = "home.html";
} else {
errorElement.textContent = "Invalid username or password";
}
} catch (error) {
errorElement.textContent = "An error occurred. Please try again later.";
console.error("Login error:", error);
} }
}); });
} }
}
// Initialize auth manager when DOM is loaded // Event listeners
document.addEventListener('DOMContentLoaded', () => { const logoutButton = document.getElementById("logoutButton");
window.authManager = new AuthManager(); if (logoutButton) {
logoutButton.addEventListener("click", logout);
}
document.querySelectorAll(".navbar-item").forEach((el) => {
if (el.textContent.includes("Home")) {
el.addEventListener("click", (e) => {
if (isSessionValid()) {
e.preventDefault();
window.location.href = "home.html";
}
});
}
});
// Periodic session check
if (protectedPages.includes(currentPage)) {
setInterval(isSessionValid, 60000); // Check every minute
}
}); });

View File

@@ -97,7 +97,11 @@ function handleRefreshClick() {
} }
} }
Promise.all([fetchATCommandData(), fetchConnectionStatus(), fetchTrafficStats]).finally(() => { Promise.all([
fetchATCommandData(),
fetchConnectionStatus(),
fetchTrafficStats,
]).finally(() => {
if (refreshButton) { if (refreshButton) {
refreshButton.disabled = false; refreshButton.disabled = false;
const icon = refreshButton.querySelector("i"); const icon = refreshButton.querySelector("i");
@@ -184,7 +188,10 @@ function startPeriodicRefresh(refreshRate = DEFAULT_REFRESH_RATE) {
// Start new intervals // Start new intervals
atCommandInterval = setInterval(fetchATCommandData, refreshRate); atCommandInterval = setInterval(fetchATCommandData, refreshRate);
trafficStatsInterval = setInterval(fetchTrafficStats, TRAFFIC_STATS_REFRESH_RATE); trafficStatsInterval = setInterval(
fetchTrafficStats,
TRAFFIC_STATS_REFRESH_RATE
);
connectionStatusInterval = setInterval( connectionStatusInterval = setInterval(
fetchConnectionStatus, fetchConnectionStatus,
refreshRate * CONNECTION_CHECK_MULTIPLIER refreshRate * CONNECTION_CHECK_MULTIPLIER
@@ -620,22 +627,56 @@ function createBandTableRow(bandData, networkType, servingCellJSON) {
?.replace("LTE BAND ", "B") ?.replace("LTE BAND ", "B")
.replace("NR5G BAND ", "N"); .replace("NR5G BAND ", "N");
// Create row HTML // Create both desktop and mobile versions of the content
row.innerHTML = ` const desktopContent = `
<td>${formattedBandNumber || "N/A"}</td> <td>${formattedBandNumber || "N/A"}</td>
<td>${earfcn || "N/A"}</td> <td>${earfcn || "N/A"}</td>
<td>${bandwidth || "N/A"}</td> <td>${bandwidth || "N/A"}</td>
<td>${pci || "N/A"}</td> <td>${pci || "N/A"}</td>
<td class="is-hidden-mobile"> <td>${rsrp ? createSignalTag(rsrp, "RSRP") : "N/A"}</td>
${rsrp ? createSignalTag(rsrp, "RSRP") : "N/A"} <td>${rsrq ? createSignalTag(rsrq, "RSRQ") : "N/A"}</td>
</td> <td>${sinr ? createSignalTag(sinr, "SINR") : "N/A"}</td>
<td class="is-hidden-mobile">
${rsrq ? createSignalTag(rsrq, "RSRQ") : "N/A"}
</td>
<td class="is-hidden-mobile">
${sinr ? createSignalTag(sinr, "SINR") : "N/A"}
</td>
`; `;
const mobileContent = `
<div class="cell-carousel__slide">
<div class="cell-card">
<div class="cell-card__item">
<span class="cell-card__label">Name</span>
<span>${formattedBandNumber || "N/A"}</span>
</div>
<div class="cell-card__item">
<span class="cell-card__label">EARFCN</span>
<span>${earfcn || "N/A"}</span>
</div>
<div class="cell-card__item">
<span class="cell-card__label">Bandwidth</span>
<span>${bandwidth || "N/A"}</span>
</div>
<div class="cell-card__item">
<span class="cell-card__label">Physical ID</span>
<span>${pci || "N/A"}</span>
</div>
<div class="cell-card__item">
<span class="cell-card__label">RSRP</span>
<span>${rsrp ? createSignalTag(rsrp, "RSRP") : "N/A"}</span>
</div>
<div class="cell-card__item">
<span class="cell-card__label">RSRQ</span>
<span>${rsrq ? createSignalTag(rsrq, "RSRQ") : "N/A"}</span>
</div>
<div class="cell-card__item">
<span class="cell-card__label">SINR</span>
<span>${sinr ? createSignalTag(sinr, "SINR") : "N/A"}</span>
</div>
</div>
</div>
`;
// Store both versions in data attributes
row.setAttribute("data-desktop", desktopContent);
row.setAttribute("data-mobile", mobileContent);
row.innerHTML = desktopContent;
} catch (error) { } catch (error) {
console.error("Error parsing band data:", error); console.error("Error parsing band data:", error);
row.innerHTML = '<td colspan="7">Error parsing band data</td>'; row.innerHTML = '<td colspan="7">Error parsing band data</td>';
@@ -930,7 +971,6 @@ async function fetchTrafficStats() {
// Update the DOM // Update the DOM
setText("download", downloadFormatted); setText("download", downloadFormatted);
setText("upload", uploadFormatted); setText("upload", uploadFormatted);
} catch (error) { } catch (error) {
console.error("There was a problem with the fetch operation:", error); console.error("There was a problem with the fetch operation:", error);
} }
@@ -1045,3 +1085,118 @@ document.addEventListener("DOMContentLoaded", () => {
setupRefreshControls(); setupRefreshControls();
setupEventListeners(); setupEventListeners();
}); });
// Mobile carousel functions
let touchStartX = 0;
let touchEndX = 0;
function initMobileCarousel() {
const table = document.getElementById("bandTable");
const tbody = table.querySelector("tbody");
const rows = tbody.querySelectorAll("tr");
if (window.innerWidth <= 768) {
// Create carousel structure
const carouselWrapper = document.createElement("div");
carouselWrapper.className = "cell-carousel";
const carouselContainer = document.createElement("div");
carouselContainer.className = "cell-carousel__container";
// Move content to carousel
rows.forEach((row) => {
carouselContainer.insertAdjacentHTML(
"beforeend",
row.getAttribute("data-mobile")
);
});
carouselWrapper.appendChild(carouselContainer);
// Add only indicators
const indicatorsHTML = `
<div class="cell-carousel__indicators">
${Array.from(
{ length: rows.length },
(_, i) =>
`<span class="cell-carousel__dot ${
i === 0 ? "cell-carousel__dot--active" : ""
}"
onclick="goToSlide(${i})"></span>`
).join("")}
</div>
`;
// Replace table with carousel
table.style.display = "none";
table.parentNode.insertBefore(carouselWrapper, table);
carouselWrapper.insertAdjacentHTML("beforeend", indicatorsHTML);
// Add touch event listeners
carouselContainer.addEventListener("touchstart", handleTouchStart, false);
carouselContainer.addEventListener("touchmove", handleTouchMove, false);
carouselContainer.addEventListener("touchend", handleTouchEnd, false);
} else {
// Restore desktop view if necessary
const carousel = document.querySelector(".cell-carousel");
if (carousel) {
carousel.remove();
table.style.display = "";
}
rows.forEach((row) => {
row.innerHTML = row.getAttribute("data-desktop");
});
}
}
function handleTouchStart(event) {
touchStartX = event.touches[0].clientX;
}
function handleTouchMove(event) {
event.preventDefault(); // Prevent scrolling while swiping
}
function handleTouchEnd(event) {
touchEndX = event.changedTouches[0].clientX;
handleSwipe();
}
function handleSwipe() {
const swipeThreshold = 50; // Minimum distance for a swipe
const container = document.querySelector(".cell-carousel__container");
const slides = container.querySelectorAll(".cell-carousel__slide");
const diffX = touchStartX - touchEndX;
if (Math.abs(diffX) > swipeThreshold) {
if (diffX > 0 && currentSlide < slides.length - 1) {
// Swipe left, go to next slide
currentSlide++;
} else if (diffX < 0 && currentSlide > 0) {
// Swipe right, go to previous slide
currentSlide--;
}
updateCarousel();
}
}
function goToSlide(index) {
currentSlide = index;
updateCarousel();
}
function updateCarousel() {
const container = document.querySelector(".cell-carousel__container");
container.style.transform = `translateX(-${currentSlide * 100}%)`;
// Update indicators
const dots = document.querySelectorAll(".cell-carousel__dot");
dots.forEach((dot, index) => {
dot.classList.toggle("cell-carousel__dot--active", index === currentSlide);
});
}
// Initialize carousel on load and resize
window.addEventListener("load", initMobileCarousel);
window.addEventListener("resize", initMobileCarousel);