Files
overleaf-cep/services/notifications/app/js/Notifications.js
Andrew Rumble f0bd9b57b8 Convert to async/await
GitOrigin-RevId: 45c139a64c848650f1f51e64e8ebd233211241fb
2025-09-12 08:07:30 +00:00

110 lines
3.6 KiB
JavaScript

import logger from '@overleaf/logger'
import { db, ObjectId } from './mongodb.js'
async function getUserNotifications(userId) {
const query = {
user_id: new ObjectId(userId),
templateKey: { $exists: true },
}
return await db.notifications.find(query).toArray()
}
async function _countExistingNotifications(userId, notification) {
const query = {
user_id: new ObjectId(userId),
key: notification.key,
}
return await db.notifications.count(query)
}
async function addNotification(userId, notification, callback) {
const count = await _countExistingNotifications(userId, notification)
if (count !== 0 && !notification.forceCreate) {
return
}
const doc = {
user_id: new ObjectId(userId),
key: notification.key,
messageOpts: notification.messageOpts,
templateKey: notification.templateKey,
}
// TTL index on the optional `expires` field, which should arrive as an iso date-string, corresponding to
// a datetime in the future when the document should be automatically removed.
// in Mongo, TTL indexes only work on date fields, and ignore the document when that field is missing
// see `README.md` for instruction on creating TTL index
if (notification.expires != null) {
try {
doc.expires = new Date(notification.expires)
// _testValue assignment will throw if `expires` is not a valid date
// eslint-disable-next-line no-unused-vars
const _testValue = doc.expires.toISOString()
} catch (error) {
logger.error(
{ userId, expires: notification.expires },
'error converting `expires` field to Date'
)
throw error
}
}
return await db.notifications.updateOne(
{ user_id: doc.user_id, key: notification.key },
{ $set: doc },
{ upsert: true }
)
}
async function removeNotificationId(userId, notificationId) {
const searchOps = {
user_id: new ObjectId(userId),
_id: new ObjectId(notificationId),
}
const updateOperation = { $unset: { templateKey: true, messageOpts: true } }
return await db.notifications.updateOne(searchOps, updateOperation)
}
async function removeNotificationKey(userId, notificationKey) {
const searchOps = {
user_id: new ObjectId(userId),
key: notificationKey,
}
const updateOperation = { $unset: { templateKey: true } }
return await db.notifications.updateOne(searchOps, updateOperation)
}
async function removeNotificationByKeyOnly(notificationKey) {
const searchOps = { key: notificationKey }
const updateOperation = { $unset: { templateKey: true } }
return await db.notifications.updateOne(searchOps, updateOperation)
}
async function countNotificationsByKeyOnly(notificationKey) {
const searchOps = { key: notificationKey, templateKey: { $exists: true } }
return await db.notifications.countDocuments(searchOps)
}
async function deleteUnreadNotificationsByKeyOnlyBulk(notificationKey) {
if (typeof notificationKey !== 'string') {
throw new Error('refusing to bulk delete arbitrary notifications')
}
const searchOps = { key: notificationKey, templateKey: { $exists: true } }
const result = await db.notifications.deleteMany(searchOps)
return result.deletedCount
}
// hard delete of doc, rather than removing the templateKey
async function deleteNotificationByKeyOnly(notificationKey) {
const searchOps = { key: notificationKey }
return await db.notifications.deleteOne(searchOps)
}
export default {
addNotification,
getUserNotifications,
removeNotificationId,
removeNotificationKey,
removeNotificationByKeyOnly,
countNotificationsByKeyOnly,
deleteUnreadNotificationsByKeyOnlyBulk,
deleteNotificationByKeyOnly,
}