use path_clean::PathClean; use std::path::{Path, PathBuf}; use tokio::signal::unix::{SignalKind, signal}; use crate::{ API_CONFIG_PATH, DB_CONFIG_PATH, containers::{ AgentConfigInfoType, ConfigInfoType, DBConfigInfoType, WithContainer, WithoutContainer, agent::SOCK_NAME, }, env::{self, EnvFile}, }; // relative to the current working directory pub fn to_absolute_path(path: &str) -> PathBuf { if Path::new(path).is_absolute() { return PathBuf::from(path); } std::env::current_dir() .map(|cwd| cwd.join(path)) .unwrap_or_else(|_| PathBuf::from(path)) .clean() } pub fn write_env_files(db_config: &DBConfigInfoType, agent_config: &Option) { let api_config_path_absolute = to_absolute_path(API_CONFIG_PATH); let db_config_path_absolute = to_absolute_path(DB_CONFIG_PATH); let (db_type, db_url) = match db_config { DBConfigInfoType::Containerized(config) => (config.db_type.clone(), config.url.clone()), DBConfigInfoType::PreExisting(config) => (config.db_type.clone(), config.url.clone()), }; let mut api_env = EnvFile::new(env::EnvFileType::Yaml); api_env.write_line("DATABASE__TYPE", db_type.to_string().as_str()); api_env.write_line("DATABASE__URL", db_url.as_str()); let mut db_env = api_env.clone(); db_env.file_type = env::EnvFileType::DotEnv; // agent related env vars if let Some(agent) = agent_config && let ConfigInfoType::Containerized(agent) = agent { api_env.write_line( "AGENT__SOCK__PATH", format!("{}/{}", &agent.config.agent_config.sock_folder, SOCK_NAME).as_str(), ); api_env.write_line( "AGENT__NGINX__CONFIG__DIR", &agent.config.agent_config.nginx_config_dir, ); } let mut api_file = std::fs::File::create(&api_config_path_absolute).expect("Failed to create API config file"); let mut db_file = std::fs::File::create(&db_config_path_absolute).expect("Failed to create DB config file"); api_env.write(&mut api_file, true); db_env.write(&mut db_file, false); } pub async fn stop_container( config: &ConfigInfoType, config_name: String, ) { match config { ConfigInfoType::Containerized(container_info) => { let container = container_info.container(); if let Err(e) = container.stop().await { eprintln!( "Failed to stop container: {}. With error: {}", config_name, e ); } else { println!("Container {} stopped successfully.", config_name); } } ConfigInfoType::PreExisting(pre_existing_info) => { pre_existing_info.on_delete(); println!( "Pre-existing resource for {} cleaned up successfully.", config_name ); } } } pub fn remove_file_if_exists(path: &str) { let path = std::path::Path::new(path); if path.exists() { if let Err(e) = std::fs::remove_file(path) { eprintln!("Failed to remove file {}: {}", path.display(), e); } else { println!("Removed existing file: {}", path.display()); } } } pub async fn await_termination_signal() { tokio::select! { _ = tokio::signal::ctrl_c() => { println!("\nReceived Ctrl+C, stopping container..."); } _ = async { #[cfg(unix)] { let mut sigterm = signal(SignalKind::terminate()).expect("Failed to register SIGTERM handler"); sigterm.recv().await; println!("\nReceived SIGTERM, stopping container..."); } #[cfg(not(unix))] { // On non-Unix systems, just wait indefinitely std::future::pending::<()>().await; } } => {} _ = async { #[cfg(unix)] { let mut sigquit = signal(SignalKind::quit()).expect("Failed to register SIGQUIT handler"); sigquit.recv().await; println!("\nReceived SIGQUIT, stopping container..."); } #[cfg(not(unix))] { // On non-Unix systems, just wait indefinitely std::future::pending::<()>().await; } } => {} } }