Dorian 30ed48ad1b Enhance Docker integration and API for container management
- Implemented Docker container scanning and periodic updates in the Server initialization.
- Added new RPC endpoints for managing Docker containers, including start, stop, and restart functionalities.
- Updated the API to handle package management for Docker-based applications.
- Improved environment variable handling for user-specific configurations in Podman and Docker clients.
- Enhanced the development startup script to include Docker container management and provide clearer instructions for full stack setup.
2026-01-27 23:21:26 +00:00

125 lines
4.0 KiB
Rust

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<ApiHandler>,
state_manager: Arc<StateManager>,
}
impl Server {
pub async fn new(config: Config) -> Result<Self> {
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<DockerPackageScanner> {
let user = std::env::var("USER").unwrap_or_else(|_| "archipelago".to_string());
let runtime: Arc<dyn archipelago_container::ContainerRuntime> = 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(())
}