package monitoring import ( "net/http" "strconv" "time" "github.com/atlasos/calypso/internal/common/database" "github.com/atlasos/calypso/internal/common/logger" "github.com/atlasos/calypso/internal/iam" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" ) // Handler handles monitoring API requests type Handler struct { alertService *AlertService metricsService *MetricsService eventHub *EventHub db *database.DB logger *logger.Logger } // NewHandler creates a new monitoring handler func NewHandler(db *database.DB, log *logger.Logger, alertService *AlertService, metricsService *MetricsService, eventHub *EventHub) *Handler { return &Handler{ alertService: alertService, metricsService: metricsService, eventHub: eventHub, db: db, logger: log, } } // ListAlerts lists alerts with optional filters func (h *Handler) ListAlerts(c *gin.Context) { filters := &AlertFilters{} // Parse query parameters if severity := c.Query("severity"); severity != "" { filters.Severity = AlertSeverity(severity) } if source := c.Query("source"); source != "" { filters.Source = AlertSource(source) } if acknowledged := c.Query("acknowledged"); acknowledged != "" { ack, err := strconv.ParseBool(acknowledged) if err == nil { filters.IsAcknowledged = &ack } } if resourceType := c.Query("resource_type"); resourceType != "" { filters.ResourceType = resourceType } if resourceID := c.Query("resource_id"); resourceID != "" { filters.ResourceID = resourceID } if limitStr := c.Query("limit"); limitStr != "" { if limit, err := strconv.Atoi(limitStr); err == nil && limit > 0 { filters.Limit = limit } } alerts, err := h.alertService.ListAlerts(c.Request.Context(), filters) if err != nil { h.logger.Error("Failed to list alerts", "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list alerts"}) return } c.JSON(http.StatusOK, gin.H{"alerts": alerts}) } // GetAlert retrieves a single alert func (h *Handler) GetAlert(c *gin.Context) { alertID := c.Param("id") alert, err := h.alertService.GetAlert(c.Request.Context(), alertID) if err != nil { h.logger.Error("Failed to get alert", "alert_id", alertID, "error", err) c.JSON(http.StatusNotFound, gin.H{"error": "alert not found"}) return } c.JSON(http.StatusOK, alert) } // AcknowledgeAlert acknowledges an alert func (h *Handler) AcknowledgeAlert(c *gin.Context) { alertID := c.Param("id") // Get current user user, exists := c.Get("user") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) return } authUser, ok := user.(*iam.User) if !ok { c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user context"}) return } if err := h.alertService.AcknowledgeAlert(c.Request.Context(), alertID, authUser.ID); err != nil { h.logger.Error("Failed to acknowledge alert", "alert_id", alertID, "error", err) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "alert acknowledged"}) } // ResolveAlert resolves an alert func (h *Handler) ResolveAlert(c *gin.Context) { alertID := c.Param("id") if err := h.alertService.ResolveAlert(c.Request.Context(), alertID); err != nil { h.logger.Error("Failed to resolve alert", "alert_id", alertID, "error", err) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "alert resolved"}) } // GetMetrics retrieves current system metrics func (h *Handler) GetMetrics(c *gin.Context) { metrics, err := h.metricsService.CollectMetrics(c.Request.Context()) if err != nil { h.logger.Error("Failed to collect metrics", "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to collect metrics"}) return } c.JSON(http.StatusOK, metrics) } // WebSocketHandler handles WebSocket connections for event streaming func (h *Handler) WebSocketHandler(c *gin.Context) { // Upgrade connection to WebSocket upgrader := websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { // Allow all origins for now (should be restricted in production) return true }, } conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) if err != nil { h.logger.Error("Failed to upgrade WebSocket connection", "error", err) return } // Register client h.eventHub.register <- conn // Keep connection alive and handle ping/pong go func() { defer func() { h.eventHub.unregister <- conn }() conn.SetReadDeadline(time.Now().Add(60 * time.Second)) conn.SetPongHandler(func(string) error { conn.SetReadDeadline(time.Now().Add(60 * time.Second)) return nil }) // Send ping every 30 seconds ticker := time.NewTicker(30 * time.Second) defer ticker.Stop() for { select { case <-ticker.C: if err := conn.WriteMessage(websocket.PingMessage, nil); err != nil { return } } } }() }