From fc18709f0b8e0450101dd730e06b65422a03df40 Mon Sep 17 00:00:00 2001 From: emmanishikawa Date: Mon, 9 Feb 2026 21:29:07 -0800 Subject: [PATCH 1/8] created notification schema --- models/Notification.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 models/Notification.ts diff --git a/models/Notification.ts b/models/Notification.ts new file mode 100644 index 0000000..659eabb --- /dev/null +++ b/models/Notification.ts @@ -0,0 +1,17 @@ +import mongoose from 'mongoose'; +const { Schema } = mongoose; + +const notificationSchema = new Schema({ +_id: { type: String, required: true }, +type: { type: String, required: true }, +labId: { type: String, required: true }, +resourceId: { type: String, required: true }, +recipients: { + type: [String], + required: true +}, +createdAt: { type: Date, required: true, default: Date.now } +}); +const Notification = mongoose.models.Notification || mongoose.model('Notification', notificationSchema); + +export default Notification; \ No newline at end of file From 5a7cc4fc9332f661f29e13c5582078ae1182d204 Mon Sep 17 00:00:00 2001 From: Alex Meng <122071020+AlexMeng0831@users.noreply.github.com> Date: Tue, 10 Feb 2026 20:14:25 -0800 Subject: [PATCH 2/8] Create watchUpdates.ts --- services/notifications/watchUpdates.ts | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 services/notifications/watchUpdates.ts diff --git a/services/notifications/watchUpdates.ts b/services/notifications/watchUpdates.ts new file mode 100644 index 0000000..40b2ea8 --- /dev/null +++ b/services/notifications/watchUpdates.ts @@ -0,0 +1,34 @@ +import mongoose from "mongoose"; +import { connectToDatabase } from "@/lib/mongoose"; +import Notification from "@/models/Notification"; + +/** + * Starts a Change Stream watcher on the "products" collection. + * Inserts a DB_UPDATE notification whenever a product is updated. + */ +export async function startNotificationWatcher() { + await connectToDatabase(); + + const collection = mongoose.connection.collection("products"); + + const changeStream = collection.watch([], { + fullDocument: "updateLookup", + }); + + console.log("[notifications] watcher started"); + + for await (const change of changeStream) { + if (change.operationType !== "update") continue; + + const updatedDoc = change.fullDocument; + if (!updatedDoc) continue; + + await Notification.create({ + _id: `notif_${Date.now()}`, + type: "DB_UPDATE", + labId: updatedDoc.labId ?? "unknown", + resourceId: String(updatedDoc._id), + recipients: [], + }); + } +} From 893affcb40aa3569ab1e2887901e46516507a832 Mon Sep 17 00:00:00 2001 From: Alex Meng <122071020+AlexMeng0831@users.noreply.github.com> Date: Tue, 10 Feb 2026 20:14:52 -0800 Subject: [PATCH 3/8] Create index.ts --- services/notifications/index.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 services/notifications/index.ts diff --git a/services/notifications/index.ts b/services/notifications/index.ts new file mode 100644 index 0000000..55ec001 --- /dev/null +++ b/services/notifications/index.ts @@ -0,0 +1,4 @@ +import { startNotificationWatcher } from "./watchUpdates"; + +// Entry point for notification service +startNotificationWatcher(); From 04f12902de567148a512d2b6983f53dcdcd77ab7 Mon Sep 17 00:00:00 2001 From: emmanishikawa Date: Mon, 16 Feb 2026 21:02:17 -0800 Subject: [PATCH 4/8] fixed formatting --- models/Notification.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/models/Notification.ts b/models/Notification.ts index 659eabb..656dd93 100644 --- a/models/Notification.ts +++ b/models/Notification.ts @@ -2,16 +2,18 @@ import mongoose from 'mongoose'; const { Schema } = mongoose; const notificationSchema = new Schema({ -_id: { type: String, required: true }, -type: { type: String, required: true }, -labId: { type: String, required: true }, -resourceId: { type: String, required: true }, -recipients: { - type: [String], - required: true -}, -createdAt: { type: Date, required: true, default: Date.now } + _id: { type: String, required: true }, + type: { type: String, required: true }, + labId: { type: String, required: true }, + resourceId: { type: String, required: true }, + recipients: { + type: [String], + required: true + }, + createdAt: { type: Date, required: true, default: Date.now } }); -const Notification = mongoose.models.Notification || mongoose.model('Notification', notificationSchema); + +const Notification = mongoose.models.Notification || + mongoose.model('Notification', notificationSchema); export default Notification; \ No newline at end of file From de8b1d264dd2d6217f06f711e97a5c84a7d6668b Mon Sep 17 00:00:00 2001 From: emmanishikawa Date: Mon, 16 Feb 2026 21:07:46 -0800 Subject: [PATCH 5/8] remove console.log, changed products to items, added useful comments --- services/notifications/watchUpdates.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/services/notifications/watchUpdates.ts b/services/notifications/watchUpdates.ts index 40b2ea8..bc94060 100644 --- a/services/notifications/watchUpdates.ts +++ b/services/notifications/watchUpdates.ts @@ -9,17 +9,20 @@ import Notification from "@/models/Notification"; export async function startNotificationWatcher() { await connectToDatabase(); - const collection = mongoose.connection.collection("products"); + const collection = mongoose.connection.collection("items"); const changeStream = collection.watch([], { fullDocument: "updateLookup", }); - console.log("[notifications] watcher started"); - for await (const change of changeStream) { if (change.operationType !== "update") continue; + /** + * Get the updated document from the change stream + * If quantity is below the threshold, continue + * else send a notification + */ const updatedDoc = change.fullDocument; if (!updatedDoc) continue; From 3f2be2001d2fbfe59a1581377c468d077e66504f Mon Sep 17 00:00:00 2001 From: emmanishikawa Date: Tue, 17 Feb 2026 09:34:09 -0800 Subject: [PATCH 6/8] deleted id field, move labId to top, add roles field to recipients --- models/Notification.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/models/Notification.ts b/models/Notification.ts index 656dd93..c1b1aba 100644 --- a/models/Notification.ts +++ b/models/Notification.ts @@ -2,13 +2,13 @@ import mongoose from 'mongoose'; const { Schema } = mongoose; const notificationSchema = new Schema({ - _id: { type: String, required: true }, - type: { type: String, required: true }, labId: { type: String, required: true }, + type: { type: String, required: true }, resourceId: { type: String, required: true }, recipients: { - type: [String], - required: true + type: [String], + roles: { type: String }, + required: true }, createdAt: { type: Date, required: true, default: Date.now } }); From c65962176d154a95e1bec85f18ce2651daa8f3de Mon Sep 17 00:00:00 2001 From: emmanishikawa Date: Wed, 18 Feb 2026 17:33:41 -0800 Subject: [PATCH 7/8] updated comments --- services/notifications/watchUpdates.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/services/notifications/watchUpdates.ts b/services/notifications/watchUpdates.ts index bc94060..8c45217 100644 --- a/services/notifications/watchUpdates.ts +++ b/services/notifications/watchUpdates.ts @@ -18,14 +18,15 @@ export async function startNotificationWatcher() { for await (const change of changeStream) { if (change.operationType !== "update") continue; - /** - * Get the updated document from the change stream - * If quantity is below the threshold, continue - * else send a notification - */ + // Get the updated document from the change stream const updatedDoc = change.fullDocument; + + // If there’s no document, skip this iteration if (!updatedDoc) continue; + // TODO: Check the quantity in updatedDoc + // If quantity is below the threshold, continue + // Otherwise, send a notification await Notification.create({ _id: `notif_${Date.now()}`, type: "DB_UPDATE", From 8248fe00a3abf8ab926e8f8dcdcb5b8bf9329040 Mon Sep 17 00:00:00 2001 From: emmanishikawa Date: Fri, 20 Feb 2026 10:49:24 -0800 Subject: [PATCH 8/8] change recipients to list with role instead of type String --- models/Notification.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/models/Notification.ts b/models/Notification.ts index c1b1aba..a1b5b2a 100644 --- a/models/Notification.ts +++ b/models/Notification.ts @@ -5,11 +5,12 @@ const notificationSchema = new Schema({ labId: { type: String, required: true }, type: { type: String, required: true }, resourceId: { type: String, required: true }, - recipients: { - type: [String], - roles: { type: String }, - required: true - }, + recipients: [ + { + role: { type: String }, + required: true + } + ], createdAt: { type: Date, required: true, default: Date.now } });