use anyhow::Result; use serde::{Deserialize, Serialize}; use std::path::Path; /// Main dashboard configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DashboardConfig { pub zmq: ZmqConfig, pub hosts: std::collections::HashMap, pub system: SystemConfig, pub ssh: SshConfig, pub service_logs: std::collections::HashMap>, } /// ZMQ consumer configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ZmqConfig { pub subscriber_ports: Vec, /// Heartbeat timeout in seconds - hosts considered offline if no heartbeat received within this time #[serde(default = "default_heartbeat_timeout_seconds")] pub heartbeat_timeout_seconds: u64, } fn default_heartbeat_timeout_seconds() -> u64 { 10 // Default to 10 seconds - allows for multiple missed heartbeats } /// Individual host configuration details #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HostDetails { pub mac_address: Option, /// Primary IP address (local network) pub ip: Option, } impl HostDetails { /// Get the IP address for connection (uses ip field or hostname as fallback) pub fn get_connection_ip(&self, hostname: &str) -> String { self.ip.as_ref().unwrap_or(&hostname.to_string()).clone() } } /// System configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SystemConfig { pub nixos_config_git_url: String, pub nixos_config_branch: String, pub nixos_config_working_dir: String, pub nixos_config_api_key_file: Option, } /// SSH configuration for rebuild and backup operations #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SshConfig { pub rebuild_user: String, pub rebuild_cmd: String, pub service_manage_cmd: String, } /// Service log file configuration per host #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ServiceLogConfig { pub service_name: String, pub log_file_path: String, } impl DashboardConfig { pub fn load_from_file>(path: P) -> Result { let path = path.as_ref(); let content = std::fs::read_to_string(path)?; let config: DashboardConfig = toml::from_str(&content)?; Ok(config) } } impl Default for DashboardConfig { fn default() -> Self { panic!("Dashboard configuration must be loaded from file - no hardcoded defaults allowed") } } impl Default for ZmqConfig { fn default() -> Self { panic!("Dashboard configuration must be loaded from file - no hardcoded defaults allowed") } }