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
+15 -96
View File
@@ -1,98 +1,17 @@
/**
* 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.
*/
// cron/cleanInactiveNotifications.cron.js
const cron = require("node-cron");
const { Op } = require("sequelize");
const { sequelize, notification, userNotification } = require("../app/models");
const {
sequelize,
notification,
userNotification,
} = require("../app/models");
function startCleanInactiveNotificationsCron() {
console.log("Inactive Notification Cleanup Cron Started");
const task = cron.schedule(
"0 0 * * *", // Run every day at midnight
async () => {
console.log(
`[CRON] Cleaning inactive notifications... ${new Date().toISOString()}`,
);
try {
const inactiveNotifications = await notification.findAll({
where: {
isActive: false,
},
attributes: ["notification_id"],
});
if (!inactiveNotifications.length) {
console.log("[CRON] No inactive notifications found.");
return;
}
const notificationIds = inactiveNotifications.map(
(item) => item.notification_id,
);
const transaction = await sequelize.transaction();
try {
// Delete user-notification relationships first
const deletedUserNotifications = await userNotification.destroy({
where: {
notification_id: notificationIds,
},
transaction,
});
// Delete inactive notifications
const deletedNotifications = await notification.destroy({
where: {
notification_id: notificationIds,
isActive: false,
},
transaction,
});
await transaction.commit();
console.log(
`[CRON] Cleanup completed. Deleted ${deletedNotifications} notifications and ${deletedUserNotifications} user notification records.`,
);
} catch (err) {
await transaction.rollback();
console.error(
"[CRON] Failed to clean inactive notifications:",
err,
);
}
} catch (err) {
console.error(
"[CRON] Error while checking inactive notifications:",
err,
);
}
},
{
scheduled: false,
},
);
task.start();
return task;
}
module.exports = startCleanInactiveNotificationsCron;
module.exports = function startCleanInactiveNotificationsCron() {
const task = cron.schedule("0 0 * * *", async () => {
const cutoff = new Date(Date.now() - Number(process.env.NOTIFICATION_RETENTION_DAYS || 90) * 86400000);
for (;;) {
const rows = await notification.findAll({ where: { isActive: false, updatedAt: { [Op.lt]: cutoff } }, attributes: ["notification_id"], limit: 500, order: [["updatedAt", "ASC"]] });
if (!rows.length) break;
const ids = rows.map((row) => row.notification_id);
await sequelize.transaction(async (transaction) => { await userNotification.destroy({ where: { notification_id: { [Op.in]: ids } }, transaction }); await notification.destroy({ where: { notification_id: { [Op.in]: ids }, isActive: false }, transaction }); });
if (rows.length < 500) break;
}
}, { scheduled: false });
task.start(); return task;
};