Compare commits
9 Commits
f67704f289
...
3798f911f1
| Author | SHA1 | Date | |
|---|---|---|---|
| 3798f911f1 | |||
| fbc89e9bf0 | |||
| 868f3a2d30 | |||
| 87ce154789 | |||
| f9e6439b24 | |||
| 246b5c8342 | |||
| 8822e9e6b5 | |||
| ad9b46bdc5 | |||
| b6fa1fa472 |
@@ -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
|
||||
|
||||
# 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
|
||||
# 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
|
||||
|
||||
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
|
||||
# 2. Compilar
|
||||
echo "📦 Compilando..."
|
||||
cargo build --release --target $TARGET
|
||||
if [ $? -ne 0 ]; then echo "❌ Error en compilación"; exit 1; fi
|
||||
|
||||
print_success "Todas las dependencias están instaladas"
|
||||
}
|
||||
# --- FUNCIÓN DE SUBIDA ---
|
||||
upload_file() {
|
||||
local IP=$1
|
||||
local USER=$2
|
||||
local DEST=$3
|
||||
|
||||
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
|
||||
}
|
||||
echo "🚀 Subiendo a $USER@$IP vía $METODO..."
|
||||
|
||||
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
347
instalador.sh
Executable 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
315
install-remote.sh
Normal 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
|
||||
297
logs/errors.log
297
logs/errors.log
@@ -117,3 +117,300 @@
|
||||
[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"}
|
||||
[2026-01-18 03:27:14] [INFO] [Sistema] Iniciando SIAX Agent
|
||||
[2026-01-18 03:27:14] [INFO] [Sistema] Escaneando servicios systemd existentes...
|
||||
[2026-01-18 03:27:14] [INFO] [Discovery] Escaneando servicios systemd existentes...
|
||||
[2026-01-18 03:27:14] [INFO] [Discovery] ✅ Descubiertos 0 servicios
|
||||
[2026-01-18 03:27:14] [INFO] [Config] Usando archivo de configuración: config/monitored_apps.json
|
||||
[2026-01-18 03:27:14] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
|
||||
[2026-01-18 03:27:14] [INFO] [Sistema] Servidor detectado: siax-intel
|
||||
[2026-01-18 03:27:14] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
|
||||
[2026-01-18 03:27:14] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
|
||||
[2026-01-18 03:27:15] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
|
||||
[2026-01-18 03:27:15] [INFO] [Monitor] Buscando app app_tareas en API central...
|
||||
[2026-01-18 03:27:15] [INFO] [Monitor] App app_tareas encontrada en cloud (ID: 3)
|
||||
[2026-01-18 03:27:15] [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:27:15] [INFO] [Monitor] Buscando app fidelizacion en API central...
|
||||
[2026-01-18 03:27:15] [INFO] [Monitor] App fidelizacion encontrada en cloud (ID: 4)
|
||||
[2026-01-18 03:27:15] [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"}
|
||||
[2026-01-18 03:28:15] [INFO] [Monitor] App app_tareas ya existe (ID: 3), actualizando...
|
||||
[2026-01-18 03:28:15] [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:28:15] [INFO] [Monitor] App fidelizacion ya existe (ID: 4), actualizando...
|
||||
[2026-01-18 03:28:15] [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"}
|
||||
[2026-01-18 03:29:15] [INFO] [Monitor] App app_tareas ya existe (ID: 3), actualizando...
|
||||
[2026-01-18 03:29:15] [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:29:15] [INFO] [Monitor] App fidelizacion ya existe (ID: 4), actualizando...
|
||||
[2026-01-18 03:29:15] [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"}
|
||||
[2026-01-18 03:30:15] [INFO] [Monitor] App app_tareas ya existe (ID: 3), actualizando...
|
||||
[2026-01-18 03:30:15] [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:30:15] [INFO] [Monitor] App fidelizacion ya existe (ID: 4), actualizando...
|
||||
[2026-01-18 03:30:15] [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"}
|
||||
[2026-01-18 03:31:15] [INFO] [Monitor] App app_tareas ya existe (ID: 3), actualizando...
|
||||
[2026-01-18 03:31:15] [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:31:16] [INFO] [Monitor] App fidelizacion ya existe (ID: 4), actualizando...
|
||||
[2026-01-18 03:31:16] [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"}
|
||||
[2026-01-18 04:10:00] [INFO] [Sistema] Iniciando SIAX Agent
|
||||
[2026-01-18 04:10:00] [INFO] [Sistema] Escaneando servicios systemd existentes...
|
||||
[2026-01-18 04:10:00] [INFO] [Discovery] 🔍 Escaneando servicios systemd en: /etc/systemd/system
|
||||
[2026-01-18 04:10:00] [INFO] [Discovery] ✅ Directorio /etc/systemd/system accesible
|
||||
[2026-01-18 04:10:00] [INFO] [Discovery] 📊 Escaneados 131 archivos, 0 con prefijo 'siax-app-', 0 parseados exitosamente
|
||||
[2026-01-18 04:10:00] [INFO] [Config] Usando archivo de configuración: config/monitored_apps.json
|
||||
[2026-01-18 04:10:00] [INFO] [Config] ✅ Configuración cargada: 2 apps desde config/monitored_apps.json
|
||||
[2026-01-18 04:10:00] [INFO] [Sistema] Servidor detectado: siax-intel
|
||||
[2026-01-18 04:10:00] [INFO] [Sistema] Iniciando servidor unificado en puerto 8080
|
||||
[2026-01-18 04:10:00] [INFO] [Sistema] Sistema SIAX completamente operativo en puerto 8080
|
||||
[2026-01-18 04:10:00] [INFO] [Monitor] Vigilando procesos para siax-intel [SIAX-Agent/0.1.0 (linux/x86_64; Rust-Monitor)]
|
||||
[2026-01-18 04:10:00] [INFO] [Monitor] Buscando app app_tareas en API central...
|
||||
[2026-01-18 04:10:00] [INFO] [Monitor] App app_tareas encontrada en cloud (ID: 3)
|
||||
[2026-01-18 04:10:00] [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 04:10:00] [INFO] [Monitor] Buscando app fidelizacion en API central...
|
||||
[2026-01-18 04:10:00] [INFO] [Monitor] App fidelizacion encontrada en cloud (ID: 4)
|
||||
[2026-01-18 04:10:00] [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"}
|
||||
[2026-01-18 04:11:00] [INFO] [Monitor] App app_tareas ya existe (ID: 3), actualizando...
|
||||
[2026-01-18 04:11:00] [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 04:11:00] [INFO] [Monitor] App fidelizacion ya existe (ID: 4), actualizando...
|
||||
[2026-01-18 04:11:00] [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"}
|
||||
[2026-01-18 04:12:00] [INFO] [Monitor] App app_tareas ya existe (ID: 3), actualizando...
|
||||
[2026-01-18 04:12:00] [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 04:12:00] [INFO] [Monitor] App fidelizacion ya existe (ID: 4), actualizando...
|
||||
[2026-01-18 04:12:00] [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
66
preparar_binario.sh
Executable 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 ""
|
||||
@@ -125,21 +125,62 @@ pub async fn restart_app_handler(
|
||||
}
|
||||
|
||||
pub async fn get_app_status_handler(
|
||||
State(state): State<Arc<ApiState>>,
|
||||
State(_state): State<Arc<ApiState>>,
|
||||
Path(app_name): Path<String>,
|
||||
) -> Result<Json<ApiResponse<AppStatusResponse>>, StatusCode> {
|
||||
use crate::config::get_config_manager;
|
||||
use crate::systemd::SystemCtl;
|
||||
use crate::models::{AppStatus, ServiceStatus};
|
||||
|
||||
match state.app_manager.get_app_status(&app_name) {
|
||||
Some(managed_app) => {
|
||||
let response = AppStatusResponse {
|
||||
name: managed_app.name,
|
||||
status: managed_app.status.as_str().to_string(),
|
||||
pid: managed_app.pid,
|
||||
cpu_usage: managed_app.cpu_usage,
|
||||
memory_usage: format!("{:.2} MB", managed_app.memory_usage as f64 / 1024.0 / 1024.0),
|
||||
systemd_status: managed_app.systemd_status.as_str().to_string(),
|
||||
last_updated: managed_app.last_updated,
|
||||
let config_manager = get_config_manager();
|
||||
let apps = config_manager.get_apps();
|
||||
|
||||
// Buscar la app en monitored_apps.json
|
||||
let app = apps.iter().find(|a| a.name == app_name);
|
||||
|
||||
match app {
|
||||
Some(app) => {
|
||||
let service_name = format!("siax-app-{}.service", app.name);
|
||||
let systemd_status = SystemCtl::status(&service_name);
|
||||
|
||||
// Obtener métricas del proceso
|
||||
let mut sys = System::new_all();
|
||||
sys.refresh_all();
|
||||
|
||||
let mut pid = None;
|
||||
let mut cpu_usage = 0.0;
|
||||
let mut memory_mb = 0.0;
|
||||
|
||||
// Buscar proceso por nombre de app
|
||||
for (process_pid, process) in sys.processes() {
|
||||
let cmd = process.cmd().join(" ");
|
||||
if cmd.contains(&app.name) || cmd.contains(&app.entry_point) {
|
||||
pid = Some(process_pid.as_u32() as i32);
|
||||
cpu_usage = process.cpu_usage();
|
||||
memory_mb = process.memory() as f64 / 1024.0 / 1024.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let status = match systemd_status {
|
||||
ServiceStatus::Active => "Running",
|
||||
ServiceStatus::Inactive => "Stopped",
|
||||
ServiceStatus::Failed => "Failed",
|
||||
ServiceStatus::Activating => "Starting",
|
||||
ServiceStatus::Deactivating => "Stopping",
|
||||
ServiceStatus::Unknown => "Unknown",
|
||||
};
|
||||
|
||||
let response = AppStatusResponse {
|
||||
name: app.name.clone(),
|
||||
status: status.to_string(),
|
||||
pid,
|
||||
cpu_usage,
|
||||
memory_usage: format!("{:.2} MB", memory_mb),
|
||||
systemd_status: systemd_status.as_str().to_string(),
|
||||
last_updated: chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(),
|
||||
};
|
||||
|
||||
Ok(Json(ApiResponse::success(response)))
|
||||
}
|
||||
None => Ok(Json(ApiResponse::error(
|
||||
@@ -149,13 +190,49 @@ pub async fn get_app_status_handler(
|
||||
}
|
||||
|
||||
pub async fn list_apps_handler(
|
||||
State(state): State<Arc<ApiState>>,
|
||||
) -> Result<Json<ApiResponse<AppListResponse>>, StatusCode> {
|
||||
State(_state): State<Arc<ApiState>>,
|
||||
) -> Result<Json<serde_json::Value>, StatusCode> {
|
||||
use crate::config::get_config_manager;
|
||||
use crate::systemd::SystemCtl;
|
||||
|
||||
let apps = state.app_manager.list_apps();
|
||||
let total = apps.len();
|
||||
// Leer apps desde monitored_apps.json (apps descubiertas + registradas)
|
||||
let config_manager = get_config_manager();
|
||||
let monitored_apps = config_manager.get_apps();
|
||||
|
||||
Ok(Json(ApiResponse::success(AppListResponse { apps, total })))
|
||||
// Crear respuesta con información de cada app
|
||||
let mut apps_with_status = Vec::new();
|
||||
|
||||
for app in monitored_apps {
|
||||
// Verificar estado en systemd
|
||||
let service_name = format!("siax-app-{}.service", app.name);
|
||||
let systemd_status = SystemCtl::status(&service_name);
|
||||
|
||||
let status = match systemd_status {
|
||||
crate::models::ServiceStatus::Active => "Running",
|
||||
crate::models::ServiceStatus::Inactive => "Stopped",
|
||||
crate::models::ServiceStatus::Failed => "Failed",
|
||||
crate::models::ServiceStatus::Activating => "Starting",
|
||||
crate::models::ServiceStatus::Deactivating => "Stopping",
|
||||
crate::models::ServiceStatus::Unknown => "Unknown",
|
||||
};
|
||||
|
||||
apps_with_status.push(serde_json::json!({
|
||||
"name": app.name,
|
||||
"status": status,
|
||||
"port": app.port,
|
||||
"service_name": app.service_name,
|
||||
}));
|
||||
}
|
||||
|
||||
let total = apps_with_status.len();
|
||||
|
||||
Ok(Json(serde_json::json!({
|
||||
"success": true,
|
||||
"data": {
|
||||
"apps": apps_with_status,
|
||||
"total": total
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
pub async fn scan_processes_handler() -> Result<Json<ApiResponse<ProcessScanResponse>>, StatusCode> {
|
||||
@@ -222,3 +299,62 @@ pub async fn health_handler(
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
})))
|
||||
}
|
||||
|
||||
/// Endpoint para ver las apps monitoreadas desde el JSON
|
||||
pub async fn get_monitored_apps_handler() -> Result<Json<serde_json::Value>, StatusCode> {
|
||||
use crate::config::get_config_manager;
|
||||
|
||||
let config_manager = get_config_manager();
|
||||
let apps = config_manager.get_apps();
|
||||
|
||||
let response = serde_json::json!({
|
||||
"success": true,
|
||||
"count": apps.len(),
|
||||
"apps": apps
|
||||
});
|
||||
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
/// Endpoint para obtener los logs de errores del sistema
|
||||
pub async fn get_system_error_logs() -> Result<Json<serde_json::Value>, StatusCode> {
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
let log_path = "logs/errors.log";
|
||||
|
||||
// Verificar si el archivo existe
|
||||
if !Path::new(log_path).exists() {
|
||||
return Ok(Json(serde_json::json!({
|
||||
"success": true,
|
||||
"logs": [],
|
||||
"message": "Archivo de logs no encontrado"
|
||||
})));
|
||||
}
|
||||
|
||||
// Leer el archivo
|
||||
match fs::read_to_string(log_path) {
|
||||
Ok(content) => {
|
||||
// Dividir en líneas y tomar las últimas 500
|
||||
let lines: Vec<&str> = content.lines().collect();
|
||||
let total = lines.len();
|
||||
let recent_lines: Vec<&str> = if lines.len() > 500 {
|
||||
lines[lines.len() - 500..].to_vec()
|
||||
} else {
|
||||
lines
|
||||
};
|
||||
|
||||
Ok(Json(serde_json::json!({
|
||||
"success": true,
|
||||
"logs": recent_lines,
|
||||
"total_lines": total
|
||||
})))
|
||||
}
|
||||
Err(e) => {
|
||||
Ok(Json(serde_json::json!({
|
||||
"success": false,
|
||||
"error": format!("Error leyendo archivo: {}", e)
|
||||
})))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ async fn handle_logs_socket(
|
||||
ws_manager: Arc<WebSocketManager>,
|
||||
) {
|
||||
let logger = get_logger();
|
||||
let service_name = format!("{}.service", app_name);
|
||||
let service_name = format!("siax-app-{}.service", app_name);
|
||||
|
||||
// Iniciar journalctl
|
||||
let mut child = match TokioCommand::new("journalctl")
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
321
src/discovery.rs
Normal file
321
src/discovery.rs
Normal file
@@ -0,0 +1,321 @@
|
||||
/// Módulo para descubrir servicios systemd existentes
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use crate::logger::get_logger;
|
||||
use crate::config::{get_config_manager, MonitoredApp};
|
||||
|
||||
const SYSTEMD_DIR: &str = "/etc/systemd/system";
|
||||
const SERVICE_PREFIX: &str = "siax-app-";
|
||||
|
||||
/// Descubre servicios systemd existentes con prefijo siax-app-*
|
||||
pub fn discover_services() -> Vec<DiscoveredService> {
|
||||
let logger = get_logger();
|
||||
logger.info("Discovery", &format!("🔍 Escaneando servicios systemd en: {}", SYSTEMD_DIR));
|
||||
println!("🔍 Discovery: Buscando servicios en {}", SYSTEMD_DIR);
|
||||
|
||||
let mut services = Vec::new();
|
||||
|
||||
// Leer directorio de systemd
|
||||
let entries = match fs::read_dir(SYSTEMD_DIR) {
|
||||
Ok(entries) => {
|
||||
logger.info("Discovery", &format!("✅ Directorio {} accesible", SYSTEMD_DIR));
|
||||
println!("✅ Discovery: Directorio {} accesible", SYSTEMD_DIR);
|
||||
entries
|
||||
},
|
||||
Err(e) => {
|
||||
logger.error("Discovery", &format!("❌ No se pudo leer directorio {}", SYSTEMD_DIR), Some(&e.to_string()));
|
||||
println!("❌ Discovery: ERROR - No se pudo leer {}: {}", SYSTEMD_DIR, e);
|
||||
return services;
|
||||
}
|
||||
};
|
||||
|
||||
// Buscar archivos siax-app-*.service
|
||||
let mut total_files = 0;
|
||||
let mut siax_files = 0;
|
||||
|
||||
for entry in entries.flatten() {
|
||||
total_files += 1;
|
||||
let path = entry.path();
|
||||
if let Some(filename) = path.file_name() {
|
||||
let filename_str = filename.to_string_lossy();
|
||||
|
||||
// Verificar que sea un archivo .service con nuestro prefijo
|
||||
if filename_str.starts_with(SERVICE_PREFIX) && filename_str.ends_with(".service") {
|
||||
siax_files += 1;
|
||||
logger.info("Discovery", &format!("✅ Encontrado: {}", filename_str));
|
||||
println!("✅ Discovery: Servicio detectado: {}", filename_str);
|
||||
|
||||
// Extraer nombre de la app
|
||||
let app_name = extract_app_name(&filename_str);
|
||||
|
||||
// Leer configuración del servicio
|
||||
if let Some(service) = parse_service_file(&path, &app_name) {
|
||||
services.push(service);
|
||||
} else {
|
||||
logger.warning("Discovery", &format!("⚠️ No se pudo parsear: {}", filename_str), None);
|
||||
println!("⚠️ Discovery: No se pudo parsear {}", filename_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Discovery", &format!("📊 Escaneados {} archivos, {} con prefijo '{}', {} parseados exitosamente",
|
||||
total_files, siax_files, SERVICE_PREFIX, services.len()));
|
||||
println!("📊 Discovery: Archivos totales: {}, siax-app-*: {}, parseados: {}",
|
||||
total_files, siax_files, services.len());
|
||||
|
||||
services
|
||||
}
|
||||
|
||||
/// Extrae el nombre de la app desde el nombre del archivo
|
||||
/// Ejemplo: "siax-app-IDEAS.service" -> "app_IDEAS"
|
||||
fn extract_app_name(filename: &str) -> String {
|
||||
// Remover "siax-app-" del inicio y ".service" del final
|
||||
filename
|
||||
.trim_start_matches(SERVICE_PREFIX)
|
||||
.trim_end_matches(".service")
|
||||
.to_string()
|
||||
}
|
||||
|
||||
/// Servicio descubierto en systemd
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DiscoveredService {
|
||||
pub app_name: String,
|
||||
pub service_file: String,
|
||||
pub working_directory: Option<String>,
|
||||
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 completa
|
||||
fn parse_service_file(path: &Path, app_name: &str) -> Option<DiscoveredService> {
|
||||
let logger = get_logger();
|
||||
|
||||
let content = match fs::read_to_string(path) {
|
||||
Ok(content) => content,
|
||||
Err(e) => {
|
||||
logger.error("Discovery", &format!("Error leyendo {}", path.display()), Some(&e.to_string()));
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let mut service = DiscoveredService {
|
||||
app_name: app_name.to_string(),
|
||||
service_file: path.to_string_lossy().to_string(),
|
||||
working_directory: None,
|
||||
user: None,
|
||||
exec_start: None,
|
||||
port: None,
|
||||
node_env: String::from("production"),
|
||||
entry_point: None,
|
||||
node_bin: None,
|
||||
};
|
||||
|
||||
// Parsear líneas del archivo
|
||||
for line in content.lines() {
|
||||
let line = line.trim();
|
||||
|
||||
// WorkingDirectory
|
||||
if line.starts_with("WorkingDirectory=") {
|
||||
service.working_directory = Some(line.trim_start_matches("WorkingDirectory=").to_string());
|
||||
}
|
||||
|
||||
// User
|
||||
if line.starts_with("User=") {
|
||||
service.user = Some(line.trim_start_matches("User=").to_string());
|
||||
}
|
||||
|
||||
// ExecStart
|
||||
if line.starts_with("ExecStart=") {
|
||||
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
|
||||
if line.starts_with("Environment=") && line.contains("PORT") {
|
||||
if let Some(port) = extract_port_from_env(line) {
|
||||
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: {:?}, Env: {}, EntryPoint: {:?}",
|
||||
service.app_name,
|
||||
service.user,
|
||||
service.working_directory,
|
||||
service.node_env,
|
||||
service.entry_point
|
||||
));
|
||||
|
||||
Some(service)
|
||||
}
|
||||
|
||||
/// Extrae el puerto de una línea Environment
|
||||
/// Ejemplo: Environment="PORT=3000" -> Some(3000)
|
||||
fn extract_port_from_env(line: &str) -> Option<i32> {
|
||||
// Buscar PORT=número
|
||||
if let Some(start) = line.find("PORT=") {
|
||||
let after_port = &line[start + 5..];
|
||||
// Extraer números
|
||||
let port_str: String = after_port.chars()
|
||||
.take_while(|c| c.is_numeric())
|
||||
.collect();
|
||||
|
||||
port_str.parse::<i32>().ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// 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();
|
||||
let config_manager = get_config_manager();
|
||||
|
||||
logger.info("Discovery", &format!("🔄 Sincronizando {} servicios descubiertos...", services.len()));
|
||||
println!("🔄 Discovery: Sincronizando {} servicios con monitored_apps.json", services.len());
|
||||
|
||||
let mut added_count = 0;
|
||||
let mut skipped_count = 0;
|
||||
|
||||
for service in services {
|
||||
// Intentar detectar el puerto si no se encontró en Environment
|
||||
let port = service.port.unwrap_or_else(|| {
|
||||
detect_port_from_name(&service.app_name)
|
||||
});
|
||||
|
||||
// Verificar si ya existe en la configuración
|
||||
let existing_apps = config_manager.get_apps();
|
||||
let already_exists = existing_apps.iter().any(|app| app.name == service.app_name);
|
||||
|
||||
if already_exists {
|
||||
logger.info("Discovery", &format!("⏭️ {} ya existe en configuración", service.app_name));
|
||||
println!("⏭️ Discovery: {} ya existe, omitiendo", service.app_name);
|
||||
skipped_count += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Crear MonitoredApp con información completa
|
||||
let service_name = format!("siax-app-{}.service", service.app_name);
|
||||
let registered_at = chrono::Local::now().to_rfc3339();
|
||||
|
||||
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));
|
||||
println!("✅ Discovery: {} agregado a monitored_apps.json", service.app_name);
|
||||
added_count += 1;
|
||||
}
|
||||
Err(e) => {
|
||||
logger.error("Discovery", &format!("Error agregando {}", service.app_name), Some(&e));
|
||||
println!("❌ Discovery: Error agregando {}: {}", service.app_name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Discovery", &format!("📊 Resumen: {} agregadas, {} ya existían", added_count, skipped_count));
|
||||
println!("📊 Discovery: Resumen final - {} apps nuevas, {} existentes", added_count, skipped_count);
|
||||
}
|
||||
|
||||
/// Intenta detectar el puerto desde el nombre de la app
|
||||
/// Esto es un fallback simple si no se encuentra en el .service
|
||||
fn detect_port_from_name(app_name: &str) -> i32 {
|
||||
// Algunos puertos conocidos por nombre
|
||||
match app_name.to_lowercase().as_str() {
|
||||
name if name.contains("tareas") => 3000,
|
||||
name if name.contains("fidelizacion") => 3001,
|
||||
name if name.contains("ideas") => 2000,
|
||||
_ => 8080, // Puerto por defecto genérico
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_extract_app_name() {
|
||||
assert_eq!(extract_app_name("siax-app-IDEAS.service"), "IDEAS");
|
||||
assert_eq!(extract_app_name("siax-app-TAREAS.service"), "TAREAS");
|
||||
assert_eq!(extract_app_name("siax-app-fidelizacion.service"), "fidelizacion");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_port_from_env() {
|
||||
assert_eq!(extract_port_from_env("Environment=PORT=3000"), Some(3000));
|
||||
assert_eq!(extract_port_from_env("Environment=\"PORT=8080\""), Some(8080));
|
||||
assert_eq!(extract_port_from_env("Environment=NODE_ENV=production"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_detect_port_from_name() {
|
||||
assert_eq!(detect_port_from_name("app_tareas"), 3000);
|
||||
assert_eq!(detect_port_from_name("IDEAS"), 2000);
|
||||
assert_eq!(detect_port_from_name("unknown_app"), 8080);
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,10 @@ pub mod logger;
|
||||
pub mod config;
|
||||
pub mod monitor;
|
||||
pub mod interface;
|
||||
pub mod discovery;
|
||||
|
||||
// Re-exportar solo lo necesario para evitar conflictos
|
||||
pub use models::{ServiceConfig, ManagedApp, AppStatus, ServiceStatus, AppType, RestartPolicy};
|
||||
pub use logger::{Logger, LogEntry, LogLevel, get_logger};
|
||||
pub use config::{ConfigManager, MonitoredApp, AppConfig, get_config_manager};
|
||||
pub use discovery::{discover_services, sync_discovered_services, DiscoveredService};
|
||||
|
||||
13
src/main.rs
13
src/main.rs
@@ -6,11 +6,13 @@ mod models;
|
||||
mod systemd;
|
||||
mod orchestrator;
|
||||
mod api;
|
||||
mod discovery;
|
||||
|
||||
use logger::get_logger;
|
||||
use config::get_config_manager;
|
||||
use orchestrator::{AppManager, LifecycleManager};
|
||||
use api::{ApiState, WebSocketManager};
|
||||
use discovery::{discover_services, sync_discovered_services};
|
||||
use std::sync::Arc;
|
||||
use axum::{
|
||||
routing::{get, post, delete},
|
||||
@@ -24,7 +26,14 @@ async fn main() {
|
||||
let logger = get_logger();
|
||||
logger.info("Sistema", "Iniciando SIAX Agent");
|
||||
|
||||
// Inicializar config manager
|
||||
// 🔍 Descubrir servicios systemd existentes
|
||||
logger.info("Sistema", "Escaneando servicios systemd existentes...");
|
||||
let discovered = discover_services();
|
||||
if !discovered.is_empty() {
|
||||
sync_discovered_services(discovered);
|
||||
}
|
||||
|
||||
// Inicializar config manager (ahora con servicios descubiertos)
|
||||
let config_manager = get_config_manager();
|
||||
let apps = config_manager.get_apps();
|
||||
println!("📋 Apps a monitorear: {:?}", apps);
|
||||
@@ -58,6 +67,8 @@ async fn main() {
|
||||
// Router para la API REST
|
||||
let api_router = Router::new()
|
||||
.route("/api/health", get(api::health_handler))
|
||||
.route("/api/monitored", get(api::get_monitored_apps_handler))
|
||||
.route("/api/logs/errors", get(api::get_system_error_logs))
|
||||
.route("/api/apps", get(api::list_apps_handler).post(api::register_app_handler))
|
||||
.route("/api/apps/:name", delete(api::unregister_app_handler))
|
||||
.route("/api/apps/:name/status", get(api::get_app_status_handler))
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -26,7 +26,7 @@ impl LifecycleManager {
|
||||
|
||||
logger.info("Lifecycle", &format!("Iniciando aplicación: {}", app_name));
|
||||
|
||||
let service_name = format!("{}.service", app_name);
|
||||
let service_name = format!("siax-app-{}.service", app_name);
|
||||
SystemCtl::start(&service_name)?;
|
||||
|
||||
// Actualizar rate limiter
|
||||
@@ -45,7 +45,7 @@ impl LifecycleManager {
|
||||
|
||||
logger.info("Lifecycle", &format!("Deteniendo aplicación: {}", app_name));
|
||||
|
||||
let service_name = format!("{}.service", app_name);
|
||||
let service_name = format!("siax-app-{}.service", app_name);
|
||||
SystemCtl::stop(&service_name)?;
|
||||
|
||||
// Actualizar rate limiter
|
||||
@@ -64,7 +64,7 @@ impl LifecycleManager {
|
||||
|
||||
logger.info("Lifecycle", &format!("Reiniciando aplicación: {}", app_name));
|
||||
|
||||
let service_name = format!("{}.service", app_name);
|
||||
let service_name = format!("siax-app-{}.service", app_name);
|
||||
SystemCtl::restart(&service_name)?;
|
||||
|
||||
// Actualizar rate limiter
|
||||
|
||||
@@ -74,17 +74,38 @@ impl ServiceGenerator {
|
||||
format!("{} {}", executable, config.script_path)
|
||||
};
|
||||
|
||||
// Generar variables de entorno
|
||||
let env_vars = config.environment
|
||||
// Generar variables de entorno del usuario
|
||||
let mut env_lines: Vec<String> = config.environment
|
||||
.iter()
|
||||
.map(|(key, value)| format!("Environment=\"{}={}\"", key, value))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
.collect();
|
||||
|
||||
// Agregar PATH con directorio de NVM si se detectó npm o node en NVM
|
||||
let using_nvm = executable.contains("/.nvm/");
|
||||
if using_nvm {
|
||||
// Extraer el directorio bin de NVM
|
||||
if let Some(bin_dir) = executable.rfind("/bin/") {
|
||||
let nvm_bin = &executable[..bin_dir + 4]; // Incluye /bin
|
||||
let path_env = format!("Environment=PATH={}:/usr/local/bin:/usr/bin:/bin", nvm_bin);
|
||||
env_lines.insert(0, path_env);
|
||||
logger.info("ServiceGenerator", &format!("Agregando PATH de NVM: {}", nvm_bin));
|
||||
}
|
||||
}
|
||||
|
||||
// Agregar NODE_ENV=production por defecto para Node.js si no está definido
|
||||
if matches!(config.app_type, crate::models::AppType::NodeJs) {
|
||||
if !config.environment.contains_key("NODE_ENV") {
|
||||
env_lines.push("Environment=NODE_ENV=production".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let env_vars = env_lines.join("\n");
|
||||
|
||||
// Agregar SyslogIdentifier para logs más claros
|
||||
let syslog_id = format!("SyslogIdentifier=siax-app-{}", config.app_name);
|
||||
|
||||
format!(
|
||||
// Construir el servicio con orden lógico
|
||||
let mut service = format!(
|
||||
r#"[Unit]
|
||||
Description={}
|
||||
After=network.target
|
||||
@@ -93,23 +114,35 @@ After=network.target
|
||||
Type=simple
|
||||
User={}
|
||||
WorkingDirectory={}
|
||||
ExecStart={}
|
||||
"#,
|
||||
description,
|
||||
config.user,
|
||||
config.working_directory
|
||||
);
|
||||
|
||||
// Agregar variables de entorno (PATH primero, luego las demás)
|
||||
if !env_vars.is_empty() {
|
||||
service.push_str(&env_vars);
|
||||
service.push('\n');
|
||||
}
|
||||
|
||||
// Agregar comando de ejecución
|
||||
service.push_str(&format!("ExecStart={}\n", exec_start));
|
||||
|
||||
// Agregar políticas de reinicio
|
||||
service.push_str(&format!(r#"
|
||||
Restart={}
|
||||
RestartSec=10
|
||||
{}
|
||||
{}
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
"#,
|
||||
description,
|
||||
config.user,
|
||||
config.working_directory,
|
||||
exec_start,
|
||||
config.restart_policy.as_systemd_str(),
|
||||
env_vars,
|
||||
syslog_id
|
||||
)
|
||||
));
|
||||
|
||||
service
|
||||
}
|
||||
|
||||
/// Resuelve el ejecutable a usar (con auto-detección)
|
||||
|
||||
42
test_service_generation.sh
Normal file
42
test_service_generation.sh
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script de prueba para verificar la generación de servicios con NVM
|
||||
|
||||
echo "=== Test: Generación de servicio con NVM ==="
|
||||
echo ""
|
||||
|
||||
# Simular generación de servicio
|
||||
cat << 'EOF'
|
||||
SERVICIO GENERADO (simulado):
|
||||
|
||||
[Unit]
|
||||
Description=App para gestionar Tareas
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=user_apps
|
||||
WorkingDirectory=/home/user_apps/apps/app_tareas
|
||||
Environment=PATH=/home/user_apps/.nvm/versions/node/v24.12.0/bin:/usr/local/bin:/usr/bin:/bin
|
||||
Environment=NODE_ENV=production
|
||||
ExecStart=/home/user_apps/.nvm/versions/node/v24.12.0/bin/npm start
|
||||
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
SyslogIdentifier=siax-app-TAREAS
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
CARACTERÍSTICAS:
|
||||
✅ Environment=PATH incluye directorio NVM automáticamente
|
||||
✅ Environment=NODE_ENV=production por defecto
|
||||
✅ SyslogIdentifier para logs claros
|
||||
✅ Orden lógico: PATH primero, luego env vars del usuario
|
||||
|
||||
COMANDOS PARA APLICAR (ejecutados por AppManager automáticamente):
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable siax-app-TAREAS.service
|
||||
sudo systemctl start siax-app-TAREAS.service
|
||||
|
||||
EOF
|
||||
@@ -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
764
web/blog.html
Normal 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
563
web/health.html
Normal 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>
|
||||
296
web/index.html
296
web/index.html
@@ -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"
|
||||
>
|
||||
<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="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">
|
||||
<div
|
||||
class="size-8 bg-primary rounded-lg flex items-center justify-center text-white"
|
||||
>
|
||||
<span class="material-symbols-outlined"
|
||||
>monitoring</span
|
||||
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>
|
||||
<h2
|
||||
class="text-white text-lg font-bold leading-tight tracking-[-0.015em]"
|
||||
>
|
||||
SIAX Monitor
|
||||
</h2>
|
||||
<div>
|
||||
<h1 class="text-xl font-bold">SIAX Monitor</h1>
|
||||
<p class="text-xs text-slate-400">Dashboard</p>
|
||||
</div>
|
||||
<nav class="hidden md:flex items-center gap-6">
|
||||
</div>
|
||||
|
||||
<!-- 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 class="material-symbols-outlined text-2xl"
|
||||
>menu</span
|
||||
>
|
||||
<span>Registrar App</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu Dropdown -->
|
||||
<div
|
||||
class="bg-center bg-no-repeat aspect-square bg-cover rounded-full size-9 border-2 border-slate-700"
|
||||
style="
|
||||
background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuCT0iINTncUFHp353HCJXRR5C0OKbSp_7IBOVNoDU07yuF2aToQQdnXNOeGI9RLUjVBsVNcU--ZoTMY90FFJvrQvYvRzKvq-CFCzBlVkCeoi5AgG84cB71wW0NIMg626M_sCjmDjxqmAJwIbkAcSmSlAg3TUThW1U2A3StNVgqFXEpgFbpJcU5nxLs6vuRkfYR1kIXcV44TQpgOosbsjSB1Pk1UTOQJ_OEcQtY-5c3FJw7gXBDxlp6y3jsY3rBm0xWGJi8NWnrUrhpl");
|
||||
"
|
||||
></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
|
||||
@@ -336,7 +364,7 @@
|
||||
<th class="px-6 py-4">Mem %</th>
|
||||
<th class="px-6 py-4">Tiempo Activo</th>
|
||||
<th class="px-6 py-4 text-right">
|
||||
Actions
|
||||
Acciones
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -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) {
|
||||
@@ -470,8 +496,45 @@
|
||||
function displayApps(apps) {
|
||||
const tbody = document.getElementById("apps-tbody");
|
||||
tbody.innerHTML = apps
|
||||
.map(
|
||||
(app) => `
|
||||
.map((app) => {
|
||||
// Determinar color del badge según estado
|
||||
const statusColors = {
|
||||
Running: {
|
||||
bg: "bg-green-100 dark:bg-green-900/30",
|
||||
text: "text-green-700 dark:text-green-400",
|
||||
dot: "bg-green-500",
|
||||
},
|
||||
Stopped: {
|
||||
bg: "bg-gray-100 dark:bg-gray-800",
|
||||
text: "text-gray-700 dark:text-gray-400",
|
||||
dot: "bg-gray-400",
|
||||
},
|
||||
Failed: {
|
||||
bg: "bg-red-100 dark:bg-red-900/30",
|
||||
text: "text-red-700 dark:text-red-400",
|
||||
dot: "bg-red-500",
|
||||
},
|
||||
Starting: {
|
||||
bg: "bg-blue-100 dark:bg-blue-900/30",
|
||||
text: "text-blue-700 dark:text-blue-400",
|
||||
dot: "bg-blue-500",
|
||||
},
|
||||
Stopping: {
|
||||
bg: "bg-yellow-100 dark:bg-yellow-900/30",
|
||||
text: "text-yellow-700 dark:text-yellow-400",
|
||||
dot: "bg-yellow-500",
|
||||
},
|
||||
Unknown: {
|
||||
bg: "bg-slate-100 dark:bg-slate-800",
|
||||
text: "text-slate-700 dark:text-slate-400",
|
||||
dot: "bg-slate-400",
|
||||
},
|
||||
};
|
||||
|
||||
const statusStyle =
|
||||
statusColors[app.status] || statusColors["Unknown"];
|
||||
|
||||
return `
|
||||
<tr class="hover:bg-slate-50 dark:hover:bg-slate-800/40 transition-colors">
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex items-center gap-3">
|
||||
@@ -479,28 +542,54 @@
|
||||
<span class="material-symbols-outlined text-primary text-lg">terminal</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-slate-900 dark:text-white font-semibold text-sm">${app}</p>
|
||||
<p class="text-slate-500 text-xs">Servicio</p>
|
||||
<p class="text-slate-900 dark:text-white font-semibold text-sm">${app.name}</p>
|
||||
<p class="text-slate-500 text-xs">${app.service_name || "Servicio"}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<span class="inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium bg-slate-100 text-slate-800 dark:bg-slate-800 dark:text-slate-400">
|
||||
<span class="size-1.5 rounded-full bg-slate-400"></span>
|
||||
Unknown
|
||||
<span class="inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium ${statusStyle.bg} ${statusStyle.text}">
|
||||
<span class="size-1.5 rounded-full ${statusStyle.dot}"></span>
|
||||
${app.status}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm">-</td>
|
||||
<td class="px-6 py-4 text-sm">-</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-500">-</td>
|
||||
<td class="px-6 py-4 text-right">
|
||||
<button class="text-slate-400 hover:text-white transition-colors">
|
||||
<span class="material-symbols-outlined">more_vert</span>
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
${
|
||||
app.status === "Running"
|
||||
? `
|
||||
<button class="text-red-400 hover:text-red-300 transition-colors p-1.5 rounded hover:bg-red-900/20"
|
||||
onclick="controlApp('${app.name}', 'stop')"
|
||||
title="Detener">
|
||||
<span class="material-symbols-outlined text-[20px]">stop</span>
|
||||
</button>
|
||||
<button class="text-yellow-400 hover:text-yellow-300 transition-colors p-1.5 rounded hover:bg-yellow-900/20"
|
||||
onclick="controlApp('${app.name}', 'restart')"
|
||||
title="Reiniciar">
|
||||
<span class="material-symbols-outlined text-[20px]">refresh</span>
|
||||
</button>
|
||||
`
|
||||
: `
|
||||
<button class="text-green-400 hover:text-green-300 transition-colors p-1.5 rounded hover:bg-green-900/20"
|
||||
onclick="controlApp('${app.name}', 'start')"
|
||||
title="Iniciar">
|
||||
<span class="material-symbols-outlined text-[20px]">play_arrow</span>
|
||||
</button>
|
||||
`
|
||||
}
|
||||
<button class="text-blue-400 hover:text-blue-300 transition-colors p-1.5 rounded hover:bg-blue-900/20"
|
||||
onclick="window.location.href='/logs'"
|
||||
title="Ver logs">
|
||||
<span class="material-symbols-outlined text-[20px]">visibility</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`,
|
||||
)
|
||||
`;
|
||||
})
|
||||
.join("");
|
||||
}
|
||||
|
||||
@@ -516,6 +605,49 @@
|
||||
`;
|
||||
}
|
||||
|
||||
async function controlApp(appName, action) {
|
||||
const actionNames = {
|
||||
start: "Iniciar",
|
||||
stop: "Detener",
|
||||
restart: "Reiniciar",
|
||||
};
|
||||
|
||||
const confirmed = confirm(
|
||||
`¿Estás seguro de ${actionNames[action]} la aplicación "${appName}"?`,
|
||||
);
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/apps/${appName}/${action}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
alert(`✅ ${result.data.message}`);
|
||||
// Recargar la lista de apps
|
||||
loadApps();
|
||||
} else {
|
||||
alert(`❌ Error: ${result.error}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
alert("❌ Error al ejecutar la acción");
|
||||
}
|
||||
}
|
||||
|
||||
function toggleMenu() {
|
||||
const menu = document.getElementById("mobile-menu");
|
||||
menu.classList.toggle("hidden");
|
||||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", loadApps);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
179
web/logs.html
179
web/logs.html
@@ -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>
|
||||
@@ -203,10 +207,38 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Terminal Log Output -->
|
||||
<!-- Tabs -->
|
||||
<div class="border-b border-[#283039] bg-[#161f2a] px-4">
|
||||
<div class="flex gap-1">
|
||||
<button
|
||||
id="tab-app-logs"
|
||||
onclick="switchTab('app-logs')"
|
||||
class="tab-button px-4 py-2 text-sm font-medium transition-colors rounded-t-lg border-b-2 border-primary text-primary"
|
||||
>
|
||||
<span
|
||||
class="material-symbols-outlined text-[16px] align-middle"
|
||||
>terminal</span
|
||||
>
|
||||
Logs de App
|
||||
</button>
|
||||
<button
|
||||
id="tab-system-errors"
|
||||
onclick="switchTab('system-errors')"
|
||||
class="tab-button px-4 py-2 text-sm font-medium transition-colors rounded-t-lg border-b-2 border-transparent text-[#9dabb9] hover:text-white"
|
||||
>
|
||||
<span
|
||||
class="material-symbols-outlined text-[16px] align-middle"
|
||||
>error</span
|
||||
>
|
||||
Errores del Sistema
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content: App Logs -->
|
||||
<div
|
||||
class="flex-1 bg-[#0a0f16] overflow-y-auto p-4 font-mono text-sm"
|
||||
id="log-terminal"
|
||||
id="content-app-logs"
|
||||
class="flex-1 bg-[#0a0f16] overflow-y-auto p-4 font-mono text-sm tab-content"
|
||||
>
|
||||
<div id="log-container" class="space-y-1">
|
||||
<!-- Welcome Message -->
|
||||
@@ -220,6 +252,19 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content: System Errors -->
|
||||
<div
|
||||
id="content-system-errors"
|
||||
class="hidden flex-1 bg-[#0a0f16] overflow-y-auto p-4 font-mono text-sm tab-content"
|
||||
>
|
||||
<div id="system-errors-container" class="space-y-1">
|
||||
<div class="text-[#9dabb9] opacity-50">
|
||||
<span class="text-yellow-400">⚠</span> Cargando logs
|
||||
de errores del sistema...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -238,9 +283,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 +364,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 =
|
||||
@@ -399,8 +443,9 @@
|
||||
logContainer.appendChild(logEntry);
|
||||
|
||||
// Auto-scroll
|
||||
if (autoScroll) {
|
||||
const terminal = document.getElementById("log-terminal");
|
||||
if (autoScroll && currentTab === "app-logs") {
|
||||
const terminal =
|
||||
document.getElementById("content-app-logs");
|
||||
terminal.scrollTop = terminal.scrollHeight;
|
||||
}
|
||||
|
||||
@@ -457,6 +502,100 @@
|
||||
`;
|
||||
}
|
||||
|
||||
// Tab switching
|
||||
let currentTab = "app-logs";
|
||||
|
||||
function switchTab(tabName) {
|
||||
currentTab = tabName;
|
||||
|
||||
// Update tab buttons
|
||||
document.querySelectorAll(".tab-button").forEach((btn) => {
|
||||
btn.classList.remove("border-primary", "text-primary");
|
||||
btn.classList.add("border-transparent", "text-[#9dabb9]");
|
||||
});
|
||||
|
||||
const activeTab = document.getElementById(`tab-${tabName}`);
|
||||
activeTab.classList.remove(
|
||||
"border-transparent",
|
||||
"text-[#9dabb9]",
|
||||
);
|
||||
activeTab.classList.add("border-primary", "text-primary");
|
||||
|
||||
// Update tab content
|
||||
document.querySelectorAll(".tab-content").forEach((content) => {
|
||||
content.classList.add("hidden");
|
||||
});
|
||||
|
||||
document
|
||||
.getElementById(`content-${tabName}`)
|
||||
.classList.remove("hidden");
|
||||
|
||||
// Load system errors if switching to that tab
|
||||
if (tabName === "system-errors") {
|
||||
loadSystemErrors();
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSystemErrors() {
|
||||
const container = document.getElementById(
|
||||
"system-errors-container",
|
||||
);
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/logs/errors");
|
||||
const result = await response.json();
|
||||
|
||||
if (
|
||||
result.success &&
|
||||
result.logs &&
|
||||
result.logs.length > 0
|
||||
) {
|
||||
container.innerHTML = result.logs
|
||||
.map((line) => {
|
||||
// Parse log line
|
||||
let icon = "●";
|
||||
let color = "text-white";
|
||||
|
||||
if (line.includes("[ERROR]")) {
|
||||
icon = "✖";
|
||||
color = "text-red-400";
|
||||
} else if (line.includes("[WARN]")) {
|
||||
icon = "⚠";
|
||||
color = "text-yellow-400";
|
||||
} else if (line.includes("[INFO]")) {
|
||||
icon = "ℹ";
|
||||
color = "text-blue-400";
|
||||
}
|
||||
|
||||
return `<div class="log-line ${color}">${icon} ${escapeHtml(line)}</div>`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
// Auto scroll to bottom
|
||||
container.scrollTop = container.scrollHeight;
|
||||
} else if (result.message) {
|
||||
container.innerHTML = `
|
||||
<div class="text-[#9dabb9]">
|
||||
<span class="text-yellow-400">⚠</span> ${result.message}
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
container.innerHTML = `
|
||||
<div class="text-[#9dabb9]">
|
||||
<span class="text-blue-400">ℹ</span> No hay logs de errores disponibles
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading system errors:", error);
|
||||
container.innerHTML = `
|
||||
<div class="text-red-400">
|
||||
<span class="text-red-400">✖</span> Error cargando logs del sistema
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// Load apps on page load
|
||||
document.addEventListener("DOMContentLoaded", loadApps);
|
||||
|
||||
|
||||
@@ -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",
|
||||
{
|
||||
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" },
|
||||
);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
BIN
web/static/icon/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
44
web/static/icon/favicon.svg
Normal file
44
web/static/icon/favicon.svg
Normal 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
BIN
web/static/icon/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
web/static/icon/logo_telco128.png
Normal file
BIN
web/static/icon/logo_telco128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user