use crate::api::ApiHandler; use crate::config::{Config, ContainerRuntime}; use crate::container::DockerPackageScanner; use crate::state::StateManager; use anyhow::Result; use hyper::server::conn::Http; use hyper::service::service_fn; use std::net::SocketAddr; use std::sync::Arc; use std::time::Duration; use tokio::net::TcpListener; use tracing::{error, info}; pub struct Server { _config: Config, api_handler: Arc, state_manager: Arc, } impl Server { pub async fn new(config: Config) -> Result { let state_manager = Arc::new(StateManager::new()); let api_handler = Arc::new(ApiHandler::new(config.clone(), state_manager.clone()).await?); // Initialize Docker scanner if in dev mode if config.dev_mode { let scanner = create_docker_scanner(&config).await?; let state = state_manager.clone(); // Initial scan tokio::spawn(async move { info!("🐳 Scanning Docker containers..."); if let Err(e) = scan_and_update_packages(&scanner, &state).await { error!("Failed to scan Docker containers: {}", e); } // Periodic scan every 5 seconds let mut interval = tokio::time::interval(Duration::from_secs(5)); loop { interval.tick().await; if let Err(e) = scan_and_update_packages(&scanner, &state).await { error!("Failed to update Docker containers: {}", e); } } }); } Ok(Self { _config: config, api_handler, state_manager, }) } pub async fn serve(&self, addr: SocketAddr) -> Result<()> { let listener = TcpListener::bind(addr).await?; loop { let (stream, peer_addr) = match listener.accept().await { Ok(conn) => conn, Err(e) => { error!("Failed to accept connection: {}", e); continue; } }; let handler = self.api_handler.clone(); tokio::spawn(async move { let service = service_fn(move |req| { let handler = handler.clone(); async move { handler.handle_request(req).await .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("{}", e))) } }); if let Err(e) = Http::new() .serve_connection(stream, service) .with_upgrades() .await { error!("Error serving connection from {}: {}", peer_addr, e); } }); } } } async fn create_docker_scanner(config: &Config) -> Result { let user = std::env::var("USER").unwrap_or_else(|_| "archipelago".to_string()); let runtime: Arc = match &config.container_runtime { ContainerRuntime::Podman => { Arc::new(archipelago_container::PodmanRuntime::new(user.clone())) } ContainerRuntime::Docker => { Arc::new(archipelago_container::DockerRuntime::new(user.clone())) } ContainerRuntime::Auto => { Arc::new( archipelago_container::AutoRuntime::new(user.clone()) .await? ) } }; Ok(DockerPackageScanner::new(runtime)) } async fn scan_and_update_packages( scanner: &DockerPackageScanner, state: &StateManager, ) -> Result<()> { let packages = scanner.scan_containers().await?; if !packages.is_empty() { let (mut data, _) = state.get_snapshot().await; data.package_data = packages; state.update_data(data).await; } Ok(()) }