use serde::{Deserialize, Serialize}; use crate::Status; /// Complete structured data from an agent #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AgentData { pub hostname: String, pub agent_version: String, pub build_version: Option, pub timestamp: u64, pub system: SystemData, pub services: Vec, pub backup: BackupData, } /// System-level monitoring data #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SystemData { pub network: NetworkData, pub cpu: CpuData, pub memory: MemoryData, pub storage: StorageData, } /// Network interface monitoring data #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NetworkData { pub interfaces: Vec, } /// Individual network interface data #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NetworkInterfaceData { pub name: String, pub ipv4_addresses: Vec, pub ipv6_addresses: Vec, pub is_physical: bool, pub link_status: Status, pub parent_interface: Option, pub vlan_id: Option, pub connection_method: Option, // For Tailscale: "direct", "relay", or "proxy" } /// CPU C-state usage information #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CStateInfo { pub name: String, pub percent: f32, } /// CPU monitoring data #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CpuData { pub load_1min: f32, pub load_5min: f32, pub load_15min: f32, pub cstates: Vec, // C-state usage percentages (C1, C6, C10, etc.) - indicates CPU idle depth distribution pub temperature_celsius: Option, pub load_status: Status, pub temperature_status: Status, // Static CPU information (collected once at startup) #[serde(skip_serializing_if = "Option::is_none")] pub model_name: Option, #[serde(skip_serializing_if = "Option::is_none")] pub core_count: Option, } /// Memory monitoring data #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MemoryData { pub usage_percent: f32, pub total_gb: f32, pub used_gb: f32, pub available_gb: f32, pub swap_total_gb: f32, pub swap_used_gb: f32, pub tmpfs: Vec, pub usage_status: Status, } /// Tmpfs filesystem data #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TmpfsData { pub mount: String, pub usage_percent: f32, pub used_gb: f32, pub total_gb: f32, } /// Storage monitoring data #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StorageData { pub drives: Vec, pub pools: Vec, } /// Individual drive data #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DriveData { pub name: String, pub serial_number: Option, pub health: String, pub temperature_celsius: Option, pub wear_percent: Option, pub filesystems: Vec, pub temperature_status: Status, pub health_status: Status, } /// Filesystem on a drive #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FilesystemData { pub mount: String, pub usage_percent: f32, pub used_gb: f32, pub total_gb: f32, pub usage_status: Status, } /// Storage pool (MergerFS, RAID, etc.) #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PoolData { pub name: String, pub mount: String, pub pool_type: String, // "mergerfs", "raid", etc. pub health: String, pub usage_percent: f32, pub used_gb: f32, pub total_gb: f32, pub data_drives: Vec, pub parity_drives: Vec, pub health_status: Status, pub usage_status: Status, } /// Drive in a storage pool #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PoolDriveData { pub name: String, pub serial_number: Option, pub temperature_celsius: Option, pub wear_percent: Option, pub health: String, pub health_status: Status, pub temperature_status: Status, } /// Service monitoring data #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ServiceData { pub name: String, pub user_stopped: bool, pub service_status: Status, pub sub_services: Vec, /// Memory usage in bytes (from MemoryCurrent) pub memory_bytes: Option, /// Number of service restarts (from NRestarts) pub restart_count: Option, /// Uptime in seconds (calculated from ExecMainStartTimestamp) pub uptime_seconds: Option, } /// Sub-service data (nginx sites, docker containers, etc.) #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SubServiceData { pub name: String, pub service_status: Status, pub metrics: Vec, /// Type of sub-service: "nginx_site", "container", "image" #[serde(default)] pub service_type: String, } /// Individual metric for a sub-service #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SubServiceMetric { pub label: String, pub value: f32, pub unit: Option, } /// Backup system data #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BackupData { pub repositories: Vec, pub repository_status: Status, pub disks: Vec, } /// Backup repository disk information #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BackupDiskData { pub serial: String, pub product_name: Option, pub wear_percent: Option, pub temperature_celsius: Option, pub last_backup_time: Option, pub backup_status: Status, pub disk_usage_percent: f32, pub disk_used_gb: f32, pub disk_total_gb: f32, pub usage_status: Status, pub services: Vec, pub archives_min: i64, pub archives_max: i64, } impl AgentData { /// Create new agent data with current timestamp pub fn new(hostname: String, agent_version: String) -> Self { Self { hostname, agent_version, build_version: None, timestamp: chrono::Utc::now().timestamp() as u64, system: SystemData { network: NetworkData { interfaces: Vec::new(), }, cpu: CpuData { load_1min: 0.0, load_5min: 0.0, load_15min: 0.0, cstates: Vec::new(), temperature_celsius: None, load_status: Status::Unknown, temperature_status: Status::Unknown, model_name: None, core_count: None, }, memory: MemoryData { usage_percent: 0.0, total_gb: 0.0, used_gb: 0.0, available_gb: 0.0, swap_total_gb: 0.0, swap_used_gb: 0.0, tmpfs: Vec::new(), usage_status: Status::Unknown, }, storage: StorageData { drives: Vec::new(), pools: Vec::new(), }, }, services: Vec::new(), backup: BackupData { repositories: Vec::new(), repository_status: Status::Unknown, disks: Vec::new(), }, } } }