// Linux BenchTools - Dashboard Logic // Access utilities and API const utils = window.BenchUtils; const apiClient = window.BenchAPI; // Global state let allDevices = []; let isLoading = false; let apiToken = null; let iperfServer = null; // Load backend config (API token, etc.) async function loadBackendConfig() { try { const response = await fetch(`${window.BenchConfig.backendApiUrl}/config`); if (response.ok) { const config = await response.json(); apiToken = config.api_token; iperfServer = config.iperf_server || '10.0.0.50'; updateBenchCommandDisplay(); } else { console.error('Failed to load backend config - HTTP', response.status); // Set default values to allow the page to render apiToken = 'LOADING_ERROR'; iperfServer = '10.0.0.50'; updateBenchCommandDisplay(); } } catch (error) { console.error('Failed to load backend config:', error); // Set default values to allow the page to render apiToken = 'LOADING_ERROR'; iperfServer = '10.0.0.50'; updateBenchCommandDisplay(); } } // Load dashboard data async function loadDashboard() { if (isLoading) return; isLoading = true; updateRefreshButton(true); try { await Promise.all([ loadStats(), loadTopDevices() ]); // Update last refresh time updateLastRefreshTime(); } catch (error) { console.error('Failed to load dashboard:', error); utils.showToast('Erreur lors du chargement des données', 'error'); } finally { isLoading = false; updateRefreshButton(false); } } // Update refresh button state function updateRefreshButton(loading) { const btn = document.getElementById('refreshBtn'); if (!btn) return; if (loading) { btn.disabled = true; btn.innerHTML = '⏳ Chargement...'; } else { btn.disabled = false; btn.innerHTML = '🔄 Actualiser'; } } async function backupDatabase() { const btn = document.getElementById('backupBtn'); if (btn) { btn.disabled = true; btn.textContent = '💾 Backup...'; } try { const result = await apiClient.backupDatabase(); const files = (result.backups || []).map(b => b.filename).join(', '); utils.showToast(`Backup créé${files ? `: ${files}` : ''}`, 'success'); } catch (error) { console.error('Backup failed:', error); utils.showToast(`Backup échoué: ${error.message}`, 'error'); } finally { if (btn) { btn.disabled = false; btn.textContent = '💾 Backup DB'; } } } // Update last refresh time function updateLastRefreshTime() { const element = document.getElementById('lastUpdate'); if (!element) return; const now = new Date(); element.textContent = `Mis à jour: ${now.toLocaleTimeString('fr-FR')}`; } // Load statistics async function loadStats() { try { const devices = await apiClient.getDevices({ page_size: 100 }); const totalDevices = devices.total || 0; let totalBenchmarks = 0; let scoreSum = 0; let scoreCount = 0; let lastBenchDate = null; // Calculate stats from devices devices.items.forEach(device => { if (device.last_benchmark) { totalBenchmarks++; if (device.last_benchmark.global_score !== null) { scoreSum += device.last_benchmark.global_score; scoreCount++; } const benchDate = new Date(device.last_benchmark.run_at); if (!lastBenchDate || benchDate > lastBenchDate) { lastBenchDate = benchDate; } } }); const avgScore = scoreCount > 0 ? Math.ceil(scoreSum / scoreCount) : 0; // Update UI document.getElementById('totalDevices').textContent = totalDevices; document.getElementById('totalBenchmarks').textContent = totalBenchmarks; document.getElementById('avgScore').textContent = avgScore; document.getElementById('lastBench').textContent = lastBenchDate ? utils.formatRelativeTime(lastBenchDate.toISOString()) : 'Aucun'; } catch (error) { console.error('Failed to load stats:', error); // Set default values on error document.getElementById('totalDevices').textContent = '0'; document.getElementById('totalBenchmarks').textContent = '0'; document.getElementById('avgScore').textContent = '0'; document.getElementById('lastBench').textContent = 'N/A'; } } // Load top devices async function loadTopDevices() { const container = document.getElementById('devicesTable'); try { const data = await apiClient.getDevices({ page_size: 50 }); if (!data.items || data.items.length === 0) { utils.showEmptyState(container, 'Aucun device trouvé. Exécutez un benchmark sur une machine pour commencer.', '📊'); allDevices = []; return; } // Store all devices for filtering allDevices = data.items; // Sort by global_score descending const sortedDevices = allDevices.sort((a, b) => { const scoreA = a.last_benchmark?.global_score ?? -1; const scoreB = b.last_benchmark?.global_score ?? -1; return scoreB - scoreA; }); // Render devices renderDevicesTable(sortedDevices); } catch (error) { console.error('Failed to load devices:', error); container.innerHTML = `

