Skip to content

Commit

Permalink
Convert fcm middleware -> notification service
Browse files Browse the repository at this point in the history
  • Loading branch information
Timothy-Gonzalez committed Sep 13, 2024
1 parent 76fa5dd commit 85662a8
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 86 deletions.
20 changes: 0 additions & 20 deletions src/middleware/fcm.ts

This file was deleted.

124 changes: 58 additions & 66 deletions src/services/notification/notification-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { JwtPayload } from "../auth/auth-models";
import { hasAdminPerms, hasStaffPerms } from "../auth/auth-lib";
import { NotificationSendFormat, isValidNotificationSendFormat } from "./notification-formats";
import { StaffShift } from "database/staff-db";
import { NotificationsMiddleware } from "../../middleware/fcm";
import Config from "../../config";
import { sendNotification } from "./notification-service";

const notificationsRouter = Router();

Expand Down Expand Up @@ -102,72 +102,64 @@ notificationsRouter.post("/batch/", strongJwtVerification, async (req: Request,

// Sends notifications to a batch of users, gotten from /notification/batch
// Only accepts Config.NOTIFICATION_BATCH_SIZE users.
notificationsRouter.post(
"/send",
strongJwtVerification,
NotificationsMiddleware,
async (req: Request, res: Response, next: NextFunction) => {
const payload = res.locals.payload as JwtPayload;
const admin = res.locals.fcm;
const sendRequest = req.body as { batchId: string };

if (!hasAdminPerms(payload)) {
return next(new RouterError(StatusCode.ClientErrorForbidden, "Forbidden"));
}
if (!sendRequest.batchId) {
return next(new RouterError(StatusCode.ClientErrorBadRequest, "NoBatchId"));
}

const decodedBatchId = Buffer.from(sendRequest.batchId, "base64url").toString("utf-8");
const [messageId, targetUserIds] = JSON.parse(decodedBatchId) as [string, string[]];

const message = await Models.NotificationMessages.findById(messageId);

if (!message || !targetUserIds || targetUserIds.length > Config.NOTIFICATION_BATCH_SIZE) {
return next(new RouterError(StatusCode.ClientErrorBadRequest, "InvalidBatch"));
}
const messageTemplate = {
notification: {
title: message.title,
body: message.body,
},
};
const startTime = new Date();
let notifMappings = await Models.NotificationMappings.find({ userId: { $in: targetUserIds } }).exec();
notifMappings = notifMappings.filter((x) => x?.deviceToken != undefined);

const sent: string[] = [];
const failed: string[] = [];

const messages = notifMappings.map((mapping) =>
admin
.messaging()
.send({ token: mapping.deviceToken, ...messageTemplate })
.then(() => {
sent.push(mapping.userId);
})
.catch(() => {
failed.push(mapping.userId);
}),
);
await Promise.all(messages);
await Models.NotificationMessages.findOneAndUpdate(
{
_id: messageId,
},
{
$push: {
batches: {
sent,
failed,
},
notificationsRouter.post("/send", strongJwtVerification, async (req: Request, res: Response, next: NextFunction) => {
const payload = res.locals.payload as JwtPayload;
const sendRequest = req.body as { batchId: string };

if (!hasAdminPerms(payload)) {
return next(new RouterError(StatusCode.ClientErrorForbidden, "Forbidden"));
}
if (!sendRequest.batchId) {
return next(new RouterError(StatusCode.ClientErrorBadRequest, "NoBatchId"));
}

const decodedBatchId = Buffer.from(sendRequest.batchId, "base64url").toString("utf-8");
const [messageId, targetUserIds] = JSON.parse(decodedBatchId) as [string, string[]];

const message = await Models.NotificationMessages.findById(messageId);

if (!message || !targetUserIds || targetUserIds.length > Config.NOTIFICATION_BATCH_SIZE) {
return next(new RouterError(StatusCode.ClientErrorBadRequest, "InvalidBatch"));
}
const messageTemplate = {
notification: {
title: message.title,
body: message.body,
},
};
const startTime = new Date();
let notifMappings = await Models.NotificationMappings.find({ userId: { $in: targetUserIds } }).exec();
notifMappings = notifMappings.filter((x) => x?.deviceToken != undefined);

const sent: string[] = [];
const failed: string[] = [];

const messages = notifMappings.map((mapping) =>
sendNotification({ token: mapping.deviceToken, ...messageTemplate })
.then(() => {
sent.push(mapping.userId);
})
.catch(() => {
failed.push(mapping.userId);
}),
);
await Promise.all(messages);
await Models.NotificationMessages.findOneAndUpdate(
{
_id: messageId,
},
{
$push: {
batches: {
sent,
failed,
},
},
);
const endTime = new Date();
const timeElapsed = endTime.getTime() - startTime.getTime();
return res.status(StatusCode.SuccessOK).send({ status: "Success", sent, failed, time_ms: timeElapsed });
},
);
},
);
const endTime = new Date();
const timeElapsed = endTime.getTime() - startTime.getTime();
return res.status(StatusCode.SuccessOK).send({ status: "Success", sent, failed, time_ms: timeElapsed });
});

export default notificationsRouter;
22 changes: 22 additions & 0 deletions src/services/notification/notification-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Message } from "firebase-admin/lib/messaging/messaging-api";
import Config from "../../config";
import admin, { ServiceAccount } from "firebase-admin";

function initializeFCM(): void {
if (!admin.apps.length) {
const encodedKey = Config.FCM_SERVICE_ACCOUNT;
const serviceAccount = JSON.parse(atob(encodedKey)) as ServiceAccount;
const projectName = serviceAccount.projectId;
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: `https://${projectName}.firebaseio.com/`,
});
``;
}
}

export function sendNotification(message: Message): Promise<string> {
initializeFCM();

return admin.messaging().send(message);
}

0 comments on commit 85662a8

Please sign in to comment.