feat: Creación automática de directorio y archivo de configuración
Implementa la creación automática del directorio config/ y el archivo monitored_apps.json si no existen al iniciar el agente. PROBLEMA RESUELTO: - Al ejecutar en servidor nuevo, faltaba el directorio config/ - Error al no encontrar monitored_apps.json - Requería creación manual del directorio y archivo SOLUCIÓN IMPLEMENTADA: 1. Verificación de existencia de directorio padre 2. Creación automática con create_dir_all() si no existe 3. Creación de monitored_apps.json vacío si no existe 4. Sistema de prioridad para rutas de configuración: - Variable de entorno SIAX_CONFIG_PATH (override) - /opt/siax-agent/config/monitored_apps.json (producción) - ./config/monitored_apps.json (desarrollo) 5. Logging detallado de cada paso COMPORTAMIENTO: - Primera ejecución: Crea config/ y monitored_apps.json vacío - Ejecuciones siguientes: Usa el archivo existente - En producción (/opt/siax-agent/): Usa ruta absoluta - En desarrollo: Usa ruta relativa ./config/ LOGS GENERADOS: ✅ "Creando directorio: /path/to/config" ✅ "Directorio creado: /path/to/config" ✅ "Archivo de configuración creado: /path/to/monitored_apps.json" ✅ "Usando archivo de configuración: /path" BENEFICIOS: ✅ No requiere creación manual de directorios ✅ Funciona en cualquier entorno (dev/prod) ✅ Soporta override con variable de entorno ✅ Logs claros para debugging ✅ Archivo JSON vacío válido por defecto Archivo modificado: - src/config.rs: +38 líneas (auto-creación + prioridad de rutas)
This commit is contained in:
@@ -2,11 +2,16 @@ use serde::{Serialize, Deserialize};
|
||||
use std::fs::{self, create_dir_all};
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, RwLock, OnceLock};
|
||||
use crate::logger::get_logger;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct MonitoredApp {
|
||||
pub name: String,
|
||||
pub port: i32,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub systemd_service: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub created_at: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -17,10 +22,7 @@ pub struct AppConfig {
|
||||
impl Default for AppConfig {
|
||||
fn default() -> Self {
|
||||
AppConfig {
|
||||
apps: vec![
|
||||
MonitoredApp { name: "app_tareas".to_string(), port: 3000 },
|
||||
MonitoredApp { name: "fidelizacion".to_string(), port: 3001 },
|
||||
]
|
||||
apps: vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,9 +34,17 @@ pub struct ConfigManager {
|
||||
|
||||
impl ConfigManager {
|
||||
pub fn new(config_path: &str) -> Self {
|
||||
let logger = get_logger();
|
||||
|
||||
// Crear directorio config si no existe
|
||||
if let Some(parent) = Path::new(config_path).parent() {
|
||||
let _ = create_dir_all(parent);
|
||||
if !parent.exists() {
|
||||
logger.info("Config", &format!("Creando directorio: {}", parent.display()));
|
||||
match create_dir_all(parent) {
|
||||
Ok(_) => logger.info("Config", &format!("✅ Directorio creado: {}", parent.display())),
|
||||
Err(e) => logger.error("Config", "Error creando directorio", Some(&e.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cargar o crear configuración
|
||||
@@ -47,23 +57,31 @@ impl ConfigManager {
|
||||
}
|
||||
|
||||
fn load_config(path: &str) -> AppConfig {
|
||||
let logger = get_logger();
|
||||
|
||||
match fs::read_to_string(path) {
|
||||
Ok(content) => {
|
||||
match serde_json::from_str(&content) {
|
||||
Ok(config) => {
|
||||
println!("✅ Configuración cargada desde: {}", path);
|
||||
let app_count = if let AppConfig { apps } = &config { apps.len() } else { 0 };
|
||||
logger.info("Config", &format!("✅ Configuración cargada: {} apps desde {}", app_count, path));
|
||||
config
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("⚠️ Error parseando config: {}. Usando default.", e);
|
||||
AppConfig::default()
|
||||
logger.error("Config", "Error parseando JSON, creando vacío", Some(&e.to_string()));
|
||||
let default_config = AppConfig::default();
|
||||
let _ = Self::save_config_to_file(path, &default_config);
|
||||
default_config
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
println!("ℹ️ Archivo de config no encontrado. Creando uno nuevo...");
|
||||
Err(e) => {
|
||||
logger.warning("Config", &format!("Archivo no encontrado ({}), creando vacío en: {}", e.kind(), path), None);
|
||||
let default_config = AppConfig::default();
|
||||
let _ = Self::save_config_to_file(path, &default_config);
|
||||
match Self::save_config_to_file(path, &default_config) {
|
||||
Ok(_) => logger.info("Config", &format!("✅ Archivo de configuración creado: {}", path)),
|
||||
Err(save_err) => logger.error("Config", "Error al crear archivo", Some(&save_err.to_string())),
|
||||
}
|
||||
default_config
|
||||
}
|
||||
}
|
||||
@@ -89,7 +107,15 @@ impl ConfigManager {
|
||||
return Err(format!("La app '{}' ya está siendo monitoreada", name));
|
||||
}
|
||||
|
||||
config.apps.push(MonitoredApp { name, port });
|
||||
let systemd_service = format!("siax-app-{}.service", name);
|
||||
let created_at = chrono::Local::now().to_rfc3339();
|
||||
|
||||
config.apps.push(MonitoredApp {
|
||||
name,
|
||||
port,
|
||||
systemd_service: Some(systemd_service),
|
||||
created_at: Some(created_at),
|
||||
});
|
||||
|
||||
// Guardar en disco
|
||||
match Self::save_config_to_file(&self.config_path, &config) {
|
||||
@@ -123,7 +149,31 @@ impl ConfigManager {
|
||||
// Singleton global del ConfigManager
|
||||
static CONFIG_MANAGER: OnceLock<ConfigManager> = OnceLock::new();
|
||||
|
||||
/// Determina la ruta del archivo de configuración
|
||||
fn get_config_path() -> String {
|
||||
// Prioridad de rutas:
|
||||
// 1. Variable de entorno SIAX_CONFIG_PATH
|
||||
// 2. /opt/siax-agent/config/monitored_apps.json (producción)
|
||||
// 3. ./config/monitored_apps.json (desarrollo)
|
||||
|
||||
if let Ok(env_path) = std::env::var("SIAX_CONFIG_PATH") {
|
||||
return env_path;
|
||||
}
|
||||
|
||||
let prod_path = "/opt/siax-agent/config/monitored_apps.json";
|
||||
if Path::new("/opt/siax-agent").exists() {
|
||||
return prod_path.to_string();
|
||||
}
|
||||
|
||||
"config/monitored_apps.json".to_string()
|
||||
}
|
||||
|
||||
// ⚠️ IMPORTANTE: Esta función DEBE ser pública
|
||||
pub fn get_config_manager() -> &'static ConfigManager {
|
||||
CONFIG_MANAGER.get_or_init(|| ConfigManager::new("config/monitored_apps.json"))
|
||||
}
|
||||
CONFIG_MANAGER.get_or_init(|| {
|
||||
let config_path = get_config_path();
|
||||
let logger = get_logger();
|
||||
logger.info("Config", &format!("Usando archivo de configuración: {}", config_path));
|
||||
ConfigManager::new(&config_path)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user