Buscar en la documentación

Módulo System

Panel de salud, eventos del sistema agrupados por huella y acciones del Super admin (reiniciar, limpiar caché, mantenimiento y jobs fallidos), sin acceso a Docker.

7 min de lecturaVer en GitHub

Le da al Super admin una pantalla para ver si algo falla y resolverlo sin entrar al servidor. Mide la salud de cada pieza, registra los eventos relevantes agrupados y ejecuta acciones dentro del alcance de la app: nunca toca Docker (ADR-0022, ADR-0023 y ADR-0059).

Endpoints

MétodoRutaPara quéPermiso
GET/system/healthPanel de saludsystem.health.view
GET/system/eventsEventos por cursor, con filtrossystem.logs.view
GET/system/events/typesCatálogo de tipos, para el filtrosystem.logs.view
PATCH/system/events/{id}Cambiar el estado (status)system.logs.view
GET/system/failed-jobsLos 50 jobs fallidos más recientes y el totalsystem.health.view
POST/system/failed-jobs/retryReintentar ids o all: trueAcción
DELETE/system/failed-jobs/{uuid}Descartar unoAcción
POST/system/actions/{action}restart-workers, restart-realtime o clear-cacheAcción
PUT/system/maintenanceActivar o desactivar (enabled)Acción
GET/docsContrato OpenAPI en JSON (API)system.health.view

Todas van bajo /api/v1 y exigen sesión. Los tres permisos son solo del Super admin, salvo que se concedan a una persona. “Acción” quiere decir:

  • Permiso system.actions.execute, revisado antes de validar.
  • Reautenticación: el código de la aplicación de autenticación en mfa_code. Sin él responde 422 REAUTHENTICATION_REQUIRED, y el front abre el diálogo del código.
  • 5 acciones por minuto por persona (throttle:system-actions).
  • Registro en admin_actions con un tipo system_*, visible en Auditoría.

Salud

GET /system/health mide en el momento (LaravelHealthProbe) y lo interpreta con HealthRules:

ComponenteQué mideAtenciónFalla
databaseUna consulta a MySQLMás de 200 msNo responde
redisUn PINGMás de 200 msNo responde
queuePendientes, antigüedad del más viejo y fallidosMás de 100 pendientes, el más viejo con más de 5 min, o algún fallido—
workerSu señal de vidaSin señal todavía (recién arrancado)Señal de más de 2 min
schedulerSu señal de vidaSin señal todavíaSeñal de más de 2 min
realtimeConexión al puerto de Reverb—No acepta conexiones
storageEspacio libre del discoMenos de 10 %Menos de 5 %

La respuesta trae el estado general (el peor de todos), cada componente con sus datos, el modo mantenimiento (desde cuándo y quién lo activó) y la app (entorno, versión de Laravel y de PHP).

Señales de vida. El scheduler corre system:check cada minuto: deja su señal en Redis y encola WorkerHeartbeat, que deja la del worker cuando este lo procesa. Así se comprueba que la cola de verdad se procesa, no solo que el proceso existe.

Eventos

SystemEvents (puerto compartido en Shared) es la única forma de registrar. Lo usan:

TipoCategoríaSeveridadQuién lo registra
unhandled_errorFallaCríticaEl manejo de errores, con cada 500 de la API
job_failedFallaAtenciónEl oyente de JobFailed (una vez, al agotar los intentos)
queue_backlogFallaAtenciónsystem:check
service_downInfraestructuraCríticasystem:check (Redis, Reverb o worker)
external_service_unavailableInfraestructuraAtenciónLa consulta de contraseñas filtradas
login_lockedSeguridadAtenciónEl login, al bloquear una cuenta
mfa_attempts_exceededSeguridadAtenciónEl segundo paso, al cortar un ingreso
reset_code_voidedSeguridadAtenciónLa recuperación, al anular un código
reauthentication_lockedSeguridadCríticaLa reautenticación, al quinto código incorrecto
access_denied_repeatedSeguridadAtenciónWatchDeniedAccess, con 10 respuestas 403 de la misma persona en 5 min
  • Agrupación. La huella es SHA-256 del tipo y su origen (la clase y la línea de un error, un servicio, una cuenta). DatabaseSystemEvents hace un INSERT … ON DUPLICATE KEY UPDATE: suma al contador, actualiza mensaje, contexto, última vez y código de soporte, y reabre lo resuelto.
  • Sin datos sensibles. De un error de base de datos queda solo su código SQLSTATE. La cuenta de un login bloqueado va enmascarada (EmailAddress::masked). Nunca se guardan tokens, contraseñas ni contenido de jobs.
  • Nunca rompe nada. Si registrar falla, queda en el log de Laravel y la petición sigue.
  • Estados: abierto → revisado → resuelto; un resuelto se puede reabrir. Al cambiarlo queda quién y cuándo. Pedir el mismo estado no cambia nada.
  • Lista: de la última vez más reciente a la más antigua, con filtros por estado, severidad y tipo (separados por comas) y el mismo cursor de la auditoría (TimeCursor).
  • Limpieza: system:prune-events borra cada día los resueltos hace más de 90 días (trazo.system.events_retention_days).

