Saltar a contenido

Panel de cron, logs y alertas (Dabra)

Implementa la sección 4 de ../RECOMENDACIONES_GLOBAL.md: estado de jobs, historial y tendencia, visor de logs, alertas con niveles configurables, reglas por patrón/código/job con escalado, cron (lectura y validación), destinatarios, ejecutar ahora con rango y bitácora de cambios.

Un solo contenedor: FastAPI + SQLite (WAL) + plantillas Jinja, sin JS externo ni CDN. El mismo docker-compose.yml sirve en Windows (Docker Desktop) y en Linux.

Probarlo en Windows (local)

cd <ruta de Windows>
copy .env.example .env      # completar SECRET_KEY, ADMIN_PASSWORD y API_TOKEN (ver comentarios del archivo)
docker compose up -d --build

Abrir http://localhost:8080 e ingresar con ADMIN_USER / ADMIN_PASSWORD. Los jobs demo_ok (cada 2 min) y demo_error (cada 5 min) ya vienen activos para ver estado, logs y alertas; elimínelos o desactívelos cuando no hagan falta (job → Configuración, o activo desmarcado).

Comandos útiles: docker compose logs -f panel · docker compose down (conserva datos) · docker compose down -v (borra base y logs).

Pruebas automáticas (dentro de la imagen, 16 tests):

docker run --rm -v "${PWD}\tests:/app/tests:ro" -w /app dabra-panel:latest python -m pytest -q -p no:cacheprovider tests

Si docker build falla con CERTIFICATE_VERIFY_FAILED: un antivirus o proxy inspecciona HTTPS (en esta PC es AVG Web/Mail Shield). Ponga la CA raíz en formato PEM como certs/*.crt y reconstruya: el Dockerfile la instala sin desactivar verificaciones. En el servidor Linux no hace falta (certs/ queda vacía). No versione esos .crt.

Desplegar en el servidor Linux

  1. Copiar la carpeta (sin .env ni certs/*.crt) o construir/subir la imagen: docker build -t dabra-panel . → docker save / registry.
  2. Crear .env en el servidor con valores nuevos (chmod 600 .env). PANEL_PORT=8080, COOKIE_SECURE=true si va tras HTTPS.
  3. Si cron del host va a lanzar los jobs con el wrapper, comparta los logs: LOGS_HOST_DIR=/var/log/dabra en .env (carpeta existente; el contenedor corre con UID 10001, chown 10001 /var/log/dabra o chmod g+w según su política).
  4. docker compose up -d --build. El contenedor tiene restart: unless-stopped y healthcheck.
  5. Poner nginx delante (TLS y, si desean, SSO por proxy). El panel ya pide login propio con roles; no lo exponga sin HTTPS.

Antes de ir a producción: SECRET_KEY y API_TOKEN únicos, contraseña del admin cambiada desde Administración, y usuarios con el rol mínimo necesario.

Dos formas de ejecutar los jobs

Modo Cuándo Cómo
Cron externo + wrapper (recomendado para los jobs reales) Los scripts tienen dependencias propias (pandas, ODBC, SFTP…) y viven en el host o en su propio contenedor clients/run_job.sh <job> desde /etc/cron.d/dabra: flock, timeout, log por corrida y reporte a POST /api/runs. El panel detecta corridas faltantes por la expresión cron cargada
Interno Jobs livianos o de prueba Marcar Lo ejecuta el panel y definir comando; el planificador del contenedor lo lanza, sin shell, con timeout y bloqueo por job

Las dependencias de los scripts de negocio no están en esta imagen a propósito. Para ejecutarlos dentro del panel, derive una imagen (FROM dabra-panel, pip install ..., msodbcsql18 si usa ODBC) y monte el código en ./jobs (→ /opt/dabra/jobs).

Wrapper en Linux

/etc/dabra/panel.env        # chmod 600
DABRA_PANEL_URL=http://127.0.0.1:8080
DABRA_PANEL_TOKEN=<API_TOKEN del panel>
DABRA_LOG_DIR=/var/log/dabra
/etc/dabra/jobs.d/nike.cmd  # una línea: /opt/dabra/venv/bin/python /opt/dabra/nike/main.py
CRON_TZ=America/Argentina/Buenos_Aires
0 7 * * *  dabra  /opt/dabra/bin/run_job.sh nike

Instalar con install -m 755 clients/run_job.sh /opt/dabra/bin/. El nombre del job debe coincidir con el cargado en el panel (Cron): ahí se activa el control de corrida faltante.

Alertas desde los scripts

from dabra_alerts import alert          # clients/dabra_alerts.py (solo librería estándar)
alert("SFTP_UPLOAD_FAILED", job="nike", detail="timeout tras 3 intentos", context={"fecha": "2026-10-06"})

El código es estable; el nivel lo decide Reglas (job+código → código → catálogo → intermedio). Un script sin modificar también genera alertas: por exit code, timeout, duración > 2× la mediana y reglas por patrón sobre su log.

Qué hace cada alerta

Nivel Notificación
crítico Inmediata (mail/Telegram a los destinatarios con nivel ≤ crítico) y se repite cada CRITICAL_REPEAT_MIN hasta reconocer
urgente Inmediata, una vez
intermedio Resumen diario a las DIGEST_HOUR
bajo Solo en el panel

Sin SMTP_*/TELEGRAM_BOT_TOKEN las alertas igualmente se registran y se ven en el panel (el resultado de cada intento queda en la alerta). Silenciar suprime la notificación de nuevas alertas del mismo job+código durante el plazo; escalado: una regla con «N días» sube un nivel si el código ocurrió N días seguidos.

