fix: Complete AgentHealth improvements and build fixes
- Update Update scanner default from 15min to 12 hours (backend) - Add 1 week and 2 week frequency options (frontend) - Rename AgentScanners to AgentHealth component - Add OS-aware package manager badges (APT, DNF, Windows/Winget, Docker) - Fix all build errors (types, imports, storage metrics) - Add useMemo optimization for enabled/auto-run counts
This commit is contained in:
@@ -1,16 +1,14 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/Fimeg/RedFlag/aggregator-server/internal/database/queries"
|
||||
"github.com/Fimeg/RedFlag/aggregator-server/internal/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
// StorageMetricsHandler handles storage metrics endpoints
|
||||
@@ -25,28 +23,21 @@ func NewStorageMetricsHandler(queries *queries.StorageMetricsQueries) *StorageMe
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
// ReportStorageMetrics handles POST /api/v1/agents/:id/storage-metrics
|
||||
func (h *StorageMetricsHandler) ReportStorageMetrics(c *gin.Context) {
|
||||
// Get agent ID from context (set by middleware)
|
||||
agentID := c.MustGet("agent_id").(uuid.UUID)
|
||||
|
||||
// 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)
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate agent ID matches
|
||||
if req.AgentID != agentID {
|
||||
http.Error(w, "Agent ID mismatch", http.StatusBadRequest)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Agent ID mismatch"})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -68,58 +59,34 @@ func (h *StorageMetricsHandler) ReportStorageMetrics(w http.ResponseWriter, r *h
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := h.queries.InsertStorageMetric(r.Context(), dbMetric); err != nil {
|
||||
if err := h.queries.InsertStorageMetric(c.Request.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)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert storage metric"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]string{
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"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)
|
||||
// GetStorageMetrics handles GET /api/v1/agents/:id/storage-metrics
|
||||
func (h *StorageMetricsHandler) GetStorageMetrics(c *gin.Context) {
|
||||
// Get agent ID from context (set by middleware)
|
||||
agentID := c.MustGet("agent_id").(uuid.UUID)
|
||||
|
||||
// Get storage metrics
|
||||
metrics, err := h.queries.GetStorageMetricsByAgentID(r.Context(), agentID, limit, offset)
|
||||
metrics, err := h.queries.GetStorageMetricsByAgentID(c.Request.Context(), agentID, 100, 0)
|
||||
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)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve storage metrics"})
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"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
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/Fimeg/RedFlag/aggregator-server/internal/models"
|
||||
"github.com/google/uuid"
|
||||
@@ -136,7 +135,7 @@ func (q *StorageMetricsQueries) GetLatestStorageMetrics(ctx context.Context, age
|
||||
// GetStorageMetricsSummary returns summary statistics for an agent
|
||||
func (q *StorageMetricsQueries) GetStorageMetricsSummary(ctx context.Context, agentID uuid.UUID) (map[string]interface{}, error) {
|
||||
query := `
|
||||
SELECT
|
||||
SELECT
|
||||
COUNT(*) as total_disks,
|
||||
COUNT(CASE WHEN severity = 'critical' THEN 1 END) as critical_disks,
|
||||
COUNT(CASE WHEN severity = 'important' THEN 1 END) as important_disks,
|
||||
@@ -149,19 +148,38 @@ func (q *StorageMetricsQueries) GetStorageMetricsSummary(ctx context.Context, ag
|
||||
AND created_at >= NOW() - INTERVAL '24 hours'
|
||||
`
|
||||
|
||||
var summary map[string]interface{}
|
||||
var (
|
||||
totalDisks int
|
||||
criticalDisks int
|
||||
importantDisks int
|
||||
avgUsedPercent sql.NullFloat64
|
||||
maxUsedPercent sql.NullFloat64
|
||||
firstCollectedAt sql.NullTime
|
||||
lastCollectedAt sql.NullTime
|
||||
)
|
||||
|
||||
err := q.db.QueryRowContext(ctx, query, agentID).Scan(
|
||||
&summary["total_disks"],
|
||||
&summary["critical_disks"],
|
||||
&summary["important_disks"],
|
||||
&summary["avg_used_percent"],
|
||||
&summary["max_used_percent"],
|
||||
&summary["first_collected_at"],
|
||||
&summary["last_collected_at"],
|
||||
&totalDisks,
|
||||
&criticalDisks,
|
||||
&importantDisks,
|
||||
&avgUsedPercent,
|
||||
&maxUsedPercent,
|
||||
&firstCollectedAt,
|
||||
&lastCollectedAt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get storage metrics summary: %w", err)
|
||||
}
|
||||
|
||||
summary := map[string]interface{}{
|
||||
"total_disks": totalDisks,
|
||||
"critical_disks": criticalDisks,
|
||||
"important_disks": importantDisks,
|
||||
"avg_used_percent": avgUsedPercent.Float64,
|
||||
"max_used_percent": maxUsedPercent.Float64,
|
||||
"first_collected_at": firstCollectedAt.Time,
|
||||
"last_collected_at": lastCollectedAt.Time,
|
||||
}
|
||||
|
||||
return summary, nil
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user