package monitoring import ( "encoding/json" "fmt" "html/template" "net/http" "strconv" "time" "github.com/fraktal/mev-beta/internal/logger" ) // DashboardServer provides a web-based monitoring dashboard type DashboardServer struct { logger *logger.Logger integrityMonitor *IntegrityMonitor healthChecker *HealthCheckRunner port int server *http.Server } // NewDashboardServer creates a new dashboard server func NewDashboardServer(logger *logger.Logger, integrityMonitor *IntegrityMonitor, healthChecker *HealthCheckRunner, port int) *DashboardServer { return &DashboardServer{ logger: logger, integrityMonitor: integrityMonitor, healthChecker: healthChecker, port: port, } } // Start starts the dashboard HTTP server func (ds *DashboardServer) Start() error { mux := http.NewServeMux() // Register endpoints mux.HandleFunc("/", ds.handleDashboard) mux.HandleFunc("/api/health", ds.handleAPIHealth) mux.HandleFunc("/api/metrics", ds.handleAPIMetrics) mux.HandleFunc("/api/history", ds.handleAPIHistory) mux.HandleFunc("/api/alerts", ds.handleAPIAlerts) mux.HandleFunc("/static/", ds.handleStatic) ds.server = &http.Server{ Addr: fmt.Sprintf(":%d", ds.port), Handler: mux, } ds.logger.Info("Starting monitoring dashboard", "port", ds.port, "url", fmt.Sprintf("http://localhost:%d", ds.port)) return ds.server.ListenAndServe() } // Stop stops the dashboard server func (ds *DashboardServer) Stop() error { if ds.server != nil { return ds.server.Close() } return nil } // handleDashboard serves the main dashboard HTML page func (ds *DashboardServer) handleDashboard(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") // Get current metrics and health data metrics := ds.integrityMonitor.GetMetrics() healthSummary := ds.integrityMonitor.GetHealthSummary() healthHistory := ds.healthChecker.GetRecentSnapshots(20) // Render dashboard template tmpl := ds.getDashboardTemplate() data := struct { Metrics MetricsSnapshot HealthSummary map[string]interface{} HealthHistory []HealthSnapshot Timestamp time.Time }{ Metrics: metrics, HealthSummary: healthSummary, HealthHistory: healthHistory, Timestamp: time.Now(), } if err := tmpl.Execute(w, data); err != nil { ds.logger.Error("Failed to render dashboard", "error", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } } // handleAPIHealth provides JSON health endpoint func (ds *DashboardServer) handleAPIHealth(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") healthSummary := ds.integrityMonitor.GetHealthSummary() healthCheckerSummary := ds.healthChecker.GetHealthSummary() // Combine summaries response := map[string]interface{}{ "integrity_monitor": healthSummary, "health_checker": healthCheckerSummary, "timestamp": time.Now(), } if err := json.NewEncoder(w).Encode(response); err != nil { ds.logger.Error("Failed to encode health response", "error", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } } // handleAPIMetrics provides JSON metrics endpoint func (ds *DashboardServer) handleAPIMetrics(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") metrics := ds.integrityMonitor.GetMetrics() if err := json.NewEncoder(w).Encode(metrics); err != nil { ds.logger.Error("Failed to encode metrics response", "error", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } } // handleAPIHistory provides JSON health history endpoint func (ds *DashboardServer) handleAPIHistory(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") // Get count parameter (default 20) countStr := r.URL.Query().Get("count") count := 20 if countStr != "" { if c, err := strconv.Atoi(countStr); err == nil && c > 0 && c <= 100 { count = c } } history := ds.healthChecker.GetRecentSnapshots(count) if err := json.NewEncoder(w).Encode(history); err != nil { ds.logger.Error("Failed to encode history response", "error", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } } // handleAPIAlerts provides recent alerts for integrity and health monitoring. func (ds *DashboardServer) handleAPIAlerts(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") limit := 20 if q := r.URL.Query().Get("limit"); q != "" { if parsed, err := strconv.Atoi(q); err == nil && parsed > 0 && parsed <= 200 { limit = parsed } } alerts := ds.integrityMonitor.GetRecentAlerts(limit) payload := map[string]interface{}{ "alerts": alerts, "count": len(alerts), "timestamp": time.Now(), } if err := json.NewEncoder(w).Encode(payload); err != nil { ds.logger.Error("Failed to encode alerts response", "error", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } } // handleStatic serves static assets (CSS, JS) func (ds *DashboardServer) handleStatic(w http.ResponseWriter, r *http.Request) { // For simplicity, we'll inline CSS and JS in the HTML template // In a production system, you'd serve actual static files http.NotFound(w, r) } // getDashboardTemplate returns the HTML template for the dashboard func (ds *DashboardServer) getDashboardTemplate() *template.Template { htmlTemplate := `
Real-time monitoring of corruption detection and recovery systems