debug(dashboard): add visible API debug panel to diagnose fetch failures in browser
API endpoints return valid data via curl but browser JS shows 'Failed to load'. Added verbose logging and a fixed-position debug panel to api.js that displays the exact error type (JSON parse, API error, or network exception) on-page. Bumped cache-bust to ?v=4.
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>LLM Proxy Gateway - Admin Dashboard</title>
|
<title>LLM Proxy Gateway - Admin Dashboard</title>
|
||||||
<link rel="stylesheet" href="/css/dashboard.css?v=3">
|
<link rel="stylesheet" href="/css/dashboard.css?v=4">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
<link rel="icon" href="img/logo-icon.png" type="image/png" sizes="any">
|
<link rel="icon" href="img/logo-icon.png" type="image/png" sizes="any">
|
||||||
<link rel="apple-touch-icon" href="img/logo-icon.png">
|
<link rel="apple-touch-icon" href="img/logo-icon.png">
|
||||||
@@ -166,19 +166,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Scripts (cache-busted with version query params) -->
|
<!-- Scripts (cache-busted with version query params) -->
|
||||||
<script src="/js/api.js?v=3"></script>
|
<script src="/js/api.js?v=4"></script>
|
||||||
<script src="/js/auth.js?v=3"></script>
|
<script src="/js/auth.js?v=4"></script>
|
||||||
<script src="/js/dashboard.js?v=3"></script>
|
<script src="/js/dashboard.js?v=4"></script>
|
||||||
<script src="/js/websocket.js?v=3"></script>
|
<script src="/js/websocket.js?v=4"></script>
|
||||||
<script src="/js/charts.js?v=3"></script>
|
<script src="/js/charts.js?v=4"></script>
|
||||||
<script src="/js/pages/overview.js?v=3"></script>
|
<script src="/js/pages/overview.js?v=4"></script>
|
||||||
<script src="/js/pages/analytics.js?v=3"></script>
|
<script src="/js/pages/analytics.js?v=4"></script>
|
||||||
<script src="/js/pages/costs.js?v=3"></script>
|
<script src="/js/pages/costs.js?v=4"></script>
|
||||||
<script src="/js/pages/clients.js?v=3"></script>
|
<script src="/js/pages/clients.js?v=4"></script>
|
||||||
<script src="/js/pages/providers.js?v=3"></script>
|
<script src="/js/pages/providers.js?v=4"></script>
|
||||||
<script src="/js/pages/models.js?v=3"></script>
|
<script src="/js/pages/models.js?v=4"></script>
|
||||||
<script src="/js/pages/monitoring.js?v=3"></script>
|
<script src="/js/pages/monitoring.js?v=4"></script>
|
||||||
<script src="/js/pages/settings.js?v=3"></script>
|
<script src="/js/pages/settings.js?v=4"></script>
|
||||||
<script src="/js/pages/logs.js?v=3"></script>
|
<script src="/js/pages/logs.js?v=4"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -18,24 +18,62 @@ class ApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log(`[API] Fetching ${url}...`);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
...options,
|
...options,
|
||||||
headers
|
headers
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await response.json();
|
console.log(`[API] ${url} → status=${response.status} ok=${response.ok} type=${response.headers.get('content-type')}`);
|
||||||
|
|
||||||
|
const text = await response.text();
|
||||||
|
console.log(`[API] ${url} → body length=${text.length}, first 200 chars:`, text.substring(0, 200));
|
||||||
|
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = JSON.parse(text);
|
||||||
|
} catch (parseErr) {
|
||||||
|
const msg = `JSON parse failed for ${url}: ${parseErr.message}. Body: ${text.substring(0, 300)}`;
|
||||||
|
console.error(`[API] ${msg}`);
|
||||||
|
this._addDebugEntry(url, 'JSON_PARSE_ERROR', msg);
|
||||||
|
throw new Error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
if (!response.ok || !result.success) {
|
if (!response.ok || !result.success) {
|
||||||
|
const msg = `API error for ${url}: ok=${response.ok} success=${result.success} error=${result.error} status=${response.status}`;
|
||||||
|
console.error(`[API] ${msg}`);
|
||||||
|
this._addDebugEntry(url, 'API_ERROR', msg);
|
||||||
throw new Error(result.error || `HTTP error! status: ${response.status}`);
|
throw new Error(result.error || `HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`[API] ${url} → SUCCESS, data keys:`, result.data ? Object.keys(result.data) : 'null');
|
||||||
return result.data;
|
return result.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`API Request failed (${path}):`, error);
|
console.error(`[API] Request failed (${path}):`, error);
|
||||||
|
this._addDebugEntry(url, 'EXCEPTION', error.message);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Visible on-page debug panel for diagnosing fetch failures
|
||||||
|
_addDebugEntry(url, status, detail) {
|
||||||
|
let panel = document.getElementById('api-debug-panel');
|
||||||
|
if (!panel) {
|
||||||
|
panel = document.createElement('div');
|
||||||
|
panel.id = 'api-debug-panel';
|
||||||
|
panel.style.cssText = 'position:fixed;bottom:0;left:0;right:0;max-height:200px;overflow-y:auto;background:#1d2021;color:#fbf1c7;font-family:monospace;font-size:11px;padding:8px;z-index:99999;border-top:2px solid #cc241d;';
|
||||||
|
const title = document.createElement('div');
|
||||||
|
title.style.cssText = 'font-weight:bold;margin-bottom:4px;color:#fb4934;';
|
||||||
|
title.textContent = 'API Debug Panel (remove after fixing)';
|
||||||
|
panel.appendChild(title);
|
||||||
|
document.body.appendChild(panel);
|
||||||
|
}
|
||||||
|
const entry = document.createElement('div');
|
||||||
|
entry.style.cssText = 'margin:2px 0;padding:2px 4px;background:#282828;border-left:3px solid ' + (status === 'EXCEPTION' ? '#fb4934' : '#fabd2f') + ';';
|
||||||
|
entry.textContent = `[${status}] ${url}: ${detail}`;
|
||||||
|
panel.appendChild(entry);
|
||||||
|
}
|
||||||
|
|
||||||
async get(path) {
|
async get(path) {
|
||||||
return this.request(path, { method: 'GET' });
|
return this.request(path, { method: 'GET' });
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user