Códigos generados por el panel: EXIT_NONZERO, JOB_TIMEOUT, RUN_MISSING, RUN_INTERRUPTED, LOCK_HELD, DURATION_HIGH, AUDIT_MISSING, AUDIT_ERROR. El resto del catálogo (seed/alert_catalog.json) está pre-cargado desde los RECOMENDACIONES.md, y las reglas de ejemplo en seed/alert_rules.json. La carga inicial solo ocurre con tablas vacías; después se edita todo desde el panel.

Auditoría existente (ods.tbl_control_procesos)

Con AUDIT_DB_URL=mssql+pymssql://usuario:clave@servidor/base (usuario solo lectura), cada job con Prefijo de ID cargado:

  • muestra las últimas filas en su detalle;
  • si el job terminó OK y 10 min después no hay fila → AUDIT_MISSING; si la fila tiene Status=Error → AUDIT_ERROR.

Hoy solo nike trae el prefijo precargado; los demás IDs son inconsistentes (ver recomendaciones, ítem 25) y deben completarse a mano.

Seguridad

  • Login con PBKDF2, sesión firmada (12 h, SameSite=Lax), CSRF en todos los formularios, roles viewer/operator/admin. API de wrappers con X-API-Token (comparación en tiempo constante).
  • Los logs solo se leen bajo LOG_DIR (lista blanca, rutas resueltas); los nombres de job y las fechas se validan; los comandos se ejecutan sin shell.
  • Editar el comando de un job equivale a ejecutar código en el contenedor: solo admin y queda en la bitácora. «Ejecutar ahora» exige operator.
  • Un solo worker de uvicorn (el planificador vive en el proceso). No escale a varios workers/réplicas sin separar el planificador.
  • El contenedor corre sin root. Los secretos van solo en .env/variables de entorno; el token de Telegram nunca se escribe en los logs.

Límites conocidos de esta versión (MVP)

  • Zona horaria única para todo el panel (TZ). Cron solo en lectura: el panel no edita /etc/cron.d.
  • No hay SSO propio; use el login local o delegue en el proxy.
  • Edición de reglas sin vista previa de regex contra logs reales (la sintaxis sí se valida al guardar).
  • Si el planificador interno reinicia, las corridas en curso se cierran como RUN_INTERRUPTED; no hay catch-up de ejecuciones perdidas (para eso está RUN_MISSING).
  • Pendiente de validar con datos reales: AUDIT_DB_URL (sin servidor de pruebas aquí) y el envío SMTP/Telegram (sin credenciales).

Estructura

app/            main, routers (ui, api), scheduler, alerts, logs, notify, audit_source, models, templates/
clients/        run_job.sh (wrapper de cron) · dabra_alerts.py
seed/           jobs, catálogo y reglas iniciales (JSON)
jobs_demo/      dos jobs de prueba
tests/          pytest (16 casos)
certs/          CAs extra opcionales para el build

Fuente: repositorio panel-cron-alertas, versión 8901ca2. Página generada con tools/publicar_docs.py.