✨ 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
129 lines
4.0 KiB
Rust
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()
|
|
}
|
|
}
|