❌ Impossible de charger les devices

${utils.escapeHtml(error.message)}

`; } } // Render devices table function renderDevicesTable(devices) { const container = document.getElementById('devicesTable'); if (devices.length === 0) { container.innerHTML = `

Aucun device trouvé avec ces critères de recherche.

`; return; } container.innerHTML = `
${devices.map((device, index) => createDeviceRow(device, index + 1)).join('')}
# Hostname Description Score Global CPU MEM DISK NET GPU Dernier Bench Action
`; } // Create device row HTML function createDeviceRow(device, rank) { const bench = device.last_benchmark; const globalScore = bench?.global_score; const cpuScore = bench?.cpu_score; const memScore = bench?.memory_score; const diskScore = bench?.disk_score; const netScore = bench?.network_score; const gpuScore = bench?.gpu_score; const runAt = bench?.run_at; const globalScoreHtml = globalScore !== null && globalScore !== undefined ? `${utils.getScoreBadgeText(globalScore)}` : 'N/A'; return ` ${rank} ${utils.escapeHtml(device.hostname)} ${utils.escapeHtml(device.description || 'Aucune description')} ${globalScoreHtml} ${utils.getScoreBadgeText(cpuScore)} ${utils.getScoreBadgeText(memScore)} ${utils.getScoreBadgeText(diskScore)} ${utils.getScoreBadgeText(netScore)} ${utils.getScoreBadgeText(gpuScore)} ${runAt ? utils.formatRelativeTime(runAt) : 'Jamais'} Voir `; } function buildBenchCommand() { const cfg = window.BenchConfig || {}; const frontendBase = (cfg.frontendBaseUrl || window.location.origin).replace(/\/$/, ''); const scriptPath = cfg.benchScriptPath || '/scripts/bench.sh'; const backendBase = (cfg.backendApiUrl || `${window.location.protocol}//${window.location.hostname}:8007/api`).replace(/\/$/, ''); const token = apiToken || 'LOADING...'; const iperf = iperfServer || '10.0.0.50'; // Extract backend URL without /api suffix const backendUrl = backendBase.replace(/\/api$/, ''); return `curl -fsSL ${frontendBase}${scriptPath} | sudo bash -s -- --server ${backendUrl} --token "${token}" --iperf-server ${iperf}`; } function updateBenchCommandDisplay() { const element = document.getElementById('benchCommand'); if (!element) return; element.textContent = buildBenchCommand(); } // Copy bench command to clipboard async function copyBenchCommand() { const command = document.getElementById('benchCommand').textContent; const success = await utils.copyToClipboard(command); if (success) { utils.showToast('Commande copiée dans le presse-papier !', 'success'); } else { utils.showToast('Erreur lors de la copie', 'error'); } } // Filter devices based on search query function filterDevices(query) { if (!query || query.trim() === '') { renderDevicesTable(allDevices); return; } const lowerQuery = query.toLowerCase(); const filtered = allDevices.filter(device => { const hostname = (device.hostname || '').toLowerCase(); const description = (device.description || '').toLowerCase(); const location = (device.location || '').toLowerCase(); return hostname.includes(lowerQuery) || description.includes(lowerQuery) || location.includes(lowerQuery); }); renderDevicesTable(filtered); } // Debounced search const debouncedSearch = utils.debounce((query) => { filterDevices(query); }, 300); // Handle search input function handleSearch(event) { const query = event.target.value; debouncedSearch(query); } // Clear search function clearSearch() { const searchInput = document.getElementById('searchInput'); if (searchInput) { searchInput.value = ''; filterDevices(''); } } // Refresh dashboard manually function refreshDashboard() { if (!isLoading) { loadDashboard(); } } // Initialize dashboard on page load document.addEventListener('DOMContentLoaded', async () => { // Load backend config first to get API token await loadBackendConfig(); // Then load dashboard data loadDashboard(); // Setup search input listener const searchInput = document.getElementById('searchInput'); if (searchInput) { searchInput.addEventListener('input', handleSearch); } // Refresh every 30 seconds setInterval(loadDashboard, 30000); }); // Make functions available globally window.copyBenchCommand = copyBenchCommand; window.clearSearch = clearSearch; window.refreshDashboard = refreshDashboard;