// /components/notifications/notificationService.js const Notification = require('./notificationModel'); const User = require('../users/userModel'); const AppError = require('../../utils/AppError'); const { sendEmail } = require('../../utils/senders/emailSender'); const { sendSMS } = require('../../utils/senders/smsSender'); const { sendBaleMessage } = require('../../utils/senders/baleBotSender'); const { parsePaginationAndSort, buildFilterQuery, calculateMeta } = require('../../utils/pagination'); const createNotification = async (data) => { const user = await User.findById(data.user); if (!user) throw new AppError('USER_NOT_FOUND'); const notification = await Notification.create(data); // Attempt sending immediately try { if (data.channel === 'email' && user.email) { await sendEmail({ to: user.email, subject: data.subject, body: data.body }); } else if (data.channel === 'baleBot') { await sendBaleMessage({ chatId: user.phoneNumber, body: data.body }); } else { await sendSMS({ phoneNumber: user.phoneNumber, body: data.body }); } notification.status = 'sent'; notification.sentAt = new Date(); await notification.save(); } catch (err) { notification.status = 'failed'; notification.lastError = err.message; notification.retryCount = 1; await notification.save(); } return notification; }; const getNotificationById = async (id) => { const notification = await Notification.findById(id).populate('user', 'name surname username email phoneNumber'); if (!notification) { throw new AppError('NOTIFICATION_NOT_FOUND'); } return notification; }; const getAllNotifications = async (queryParams) => { const { page, limit, skip, sort } = parsePaginationAndSort(queryParams); const filter = buildFilterQuery(queryParams, ['subject', 'body']); const [notifications, totalCount] = await Promise.all([ Notification.find(filter).populate('user', 'name surname username').sort(sort).skip(skip).limit(limit), Notification.countDocuments(filter) ]); const meta = calculateMeta(totalCount, page, limit); return { data: notifications, meta }; }; const updateNotification = async (id, updateData) => { const notification = await Notification.findById(id); if (!notification) { throw new AppError('NOTIFICATION_NOT_FOUND'); } Object.assign(notification, updateData); await notification.save(); return notification; }; const deleteNotification = async (id) => { const notification = await Notification.findById(id); if (!notification) { throw new AppError('NOTIFICATION_NOT_FOUND'); } await Notification.findByIdAndDelete(id); return null; }; const searchNotifications = async (queryParams) => { return getAllNotifications(queryParams); }; const retryNotification = async (id) => { const notification = await Notification.findById(id).populate('user'); if (!notification) { throw new AppError('NOTIFICATION_NOT_FOUND'); } const user = notification.user; if (!user) throw new AppError('USER_NOT_FOUND'); notification.status = 'retrying'; notification.retryCount += 1; await notification.save(); try { if (notification.channel === 'email' && user.email) { await sendEmail({ to: user.email, subject: notification.subject, body: notification.body }); } else if (notification.channel === 'baleBot') { await sendBaleMessage({ chatId: user.phoneNumber, body: notification.body }); } else { await sendSMS({ phoneNumber: user.phoneNumber, body: notification.body }); } notification.status = 'sent'; notification.sentAt = new Date(); await notification.save(); return { success: true, notification }; } catch (err) { notification.status = 'failed'; notification.lastError = err.message; await notification.save(); throw new Error(`Retry attempt ${notification.retryCount} failed: ${err.message}`); } }; const getMyNotifications = async (userId, queryParams) => { const filter = { user: userId, ...buildFilterQuery(queryParams) }; const { page, limit, skip, sort } = parsePaginationAndSort(queryParams); const [notifications, totalCount] = await Promise.all([ Notification.find(filter).sort(sort).skip(skip).limit(limit), Notification.countDocuments(filter) ]); const meta = calculateMeta(totalCount, page, limit); return { data: notifications, meta }; }; module.exports = { createNotification, getNotificationById, getAllNotifications, updateNotification, deleteNotification, searchNotifications, retryNotification, getMyNotifications };