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 { 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 { 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 { 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 { 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 } }