mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-27 02:51:57 +02:00
Merge pull request #5306 from overleaf/jpa-clear-institution-notifications
[web] add a new script for clearing institution notifications GitOrigin-RevId: d102b484c4fb9816832b48c2dfa66953bc996667
This commit is contained in:
@@ -35,6 +35,8 @@ app.delete(
|
||||
)
|
||||
app.delete('/user/:user_id', controller.removeNotificationKey)
|
||||
app.delete('/key/:key', controller.removeNotificationByKeyOnly)
|
||||
app.get('/key/:key/count', controller.countNotificationsByKeyOnly)
|
||||
app.delete('/key/:key/bulk', controller.deleteUnreadNotificationsByKeyOnlyBulk)
|
||||
|
||||
app.get('/status', (req, res) => res.send('notifications sharelatex up'))
|
||||
|
||||
|
||||
@@ -112,6 +112,22 @@ module.exports = Notifications = {
|
||||
db.notifications.updateOne(searchOps, updateOperation, callback)
|
||||
},
|
||||
|
||||
countNotificationsByKeyOnly(notificationKey, callback) {
|
||||
const searchOps = { key: notificationKey, templateKey: { $exists: true } }
|
||||
db.notifications.count(searchOps, callback)
|
||||
},
|
||||
|
||||
deleteUnreadNotificationsByKeyOnlyBulk(notificationKey, callback) {
|
||||
if (typeof notificationKey !== 'string') {
|
||||
throw new Error('refusing to bulk delete arbitrary notifications')
|
||||
}
|
||||
const searchOps = { key: notificationKey, templateKey: { $exists: true } }
|
||||
db.notifications.deleteMany(searchOps, (err, result) => {
|
||||
if (err) return callback(err)
|
||||
callback(null, result.deletedCount)
|
||||
})
|
||||
},
|
||||
|
||||
// hard delete of doc, rather than removing the templateKey
|
||||
deleteNotificationByKeyOnly(notification_key, callback) {
|
||||
const searchOps = { key: notification_key }
|
||||
|
||||
@@ -84,4 +84,29 @@ module.exports = {
|
||||
(err, notifications) => res.sendStatus(200)
|
||||
)
|
||||
},
|
||||
|
||||
countNotificationsByKeyOnly(req, res) {
|
||||
const notificationKey = req.params.key
|
||||
Notifications.countNotificationsByKeyOnly(notificationKey, (err, count) => {
|
||||
if (err) {
|
||||
logger.err({ err, notificationKey }, 'cannot count by key')
|
||||
return res.sendStatus(500)
|
||||
}
|
||||
res.json({ count })
|
||||
})
|
||||
},
|
||||
|
||||
deleteUnreadNotificationsByKeyOnlyBulk(req, res) {
|
||||
const notificationKey = req.params.key
|
||||
Notifications.deleteUnreadNotificationsByKeyOnlyBulk(
|
||||
notificationKey,
|
||||
(err, count) => {
|
||||
if (err) {
|
||||
logger.err({ err, notificationKey }, 'cannot bulk remove by key')
|
||||
return res.sendStatus(500)
|
||||
}
|
||||
res.json({ count })
|
||||
}
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const async = require('async')
|
||||
const { callbackify } = require('util')
|
||||
const { callbackify, promisify } = require('util')
|
||||
const { ObjectId } = require('mongodb')
|
||||
const Settings = require('@overleaf/settings')
|
||||
const {
|
||||
@@ -10,6 +10,7 @@ const FeaturesUpdater = require('../Subscription/FeaturesUpdater')
|
||||
const FeaturesHelper = require('../Subscription/FeaturesHelper')
|
||||
const UserGetter = require('../User/UserGetter')
|
||||
const NotificationsBuilder = require('../Notifications/NotificationsBuilder')
|
||||
const NotificationsHandler = require('../Notifications/NotificationsHandler')
|
||||
const SubscriptionLocator = require('../Subscription/SubscriptionLocator')
|
||||
const { Institution } = require('../../models/Institution')
|
||||
const { Subscription } = require('../../models/Subscription')
|
||||
@@ -186,6 +187,34 @@ async function checkInstitutionUsers(institutionId, emitNonProUserIds) {
|
||||
}
|
||||
|
||||
const InstitutionsManager = {
|
||||
clearInstitutionNotifications(institutionId, dryRun, callback) {
|
||||
function clear(key, cb) {
|
||||
const run = dryRun
|
||||
? NotificationsHandler.previewMarkAsReadByKeyOnlyBulk
|
||||
: NotificationsHandler.markAsReadByKeyOnlyBulk
|
||||
|
||||
run(key, cb)
|
||||
}
|
||||
|
||||
async.series(
|
||||
{
|
||||
ipMatcherAffiliation(cb) {
|
||||
const key = `ip-matched-affiliation-${institutionId}`
|
||||
clear(key, cb)
|
||||
},
|
||||
featuresUpgradedByAffiliation(cb) {
|
||||
const key = `features-updated-by=${institutionId}`
|
||||
clear(key, cb)
|
||||
},
|
||||
redundantPersonalSubscription(cb) {
|
||||
const key = `redundant-personal-subscription-${institutionId}`
|
||||
clear(key, cb)
|
||||
},
|
||||
},
|
||||
callback
|
||||
)
|
||||
},
|
||||
|
||||
refreshInstitutionUsers(institutionId, notify, callback) {
|
||||
const refreshFunction = notify ? refreshFeaturesAndNotify : refreshFeatures
|
||||
async.waterfall(
|
||||
@@ -313,6 +342,9 @@ var notifyUser = (user, affiliation, subscription, featuresChanged, callback) =>
|
||||
|
||||
InstitutionsManager.promises = {
|
||||
checkInstitutionUsers,
|
||||
clearInstitutionNotifications: promisify(
|
||||
InstitutionsManager.clearInstitutionNotifications
|
||||
),
|
||||
}
|
||||
|
||||
module.exports = InstitutionsManager
|
||||
|
||||
@@ -101,4 +101,38 @@ module.exports = {
|
||||
}
|
||||
makeRequest(opts, callback)
|
||||
},
|
||||
|
||||
previewMarkAsReadByKeyOnlyBulk(key, callback) {
|
||||
const opts = {
|
||||
uri: `${notificationsApi}/key/${key}/count`,
|
||||
method: 'GET',
|
||||
timeout: 10 * oneSecond,
|
||||
json: true,
|
||||
}
|
||||
makeRequest(opts, (err, res, body) => {
|
||||
if (err) return callback(err)
|
||||
if (res.statusCode !== 200) {
|
||||
return callback(
|
||||
new Error('cannot preview bulk delete notification: ' + key)
|
||||
)
|
||||
}
|
||||
callback(null, (body && body.count) || 0)
|
||||
})
|
||||
},
|
||||
|
||||
markAsReadByKeyOnlyBulk(key, callback) {
|
||||
const opts = {
|
||||
uri: `${notificationsApi}/key/${key}/bulk`,
|
||||
method: 'DELETE',
|
||||
timeout: 10 * oneSecond,
|
||||
json: true,
|
||||
}
|
||||
makeRequest(opts, (err, res, body) => {
|
||||
if (err) return callback(err)
|
||||
if (res.statusCode !== 200) {
|
||||
return callback(new Error('cannot bulk delete notification: ' + key))
|
||||
}
|
||||
callback(null, (body && body.count) || 0)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
47
services/web/scripts/clear_institution_notifications.js
Normal file
47
services/web/scripts/clear_institution_notifications.js
Normal file
@@ -0,0 +1,47 @@
|
||||
const { promisify } = require('util')
|
||||
const InstitutionsManager = require('../app/src/Features/Institutions/InstitutionsManager')
|
||||
const sleep = promisify(setTimeout)
|
||||
|
||||
async function main() {
|
||||
const institutionId = parseInt(process.argv[2])
|
||||
if (isNaN(institutionId)) throw new Error('No institution id')
|
||||
const dryRun = process.argv.includes('--dry-run')
|
||||
|
||||
console.log('Deleting notifications of institution', institutionId)
|
||||
|
||||
const preview = await InstitutionsManager.promises.clearInstitutionNotifications(
|
||||
institutionId,
|
||||
true
|
||||
)
|
||||
console.log('--- Preview ---')
|
||||
console.log(JSON.stringify(preview, null, 4))
|
||||
console.log('---------------')
|
||||
|
||||
if (dryRun) {
|
||||
console.log('Exiting early due to --dry-run flag')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('Exit in the next 10s in case these numbers are off.')
|
||||
await sleep(10 * 1000)
|
||||
|
||||
const cleared = await InstitutionsManager.promises.clearInstitutionNotifications(
|
||||
institutionId,
|
||||
false
|
||||
)
|
||||
console.log('--- Cleared ---')
|
||||
console.log(JSON.stringify(cleared, null, 4))
|
||||
console.log('---------------')
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main()
|
||||
.then(() => {
|
||||
console.log('Done.')
|
||||
process.exit(0)
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user