feat: Implement Phase 2 cross-cutting services with email and notification enhancements

- Refactor email verification and password reset utilities to use new email service.
- Introduce email delivery queue and notification delivery model for better tracking.
- Enhance file validation and storage services for improved security and ownership management.
- Add cron job for cleaning inactive notifications with retention policy.
- Update document worker to handle document generation and storage more efficiently.
- Implement logging improvements in activity and log workers.
- Create comprehensive documentation for new API endpoints and services.
- Add unit tests for file validation and notification policies to ensure robustness.
This commit is contained in:
Sathira Sri Sathara
2026-09-03 14:22:34 +05:30
parent 9d3d431416
commit b6b345f245
54 changed files with 593 additions and 1248 deletions
+16 -134
View File
@@ -1,138 +1,20 @@
/**
* Copyright (c) 2026 Niolla
* All rights reserved.
*
* This source code is proprietary and confidential.
* Unauthorized copying, modification, distribution, or use
* of this file, via any medium, is strictly prohibited.
*/
// app/controllers/notification.controller.js
const crypto = require("crypto");
const db = require("../models");
const Notification = db.notification;
const UserNotification = db.userNotification;
const { Op } = require("sequelize");
const log = require("../utils/consoleLog.utill").log;
// Controller for managing notifications
exports.createNotification = async (req, res) => {
exports.createNotification = async (req, res, next) => {
const { notificationHeadline, notificationDescription, notificationType, userIds = [] } = req.body;
if (!notificationHeadline || !["USER", "ANNOUNCEMENT"].includes(notificationType)) return res.status(400).json({ success: false, error: { code: "VALIDATION_ERROR", message: "Valid headline and notificationType are required" } });
if (notificationType === "USER" && (!Array.isArray(userIds) || !userIds.length)) return res.status(400).json({ success: false, error: { code: "VALIDATION_ERROR", message: "USER notifications require userIds" } });
const transaction = await db.sequelize.transaction();
try {
const { notificationHeadline, notificationDescription, notificationType } =
req.body;
const id = Date.now().toString(); // Generate a unique ID based on the current timestamp
const notification_id = `notif_${id}`; // Prefix the ID with "notif_"
// Create a new notification
const newNotification = await Notification.create({
notification_id,
notificationHeadline,
notificationDescription,
notificationType,
dateCreated: new Date(),
});
res.status(201).json({
success: true,
message: "Notification created successfully",
notification: newNotification,
});
} catch (error) {
log("Error creating notification:", error.message);
res.status(500).json({
success: false,
message: "Internal server error",
error: error.message
});
}
const notification = await db.notification.create({ notification_id: `notif_${crypto.randomUUID()}`, notificationHeadline, notificationDescription, notificationType, dateCreated: new Date() }, { transaction });
const uniqueUsers = [...new Set(userIds)];
if (uniqueUsers.length) await db.userNotification.bulkCreate(uniqueUsers.map((user_id) => ({ user_id, notification_id: notification.notification_id })), { transaction, ignoreDuplicates: true });
await transaction.commit(); return res.status(201).json({ success: true, data: { notification, assignedUsers: uniqueUsers.length } });
} catch (error) { await transaction.rollback(); return next(error); }
};
// Mark a notification as read for a user
exports.markAsRead = async (req, res) => {
try {
const { userId, notificationId } = req.params;
// Find the user notification entry
const userNotification = await UserNotification.findOne({
where: { user_id: userId, notification_id: notificationId },
});
if (!userNotification) {
return res.status(404).json({ success: false, error: "User notification not found" });
}
// Mark the notification as read
userNotification.isRead = true;
await userNotification.save();
res.status(200).json({
success: true,
message: "Notification marked as read",
});
}
catch (error) {
log("Error marking notification as read:", error.message);
res.status(500).json({
success: false,
message: "Internal server error",
error: error.message
});
}
};
// Get announcements for all users
exports.getAnnouncements = async (req, res) => {
try {
// Fetch all announcements
const announcements = await Notification.findAll({
where: { notificationType: "ANNOUNCEMENT", isActive: true },
});
res.status(200).json({
success: true,
announcements,
});
} catch (error) {
log("Error fetching announcements:", error.message);
res.status(500).json({
success: false,
message: "Internal server error",
error: error.message
});
}
}
// Get all notifications for a user
exports.getUserNotifications = async (req, res) => {
try {
const { userId } = req.params;
// Fetch notifications for the user
const notifications = await UserNotification.findAll({
where: { user_id: userId, isRead: false },
include: [
{
model: Notification,
as: "notification",
},
],
});
res.status(200).json({
success: true,
notifications,
});
}
catch (error) {
log("Error fetching user notifications:", error.message);
res.status(500).json({
success: false,
message: "Internal server error",
error: error.message
});
}
};
exports.getAnnouncements = async (_req, res, next) => { try { return res.json({ success: true, data: await db.notification.findAll({ where: { notificationType: "ANNOUNCEMENT", isActive: true }, order: [["createdAt", "DESC"]] }) }); } catch (e) { return next(e); } };
exports.getMyNotifications = async (req, res, next) => { try { const rows = await db.userNotification.findAll({ where: { user_id: req.user.id }, include: [{ model: db.notification, as: "notification", where: { isActive: true } }], order: [["createdAt", "DESC"]] }); return res.json({ success: true, data: rows }); } catch (e) { return next(e); } };
exports.markAsRead = async (req, res, next) => { try { const [count] = await db.userNotification.update({ isRead: true }, { where: { user_id: req.user.id, notification_id: req.params.notificationId } }); if (!count) return res.status(404).json({ success: false, error: { code: "NOT_FOUND", message: "Notification not found" } }); return res.json({ success: true }); } catch (e) { return next(e); } };
exports.markAllAsRead = async (req, res, next) => { try { const [count] = await db.userNotification.update({ isRead: true }, { where: { user_id: req.user.id, isRead: false } }); return res.json({ success: true, data: { updated: count } }); } catch (e) { return next(e); } };