UNPKG

@ideal-photography/shared

Version:

Shared MongoDB and utility logic for Ideal Photography PWAs: users, products, services, bookings, orders/cart, galleries, reviews, notifications, campaigns, settings, audit logs, minimart items/orders, and push notification subscriptions.

107 lines (93 loc) 4.53 kB
import { models } from '../mongoDB/index.js'; import { sendNotificationToUsers, sendNotificationToAdmins, sendNotificationToAllUsers, shouldSendPushNotification } from '../utils/notifications.js'; import EmailNotificationService from './EmailNotificationService.js'; /** * NotificationService handles persistence of Notification documents * and dispatching of channel-specific deliveries (currently push only). */ class NotificationService { /** * Persist a notification document and enqueue channel dispatches. * * @param {object} alertMeta Output from a handler: { title, message, type, recipients, channels, priority } * @param {object} opts Additional options passed from emitAlert (e.g., override channels, priority) */ static async createAndSend(alertMeta, opts = {}) { const meta = { ...alertMeta }; // Apply option overrides if (opts.channels) meta.channels = { ...meta.channels, ...opts.channels }; if (opts.priority) meta.priority = opts.priority; // Persist notification (status draft initially) const Notification = models.Notification || (await import('../models/Notification.js')).default; const notificationDoc = await Notification.create({ ...meta, status: 'sent', // We send immediately for now trigger: opts.trigger || null, sentAt: new Date(), createdBy: meta.createdBy || null // should be set by handler if system user }); // Dispatch push notifications if enabled if (shouldSendPushNotification(meta)) { const payload = { title: meta.title, body: meta.message, tag: meta.type, url: meta.url || '/', priority: meta.priority, data: meta.data || {} }; // Broadcast to all users if (meta.recipients?.broadcast) { await sendNotificationToAllUsers({ ...payload, tag: meta.type }); } // Target specific users const userIds = Array.isArray(meta.recipients?.users) ? meta.recipients.users : []; if (userIds.length) { await sendNotificationToUsers(userIds, payload); } // Target admins by roles const roles = Array.isArray(meta.recipients?.roles) ? meta.recipients.roles : []; const adminRoles = ['admin', 'manager', 'super_admin']; if (roles.some(r => adminRoles.includes(r))) { const admins = await models.Admin.find({ role: { $in: adminRoles }, isActive: true }).select('_id'); const adminIds = admins.map(a => a._id); if (adminIds.length) { await sendNotificationToAdmins(adminIds, payload); } } } // Dispatch email if enabled if (meta.channels?.email) { const recipientEmails = []; // 1. User-specific emails if (Array.isArray(meta.recipients?.users) && meta.recipients.users.length) { const users = await models.User.find({ _id: { $in: meta.recipients.users } }).select('email name'); users.forEach(u => { if (u?.email) { recipientEmails.push({ email: u.email, name: u.name }); } }); } // 2. Admin/broadcast emails – use SMTP_USER env variable const wantsAdminEmail = meta.recipients?.admins || meta.recipients?.roles?.includes?.('admin'); if (wantsAdminEmail) { const adminEmail = process.env.SMTP_USER; // Single source of truth if (adminEmail) { recipientEmails.push({ email: adminEmail, name: 'Admin' }); } } if (recipientEmails.length) { const sendPromises = recipientEmails.map(({ email, name }) => EmailNotificationService.send( meta.emailTemplate || meta.type, { name, ...meta.emailData }, [email] ) ); await Promise.allSettled(sendPromises); } } return notificationDoc; } } export default NotificationService;