use deployer_types::NodeInfo; use std::collections::HashMap; use tokio::sync::{Mutex, RwLock}; use tracing::{info, warn}; use crate::config::Config; use crate::local_storage::LocalStorage; use crate::storage::NodeStorage; /// Application state shared across handlers pub struct AppState { /// Server configuration pub config: Config, /// ChainFire-backed storage (when available) pub storage: Option>, /// Local file-backed storage (when configured) pub local_storage: Option>, /// Fallback in-memory node registry /// Key: node_id, Value: NodeInfo pub nodes: RwLock>, /// Fallback in-memory machine_id → (node_id, NodeConfig) mapping pub machine_configs: RwLock>, } impl AppState { /// Create new application state with default config pub fn new() -> Self { Self::with_config(Config::default()) } /// Create application state with custom config pub fn with_config(config: Config) -> Self { Self { config, storage: None, local_storage: None, nodes: RwLock::new(HashMap::new()), machine_configs: RwLock::new(HashMap::new()), } } /// Initialize ChainFire storage connection pub async fn init_storage(&mut self) -> anyhow::Result<()> { if let Some(path) = self.config.local_state_path.clone() { match LocalStorage::open(path) { Ok(storage) => { info!("Local storage initialized for Deployer bootstrapper"); self.local_storage = Some(Mutex::new(storage)); } Err(e) => { warn!(error = %e, "Failed to initialize local storage"); } } } if self.config.chainfire.endpoints.is_empty() { if self.config.require_chainfire { return Err(anyhow::anyhow!( "No ChainFire endpoints configured while require_chainfire=true" )); } warn!("No ChainFire endpoints configured, using in-memory storage"); return Ok(()); } let namespace = &self.config.chainfire.namespace; for endpoint in &self.config.chainfire.endpoints { match NodeStorage::connect(endpoint, namespace).await { Ok(storage) => { info!( endpoint = %endpoint, namespace = %namespace, "Connected to ChainFire storage" ); self.storage = Some(Mutex::new(storage)); return Ok(()); } Err(e) => { warn!( endpoint = %endpoint, error = %e, "Failed to connect to ChainFire endpoint" ); } } } if self.config.require_chainfire { Err(anyhow::anyhow!( "Failed to connect to any ChainFire endpoints and require_chainfire=true" )) } else { warn!("Failed to connect to any ChainFire endpoints, using in-memory storage"); Ok(()) } } /// Check if ChainFire storage is available pub fn has_storage(&self) -> bool { self.storage.is_some() } /// Check if local storage is available pub fn has_local_storage(&self) -> bool { self.local_storage.is_some() } } impl Default for AppState { fn default() -> Self { Self::new() } }