Notifications.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import logger from '@overleaf/logger'
  2. import { db, ObjectId } from './mongodb.js'
  3. export default {
  4. getUserNotifications(userId, callback) {
  5. if (callback == null) {
  6. callback = function () {}
  7. }
  8. const query = {
  9. user_id: new ObjectId(userId),
  10. templateKey: { $exists: true },
  11. }
  12. db.notifications.find(query).toArray(callback)
  13. },
  14. _countExistingNotifications(userId, notification, callback) {
  15. if (callback == null) {
  16. callback = function () {}
  17. }
  18. const query = {
  19. user_id: new ObjectId(userId),
  20. key: notification.key,
  21. }
  22. return db.notifications.count(query, function (err, count) {
  23. if (err != null) {
  24. return callback(err)
  25. }
  26. return callback(null, count)
  27. })
  28. },
  29. addNotification(userId, notification, callback) {
  30. return this._countExistingNotifications(
  31. userId,
  32. notification,
  33. function (err, count) {
  34. if (err != null) {
  35. return callback(err)
  36. }
  37. if (count !== 0 && !notification.forceCreate) {
  38. return callback()
  39. }
  40. const doc = {
  41. user_id: new ObjectId(userId),
  42. key: notification.key,
  43. messageOpts: notification.messageOpts,
  44. templateKey: notification.templateKey,
  45. }
  46. // TTL index on the optional `expires` field, which should arrive as an iso date-string, corresponding to
  47. // a datetime in the future when the document should be automatically removed.
  48. // in Mongo, TTL indexes only work on date fields, and ignore the document when that field is missing
  49. // see `README.md` for instruction on creating TTL index
  50. if (notification.expires != null) {
  51. try {
  52. doc.expires = new Date(notification.expires)
  53. const _testValue = doc.expires.toISOString()
  54. } catch (error) {
  55. err = error
  56. logger.error(
  57. { userId, expires: notification.expires },
  58. 'error converting `expires` field to Date'
  59. )
  60. return callback(err)
  61. }
  62. }
  63. db.notifications.updateOne(
  64. { user_id: doc.user_id, key: notification.key },
  65. { $set: doc },
  66. { upsert: true },
  67. callback
  68. )
  69. }
  70. )
  71. },
  72. removeNotificationId(userId, notificationId, callback) {
  73. const searchOps = {
  74. user_id: new ObjectId(userId),
  75. _id: new ObjectId(notificationId),
  76. }
  77. const updateOperation = { $unset: { templateKey: true, messageOpts: true } }
  78. db.notifications.updateOne(searchOps, updateOperation, callback)
  79. },
  80. removeNotificationKey(userId, notificationKey, callback) {
  81. const searchOps = {
  82. user_id: new ObjectId(userId),
  83. key: notificationKey,
  84. }
  85. const updateOperation = { $unset: { templateKey: true } }
  86. db.notifications.updateOne(searchOps, updateOperation, callback)
  87. },
  88. removeNotificationByKeyOnly(notificationKey, callback) {
  89. const searchOps = { key: notificationKey }
  90. const updateOperation = { $unset: { templateKey: true } }
  91. db.notifications.updateOne(searchOps, updateOperation, callback)
  92. },
  93. countNotificationsByKeyOnly(notificationKey, callback) {
  94. const searchOps = { key: notificationKey, templateKey: { $exists: true } }
  95. db.notifications.count(searchOps, callback)
  96. },
  97. deleteUnreadNotificationsByKeyOnlyBulk(notificationKey, callback) {
  98. if (typeof notificationKey !== 'string') {
  99. throw new Error('refusing to bulk delete arbitrary notifications')
  100. }
  101. const searchOps = { key: notificationKey, templateKey: { $exists: true } }
  102. db.notifications.deleteMany(searchOps, (err, result) => {
  103. if (err) return callback(err)
  104. callback(null, result.deletedCount)
  105. })
  106. },
  107. // hard delete of doc, rather than removing the templateKey
  108. deleteNotificationByKeyOnly(notificationKey, callback) {
  109. const searchOps = { key: notificationKey }
  110. db.notifications.deleteOne(searchOps, callback)
  111. },
  112. }