diff --git a/src/database/models/account_notification.ts b/src/database/models/account_notification.ts index e68cf24..6a52968 100644 --- a/src/database/models/account_notification.ts +++ b/src/database/models/account_notification.ts @@ -35,6 +35,16 @@ AccountNotification.init( type: DataTypes.DATE, allowNull: false, defaultValue: DataTypes.NOW, + }, + testQueued: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }, + delayedTestQueued: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false } }, { sequelize } diff --git a/src/index.ts b/src/index.ts index 48be233..dcb07ba 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,10 +11,10 @@ import { import sequelize from "./database/database"; // Helper functions -import { addAccountNotification } from "./notifications/notifications"; +import { addAccountNotification, generateTestNotification } from "./notifications/notifications"; // Start cron job -import notificationService from "./notifications/service/notification_service"; +import { notificationService, checkNotifications } from "./notifications/service/notification_service"; import AccountNotification from "./database/models/account_notification"; notificationService.start(); @@ -66,6 +66,25 @@ app.delete("/notifications", (req, res) => { } }); +app.post("/test", async (req: Request, res: Response) => { + const { jwt } = req.body as NotificationRequest; + + if (!jwt) + return res.status(400).send("Missing one or more required parameters"); + + try { + let result = await generateTestNotification(jwt); + + res.status(201).json(result); + + // Do a check right away + checkNotifications(); + } catch (error) { + console.error("Error creating test notification:", error); + res.status(500).json({ error: "Internal Server Error" }); + } +}); + // Start the server and connect to the database app.listen(port, async () => { console.log(`Server is running on http://localhost:${port}`); diff --git a/src/notifications/notifications.ts b/src/notifications/notifications.ts index efa477b..e653bf1 100644 --- a/src/notifications/notifications.ts +++ b/src/notifications/notifications.ts @@ -68,4 +68,10 @@ async function deleteAccountNotification( return await AccountNotification.destroy({ where: { token } }); } -export { addAccountNotification, getAccountNotification, deleteAccountNotification }; +async function generateTestNotification( + jwt: string | undefined, +): Promise<[affectedCount: number]> { + return AccountNotification.update({ testQueued: true }, { where: { jwt } }); +} + +export { addAccountNotification, getAccountNotification, deleteAccountNotification, generateTestNotification }; diff --git a/src/notifications/service/notification_service.ts b/src/notifications/service/notification_service.ts index 5089f94..6243908 100644 --- a/src/notifications/service/notification_service.ts +++ b/src/notifications/service/notification_service.ts @@ -2,10 +2,11 @@ import cron from "node-cron"; import { Op } from "sequelize"; import { isAfter, subMinutes } from "date-fns"; import { LemmyHttp } from "lemmy-js-client"; +import { Sequelize } from "sequelize"; import AccountNotification from "../../database/models/account_notification"; import { provider, createAPNSNotification } from "../apns/apns"; -import { createUnifiedPushNotification, sendUnifiedPushNotification } from "../unifiedpush/unifiedpush"; +import { createUnifiedPushNotification, sendTestUnifiedPushNotification, sendUnifiedPushNotification } from "../unifiedpush/unifiedpush"; // The interval in minutes to check for new notifications const INTERVAL = 1; @@ -147,6 +148,40 @@ async function checkUnreadMessages( } } +async function checkTests(notification: AccountNotification) { + if (notification.get("testQueued") as boolean) { + console.log('Found 1 test queued'); + + const notificationType = notification.get("type") as string; + const token = notification.get("token") as string; + + switch (notificationType) { + case "apn": + // TODO: Implement this for APN + case "unifiedPush": + await sendTestUnifiedPushNotification(token); + break; + default: + break; + } + } + + if (notification.get("delayedTestQueued") as boolean) { + const notificationType = notification.get("type") as string; + const token = notification.get("token") as string; + + switch (notificationType) { + case "apn": + // TODO: Implement this for APN + case "unifiedPush": + await sendTestUnifiedPushNotification(token); + break; + default: + break; + } + } +} + /** * The main function that checks for new notifications. This is triggered from a cron job and is ran at every `INTERVAL` minutes. */ @@ -154,7 +189,7 @@ const checkNotifications = async () => { triggerDateTime = new Date(); const notifications = await AccountNotification.findAll({ - where: { timestamp: { [Op.lt]: subMinutes(triggerDateTime, INTERVAL) } }, + where: Sequelize.or({ timestamp: { [Op.lt]: subMinutes(triggerDateTime, INTERVAL) } }, { testQueued: true }, { delayedTestQueued: true }), }); console.log("Found " + notifications.length + " notifications to check"); @@ -191,9 +226,10 @@ const checkNotifications = async () => { await checkUnreadReplies(client, notification); await checkUnreadMentions(client, notification); await checkUnreadMessages(client, notification); + await checkTests(notification); // Update the notification timestamp - await notification.update({ timestamp: triggerDateTime }); + await notification.update({ timestamp: triggerDateTime, testQueued: false, delayedTestQueued: false }); } catch (error) { console.error(error); } @@ -221,4 +257,4 @@ const notificationService = cron.schedule( } ); -export default notificationService; +export { notificationService, checkNotifications }; diff --git a/src/notifications/unifiedpush/unifiedpush.ts b/src/notifications/unifiedpush/unifiedpush.ts index eac75fe..f605b85 100644 --- a/src/notifications/unifiedpush/unifiedpush.ts +++ b/src/notifications/unifiedpush/unifiedpush.ts @@ -48,4 +48,19 @@ async function sendUnifiedPushNotification( }); } -export { createUnifiedPushNotification, sendUnifiedPushNotification }; +/** + * Sends the notification through UnifiedPush. + */ +async function sendTestUnifiedPushNotification( + token: string +) { + fetch(token, { + method: "POST", + body: 'test', + headers: { + "Content-type": "application/json; charset=UTF-8", + }, + }); +} + +export { createUnifiedPushNotification, sendUnifiedPushNotification, sendTestUnifiedPushNotification };