feat: Auto-carga de variables de entorno desde archivo .env
- Agregada dependencia dotenvy para parsear archivos .env - Implementada función read_env_file() que lee y parsea archivos .env - Soporta comentarios (#), líneas vacías, y valores con/sin comillas - Auto-detección: si existe .env en WorkingDirectory, se carga automáticamente - Merge inteligente: .env primero, luego variables manuales (sobrescriben) - Las apps ahora pueden usar su .env sin tener que copiar 17+ variables manualmente - Logs claros: informa cuántas variables se cargaron desde .env - Beneficio: registrar apps es mucho más rápido y menos propenso a errores
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -281,6 +281,12 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dotenvy"
|
||||||
|
version = "0.15.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
@@ -1332,6 +1338,7 @@ dependencies = [
|
|||||||
"axum",
|
"axum",
|
||||||
"chrono",
|
"chrono",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
|
"dotenvy",
|
||||||
"futures",
|
"futures",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ tokio-stream = "0.1"
|
|||||||
regex = "1.10"
|
regex = "1.10"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
dashmap = "5.5"
|
dashmap = "5.5"
|
||||||
|
dotenvy = "0.15"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.8"
|
tempfile = "3.8"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use crate::models::ServiceConfig;
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
use std::collections::HashMap;
|
||||||
use crate::logger::get_logger;
|
use crate::logger::get_logger;
|
||||||
|
|
||||||
pub struct ServiceGenerator;
|
pub struct ServiceGenerator;
|
||||||
@@ -74,8 +75,21 @@ impl ServiceGenerator {
|
|||||||
format!("{} {}", executable, config.script_path)
|
format!("{} {}", executable, config.script_path)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generar variables de entorno del usuario
|
// 1. Leer variables del archivo .env si existe
|
||||||
let mut env_lines: Vec<String> = config.environment
|
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
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(key, value)| format!("Environment=\"{}={}\"", key, value))
|
.map(|(key, value)| format!("Environment=\"{}={}\"", key, value))
|
||||||
.collect();
|
.collect();
|
||||||
@@ -295,4 +309,55 @@ WantedBy=multi-user.target
|
|||||||
Err(_) => false,
|
Err(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Lee el archivo .env del directorio de trabajo y retorna las variables
|
||||||
|
pub fn read_env_file(working_directory: &str) -> HashMap<String, String> {
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user