Notificaciones
Endpoints para gestionar notificaciones de usuario y suscripciones push.
Modelos de Datos
Section titled “Modelos de Datos”Los siguientes modelos de Prisma pertenecen al schema notifications:
Notification
Section titled “Notification”model Notification { id String @id @default(cuid()) userProfileId String @map("user_profile_id") notificationTemplateId String? @map("notification_template_id")
type NotificationType title String body String icon String? badge String? image String?
priority NotificationPriority @default(NORMAL) requiresInteraction Boolean @default(false) @map("requires_interaction") silent Boolean @default(false)
status NotificationStatus @default(PENDING) scheduledAt DateTime? @map("scheduled_at") sentAt DateTime? @map("sent_at") deliveredAt DateTime? @map("delivered_at") readAt DateTime? @map("read_at")
data Json? actions Json?
relatedEntityType String? @map("related_entity_type") relatedEntityId String? @map("related_entity_id")
failureReason String? @map("failure_reason") retryCount Int @default(0) @map("retry_count")
expiresAt DateTime? @map("expires_at")
userProfile UserProfile @relation(fields: [userProfileId], references: [id], onDelete: Cascade) notificationTemplate NotificationTemplate? @relation(fields: [notificationTemplateId], references: [id], onDelete: SetNull)
createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at")
@@index([userProfileId, status]) @@index([scheduledAt]) @@index([type]) @@map("notifications") @@schema("notifications")}PushSubscription
Section titled “PushSubscription”model PushSubscription { id String @id @default(cuid()) userProfileId String @map("user_profile_id") endpoint String p256dh String auth String userAgent String? @map("user_agent") deviceName String? @map("device_name") isActive Boolean @default(true) @map("is_active")
userProfile UserProfile @relation(fields: [userProfileId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at")
@@unique([userProfileId, endpoint]) @@map("push_subscriptions") @@schema("notifications")}NotificationTemplate
Section titled “NotificationTemplate”model NotificationTemplate { id String @id @default(cuid()) type NotificationType @unique title String body String icon String? badge String? image String? priority NotificationPriority @default(NORMAL)
actions Json?
requiresInteraction Boolean @default(false) @map("requires_interaction") silent Boolean @default(false) ttl Int? @default(86400)
variables String[]
isActive Boolean @default(true) @map("is_active")
notifications Notification[]
createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at")
@@map("notification_templates") @@schema("notifications")}NotificationType (Enum)
Section titled “NotificationType (Enum)”enum NotificationType { CLASS_REMINDER RESERVATION_CONFIRMED RESERVATION_CANCELLED MEMBERSHIP_EXPIRING PAYMENT_REMINDER ACHIEVEMENT_UNLOCKED STREAK_MILESTONE GOAL_REMINDER FRIEND_ACTIVITY CLASS_CANCELLED TRAINER_ASSIGNED WORKOUT_REMINDER SYSTEM_ANNOUNCEMENT CUSTOM
@@schema("notifications")}NotificationPriority (Enum)
Section titled “NotificationPriority (Enum)”enum NotificationPriority { LOW NORMAL HIGH URGENT
@@schema("notifications")}NotificationStatus (Enum)
Section titled “NotificationStatus (Enum)”enum NotificationStatus { PENDING SENT DELIVERED READ FAILED EXPIRED
@@schema("notifications")}Sistema de Notificaciones
Section titled “Sistema de Notificaciones”El sistema soporta notificaciones in-app y push notifications usando Web Push. Las notificaciones pueden ser programadas, enviadas inmediatamente, o basadas en plantillas.
Endpoints
Section titled “Endpoints”Obtener Notificaciones
Section titled “Obtener Notificaciones”Obtener notificaciones del usuario.
- URL:
/api/v1/notifications - Método:
GET - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint obtiene notificaciones del usuario con filtrado:
-
Filtros disponibles:
type: Tipo de notificaciónstatus: PENDING, SENT, DELIVERED, READ, FAILED, EXPIREDpriority: Prioridad de la notificaciónisRead: ‘true’ o ‘false’ para filtrar leídas/no leídasdateFrom/dateTo: Rango de fechas
-
Incluye:
- userProfile (id, name)
- notificationTemplate si se usó plantilla
-
Ordenamiento dinámico por sortBy/sortOrder
Parámetros de Consulta de Solicitud
Section titled “Parámetros de Consulta de Solicitud”read: Filtrar por status de lecturatype: Filtrar por tipo de notificaciónstatus: Filtrar por statuspage: Número de páginalimit: Resultados por página
Respuesta
Section titled “Respuesta”{ "data": [ { "id": "...", "type": "RESERVATION_CONFIRMED", "title": "Reserva confirmada", "body": "Tu reserva para Yoga ha sido confirmada", "status": "DELIVERED", "readAt": null, "createdAt": "2023-10-27T10:00:00Z" } ], "pagination": {...}}Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.notifications.$get({ query: { read: 'false' }})Obtener Conteo de No Leídas
Section titled “Obtener Conteo de No Leídas”Obtener conteo de notificaciones no leídas.
- URL:
/api/v1/notifications/unread-count - Método:
GET - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint cuenta notificaciones no leídas:
-
Filtra notificaciones donde:
- userProfileId coincide
- readAt es null
- status no es EXPIRED
-
Retorna solo el conteo
Respuesta
Section titled “Respuesta”{ "data": { "unreadCount": 5 }}Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.notifications['unread-count'].$get()Marcar Como Leída
Section titled “Marcar Como Leída”Marcar una notificación como leída.
- URL:
/api/v1/notifications/:notificationId/read - Método:
PATCH - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint marca una notificación como leída:
-
Actualiza la notificación:
- status: READ
- readAt: Fecha actual
-
Retorna la notificación actualizada
Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.notifications[':notificationId'].read.$patch({ param: { notificationId: 'notification_id' }})Marcar Todas Como Leídas
Section titled “Marcar Todas Como Leídas”Marcar todas las notificaciones del usuario como leídas.
- URL:
/api/v1/notifications/read-all - Método:
PATCH - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint marca todas las notificaciones como leídas:
-
Actualiza en batch todas las notificaciones donde:
- userProfileId coincide
- readAt es null
-
Establece:
- status: READ
- readAt: Fecha actual
-
Retorna conteo de actualizadas
Respuesta
Section titled “Respuesta”{ "data": { "updated": 5, "message": "5 notificaciones marcadas como leídas" }}Crear Suscripción Push
Section titled “Crear Suscripción Push”Registrar una suscripción push.
- URL:
/api/v1/notifications/subscriptions - Método:
POST - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint registra una suscripción push:
- Verifica si existe suscripción con mismo endpoint
- Si existe: Actualiza p256dh, auth, userAgent, deviceName y reactiva
- Si no existe: Crea nueva suscripción
Campos de Suscripción
Section titled “Campos de Suscripción”endpoint: URL del servicio pushp256dh: Clave pública del clienteauth: Secreto de autenticaciónuserAgent: Identificador del navegadordeviceName: Nombre del dispositivo (opcional)
Cuerpo de Solicitud
Section titled “Cuerpo de Solicitud”{ "endpoint": "https://fcm.googleapis.com/...", "keys": { "p256dh": "...", "auth": "..." }}Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.notifications.subscriptions.$post({ json: { endpoint: 'https://...', keys: { p256dh: '...', auth: '...' } }})Eliminar Suscripción Push
Section titled “Eliminar Suscripción Push”Eliminar una suscripción push.
- URL:
/api/v1/notifications/subscriptions/:id - Método:
DELETE - Autenticación Requerida: Sí
Descripción Interna
Section titled “Descripción Interna”Este endpoint elimina una suscripción push:
- Elimina la suscripción por ID
- Retorna confirmación
Crear Notificación (Admin)
Section titled “Crear Notificación (Admin)”Crear y enviar una notificación.
- URL:
/api/v1/notifications - Método:
POST - Autenticación Requerida: Sí (Admin)
Descripción Interna
Section titled “Descripción Interna”Este endpoint crea y envía una notificación:
-
Crea la notificación en la base de datos con:
- Todos los campos proporcionados
- status: SENT (o PENDING si programada)
- sentAt: Ahora (o null si programada)
-
Si no está programada, envía inmediatamente:
- Obtiene suscripciones push activas del usuario
- Construye payload con título, body, icon, etc.
- Envía a todos los dispositivos
- Actualiza status a DELIVERED si exitoso
-
Manejo de errores push:
- Si error 410 (Gone): Desactiva la suscripción
Cuerpo de Solicitud
Section titled “Cuerpo de Solicitud”{ "userProfileId": "user_id", "type": "CUSTOM", "title": "New Announcement", "body": "Check out our new classes!", "icon": "🔔", "priority": "HIGH", "data": { "url": "/classes" }}Difundir Notificación (Admin)
Section titled “Difundir Notificación (Admin)”Enviar notificación a múltiples usuarios.
- URL:
/api/v1/notifications/broadcast - Método:
POST - Autenticación Requerida: Sí (Admin)
Descripción Interna
Section titled “Descripción Interna”Este endpoint envía notificaciones masivas:
-
Obtiene usuarios objetivo según filtros:
targetRoles: Filtrar por roles específicostargetUserIds: Lista específica de usuarios
-
Valida que haya usuarios para enviar
-
Crea notificación para cada usuario encontrado
-
Retorna conteo de enviadas
Cuerpo de Solicitud
Section titled “Cuerpo de Solicitud”{ "targetRoles": ["MEMBER"], "type": "ANNOUNCEMENT", "title": "Gym Update", "body": "New equipment available!"}Obtener Plantillas de Notificación (Admin)
Section titled “Obtener Plantillas de Notificación (Admin)”Obtener todas las plantillas de notificación.
- URL:
/api/v1/notifications/templates - Método:
GET - Autenticación Requerida: Sí (Admin)
Descripción Interna
Section titled “Descripción Interna”Este endpoint obtiene plantillas de notificación:
-
Filtros disponibles:
type: Tipo de plantillaisActive: Solo plantillas activas
-
Ordenamiento por sortBy/sortOrder
-
Paginación estándar
Plantillas
Section titled “Plantillas”Las plantillas permiten definir notificaciones reutilizables con variables como {{userName}}, {{className}}, etc.
Enviar Desde Plantilla (Admin)
Section titled “Enviar Desde Plantilla (Admin)”Enviar notificación usando una plantilla.
- URL:
/api/v1/notifications/from-template - Método:
POST - Autenticación Requerida: Sí (Admin)
Descripción Interna
Section titled “Descripción Interna”Este endpoint envía notificaciones desde plantilla:
- Obtiene plantilla por tipo
- Verifica que esté activa
- Reemplaza variables en título y body:
- Busca
{{variable}}y reemplaza con valores proporcionados
- Busca
- Crea notificaciones para cada userProfileId
- Configura expiración según TTL de plantilla
Cuerpo de Solicitud
Section titled “Cuerpo de Solicitud”{ "type": "RESERVATION_REMINDER", "userProfileIds": ["user_1", "user_2"], "variables": { "className": "Yoga", "date": "2023-10-27" }}Obtener Estadísticas de Notificaciones (Admin)
Section titled “Obtener Estadísticas de Notificaciones (Admin)”Obtener estadísticas de notificaciones.
- URL:
/api/v1/notifications/stats - Método:
GET - Autenticación Requerida: Sí (Admin)
Descripción Interna
Section titled “Descripción Interna”Este endpoint calcula estadísticas de notificaciones:
-
Conteos:
- Total de notificaciones
- No leídas
- Leídas
-
Tasa de lectura: (leídas / total) × 100
-
Agrupaciones:
- Por status
- Por tipo
- Por prioridad
Respuesta
Section titled “Respuesta”{ "data": { "total": 1000, "unread": 150, "read": 850, "readRate": 85.0, "byStatus": [ { "status": "READ", "count": 850 }, { "status": "DELIVERED", "count": 100 } ], "byType": [...], "byPriority": [...] }}