Skip to content

🏗️ Arquitectura y Riesgos — Log Management Dashboard

Proyecto: Panel de Administración y Gestión de Logs Multi-Aplicación Fecha: 2026-03-03 Skill: System Architect — C4 Model + STRIDE + NFRs Estado: FASE 2 Completada


1. Requisitos No Funcionales (NFRs)

1.1 Escalabilidad

NFRDescripciónDecisión
NFR-ESC-01Monocliente con un único usuario administrador. No se requiere escalado horizontal de frontend.El backend se ejecuta como proceso único PHP-FPM (Laravel). Sin necesidad de balanceador de carga.
NFR-ESC-02Volumen de logs: múltiples aplicaciones. Puede crecer a miles de registros por día.PostgreSQL con índices en (severity, created_at, application_id). Paginación obligatoria en todas las queries.
NFR-ESC-03El histórico es de escritura única y lectura frecuente.Tabla archived_logs separada de logs. Sin TTL. Candidata a particionado por año si crece.

1.2 Disponibilidad (SLA)

NFRDescripciónDecisión
NFR-DIS-01Sistema interno de administración. SLA objetivo: 99.5% (uso en horario laboral).No requiere configuración HA compleja. Un proceso de reinicio automático (systemd + PHP-FPM/Supervisor) es suficiente.
NFR-DIS-02La caída del panel no afecta a la ingesta de logs (n8n → Postgres es independiente).Desacoplamiento garantizado por diseño. Riesgo bajo.

1.3 Latencia

NFRDescripciónDecisión
NFR-LAT-01Dashboard debe cargar en < 1 s. Las cards solo cuentan registros agrupados (queries agregadas).Usar COUNT(*) GROUP BY severity con índice. Cachear resultado en memoria por 5-10 s antes de broadcast SSE.
NFR-LAT-02Listado tabular con filtros: respuesta < 500 ms.Índices compuestos en columnas de filtro. LIMIT/OFFSET o cursor-based pagination.
NFR-LAT-03SSE: evento push en < 2 s desde inserción en DB por n8n.Polling interno ligero (1-2 s) sobre tabla logs o trigger NOTIFY/LISTEN de PostgreSQL.

1.4 Observabilidad

NFRDescripciónDecisión
NFR-OBS-01El propio sistema gestiona logs de otras apps; sus propios errores internos se escriben en fichero local.Logs de aplicación en /var/log/log-dashboard/app.log con rotación. No se auto-reportan en Postgres para evitar recursión.
NFR-OBS-02Auditoría de acciones del administrador (archivado, comentarios).Campo archived_by, archived_at, updated_at en tabla archived_logs.

2. Decisiones Tecnológicas Justificadas

⚠️ Actualizado 2026-03-14: Stack cambiado de Laravel + React 19 (SPA) a Laravel 12 + Livewire 3 (SSR reactivo). Sin SPA independiente.

ComponenteTecnología ElegidaAlternativa DescartadaJustificación
BackendLaravel 12 (PHP)Framework maduro con ORM Eloquent. Controladores web clásicos. SSE via StreamedResponse.
Frontend dinámicoLivewire 3React 19 + Vite, Vue 3Componentes reactivos server-side sin necesidad de SPA ni build JS separado. Ideal para herramienta interna. Reduce complejidad operacional enormemente.
MicrointeraccionesAlpine.js (incluido con Livewire)React hooksMicrointeracciones en cliente (modales, toggles). Sin bundle JS complejo.
Estructura proyectoMonolito Laravel: Blade + LivewireSPA separada, Inertia.jsUn único proceso, un despliegue. Sin CORS. Sin doble repositorio. Las vistas son Blade con componentes Livewire embebidos.
API ContractLivewire Actions (wire:click, wire:model)REST puro, GraphQLCon Livewire no se necesita una REST API para el frontend. El SSE sigue siendo un endpoint HTTP independiente.
Tiempo realSSE via StreamedResponse de LaravelWebSockets (Reverb/Pusher)Comunicación unidireccional (servidor → cliente). Compatible con Livewire: el cliente JS actualiza el componente al recibir el evento.
Rich Text EditorTipTap 2 + Alpine.js bridgeQuill.js, CKEditorTipTap es headless y funciona con Livewire mediante un componente Alpine que sincroniza el contenido con wire:model. Soporte de imágenes y formato enriquecido.
Base de datosPostgreSQL (existente)Ya existe el flujo n8n → Postgres. Eloquent ORM soporta PostgreSQL de forma nativa.
Notificaciones DBpg_notify / LISTEN via worker LaravelPolling puroReduce latencia SSE. Proceso Artisan Command gestionado por Supervisor.
AutenticaciónSesión externa (API) + mock de sesiónLaravel Sanctum SPA, PassportLa pantalla de Login queda FUERA del alcance. El usuario llega autenticado desde un sistema externo. En desarrollo se simula con un mock de usuario en sesión. No hay endpoint de login en este panel.
i18nLaravel Lang (resources/lang/)Stubs en bladeTextos UI en archivos de idioma. Soporte base para es y estructura preparada para va.

