Add hasDiscount, freeSpots, and showOnFrontend on classes with a public get-all endpoint and richer notification user populate.
139 lines
4.5 KiB
JavaScript
139 lines
4.5 KiB
JavaScript
// /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 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 username phoneNumber').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
|
|
};
|