Files
analiticaNucleo/docs/sistema-notificaciones.md
josedario87 9979c148a9
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 9s
Docs: Agregar documentación completa del sistema de notificaciones
- Explicación de arquitectura y componentes
- Guía de uso para desarrolladores
- Ejemplos de código y casos de uso
- Detalles de separación por usuario
- Configuración de limpieza automática
- Sincronización entre pestañas
- FAQ y mejoras futuras
2025-10-30 18:15:15 -06:00

17 KiB

Sistema de Notificaciones

Sistema de notificaciones con historial persistente por usuario, implementado con localStorage y separación automática basada en el UID de Authentik.

Tabla de Contenidos


Características Principales

Historial separado por usuario: Cada usuario tiene su propio historial usando su UID de Authentik como clave

Persistencia local: Las notificaciones se guardan en localStorage del navegador

Guardado automático de toasts: Todos los toasts se guardan automáticamente en el historial

Auto-limpieza: Notificaciones mayores a 30 días se eliminan automáticamente

Sincronización entre pestañas: Los cambios se reflejan en tiempo real en todas las pestañas

Filtrado y búsqueda: Filtrar por tipo (info, warning, success, error) y buscar por texto

Múltiples orígenes: Soporta notificaciones desde toasts, backend y manuales


Arquitectura del Sistema

┌─────────────────────────────────────────────────────────────┐
│                     Aplicación Nuxt                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌──────────────────────────────────────────────────┐      │
│  │              useNotifications()                   │      │
│  │  - Gestión de notificaciones                     │      │
│  │  - Filtrado y búsqueda                          │      │
│  │  - Limpieza automática                          │      │
│  └──────────────────────────────────────────────────┘      │
│                        ▲                                    │
│                        │                                    │
│  ┌──────────────────────────────────────────────────┐      │
│  │               useToast()                         │      │
│  │  - Wrapper que intercepta toasts                │      │
│  │  - Guarda automáticamente en historial          │      │
│  └──────────────────────────────────────────────────┘      │
│                        ▲                                    │
│                        │                                    │
│  ┌──────────────────────────────────────────────────┐      │
│  │          API Endpoints (Backend)                 │      │
│  │  - /api/notifications/send                      │      │
│  │  - /api/notifications/list                      │      │
│  └──────────────────────────────────────────────────┘      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
                        │
                        ▼
            ┌────────────────────────┐
            │   localStorage         │
            │  notifications-{uid}   │
            └────────────────────────┘

Componentes Principales

1. Composable useNotifications()

Ubicación: app/composables/useNotifications.ts

El composable principal que gestiona todo el sistema de notificaciones.

Funciones disponibles:

const {
  notifications,        // Ref<Notification[]> - Array reactivo de todas las notificaciones
  unreadCount,         // ComputedRef<number> - Contador de notificaciones no leídas
  addNotification,     // Agregar una notificación al historial
  markAsRead,          // Marcar una notificación como leída
  markAllAsRead,       // Marcar todas como leídas
  removeNotification,  // Eliminar una notificación específica
  clearAll,            // Eliminar todas las notificaciones
  cleanupOldNotifications, // Eliminar notificaciones > 30 días
  getFilteredNotifications, // Obtener notificaciones filtradas
  initialize           // Inicializar el sistema (se llama automáticamente)
} = useNotifications()

2. Wrapper useToast()

Ubicación: app/composables/useToast.ts

Wrapper transparente que intercepta todos los toasts de Nuxt UI y los guarda automáticamente en el historial.

Uso (sin cambios en código existente):

const toast = useToast()

// Funciona exactamente igual que antes, pero ahora se guarda en el historial
toast.add({
  title: 'Operación exitosa',
  description: 'Los datos se guardaron correctamente',
  color: 'success'
})

3. API del Backend

Ubicación: server/api/notifications/

Endpoints para crear y obtener notificaciones desde el servidor.


Uso Básico

Agregar una notificación manualmente

const { addNotification } = useNotifications()

addNotification({
  type: 'success',
  title: 'Proceso completado',
  message: 'El reporte se generó correctamente',
  origin: 'manual',
  metadata: {
    reportId: 123,
    timestamp: new Date().toISOString()
  }
})

Marcar notificación como leída

const { markAsRead } = useNotifications()

markAsRead('notification-id-123')

Obtener notificaciones filtradas

const { getFilteredNotifications } = useNotifications()

// Solo notificaciones de error no leídas
const errorNotifications = getFilteredNotifications({
  type: ['error'],
  read: false
})

// Buscar por texto
const searchResults = getFilteredNotifications({
  search: 'reporte'
})

// Limitar resultados
const latest = getFilteredNotifications({
  limit: 10
})

API del Backend

Enviar notificación desde el servidor

Endpoint: POST /api/notifications/send

