You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							274 lines
						
					
					
						
							9.0 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							274 lines
						
					
					
						
							9.0 KiB
						
					
					
				| 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">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 || {}); | |
|              | |
|  | |
|              | |
|             // 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)) | |
| }
 |