feat: Sistema de variables de entorno mejorado con EnvironmentFile

- 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
This commit is contained in:
2026-01-21 21:48:59 -05:00
parent 93d178b216
commit 058e4781e6
6 changed files with 68 additions and 24 deletions

View File

@@ -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,
};

View File

@@ -57,6 +57,12 @@ pub struct MonitoredApp {
#[serde(skip_serializing_if = "Option::is_none")]
pub deleted_reason: Option<String>,
// --- 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<String, String>,
// DEPRECATED: Mantener por compatibilidad con versiones antiguas
#[serde(skip_serializing_if = "Option::is_none")]
pub systemd_service: Option<String>,
@@ -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,
};

View File

@@ -261,6 +261,7 @@ pub fn sync_discovered_services(services: Vec<DiscoveredService>) {
deleted: false,
deleted_at: None,
deleted_reason: None,
environment: std::collections::HashMap::new(),
systemd_service: None,
created_at: None,
};

View File

@@ -92,6 +92,7 @@ impl AppManager {
deleted: false,
deleted_at: None,
deleted_reason: None,
environment: config.environment.clone(),
systemd_service: None,
created_at: None,
};

View File

@@ -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<String> = merged_env
// Generar líneas de variables de entorno ADICIONALES (solo las del config, no del .env)
let mut env_lines: Vec<String> = 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<String> = 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');
}