// Desde cualquier endpoint del servidor
const response = await $fetch('/api/notifications/send', {
  method: 'POST',
  body: {
    targetUserId: 'uid-del-usuario-destino',
    type: 'info',
    title: 'Nueva actualización',
    message: 'Hay una nueva versión disponible',
    metadata: {
      version: '2.0.0',
      changelog: 'https://...'
    }
  }
})

Parámetros:

  • targetUserId (string, requerido): UID del usuario destino
  • type (string, requerido): Tipo de notificación ('info', 'warning', 'success', 'error')
  • title (string, requerido): Título de la notificación
  • message (string, opcional): Mensaje descriptivo
  • metadata (object, opcional): Datos adicionales

Obtener notificaciones pendientes

Endpoint: GET /api/notifications/list

// Desde el cliente (automático)
const { data } = await $fetch('/api/notifications/list')

console.log(data.notifications) // Array de notificaciones pendientes
console.log(data.count)         // Número de notificaciones

Nota: Este endpoint usa una cola en memoria. En producción, considera usar Redis o una base de datos para persistencia.


Estructura de Datos

Interfaz Notification

interface Notification {
  id: string                    // ID único generado automáticamente
  type: 'info' | 'warning' | 'success' | 'error'
  title: string                 // Título de la notificación
  message: string               // Mensaje descriptivo
  timestamp: number             // Timestamp en milisegundos
  read: boolean                 // Estado de lectura
  origin: 'toast' | 'backend' | 'manual' // Origen de la notificación
  metadata?: Record<string, any> // Datos adicionales opcionales
}

Ejemplo de notificación guardada

{
  "id": "1730328465123-a3f8d9e2b",
  "type": "success",
  "title": "Tema guardado",
  "message": "Los cambios se aplicaron correctamente",
  "timestamp": 1730328465123,
  "read": false,
  "origin": "toast",
  "metadata": {
    "icon": "i-lucide-check",
    "color": "green",
    "hasActions": false
  }
}

Separación por Usuario

El sistema utiliza el UID de Authentik como clave única para separar las notificaciones de cada usuario.

Cómo funciona

  1. Autenticación con Authentik:

    • El usuario se autentica mediante Authentik Proxy Outpost
    • El header x-authentik-uid contiene el UID único del usuario
  2. Clave de localStorage:

    const storageKey = `notifications-${user.uid}`
    // Ejemplo: "notifications-a3f8d9e2b4c1a5d6"
    
  3. Cambio de usuario:

    • Al detectar un cambio en el UID, el sistema automáticamente:
      • Guarda las notificaciones del usuario anterior
      • Carga las notificaciones del nuevo usuario
      • Limpia el estado en memoria

Ejemplo práctico

Usuario A (UID: abc123):
  localStorage["notifications-abc123"] = [notif1, notif2, notif3]

Usuario B (UID: xyz789):
  localStorage["notifications-xyz789"] = [notif4, notif5]

// Cada usuario solo ve sus propias notificaciones
// Incluso si usan el mismo navegador en la misma máquina

Limpieza Automática

El sistema elimina automáticamente las notificaciones antiguas para evitar acumulación excesiva.

Configuración

// En useNotifications.ts
const RETENTION_DAYS = 30 // Días de retención

Cuándo se ejecuta

  1. Al iniciar la aplicación:

    • Se ejecuta cleanupOldNotifications() en app.vue
    • Elimina notificaciones mayores a 30 días
  2. Al cargar notificaciones:

    • Las notificaciones antiguas se filtran automáticamente
    • Solo se cargan las recientes

Modificar el período de retención

Si necesitas cambiar el período de retención, modifica la constante:

// app/composables/useNotifications.ts
const RETENTION_DAYS = 60 // Cambiar a 60 días

Sincronización entre Pestañas

El sistema mantiene sincronizadas todas las pestañas del navegador usando el evento storage.

Cómo funciona

// Cuando cambia el localStorage en una pestaña...
window.addEventListener('storage', (event) => {
  if (event.key === storageKey && event.newValue) {
    // ...todas las demás pestañas se actualizan automáticamente
    notifications.value = JSON.parse(event.newValue)
  }
})

Casos de uso

  1. Usuario agrega notificación en Pestaña A:

    • Se guarda en localStorage
    • Pestaña B y C reciben el evento storage
    • Todas las pestañas muestran la nueva notificación
  2. Usuario elimina notificación en Pestaña B:

    • Se actualiza localStorage
    • Pestaña A y C se sincronizan automáticamente

Página de Notificaciones

Ubicación: app/pages/notifications.vue

Características

  • Lista completa de notificaciones del usuario actual
  • Búsqueda por texto (título y mensaje)
  • Filtros por tipo (info, warning, success, error)
  • Marcar como leídas (individual o todas)
  • Eliminar (individual o todas)
  • Indicadores de tiempo relativo ("hace 2 horas", "hace 3 días")
  • Badges de origen (toast, backend, manual)
  • Estados vacíos informativos

