diff --git a/Cargo.lock b/Cargo.lock index fde31ad..51c212e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -281,6 +281,12 @@ dependencies = [ "syn", ] +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "either" version = "1.15.0" @@ -1332,6 +1338,7 @@ dependencies = [ "axum", "chrono", "dashmap", + "dotenvy", "futures", "regex", "reqwest", diff --git a/Cargo.toml b/Cargo.toml index 27323a5..a69a269 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ tokio-stream = "0.1" regex = "1.10" thiserror = "1.0" dashmap = "5.5" +dotenvy = "0.15" [dev-dependencies] tempfile = "3.8" diff --git a/src/systemd/service_generator.rs b/src/systemd/service_generator.rs index f8d7da4..e1115b8 100644 --- a/src/systemd/service_generator.rs +++ b/src/systemd/service_generator.rs @@ -3,6 +3,7 @@ use crate::models::ServiceConfig; use std::fs; use std::path::Path; use std::process::Command; +use std::collections::HashMap; use crate::logger::get_logger; pub struct ServiceGenerator; @@ -74,8 +75,21 @@ impl ServiceGenerator { format!("{} {}", executable, config.script_path) }; - // Generar variables de entorno del usuario - let mut env_lines: Vec = config.environment + // 1. Leer variables del archivo .env si existe + let env_file_vars = Self::read_env_file(&config.working_directory); + + // 2. Merge: .env primero, luego sobrescribir con variables del config + let mut merged_env = env_file_vars.clone(); + for (key, value) in &config.environment { + merged_env.insert(key.clone(), value.clone()); + } + + if !env_file_vars.is_empty() { + logger.info("ServiceGenerator", &format!("đź“„ Usando {} variables desde .env", env_file_vars.len())); + } + + // Generar variables de entorno (desde .env + config manual) + let mut env_lines: Vec = merged_env .iter() .map(|(key, value)| format!("Environment=\"{}={}\"", key, value)) .collect(); @@ -295,4 +309,55 @@ WantedBy=multi-user.target Err(_) => false, } } + + /// Lee el archivo .env del directorio de trabajo y retorna las variables + pub fn read_env_file(working_directory: &str) -> HashMap { + let logger = get_logger(); + let env_path = Path::new(working_directory).join(".env"); + + let mut env_vars = HashMap::new(); + + if !env_path.exists() { + logger.info("ServiceGenerator", &format!("No se encontrĂł archivo .env en: {}", env_path.display())); + return env_vars; + } + + logger.info("ServiceGenerator", &format!("Leyendo archivo .env desde: {}", env_path.display())); + + match fs::read_to_string(&env_path) { + Ok(content) => { + for line in content.lines() { + let line = line.trim(); + + // Ignorar lĂ­neas vacĂ­as y comentarios + if line.is_empty() || line.starts_with('#') { + continue; + } + + // Parsear lĂ­nea KEY=VALUE + if let Some(pos) = line.find('=') { + let key = line[..pos].trim().to_string(); + let mut value = line[pos + 1..].trim().to_string(); + + // Remover comillas simples o dobles + if (value.starts_with('\'') && value.ends_with('\'')) || + (value.starts_with('"') && value.ends_with('"')) { + value = value[1..value.len()-1].to_string(); + } + + if !key.is_empty() { + env_vars.insert(key, value); + } + } + } + + logger.info("ServiceGenerator", &format!("âś… Cargadas {} variables desde .env", env_vars.len())); + } + Err(e) => { + logger.warning("ServiceGenerator", &format!("Error leyendo .env: {}", e), None); + } + } + + env_vars + } }