package queries import ( "context" "database/sql" "fmt" "github.com/Fimeg/RedFlag/aggregator-server/internal/models" "github.com/google/uuid" ) // StorageMetricsQueries handles storage metrics database operations type StorageMetricsQueries struct { db *sql.DB } // NewStorageMetricsQueries creates a new storage metrics queries instance func NewStorageMetricsQueries(db *sql.DB) *StorageMetricsQueries { return &StorageMetricsQueries{db: db} } // InsertStorageMetric inserts a new storage metric func (q *StorageMetricsQueries) InsertStorageMetric(ctx context.Context, metric models.StorageMetric) error { query := ` INSERT INTO storage_metrics ( id, agent_id, mountpoint, device, disk_type, filesystem, total_bytes, used_bytes, available_bytes, used_percent, severity, metadata, created_at ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) ` _, err := q.db.ExecContext(ctx, query, metric.ID, metric.AgentID, metric.Mountpoint, metric.Device, metric.DiskType, metric.Filesystem, metric.TotalBytes, metric.UsedBytes, metric.AvailableBytes, metric.UsedPercent, metric.Severity, metric.Metadata, metric.CreatedAt, ) if err != nil { return fmt.Errorf("failed to insert storage metric: %w", err) } return nil } // GetStorageMetricsByAgentID retrieves storage metrics for an agent func (q *StorageMetricsQueries) GetStorageMetricsByAgentID(ctx context.Context, agentID uuid.UUID, limit, offset int) ([]models.StorageMetric, error) { query := ` SELECT id, agent_id, mountpoint, device, disk_type, filesystem, total_bytes, used_bytes, available_bytes, used_percent, severity, metadata, created_at FROM storage_metrics WHERE agent_id = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3 ` rows, err := q.db.QueryContext(ctx, query, agentID, limit, offset) if err != nil { return nil, fmt.Errorf("failed to query storage metrics: %w", err) } defer rows.Close() var metrics []models.StorageMetric for rows.Next() { var metric models.StorageMetric var metadataMap map[string]interface{} err := rows.Scan( &metric.ID, &metric.AgentID, &metric.Mountpoint, &metric.Device, &metric.DiskType, &metric.Filesystem, &metric.TotalBytes, &metric.UsedBytes, &metric.AvailableBytes, &metric.UsedPercent, &metric.Severity, &metadataMap, &metric.CreatedAt, ) if err != nil { return nil, fmt.Errorf("failed to scan storage metric: %w", err) } metric.Metadata = metadataMap metrics = append(metrics, metric) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("error iterating storage metrics: %w", err) } return metrics, nil } // GetLatestStorageMetrics retrieves the most recent storage metrics per mountpoint func (q *StorageMetricsQueries) GetLatestStorageMetrics(ctx context.Context, agentID uuid.UUID) ([]models.StorageMetric, error) { query := ` SELECT DISTINCT ON (mountpoint) id, agent_id, mountpoint, device, disk_type, filesystem, total_bytes, used_bytes, available_bytes, used_percent, severity, metadata, created_at FROM storage_metrics WHERE agent_id = $1 ORDER BY mountpoint, created_at DESC ` rows, err := q.db.QueryContext(ctx, query, agentID) if err != nil { return nil, fmt.Errorf("failed to query latest storage metrics: %w", err) } defer rows.Close() var metrics []models.StorageMetric for rows.Next() { var metric models.StorageMetric var metadataMap map[string]interface{} err := rows.Scan( &metric.ID, &metric.AgentID, &metric.Mountpoint, &metric.Device, &metric.DiskType, &metric.Filesystem, &metric.TotalBytes, &metric.UsedBytes, &metric.AvailableBytes, &metric.UsedPercent, &metric.Severity, &metadataMap, &metric.CreatedAt, ) if err != nil { return nil, fmt.Errorf("failed to scan storage metric: %w", err) } metric.Metadata = metadataMap metrics = append(metrics, metric) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("error iterating latest storage metrics: %w", err) } return metrics, nil } // GetStorageMetricsSummary returns summary statistics for an agent func (q *StorageMetricsQueries) GetStorageMetricsSummary(ctx context.Context, agentID uuid.UUID) (map[string]interface{}, error) { query := ` 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, AVG(used_percent) as avg_used_percent, MAX(used_percent) as max_used_percent, MIN(created_at) as first_collected_at, MAX(created_at) as last_collected_at FROM storage_metrics WHERE agent_id = $1 AND created_at >= NOW() - INTERVAL '24 hours' ` 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( &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 }