feat: Agregar campo id (UUID v4) a MonitoredApp

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 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 09:05:13 -05:00
parent 058e4781e6
commit aa41c7dd75
5 changed files with 78 additions and 4 deletions

View File

@@ -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<String>,
}
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<String>) -> Result<(), String> {
let mut config = self.config.write().unwrap();

View File

@@ -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<DiscoveredService>) {
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<DiscoveredService>) {
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(),

View File

@@ -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<DashMap<String, ServiceConfig>>,
@@ -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(),