use async_trait::async_trait; use cm_dashboard_shared::{AgentData, BackupData}; use serde::{Deserialize, Serialize}; use std::fs; use std::path::Path; use tracing::debug; use super::{Collector, CollectorError}; /// Backup collector that reads backup status from JSON files with structured data output pub struct BackupCollector { /// Path to backup status file status_file_path: String, } impl BackupCollector { pub fn new() -> Self { Self { status_file_path: "/var/lib/backup/status.json".to_string(), } } /// Read backup status from JSON file async fn read_backup_status(&self) -> Result, CollectorError> { if !Path::new(&self.status_file_path).exists() { debug!("Backup status file not found: {}", self.status_file_path); return Ok(None); } let content = fs::read_to_string(&self.status_file_path) .map_err(|e| CollectorError::SystemRead { path: self.status_file_path.clone(), error: e.to_string(), })?; let status: BackupStatus = serde_json::from_str(&content) .map_err(|e| CollectorError::Parse { value: content.clone(), error: format!("Failed to parse backup status JSON: {}", e), })?; Ok(Some(status)) } /// Convert BackupStatus to BackupData and populate AgentData async fn populate_backup_data(&self, agent_data: &mut AgentData) -> Result<(), CollectorError> { if let Some(backup_status) = self.read_backup_status().await? { let backup_data = BackupData { status: backup_status.status, last_run: Some(backup_status.last_run), next_scheduled: Some(backup_status.next_scheduled), total_size_gb: Some(backup_status.total_size_gb), repository_health: Some(backup_status.repository_health), }; agent_data.backup = backup_data; } else { // No backup status available - set default values agent_data.backup = BackupData { status: "unavailable".to_string(), last_run: None, next_scheduled: None, total_size_gb: None, repository_health: None, }; } Ok(()) } } #[async_trait] impl Collector for BackupCollector { async fn collect_structured(&self, agent_data: &mut AgentData) -> Result<(), CollectorError> { debug!("Collecting backup status"); self.populate_backup_data(agent_data).await } } /// Backup status structure from JSON file #[derive(Debug, Clone, Serialize, Deserialize)] struct BackupStatus { pub status: String, // "completed", "running", "failed", etc. pub last_run: u64, // Unix timestamp pub next_scheduled: u64, // Unix timestamp pub total_size_gb: f32, // Total backup size in GB pub repository_health: String, // "ok", "warning", "error" }