feat: Sistema de monitoreo base con logging y configuración dinámica
- Implementado monitor de procesos Node.js con detección automática - Sistema de logging con niveles (Info, Warning, Error, Critical) - ConfigManager para gestión dinámica de apps monitoreadas - Interfaz web básica con escaneo de procesos - Integración con API central para reportar estados - User-Agent tracking para identificación de agentes - Persistencia de configuración en JSON - Logs almacenados en archivo con rotación - Sistema modular: monitor, interface, logger, config Estructura: - src/main.rs: Orquestador principal - src/monitor.rs: Monitoreo de procesos y envío a API - src/interface.rs: Servidor web Axum con endpoints - src/logger.rs: Sistema de logging a archivo y consola - src/config.rs: Gestión de configuración persistente - web/: Templates HTML para interfaz web - config/: Configuración de apps monitoreadas - logs/: Archivos de log del sistema Features implementadas: ✅ Detección automática de procesos Node.js ✅ Monitoreo de CPU y RAM por proceso ✅ Reportes periódicos a API central (cada 60s) ✅ Interfaz web en puerto 8080 ✅ Logs estructurados con timestamps ✅ Configuración dinámica sin reinicio ✅ Script de despliegue automatizado Próximos pasos: - Integración con systemd para control de procesos - Dashboard mejorado con cards de apps - Logs en tiempo real vía WebSocket - Start/Stop/Restart de aplicaciones
This commit is contained in:
129
src/config.rs
129
src/config.rs
@@ -0,0 +1,129 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use std::fs::{self, create_dir_all};
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, RwLock, OnceLock};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct MonitoredApp {
|
||||
pub name: String,
|
||||
pub port: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AppConfig {
|
||||
pub apps: Vec<MonitoredApp>,
|
||||
}
|
||||
|
||||
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 },
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConfigManager {
|
||||
config_path: String,
|
||||
config: Arc<RwLock<AppConfig>>,
|
||||
}
|
||||
|
||||
impl ConfigManager {
|
||||
pub fn new(config_path: &str) -> Self {
|
||||
// Crear directorio config si no existe
|
||||
if let Some(parent) = Path::new(config_path).parent() {
|
||||
let _ = create_dir_all(parent);
|
||||
}
|
||||
|
||||
// Cargar o crear configuración
|
||||
let config = Self::load_config(config_path);
|
||||
|
||||
ConfigManager {
|
||||
config_path: config_path.to_string(),
|
||||
config: Arc::new(RwLock::new(config)),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_config(path: &str) -> AppConfig {
|
||||
match fs::read_to_string(path) {
|
||||
Ok(content) => {
|
||||
match serde_json::from_str(&content) {
|
||||
Ok(config) => {
|
||||
println!("✅ Configuración cargada desde: {}", path);
|
||||
config
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("⚠️ Error parseando config: {}. Usando default.", e);
|
||||
AppConfig::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
println!("ℹ️ Archivo de config no encontrado. Creando uno nuevo...");
|
||||
let default_config = AppConfig::default();
|
||||
let _ = Self::save_config_to_file(path, &default_config);
|
||||
default_config
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn save_config_to_file(path: &str, config: &AppConfig) -> std::io::Result<()> {
|
||||
let json = serde_json::to_string_pretty(config)?;
|
||||
fs::write(path, json)?;
|
||||
println!("💾 Configuración guardada en: {}", path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_apps(&self) -> Vec<MonitoredApp> {
|
||||
let config = self.config.read().unwrap();
|
||||
config.apps.clone()
|
||||
}
|
||||
|
||||
pub fn add_app(&self, name: String, port: i32) -> Result<(), String> {
|
||||
let mut config = self.config.write().unwrap();
|
||||
|
||||
// Verificar si ya existe
|
||||
if config.apps.iter().any(|app| app.name == name) {
|
||||
return Err(format!("La app '{}' ya está siendo monitoreada", name));
|
||||
}
|
||||
|
||||
config.apps.push(MonitoredApp { name, port });
|
||||
|
||||
// Guardar en disco
|
||||
match Self::save_config_to_file(&self.config_path, &config) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(format!("Error al guardar configuración: {}", e))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_app(&self, name: &str) -> Result<(), String> {
|
||||
let mut config = self.config.write().unwrap();
|
||||
|
||||
let original_len = config.apps.len();
|
||||
config.apps.retain(|app| app.name != name);
|
||||
|
||||
if config.apps.len() == original_len {
|
||||
return Err(format!("La app '{}' no se encontró", name));
|
||||
}
|
||||
|
||||
// Guardar en disco
|
||||
match Self::save_config_to_file(&self.config_path, &config) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(format!("Error al guardar configuración: {}", e))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_arc(&self) -> Arc<RwLock<AppConfig>> {
|
||||
Arc::clone(&self.config)
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton global del ConfigManager
|
||||
static CONFIG_MANAGER: OnceLock<ConfigManager> = OnceLock::new();
|
||||
|
||||
// ⚠️ 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"))
|
||||
}
|
||||
Reference in New Issue
Block a user