tractatus/public/js/admin-disk-monitoring.js
TheFlow 493e540437 fix(disk-monitoring): use correct admin_token from localStorage
Fix authentication error in disk monitoring dashboard:
- Change from 'token' to 'admin_token' for consistency with admin system
- Add check for missing token with user-friendly error message
- Prevents 401 errors when admin is logged in

Fixes: Failed to fetch metrics: 401 (Unauthorized)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 11:58:26 +13:00

217 lines
6.3 KiB
JavaScript

// Disk Monitoring - Admin UI
// CSP-compliant implementation using DOM manipulation
async function loadMetrics() {
const loading = document.getElementById('loading');
const metricsContainer = document.getElementById('metrics-container');
const errorDiv = document.getElementById('error');
try {
loading.classList.remove('hidden');
metricsContainer.classList.add('hidden');
errorDiv.classList.add('hidden');
const token = localStorage.getItem('admin_token');
if (!token) {
throw new Error('Not authenticated. Please log in.');
}
const response = await fetch('/api/admin/disk-metrics', {
headers: { 'Authorization': 'Bearer ' + token }
});
if (!response.ok) {
throw new Error('Failed to fetch metrics: ' + response.status);
}
const result = await response.json();
if (!result.success) {
throw new Error(result.error || 'Unknown error');
}
renderMetrics(result.data);
loading.classList.add('hidden');
metricsContainer.classList.remove('hidden');
} catch (err) {
console.error('Load metrics error:', err);
loading.classList.add('hidden');
errorDiv.classList.remove('hidden');
const errorMessage = document.getElementById('error-message');
errorMessage.textContent = err.message || 'An unexpected error occurred';
}
}
function renderMetrics(data) {
const localContainer = document.getElementById('local-metrics');
const remoteContainer = document.getElementById('remote-metrics');
// Clear existing content
localContainer.textContent = '';
remoteContainer.textContent = '';
// Render local metrics
if (data.local) {
renderSystemMetrics(localContainer, data.local, 'Local Development');
} else {
renderError(localContainer, 'Local metrics unavailable');
}
// Render remote metrics
if (data.remote) {
renderSystemMetrics(remoteContainer, data.remote, 'Production VPS');
} else {
renderError(remoteContainer, 'Remote metrics unavailable');
}
}
function renderSystemMetrics(container, metrics, label) {
// Disk Usage Card
const diskCard = createMetricCard(
'Disk Usage',
metrics.health,
[
{ label: 'Total', value: metrics.total },
{ label: 'Used', value: metrics.used },
{ label: 'Available', value: metrics.available },
{ label: 'Usage', value: metrics.usedPercent + '%', progress: metrics.usedPercent }
]
);
container.appendChild(diskCard);
// Memory Card
if (metrics.memory) {
const memoryCard = createMetricCard(
'Memory',
{ level: metrics.memory.usedPercent >= 90 ? 'critical' : metrics.memory.usedPercent >= 80 ? 'warning' : 'healthy' },
[
{ label: 'Total', value: metrics.memory.total },
{ label: 'Used', value: metrics.memory.usedPercent + '%', progress: metrics.memory.usedPercent }
]
);
container.appendChild(memoryCard);
}
// System Info Card
const sysCard = createMetricCard(
'System Info',
null,
[
{ label: 'Hostname', value: metrics.hostname || 'Unknown' },
{ label: 'Platform', value: metrics.platform || 'Unknown' },
{ label: 'Uptime', value: (metrics.uptime || 0) + ' hours' }
]
);
container.appendChild(sysCard);
// Docker Volumes (if present)
if (metrics.docker) {
const dockerCard = createMetricCard(
'Docker Volumes',
null,
[
{ label: 'Total', value: metrics.docker.total },
{ label: 'Used', value: metrics.docker.used }
]
);
container.appendChild(dockerCard);
}
}
function createMetricCard(title, health, items) {
const card = document.createElement('div');
card.className = 'metric-card bg-white rounded-lg shadow-lg p-6';
// Card header
const header = document.createElement('div');
header.className = 'flex items-center justify-between mb-4';
const titleEl = document.createElement('h3');
titleEl.className = 'text-lg font-semibold text-gray-900';
titleEl.textContent = title;
header.appendChild(titleEl);
// Health indicator (if present)
if (health) {
const indicator = document.createElement('span');
indicator.className = 'health-indicator health-' + health.level;
indicator.title = health.level.charAt(0).toUpperCase() + health.level.slice(1);
header.appendChild(indicator);
}
card.appendChild(header);
// Card content
items.forEach(item => {
const row = document.createElement('div');
row.className = 'mb-3';
const labelDiv = document.createElement('div');
labelDiv.className = 'flex justify-between text-sm mb-1';
const labelSpan = document.createElement('span');
labelSpan.className = 'text-gray-600';
labelSpan.textContent = item.label;
labelDiv.appendChild(labelSpan);
const valueSpan = document.createElement('span');
valueSpan.className = 'font-semibold text-gray-900';
valueSpan.textContent = item.value;
labelDiv.appendChild(valueSpan);
row.appendChild(labelDiv);
// Progress bar (if present)
if (item.progress !== undefined) {
const progressBg = document.createElement('div');
progressBg.className = 'w-full bg-gray-200 rounded-full h-2';
const progressBar = document.createElement('div');
progressBar.className = 'progress-bar h-2 rounded-full ' + getProgressColor(item.progress);
progressBar.style.width = item.progress + '%';
progressBg.appendChild(progressBar);
row.appendChild(progressBg);
}
card.appendChild(row);
});
return card;
}
function renderError(container, message) {
const errorDiv = document.createElement('div');
errorDiv.className = 'col-span-3 bg-yellow-50 border-l-4 border-yellow-500 p-4 rounded';
const errorText = document.createElement('p');
errorText.className = 'text-yellow-800';
errorText.textContent = '⚠️ ' + message;
errorDiv.appendChild(errorText);
container.appendChild(errorDiv);
}
function getProgressColor(percent) {
if (percent >= 90) return 'bg-red-600';
if (percent >= 80) return 'bg-orange-500';
if (percent >= 70) return 'bg-yellow-500';
return 'bg-green-600';
}
// Refresh functionality
document.addEventListener('DOMContentLoaded', () => {
loadMetrics();
// Refresh button
const refreshBtn = document.getElementById('refresh-btn');
if (refreshBtn) {
refreshBtn.addEventListener('click', loadMetrics);
}
// Auto-refresh every 5 minutes
setInterval(loadMetrics, 5 * 60 * 1000);
});