Re_Backend/src/services/notification.service.ts

73 lines
2.5 KiB
TypeScript

import webpush from 'web-push';
import logger from '@utils/logger';
import { Subscription } from '@models/Subscription';
type PushSubscription = any; // Web Push protocol JSON
class NotificationService {
private userIdToSubscriptions: Map<string, PushSubscription[]> = new Map();
configure(vapidPublicKey?: string, vapidPrivateKey?: string, mailto?: string) {
const pub = vapidPublicKey || process.env.VAPID_PUBLIC_KEY || '';
const priv = vapidPrivateKey || process.env.VAPID_PRIVATE_KEY || '';
const contact = mailto || process.env.VAPID_CONTACT || 'mailto:admin@example.com';
if (!pub || !priv) {
logger.warn('VAPID keys are not configured. Push notifications are disabled.');
return;
}
webpush.setVapidDetails(contact, pub, priv);
logger.info('Web Push configured');
}
async addSubscription(userId: string, subscription: PushSubscription, userAgent?: string) {
// Persist to DB (upsert by endpoint)
try {
const endpoint: string = subscription?.endpoint || '';
const keys = subscription?.keys || {};
if (!endpoint || !keys?.p256dh || !keys?.auth) throw new Error('Invalid subscription payload');
await Subscription.upsert({
userId,
endpoint,
p256dh: keys.p256dh,
auth: keys.auth,
userAgent: userAgent || null,
} as any);
} catch (e) {
logger.error('Failed to persist subscription', e);
}
const list = this.userIdToSubscriptions.get(userId) || [];
const already = list.find((s) => JSON.stringify(s) === JSON.stringify(subscription));
if (!already) {
list.push(subscription);
this.userIdToSubscriptions.set(userId, list);
}
logger.info(`Subscription stored for user ${userId}. Total: ${list.length}`);
}
async sendToUsers(userIds: string[], payload: any) {
const message = JSON.stringify(payload);
for (const uid of userIds) {
let subs = this.userIdToSubscriptions.get(uid) || [];
// Load from DB if memory empty
if (subs.length === 0) {
try {
const rows = await Subscription.findAll({ where: { userId: uid } });
subs = rows.map((r: any) => ({ endpoint: r.endpoint, keys: { p256dh: r.p256dh, auth: r.auth } }));
} catch {}
}
for (const sub of subs) {
try {
await webpush.sendNotification(sub, message);
} catch (err) {
logger.error(`Failed to send push to ${uid}:`, err);
}
}
}
}
}
export const notificationService = new NotificationService();
notificationService.configure();