Lo que no se registra (es una respuesta esperada, no una falla): las reglas de negocio (DomainException), los errores de validación, los 404 y los tokens vencidos. bootstrap/app.php las excluye del reporte de Laravel.

Acciones

AcciónQué haceTipo en admin_actions
Reiniciar workersqueue:restart: cada worker termina el job en curso y Docker lo levantasystem_workers_restarted
Reiniciar el tiempo realDeja en la caché la misma señal que reverb:restart (laravel:reverb:restart): Reverb la revisa, se cierra y Docker lo levanta; las pantallas se reconectan solas. Se escribe directo porque Reverb solo registra sus comandos en la consola, no en una petición websystem_realtime_restarted
Limpiar la caché de datosSube la generación de la caché del dashboard. Bloqueos, límites y sesiones no se tocansystem_cache_cleared
Activar o desactivar mantenimientoMarca en Redis con quién y desde cuándosystem_maintenance_enabled / _disabled
Reintentar jobsqueue:retry de los elegidos o de todossystem_jobs_retried (count)
Descartar un jobqueue:forgetsystem_job_discarded (job, queue)

Modo mantenimiento

EnforceMaintenance corre en todas las rutas de la API:

  • Sin mantenimiento, no hace nada.
  • Con mantenimiento, quien no tiene system.actions.execute recibe 503 SERVICE_UNAVAILABLE. Quien lo tiene sigue trabajando y cada respuesta lleva X-Maintenance: on.
  • Siempre abiertos: auth.login, auth.mfa.challenge.* y auth.logout, para que el Super admin pueda entrar. /up no pasa por la API y responde normal.
  • Si Redis no responde, se considera apagado: no conviene cerrar la app por una falla que ya es mayor.

Errores

CódigoEstadoCuándo
REAUTHENTICATION_REQUIRED, MFA_CODE_INVALID, REAUTHENTICATION_LOCKED, REAUTHENTICATION_UNAVAILABLE422 / 423Ver Users
MAINTENANCE_ALREADY_ENABLED, MAINTENANCE_ALREADY_DISABLED409Pedir el estado que ya tiene
NO_FAILED_JOBS409Reintentar todos sin ninguno
FAILED_JOB_NOT_FOUND404Otra persona ya lo reintentó o lo descartó
SYSTEM_EVENT_NOT_FOUND404Se borró por antigüedad
INVALID_CURSOR422Cursor alterado a mano
SERVICE_UNAVAILABLE503Modo mantenimiento

Capas

CapaPiezas
DomainOperator (permisos), HealthRules, HealthStatus, EventStatus, SystemAction, Fingerprint, Exceptions/SystemExceptions
ApplicationReadHealth, ManageSystemEvents, ManageFailedJobs, RunSystemAction, SetMaintenance, CheckSystem, SystemGuard (permiso y reautenticación); datos (HealthReport, HealthMeasurements, ComponentHealth, MaintenanceState, EventRecord, EventPage, EventCriteria, EventType, FailedJob, FailedJobList); puertos HealthProbe, Heartbeats, MaintenanceSwitch, SystemControls, FailedJobs, SystemEventLog, IdentityCheck
InfrastructureDatabaseSystemEvents (implementa el puerto compartido), DatabaseEventLog, LaravelHealthProbe, CacheHeartbeats, WorkerHeartbeat, CacheMaintenanceSwitch, LaravelSystemControls, DatabaseFailedJobs, UsersIdentityCheck, FailureEvents, Console/CheckSystemCommand, Console/PruneSystemEventsCommand, SystemServiceProvider
HttpSystemController, ApiDocsController (contrato OpenAPI), SystemResource, RequestOperator, Requests/*, Middleware/EnforceMaintenance, Middleware/WatchDeniedAccess

Dónde está en el código