14 servicios eliminados, 7 controllers local-only, crons locales, config sin MONDAY_*, frontend sin banners/avisos Monday. 552 tests API + 7 apps construidas.
79 lines
2.3 KiB
TypeScript
79 lines
2.3 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
|
|
}
|
|
|
|
/** 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
|
|
}
|
|
}
|