202 lines
5.8 KiB
JavaScript
202 lines
5.8 KiB
JavaScript
const admin = require("firebase-admin");
|
|
const { PrismaClient: CMSClient } = require("../../prisma/clients/cms");
|
|
const logger = require("./logger.services");
|
|
const { localTime } = require("./time.services.js");
|
|
|
|
const prisma = new CMSClient();
|
|
|
|
async function sendNotification(token, title, body, data = {}, imageUrl = null) {
|
|
if (!token) throw new Error("Missing FCM token");
|
|
|
|
const stringData = {};
|
|
for (const [key, value] of Object.entries(data)) {
|
|
stringData[key] = typeof value === 'string' ? value : JSON.stringify(value);
|
|
}
|
|
|
|
if (imageUrl) {
|
|
stringData.image = imageUrl;
|
|
}
|
|
|
|
const message = {
|
|
notification: { title, body },
|
|
data: stringData,
|
|
token,
|
|
};
|
|
|
|
if (imageUrl) {
|
|
message.android = {
|
|
notification: {
|
|
imageUrl: imageUrl
|
|
}
|
|
};
|
|
}
|
|
|
|
return await admin.messaging().send(message);
|
|
}
|
|
|
|
async function sendToTopic(title, body, data = {}, imageUrl = null) {
|
|
if (!title || !body) throw new Error("Missing notification title or body");
|
|
|
|
const tokens = await prisma.usersToken.findMany({
|
|
select: {
|
|
UserID_UT: true,
|
|
Token_UT: true
|
|
}
|
|
});
|
|
|
|
const tokenList = tokens.filter(t => t.Token_UT);
|
|
|
|
if (tokenList.length === 0) {
|
|
return {
|
|
successCount: 0,
|
|
failureCount: 0,
|
|
targetUsers: 0,
|
|
deliveryDetails: [],
|
|
message: "No tokens registered"
|
|
};
|
|
}
|
|
|
|
const stringData = {};
|
|
for (const [key, value] of Object.entries(data)) {
|
|
stringData[key] = typeof value === 'string' ? value : JSON.stringify(value);
|
|
}
|
|
|
|
if (imageUrl) {
|
|
stringData.image = imageUrl;
|
|
}
|
|
|
|
const chunkSize = 500;
|
|
let successTotal = 0;
|
|
let failureTotal = 0;
|
|
const deliveryDetails = [];
|
|
|
|
for (let i = 0; i < tokenList.length; i += chunkSize) {
|
|
const tokensChunk = tokenList.slice(i, i + chunkSize);
|
|
const tokensOnly = tokensChunk.map(t => t.Token_UT);
|
|
|
|
const message = {
|
|
notification: { title, body },
|
|
data: stringData,
|
|
tokens: tokensOnly,
|
|
};
|
|
|
|
if (imageUrl) {
|
|
message.android = {
|
|
notification: {
|
|
imageUrl: imageUrl
|
|
}
|
|
};
|
|
}
|
|
|
|
const response = await admin.messaging().sendEachForMulticast(message);
|
|
successTotal += response.successCount;
|
|
failureTotal += response.failureCount;
|
|
|
|
response.responses.forEach((resp, index) => {
|
|
const userToken = tokensChunk[index];
|
|
deliveryDetails.push({
|
|
userID: userToken.UserID_UT,
|
|
token: userToken.Token_UT,
|
|
success: resp.success,
|
|
messageId: resp.messageId || null,
|
|
error: resp.error ? {
|
|
code: resp.error.code,
|
|
message: resp.error.message
|
|
} : null
|
|
});
|
|
});
|
|
}
|
|
|
|
logger.info(`Sent ${successTotal} notifications successfully and ${failureTotal} notifications failed`);
|
|
|
|
return {
|
|
successCount: successTotal,
|
|
failureCount: failureTotal,
|
|
targetUsers: tokenList.length,
|
|
deliveryRate: tokenList.length > 0 ? ((successTotal / tokenList.length) * 100).toFixed(2) : 0,
|
|
deliveryDetails: deliveryDetails
|
|
};
|
|
}
|
|
|
|
async function sendCampaignWithTracking(campaignID, title, body, data = {}, imageUrl = null) {
|
|
try {
|
|
const sentAt = new Date();
|
|
|
|
const users = await prisma.usersToken.findMany({
|
|
select: {
|
|
UserID_UT: true,
|
|
Token_UT: true
|
|
}
|
|
});
|
|
|
|
const validUsers = users.filter(u => u.Token_UT);
|
|
|
|
if (validUsers.length === 0) {
|
|
return {
|
|
success: false,
|
|
message: "No valid tokens found",
|
|
targetUsers: 0,
|
|
successCount: 0,
|
|
failureCount: 0
|
|
};
|
|
}
|
|
|
|
await prisma.campaignDelivery.createMany({
|
|
data: validUsers.map(user => ({
|
|
Campaign_CD: campaignID,
|
|
UserID_CD: user.UserID_UT,
|
|
Token_CD: user.Token_UT,
|
|
Status_CD: 'pending',
|
|
CreatedAt_CD: sentAt
|
|
}))
|
|
});
|
|
|
|
const result = await sendToTopic(title, body, { ...data, campaignID }, imageUrl);
|
|
|
|
for (const delivery of result.deliveryDetails) {
|
|
const updateData = {
|
|
SentAt_CD: sentAt,
|
|
UpdatedAt_CD: localTime(new Date())
|
|
};
|
|
|
|
if (delivery.success) {
|
|
updateData.Status_CD = 'delivered';
|
|
updateData.DeliveredAt_CD = localTime(new Date());
|
|
updateData.ResponseData_CD = JSON.stringify({ messageId: delivery.messageId });
|
|
} else {
|
|
updateData.Status_CD = 'failed';
|
|
updateData.FailedAt_CD = localTime(new Date());
|
|
updateData.ErrorMessage_CD = delivery.error ? delivery.error.message : 'Unknown error';
|
|
updateData.ResponseData_CD = JSON.stringify(delivery.error);
|
|
}
|
|
|
|
await prisma.campaignDelivery.updateMany({
|
|
where: {
|
|
Campaign_CD: campaignID,
|
|
UserID_CD: delivery.userID
|
|
},
|
|
data: updateData
|
|
});
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
targetUsers: validUsers.length,
|
|
successCount: result.successCount,
|
|
failureCount: result.failureCount,
|
|
deliveryRate: result.deliveryRate,
|
|
sentAt: sentAt
|
|
};
|
|
|
|
} catch (error) {
|
|
logger.error(`Error in sendCampaignWithTracking: ${error}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
sendNotification,
|
|
sendToTopic,
|
|
sendCampaignWithTracking,
|
|
};
|