feat: Mejorar estructura de monitored_apps.json con metadata completa

- Añadir campos al modelo MonitoredApp:
  * service_name: Nombre del servicio systemd
  * path: WorkingDirectory de la aplicación
  * entry_point: Archivo de entrada (server.js, app.js, etc.)
  * node_bin: Ruta completa al binario de node/python
  * mode: Modo de ejecución (production, development, test)
  * service_file_path: Ruta al archivo .service de systemd
  * registered_at: Timestamp de registro (ISO 8601)

- Actualizar discovery.rs para extraer toda la información:
  * Parsear ExecStart para obtener node_bin y entry_point
  * Extraer NODE_ENV para determinar el modo
  * Guardar ruta completa al archivo .service
  * Usar add_app_full() con información completa

- Integrar ConfigManager en AppManager:
  * Guardar automáticamente en monitored_apps.json al registrar apps
  * Eliminar del JSON al desregistrar apps
  * Extraer metadata desde ServiceConfig (puerto, entry_point, mode, etc.)

- Mantener retrocompatibilidad con JSON antiguo mediante campos deprecated
- Todos los nuevos campos usan #[serde(default)] para evitar errores de deserialización
This commit is contained in:
2026-01-18 03:26:42 -05:00
parent ad9b46bdc5
commit 8822e9e6b5
21 changed files with 3246 additions and 642 deletions

View File

