feat: Implementación completa Fase 4 - Sistema de monitoreo con API REST y WebSocket

 Nuevas funcionalidades:
- API REST unificada en puerto 8080 (eliminado CORS)
- WebSocket para logs en tiempo real desde journalctl
- Integración completa con systemd para gestión de servicios
- Escaneo automático de procesos Node.js y Python
- Rate limiting (1 operación/segundo por app)
- Interface web moderna con Tailwind CSS (tema oscuro)
- Documentación API estilo Swagger completamente en español

🎨 Interface Web (todas las páginas en español):
- Dashboard con estadísticas en tiempo real
- Visor de escaneo de procesos con filtros
- Formulario de registro de aplicaciones con variables de entorno
- Visor de logs en tiempo real con WebSocket y sidebar
- Página de selección de apps detectadas
- Documentación completa de API REST

🏗️ Arquitectura:
- Módulo models: ServiceConfig, ManagedApp, AppStatus
- Módulo systemd: wrapper de systemctl, generador de .service, parser
- Módulo orchestrator: AppManager, LifecycleManager con validaciones
- Módulo api: handlers REST, WebSocket manager, DTOs
- Servidor unificado en puerto 8080 (Web + API + WS)

🔧 Mejoras técnicas:
- Eliminación de CORS mediante servidor unificado
- Separación clara frontend/backend con carga dinámica
- Thread-safe con Arc<DashMap> para estado compartido
- Reconciliación de estados: sysinfo vs systemd
- Validaciones de paths, usuarios y configuraciones
- Manejo robusto de errores con thiserror

📝 Documentación:
- README.md actualizado con arquitectura completa
- EJEMPLOS.md con casos de uso detallados
- ESTADO_PROYECTO.md con progreso de Fase 4
- API docs interactiva en /api-docs
- Script de despliegue mejorado con health checks

🚀 Producción:
- Deployment script con validaciones
- Health checks y rollback capability
- Configuración de sudoers para systemctl
- Hardening de seguridad en servicios systemd
This commit is contained in:
2026-01-13 08:24:13 -05:00
parent 3595e55a1e
commit b0489739cf
33 changed files with 6893 additions and 1261 deletions

504
EJEMPLOS.md Normal file
View File

