From 058e4781e688439b2fe4774ffa23e6d5a2c3f0be Mon Sep 17 00:00:00 2001 From: pablinux Date: Wed, 21 Jan 2026 21:48:59 -0500 Subject: [PATCH] feat: Sistema de variables de entorno mejorado con EnvironmentFile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Agregado campo 'environment' a MonitoredApp para almacenar variables ADICIONALES - Solo se almacenan en JSON las variables agregadas manualmente desde el panel - Las variables del .env del proyecto se cargan automáticamente con EnvironmentFile - Modificado service_generator.rs para usar directiva EnvironmentFile en systemd - Fix: Usuario ahora se lee correctamente del JSON sin fallback a 'root' - Edit.html pre-carga variables adicionales del JSON al editar - Separación clara: .env (proyecto) vs variables adicionales (JSON) - Transparencia total con .env nativo del proyecto Beneficios: ✅ No duplicación de variables (.env es la fuente de verdad) ✅ JSON solo guarda variables extras (pequeño y limpio) ✅ .env funciona igual que en desarrollo ✅ systemd lee .env con EnvironmentFile ✅ Variables adicionales se persisten en JSON ✅ Al editar, se pre-cargan variables adicionales guardadas --- src/api/handlers.rs | 1 + src/config.rs | 7 +++++ src/discovery.rs | 1 + src/orchestrator/app_manager.rs | 1 + src/systemd/service_generator.rs | 50 +++++++++++++++++++------------- web/edit.html | 32 +++++++++++++++++--- 6 files changed, 68 insertions(+), 24 deletions(-) diff --git a/src/api/handlers.rs b/src/api/handlers.rs index 11af022..7d5c39c 100644 --- a/src/api/handlers.rs +++ b/src/api/handlers.rs @@ -176,6 +176,7 @@ pub async fn update_app_handler( deleted: false, deleted_at: None, deleted_reason: None, + environment: config.environment.clone(), systemd_service: None, created_at: None, }; diff --git a/src/config.rs b/src/config.rs index 3c11d4f..e902e35 100644 --- a/src/config.rs +++ b/src/config.rs @@ -57,6 +57,12 @@ pub struct MonitoredApp { #[serde(skip_serializing_if = "Option::is_none")] pub deleted_reason: Option, + // --- VARIABLES DE ENTORNO ADICIONALES --- + /// Variables de entorno ADICIONALES (las del .env se cargan con EnvironmentFile) + /// Solo almacenamos aquí las variables que el usuario agrega manualmente desde el panel + #[serde(default, skip_serializing_if = "std::collections::HashMap::is_empty")] + pub environment: std::collections::HashMap, + // DEPRECATED: Mantener por compatibilidad con versiones antiguas #[serde(skip_serializing_if = "Option::is_none")] pub systemd_service: Option, @@ -217,6 +223,7 @@ impl ConfigManager { deleted: false, deleted_at: None, deleted_reason: None, + environment: std::collections::HashMap::new(), systemd_service: None, created_at: None, }; diff --git a/src/discovery.rs b/src/discovery.rs index b377da2..055ca04 100644 --- a/src/discovery.rs +++ b/src/discovery.rs @@ -261,6 +261,7 @@ pub fn sync_discovered_services(services: Vec) { deleted: false, deleted_at: None, deleted_reason: None, + environment: std::collections::HashMap::new(), systemd_service: None, created_at: None, }; diff --git a/src/orchestrator/app_manager.rs b/src/orchestrator/app_manager.rs index 80dd74d..81145c4 100644 --- a/src/orchestrator/app_manager.rs +++ b/src/orchestrator/app_manager.rs @@ -92,6 +92,7 @@ impl AppManager { deleted: false, deleted_at: None, deleted_reason: None, + environment: config.environment.clone(), systemd_service: None, created_at: None, }; diff --git a/src/systemd/service_generator.rs b/src/systemd/service_generator.rs index e1115b8..f89e208 100644 --- a/src/systemd/service_generator.rs +++ b/src/systemd/service_generator.rs @@ -75,21 +75,8 @@ impl ServiceGenerator { format!("{} {}", executable, config.script_path) }; - // 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 + // Generar líneas de variables de entorno ADICIONALES (solo las del config, no del .env) + let mut env_lines: Vec = config.environment .iter() .map(|(key, value)| format!("Environment=\"{}={}\"", key, value)) .collect(); @@ -113,11 +100,17 @@ impl ServiceGenerator { } } - let env_vars = env_lines.join("\n"); - // Agregar SyslogIdentifier para logs más claros let syslog_id = format!("SyslogIdentifier=siax-app-{}", config.app_name); + // Verificar si existe .env en el proyecto + let env_file_path = Path::new(&config.working_directory).join(".env"); + let has_env_file = env_file_path.exists(); + + if has_env_file { + logger.info("ServiceGenerator", &format!("📄 .env encontrado, usando EnvironmentFile: {}", env_file_path.display())); + } + // Construir el servicio con orden lógico let mut service = format!( r#"[Unit] @@ -134,9 +127,26 @@ WorkingDirectory={} config.working_directory ); - // Agregar variables de entorno (PATH primero, luego las demás) - if !env_vars.is_empty() { - service.push_str(&env_vars); + // Agregar PATH si usa NVM (debe ir primero) + // Extraer PATH de env_lines si está en la primera posición + let mut path_line: Option = None; + if !env_lines.is_empty() && env_lines[0].starts_with("Environment=PATH=") { + path_line = Some(env_lines.remove(0)); + } + + if let Some(path) = path_line { + service.push_str(&path); + service.push('\n'); + } + + // ✅ AGREGAR EnvironmentFile si existe .env en el proyecto + if has_env_file { + service.push_str(&format!("EnvironmentFile={}\n", env_file_path.display())); + } + + // Agregar variables de entorno ADICIONALES (las del formulario/JSON) + if !env_lines.is_empty() { + service.push_str(&env_lines.join("\n")); service.push('\n'); } diff --git a/web/edit.html b/web/edit.html index 2421f6e..843cd4a 100644 --- a/web/edit.html +++ b/web/edit.html @@ -374,6 +374,10 @@