Resumen
Módulo JavaScript (static/js/modules/agent_lna_notice.js) que contextualiza el prompt del navegador sobre Local Network Access (LNA) antes de que aparezca. Detecta vía Permissions API si el estado es prompt y, si es así, renderiza un aviso propio explicando la conexión con el Local Agent.
Archivo: static/js/modules/agent_lna_notice.js
Tamaño: ~86 LOC
Dependencias: Permissions API (navigator.permissions.query)
Integración: templates/base.html (línea ~974, <script defer>)
Arquitectura Interna
Flujo de Ejecución
1. Módulo IIFE (Immediately Invoked Function Expression)
↓
2. Verificar ruta (pageOnAgentPage)
→ Si no está en AGENT_PATHS → return early
↓
3. Verificar sessionStorage['lna_notice_dismissed']
→ Si existe → return early (ya mostrado esta sesión)
↓
4. maybeShowNotice() async
├─ Verificar navigator.permissions existe
├─ Query permissions.query({name: 'local-network-access'})
├─ Si estado ≠ 'prompt' → return early
└─ Llamar showNotice()
↓
5. showNotice()
├─ Crear <div class="lna-notice">
├─ Rellenar HTML (título + párrafo + botón)
├─ Adjuntar al <body>
└─ Registrar evento click en botón
↓
6. dismiss() (al pulsar "Entendido")
├─ Marcar sessionStorage['lna_notice_dismissed'] = '1'
├─ Animar fade-out (0.4s)
└─ Eliminar nodo del DOM
Constantes y Variables Clave
| Variable | Tipo | Valor | Propósito |
|---|---|---|---|
AGENT_PATHS | Array | ['/', '/editor', '/terminal', '/monitoring', '/wireless', '/maps', '/blueprints'] | Rutas que contactan con el Agent (espejadas de dashboard_widgets.js + core/middleware/csp.py) |
SESSION_KEY | String | 'lna_notice_dismissed' | Clave de sessionStorage para marcar como mostrado |
el | HTMLElement | Dinámico | Referencia al div .lna-notice |
Métodos Públicos
maybeShowNotice() (async)
- Punto de entrada.
- Valida que
navigator.permissionsexista. - Consulta
navigator.permissions.query({name: 'local-network-access'}). - Si el estado es
'prompt', llama ashowNotice(). - Try-catch: si el navegador no soporta el permiso, sale silenciosamente (no-op).
showNotice()
- Crea el elemento DOM con HTML embebido:
- Clase:
lna-notice(fijo, abajo-derecha) - Contenido: título (“Conexión con tu Agente Local”) + párrafo explicativo + botón “Entendido”
- Atributo:
role="status"(accesibilidad) - Event listener: botón vinculado a
dismiss()
- Clase:
dismiss()
- Marca
sessionStorage['lna_notice_dismissed'] = '1'. - Aplica CSS
animation: fadeOut 0.4s ease-out forwards. - Tras 400ms, elimina el nodo del DOM.
Estilos CSS
Definidos en static/css/components.css (líneas ~903–949):
| Clase | Propósito |
|---|---|
.lna-notice | Contenedor: fijo (bottom 20px, right 20px), z-index 99998, max-width 380px, tarjeta con borde izquierdo acentuado, animación slide-in 0.3s |
.lna-notice-title | Título: fuente heading, 1rem, margen inferior 6px |
.lna-notice-body p | Párrafo: font-size 0.875rem, line-height 1.45, color secundaria |
.lna-notice .btn | Botón: alineado a flex-end |
@media (max-width: 480px) | Responsive: left/right 12px, ancho 100% |
Animaciones reutilizadas:
slideIn(0.3s ease-out, definida en otro lugar)fadeOut(0.4s ease-out, definida en otro lugar)
Ciclo de Vida
- Carga de página: Script se ejecuta con
defer. - DOMContentLoaded o después: Invoca
maybeShowNotice(). - Detección: Consulta Permissions API.
- Si navegador no soporta → nada.
- Si estado =
'granted'/'denied'→ nada (ya decidió). - Si estado =
'prompt'→ muestra aviso.
- Usuario cierra aviso: Marca
sessionStorage, anima fade-out, borra DOM. - Resto de sesión: sessionStorage persiste,
maybeShowNotice()no muestra de nuevo (early return). - Cierra navegador: sessionStorage se olvida, próxima sesión repite desde paso 2.
Integración con Otros Módulos
| Módulo | Relación |
|---|---|
dashboard_widgets.js | Define _AGENT_PATHS (aquí replicada en AGENT_PATHS). Futura sincronización: considerar importar la constante |
core/middleware/csp.py | Define _AGENT_PREFIXES, _AGENT_EXACT para sondeo del Agent. El aviso respeta las mismas rutas |
static/css/components.css | Estilos .lna-notice* |
templates/base.html | Carga el script con <script defer> |
Limitaciones y Notas
-
Sin sincronización dinámica de rutas:
AGENT_PATHSestá hardcodeada. Si se añaden nuevas rutas al Agent, hay que actualizar manualmente aquí y endashboard_widgets.js.- TODO: Considerar endpoint
/api/agent/pathso inyectar en template.
- TODO: Considerar endpoint
-
sessionStorage vs localStorage: Se usa
sessionStorageporque el permiso concedido persiste en el navegador; una sesión nueva vería el prompt de nuevo si no existiera. El aviso propio (sessionStorage) es para no molestar múltiples veces en la misma sesión. -
Navegadores sin LNA: El módulo detecta ausencia de Permissions API y sale. No hay polyfill.
-
Timing de animaciones: Las timelines están hardcodeadas (0.3s slide-in, 0.4s fade-out). Deben coincidir con CSS.
Fuentes y Referencias
- Implementación: Commit f494fca8c040a41b05d07e9b367362f4c614bac1
- Microsoft Permissions API: https://learn.microsoft.com/deployedge/ms-edge-local-network-access
- MDN Permissions API: https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API
- Local Agent docs:
Documentation/backend/LOCAL_AGENT.md(sección “Seguridad”)
Véase también
- [[feature—core—aviso-lna]]
- [[crearack—terminal—local-agent]]
- [[concept—saas—security]]
- [[entity—core—service—dashboard-widgets]]
- [[entity—core—middleware—csp]]