Files
FRASA-CAS/api-gateway/src/services/push.service.ts
Consult AS Technology c77b4c0628 feat(mejoras): 14 mejoras al CRM Frasa
Fase 1 - UI: dark mode, skeletons, toasts
Fase 2 - Filtros: avanzados Mi día, búsqueda global Ctrl+K
Fase 3 - Datos: export Excel, notas apartados, auditoría
Fase 4 - Dashboard: gráficas vendedor + métricas uso
Fase 5 - Responsive: tablets optimizadas
Fase 6 - PWA: IndexedDB offline, push notifications

Backend: 3 tablas nuevas, 6 services, 6 endpoints nuevos
721 API tests + fronts verdes.
2026-09-07 06:04:49 +00:00

88 lines
2.6 KiB
TypeScript

import webPush from 'web-push'
import { config } from '../config/index.js'
import { prisma } from './prisma.js'
// Configurar VAPID solo si las claves están presentes
if (config.push.vapidPublicKey && config.push.vapidPrivateKey) {
webPush.setVapidDetails(
config.push.vapidEmail,
config.push.vapidPublicKey,
config.push.vapidPrivateKey,
)
}
export interface PushSubscriptionInput {
endpoint: string
p256dh: string
auth: string
}
/** Registra o actualiza la suscripción push de un usuario */
export async function registrarSuscripcion(usuarioId: string, subscription: PushSubscriptionInput): Promise<void> {
await prisma.pushSubscription.upsert({
where: { usuarioId },
update: {
endpoint: subscription.endpoint,
p256dh: subscription.p256dh,
auth: subscription.auth,
},
create: {
usuarioId,
endpoint: subscription.endpoint,
p256dh: subscription.p256dh,
auth: subscription.auth,
},
})
}
/** Elimina la suscripción push de un usuario */
export async function eliminarSuscripcion(usuarioId: string): Promise<void> {
await prisma.pushSubscription.deleteMany({ where: { usuarioId } })
}
/** Devuelve la VAPID public key para el frontend */
export function obtenerVapidPublicKey(): string {
return config.push.vapidPublicKey
}
/** Busca el usuarioId local a partir de un mondayUserId */
export async function usuarioIdPorMondayUserId(mondayUserId: number): Promise<string | null> {
const usuario = await prisma.usuario.findFirst({
where: { mondayUserId },
select: { id: true },
})
return usuario?.id ?? null
}
/** Envía una notificación push a un usuario. Silencioso si no tiene suscripción o si web-push no está configurado. */
export async function enviarPush(
usuarioId: string,
titulo: string,
cuerpo: string,
url?: string,
): Promise<boolean> {
if (!config.push.vapidPublicKey || !config.push.vapidPrivateKey) return false
const sub = await prisma.pushSubscription.findUnique({ where: { usuarioId } })
if (!sub) return false
const payload = JSON.stringify({ titulo, cuerpo, url: url ?? '/' })
try {
await webPush.sendNotification(
{ endpoint: sub.endpoint, keys: { p256dh: sub.p256dh, auth: sub.auth } },
payload,
)
return true
} catch (err: unknown) {
// Si la suscripción ya no es válida (404 o 410), eliminarla
const status = (err as { statusCode?: number }).statusCode
if (status === 404 || status === 410) {
await prisma.pushSubscription.deleteMany({ where: { usuarioId } })
}
// eslint-disable-next-line no-console
console.error(`[push] No se pudo enviar a ${usuarioId}:`, err)
return false
}
}