@@ -1,347 +1,51 @@
#!/bin/bash
#######################################
# SIAX Agent - Script de Despliegue
# Instalación automática production-ready
#######################################
# --- CONFIGURACIÓN ---
BINARY_NAME="siax_monitor"
TARGET="x86_64-unknown-linux-gnu"
LOCAL_PATH="target/$TARGET/release/$BINARY_NAME"
set -e # Salir si hay errores
# 1. Preguntar método de transferencia
echo "Selecciona el método de transferencia:"
select METODO in "scp" "rsync"; do
case $METODO in
scp|rsync) break ;;
*) echo "Opción inválida, elige 1 o 2." ;;
esac
done
# Colores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 2. Compilar
echo "📦 Compilando..."
cargo build --release --target $TARGET
if [ $? -ne 0 ]; then echo "❌ Error en compilación"; exit 1; fi
# Variables
INSTALL_DIR="/opt/siax-agent"
SERVICE_USER="siax-agent"
BACKUP_DIR="/tmp/siax-agent-backup-$(date +%s)"
# --- FUNCIÓN DE SUBIDA ---
upload_file() {
local IP=$1
local USER=$2
local DEST=$3
#######################################
# Funciones
#######################################
echo "🚀 Subiendo a $USER@$IP vía $METODO..."
print_header() {
echo -e "${BLUE}"
echo "============================================"
echo " SIAX Agent - Deployment Script"
echo "============================================"
echo -e "${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_info() {
echo -e "${BLUE} $1${NC}"
}
check_root() {
if [ "$EUID" -ne 0 ]; then
print_error "Este script debe ejecutarse como root"
echo "Usa: sudo ./desplegar_agent.sh"
exit 1
fi
}
check_dependencies() {
print_info "Verificando dependencias..."
local deps=("systemctl" "cargo" "rustc")
local missing=()
for dep in "${deps[@]}"; do
if ! command -v $dep &> /dev/null; then
missing+=($dep)
fi
done
if [ ${#missing[@]} -ne 0 ]; then
print_error "Faltan dependencias: ${missing[*]}"
echo ""
echo "Instalación de Rust:"
echo " curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
echo ""
echo "Instalación de systemd (debería estar instalado por defecto):"
echo " sudo apt-get install systemd # Debian/Ubuntu"
echo " sudo yum install systemd # RedHat/CentOS"
exit 1
fi
print_success "Todas las dependencias están instaladas"
}
backup_existing() {
if [ -d "$INSTALL_DIR" ]; then
print_warning "Instalación existente detectada"
print_info "Creando backup en: $BACKUP_DIR"
mkdir -p "$BACKUP_DIR"
cp -r "$INSTALL_DIR" "$BACKUP_DIR/"
print_success "Backup creado"
fi
}
compile_release() {
print_info "Compilando SIAX Agent en modo release..."
if cargo build --release; then
print_success "Compilación exitosa"
if [ "$METODO" = "scp" ]; then
scp "$LOCAL_PATH" "$USER@$IP:$DEST/"
else
print_error "Error en la compilación"
rollback
exit 1
# rsync -avz: a (archivo/permisos), v (visual), z (comprimido)
rsync -avz "$LOCAL_PATH" "$USER@$IP:$DEST/"
fi
}
create_user() {
if id "$SERVICE_USER" &>/dev/null; then
print_info "Usuario $SERVICE_USER ya existe"
if [ $? -eq 0 ]; then
echo "$IP: Completado."
else
print_info "Creando usuario del sistema: $SERVICE_USER"
useradd --system --no-create-home --shell /bin/false "$SERVICE_USER"
print_success "Usuario creado"
echo "$IP: Falló la subida."
fi
}
install_binary() {
print_info "Instalando binario en $INSTALL_DIR..."
# --- LISTA DE SERVIDORES ---
# Formato: upload_file "IP" "USUARIO" "RUTA_DESTINO"
upload_file "192.168.10.145" "root" "/root/app"
upload_file "192.168.10.150" "pablinux" "/home/pablinux/app"
upload_file "192.168.10.160" "user_apps" "/home/user_apps/apps"
mkdir -p "$INSTALL_DIR"
mkdir -p "$INSTALL_DIR/config"
mkdir -p "$INSTALL_DIR/logs"
cp target/release/siax_monitor "$INSTALL_DIR/siax-agent"
chmod +x "$INSTALL_DIR/siax-agent"
# Copiar archivos de configuración si existen
if [ -f "config/monitored_apps.json" ]; then
cp config/monitored_apps.json "$INSTALL_DIR/config/"
fi
# Copiar archivos web
if [ -d "web" ]; then
cp -r web "$INSTALL_DIR/"
fi
# Permisos
chown -R $SERVICE_USER:$SERVICE_USER "$INSTALL_DIR"
print_success "Binario instalado"
}
configure_sudoers() {
print_info "Configurando permisos sudo para systemctl..."
local sudoers_file="/etc/sudoers.d/siax-agent"
cat > "$sudoers_file" << EOF
# SIAX Agent - Permisos para gestionar servicios systemd
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl start *
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl stop *
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl restart *
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl status *
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl enable *
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl disable *
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl daemon-reload
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl is-active *
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl list-unit-files *
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/journalctl *
EOF
chmod 0440 "$sudoers_file"
# Validar sintaxis
if visudo -c -f "$sudoers_file" &>/dev/null; then
print_success "Configuración de sudoers creada"
else
print_error "Error en configuración de sudoers"
rm -f "$sudoers_file"
exit 1
fi
}
create_systemd_service() {
print_info "Creando servicio systemd para SIAX Agent..."
cat > /etc/systemd/system/siax-agent.service << EOF
[Unit]
Description=SIAX Agent - Process Monitor and Manager
After=network.target
[Service]
Type=simple
User=$SERVICE_USER
WorkingDirectory=$INSTALL_DIR
ExecStart=$INSTALL_DIR/siax-agent
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
# Security hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=$INSTALL_DIR/config $INSTALL_DIR/logs /etc/systemd/system
ProtectHome=true
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable siax-agent.service
print_success "Servicio systemd creado y habilitado"
}
verify_installation() {
print_info "Verificando instalación..."
local errors=0
# Verificar binario
if [ ! -f "$INSTALL_DIR/siax-agent" ]; then
print_error "Binario no encontrado"
((errors++))
fi
# Verificar permisos
if [ ! -r "$INSTALL_DIR/siax-agent" ]; then
print_error "Permisos incorrectos en binario"
((errors++))
fi
# Verificar servicio
if ! systemctl is-enabled siax-agent.service &>/dev/null; then
print_error "Servicio no habilitado"
((errors++))
fi
# Verificar sudoers
if [ ! -f "/etc/sudoers.d/siax-agent" ]; then
print_warning "Configuración de sudoers no encontrada"
echo " El agente podría tener problemas para gestionar servicios"
fi
if [ $errors -eq 0 ]; then
print_success "Verificación exitosa"
return 0
else
print_error "Verificación falló con $errors errores"
return 1
fi
}
start_service() {
print_info "Iniciando SIAX Agent..."
if systemctl start siax-agent.service; then
sleep 2
if systemctl is-active siax-agent.service &>/dev/null; then
print_success "SIAX Agent iniciado correctamente"
return 0
else
print_error "SIAX Agent no pudo iniciarse"
echo ""
echo "Ver logs con: journalctl -u siax-agent.service -n 50"
return 1
fi
else
print_error "Error al iniciar el servicio"
return 1
fi
}
rollback() {
print_warning "Ejecutando rollback..."
systemctl stop siax-agent.service 2>/dev/null || true
systemctl disable siax-agent.service 2>/dev/null || true
if [ -d "$BACKUP_DIR" ]; then
rm -rf "$INSTALL_DIR"
cp -r "$BACKUP_DIR/siax-agent" "$INSTALL_DIR"
systemctl start siax-agent.service 2>/dev/null || true
print_success "Rollback completado"
else
print_warning "No hay backup disponible para rollback"
fi
}
print_summary() {
echo ""
echo -e "${GREEN}============================================${NC}"
echo -e "${GREEN} ✅ SIAX Agent instalado exitosamente${NC}"
echo -e "${GREEN}============================================${NC}"
echo ""
echo "📊 Interface Web: http://localhost:8080"
echo "🔌 API REST: http://localhost:8081/api"
echo "📡 WebSocket: ws://localhost:8081/ws/logs/:app_name"
echo ""
echo "Comandos útiles:"
echo " Estado: sudo systemctl status siax-agent"
echo " Logs: sudo journalctl -u siax-agent -f"
echo " Reiniciar: sudo systemctl restart siax-agent"
echo " Detener: sudo systemctl stop siax-agent"
echo ""
echo "Directorio de instalación: $INSTALL_DIR"
echo "Configuración: $INSTALL_DIR/config/monitored_apps.json"
echo ""
}
#######################################
# Main
#######################################
main() {
print_header
check_root
check_dependencies
backup_existing
compile_release
create_user
install_binary
configure_sudoers
create_systemd_service
if verify_installation; then
if start_service; then
print_summary
exit 0
else
print_error "El servicio no pudo iniciarse correctamente"
print_info "Revisa los logs: journalctl -u siax-agent -n 50"
echo ""
echo "¿Deseas hacer rollback? (y/n)"
read -r response
if [[ "$response" =~ ^[Yy]$ ]]; then
rollback
fi
exit 1
fi
else
print_error "La verificación falló"
echo ""
echo "¿Deseas hacer rollback? (y/n)"
read -r response
if [[ "$response" =~ ^[Yy]$ ]]; then
rollback
fi
exit 1
fi
}
main
echo "------------------------------------------------"
echo "Done!"

347
instalador.sh Executable file
View File

@@ -0,0 +1,347 @@
#!/bin/bash
#######################################
# SIAX Agent - Script de Despliegue
# Instalación automática production-ready
#######################################
set -e # Salir si hay errores
# Colores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Variables
INSTALL_DIR="/opt/siax-agent"
SERVICE_USER="siax-agent"
BACKUP_DIR="/tmp/siax-agent-backup-$(date +%s)"
#######################################
# Funciones
#######################################
print_header() {
echo -e "${BLUE}"
echo "============================================"
echo " SIAX Agent - Deployment Script"
echo "============================================"
echo -e "${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_info() {
echo -e "${BLUE} $1${NC}"
}
check_root() {
if [ "$EUID" -ne 0 ]; then
print_error "Este script debe ejecutarse como root"
echo "Usa: sudo ./desplegar_agent.sh"
exit 1
fi
}
check_dependencies() {
print_info "Verificando dependencias..."
local deps=("systemctl" "cargo" "rustc")
local missing=()
for dep in "${deps[@]}"; do
if ! command -v $dep &> /dev/null; then
missing+=($dep)
fi
done
if [ ${#missing[@]} -ne 0 ]; then
print_error "Faltan dependencias: ${missing[*]}"
echo ""
echo "Instalación de Rust:"
echo " curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
echo ""
echo "Instalación de systemd (debería estar instalado por defecto):"
echo " sudo apt-get install systemd # Debian/Ubuntu"
echo " sudo yum install systemd # RedHat/CentOS"
exit 1
fi
print_success "Todas las dependencias están instaladas"
}
backup_existing() {
if [ -d "$INSTALL_DIR" ]; then
print_warning "Instalación existente detectada"
print_info "Creando backup en: $BACKUP_DIR"
mkdir -p "$BACKUP_DIR"
cp -r "$INSTALL_DIR" "$BACKUP_DIR/"
print_success "Backup creado"
fi
}
compile_release() {
print_info "Compilando SIAX Agent en modo release..."
if cargo build --release; then
print_success "Compilación exitosa"
else
print_error "Error en la compilación"
rollback
exit 1
fi
}
create_user() {
if id "$SERVICE_USER" &>/dev/null; then
print_info "Usuario $SERVICE_USER ya existe"
else
print_info "Creando usuario del sistema: $SERVICE_USER"
useradd --system --no-create-home --shell /bin/false "$SERVICE_USER"
print_success "Usuario creado"
fi
}
install_binary() {
print_info "Instalando binario en $INSTALL_DIR..."
mkdir -p "$INSTALL_DIR"
mkdir -p "$INSTALL_DIR/config"
mkdir -p "$INSTALL_DIR/logs"
cp target/release/siax_monitor "$INSTALL_DIR/siax-agent"
chmod +x "$INSTALL_DIR/siax-agent"
# Copiar archivos de configuración si existen
if [ -f "config/monitored_apps.json" ]; then
cp config/monitored_apps.json "$INSTALL_DIR/config/"
fi
# Copiar archivos web
if [ -d "web" ]; then
cp -r web "$INSTALL_DIR/"
fi
# Permisos
chown -R $SERVICE_USER:$SERVICE_USER "$INSTALL_DIR"
print_success "Binario instalado"
}
configure_sudoers() {
print_info "Configurando permisos sudo para systemctl..."
local sudoers_file="/etc/sudoers.d/siax-agent"
cat > "$sudoers_file" << EOF
# SIAX Agent - Permisos para gestionar servicios systemd
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl start *
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl stop *
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl restart *
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl status *
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl enable *
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl disable *
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl daemon-reload
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl is-active *
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl list-unit-files *
$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/journalctl *
EOF
chmod 0440 "$sudoers_file"
# Validar sintaxis
if visudo -c -f "$sudoers_file" &>/dev/null; then
print_success "Configuración de sudoers creada"
else
print_error "Error en configuración de sudoers"
rm -f "$sudoers_file"
exit 1
fi
}
create_systemd_service() {
print_info "Creando servicio systemd para SIAX Agent..."
cat > /etc/systemd/system/siax-agent.service << EOF
[Unit]
Description=SIAX Agent - Process Monitor and Manager
After=network.target
[Service]
Type=simple
User=$SERVICE_USER
WorkingDirectory=$INSTALL_DIR
ExecStart=$INSTALL_DIR/siax-agent
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
# Security hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=$INSTALL_DIR/config $INSTALL_DIR/logs /etc/systemd/system
ProtectHome=true
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable siax-agent.service
print_success "Servicio systemd creado y habilitado"
}
verify_installation() {
print_info "Verificando instalación..."
local errors=0
# Verificar binario
if [ ! -f "$INSTALL_DIR/siax-agent" ]; then
print_error "Binario no encontrado"
((errors++))
fi
# Verificar permisos
if [ ! -r "$INSTALL_DIR/siax-agent" ]; then
print_error "Permisos incorrectos en binario"
((errors++))
fi
# Verificar servicio
if ! systemctl is-enabled siax-agent.service &>/dev/null; then
print_error "Servicio no habilitado"
((errors++))
fi
# Verificar sudoers
if [ ! -f "/etc/sudoers.d/siax-agent" ]; then
print_warning "Configuración de sudoers no encontrada"
echo " El agente podría tener problemas para gestionar servicios"
fi
if [ $errors -eq 0 ]; then
print_success "Verificación exitosa"
return 0
else
print_error "Verificación falló con $errors errores"
return 1
fi
}
start_service() {
print_info "Iniciando SIAX Agent..."
if systemctl start siax-agent.service; then
sleep 2
if systemctl is-active siax-agent.service &>/dev/null; then
print_success "SIAX Agent iniciado correctamente"
return 0
else
print_error "SIAX Agent no pudo iniciarse"
echo ""
echo "Ver logs con: journalctl -u siax-agent.service -n 50"
return 1
fi
else
print_error "Error al iniciar el servicio"
return 1
fi
}
rollback() {
print_warning "Ejecutando rollback..."
systemctl stop siax-agent.service 2>/dev/null || true
systemctl disable siax-agent.service 2>/dev/null || true
if [ -d "$BACKUP_DIR" ]; then
rm -rf "$INSTALL_DIR"
cp -r "$BACKUP_DIR/siax-agent" "$INSTALL_DIR"
systemctl start siax-agent.service 2>/dev/null || true
print_success "Rollback completado"
else
print_warning "No hay backup disponible para rollback"
fi
}
print_summary() {
echo ""
echo -e "${GREEN}============================================${NC}"
echo -e "${GREEN} ✅ SIAX Agent instalado exitosamente${NC}"
echo -e "${GREEN}============================================${NC}"
echo ""
echo "📊 Interface Web: http://localhost:8080"
echo "🔌 API REST: http://localhost:8081/api"
echo "📡 WebSocket: ws://localhost:8081/ws/logs/:app_name"
echo ""
echo "Comandos útiles:"
echo " Estado: sudo systemctl status siax-agent"
echo " Logs: sudo journalctl -u siax-agent -f"
echo " Reiniciar: sudo systemctl restart siax-agent"
echo " Detener: sudo systemctl stop siax-agent"
echo ""
echo "Directorio de instalación: $INSTALL_DIR"
echo "Configuración: $INSTALL_DIR/config/monitored_apps.json"
echo ""
}
#######################################
# Main
#######################################
main() {
print_header
check_root
check_dependencies
backup_existing
compile_release
create_user
install_binary
configure_sudoers
create_systemd_service
if verify_installation; then
if start_service; then
print_summary
exit 0
else
print_error "El servicio no pudo iniciarse correctamente"
print_info "Revisa los logs: journalctl -u siax-agent -n 50"
echo ""
echo "¿Deseas hacer rollback? (y/n)"
read -r response
if [[ "$response" =~ ^[Yy]$ ]]; then
rollback
fi
exit 1
fi
else
print_error "La verificación falló"
echo ""
echo "¿Deseas hacer rollback? (y/n)"
read -r response
if [[ "$response" =~ ^[Yy]$ ]]; then
rollback
fi
exit 1
fi
}
main

315
install-remote.sh Normal file
View File

@@ -0,0 +1,315 @@
#!/bin/bash
#######################################
# SIAX Agent - Script de Instalación Remota
# Descarga e instala SIAX Agent desde servidor central
#######################################
set -e # Salir si hay errores
# Variables (CONFIGURAR AQUÍ)
CENTRAL_SERVER="${SIAX_SERVER:-localhost:8080}" # Servidor central
INSTALL_DIR="/opt/siax-agent"
SERVICE_USER="siax-agent"
BACKUP_DIR="/tmp/siax-agent-backup-$(date +%s)"
DOWNLOAD_DIR="/tmp/siax-agent-download-$(date +%s)"
# Colores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
#######################################
# Funciones
#######################################
print_header() {
echo -e "${BLUE}"
echo "============================================"
echo " SIAX Agent - Remote Installation"
echo " Server: $CENTRAL_SERVER"
echo "============================================"
echo -e "${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_info() {
echo -e "${BLUE} $1${NC}"
}
check_root() {
if [ "$EUID" -ne 0 ]; then
print_error "Este script debe ejecutarse como root"
echo "Usa: curl -sSL http://$CENTRAL_SERVER/install.sh | sudo bash"
echo "O con variable: curl -sSL http://$CENTRAL_SERVER/install.sh | sudo SIAX_SERVER=tu-servidor:8080 bash"
exit 1
fi
}
check_dependencies() {
print_info "Verificando dependencias..."
local deps=("systemctl" "curl")
local missing=()
for dep in "${deps[@]}"; do
if ! command -v $dep &> /dev/null; then
missing+=($dep)
fi
done
if [ ${#missing[@]} -ne 0 ]; then
print_error "Faltan dependencias: ${missing[*]}"
echo ""
echo "Instalación en Debian/Ubuntu:"
echo " sudo apt-get update && sudo apt-get install -y curl systemd"
echo ""
echo "Instalación en RedHat/CentOS:"
echo " sudo yum install -y curl systemd"
exit 1
fi
print_success "Todas las dependencias están instaladas"
}
download_binary() {
print_info "Descargando binario desde $CENTRAL_SERVER..."
mkdir -p "$DOWNLOAD_DIR"
# Intentar descargar el binario pre-compilado
if curl -f -L -o "$DOWNLOAD_DIR/siax-agent" "http://$CENTRAL_SERVER/static/binary/siax-agent"; then
chmod +x "$DOWNLOAD_DIR/siax-agent"
print_success "Binario descargado"
else
print_error "No se pudo descargar el binario desde http://$CENTRAL_SERVER/static/binary/siax-agent"
echo ""
echo "Asegúrate de que:"
echo " 1. El servidor $CENTRAL_SERVER está accesible"
echo " 2. El binario está en web/static/binary/siax-agent"
echo " 3. Compilaste con: cargo build --release && cp target/release/siax_monitor web/static/binary/siax-agent"
rm -rf "$DOWNLOAD_DIR"
exit 1
fi
}
download_web_files() {
print_info "Descargando archivos web..."
mkdir -p "$DOWNLOAD_DIR/web"
# Descargar archivos HTML principales (opcional, solo si quieres que cada agente tenga su propia interfaz)
# Para agentes worker, probablemente no necesites esto
print_info "Archivos web no necesarios para worker nodes (omitiendo)"
}
backup_existing() {
if [ -d "$INSTALL_DIR" ]; then
print_warning "Instalación existente detectada"
print_info "Creando backup en: $BACKUP_DIR"
mkdir -p "$BACKUP_DIR"
cp -r "$INSTALL_DIR" "$BACKUP_DIR/"
print_success "Backup creado"
fi
}
create_user() {
if id "$SERVICE_USER" &>/dev/null; then
print_info "Usuario $SERVICE_USER ya existe"
else
print_info "Creando usuario del sistema: $SERVICE_USER"
useradd --system --no-create-home --shell /bin/false "$SERVICE_USER"
print_success "Usuario creado"
fi
}
install_binary() {
print_info "Instalando binario en $INSTALL_DIR..."
mkdir -p "$INSTALL_DIR"
mkdir -p "$INSTALL_DIR/config"
mkdir -p "$INSTALL_DIR/logs"
mkdir -p "$INSTALL_DIR/web/static"
# Copiar binario
cp "$DOWNLOAD_DIR/siax-agent" "$INSTALL_DIR/siax-agent"
chmod +x "$INSTALL_DIR/siax-agent"
# Crear configuración inicial vacía si no existe
if [ ! -f "$INSTALL_DIR/config/monitored_apps.json" ]; then
echo '{"apps":[]}' > "$INSTALL_DIR/config/monitored_apps.json"
fi
# Permisos
chown -R $SERVICE_USER:$SERVICE_USER "$INSTALL_DIR"
print_success "Binario instalado"
}
configure_sudoers() {
print_info "Configurando permisos sudo para systemctl..."
local sudoers_file="/etc/sudoers.d/siax-agent"
cat > "$sudoers_file" << 'EOF'
# SIAX Agent - Permisos para gestionar servicios systemd
siax-agent ALL=(ALL) NOPASSWD: /bin/systemctl start *
siax-agent ALL=(ALL) NOPASSWD: /bin/systemctl stop *
siax-agent ALL=(ALL) NOPASSWD: /bin/systemctl restart *
siax-agent ALL=(ALL) NOPASSWD: /bin/systemctl status *
siax-agent ALL=(ALL) NOPASSWD: /bin/systemctl enable *
siax-agent ALL=(ALL) NOPASSWD: /bin/systemctl disable *
siax-agent ALL=(ALL) NOPASSWD: /bin/systemctl daemon-reload
siax-agent ALL=(ALL) NOPASSWD: /bin/systemctl is-active *
siax-agent ALL=(ALL) NOPASSWD: /bin/systemctl list-unit-files *
siax-agent ALL=(ALL) NOPASSWD: /bin/journalctl *
siax-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl start *
siax-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop *
siax-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart *
siax-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl status *
siax-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl enable *
siax-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl disable *
siax-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl daemon-reload
siax-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl is-active *
siax-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl list-unit-files *
siax-agent ALL=(ALL) NOPASSWD: /usr/bin/journalctl *
EOF
chmod 0440 "$sudoers_file"
# Validar sintaxis
if visudo -c -f "$sudoers_file" &>/dev/null; then
print_success "Configuración de sudoers creada"
else
print_error "Error en configuración de sudoers"
rm -f "$sudoers_file"
exit 1
fi
}
create_systemd_service() {
print_info "Creando servicio systemd para SIAX Agent..."
cat > /etc/systemd/system/siax-agent.service << EOF
[Unit]
Description=SIAX Agent - Process Monitor and Manager
After=network.target
[Service]
Type=simple
User=$SERVICE_USER
WorkingDirectory=$INSTALL_DIR
ExecStart=$INSTALL_DIR/siax-agent
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
# Security hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=$INSTALL_DIR/config $INSTALL_DIR/logs /etc/systemd/system
ProtectHome=true
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable siax-agent.service
print_success "Servicio systemd creado y habilitado"
}
start_service() {
print_info "Iniciando SIAX Agent..."
if systemctl start siax-agent.service; then
sleep 2
if systemctl is-active siax-agent.service &>/dev/null; then
print_success "SIAX Agent iniciado correctamente"
return 0
else
print_error "SIAX Agent no pudo iniciarse"
echo ""
echo "Ver logs con: journalctl -u siax-agent.service -n 50"
return 1
fi
else
print_error "Error al iniciar el servicio"
return 1
fi
}
cleanup() {
print_info "Limpiando archivos temporales..."
rm -rf "$DOWNLOAD_DIR"
print_success "Limpieza completada"
}
print_summary() {
echo ""
echo -e "${GREEN}============================================${NC}"
echo -e "${GREEN} ✅ SIAX Agent instalado exitosamente${NC}"
echo -e "${GREEN}============================================${NC}"
echo ""
echo "📊 Interface Web: http://localhost:8080"
echo "🔌 API REST: http://localhost:8080/api"
echo "📡 WebSocket: ws://localhost:8080/api/apps/:name/logs"
echo ""
echo "Comandos útiles:"
echo " Estado: sudo systemctl status siax-agent"
echo " Logs: sudo journalctl -u siax-agent -f"
echo " Reiniciar: sudo systemctl restart siax-agent"
echo " Detener: sudo systemctl stop siax-agent"
echo ""
echo "Directorio de instalación: $INSTALL_DIR"
echo "Configuración: $INSTALL_DIR/config/monitored_apps.json"
echo ""
echo "🌐 Servidor Central: $CENTRAL_SERVER"
echo ""
}
#######################################
# Main
#######################################
main() {
print_header
check_root
check_dependencies
backup_existing
download_binary
create_user
install_binary
configure_sudoers
create_systemd_service
if start_service; then
cleanup
print_summary
exit 0
else
print_error "El servicio no pudo iniciarse correctamente"
print_info "Revisa los logs: journalctl -u siax-agent -n 50"
cleanup
exit 1
fi
}
main

View File

@@ -117,3 +117,243 @@
[2026-01-13 08:16:16] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 08:16:16] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 08:16:16] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 11:34:33] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 11:34:33] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 11:34:33] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 11:34:33] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 11:37:28] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 11:37:28] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 11:37:28] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 11:37:28] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 11:43:43] [ERROR] [Monitor] Error enviando app_tareas | error sending request for url (https://api.siax-system.net/api/apps_servcs/apps): operation timed out
[2026-01-13 11:43:54] [ERROR] [Monitor] Error enviando fidelizacion | error sending request for url (https://api.siax-system.net/api/apps_servcs/apps): operation timed out
[2026-01-13 11:48:54] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 11:48:54] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 11:48:54] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 11:48:54] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 11:54:16] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 11:54:16] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 11:54:16] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 11:54:16] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 11:57:20] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 11:57:20] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 11:57:20] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 11:57:20] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 11:58:31] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 11:58:31] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 11:58:31] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 11:58:31] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 12:01:28] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 12:01:28] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 12:01:28] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 12:01:28] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 12:02:46] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 12:02:46] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 12:02:46] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 12:02:46] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 12:03:45] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 12:03:45] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 12:03:45] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 12:03:46] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 12:06:23] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 12:06:23] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 12:06:23] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 12:06:23] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 12:07:35] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 12:07:35] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 12:07:35] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 12:07:35] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 12:09:36] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 12:09:36] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 12:09:36] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 12:09:36] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 12:12:40] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 12:12:40] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 12:12:40] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 12:12:41] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 12:14:45] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 12:14:45] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 12:14:45] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 12:14:45] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 12:16:41] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 12:16:41] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 12:16:41] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 12:16:41] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 12:16:58] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 12:16:58] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 12:16:58] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 12:16:58] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 12:18:26] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 12:18:26] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 12:18:26] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 12:18:26] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 12:21:10] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 12:21:10] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 12:21:10] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 12:21:10] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 12:24:50] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 12:24:50] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 12:24:50] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 12:24:50] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 12:30:18] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 12:30:18] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 12:30:18] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 12:30:18] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 12:32:13] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 12:32:13] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 12:32:13] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 12:32:13] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 12:36:39] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 12:36:39] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 12:36:39] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 12:36:39] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 20:54:14] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 20:54:14] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 20:54:14] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 20:54:14] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 21:24:19] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 21:24:19] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 21:24:19] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 21:24:19] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 21:26:58] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 21:26:58] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 21:26:58] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 21:26:59] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 21:44:25] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 21:44:25] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 21:44:25] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 21:44:25] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 22:15:29] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 22:15:29] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 22:15:29] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 22:15:29] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-13 23:10:54] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-13 23:10:54] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-13 23:10:54] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-13 23:10:54] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 00:04:25] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 00:04:25] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 00:04:25] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 00:04:25] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 00:04:25] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 00:13:17] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 00:13:17] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 00:13:17] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 00:13:17] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 00:13:17] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 00:20:19] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 00:20:19] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 00:20:19] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 00:20:19] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 00:20:19] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 00:26:24] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 00:26:24] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 00:26:24] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 00:26:24] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 00:26:24] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 00:26:31] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 00:26:31] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 00:26:31] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 00:26:31] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 00:26:31] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 00:27:52] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 00:27:52] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 00:27:52] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 00:27:52] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 00:27:52] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 00:29:23] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 00:29:23] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 00:29:23] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 00:29:23] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 00:29:23] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 00:32:59] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 00:32:59] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 00:32:59] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 00:32:59] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 00:32:59] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 00:34:44] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 00:34:44] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 00:34:44] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 00:34:44] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 00:34:44] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 00:35:49] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 00:35:49] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 00:35:49] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 00:35:49] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 00:35:49] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 00:39:01] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 00:39:01] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 00:39:01] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 00:39:01] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 00:39:01] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 00:42:49] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 00:42:49] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 00:42:49] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 00:42:49] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 00:42:49] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 00:43:09] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 00:43:09] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 00:43:09] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 00:43:09] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 00:43:09] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 00:50:38] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 00:50:38] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 00:50:38] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 00:50:38] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 00:50:38] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 00:51:57] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 00:51:57] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 00:51:57] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 00:51:57] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 00:51:57] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 01:13:51] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 01:13:51] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 01:13:51] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 01:13:51] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 01:13:51] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 01:14:41] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 01:14:41] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 01:14:41] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 01:14:41] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 01:14:41] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 01:16:36] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 01:16:36] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 01:16:36] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 01:16:36] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 01:16:36] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 01:19:24] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 01:19:24] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 01:19:24] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 01:19:24] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 01:19:24] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 01:19:52] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 01:19:52] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 01:19:52] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 01:19:52] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 01:19:52] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-14 01:20:03] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-14 01:20:03] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-14 01:20:03] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-14 01:20:03] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-14 01:20:03] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-15 02:36:15] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-15 02:36:15] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-15 02:36:15] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-15 02:36:15] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-15 02:36:15] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-18 03:26:07] [INFO] [Sistema] Iniciando SIAX Agent
[2026-01-18 03:26:07] [INFO] [Sistema] Escaneando servicios systemd existentes...
[2026-01-18 03:26:07] [INFO] [Discovery] Escaneando servicios systemd existentes...
[2026-01-18 03:26:07] [INFO] [Discovery] ✅ Descubiertos 0 servicios
[2026-01-18 03:26:07] [INFO] [Config] Usando archivo de configuración: config/monitored_apps.json
[2026-01-18 03:26:07] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
[2026-01-18 03:26:07] [INFO] [Sistema] Servidor detectado: siax-intel
[2026-01-18 03:26:07] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
[2026-01-18 03:26:07] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
[2026-01-18 03:26:07] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
[2026-01-18 03:26:07] [INFO] [Monitor] Buscando app app_tareas en API central...
[2026-01-18 03:26:07] [INFO] [Monitor] App app_tareas encontrada en cloud (ID: 3)
[2026-01-18 03:26:07] [ERROR] [Monitor] Error sincronizando app_tareas | HTTP 500 Internal Server Error: {"success":false,"message":"Error al actualizar el estado","error":"Table 'webControl.app_service_history' doesn't exist"}
[2026-01-18 03:26:07] [INFO] [Monitor] Buscando app fidelizacion en API central...
[2026-01-18 03:26:07] [INFO] [Monitor] App fidelizacion encontrada en cloud (ID: 4)
[2026-01-18 03:26:07] [ERROR] [Monitor] Error sincronizando fidelizacion | HTTP 500 Internal Server Error: {"success":false,"message":"Error al actualizar el estado","error":"Table 'webControl.app_service_history' doesn't exist"}

