package handlers import ( "encoding/json" "fmt" "net/http" "time" "github.com/Fimeg/RedFlag/aggregator-server/internal/database/queries" "github.com/Fimeg/RedFlag/aggregator-server/internal/models" "github.com/google/uuid" "github.com/gorilla/mux" "github.com/lib/pq" ) // StorageMetricsHandler handles storage metrics endpoints type StorageMetricsHandler struct { queries *queries.StorageMetricsQueries } // NewStorageMetricsHandler creates a new storage metrics handler func NewStorageMetricsHandler(queries *queries.StorageMetricsQueries) *StorageMetricsHandler { return &StorageMetricsHandler{ queries: queries, } } // ReportStorageMetrics handles POST /api/v1/agents/{id}/storage-metrics func (h *StorageMetricsHandler) ReportStorageMetrics(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) agentIDStr := vars["id"] // Parse agent ID agentID, err := uuid.Parse(agentIDStr) if err != nil { http.Error(w, "Invalid agent ID", http.StatusBadRequest) return } // Parse request body var req models.StorageMetricRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } // Validate agent ID matches if req.AgentID != agentID { http.Error(w, "Agent ID mismatch", http.StatusBadRequest) return } // Insert storage metrics with error isolation for _, metric := range req.Metrics { dbMetric := models.StorageMetric{ ID: uuid.New(), AgentID: req.AgentID, Mountpoint: metric.Mountpoint, Device: metric.Device, DiskType: metric.DiskType, Filesystem: metric.Filesystem, TotalBytes: metric.TotalBytes, UsedBytes: metric.UsedBytes, AvailableBytes: metric.AvailableBytes, UsedPercent: metric.UsedPercent, Severity: metric.Severity, Metadata: metric.Metadata, CreatedAt: time.Now(), } if err := h.queries.InsertStorageMetric(r.Context(), dbMetric); err != nil { log.Printf("[ERROR] Failed to insert storage metric for agent %s: %v\n", agentID, err) http.Error(w, "Failed to insert storage metric", http.StatusInternalServerError) return } } w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(map[string]string{ "status": "success", "message": "Storage metrics reported successfully", }) } // GetStorageMetrics handles GET /api/v1/agents/{id}/storage-metrics func (h *StorageMetricsHandler) GetStorageMetrics(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) agentIDStr := vars["id"] // Parse agent ID agentID, err := uuid.Parse(agentIDStr) if err != nil { http.Error(w, "Invalid agent ID", http.StatusBadRequest) return } // Optional query parameters for pagination/limit limit := parseIntQueryParam(r, "limit", 100) offset := parseIntQueryParam(r, "offset", 0) // Get storage metrics metrics, err := h.queries.GetStorageMetricsByAgentID(r.Context(), agentID, limit, offset) if err != nil { log.Printf("[ERROR] Failed to retrieve storage metrics for agent %s: %v\n", agentID, err) http.Error(w, "Failed to retrieve storage metrics", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "metrics": metrics, "total": len(metrics), }) } // parseIntQueryParam safely parses integer query parameters with defaults func parseIntQueryParam(r *http.Request, key string, defaultValue int) int { if val := r.URL.Query().Get(key); val != "" { var result int if _, err := fmt.Sscanf(val, "%d", &result); err == nil && result > 0 { return result } } return defaultValue }