@@ -0,0 +1,504 @@
# Ejemplos Prácticos de Uso - SIAX Monitor
## Tabla de Contenidos
1. [Instalación](#instalación)
2. [Gestión de Aplicaciones Node.js](#nodejs)
3. [Gestión de Aplicaciones Python](#python)
4. [Monitoreo y Logs](#monitoreo)
5. [Casos de Uso Reales](#casos-de-uso)
6. [Troubleshooting](#troubleshooting)
---
## Instalación
### Instalación Completa
```bash
# Clonar el proyecto
git clone <repo-url>
cd siax_monitor
# Ejecutar script de deployment
sudo ./desplegar_agent.sh
# Verificar instalación
sudo systemctl status siax-agent
```
### Verificar que todo funciona
```bash
# Verificar servicio
curl http://localhost:8081/api/apps
# Debe responder:
# {"success":true,"data":{"apps":[],"total":0}}
```
---
## <a name="nodejs"></a>Gestión de Aplicaciones Node.js
### Ejemplo 1: API Express Básica
```bash
# Registrar aplicación
curl -X POST http://localhost:8081/api/apps \
-H "Content-Type: application/json" \
-d '{
"app_name": "api-express",
"script_path": "/home/nodejs/api-express/server.js",
"working_directory": "/home/nodejs/api-express",
"user": "nodejs",
"environment": {
"PORT": "3000",
"NODE_ENV": "production"
},
"restart_policy": "always",
"app_type": "nodejs",
"description": "API REST con Express"
}'
# Iniciar aplicación
curl -X POST http://localhost:8081/api/apps/api-express/start
# Ver estado
curl http://localhost:8081/api/apps/api-express/status | jq
```
### Ejemplo 2: Aplicación Next.js
```bash
curl -X POST http://localhost:8081/api/apps \
-H "Content-Type: application/json" \
-d '{
"app_name": "frontend-nextjs",
"script_path": "/var/www/frontend/.next/standalone/server.js",
"working_directory": "/var/www/frontend",
"user": "www-data",
"environment": {
"PORT": "3000",
"NODE_ENV": "production",
"HOSTNAME": "0.0.0.0"
},
"restart_policy": "always",
"app_type": "nodejs"
}'
curl -X POST http://localhost:8081/api/apps/frontend-nextjs/start
```
### Ejemplo 3: Worker de Background (Bull Queue)
```bash
curl -X POST http://localhost:8081/api/apps \
-H "Content-Type: application/json" \
-d '{
"app_name": "queue-worker",
"script_path": "/opt/workers/queue-worker/index.js",
"working_directory": "/opt/workers/queue-worker",
"user": "workers",
"environment": {
"REDIS_URL": "redis://localhost:6379",
"CONCURRENCY": "5"
},
"restart_policy": "on-failure",
"app_type": "nodejs",
"description": "Worker de procesamiento de colas"
}'
```
---
## <a name="python"></a>Gestión de Aplicaciones Python
### Ejemplo 1: API FastAPI
```bash
curl -X POST http://localhost:8081/api/apps \
-H "Content-Type: application/json" \
-d '{
"app_name": "ml-api",
"script_path": "/home/python/ml-api/main.py",
"working_directory": "/home/python/ml-api",
"user": "python",
"environment": {
"PORT": "8000",
"WORKERS": "4",
"PYTHONUNBUFFERED": "1"
},
"restart_policy": "always",
"app_type": "python",
"description": "API de Machine Learning"
}'
curl -X POST http://localhost:8081/api/apps/ml-api/start
```
**Nota:** Asegúrate de que tu `main.py` tiene un servidor ASGI:
```python
# main.py
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
if __name__ == "__main__":
import os
port = int(os.getenv("PORT", 8000))
workers = int(os.getenv("WORKERS", 4))
uvicorn.run("main:app", host="0.0.0.0", port=port, workers=workers)
```
### Ejemplo 2: Script de Data Processing
```bash
curl -X POST http://localhost:8081/api/apps \
-H "Content-Type: application/json" \
-d '{
"app_name": "data-processor",
"script_path": "/opt/scripts/data-processor/processor.py",
"working_directory": "/opt/scripts/data-processor",
"user": "dataops",
"environment": {
"DB_HOST": "localhost",
"DB_PORT": "5432",
"LOG_LEVEL": "INFO"
},
"restart_policy": "on-failure",
"app_type": "python"
}'
```
---
## <a name="monitoreo"></a>Monitoreo y Logs
### Ver logs en tiempo real con WebSocket
#### Opción 1: JavaScript (Browser)
```javascript
// Conectar a logs
const ws = new WebSocket('ws://localhost:8081/ws/logs/api-express');
ws.onopen = () => {
console.log('Conectado a logs de api-express');
};
ws.onmessage = (event) => {
console.log('LOG:', event.data);
// Mostrar en UI
document.getElementById('logs').innerHTML += event.data + '<br>';
};
ws.onerror = (error) => {
console.error('Error:', error);
};
ws.onclose = () => {
console.log('Desconectado');
};
```
#### Opción 2: wscat (CLI)
```bash
# Instalar wscat
npm install -g wscat
# Conectar a logs
wscat -c ws://localhost:8081/ws/logs/api-express
```
#### Opción 3: Python
```python
import websocket
import json
def on_message(ws, message):
try:
data = json.loads(message)
print(f"[{data.get('timestamp', 'N/A')}] {data.get('MESSAGE', message)}")
except:
print(message)
def on_error(ws, error):
print(f"Error: {error}")
def on_close(ws, close_status_code, close_msg):
print("Conexión cerrada")
def on_open(ws):
print("Conectado a logs")
ws = websocket.WebSocketApp(
"ws://localhost:8081/ws/logs/api-express",
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close
)
ws.run_forever()
```
### Verificar estado de múltiples apps
```bash
#!/bin/bash
# check-all-apps.sh
echo "Estado de todas las aplicaciones:"
echo "=================================="
# Obtener lista de apps
apps=$(curl -s http://localhost:8081/api/apps | jq -r '.data.apps[]')
for app in $apps; do
status=$(curl -s http://localhost:8081/api/apps/$app/status | jq -r '.data.status')
pid=$(curl -s http://localhost:8081/api/apps/$app/status | jq -r '.data.pid')
cpu=$(curl -s http://localhost:8081/api/apps/$app/status | jq -r '.data.cpu_usage')
mem=$(curl -s http://localhost:8081/api/apps/$app/status | jq -r '.data.memory_usage')
printf "%-20s [%s] PID: %-6s CPU: %-6s RAM: %s\n" "$app" "$status" "$pid" "$cpu" "$mem"
done
```
---
## <a name="casos-de-uso"></a>Casos de Uso Reales
### Caso 1: Microservicios
```bash
# Registrar todos los microservicios
# Auth Service
curl -X POST http://localhost:8081/api/apps -H "Content-Type: application/json" -d '{
"app_name": "auth-service",
"script_path": "/services/auth/index.js",
"working_directory": "/services/auth",
"user": "services",
"environment": {"PORT": "3001", "NODE_ENV": "production"},
"restart_policy": "always",
"app_type": "nodejs"
}'
# Users Service
curl -X POST http://localhost:8081/api/apps -H "Content-Type: application/json" -d '{
"app_name": "users-service",
"script_path": "/services/users/index.js",
"working_directory": "/services/users",
"user": "services",
"environment": {"PORT": "3002", "NODE_ENV": "production"},
"restart_policy": "always",
"app_type": "nodejs"
}'
# Orders Service
curl -X POST http://localhost:8081/api/apps -H "Content-Type: application/json" -d '{
"app_name": "orders-service",
"script_path": "/services/orders/index.js",
"working_directory": "/services/orders",
"user": "services",
"environment": {"PORT": "3003", "NODE_ENV": "production"},
"restart_policy": "always",
"app_type": "nodejs"
}'
# Iniciar todos
for service in auth-service users-service orders-service; do
curl -X POST http://localhost:8081/api/apps/$service/start
echo "Iniciado: $service"
done
```
### Caso 2: Deployment con Script
```bash
#!/bin/bash
# deploy-app.sh
APP_NAME=$1
APP_PATH=$2
PORT=$3
if [ -z "$APP_NAME" ] || [ -z "$APP_PATH" ] || [ -z "$PORT" ]; then
echo "Uso: ./deploy-app.sh <nombre> <path> <puerto>"
exit 1
fi
echo "Desplegando $APP_NAME..."
# Registrar app
curl -X POST http://localhost:8081/api/apps \
-H "Content-Type: application/json" \
-d "{
\"app_name\": \"$APP_NAME\",
\"script_path\": \"$APP_PATH/index.js\",
\"working_directory\": \"$APP_PATH\",
\"user\": \"nodejs\",
\"environment\": {\"PORT\": \"$PORT\", \"NODE_ENV\": \"production\"},
\"restart_policy\": \"always\",
\"app_type\": \"nodejs\"
}"
echo ""
echo "Iniciando $APP_NAME..."
curl -X POST http://localhost:8081/api/apps/$APP_NAME/start
echo ""
echo "Estado de $APP_NAME:"
curl http://localhost:8081/api/apps/$APP_NAME/status | jq
```
**Uso:**
```bash
./deploy-app.sh mi-nueva-app /opt/apps/mi-nueva-app 3004
```
### Caso 3: Rolling Restart
```bash
#!/bin/bash
# rolling-restart.sh - Reinicia servicios uno por uno
APPS=("auth-service" "users-service" "orders-service")
for app in "${APPS[@]}"; do
echo "Reiniciando $app..."
curl -X POST http://localhost:8081/api/apps/$app/restart
# Esperar que esté activo
sleep 5
status=$(curl -s http://localhost:8081/api/apps/$app/status | jq -r '.data.status')
if [ "$status" = "running" ]; then
echo "$app reiniciado correctamente"
else
echo "❌ ERROR: $app no está corriendo"
exit 1
fi
# Delay entre reinici os
sleep 10
done
echo "✅ Rolling restart completado"
```
---
## <a name="troubleshooting"></a>Troubleshooting
### Problema: App no inicia
```bash
# 1. Ver estado en systemd
sudo systemctl status mi-app.service
# 2. Ver logs de systemd
sudo journalctl -u mi-app.service -n 100
# 3. Ver logs en tiempo real
wscat -c ws://localhost:8081/ws/logs/mi-app
# 4. Verificar permisos del archivo
ls -la /path/to/script.js
# 5. Probar ejecución manual
sudo -u nodejs node /path/to/script.js
```
### Problema: Rate limit excedido
```bash
# Error: "Rate limit excedido para: mi-app"
# Solución: Esperar 1 segundo entre operaciones
curl -X POST http://localhost:8081/api/apps/mi-app/restart
sleep 2 # Esperar antes de la siguiente operación
curl -X POST http://localhost:8081/api/apps/otra-app/restart
```
### Problema: WebSocket no conecta
```bash
# Verificar que el puerto está abierto
sudo netstat -tlnp | grep 8081
# Verificar que hay menos de 5 conexiones activas
# (límite por app)
# Probar con curl primero
curl http://localhost:8081/api/apps
```
### Problema: Permisos sudo
```bash
# Verificar configuración
sudo cat /etc/sudoers.d/siax-agent
# Probar manualmente
sudo -u siax-agent sudo systemctl status siax-agent
# Si falla, re-ejecutar deployment
sudo ./desplegar_agent.sh
```
---
## Scripts Útiles
### Monitor Dashboard (Bash)
```bash
#!/bin/bash
# dashboard.sh - Dashboard simple en terminal
watch -n 2 '
echo "=== SIAX Monitor Dashboard ==="
echo ""
curl -s http://localhost:8081/api/apps | jq -r ".data.apps[]" | while read app; do
status=$(curl -s http://localhost:8081/api/apps/$app/status)
echo "$status" | jq -r "\"[\(.data.status)] \(.data.name) - PID: \(.data.pid) CPU: \(.data.cpu_usage) RAM: \(.data.memory_usage)\""
done
'
```
### Auto-Restart on Crash
```bash
#!/bin/bash
# auto-restart.sh - Reinicia apps crasheadas automáticamente
while true; do
apps=$(curl -s http://localhost:8081/api/apps | jq -r '.data.apps[]')
for app in $apps; do
status=$(curl -s http://localhost:8081/api/apps/$app/status | jq -r '.data.status')
if [ "$status" = "crashed" ] || [ "$status" = "failed" ]; then
echo "[$(date)] Detectado $app en estado $status, reiniciando..."
curl -X POST http://localhost:8081/api/apps/$app/restart
sleep 2
fi
done
sleep 30
done
```
---
¡Estos ejemplos cubren los casos de uso más comunes! Para más información, consulta el `README.md`.