66
preparar_binario.sh Executable file
View File

@@ -0,0 +1,66 @@
#!/bin/bash
#######################################
# SIAX Agent - Preparar Binario para Distribución
# Compila y copia el binario a web/static/binary/
#######################################
set -e
GREEN='\033[0;32m'
BLUE='\033[0;34m'
RED='\033[0;31m'
NC='\033[0m'
echo -e "${BLUE}============================================${NC}"
echo -e "${BLUE} Preparando SIAX Agent para Distribución${NC}"
echo -e "${BLUE}============================================${NC}"
echo ""
# Compilar en release
echo -e "${BLUE}📦 Compilando en modo release...${NC}"
cargo build --release
if [ ! -f "target/release/siax_monitor" ]; then
echo -e "${RED}❌ Error: No se pudo compilar el binario${NC}"
exit 1
fi
echo -e "${GREEN}✅ Compilación exitosa${NC}"
echo ""
# Crear directorio para binarios
echo -e "${BLUE}📁 Creando directorio web/static/binary/${NC}"
mkdir -p web/static/binary
# Copiar binario
echo -e "${BLUE}📋 Copiando binario...${NC}"
cp target/release/siax_monitor web/static/binary/siax-agent
chmod +x web/static/binary/siax-agent
echo -e "${GREEN}✅ Binario copiado a web/static/binary/siax-agent${NC}"
echo ""
# Mostrar información
BINARY_SIZE=$(du -h web/static/binary/siax-agent | cut -f1)
echo -e "${GREEN}============================================${NC}"
echo -e "${GREEN} ✅ Preparación completada${NC}"
echo -e "${GREEN}============================================${NC}"
echo ""
echo "📊 Tamaño del binario: $BINARY_SIZE"
echo "📂 Ubicación: web/static/binary/siax-agent"
echo ""
echo "🚀 Ahora puedes:"
echo ""
echo " 1. Iniciar el servidor:"
echo " cargo run --release"
echo ""
echo " 2. Desde otro servidor, instalar con:"
echo " curl -sSL http://TU-SERVIDOR:8080/install.sh | sudo bash"
echo ""
echo " O especificar el servidor:"
echo " curl -sSL http://TU-SERVIDOR:8080/install.sh | sudo SIAX_SERVER=TU-SERVIDOR:8080 bash"
echo ""
echo "Ejemplo VPN:"
echo " curl -sSL http://10.8.0.1:8080/install.sh | sudo SIAX_SERVER=10.8.0.1:8080 bash"
echo ""

View File