3. Modelado de Amenazas — STRIDE

Componentes críticos analizados

  1. Backend Laravel (controladores + Livewire Actions + SSE)
  2. Base de Datos PostgreSQL (logs activos + histórico + error_codes)
  3. Frontend Blade/Livewire (panel admin, SSR)
  4. Editor Rich Text (comentarios con imágenes — TipTap 2 + Alpine.js)

3.1 Componente: API Backend

STRIDEAmenazaRiesgoMitigación
S SpoofingUn agente externo suplanta al administrador y accede al panel.ALTOAutenticación con sesión firmada (JWT/cookie httpOnly). Aunque es un único usuario, se requiere login.
T TamperingModificación de un log activo antes de archivarlo.MEDIOLos logs activos son solo lectura desde la API (solo n8n puede insertar). Endpoints de escritura solo para archived_logs.
R RepudiationNo hay trazabilidad de quién archivó o comentó.BAJOCampos archived_by y created_at en todas las acciones de escritura.
I Information DisclosureEndpoint SSE expuesto sin autenticación filtra logs en tiempo real.ALTOEl stream SSE debe requerir sesión válida antes de abrirse.
D Denial of ServicePeticiones masivas al endpoint de listado con filtros pesados.BAJOMonocliente + red interna. Rate limiting básico como precaución.
E Elevation of PrivilegeNo aplica (único rol administrador).N/A

3.2 Componente: Base de Datos PostgreSQL

STRIDEAmenazaRiesgoMitigación
S SpoofingOtro servicio usa las credenciales de DB del panel.MEDIOUsuario de DB dedicado con permisos mínimos (SELECT en logs, CRUD en archived_logs).
T TamperingBorrado accidental o malicioso del histórico.ALTOEl usuario de DB del panel no tiene permiso DELETE sobre archived_logs. Solo INSERT/UPDATE.
R RepudiationSin log de cambios en DB.BAJOTimestamps en todas las tablas (created_at, updated_at).
I Information DisclosureCredenciales de DB en código fuente.ALTOCredenciales en variables de entorno (.env, nunca en repositorio).
D Denial of ServiceQuery sin LIMIT satura la DB.MEDIOORM/query builder con LIMIT máximo hardcoded (ej. 500 filas por petición).
E Elevation of PrivilegeEscalado de permisos vía SQL injection.MEDIOUso exclusivo de queries parametrizadas / ORM. Nunca concatenar strings SQL.

3.3 Componente: Editor Rich Text (Comentarios)

STRIDEAmenazaRiesgoMitigación
S SpoofingNo aplica (editor local del admin).N/A
T TamperingXSS embebido en HTML del comentario guardado.ALTOSanitizar HTML en el backend antes de persistir (ej. DOMPurify server-side). Nunca renderizar HTML crudo sin sanitizar.
R RepudiationN/A
I Information DisclosureImágenes subidas expuestas sin control.MEDIOSi las imágenes se suben a storage, requerir token de acceso firmado. Alternativa: almacenar como base64 en la propia entrada (más simple pero limita tamaño).
D Denial of ServiceUpload de imágenes enormes agota almacenamiento.MEDIOLímite de tamaño por upload (ej. 2 MB) y por comentario (ej. 10 MB total).
E Elevation of PrivilegeArchivo malicioso disfrazado de imagen.MEDIOValidar MIME type real (magic bytes) en el backend, no solo la extensión.

4. Resumen de Riesgos por Severidad

SeveridadAmenazas
🔴 ALTOSSE sin autenticación · Borrado de histórico · Credenciales en repo · XSS en comentarios
🟡 MEDIOSuplantación de servicio DB · SQL Injection · Imágenes sin control · DoS por queries
🟢 BAJORepudio de acciones · DoS en red interna

5. Restricciones Técnicas Identificadas

  1. La estructura de la tabla logs en Postgres ya existe — el schema se debe descubrir/documentar antes del desarrollo (campo severity enum, FK application_id, campo message, timestamps). La tabla applications es nueva y debe crearse con migración.
  2. El flujo n8n no se modifica — la ingesta de logs es ajena al alcance de este proyecto.
  3. Sin pantalla de login en este panel — la autenticación viene de un sistema externo. Para desarrollo, mock de usuario en sesión Laravel.
  4. Sin despliegue cloud — la app corre en infraestructura local.
  5. Sin SPA / sin build Vite separado — el frontend es Blade + Livewire servido directamente por Laravel.
  6. Tabla error_codes es nueva — debe crearse con migración. Clave compuesta (code + application_id) (FK a tabla applications). CRUD completo en este panel.
  7. Comentarios son exclusivos del Histórico y Error Codes — no aparecen en la vista de logs activos.
  8. Filtros → GET, Orden → POST — los filtros y página se pasan como query params en la URL (persistibles/compartibles). La ordenación de columnas se envía como formulario POST y no persiste en URL.

Log Management Dashboard — Documentación del Proyecto