Files
josedario87 b6dc08e599
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 48s
Feat: Implementar sistema de notificaciones con historial por usuario
- Crear composable useNotifications con gestión por localStorage
  - Almacenamiento separado por usuario usando UID de Authentik
  - Auto-limpieza de notificaciones mayores a 30 días
  - Sincronización automática entre pestañas
  - Filtrado por tipo, búsqueda y gestión completa

- Crear wrapper useToast para guardar toasts automáticamente
  - Intercepta todos los toasts de la aplicación
  - Guarda historial sin afectar funcionalidad existente

- Implementar endpoints de API para notificaciones del backend
  - POST /api/notifications/send para enviar notificaciones
  - GET /api/notifications/list para obtener pendientes

- Actualizar página de notificaciones con funcionalidad real
  - Búsqueda y filtros por tipo (info, warning, success, error)
  - Eliminar individual o todas las notificaciones
  - Marcar como leídas individual o todas
  - Badges de origen (toast, backend, manual)
  - Estados vacíos con mensajes informativos

- Actualizar badge del sidebar con contador dinámico
  - Muestra número real de notificaciones no leídas
  - Se oculta cuando no hay notificaciones

- Inicializar sistema en app.vue
  - Auto-inicialización al montar la app
  - Limpieza automática de notificaciones antiguas
2025-10-30 18:03:37 -06:00

96 lines
2.8 KiB
TypeScript

/**
* Endpoint para enviar notificaciones a usuarios específicos desde el backend
*
* Esta API permite crear notificaciones que se entregarán directamente
* al cliente para ser guardadas en su localStorage.
*
* Casos de uso:
* - Notificaciones del sistema
* - Alertas generadas por procesos del backend
* - Notificaciones administrativas
*/
export default defineEventHandler(async (event) => {
try {
// Leer el body de la petición
const body = await readBody(event)
// Validar campos requeridos
if (!body.targetUserId || !body.title) {
throw createError({
statusCode: 400,
statusMessage: 'Faltan campos requeridos: targetUserId y title'
})
}
// Validar tipo de notificación
const validTypes = ['info', 'warning', 'success', 'error']
const type = body.type || 'info'
if (!validTypes.includes(type)) {
throw createError({
statusCode: 400,
statusMessage: `Tipo inválido. Debe ser uno de: ${validTypes.join(', ')}`
})
}
// Leer el usuario actual de los headers de Authentik
const headers = getHeaders(event)
const currentUserId = headers['x-authentik-uid']
const currentUsername = headers['x-authentik-username']
// Verificar que el usuario esté autenticado
if (!currentUserId) {
throw createError({
statusCode: 401,
statusMessage: 'Usuario no autenticado'
})
}
// Opcional: verificar permisos (por ejemplo, solo admins pueden enviar notificaciones)
// const groups = headers['x-authentik-groups']?.split('|') || []
// if (!groups.includes('admins')) {
// throw createError({
// statusCode: 403,
// statusMessage: 'No tienes permisos para enviar notificaciones'
// })
// }
// Construir la notificación
const notification = {
targetUserId: body.targetUserId,
type: type as 'info' | 'warning' | 'success' | 'error',
title: body.title,
message: body.message || '',
origin: 'backend' as const,
metadata: {
sentBy: currentUsername,
sentAt: new Date().toISOString(),
...body.metadata
}
}
// Nota: Como usamos localStorage en el cliente, no guardamos en BD
// En su lugar, retornamos la notificación para que el cliente la guarde
// El cliente debe hacer polling o usar SSE/WebSocket para obtener notificaciones
return {
success: true,
notification,
message: 'Notificación creada correctamente'
}
} catch (error: any) {
console.error('Error al enviar notificación:', error)
// Si ya es un error de H3, relanzarlo
if (error.statusCode) {
throw error
}
// Error genérico
throw createError({
statusCode: 500,
statusMessage: 'Error al procesar la notificación'
})
}
})