Files
SIAX-MONITOR/src/orchestrator/app_manager.rs
pablinux b0489739cf feat: Implementación completa Fase 4 - Sistema de monitoreo con API REST y WebSocket
 Nuevas funcionalidades:
- API REST unificada en puerto 8080 (eliminado CORS)
- WebSocket para logs en tiempo real desde journalctl
- Integración completa con systemd para gestión de servicios
- Escaneo automático de procesos Node.js y Python
- Rate limiting (1 operación/segundo por app)
- Interface web moderna con Tailwind CSS (tema oscuro)
- Documentación API estilo Swagger completamente en español

🎨 Interface Web (todas las páginas en español):
- Dashboard con estadísticas en tiempo real
- Visor de escaneo de procesos con filtros
- Formulario de registro de aplicaciones con variables de entorno
- Visor de logs en tiempo real con WebSocket y sidebar
- Página de selección de apps detectadas
- Documentación completa de API REST

🏗️ Arquitectura:
- Módulo models: ServiceConfig, ManagedApp, AppStatus
- Módulo systemd: wrapper de systemctl, generador de .service, parser
- Módulo orchestrator: AppManager, LifecycleManager con validaciones
- Módulo api: handlers REST, WebSocket manager, DTOs
- Servidor unificado en puerto 8080 (Web + API + WS)

🔧 Mejoras técnicas:
- Eliminación de CORS mediante servidor unificado
- Separación clara frontend/backend con carga dinámica
- Thread-safe con Arc<DashMap> para estado compartido
- Reconciliación de estados: sysinfo vs systemd
- Validaciones de paths, usuarios y configuraciones
- Manejo robusto de errores con thiserror

📝 Documentación:
- README.md actualizado con arquitectura completa
- EJEMPLOS.md con casos de uso detallados
- ESTADO_PROYECTO.md con progreso de Fase 4
- API docs interactiva en /api-docs
- Script de despliegue mejorado con health checks

🚀 Producción:
- Deployment script con validaciones
- Health checks y rollback capability
- Configuración de sudoers para systemctl
- Hardening de seguridad en servicios systemd
2026-01-13 08:24:13 -05:00

129 lines
4.0 KiB
Rust

use super::{Result, OrchestratorError};
use crate::models::{ServiceConfig, ManagedApp, AppStatus};
use crate::systemd::{ServiceGenerator, SystemCtl};
use crate::logger::get_logger;
use dashmap::DashMap;
use std::sync::Arc;
pub struct AppManager {
apps: Arc<DashMap<String, ServiceConfig>>,
}
impl AppManager {
pub fn new() -> Self {
AppManager {
apps: Arc::new(DashMap::new()),
}
}
pub fn register_app(&self, config: ServiceConfig) -> Result<()> {
let logger = get_logger();
// Validar configuración
config.validate()
.map_err(|e| OrchestratorError::ValidationError(e))?;
// Verificar si ya existe
if self.apps.contains_key(&config.app_name) {
logger.warning("AppManager", "Aplicación ya registrada", Some(&config.app_name));
return Err(OrchestratorError::AppAlreadyExists(config.app_name.clone()));
}
// Verificar si el servicio ya existe en systemd
if SystemCtl::is_service_exists(&config.service_name()) {
logger.warning("AppManager", "Servicio systemd ya existe", Some(&config.service_name()));
return Err(OrchestratorError::AppAlreadyExists(
format!("El servicio {} ya existe en systemd", config.service_name())
));
}
logger.info("AppManager", &format!("Registrando aplicación: {}", config.app_name));
// Generar archivo de servicio
let service_content = ServiceGenerator::create_service(&config)?;
ServiceGenerator::write_service_file(&config, &service_content)?;
// Recargar daemon de systemd
SystemCtl::daemon_reload()?;
// Habilitar el servicio
SystemCtl::enable(&config.service_name())?;
// Guardar en memoria
self.apps.insert(config.app_name.clone(), config.clone());
logger.info("AppManager", &format!("Aplicación {} registrada exitosamente", config.app_name));
Ok(())
}
pub fn unregister_app(&self, app_name: &str) -> Result<()> {
let logger = get_logger();
logger.info("AppManager", &format!("Desregistrando aplicación: {}", app_name));
// Obtener configuración
let config = self.apps.get(app_name)
.ok_or_else(|| OrchestratorError::AppNotFound(app_name.to_string()))?;
let service_name = config.service_name();
drop(config); // Liberar el lock
// Detener el servicio si está corriendo
let _ = SystemCtl::stop(&service_name);
// Deshabilitar el servicio
let _ = SystemCtl::disable(&service_name);
// Eliminar archivo de servicio
ServiceGenerator::delete_service_file(&service_name)?;
// Recargar daemon
SystemCtl::daemon_reload()?;
// Eliminar de memoria
self.apps.remove(app_name);
logger.info("AppManager", &format!("Aplicación {} desregistrada exitosamente", app_name));
Ok(())
}
pub fn list_apps(&self) -> Vec<String> {
self.apps.iter()
.map(|entry| entry.key().clone())
.collect()
}
pub fn get_app(&self, app_name: &str) -> Option<ServiceConfig> {
self.apps.get(app_name).map(|entry| entry.clone())
}
pub fn app_exists(&self, app_name: &str) -> bool {
self.apps.contains_key(app_name)
}
pub fn get_app_status(&self, app_name: &str) -> Option<ManagedApp> {
let config = self.get_app(app_name)?;
let systemd_status = SystemCtl::status(&config.service_name());
// Por ahora retornamos información básica
// El monitor.rs se encargará de enriquecer con PID, CPU, RAM
Some(ManagedApp {
name: app_name.to_string(),
status: AppStatus::reconcile(false, &systemd_status),
pid: None,
cpu_usage: 0.0,
memory_usage: 0,
systemd_status,
last_updated: chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(),
})
}
}
impl Default for AppManager {
fn default() -> Self {
Self::new()
}
}