Add timeout handling and loading state management in API calls

This commit is contained in:
Andy Oknen 2025-07-31 04:51:55 +00:00
parent 1f75299312
commit fcb5efd753
4 changed files with 76 additions and 2 deletions

View file

@ -15,12 +15,18 @@ class YggdrasilAPI {
*/
async callAdmin(command, args = {}) {
const url = command ? `${this.baseURL}/${command}` : this.baseURL;
// Create AbortController for timeout functionality
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout
const options = {
method: Object.keys(args).length > 0 ? 'POST' : 'GET',
headers: {
'Content-Type': 'application/json',
},
credentials: 'same-origin' // Include session cookies
credentials: 'same-origin', // Include session cookies
signal: controller.signal
};
if (Object.keys(args).length > 0) {
@ -29,6 +35,7 @@ class YggdrasilAPI {
try {
const response = await fetch(url, options);
clearTimeout(timeoutId); // Clear timeout on successful response
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
@ -42,6 +49,13 @@ class YggdrasilAPI {
return data.response || data.commands;
} catch (error) {
clearTimeout(timeoutId); // Clear timeout on error
if (error.name === 'AbortError') {
console.error(`API call timeout for ${command}:`, error);
throw new Error('Request timeout - service may be unavailable');
}
console.error(`API call failed for ${command}:`, error);
throw error;
}

View file

@ -7,12 +7,22 @@
window.nodeInfo = null;
window.peersData = null;
let isLoading = false;
let isLoadingNodeInfo = false;
let isLoadingPeers = false;
/**
* Load and display node information
*/
async function loadNodeInfo() {
if (isLoadingNodeInfo) {
console.log('Node info request already in progress, skipping...');
return window.nodeInfo;
}
try {
isLoadingNodeInfo = true;
const info = await window.yggAPI.getSelf();
window.nodeInfo = info;
updateNodeInfoDisplay(info);
@ -21,6 +31,8 @@ async function loadNodeInfo() {
console.error('Failed to load node info:', error);
showError('Failed to load node information: ' + error.message);
throw error;
} finally {
isLoadingNodeInfo = false;
}
}
@ -28,7 +40,13 @@ async function loadNodeInfo() {
* Load and display peers information
*/
async function loadPeers() {
if (isLoadingPeers) {
console.log('Peers request already in progress, skipping...');
return window.peersData;
}
try {
isLoadingPeers = true;
const data = await window.yggAPI.getPeers();
window.peersData = data;
updatePeersDisplay(data);
@ -37,6 +55,8 @@ async function loadPeers() {
console.error('Failed to load peers:', error);
showError('Failed to load peers information: ' + error.message);
throw error;
} finally {
isLoadingPeers = false;
}
}
@ -100,6 +120,8 @@ function updatePeersDisplay(data) {
window.updateNodeInfoDisplay = updateNodeInfoDisplay;
window.updatePeersDisplay = updatePeersDisplay;
// Expose copy functions to window for access from HTML onclick handlers
window.copyNodeKey = copyNodeKey;
window.copyNodeAddress = copyNodeAddress;
@ -359,18 +381,23 @@ function copyPeerKey(key) {
}
}
/**
* Auto-refresh data
*/
function startAutoRefresh() {
// Refresh every 30 seconds
setInterval(async () => {
if (!isLoading) {
// Only proceed if individual requests are not already in progress
if (!isLoadingNodeInfo && !isLoadingPeers) {
try {
await Promise.all([loadNodeInfo(), loadPeers()]);
} catch (error) {
console.error('Auto-refresh failed:', error);
}
} else {
console.log('Skipping auto-refresh - requests already in progress');
}
}, 30000);
}

View file

@ -14,6 +14,11 @@
</head>
<body>
<!-- Decorative progress bar -->
<div class="progress-timer-container">
<div class="progress-timer-bar"></div>
</div>
<div class="container">
<header>
<div class="header-content">

View file

@ -114,6 +114,16 @@
--shadow-heavy: rgba(0, 0, 0, 0.5);
}
/* Dark theme progress bar adjustments */
[data-theme="dark"] .progress-timer-container {
background: rgba(0, 0, 0, 0.4);
}
[data-theme="dark"] .progress-timer-bar {
background: linear-gradient(90deg, #3498db, #27ae60);
box-shadow: 0 0 10px rgba(52, 152, 219, 0.3);
}
* {
margin: 0;
padding: 0;
@ -128,6 +138,24 @@ body {
overflow: hidden; /* Prevent body scroll */
}
/* Decorative progress bar */
.progress-timer-container {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 4px;
background: rgba(255, 255, 255, 0.2);
z-index: 1000;
}
.progress-timer-bar {
height: 100%;
background: linear-gradient(90deg, #3498db, #2ecc71);
width: 100%;
box-shadow: 0 0 10px rgba(52, 152, 219, 0.5);
}
.container {
display: grid;
grid-template-columns: 250px 1fr;