csa-backend-test/app/services/scheduled-notification-proc...

180 lines
5.5 KiB
JavaScript

require('dotenv').config();
const cron = require('node-cron');
const logger = require('./logger.services');
const { getDueNotifications, updateNotificationStatus } = require('./notification-scheduler.services');
const { sendNotification } = require('./firebase.services');
const { PrismaClient: CMSClient } = require("../../prisma/clients/cms");
const { localTime } = require('./time.services');
const prisma = new CMSClient();
/**
* Process and send a single due notification
* @param {Object} notification - The notification record
*/
async function processDueNotification(notification) {
const startTime = Date.now();
try {
const userToken = await prisma.usersToken.findFirst({
where: { UserID_UT: notification.UserID_AIN }
});
if (!userToken || !userToken.Token_UT) {
logger.warn(`No valid FCM token for user ${notification.UserID_AIN}`);
await updateNotificationStatus(notification.UUID_AIN, {
SentStatus_AIN: 'failed',
ErrorMessage_AIN: 'No valid FCM token',
FailedAt_AIN: localTime(new Date())
});
return { success: false, reason: 'No FCM token' };
}
const messageId = await sendNotification(
userToken.Token_UT,
notification.GeneratedTitle_AIN,
notification.GeneratedDesc_AIN,
{
source: 'scheduled-notification',
aiNotificationId: notification.UUID_AIN,
scheduledDelivery: 'true',
confidence: notification.PredictedConfidence_AIN?.toString() || '0',
pattern: notification.UserEngagementPattern_AIN || 'unknown'
}
);
await updateNotificationStatus(notification.UUID_AIN, {
SentStatus_AIN: 'sent',
SentAt_AIN: localTime(new Date()),
FCMMessageId_AIN: messageId,
ProcessingTime_AIN: Date.now() - startTime
});
logger.info(`Scheduled notification sent to ${notification.UserID_AIN}`, {
notificationId: notification.UUID_AIN,
messageId,
scheduledFor: notification.ScheduledAt_AIN,
confidence: notification.PredictedConfidence_AIN,
pattern: notification.UserEngagementPattern_AIN
});
return { success: true, messageId };
} catch (error) {
logger.error(`Failed to send scheduled notification ${notification.UUID_AIN}:`, error);
await updateNotificationStatus(notification.UUID_AIN, {
SentStatus_AIN: 'failed',
ErrorMessage_AIN: error.message,
FailedAt_AIN: localTime(new Date()),
ProcessingTime_AIN: Date.now() - startTime
});
return { success: false, error: error.message };
}
}
/**
* Main processor function - checks and sends all due notifications
*/
async function processScheduledNotifications() {
try {
logger.info('🔄 Checking for due scheduled notifications...');
const dueNotifications = await getDueNotifications();
if (dueNotifications.length === 0) {
logger.info('✓ No notifications due at this time');
return {
processed: 0,
sent: 0,
failed: 0
};
}
logger.info(`Found ${dueNotifications.length} notifications to process`);
const results = {
processed: 0,
sent: 0,
failed: 0,
details: []
};
for (const notification of dueNotifications) {
results.processed++;
const result = await processDueNotification(notification);
if (result.success) {
results.sent++;
} else {
results.failed++;
}
results.details.push({
notificationId: notification.UUID_AIN,
userId: notification.UserID_AIN,
success: result.success,
messageId: result.messageId,
error: result.error,
reason: result.reason
});
await new Promise(resolve => setTimeout(resolve, 500));
}
logger.info(`Scheduled notification processing complete:`, {
processed: results.processed,
sent: results.sent,
failed: results.failed
});
return results;
} catch (error) {
logger.error('Error in scheduled notification processor:', error);
throw error;
}
}
/**
* Start the cron job for scheduled notifications
* Runs every 5 minutes
*/
function startScheduledNotificationCron() {
const cronExpression = '*/5 * * * *';
const job = cron.schedule(cronExpression, async () => {
logger.info('🕐 Scheduled notification cron job triggered');
try {
await processScheduledNotifications();
} catch (error) {
logger.error('Cron job error:', error);
}
}, {
scheduled: true,
timezone: process.env.TIMEZONE || "Asia/Jakarta"
});
logger.info(`✅ Scheduled notification cron started (runs every 5 minutes)`);
return job;
}
/**
* Manual trigger for testing
*/
async function manualTriggerScheduledProcessor() {
logger.info('🔧 Manual trigger: Processing scheduled notifications');
return await processScheduledNotifications();
}
module.exports = {
startScheduledNotificationCron,
processScheduledNotifications,
manualTriggerScheduledProcessor,
processDueNotification
};