Badge en Sidebar

Ubicación: app/components/app/AppSidebar.vue

  • Muestra el número de notificaciones no leídas
  • Se oculta automáticamente cuando no hay notificaciones
  • Indicador visual (punto rojo) cuando hay notificaciones pendientes

Inicialización del Sistema

Ubicación: app/app.vue

// Se inicializa automáticamente al montar la aplicación
const { initialize, cleanupOldNotifications } = useNotifications()

onMounted(() => {
  initialize()                  // Inicializar el sistema
  cleanupOldNotifications()     // Limpiar notificaciones antiguas
})

Flujo de inicialización

  1. App se montaonMounted() se ejecuta
  2. Usuario autenticado → Se obtiene el UID de Authentik
  3. Carga desde localStorage → Se cargan notificaciones del usuario
  4. Limpieza automática → Se eliminan notificaciones > 30 días
  5. Configurar sincronización → Se activa el listener de storage
  6. Sistema listo → El usuario puede ver su historial

Casos de Uso

1. Notificación de operación exitosa

// Desde cualquier componente
const toast = useToast()

async function saveData() {
  try {
    await $fetch('/api/save', { method: 'POST', body: data })

    // Se guarda automáticamente en el historial
    toast.add({
      title: 'Datos guardados',
      description: 'Los cambios se guardaron correctamente',
      color: 'success'
    })
  } catch (error) {
    toast.add({
      title: 'Error al guardar',
      description: error.message,
      color: 'error'
    })
  }
}

2. Notificación desde el backend

// server/api/generate-report.post.ts
export default defineEventHandler(async (event) => {
  const headers = getHeaders(event)
  const userId = headers['x-authentik-uid']

  // Generar el reporte...
  const report = await generateReport()

  // Notificar al usuario
  await $fetch('/api/notifications/send', {
    method: 'POST',
    body: {
      targetUserId: userId,
      type: 'success',
      title: 'Reporte generado',
      message: `El reporte ${report.name} está listo para descargar`,
      metadata: {
        reportId: report.id,
        downloadUrl: report.url
      }
    }
  })

  return { success: true, report }
})

3. Consultar historial programáticamente

const { getFilteredNotifications } = useNotifications()

// Obtener últimas 5 notificaciones de error
const recentErrors = getFilteredNotifications({
  type: ['error'],
  limit: 5
})

// Mostrar en consola
recentErrors.forEach(notif => {
  console.error(`[${notif.timestamp}] ${notif.title}: ${notif.message}`)
})

Mejoras Futuras

Posibles extensiones del sistema:

  1. Persistencia en base de datos:

    • Migrar de localStorage a Supabase
    • Acceso desde cualquier dispositivo
    • Historial ilimitado
  2. Notificaciones push:

    • Integración con Web Push API
    • Notificaciones incluso cuando la app está cerrada
  3. Categorías personalizadas:

    • Permitir al usuario crear categorías
    • Filtros más granulares
  4. Acciones en notificaciones:

    • Botones de acción (Descargar, Ver más, Abrir)
    • Ejecutar código al hacer clic
  5. Notificaciones programadas:

    • Enviar notificaciones en fecha/hora específica
    • Recordatorios recurrentes
  6. Sistema de preferencias:

    • Configurar qué notificaciones recibir
    • Silenciar notificaciones por tipo
    • Modo "No molestar"

Preguntas Frecuentes

¿Las notificaciones se pierden al cerrar el navegador?

No, las notificaciones se guardan en localStorage y persisten entre sesiones.

¿Qué pasa si dos usuarios usan el mismo navegador?

Cada usuario tiene su propio historial separado por su UID de Authentik. Al cambiar de usuario (logout/login), el sistema carga automáticamente las notificaciones del nuevo usuario.

¿Cuántas notificaciones se pueden guardar?

El límite lo impone localStorage (aproximadamente 5-10 MB dependiendo del navegador). Con la limpieza automática de 30 días, es poco probable alcanzar este límite.

¿Se pueden recuperar notificaciones eliminadas?

No, una vez eliminadas del localStorage, no se pueden recuperar. Considera implementar persistencia en base de datos si necesitas esta funcionalidad.

¿Las notificaciones son privadas?

Sí, están almacenadas localmente en el navegador del usuario. Solo son accesibles por el usuario que las recibió y en el dispositivo donde se guardaron.


Soporte

Para reportar problemas o sugerencias sobre el sistema de notificaciones:

  1. Revisar los logs en la consola del navegador
  2. Verificar que el UID de Authentik esté presente
  3. Comprobar el localStorage en DevTools: localStorage.getItem('notifications-{uid}')
  4. Crear un issue en el repositorio con los detalles

Última actualización: 2025-10-30 Versión: 1.0.0