package queries import ( "context" "database/sql" "fmt" "time" "github.com/Fimeg/RedFlag/aggregator-server/internal/models" "github.com/google/uuid" "github.com/lib/pq" ) // 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, pq.Array(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 summary map[string]interface{} 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"], ) if err != nil { return nil, fmt.Errorf("failed to get storage metrics summary: %w", err) } return summary, nil }