aboutsummaryrefslogtreecommitdiff
path: root/telemetry/server/dashboard/dashboard.go
diff options
context:
space:
mode:
Diffstat (limited to 'telemetry/server/dashboard/dashboard.go')
-rw-r--r--telemetry/server/dashboard/dashboard.go278
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))
+}