@@ -6,14 +6,51 @@ use crate::logger::get_logger;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MonitoredApp {
/// Nombre de la aplicación
pub name: String,
/// Nombre del servicio systemd (ej: siax-app-TAREAS.service)
#[serde(default)]
pub service_name: String,
/// Ruta completa al directorio de la aplicación (WorkingDirectory)
#[serde(default)]
pub path: String,
/// Puerto donde escucha la aplicación
pub port: i32,
/// Archivo de entrada (ej: server.js, app.js)
#[serde(default)]
pub entry_point: String,
/// Ruta completa al binario de node/python
#[serde(default)]
pub node_bin: String,
/// Modo de ejecución (production, development, test)
#[serde(default = "default_mode")]
pub mode: String,
/// Ruta completa al archivo .service de systemd
#[serde(default)]
pub service_file_path: String,
/// Fecha de registro (ISO 8601)
#[serde(default, skip_serializing_if = "String::is_empty", rename = "reg")]
pub registered_at: String,
// DEPRECATED: Mantener por compatibilidad con versiones antiguas
#[serde(skip_serializing_if = "Option::is_none")]
pub systemd_service: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<String>,
}
fn default_mode() -> String {
"production".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AppConfig {
pub apps: Vec<MonitoredApp>,
@@ -99,23 +136,16 @@ impl ConfigManager {
config.apps.clone()
}
pub fn add_app(&self, name: String, port: i32) -> Result<(), String> {
/// Agrega una app con información completa
pub fn add_app_full(&self, app: MonitoredApp) -> 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));
if config.apps.iter().any(|a| a.name == app.name) {
return Err(format!("La app '{}' ya está siendo monitoreada", app.name));
}
let systemd_service = format!("siax-app-{}.service", name);
let created_at = chrono::Local::now().to_rfc3339();
config.apps.push(MonitoredApp {
name,
port,
systemd_service: Some(systemd_service),
created_at: Some(created_at),
});
config.apps.push(app);
// Guardar en disco
match Self::save_config_to_file(&self.config_path, &config) {
@@ -124,6 +154,29 @@ impl ConfigManager {
}
}
/// Método simplificado para compatibilidad (DEPRECATED)
#[deprecated(note = "Usar add_app_full() con MonitoredApp completo")]
pub fn add_app(&self, name: String, port: i32) -> Result<(), String> {
let service_name = format!("siax-app-{}.service", name);
let registered_at = chrono::Local::now().to_rfc3339();
let app = MonitoredApp {
name,
service_name,
path: String::new(),
port,
entry_point: String::new(),
node_bin: String::new(),
mode: "production".to_string(),
service_file_path: String::new(),
registered_at,
systemd_service: None,
created_at: None,
};
self.add_app_full(app)
}
pub fn remove_app(&self, name: &str) -> Result<(), String> {
let mut config = self.config.write().unwrap();

View File

@@ -67,9 +67,12 @@ pub struct DiscoveredService {
pub user: Option<String>,
pub exec_start: Option<String>,
pub port: Option<i32>,
pub node_env: String,
pub entry_point: Option<String>,
pub node_bin: Option<String>,
}
/// Parsea un archivo .service para extraer configuración básica
/// Parsea un archivo .service para extraer configuración completa
fn parse_service_file(path: &Path, app_name: &str) -> Option<DiscoveredService> {
let logger = get_logger();
@@ -88,6 +91,9 @@ fn parse_service_file(path: &Path, app_name: &str) -> Option<DiscoveredService>
user: None,
exec_start: None,
port: None,
node_env: String::from("production"),
entry_point: None,
node_bin: None,
};
// Parsear líneas del archivo
@@ -106,7 +112,24 @@ fn parse_service_file(path: &Path, app_name: &str) -> Option<DiscoveredService>
// ExecStart
if line.starts_with("ExecStart=") {
service.exec_start = Some(line.trim_start_matches("ExecStart=").to_string());
let exec_start = line.trim_start_matches("ExecStart=").to_string();
// Extraer node_bin y entry_point del ExecStart
// Ejemplo: /home/user/.nvm/versions/node/v24.12.0/bin/node server.js
let parts: Vec<&str> = exec_start.split_whitespace().collect();
if !parts.is_empty() {
service.node_bin = Some(parts[0].to_string());
// Buscar el archivo .js como entry_point
for part in &parts[1..] {
if part.ends_with(".js") {
service.entry_point = Some(part.to_string());
break;
}
}
}
service.exec_start = Some(exec_start);
}
// Environment con PORT
@@ -115,12 +138,21 @@ fn parse_service_file(path: &Path, app_name: &str) -> Option<DiscoveredService>
service.port = Some(port);
}
}
// Environment con NODE_ENV
if line.starts_with("Environment=") && line.contains("NODE_ENV") {
if let Some(env) = extract_env_value(line, "NODE_ENV") {
service.node_env = env;
}
}
}
logger.info("Discovery", &format!(" App: {}, User: {:?}, WorkDir: {:?}",
logger.info("Discovery", &format!(" App: {}, User: {:?}, WorkDir: {:?}, Env: {}, EntryPoint: {:?}",
service.app_name,
service.user,
service.working_directory
service.working_directory,
service.node_env,
service.entry_point
));
Some(service)
@@ -143,6 +175,27 @@ fn extract_port_from_env(line: &str) -> Option<i32> {
}
}
/// Extrae un valor de variable de entorno de una línea Environment
/// Ejemplo: Environment="NODE_ENV=production" -> Some("production")
fn extract_env_value(line: &str, var_name: &str) -> Option<String> {
let pattern = format!("{}=", var_name);
if let Some(start) = line.find(&pattern) {
let after_var = &line[start + pattern.len()..];
// Extraer hasta espacios, comillas o fin de línea
let value: String = after_var.chars()
.take_while(|c| !c.is_whitespace() && *c != '"')
.collect();
if !value.is_empty() {
Some(value)
} else {
None
}
} else {
None
}
}
/// Sincroniza los servicios descubiertos con monitored_apps.json
pub fn sync_discovered_services(services: Vec<DiscoveredService>) {
let logger = get_logger();
@@ -156,7 +209,6 @@ pub fn sync_discovered_services(services: Vec<DiscoveredService>) {
for service in services {
// Intentar detectar el puerto si no se encontró en Environment
let port = service.port.unwrap_or_else(|| {
// Intentar extraer del nombre o usar puerto por defecto
detect_port_from_name(&service.app_name)
});
@@ -170,10 +222,29 @@ pub fn sync_discovered_services(services: Vec<DiscoveredService>) {
continue;
}
// Agregar a monitored_apps.json
logger.info("Discovery", &format!(" Agregando {} (puerto: {})", service.app_name, port));
// Crear MonitoredApp con información completa
let service_name = format!("siax-app-{}.service", service.app_name);
let registered_at = chrono::Local::now().to_rfc3339();
match config_manager.add_app(service.app_name.clone(), port) {
let app = MonitoredApp {
name: service.app_name.clone(),
service_name,
path: service.working_directory.unwrap_or_default(),
port,
entry_point: service.entry_point.unwrap_or_default(),
node_bin: service.node_bin.unwrap_or_default(),
mode: service.node_env,
service_file_path: service.service_file.clone(),
registered_at,
systemd_service: None,
created_at: None,
};
// Agregar a monitored_apps.json
logger.info("Discovery", &format!(" Agregando {} (puerto: {}, entry: {})",
app.name, app.port, app.entry_point));
match config_manager.add_app_full(app) {
Ok(_) => {
logger.info("Discovery", &format!("{} agregado exitosamente", service.app_name));
added_count += 1;

View File

@@ -2,6 +2,7 @@ use super::{Result, OrchestratorError};
use crate::models::{ServiceConfig, ManagedApp, AppStatus};
use crate::systemd::{ServiceGenerator, SystemCtl};
use crate::logger::get_logger;
use crate::config::{get_config_manager, MonitoredApp};
use dashmap::DashMap;
use std::sync::Arc;
@@ -52,6 +53,49 @@ impl AppManager {
// Guardar en memoria
self.apps.insert(config.app_name.clone(), config.clone());
// Guardar en monitored_apps.json con información completa
let config_manager = get_config_manager();
let service_file_path = format!("/etc/systemd/system/{}", config.service_name());
let registered_at = chrono::Local::now().to_rfc3339();
// Extraer el puerto del environment si existe
let port = config.environment.get("PORT")
.and_then(|p| p.parse::<i32>().ok())
.unwrap_or(8080);
// Determinar el entry_point desde script_path
let entry_point = std::path::Path::new(&config.script_path)
.file_name()
.and_then(|f| f.to_str())
.unwrap_or("server.js")
.to_string();
// Determinar node_bin (será resuelto por el ServiceGenerator)
let node_bin = config.custom_executable.clone().unwrap_or_default();
// Determinar mode desde NODE_ENV
let mode = config.environment.get("NODE_ENV")
.cloned()
.unwrap_or_else(|| "production".to_string());
let monitored_app = MonitoredApp {
name: config.app_name.clone(),
service_name: config.service_name(),
path: config.working_directory.clone(),
port,
entry_point,
node_bin,
mode,
service_file_path,
registered_at,
systemd_service: None,
created_at: None,
};
if let Err(e) = config_manager.add_app_full(monitored_app) {
logger.warning("AppManager", "No se pudo guardar en monitored_apps.json", Some(&e));
}
logger.info("AppManager", &format!("Aplicación {} registrada exitosamente", config.app_name));
Ok(())
@@ -84,6 +128,12 @@ impl AppManager {
// Eliminar de memoria
self.apps.remove(app_name);
// Eliminar de monitored_apps.json
let config_manager = get_config_manager();
if let Err(e) = config_manager.remove_app(app_name) {
logger.warning("AppManager", "No se pudo eliminar de monitored_apps.json", Some(&e));
}
logger.info("AppManager", &format!("Aplicación {} desregistrada exitosamente", app_name));
Ok(())

View File

@@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>Documentación API - SIAX Monitor</title>
<link rel="icon" type="image/svg+xml" href="/static/icon/favicon.svg" />
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
@@ -112,17 +113,47 @@
<div class="flex-1 flex max-w-[1400px] mx-auto w-full">
<!-- Sidebar - Table of Contents -->
<aside class="w-64 border-r border-[#283039] bg-[#161f2a] p-6 space-y-6 overflow-y-auto">
<aside
class="w-64 border-r border-[#283039] bg-[#161f2a] p-6 space-y-6 overflow-y-auto"
>
<div>
<h3 class="text-white font-bold text-sm mb-3">CONTENIDO</h3>
<nav class="space-y-2">
<a href="#intro" class="block text-[#9dabb9] text-sm hover:text-primary transition-colors">Introducción</a>
<a href="#auth" class="block text-[#9dabb9] text-sm hover:text-primary transition-colors">Autenticación</a>
<a href="#apps" class="block text-[#9dabb9] text-sm hover:text-primary transition-colors">Gestión de Apps</a>
<a href="#scan" class="block text-[#9dabb9] text-sm hover:text-primary transition-colors">Escaneo</a>
<a href="#lifecycle" class="block text-[#9dabb9] text-sm hover:text-primary transition-colors">Ciclo de Vida</a>
<a href="#websocket" class="block text-[#9dabb9] text-sm hover:text-primary transition-colors">WebSocket</a>
<a href="#errors" class="block text-[#9dabb9] text-sm hover:text-primary transition-colors">Códigos de Error</a>
<a
href="#intro"
class="block text-[#9dabb9] text-sm hover:text-primary transition-colors"
>Introducción</a
>
<a
href="#auth"
class="block text-[#9dabb9] text-sm hover:text-primary transition-colors"
>Autenticación</a
>
<a
href="#apps"
class="block text-[#9dabb9] text-sm hover:text-primary transition-colors"
>Gestión de Apps</a
>
<a
href="#scan"
class="block text-[#9dabb9] text-sm hover:text-primary transition-colors"
>Escaneo</a
>
<a
href="#lifecycle"
class="block text-[#9dabb9] text-sm hover:text-primary transition-colors"
>Ciclo de Vida</a
>
<a
href="#websocket"
class="block text-[#9dabb9] text-sm hover:text-primary transition-colors"
>WebSocket</a
>
<a
href="#errors"
class="block text-[#9dabb9] text-sm hover:text-primary transition-colors"
>Códigos de Error</a
>
</nav>
</div>
@@ -135,7 +166,9 @@
</div>
<div>
<span class="text-[#9dabb9]">Base URL:</span>
<span class="text-white font-mono">localhost:8080</span>
<span class="text-white font-mono"
>localhost:8080</span
>
</div>
<div>
<span class="text-[#9dabb9]">Protocolo:</span>
@@ -149,35 +182,70 @@
<main class="flex-1 p-8 overflow-y-auto">
<!-- Introduction -->
<section id="intro" class="mb-12">
<h1 class="text-white text-4xl font-black mb-4">Documentación API REST</h1>
<h1 class="text-white text-4xl font-black mb-4">
Documentación API REST
</h1>
<p class="text-[#9dabb9] text-lg mb-6">
API para gestión y monitoreo de aplicaciones Node.js y Python con systemd.
API para gestión y monitoreo de aplicaciones Node.js y
Python con systemd.
</p>
<div class="rounded-xl border border-primary/30 bg-primary/10 p-4 mb-6">
<div
class="rounded-xl border border-primary/30 bg-primary/10 p-4 mb-6"
>
<div class="flex items-start gap-3">
<span class="material-symbols-outlined text-primary mt-0.5">info</span>
<span
class="material-symbols-outlined text-primary mt-0.5"
>info</span
>
<div>
<p class="text-white font-semibold mb-1">Endpoint Base</p>
<code class="text-primary font-mono text-sm">http://localhost:8080/api</code>
<p class="text-white font-semibold mb-1">
Endpoint Base
</p>
<code class="text-primary font-mono text-sm"
>/api</code
>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="rounded-xl border border-[#283039] bg-[#161f2a] p-4">
<span class="material-symbols-outlined text-green-400 mb-2">check_circle</span>
<p class="text-white font-semibold text-sm">REST API</p>
<div
class="rounded-xl border border-[#283039] bg-[#161f2a] p-4"
>
<span
class="material-symbols-outlined text-green-400 mb-2"
>check_circle</span
>
<p class="text-white font-semibold text-sm">
REST API
</p>
<p class="text-[#9dabb9] text-xs">JSON responses</p>
</div>
<div class="rounded-xl border border-[#283039] bg-[#161f2a] p-4">
<span class="material-symbols-outlined text-blue-400 mb-2">bolt</span>
<p class="text-white font-semibold text-sm">WebSocket</p>
<p class="text-[#9dabb9] text-xs">Logs en tiempo real</p>
<div
class="rounded-xl border border-[#283039] bg-[#161f2a] p-4"
>
<span
class="material-symbols-outlined text-blue-400 mb-2"
>bolt</span
>
<p class="text-white font-semibold text-sm">
WebSocket
</p>
<p class="text-[#9dabb9] text-xs">
Logs en tiempo real
</p>
</div>
<div class="rounded-xl border border-[#283039] bg-[#161f2a] p-4">
<span class="material-symbols-outlined text-purple-400 mb-2">schedule</span>
<p class="text-white font-semibold text-sm">Rate Limiting</p>
<div
class="rounded-xl border border-[#283039] bg-[#161f2a] p-4"
>
<span
class="material-symbols-outlined text-purple-400 mb-2"
>schedule</span
>
<p class="text-white font-semibold text-sm">
Rate Limiting
</p>
<p class="text-[#9dabb9] text-xs">1 op/segundo</p>
</div>
</div>
@@ -185,19 +253,34 @@
<!-- Authentication -->
<section id="auth" class="mb-12">
<h2 class="text-white text-2xl font-bold mb-4 flex items-center gap-2">
<span class="material-symbols-outlined text-primary">lock</span>
<h2
class="text-white text-2xl font-bold mb-4 flex items-center gap-2"
>
<span class="material-symbols-outlined text-primary"
>lock</span
>
Autenticación
</h2>
<p class="text-[#9dabb9] mb-4">
Actualmente la API no requiere autenticación ya que está diseñada para acceso local vía VPN.
Actualmente la API no requiere autenticación ya que está
diseñada para acceso local vía VPN.
</p>
<div class="rounded-xl border border-yellow-500/30 bg-yellow-500/10 p-4">
<div
class="rounded-xl border border-yellow-500/30 bg-yellow-500/10 p-4"
>
<div class="flex items-start gap-3">
<span class="material-symbols-outlined text-yellow-400">warning</span>
<span
class="material-symbols-outlined text-yellow-400"
>warning</span
>
<div>
<p class="text-yellow-400 font-semibold">Nota de Seguridad</p>
<p class="text-[#9dabb9] text-sm">Esta API debe ser accesible solo desde redes privadas o VPN.</p>
<p class="text-yellow-400 font-semibold">
Nota de Seguridad
</p>
<p class="text-[#9dabb9] text-sm">
Esta API debe ser accesible solo desde redes
privadas o VPN.
</p>
</div>
</div>
</div>
@@ -205,52 +288,98 @@
<!-- Apps Management -->
<section id="apps" class="mb-12">
<h2 class="text-white text-2xl font-bold mb-6 flex items-center gap-2">
<span class="material-symbols-outlined text-primary">apps</span>
<h2
class="text-white text-2xl font-bold mb-6 flex items-center gap-2"
>
<span class="material-symbols-outlined text-primary"
>apps</span
>
Gestión de Aplicaciones
</h2>
<!-- List Apps -->
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
<div
class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden"
>
<div
class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]"
>
<div class="flex items-center gap-3">
<span class="px-2 py-1 rounded bg-green-500/20 text-green-400 text-xs font-bold font-mono">GET</span>
<code class="text-white font-mono text-sm">/api/apps</code>
<span
class="px-2 py-1 rounded bg-green-500/20 text-green-400 text-xs font-bold font-mono"
>GET</span
>
<code class="text-white font-mono text-sm"
>/api/apps</code
>
</div>
<p class="text-[#9dabb9] text-sm mt-2">Listar todas las aplicaciones registradas</p>
<p class="text-[#9dabb9] text-sm mt-2">
Listar todas las aplicaciones registradas
</p>
</div>
<div class="p-6 space-y-4">
<div>
<p class="text-white font-semibold text-sm mb-2">Respuesta exitosa (200)</p>
<pre class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-green-400 overflow-x-auto">{
<p
class="text-white font-semibold text-sm mb-2"
>
Respuesta exitosa (200)
</p>
<pre
class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-green-400 overflow-x-auto"
>
{
"success": true,
"data": {
"apps": ["app_tareas", "fidelizacion"],
"total": 2
},
"error": null
}</pre>
}</pre
>
</div>
<button onclick="tryEndpoint('GET', '/api/apps')" class="flex items-center gap-2 px-4 py-2 bg-primary hover:brightness-110 rounded-lg text-white text-sm font-medium transition-all">
<span class="material-symbols-outlined text-sm">play_arrow</span>
<button
onclick="tryEndpoint('GET', '/api/apps')"
class="flex items-center gap-2 px-4 py-2 bg-primary hover:brightness-110 rounded-lg text-white text-sm font-medium transition-all"
>
<span class="material-symbols-outlined text-sm"
>play_arrow</span
>
Probar endpoint
</button>
</div>
</div>
<!-- Register App -->
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
<div
class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden"
>
<div
class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]"
>
<div class="flex items-center gap-3">
<span class="px-2 py-1 rounded bg-blue-500/20 text-blue-400 text-xs font-bold font-mono">POST</span>
<code class="text-white font-mono text-sm">/api/apps</code>
<span
class="px-2 py-1 rounded bg-blue-500/20 text-blue-400 text-xs font-bold font-mono"
>POST</span
>
<code class="text-white font-mono text-sm"
>/api/apps</code
>
</div>
<p class="text-[#9dabb9] text-sm mt-2">Registrar una nueva aplicación</p>
<p class="text-[#9dabb9] text-sm mt-2">
Registrar una nueva aplicación
</p>
</div>
<div class="p-6 space-y-4">
<div>
<p class="text-white font-semibold text-sm mb-2">Body (JSON)</p>
<pre class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-blue-400 overflow-x-auto">{
<p
class="text-white font-semibold text-sm mb-2"
>
Body (JSON)
</p>
<pre
class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-blue-400 overflow-x-auto"
>
{
"app_name": "mi-app",
"script_path": "/opt/apps/mi-app/index.js",
"working_directory": "/opt/apps/mi-app",
@@ -262,11 +391,19 @@
"restart_policy": "always",
"app_type": "nodejs",
"description": "Mi aplicación Node.js"
}</pre>
}</pre
>
</div>
<div>
<p class="text-white font-semibold text-sm mb-2">Respuesta exitosa (200)</p>
<pre class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-green-400 overflow-x-auto">{
<p
class="text-white font-semibold text-sm mb-2"
>
Respuesta exitosa (200)
</p>
<pre
class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-green-400 overflow-x-auto"
>
{
"success": true,
"data": {
"app_name": "mi-app",
@@ -275,27 +412,48 @@
"message": "Aplicación registrada exitosamente"
},
"error": null
}</pre>
}</pre
>
</div>
</div>
</div>
<!-- Delete App -->
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
<div
class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden"
>
<div
class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]"
>
<div class="flex items-center gap-3">
<span class="px-2 py-1 rounded bg-red-500/20 text-red-400 text-xs font-bold font-mono">DELETE</span>
<code class="text-white font-mono text-sm">/api/apps/:name</code>
<span
class="px-2 py-1 rounded bg-red-500/20 text-red-400 text-xs font-bold font-mono"
>DELETE</span
>
<code class="text-white font-mono text-sm"
>/api/apps/:name</code
>
</div>
<p class="text-[#9dabb9] text-sm mt-2">Eliminar una aplicación registrada</p>
<p class="text-[#9dabb9] text-sm mt-2">
Eliminar una aplicación registrada
</p>
</div>
<div class="p-6 space-y-4">
<div>
<p class="text-white font-semibold text-sm mb-2">Parámetros</p>
<p
class="text-white font-semibold text-sm mb-2"
>
Parámetros
</p>
<ul class="space-y-2">
<li class="flex items-start gap-2">
<code class="text-primary font-mono text-sm">name</code>
<span class="text-[#9dabb9] text-sm">- Nombre de la aplicación</span>
<code
class="text-primary font-mono text-sm"
>name</code
>
<span class="text-[#9dabb9] text-sm"
>- Nombre de la aplicación</span
>
</li>
</ul>
</div>
@@ -303,18 +461,36 @@
</div>
<!-- Get Status -->
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
<div
class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden"
>
<div
class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]"
>
<div class="flex items-center gap-3">
<span class="px-2 py-1 rounded bg-green-500/20 text-green-400 text-xs font-bold font-mono">GET</span>
<code class="text-white font-mono text-sm">/api/apps/:name/status</code>
<span
class="px-2 py-1 rounded bg-green-500/20 text-green-400 text-xs font-bold font-mono"
>GET</span
>
<code class="text-white font-mono text-sm"
>/api/apps/:name/status</code
>
</div>
<p class="text-[#9dabb9] text-sm mt-2">Obtener estado de una aplicación</p>
<p class="text-[#9dabb9] text-sm mt-2">
Obtener estado de una aplicación
</p>
</div>
<div class="p-6 space-y-4">
<div>
<p class="text-white font-semibold text-sm mb-2">Respuesta exitosa (200)</p>
<pre class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-green-400 overflow-x-auto">{
<p
class="text-white font-semibold text-sm mb-2"
>
Respuesta exitosa (200)
</p>
<pre
class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-green-400 overflow-x-auto"
>
{
"success": true,
"data": {
"name": "mi-app",
@@ -325,7 +501,8 @@
"systemd_status": "active",
"last_updated": "2026-01-13T12:34:56"
}
}</pre>
}</pre
>
</div>
</div>
</div>
@@ -333,23 +510,45 @@
<!-- Scan -->
<section id="scan" class="mb-12">
<h2 class="text-white text-2xl font-bold mb-6 flex items-center gap-2">
<span class="material-symbols-outlined text-primary">search</span>
<h2
class="text-white text-2xl font-bold mb-6 flex items-center gap-2"
>
<span class="material-symbols-outlined text-primary"
>search</span
>
Escaneo de Procesos
</h2>
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
<div
class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden"
>
<div
class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]"
>
<div class="flex items-center gap-3">
<span class="px-2 py-1 rounded bg-green-500/20 text-green-400 text-xs font-bold font-mono">GET</span>
<code class="text-white font-mono text-sm">/api/scan</code>
<span
class="px-2 py-1 rounded bg-green-500/20 text-green-400 text-xs font-bold font-mono"
>GET</span
>
<code class="text-white font-mono text-sm"
>/api/scan</code
>
</div>
<p class="text-[#9dabb9] text-sm mt-2">Escanear procesos Node.js y Python en ejecución</p>
<p class="text-[#9dabb9] text-sm mt-2">
Escanear procesos Node.js y Python en ejecución
</p>
</div>
<div class="p-6 space-y-4">
<div>
<p class="text-white font-semibold text-sm mb-2">Respuesta exitosa (200)</p>
<pre class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-green-400 overflow-x-auto">{
<p
class="text-white font-semibold text-sm mb-2"
>
Respuesta exitosa (200)
</p>
<pre
class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-green-400 overflow-x-auto"
>
{
"success": true,
"data": {
"processes": [
@@ -364,10 +563,16 @@
],
"total": 1
}
}</pre>
}</pre
>
</div>
<button onclick="tryEndpoint('GET', '/api/scan')" class="flex items-center gap-2 px-4 py-2 bg-primary hover:brightness-110 rounded-lg text-white text-sm font-medium transition-all">
<span class="material-symbols-outlined text-sm">play_arrow</span>
<button
onclick="tryEndpoint('GET', '/api/scan')"
class="flex items-center gap-2 px-4 py-2 bg-primary hover:brightness-110 rounded-lg text-white text-sm font-medium transition-all"
>
<span class="material-symbols-outlined text-sm"
>play_arrow</span
>
Probar endpoint
</button>
</div>
@@ -376,50 +581,97 @@
<!-- Lifecycle -->
<section id="lifecycle" class="mb-12">
<h2 class="text-white text-2xl font-bold mb-6 flex items-center gap-2">
<span class="material-symbols-outlined text-primary">settings_power</span>
<h2
class="text-white text-2xl font-bold mb-6 flex items-center gap-2"
>
<span class="material-symbols-outlined text-primary"
>settings_power</span
>
Ciclo de Vida
</h2>
<!-- Start -->
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
<div
class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden"
>
<div
class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]"
>
<div class="flex items-center gap-3">
<span class="px-2 py-1 rounded bg-blue-500/20 text-blue-400 text-xs font-bold font-mono">POST</span>
<code class="text-white font-mono text-sm">/api/apps/:name/start</code>
<span
class="px-2 py-1 rounded bg-blue-500/20 text-blue-400 text-xs font-bold font-mono"
>POST</span
>
<code class="text-white font-mono text-sm"
>/api/apps/:name/start</code
>
</div>
<p class="text-[#9dabb9] text-sm mt-2">Iniciar una aplicación</p>
<p class="text-[#9dabb9] text-sm mt-2">
Iniciar una aplicación
</p>
</div>
</div>
<!-- Stop -->
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
<div
class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden"
>
<div
class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]"
>
<div class="flex items-center gap-3">
<span class="px-2 py-1 rounded bg-blue-500/20 text-blue-400 text-xs font-bold font-mono">POST</span>
<code class="text-white font-mono text-sm">/api/apps/:name/stop</code>
<span
class="px-2 py-1 rounded bg-blue-500/20 text-blue-400 text-xs font-bold font-mono"
>POST</span
>
<code class="text-white font-mono text-sm"
>/api/apps/:name/stop</code
>
</div>
<p class="text-[#9dabb9] text-sm mt-2">Detener una aplicación</p>
<p class="text-[#9dabb9] text-sm mt-2">
Detener una aplicación
</p>
</div>
</div>
<!-- Restart -->
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
<div
class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden"
>
<div
class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]"
>
<div class="flex items-center gap-3">
<span class="px-2 py-1 rounded bg-blue-500/20 text-blue-400 text-xs font-bold font-mono">POST</span>
<code class="text-white font-mono text-sm">/api/apps/:name/restart</code>
<span
class="px-2 py-1 rounded bg-blue-500/20 text-blue-400 text-xs font-bold font-mono"
>POST</span
>
<code class="text-white font-mono text-sm"
>/api/apps/:name/restart</code
>
</div>
<p class="text-[#9dabb9] text-sm mt-2">Reiniciar una aplicación</p>
<p class="text-[#9dabb9] text-sm mt-2">
Reiniciar una aplicación
</p>
</div>
</div>
<div class="rounded-xl border border-yellow-500/30 bg-yellow-500/10 p-4">
<div
class="rounded-xl border border-yellow-500/30 bg-yellow-500/10 p-4"
>
<div class="flex items-start gap-3">
<span class="material-symbols-outlined text-yellow-400">schedule</span>
<span
class="material-symbols-outlined text-yellow-400"
>schedule</span
>
<div>
<p class="text-yellow-400 font-semibold">Rate Limiting</p>
<p class="text-[#9dabb9] text-sm">Las operaciones están limitadas a 1 por segundo por aplicación.</p>
<p class="text-yellow-400 font-semibold">
Rate Limiting
</p>
<p class="text-[#9dabb9] text-sm">
Las operaciones están limitadas a 1 por
segundo por aplicación.
</p>
</div>
</div>
</div>
@@ -427,23 +679,45 @@
<!-- WebSocket -->
<section id="websocket" class="mb-12">
<h2 class="text-white text-2xl font-bold mb-6 flex items-center gap-2">
<span class="material-symbols-outlined text-primary">cable</span>
<h2
class="text-white text-2xl font-bold mb-6 flex items-center gap-2"
>
<span class="material-symbols-outlined text-primary"
>cable</span
>
WebSocket (Logs en tiempo real)
</h2>
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
<div
class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden"
>
<div
class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]"
>
<div class="flex items-center gap-3">
<span class="px-2 py-1 rounded bg-purple-500/20 text-purple-400 text-xs font-bold font-mono">WS</span>
<code class="text-white font-mono text-sm">ws://localhost:8080/api/apps/:name/logs</code>
<span
class="px-2 py-1 rounded bg-purple-500/20 text-purple-400 text-xs font-bold font-mono"
>WS</span
>
<code class="text-white font-mono text-sm"
>ws://localhost:8080/api/apps/:name/logs</code
>
</div>
<p class="text-[#9dabb9] text-sm mt-2">Stream de logs en tiempo real desde journalctl</p>
<p class="text-[#9dabb9] text-sm mt-2">
Stream de logs en tiempo real desde journalctl
</p>
</div>
<div class="p-6 space-y-4">
<div>
<p class="text-white font-semibold text-sm mb-2">Ejemplo JavaScript</p>
<pre class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-purple-400 overflow-x-auto">const ws = new WebSocket('ws://localhost:8080/api/apps/mi-app/logs');
<p
class="text-white font-semibold text-sm mb-2"
>
Ejemplo JavaScript
</p>
<pre
class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-purple-400 overflow-x-auto"
>
const ws = new WebSocket('ws://localhost:8080/api/apps/mi-app/logs');
ws.onopen = () => {
console.log('Conectado a logs');
@@ -460,18 +734,35 @@ ws.onerror = (error) => {
ws.onclose = () => {
console.log('Desconectado');
};</pre>
};</pre
>
</div>
<div>
<p class="text-white font-semibold text-sm mb-2">Límites</p>
<p
class="text-white font-semibold text-sm mb-2"
>
Límites
</p>
<ul class="space-y-2">
<li class="flex items-start gap-2">
<span class="material-symbols-outlined text-primary text-sm">check</span>
<span class="text-[#9dabb9] text-sm">Máximo 5 conexiones concurrentes por aplicación</span>
<span
class="material-symbols-outlined text-primary text-sm"
>check</span
>
<span class="text-[#9dabb9] text-sm"
>Máximo 5 conexiones concurrentes
por aplicación</span
>
</li>
<li class="flex items-start gap-2">
<span class="material-symbols-outlined text-primary text-sm">check</span>
<span class="text-[#9dabb9] text-sm">Formato JSON desde systemd journalctl</span>
<span
class="material-symbols-outlined text-primary text-sm"
>check</span
>
<span class="text-[#9dabb9] text-sm"
>Formato JSON desde systemd
journalctl</span
>
</li>
</ul>
</div>
@@ -481,52 +772,98 @@ ws.onclose = () => {
<!-- Error Codes -->
<section id="errors" class="mb-12">
<h2 class="text-white text-2xl font-bold mb-6 flex items-center gap-2">
<span class="material-symbols-outlined text-primary">error</span>
<h2
class="text-white text-2xl font-bold mb-6 flex items-center gap-2"
>
<span class="material-symbols-outlined text-primary"
>error</span
>
Códigos de Error
</h2>
<div class="space-y-4">
<div class="rounded-xl border border-[#283039] bg-[#161f2a] p-4">
<div
class="rounded-xl border border-[#283039] bg-[#161f2a] p-4"
>
<div class="flex items-center gap-3 mb-2">
<span class="px-2 py-1 rounded bg-red-500/20 text-red-400 text-xs font-bold font-mono">400</span>
<p class="text-white font-semibold">Bad Request</p>
<span
class="px-2 py-1 rounded bg-red-500/20 text-red-400 text-xs font-bold font-mono"
>400</span
>
<p class="text-white font-semibold">
Bad Request
</p>
</div>
<p class="text-[#9dabb9] text-sm">Datos de entrada inválidos o faltantes</p>
<p class="text-[#9dabb9] text-sm">
Datos de entrada inválidos o faltantes
</p>
</div>
<div class="rounded-xl border border-[#283039] bg-[#161f2a] p-4">
<div
class="rounded-xl border border-[#283039] bg-[#161f2a] p-4"
>
<div class="flex items-center gap-3 mb-2">
<span class="px-2 py-1 rounded bg-red-500/20 text-red-400 text-xs font-bold font-mono">404</span>
<p class="text-white font-semibold">Not Found</p>
<span
class="px-2 py-1 rounded bg-red-500/20 text-red-400 text-xs font-bold font-mono"
>404</span
>
<p class="text-white font-semibold">
Not Found
</p>
</div>
<p class="text-[#9dabb9] text-sm">Aplicación no encontrada</p>
<p class="text-[#9dabb9] text-sm">
Aplicación no encontrada
</p>
</div>
<div class="rounded-xl border border-[#283039] bg-[#161f2a] p-4">
<div
class="rounded-xl border border-[#283039] bg-[#161f2a] p-4"
>
<div class="flex items-center gap-3 mb-2">
<span class="px-2 py-1 rounded bg-red-500/20 text-red-400 text-xs font-bold font-mono">429</span>
<p class="text-white font-semibold">Too Many Requests</p>
<span
class="px-2 py-1 rounded bg-red-500/20 text-red-400 text-xs font-bold font-mono"
>429</span
>
<p class="text-white font-semibold">
Too Many Requests
</p>
</div>
<p class="text-[#9dabb9] text-sm">Rate limit excedido (1 operación/segundo)</p>
<p class="text-[#9dabb9] text-sm">
Rate limit excedido (1 operación/segundo)
</p>
</div>
<div class="rounded-xl border border-[#283039] bg-[#161f2a] p-4">
<div
class="rounded-xl border border-[#283039] bg-[#161f2a] p-4"
>
<div class="flex items-center gap-3 mb-2">
<span class="px-2 py-1 rounded bg-red-500/20 text-red-400 text-xs font-bold font-mono">500</span>
<p class="text-white font-semibold">Internal Server Error</p>
<span
class="px-2 py-1 rounded bg-red-500/20 text-red-400 text-xs font-bold font-mono"
>500</span
>
<p class="text-white font-semibold">
Internal Server Error
</p>
</div>
<p class="text-[#9dabb9] text-sm">Error interno del servidor</p>
<p class="text-[#9dabb9] text-sm">
Error interno del servidor
</p>
</div>
</div>
<div class="mt-6">
<p class="text-white font-semibold text-sm mb-2">Estructura de error</p>
<pre class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-red-400 overflow-x-auto">{
<p class="text-white font-semibold text-sm mb-2">
Estructura de error
</p>
<pre
class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-red-400 overflow-x-auto"
>
{
"success": false,
"data": null,
"error": "Descripción del error"
}</pre>
}</pre
>
</div>
</section>
</main>
@@ -534,14 +871,18 @@ ws.onclose = () => {
<script>
async function tryEndpoint(method, path) {
const resultDiv = event.target.parentElement.querySelector('.result') ||
event.target.parentElement.appendChild(document.createElement('div'));
resultDiv.className = 'result mt-4 code-block bg-[#0a0f16] p-4 rounded-lg text-sm overflow-x-auto';
resultDiv.textContent = 'Ejecutando...';
const resultDiv =
event.target.parentElement.querySelector(".result") ||
event.target.parentElement.appendChild(
document.createElement("div"),
);
resultDiv.className =
"result mt-4 code-block bg-[#0a0f16] p-4 rounded-lg text-sm overflow-x-auto";
resultDiv.textContent = "Ejecutando...";
try {
const response = await fetch(`http://localhost:8080${path}`, {
method: method
const response = await fetch(path, {
method: method,
});
const data = await response.json();
resultDiv.innerHTML = `<pre class="text-green-400">${JSON.stringify(data, null, 2)}</pre>`;
@@ -551,12 +892,17 @@ ws.onclose = () => {
}
// Smooth scroll for anchor links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
anchor.addEventListener("click", function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
const target = document.querySelector(
this.getAttribute("href"),
);
if (target) {
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
target.scrollIntoView({
behavior: "smooth",
block: "start",
});
}
});
});

764
web/blog.html Normal file
View File

@@ -0,0 +1,764 @@
<!doctype html>
<html class="dark" lang="es" dir="ltr">
<head>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>
SIAX Monitor: Sistema de Monitoreo de Aplicaciones en Rust - Blog
Telcotronics
</title>
<link rel="icon" type="image/svg+xml" href="/static/icon/favicon.svg" />
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap"
rel="stylesheet"
/>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
primary: "#137fec",
"background-light": "#f6f7f8",
"background-dark": "#101922",
},
fontFamily: {
display: ["Inter", "sans-serif"],
mono: ["JetBrains Mono", "monospace"],
},
},
},
};
</script>
<style>
body {
font-family: "Inter", sans-serif;
}
.material-symbols-outlined {
font-variation-settings:
"FILL" 0,
"wght" 400,
"GRAD" 0,
"opsz" 24;
}
.blog-content h2 {
font-size: 2rem;
font-weight: 800;
margin-top: 3rem;
margin-bottom: 1.5rem;
color: white;
}
.blog-content h3 {
font-size: 1.5rem;
font-weight: 700;
margin-top: 2rem;
margin-bottom: 1rem;
color: #e2e8f0;
}
.blog-content p {
margin-bottom: 1.25rem;
line-height: 1.75;
color: #cbd5e1;
}
.blog-content ul,
.blog-content ol {
margin-bottom: 1.5rem;
padding-left: 1.5rem;
}
.blog-content li {
margin-bottom: 0.75rem;
line-height: 1.75;
color: #cbd5e1;
}
.blog-content ul li {
list-style-type: disc;
}
.blog-content ol li {
list-style-type: decimal;
}
.blog-content strong {
color: white;
font-weight: 600;
}
.blog-content code {
background: #1e293b;
padding: 0.2rem 0.4rem;
border-radius: 0.25rem;
font-size: 0.9em;
color: #60a5fa;
}
.blog-content pre {
background: #0f172a;
padding: 1.5rem;
border-radius: 0.5rem;
overflow-x: auto;
margin-bottom: 1.5rem;
border: 1px solid #334155;
}
.blog-content pre code {
background: none;
padding: 0;
color: #94a3b8;
}
.blog-content blockquote {
border-left: 4px solid #137fec;
padding-left: 1.5rem;
margin: 1.5rem 0;
font-style: italic;
color: #94a3b8;
}
</style>
</head>
<body class="bg-background-dark text-slate-300">
<!-- Header -->
<header class="border-b border-slate-800 bg-[#0a0f16]">
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<div class="flex items-center justify-between">
<a
href="/"
class="text-2xl font-black text-white hover:text-primary transition-colors"
>
SIAX Monitor
</a>
<nav class="flex items-center gap-6">
<a
href="/"
class="text-slate-400 hover:text-white transition-colors"
>Dashboard</a
>
<a
href="/api-docs"
class="text-slate-400 hover:text-white transition-colors"
>API</a
>
<a
href="https://git.telcotronics.net/pablinux/SIAX-MONITOR"
target="_blank"
class="text-slate-400 hover:text-primary transition-colors flex items-center gap-1"
>
<span class="material-symbols-outlined text-sm"
>code</span
>
Git
</a>
</nav>
</div>
</div>
</header>
<!-- Article Container -->
<article class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<!-- Article Header -->
<header class="mb-12">
<!-- Categories -->
<div class="flex flex-wrap gap-2 mb-6">
<span
class="inline-flex items-center gap-1 px-3 py-1 rounded-full bg-primary/20 text-primary text-sm font-semibold"
>
<span class="material-symbols-outlined text-xs"
>folder</span
>
DevOps
</span>
<span
class="inline-flex items-center gap-1 px-3 py-1 rounded-full bg-green-500/20 text-green-400 text-sm font-semibold"
>
<span class="material-symbols-outlined text-xs"
>code</span
>
Rust
</span>
<span
class="inline-flex items-center gap-1 px-3 py-1 rounded-full bg-blue-500/20 text-blue-400 text-sm font-semibold"
>
<span class="material-symbols-outlined text-xs"
>monitoring</span
>
Monitoring
</span>
</div>
<!-- Title -->
<h1
class="text-4xl md:text-5xl font-black text-white mb-6 leading-tight"
>
SIAX Monitor: Sistema de Monitoreo y Gestión de Aplicaciones
Node.js y Python
</h1>
<!-- Meta info -->
<div
class="flex flex-wrap items-center gap-4 text-slate-400 text-sm"
>
<div class="flex items-center gap-2">
<div
class="w-10 h-10 rounded-full bg-primary/20 flex items-center justify-center"
>
<span
class="material-symbols-outlined text-primary text-lg"
>person</span
>
</div>
<span
>Por
<strong class="text-white">pablinux</strong></span
>
</div>
<div class="flex items-center gap-1">
<span class="material-symbols-outlined text-sm"
>calendar_today</span
>
<time datetime="2026-01-13">13 de enero, 2026</time>
</div>
<div class="flex items-center gap-1">
<span class="material-symbols-outlined text-sm"
>schedule</span
>
<span>10 min de lectura</span>
</div>
</div>
</header>
<!-- Featured Image -->
<div
class="mb-12 rounded-2xl overflow-hidden border border-slate-800 bg-gradient-to-br from-primary/20 via-background-dark to-background-dark p-12"
>
<div class="flex items-center justify-center">
<div class="text-center">
<div
class="inline-flex items-center justify-center w-24 h-24 rounded-2xl bg-primary/20 border-2 border-primary/30 mb-4"
>
<span
class="material-symbols-outlined text-primary"
style="font-size: 3rem"
>monitoring</span
>
</div>
<p class="text-slate-400 text-lg">
Monitoreo inteligente con Rust + Systemd
</p>
</div>
</div>
</div>
<!-- Article Content -->
<div class="blog-content">
<p class="text-xl text-slate-300 mb-8 leading-relaxed">
En el mundo del desarrollo moderno, gestionar múltiples
aplicaciones Node.js y Python en servidores de producción
puede convertirse rápidamente en un dolor de cabeza. SIAX
Monitor nace como una solución elegante, ligera y poderosa
para este problema, aprovechando la velocidad y seguridad de
Rust.
</p>
<h2>¿Qué es SIAX Monitor?</h2>
<p>
SIAX Monitor es un
<strong>agente de monitoreo inteligente</strong> diseñado
específicamente para entornos Linux con systemd. A
diferencia de soluciones enterprise como Prometheus o
Grafana que pueden resultar excesivas para equipos pequeños,
SIAX Monitor ofrece exactamente lo que necesitas sin
complicaciones innecesarias.
</p>
<p>
Desarrollado completamente en Rust, combina alto rendimiento
con un consumo mínimo de recursos. El proyecto utiliza
tecnologías modernas como Tokio para async runtime, Axum
para el servidor web, y se integra nativamente con systemd y
journalctl.
</p>
<h2>Características Principales</h2>
<h3>🔍 Escaneo Automático de Procesos</h3>
<p>
El sistema detecta automáticamente procesos Node.js y Python
en ejecución, recopilando información detallada como:
</p>
<ul>
<li>PID y nombre del proceso</li>
<li>Usuario propietario</li>
<li>Uso de CPU en tiempo real</li>
<li>Consumo de memoria RAM</li>
<li>Comando completo de ejecución</li>
</ul>
<h3>⚙️ Gestión de Ciclo de Vida</h3>
<p>
Control total sobre tus aplicaciones mediante la API REST:
</p>
<ul>
<li>
<code>POST /api/apps</code> - Registrar nueva aplicación
</li>
<li>
<code>POST /api/apps/:name/start</code> - Iniciar
servicio
</li>
<li>
<code>POST /api/apps/:name/stop</code> - Detener
servicio
</li>
<li>
<code>POST /api/apps/:name/restart</code> - Reiniciar
servicio
</li>
<li>
<code>GET /api/apps/:name/status</code> - Consultar
estado
</li>
</ul>
<p>
El sistema incluye <strong>rate limiting</strong> (1
operación/segundo por app) para evitar abusos y validaciones
de seguridad en todos los endpoints.
</p>
<h3>📝 Logs en Tiempo Real</h3>
<p>
Uno de los puntos más fuertes es el streaming de logs vía
WebSocket. Conectándote al endpoint
<code>ws://localhost:8080/api/apps/:name/logs</code>,
recibes logs en tiempo real desde journalctl sin necesidad
de SSH al servidor.
</p>
<p>La interfaz web incluye un visor tipo terminal con:</p>
<ul>
<li>Auto-scroll inteligente</li>
<li>Colores para niveles de log (ERROR, WARN, INFO)</li>
<li>Timestamps formateados</li>
<li>Botón para pausar/reanudar</li>
</ul>
<h3>🛡️ Seguridad y Validaciones</h3>
<p>SIAX Monitor toma la seguridad en serio:</p>
<ul>
<li>
Validación estricta de paths de trabajo (previene
directory traversal)
</li>
<li>Lista blanca de usuarios permitidos</li>
<li>
Configuración automatizada de sudoers para systemctl
</li>
<li>Hardening de servicios systemd generados</li>
<li>Rate limiting en operaciones críticas</li>
</ul>
<h3>🎨 Dashboard Moderno</h3>
<p>
La interfaz web está construida con
<strong>Tailwind CSS</strong> en tema oscuro (#101922 de
fondo, #137fec como color primario). Incluye:
</p>
<ul>
<li>
<strong>/</strong> - Dashboard con estadísticas y lista
de apps
</li>
<li>
<strong>/scan</strong> - Escaneo de procesos activos
</li>
<li>
<strong>/select</strong> - Selección de procesos para
registrar
</li>
<li>
<strong>/register</strong> - Formulario de registro
manual
</li>
<li>
<strong>/logs</strong> - Visor de logs en tiempo real
</li>
<li>
<strong>/api-docs</strong> - Documentación completa de
la API
</li>
</ul>
<h2>¿Cómo Funciona?</h2>
<h3>Arquitectura Multi-Threaded</h3>
<p>
SIAX Monitor utiliza una arquitectura basada en tres
componentes principales:
</p>
<p><strong>1. Monitor en Background</strong></p>
<p>Un thread dedicado ejecuta cada 60 segundos para:</p>
<ul>
<li>
Recopilar métricas de CPU y RAM usando
<code>sysinfo</code>
</li>
<li>Reconciliar estados entre sysinfo y systemd</li>
<li>Reportar al cloud API de SIAX (opcional)</li>
</ul>
<p><strong>2. Servidor Web Unificado</strong></p>
<p>Un servidor HTTP en puerto 8080 que fusiona:</p>
<ul>
<li>API REST (JSON responses)</li>
<li>WebSocket para logs</li>
<li>Interfaz web HTML estática</li>
<li>Archivos estáticos (favicon, logos)</li>
</ul>
<p>
Esto elimina problemas de CORS al servir todo desde el mismo
origen.
</p>
<p><strong>3. Integración Systemd</strong></p>
<p>
El módulo <code>systemd_manager</code> genera archivos
<code>.service</code> automáticamente con:
</p>
<pre><code>[Unit]
Description=App gestionada por SIAX Monitor
After=network.target
[Service]
Type=simple
User=app-user
WorkingDirectory=/opt/app
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target</code></pre>
<h2>Stack Tecnológico</h2>
<p>
El proyecto está construido sobre tecnologías modernas y
probadas:
</p>
<ul>
<li>
<strong>Rust</strong> - Lenguaje core (seguridad de
memoria, velocidad)
</li>
<li><strong>Tokio</strong> - Runtime asíncrono</li>
<li><strong>Axum 0.7</strong> - Framework web moderno</li>
<li><strong>Serde</strong> - Serialización JSON</li>
<li><strong>Sysinfo</strong> - Información del sistema</li>
<li>
<strong>Tower-HTTP</strong> - Middleware (CORS, static
files)
</li>
<li><strong>DashMap</strong> - HashMap thread-safe</li>
<li>
<strong>Tailwind CSS</strong> - Estilos del frontend
</li>
<li><strong>Material Symbols</strong> - Iconos</li>
</ul>
<h2>Ventajas y Consideraciones</h2>
<h3>✅ Ventajas</h3>
<ul>
<li>
<strong>Alto Rendimiento</strong>: Rust ofrece velocidad
cercana a C con seguridad de memoria garantizada
</li>
<li>
<strong>Ligero</strong>: Binario compilado de ~15MB,
consumo mínimo de RAM
</li>
<li>
<strong>Sin Dependencias</strong>: No requiere Node.js,
Python o base de datos
</li>
<li>
<strong>Integración Nativa</strong>: Aprovecha systemd y
journalctl del sistema
</li>
<li>
<strong>Fácil Despliegue</strong>: Single binary +
script de instalación
</li>
<li>
<strong>Open Source</strong>: Código auditable y
personalizable
</li>
</ul>
<h3>⚠️ Consideraciones</h3>
<ul>
<li>
<strong>Solo Linux + Systemd</strong>: Requiere
distribuciones con systemd (no macOS/Windows)
</li>
<li>
<strong>Permisos Sudo</strong>: Necesita configurar
sudoers para systemctl
</li>
<li>
<strong>Sin Métricas Históricas</strong>: No almacena
histórico, solo tiempo real
</li>
<li>
<strong>Solo Node.js y Python</strong>: Otros lenguajes
requieren extensión del código
</li>
<li>
<strong>Sin Autenticación</strong>: Diseñado para acceso
local/VPN, no exponer públicamente
</li>
</ul>
<h2>Casos de Uso</h2>
<h3>👔 Equipos DevOps</h3>
<p>
Gestión centralizada de microservicios en múltiples
servidores. El monitor actúa como worker node que reporta al
cloud API central, permitiendo visibilidad de toda la
infraestructura desde un solo panel.
</p>
<h3>💻 Desarrolladores</h3>
<p>
Monitoreo de aplicaciones en entornos de desarrollo y
staging sin la complejidad de herramientas enterprise.
Perfecto para proyectos pequeños a medianos que necesitan
control básico de servicios.
</p>
<h3>🖥️ Administradores de Sistemas</h3>
<p>
Control de servicios systemd con una interfaz web moderna.
Alternativa visual a comandos
<code>systemctl</code> repetitivos, con la ventaja de logs
centralizados y accesibles desde el navegador.
</p>
<h2>Instalación Rápida</h2>
<p>El proceso de instalación es extremadamente simple:</p>
<pre><code># Clonar el repositorio
git clone https://git.telcotronics.net/pablinux/SIAX-MONITOR.git
cd SIAX-MONITOR
# Compilar en modo release
cargo build --release
# Ejecutar instalador (crea usuario, configura sudoers, instala servicio)
sudo ./instalador.sh
# El servicio estará disponible en http://localhost:8080
</code></pre>
<p>
El script <code>instalador.sh</code> realiza
automáticamente:
</p>
<ul>
<li>Crear usuario del sistema <code>siax-agent</code></li>
<li>Configurar permisos sudoers para systemctl</li>
<li>Copiar binario a <code>/opt/siax-agent/</code></li>
<li>Instalar y habilitar servicio systemd</li>
<li>Verificar salud del servicio</li>
</ul>
<h2>Arquitectura de Despliegue</h2>
<p>
SIAX Monitor fue diseñado pensando en una arquitectura
distribuida:
</p>
<ul>
<li>
<strong>Cloud API</strong>:
<code>https://api.siax-system.net</code> - Panel central
de control
</li>
<li>
<strong>Worker Nodes</strong>: Agentes SIAX Monitor en
cada servidor
</li>
<li>
<strong>Comunicación</strong>: VPN segura entre workers
y cloud API
</li>
</ul>
<p>
Cada worker reporta cada 60 segundos su estado, permitiendo
monitoreo centralizado de toda la infraestructura sin
exponer puertos públicamente.
</p>
<h2>Conclusión</h2>
<p>
SIAX Monitor demuestra que no siempre necesitas soluciones
enterprise complejas para problemas simples. Con menos de
2,000 líneas de código Rust bien estructurado, ofrece
exactamente lo necesario para gestionar aplicaciones Node.js
y Python en producción.
</p>
<p>
La combinación de Rust + Systemd + WebSocket resulta en una
herramienta rápida, confiable y fácil de mantener. Es
perfecta para equipos pequeños o medianos que buscan
simplicidad sin sacrificar funcionalidad.
</p>
<p>
Si administras servidores Linux con aplicaciones Node.js o
Python, definitivamente vale la pena darle una oportunidad.
El código está disponible en
<a
href="https://git.telcotronics.net/pablinux/SIAX-MONITOR"
target="_blank"
class="text-primary hover:underline"
>Git Telcotronics</a
>
bajo licencia open source.
</p>
<blockquote>
"A veces la mejor solución no es la más compleja, sino la
que resuelve tu problema específico de la manera más
elegante posible." - Filosofía detrás de SIAX Monitor
</blockquote>
</div>
<!-- Article Footer -->
<footer class="mt-16 pt-8 border-t border-slate-800">
<div class="flex flex-wrap items-center justify-between gap-4">
<div class="flex items-center gap-3">
<div
class="w-12 h-12 rounded-full bg-primary/20 flex items-center justify-center"
>
<span class="material-symbols-outlined text-primary"
>person</span
>
</div>
<div>
<p class="text-white font-semibold">pablinux</p>
<p class="text-slate-400 text-sm">
DevOps Engineer · Rust Enthusiast
</p>
</div>
</div>
<div class="flex gap-3">
<a
href="https://git.telcotronics.net/pablinux/SIAX-MONITOR"
target="_blank"
class="inline-flex items-center gap-2 px-6 py-3 bg-primary hover:brightness-110 rounded-lg text-white font-semibold transition-all"
>
<span class="material-symbols-outlined text-sm"
>code</span
>
Ver en Git
</a>
<a
href="/api-docs"
class="inline-flex items-center gap-2 px-6 py-3 bg-slate-800 hover:bg-slate-700 rounded-lg text-white font-semibold transition-all"
>
<span class="material-symbols-outlined text-sm"
>description</span
>
Documentación
</a>
</div>
</div>
</footer>
<!-- Related Articles / Tags -->
<div
class="mt-12 p-6 rounded-2xl border border-slate-800 bg-[#0a0f16]"
>
<h3 class="text-lg font-bold text-white mb-4">Etiquetas</h3>
<div class="flex flex-wrap gap-2">
<span
class="px-3 py-1 rounded-full bg-slate-800 text-slate-300 text-sm hover:bg-slate-700 transition-colors cursor-pointer"
>rust</span
>
<span
class="px-3 py-1 rounded-full bg-slate-800 text-slate-300 text-sm hover:bg-slate-700 transition-colors cursor-pointer"
>systemd</span
>
<span
class="px-3 py-1 rounded-full bg-slate-800 text-slate-300 text-sm hover:bg-slate-700 transition-colors cursor-pointer"
>monitoring</span
>
<span
class="px-3 py-1 rounded-full bg-slate-800 text-slate-300 text-sm hover:bg-slate-700 transition-colors cursor-pointer"
>devops</span
>
<span
class="px-3 py-1 rounded-full bg-slate-800 text-slate-300 text-sm hover:bg-slate-700 transition-colors cursor-pointer"
>nodejs</span
>
<span
class="px-3 py-1 rounded-full bg-slate-800 text-slate-300 text-sm hover:bg-slate-700 transition-colors cursor-pointer"
>python</span
>
<span
class="px-3 py-1 rounded-full bg-slate-800 text-slate-300 text-sm hover:bg-slate-700 transition-colors cursor-pointer"
>websocket</span
>
<span
class="px-3 py-1 rounded-full bg-slate-800 text-slate-300 text-sm hover:bg-slate-700 transition-colors cursor-pointer"
>axum</span
>
<span
class="px-3 py-1 rounded-full bg-slate-800 text-slate-300 text-sm hover:bg-slate-700 transition-colors cursor-pointer"
>tokio</span
>
<span
class="px-3 py-1 rounded-full bg-slate-800 text-slate-300 text-sm hover:bg-slate-700 transition-colors cursor-pointer"
>linux</span
>
</div>
</div>
</article>
<!-- Footer -->
<footer class="bg-[#0a0f16] border-t border-slate-800 mt-20 py-12">
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-6">
<h3 class="text-2xl font-bold text-white mb-2">
SIAX Monitor
</h3>
<p class="text-slate-400">
Sistema de Monitoreo y Gestión de Aplicaciones
</p>
</div>
<div class="flex justify-center gap-8 mb-6">
<a
href="https://git.telcotronics.net/pablinux/SIAX-MONITOR"
target="_blank"
class="text-slate-400 hover:text-primary transition-colors"
>Git</a
>
<a
href="/api-docs"
class="text-slate-400 hover:text-primary transition-colors"
>API Docs</a
>
<a
href="/"
class="text-slate-400 hover:text-primary transition-colors"
>Dashboard</a
>
</div>
<p class="text-slate-500 text-sm text-center">
© 2026 SIAX Monitor. Desarrollado con 🦀 Rust y ❤️ por la
comunidad
</p>
</div>
</footer>
</body>
</html>

563
web/health.html Normal file
View File

@@ -0,0 +1,563 @@
<!doctype html>
<html class="dark" lang="es" dir="ltr">
<head>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>System Health - SIAX Monitor</title>
<link rel="icon" type="image/svg+xml" href="/static/icon/favicon.svg" />
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap"
rel="stylesheet"
/>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
primary: "#137fec",
"background-light": "#f6f7f8",
"background-dark": "#101922",
},
fontFamily: {
display: ["Inter", "sans-serif"],
mono: ["JetBrains Mono", "monospace"],
},
},
},
};
</script>
<style>
body {
font-family: "Inter", sans-serif;
}
.material-symbols-outlined {
font-variation-settings:
"FILL" 0,
"wght" 400,
"GRAD" 0,
"opsz" 24;
}
</style>
</head>
<body class="bg-background-dark text-white min-h-screen">
<!-- Header -->
<header class="border-b border-[#283039] bg-[#0a0f16]">
<div class="container mx-auto px-4 py-4">
<div class="flex items-center justify-between">
<div class="flex items-center gap-4">
<div
class="rounded-full size-9 border-2 border-slate-700 overflow-hidden"
>
<img
src="/static/icon/logo.png"
alt="Logo"
class="w-full h-full object-cover"
/>
</div>
<div>
<h1 class="text-xl font-bold">SIAX Monitor</h1>
<p class="text-xs text-slate-400">System Health</p>
</div>
</div>
<!-- Desktop Navigation -->
<nav class="hidden md:flex items-center gap-2">
<a
href="/scan"
class="px-4 py-2 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-2"
>
<span class="material-symbols-outlined text-lg"
>search</span
>
<span>Escanear</span>
</a>
<a
href="/logs"
class="px-4 py-2 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-2"
>
<span class="material-symbols-outlined text-lg"
>article</span
>
<span>Logs</span>
</a>
<a
href="/register"
class="px-4 py-2 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-2"
>
<span class="material-symbols-outlined text-lg"
>app_registration</span
>
<span>Registrar</span>
</a>
<a
href="/"
class="px-4 py-2 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-2"
>
<span class="material-symbols-outlined text-lg"
>dashboard</span
>
<span>Dashboard</span>
</a>
</nav>
<!-- Mobile Menu Button -->
<button
onclick="toggleMenu()"
class="md:hidden px-3 py-2 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors"
>
<span class="material-symbols-outlined text-2xl"
>menu</span
>
</button>
</div>
<!-- Mobile Menu Dropdown -->
<div
id="mobile-menu"
class="hidden md:hidden mt-4 pb-4 space-y-2"
>
<a
href="/health"
class="block px-4 py-3 rounded-lg bg-[#161f2a] text-primary transition-colors flex items-center gap-3"
>
<span class="material-symbols-outlined"
>monitor_heart</span
>
<span>Health</span>
</a>
<a
href="/scan"
class="block px-4 py-3 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-3"
>
<span class="material-symbols-outlined">search</span>
<span>Escanear</span>
</a>
<a
href="/logs"
class="block px-4 py-3 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-3"
>
<span class="material-symbols-outlined">article</span>
<span>Logs</span>
</a>
<a
href="/"
class="block px-4 py-3 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-3"
>
<span class="material-symbols-outlined">dashboard</span>
<span>Dashboard</span>
</a>
</div>
</div>
</header>
<!-- Main Content -->
<main class="container mx-auto px-4 py-8">
<!-- Page Header -->
<div class="mb-8">
<div class="flex items-center justify-between">
<div>
<h2 class="text-3xl font-black mb-2">System Health</h2>
<p class="text-slate-400">
Diagnóstico y estado del sistema de monitoreo
</p>
</div>
<button
onclick="refreshHealth()"
class="px-4 py-2 rounded-lg bg-primary hover:brightness-110 transition-all flex items-center gap-2"
>
<span class="material-symbols-outlined text-sm"
>refresh</span
>
<span>Actualizar</span>
</button>
</div>
</div>
<!-- Loading State -->
<div id="loading-state" class="text-center py-12">
<div
class="inline-block animate-spin rounded-full h-12 w-12 border-4 border-primary border-t-transparent"
></div>
<p class="mt-4 text-slate-400">
Cargando estado del sistema...
</p>
</div>
<!-- Health Cards Container -->
<div id="health-content" class="hidden">
<!-- Status Overview -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
<!-- Overall Status -->
<div
class="rounded-2xl border border-[#283039] bg-[#161f2a] p-6"
>
<div class="flex items-center justify-between mb-2">
<span class="text-slate-400">Estado General</span>
<span
class="material-symbols-outlined text-green-400"
id="status-icon"
>check_circle</span
>
</div>
<div class="text-2xl font-bold" id="overall-status">
OK
</div>
</div>
<!-- Config Status -->
<div
class="rounded-2xl border border-[#283039] bg-[#161f2a] p-6"
>
<div class="flex items-center justify-between mb-2">
<span class="text-slate-400">Configuración</span>
<span
class="material-symbols-outlined text-blue-400"
>settings</span
>
</div>
<div class="text-2xl font-bold" id="config-status">
Cargada
</div>
</div>
<!-- Apps Count -->
<div
class="rounded-2xl border border-[#283039] bg-[#161f2a] p-6"
>
<div class="flex items-center justify-between mb-2">
<span class="text-slate-400">Apps Registradas</span>
<span
class="material-symbols-outlined text-purple-400"
>apps</span
>
</div>
<div class="text-2xl font-bold" id="apps-count">0</div>
</div>
<!-- Version -->
<div
class="rounded-2xl border border-[#283039] bg-[#161f2a] p-6"
>
<div class="flex items-center justify-between mb-2">
<span class="text-slate-400">Versión</span>
<span
class="material-symbols-outlined text-yellow-400"
>info</span
>
</div>
<div class="text-2xl font-bold font-mono" id="version">
-
</div>
</div>
</div>
<!-- Detailed Information -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Configuration Details -->
<div
class="rounded-2xl border border-[#283039] bg-[#161f2a] p-6"
>
<div class="flex items-center gap-3 mb-6">
<div
class="w-10 h-10 rounded-xl bg-blue-500/20 flex items-center justify-center"
>
<span
class="material-symbols-outlined text-blue-400"
>folder</span
>
</div>
<div>
<h3 class="text-lg font-bold">Configuración</h3>
<p class="text-sm text-slate-400">
Detalles del archivo de configuración
</p>
</div>
</div>
<div class="space-y-4">
<div
class="flex items-start justify-between py-3 border-b border-[#283039]"
>
<div>
<p class="text-sm text-slate-400">
Ruta del archivo
</p>
<p
class="font-mono text-sm text-primary"
id="config-path"
>
-
</p>
</div>
<span
class="material-symbols-outlined text-green-400"
id="config-loaded-icon"
>check_circle</span
>
</div>
<div
class="flex items-start justify-between py-3 border-b border-[#283039]"
>
<div>
<p class="text-sm text-slate-400">Estado</p>
<p
class="font-semibold"
id="config-loaded-text"
>
Archivo cargado correctamente
</p>
</div>
</div>
<div class="flex items-start justify-between py-3">
<div>
<p class="text-sm text-slate-400">
Aplicaciones en config
</p>
<p
class="text-2xl font-bold"
id="config-apps-count"
>
0
</p>
</div>
</div>
</div>
</div>
<!-- Systemd Services -->
<div
class="rounded-2xl border border-[#283039] bg-[#161f2a] p-6"
>
<div class="flex items-center gap-3 mb-6">
<div
class="w-10 h-10 rounded-xl bg-green-500/20 flex items-center justify-center"
>
<span
class="material-symbols-outlined text-green-400"
>settings_system_daydream</span
>
</div>
<div>
<h3 class="text-lg font-bold">
Servicios Systemd
</h3>
<p class="text-sm text-slate-400">
Servicios creados por SIAX Monitor
</p>
</div>
</div>
<div
id="systemd-services-list"
class="space-y-2 max-h-80 overflow-y-auto"
>
<!-- Services will be injected here -->
</div>
<div
id="no-services"
class="hidden text-center py-8 text-slate-400"
>
<span
class="material-symbols-outlined text-4xl mb-2 opacity-50"
>info</span
>
<p>No hay servicios systemd registrados</p>
</div>
</div>
</div>
<!-- System Commands -->
<div
class="mt-6 rounded-2xl border border-[#283039] bg-[#161f2a] p-6"
>
<div class="flex items-center gap-3 mb-6">
<div
class="w-10 h-10 rounded-xl bg-purple-500/20 flex items-center justify-center"
>
<span
class="material-symbols-outlined text-purple-400"
>terminal</span
>
</div>
<div>
<h3 class="text-lg font-bold">Comandos Útiles</h3>
<p class="text-sm text-slate-400">
Comandos para gestionar servicios systemd
</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="rounded-lg bg-[#0a0f16] p-4">
<p class="text-xs text-slate-400 mb-2">
Listar servicios SIAX
</p>
<code class="text-sm text-primary font-mono"
>systemctl list-units 'siax-app-*'</code
>
</div>
<div class="rounded-lg bg-[#0a0f16] p-4">
<p class="text-xs text-slate-400 mb-2">
Ver estado de un servicio
</p>
<code class="text-sm text-primary font-mono"
>systemctl status siax-app-nombre.service</code
>
</div>
<div class="rounded-lg bg-[#0a0f16] p-4">
<p class="text-xs text-slate-400 mb-2">
Ver logs de un servicio
</p>
<code class="text-sm text-primary font-mono"
>journalctl -u siax-app-nombre -f</code
>
</div>
<div class="rounded-lg bg-[#0a0f16] p-4">
<p class="text-xs text-slate-400 mb-2">
Recargar daemon de systemd
</p>
<code class="text-sm text-primary font-mono"
>systemctl daemon-reload</code
>
</div>
</div>
</div>
</div>
</main>
<script>
async function loadHealth() {
const loading = document.getElementById("loading-state");
const content = document.getElementById("health-content");
try {
loading.classList.remove("hidden");
content.classList.add("hidden");
const response = await fetch("/api/health");
if (!response.ok) throw new Error("Failed to fetch health");
const result = await response.json();
const data = result.data;
loading.classList.add("hidden");
content.classList.remove("hidden");
// Update status cards
document.getElementById("overall-status").textContent =
data.status.toUpperCase();
document.getElementById("config-status").textContent =
data.config_loaded ? "Cargada" : "No encontrada";
document.getElementById("apps-count").textContent =
data.apps_count;
document.getElementById("version").textContent =
"v" + data.version;
// Update config details
document.getElementById("config-path").textContent =
data.config_path;
document.getElementById("config-apps-count").textContent =
data.apps_count;
const configIcon =
document.getElementById("config-loaded-icon");
const configText =
document.getElementById("config-loaded-text");
if (data.config_loaded) {
configIcon.textContent = "check_circle";
configIcon.className =
"material-symbols-outlined text-green-400";
configText.textContent =
"Archivo cargado correctamente";
} else {
configIcon.textContent = "error";
configIcon.className =
"material-symbols-outlined text-yellow-400";
configText.textContent =
"Archivo no encontrado (se creará automáticamente)";
}
// Update systemd services list
const servicesList = document.getElementById(
"systemd-services-list",
);
const noServices = document.getElementById("no-services");
if (
data.systemd_services &&
data.systemd_services.length > 0
) {
servicesList.innerHTML = data.systemd_services
.map(
(service) => `
<div class="flex items-center justify-between p-3 rounded-lg bg-[#0a0f16] hover:bg-[#0d1218] transition-colors">
<div class="flex items-center gap-3">
<span class="material-symbols-outlined text-green-400 text-sm">check_circle</span>
<span class="font-mono text-sm">${service}</span>
</div>
<button onclick="copyToClipboard('${service}')" class="text-slate-400 hover:text-primary transition-colors">
<span class="material-symbols-outlined text-sm">content_copy</span>
</button>
</div>
`,
)
.join("");
noServices.classList.add("hidden");
} else {
servicesList.innerHTML = "";
noServices.classList.remove("hidden");
}
} catch (error) {
console.error("Error loading health:", error);
loading.classList.add("hidden");
content.innerHTML = `
<div class="text-center py-12 text-red-400">
<span class="material-symbols-outlined text-6xl mb-4">error</span>
<p class="text-xl font-bold mb-2">Error al cargar el estado del sistema</p>
<p class="text-slate-400">${error.message}</p>
<button onclick="loadHealth()" class="mt-4 px-6 py-2 bg-primary rounded-lg hover:brightness-110">
Reintentar
</button>
</div>
`;
content.classList.remove("hidden");
}
}
function refreshHealth() {
loadHealth();
}
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
// Simple feedback - you could add a toast notification here
console.log("Copied:", text);
});
}
function toggleMenu() {
const menu = document.getElementById("mobile-menu");
menu.classList.toggle("hidden");
}
// Load on page load
document.addEventListener("DOMContentLoaded", loadHealth);
</script>
</body>
</html>

