From aa41c7dd75eb1fad993877cf10cee7da6c910055 Mon Sep 17 00:00:00 2001 From: pablinux Date: Sat, 21 Feb 2026 09:05:13 -0500 Subject: [PATCH] feat: Agregar campo id (UUID v4) a MonitoredApp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cada app registrada ahora tiene un identificador único UUID v4. Se agrega la dependencia uuid al proyecto y se asegura que todas las rutas de creación de MonitoredApp generen un id único. Co-Authored-By: Claude Sonnet 4.6 --- Cargo.lock | 13 +++++++ Cargo.toml | 1 + src/config.rs | 61 +++++++++++++++++++++++++++++++-- src/discovery.rs | 5 ++- src/orchestrator/app_manager.rs | 2 ++ 5 files changed, 78 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 51c212e..8e97e2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1350,6 +1350,7 @@ dependencies = [ "tokio", "tokio-stream", "tower-http", + "uuid", ] [[package]] @@ -1711,6 +1712,18 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "uuid" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +dependencies = [ + "getrandom 0.3.4", + "js-sys", + "serde_core", + "wasm-bindgen", +] + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index a69a269..14aec5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" sysinfo = "0.30" chrono = "0.4" +uuid = { version = "1.0", features = ["v4", "serde"] } tower-http = { version = "0.5", features = ["cors"] } futures = "0.3" tokio-stream = "0.1" diff --git a/src/config.rs b/src/config.rs index e902e35..64371f4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,10 +2,15 @@ use serde::{Serialize, Deserialize}; use std::fs::{self, create_dir_all}; use std::path::Path; use std::sync::{Arc, RwLock, OnceLock}; +use uuid::Uuid; use crate::logger::get_logger; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MonitoredApp { + /// ID único de la aplicación (UUID v4) + #[serde(default = "generate_uuid")] + pub id: String, + /// Nombre de la aplicación pub name: String, @@ -70,6 +75,10 @@ pub struct MonitoredApp { pub created_at: Option, } +fn generate_uuid() -> String { + Uuid::new_v4().to_string() +} + fn default_mode() -> String { "production".to_string() } @@ -186,14 +195,20 @@ impl ConfigManager { } /// Agrega una app con información completa - pub fn add_app_full(&self, app: MonitoredApp) -> Result<(), String> { + pub fn add_app_full(&self, mut app: MonitoredApp) -> Result<(), String> { let mut config = self.config.write().unwrap(); - // Verificar si ya existe - if config.apps.iter().any(|a| a.name == app.name) { + // Verificar si ya existe una app ACTIVA con el mismo nombre + // (las apps eliminadas no cuentan, pueden tener el mismo nombre) + if config.apps.iter().any(|a| a.name == app.name && !a.deleted) { return Err(format!("La app '{}' ya está siendo monitoreada", app.name)); } + // Asegurar que tenga un UUID único + if app.id.is_empty() { + app.id = Uuid::new_v4().to_string(); + } + config.apps.push(app); // Guardar en disco @@ -210,6 +225,7 @@ impl ConfigManager { let registered_at = chrono::Local::now().to_rfc3339(); let app = MonitoredApp { + id: Uuid::new_v4().to_string(), name, service_name, path: String::new(), @@ -231,6 +247,45 @@ impl ConfigManager { self.add_app_full(app) } + /// Actualiza una app existente (sin crear duplicados) + pub fn update_app(&self, name: &str, updated_app: MonitoredApp) -> Result<(), String> { + let mut config = self.config.write().unwrap(); + + // Buscar la app activa con ese nombre + let app = config.apps.iter_mut().find(|a| a.name == name && !a.deleted); + + match app { + Some(app) => { + // Preservar el ID original y la fecha de registro + let original_id = app.id.clone(); + let original_registered_at = app.registered_at.clone(); + + // Actualizar todos los campos + app.name = updated_app.name; + app.service_name = updated_app.service_name; + app.path = updated_app.path; + app.port = updated_app.port; + app.entry_point = updated_app.entry_point; + app.node_bin = updated_app.node_bin; + app.mode = updated_app.mode; + app.user = updated_app.user; + app.service_file_path = updated_app.service_file_path; + app.environment = updated_app.environment; + + // Mantener el ID original y fecha de registro + app.id = original_id; + app.registered_at = original_registered_at; + + // 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)) + } + } + None => Err(format!("La app '{}' no se encontró", name)) + } + } + /// Realiza un soft delete: marca la app como eliminada pero mantiene el registro pub fn soft_delete_app(&self, name: &str, reason: Option) -> Result<(), String> { let mut config = self.config.write().unwrap(); diff --git a/src/discovery.rs b/src/discovery.rs index 055ca04..b383012 100644 --- a/src/discovery.rs +++ b/src/discovery.rs @@ -1,6 +1,7 @@ /// Módulo para descubrir servicios systemd existentes use std::fs; use std::path::Path; +use uuid::Uuid; use crate::logger::get_logger; use crate::config::{get_config_manager, MonitoredApp}; @@ -232,7 +233,8 @@ pub fn sync_discovered_services(services: Vec) { detect_port_from_name(&service.app_name) }); - // Verificar si ya existe en la configuración + // Verificar si ya existe una app ACTIVA en la configuración + // (get_apps() ya filtra las eliminadas, así que esto está bien) let existing_apps = config_manager.get_apps(); let already_exists = existing_apps.iter().any(|app| app.name == service.app_name); @@ -248,6 +250,7 @@ pub fn sync_discovered_services(services: Vec) { let registered_at = chrono::Local::now().to_rfc3339(); let app = MonitoredApp { + id: Uuid::new_v4().to_string(), name: service.app_name.clone(), service_name, path: service.working_directory.unwrap_or_default(), diff --git a/src/orchestrator/app_manager.rs b/src/orchestrator/app_manager.rs index 81145c4..0993e4e 100644 --- a/src/orchestrator/app_manager.rs +++ b/src/orchestrator/app_manager.rs @@ -5,6 +5,7 @@ use crate::logger::get_logger; use crate::config::{get_config_manager, MonitoredApp}; use dashmap::DashMap; use std::sync::Arc; +use uuid::Uuid; pub struct AppManager { apps: Arc>, @@ -79,6 +80,7 @@ impl AppManager { .unwrap_or_else(|| "production".to_string()); let monitored_app = MonitoredApp { + id: Uuid::new_v4().to_string(), name: config.app_name.clone(), service_name: config.service_name(), path: config.working_directory.clone(),