diff options
Diffstat (limited to 'telemetry/server/dashboard/dashboard.go')
| -rw-r--r-- | telemetry/server/dashboard/dashboard.go | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/telemetry/server/dashboard/dashboard.go b/telemetry/server/dashboard/dashboard.go new file mode 100644 index 000000000..9a56c7f1b --- /dev/null +++ b/telemetry/server/dashboard/dashboard.go @@ -0,0 +1,278 @@ +package dashboard + +import ( + "net/http" +) + +type Handler struct{} + +func NewHandler() *Handler { + return &Handler{} +} + +func (h *Handler) ServeIndex(w http.ResponseWriter, r *http.Request) { + html := `<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>SeaweedFS Telemetry Dashboard</title> + <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> + <style> + body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + margin: 0; + padding: 20px; + background-color: #f5f5f5; + } + .container { + max-width: 1200px; + margin: 0 auto; + } + .header { + background: white; + padding: 20px; + border-radius: 8px; + margin-bottom: 20px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + } + .stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 20px; + margin-bottom: 20px; + } + .stat-card { + background: white; + padding: 20px; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + } + .stat-value { + font-size: 2em; + font-weight: bold; + color: #2196F3; + } + .stat-label { + color: #666; + margin-top: 5px; + } + .chart-container { + background: white; + padding: 20px; + border-radius: 8px; + margin-bottom: 20px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + } + .chart-title { + font-size: 1.2em; + font-weight: bold; + margin-bottom: 15px; + } + .loading { + text-align: center; + padding: 40px; + color: #666; + } + .error { + background: #ffebee; + color: #c62828; + padding: 15px; + border-radius: 4px; + margin: 10px 0; + } + </style> +</head> +<body> + <div class="container"> + <div class="header"> + <h1>SeaweedFS Telemetry Dashboard</h1> + <p>Privacy-respecting usage analytics for SeaweedFS</p> + </div> + + <div id="loading" class="loading">Loading telemetry data...</div> + <div id="error" class="error" style="display: none;"></div> + + <div id="dashboard" style="display: none;"> + <div class="stats-grid"> + <div class="stat-card"> + <div class="stat-value" id="totalInstances">-</div> + <div class="stat-label">Total Instances (30 days)</div> + </div> + <div class="stat-card"> + <div class="stat-value" id="activeInstances">-</div> + <div class="stat-label">Active Instances (7 days)</div> + </div> + <div class="stat-card"> + <div class="stat-value" id="totalVersions">-</div> + <div class="stat-label">Different Versions</div> + </div> + <div class="stat-card"> + <div class="stat-value" id="totalOS">-</div> + <div class="stat-label">Operating Systems</div> + </div> + </div> + + <div class="chart-container"> + <div class="chart-title">Version Distribution</div> + <canvas id="versionChart" width="400" height="200"></canvas> + </div> + + <div class="chart-container"> + <div class="chart-title">Operating System Distribution</div> + <canvas id="osChart" width="400" height="200"></canvas> + </div> + + <div class="chart-container"> + <div class="chart-title">Deployment Types</div> + <canvas id="deploymentChart" width="400" height="200"></canvas> + </div> + + <div class="chart-container"> + <div class="chart-title">Volume Servers Over Time</div> + <canvas id="serverChart" width="400" height="200"></canvas> + </div> + + <div class="chart-container"> + <div class="chart-title">Total Disk Usage Over Time</div> + <canvas id="diskChart" width="400" height="200"></canvas> + </div> + </div> + </div> + + <script> + let charts = {}; + + async function loadDashboard() { + try { + // Load stats + const statsResponse = await fetch('/api/stats'); + const stats = await statsResponse.json(); + + // Load metrics + const metricsResponse = await fetch('/api/metrics?days=30'); + const metrics = await metricsResponse.json(); + + updateStats(stats); + updateCharts(stats, metrics); + + document.getElementById('loading').style.display = 'none'; + document.getElementById('dashboard').style.display = 'block'; + } catch (error) { + console.error('Error loading dashboard:', error); + showError('Failed to load telemetry data: ' + error.message); + } + } + + function updateStats(stats) { + document.getElementById('totalInstances').textContent = stats.total_instances || 0; + document.getElementById('activeInstances').textContent = stats.active_instances || 0; + document.getElementById('totalVersions').textContent = Object.keys(stats.versions || {}).length; + document.getElementById('totalOS').textContent = Object.keys(stats.os_distribution || {}).length; + } + + function updateCharts(stats, metrics) { + // Version chart + createPieChart('versionChart', 'Version Distribution', stats.versions || {}); + + // OS chart + createPieChart('osChart', 'Operating System Distribution', stats.os_distribution || {}); + + // Deployment chart + createPieChart('deploymentChart', 'Deployment Types', stats.deployments || {}); + + // Server count over time + if (metrics.dates && metrics.server_counts) { + createLineChart('serverChart', 'Volume Servers', metrics.dates, metrics.server_counts, '#2196F3'); + } + + // Disk usage over time + if (metrics.dates && metrics.disk_usage) { + const diskUsageGB = metrics.disk_usage.map(bytes => Math.round(bytes / (1024 * 1024 * 1024))); + createLineChart('diskChart', 'Disk Usage (GB)', metrics.dates, diskUsageGB, '#4CAF50'); + } + } + + function createPieChart(canvasId, title, data) { + const ctx = document.getElementById(canvasId).getContext('2d'); + + if (charts[canvasId]) { + charts[canvasId].destroy(); + } + + const labels = Object.keys(data); + const values = Object.values(data); + + charts[canvasId] = new Chart(ctx, { + type: 'pie', + data: { + labels: labels, + datasets: [{ + data: values, + backgroundColor: [ + '#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', + '#9966FF', '#FF9F40', '#FF6384', '#C9CBCF' + ] + }] + }, + options: { + responsive: true, + plugins: { + legend: { + position: 'bottom' + } + } + } + }); + } + + function createLineChart(canvasId, label, labels, data, color) { + const ctx = document.getElementById(canvasId).getContext('2d'); + + if (charts[canvasId]) { + charts[canvasId].destroy(); + } + + charts[canvasId] = new Chart(ctx, { + type: 'line', + data: { + labels: labels, + datasets: [{ + label: label, + data: data, + borderColor: color, + backgroundColor: color + '20', + fill: true, + tension: 0.1 + }] + }, + options: { + responsive: true, + scales: { + y: { + beginAtZero: true + } + } + } + }); + } + + function showError(message) { + document.getElementById('loading').style.display = 'none'; + document.getElementById('error').style.display = 'block'; + document.getElementById('error').textContent = message; + } + + // Load dashboard on page load + loadDashboard(); + + // Refresh every 5 minutes + setInterval(loadDashboard, 5 * 60 * 1000); + </script> +</body> +</html>` + + w.Header().Set("Content-Type", "text/html") + w.WriteHeader(http.StatusOK) + w.Write([]byte(html)) +} |