View File

@@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>Panel de Monitoreo</title>
<link rel="icon" type="image/svg+xml" href="/static/icon/favicon.svg" />
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap"
@@ -54,90 +55,117 @@
>
<div class="flex h-full grow flex-col">
<!-- Sticky Top Navigation -->
<header
class="sticky top-0 z-50 w-full border-b border-slate-200 dark:border-slate-800 bg-background-light/80 dark:bg-background-dark/80 backdrop-blur-md"
>
<div
class="max-w-[1200px] mx-auto px-4 lg:px-10 py-3 flex items-center justify-between"
>
<div class="flex items-center gap-8">
<div class="flex items-center gap-3 text-primary">
<header class="border-b border-[#283039] bg-[#0a0f16]">
<div class="container mx-auto px-4 py-4">
<div class="flex items-center justify-between">
<div class="flex items-center gap-4">
<div
class="size-8 bg-primary rounded-lg flex items-center justify-center text-white"
class="rounded-full size-9 border-2 border-slate-700 overflow-hidden"
>
<span class="material-symbols-outlined"
>monitoring</span
>
<img
src="/static/icon/logo.png"
alt="Logo"
class="w-full h-full object-cover"
/>
</div>
<div>
<h1 class="text-xl font-bold">SIAX Monitor</h1>
<p class="text-xs text-slate-400">Dashboard</p>
</div>
<h2
class="text-white text-lg font-bold leading-tight tracking-[-0.015em]"
>
SIAX Monitor
</h2>
</div>
<nav class="hidden md:flex items-center gap-6">
<!-- Desktop Navigation -->
<nav class="hidden md:flex items-center gap-2">
<a
class="text-primary text-sm font-semibold leading-normal"
href="/"
>Inicio</a
href="/health"
class="px-4 py-2 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-2"
>
<span class="material-symbols-outlined text-lg"
>monitor_heart</span
>
<span>Health</span>
</a>
<a
class="text-slate-600 dark:text-slate-400 hover:text-white text-sm font-medium transition-colors"
href="/scan"
class="px-4 py-2 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-2"
>
Escanear
<span class="material-symbols-outlined text-lg"
>search</span
>
<span>Escanear</span>
</a>
<a
class="text-slate-600 dark:text-slate-400 hover:text-white text-sm font-medium transition-colors"
href="/select"
>
Agregar
</a>
<a
class="text-slate-600 dark:text-slate-400 hover:text-white text-sm font-medium transition-colors"
href="/register"
>
Nueva App
</a>
<a
class="text-slate-600 dark:text-slate-400 hover:text-white text-sm font-medium transition-colors"
href="/logs"
class="px-4 py-2 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-2"
>
Registros
<span class="material-symbols-outlined text-lg"
>article</span
>
<span>Logs</span>
</a>
<a
href="/register"
class="px-4 py-2 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-2"
>
<span class="material-symbols-outlined text-lg"
>app_registration</span
>
<span>Registrar</span>
</a>
</nav>
</div>
<div class="flex items-center gap-4">
<div class="hidden sm:block">
<label class="relative block">
<span
class="absolute inset-y-0 left-0 flex items-center pl-3 text-slate-500"
>
<span
class="material-symbols-outlined text-sm"
>
search
</span>
</span>
<input
class="form-input w-64 rounded-lg border-none bg-slate-200 dark:bg-slate-800 text-sm py-2 pl-10 pr-4 placeholder:text-slate-500 focus:ring-1 focus:ring-primary"
placeholder="Buscar..."
type="text"
/>
</label>
</div>
<!-- Mobile Menu Button -->
<button
class="hidden lg:flex cursor-pointer items-center justify-center rounded-lg h-9 px-4 bg-primary text-white text-sm font-bold transition-opacity hover:opacity-90"
onclick="window.location.href = '/register'"
onclick="toggleMenu()"
class="md:hidden px-3 py-2 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors"
>
<span>Registrar App</span>
<span class="material-symbols-outlined text-2xl"
>menu</span
>
</button>
<div
class="bg-center bg-no-repeat aspect-square bg-cover rounded-full size-9 border-2 border-slate-700"
style="
background-image: url(&quot;https://lh3.googleusercontent.com/aida-public/AB6AXuCT0iINTncUFHp353HCJXRR5C0OKbSp_7IBOVNoDU07yuF2aToQQdnXNOeGI9RLUjVBsVNcU--ZoTMY90FFJvrQvYvRzKvq-CFCzBlVkCeoi5AgG84cB71wW0NIMg626M_sCjmDjxqmAJwIbkAcSmSlAg3TUThW1U2A3StNVgqFXEpgFbpJcU5nxLs6vuRkfYR1kIXcV44TQpgOosbsjSB1Pk1UTOQJ_OEcQtY-5c3FJw7gXBDxlp6y3jsY3rBm0xWGJi8NWnrUrhpl&quot;);
"
></div>
</div>
<!-- Mobile Menu Dropdown -->
<div
id="mobile-menu"
class="hidden md:hidden mt-4 pb-4 space-y-2"
>
<a
href="/health"
class="block px-4 py-3 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-3"
>
<span class="material-symbols-outlined"
>monitor_heart</span
>
<span>Health</span>
</a>
<a
href="/scan"
class="block px-4 py-3 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-3"
>
<span class="material-symbols-outlined"
>search</span
>
<span>Escanear</span>
</a>
<a
href="/logs"
class="block px-4 py-3 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-3"
>
<span class="material-symbols-outlined"
>article</span
>
<span>Logs</span>
</a>
<a
href="/register"
class="block px-4 py-3 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-3"
>
<span class="material-symbols-outlined"
>app_registration</span
>
<span>Registrar</span>
</a>
</div>
</div>
</header>
@@ -150,7 +178,7 @@
<h1
class="text-slate-900 dark:text-white text-3xl font-black tracking-tight"
>
Dashboard Index
Panel de Control
</h1>
<p class="text-slate-500 text-sm mt-1">
Monitoreo de salud del sistema y procesos en tiempo
@@ -436,7 +464,7 @@
<a class="hover:text-primary" href="#"
>Política de Privacidad</a
>
<a class="hover:text-primary" href="#"
<a class="hover:text-primary" href="/api-docs"
>Documentación de API</a
>
</div>
@@ -446,9 +474,7 @@
<script>
async function loadApps() {
try {
const response = await fetch(
"http://localhost:8080/api/apps",
);
const response = await fetch("/api/apps");
const result = await response.json();
if (result.success && result.data && result.data.apps) {
@@ -516,6 +542,11 @@
`;
}
function toggleMenu() {
const menu = document.getElementById("mobile-menu");
menu.classList.toggle("hidden");
}
window.addEventListener("DOMContentLoaded", loadApps);
</script>
</body>

View File

@@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>Visor de Registros - SIAX Monitor</title>
<link rel="icon" type="image/svg+xml" href="/static/icon/favicon.svg" />
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
@@ -69,9 +70,11 @@
<div
class="size-8 bg-primary rounded-lg flex items-center justify-center"
>
<span class="material-symbols-outlined text-white"
>monitoring</span
>
<img
src="/static/icon/logo.png"
alt="Logo"
class="w-full h-full object-cover"
/>
</div>
<h2
class="text-white text-lg font-bold leading-tight tracking-[-0.015em]"
@@ -95,12 +98,7 @@
<a
class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors"
href="/select"
>Agregar Detectada</a
>
<a
class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors"
href="/register"
>Nueva App</a
>Selecionar Detectada</a
>
<a
class="text-primary text-sm font-medium border-b-2 border-primary pb-1"
@@ -108,6 +106,12 @@
>Registros</a
>
</nav>
<button
class="hidden lg:flex cursor-pointer items-center justify-center rounded-lg h-9 px-4 bg-primary text-white text-sm font-bold transition-opacity hover:opacity-90"
onclick="window.location.href = '/register'"
>
<span>Nueva App</span>
</button>
</div>
</div>
</header>
@@ -238,9 +242,7 @@
empty.classList.add("hidden");
try {
const response = await fetch(
"http://localhost:8080/api/apps",
);
const response = await fetch("/api/apps");
const data = await response.json();
loading.classList.add("hidden");
@@ -321,9 +323,10 @@
});
// Connect WebSocket
ws = new WebSocket(
`ws://localhost:8080/api/apps/${appName}/logs`,
);
const protocol =
window.location.protocol === "https:" ? "wss:" : "ws:";
const wsUrl = `${protocol}//${window.location.host}/api/apps/${appName}/logs`;
ws = new WebSocket(wsUrl);
ws.onopen = () => {
document.getElementById("connection-status").textContent =

View File

@@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>Registrar Aplicación - SIAX Monitor</title>
<link rel="icon" type="image/svg+xml" href="/static/icon/favicon.svg" />
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
@@ -62,9 +63,11 @@
<div
class="size-8 bg-primary rounded-lg flex items-center justify-center"
>
<span class="material-symbols-outlined text-white"
>monitoring</span
>
<img
src="/static/icon/logo.png"
alt="Logo"
class="w-full h-full object-cover"
/>
</div>
<h2
class="text-white text-lg font-bold leading-tight tracking-[-0.015em]"
@@ -88,12 +91,7 @@
<a
class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors"
href="/select"
>Agregar Detectada</a
>
<a
class="text-primary text-sm font-medium border-b-2 border-primary pb-1"
href="/register"
>Nueva App</a
>Selecionar Detectada</a
>
<a
class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors"
@@ -101,6 +99,12 @@
>Registros</a
>
</nav>
<button
class="hidden lg:flex cursor-pointer items-center justify-center rounded-lg h-9 px-4 bg-primary text-white text-sm font-bold transition-opacity hover:opacity-90"
onclick="window.location.href = '/register'"
>
<span>Nueva App</span>
</button>
</div>
</div>
</header>
@@ -461,16 +465,13 @@
});
try {
const response = await fetch(
"http://localhost:8080/api/apps",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(formData),
const response = await fetch("/api/apps", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
);
body: JSON.stringify(formData),
});
const result = await response.json();
@@ -485,7 +486,7 @@
confirm("¿Deseas iniciar la aplicación ahora?")
) {
const startResponse = await fetch(
`http://localhost:8080/api/apps/${formData.app_name}/start`,
`/api/apps/${formData.app_name}/start`,
{ method: "POST" },
);

View File

@@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>Escaneo de Procesos - SIAX Monitor</title>
<link rel="icon" type="image/svg+xml" href="/static/icon/favicon.svg" />
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
@@ -62,9 +63,7 @@
<div
class="size-8 bg-primary rounded-lg flex items-center justify-center"
>
<span class="material-symbols-outlined text-white"
>monitoring</span
>
<img src="/static/icon/logo.png" alt="Logo" class="w-full h-full object-cover">
</div>
<h2
class="text-white text-lg font-bold leading-tight tracking-[-0.015em]"
@@ -88,19 +87,21 @@
<a
class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors"
href="/select"
>Agregar Detectada</a
>
<a
class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors"
href="/register"
>Registrar Nueva</a
>Selecionar Detectada</a
>
<a
class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors"
href="/logs"
>Registros</a
>
</nav>
<button
class="hidden lg:flex cursor-pointer items-center justify-center rounded-lg h-9 px-4 bg-primary text-white text-sm font-bold transition-opacity hover:opacity-90"
onclick="window.location.href = '/register'"
>
<span>Nueva App</span>
</button>
</div>
</div>
</header>
@@ -112,7 +113,7 @@
<h1
class="text-white text-4xl font-black leading-tight tracking-[-0.033em]"
>
Process Scan View
Visualización de escaneo de procesos
</h1>
<p class="text-[#9dabb9] text-base font-normal">
Monitoreo activo de procesos Node.js y Python.
@@ -253,7 +254,7 @@
loadingState.classList.remove('hidden');
emptyState.classList.add('hidden');
const response = await fetch('http://localhost:8080/api/scan');
const response = await fetch('/api/scan');
if (!response.ok) throw new Error('Failed to fetch processes');
const data = await response.json();

View File

@@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>Agregar App Detectada - SIAX Monitor</title>
<link rel="icon" type="image/svg+xml" href="/static/icon/favicon.svg" />
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
@@ -62,9 +63,11 @@
<div
class="size-8 bg-primary rounded-lg flex items-center justify-center"
>
<span class="material-symbols-outlined text-white"
>monitoring</span
>
<img
src="/static/icon/logo.png"
alt="Logo"
class="w-full h-full object-cover"
/>
</div>
<h2
class="text-white text-lg font-bold leading-tight tracking-[-0.015em]"
@@ -88,12 +91,7 @@
<a
class="text-primary text-sm font-medium border-b-2 border-primary pb-1"
href="/select"
>Agregar Detectada</a
>
<a
class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors"
href="/register"
>Nueva App</a
>Selecionar Detectada</a
>
<a
class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors"
@@ -101,6 +99,12 @@
>Registros</a
>
</nav>
<button
class="hidden lg:flex cursor-pointer items-center justify-center rounded-lg h-9 px-4 bg-primary text-white text-sm font-bold transition-opacity hover:opacity-90"
onclick="window.location.href = '/register'"
>
<span>Nueva App</span>
</button>
</div>
</div>
</header>
@@ -111,7 +115,7 @@
<h1
class="text-white text-4xl font-black leading-tight tracking-[-0.033em]"
>
Add Detected Application
Agregar la aplicación detectada
</h1>
<p class="text-[#9dabb9] text-base font-normal">
Selecciona un proceso detectado y configúralo para
@@ -280,9 +284,7 @@
const empty = document.getElementById("empty-state");
try {
const response = await fetch(
"http://localhost:8080/api/scan",
);
const response = await fetch("/api/scan");
if (!response.ok)
throw new Error("Failed to fetch processes");

BIN
web/static/icon/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="128.000000pt" height="128.000000pt" viewBox="0 0 128.000000 128.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,128.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M518 1244 c-75 -18 -188 -75 -239 -121 l-40 -36 -39 38 c-22 21 -42
36 -46 32 -3 -3 3 -33 14 -66 20 -58 20 -61 3 -83 -66 -84 -105 -212 -105
-343 0 -166 49 -288 164 -408 55 -58 71 -70 81 -60 18 18 64 16 79 -2 9 -11
35 -15 106 -15 l94 0 0 165 0 166 -24 -18 c-23 -17 -25 -25 -28 -128 l-3 -110
-45 -3 c-24 -2 -50 -8 -57 -14 -9 -7 -17 -7 -28 2 -17 14 -18 20 -5 40 7 12
13 12 33 1 13 -7 37 -11 53 -9 l29 3 3 87 c2 74 0 88 -13 88 -12 0 -15 -13
-15 -65 l0 -65 -45 0 c-25 0 -54 -6 -65 -12 -23 -15 -46 1 -37 26 5 12 18 13
61 9 l56 -6 0 47 c0 25 -4 46 -10 46 -5 0 -10 -11 -10 -25 0 -24 -3 -25 -60
-25 -32 0 -71 -5 -85 -12 -21 -9 -29 -9 -42 4 -14 15 -13 17 6 27 15 8 27 9
40 1 31 -16 111 -13 111 5 0 11 -11 15 -38 15 -103 0 -207 61 -257 151 -26 47
-30 64 -30 129 1 112 50 201 137 246 29 15 30 15 86 -37 66 -60 72 -69 42 -69
-13 0 -37 -9 -55 -20 -93 -58 -84 -193 17 -246 59 -31 144 -6 184 53 l22 33
22 -52 c12 -28 33 -68 46 -88 24 -36 24 -42 22 -200 l-3 -163 -96 2 c-72 2
-99 -1 -107 -12 -10 -12 -4 -18 35 -35 48 -20 138 -42 173 -42 13 0 17 5 13
20 -3 11 0 20 6 20 8 0 11 51 11 170 0 107 4 170 10 170 6 0 10 -61 10 -164 0
-108 4 -167 11 -172 6 -3 8 -15 5 -26 -4 -19 -1 -20 54 -14 71 8 148 30 174
49 37 27 9 37 -105 37 l-109 0 0 159 c0 96 4 162 10 166 7 4 10 -48 10 -149
l0 -156 93 0 c65 1 98 5 111 15 23 18 67 20 83 4 16 -16 43 2 106 71 97 108
147 240 148 390 0 125 -27 227 -87 322 l-27 42 18 55 c10 31 20 62 22 69 9 23
-16 12 -51 -23 -20 -19 -38 -35 -42 -35 -3 0 -25 16 -47 36 -52 44 -163 99
-242 119 -72 18 -200 18 -277 -1z m310 -90 c72 -15 187 -56 205 -74 5 -4 -40
-31 -100 -60 -89 -44 -121 -65 -182 -126 -52 -53 -80 -74 -99 -74 -17 0 -48
23 -106 78 -66 63 -99 85 -178 122 -54 25 -98 48 -98 52 0 11 160 69 230 83
91 18 237 18 328 -1z m325 -253 c52 -48 81 -117 81 -196 -1 -82 -17 -126 -70
-185 -51 -56 -120 -89 -200 -97 -49 -4 -64 -9 -60 -19 3 -8 -1 -14 -9 -14 -8
0 -15 9 -15 19 0 10 -7 21 -15 25 -12 4 -15 -4 -15 -44 0 -50 0 -50 33 -50 18
0 38 5 44 11 13 13 33 4 33 -16 0 -20 -20 -29 -33 -16 -6 6 -30 11 -54 11
l-43 0 0 56 c0 42 -4 60 -17 70 -10 7 -21 14 -25 14 -5 0 -8 -45 -8 -100 l0
-100 34 0 c19 0 38 5 41 10 12 20 35 10 35 -14 0 -32 -19 -44 -35 -23 -8 11
-27 17 -54 17 l-41 0 0 120 c0 66 -4 120 -9 120 -22 0 -18 35 13 101 l33 71
21 -36 c78 -132 275 -81 275 71 0 56 -25 91 -88 122 l-49 25 59 53 c58 52 59
53 84 37 14 -9 39 -28 54 -43z m-103 -510 c0 -13 -27 -21 -45 -15 -25 10 -17
24 15 24 17 0 30 -4 30 -9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
web/static/icon/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -4,6 +4,7 @@
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>Éxito - SIAX Monitor</title>
<link rel="icon" type="image/svg+xml" href="/static/icon/favicon.svg" />
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
@@ -62,9 +63,11 @@
<div
class="size-8 bg-primary rounded-lg flex items-center justify-center"
>
<span class="material-symbols-outlined text-white"
>monitoring</span
>
<img
src="/static/icon/logo.png"
alt="Logo"
class="w-full h-full object-cover"
/>
</div>
<h2
class="text-white text-lg font-bold leading-tight tracking-[-0.015em]"
@@ -91,13 +94,7 @@
class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors"
href="/select"
>
Agregar Detectada
</a>
<a
class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors"
href="/register"
>
Nueva App
Selecionar Detectada
</a>
<a
class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors"
@@ -106,6 +103,12 @@
Registros
</a>
</nav>
<button
class="hidden lg:flex cursor-pointer items-center justify-center rounded-lg h-9 px-4 bg-primary text-white text-sm font-bold transition-opacity hover:opacity-90"
onclick="window.location.href = '/register'"
>
<span>Nueva App</span>
</button>
</div>
</div>
</header>