backfill_suppress_expired_token_notifications.mjs 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. // One-off backfill: mark every personal access token that was already expired
  2. // at the moment the expiry-notification feature shipped, so the recurring
  3. // notifier (notify_expiring_tokens.mjs) does not blast a "your token has
  4. // expired" email to users about long-dead tokens.
  5. //
  6. // `notificationsSuppressedAt` is set ONLY by this script. It is intentionally
  7. // distinct from `lastNotifiedAt.expired` (which records actual sends) so the
  8. // two cases remain unambiguous in the data forever.
  9. //
  10. // Idempotent: re-running does nothing further once the flag is set.
  11. //
  12. // Pass --dry-run to count matching tokens without writing.
  13. import logger from '@overleaf/logger'
  14. import {
  15. db,
  16. READ_PREFERENCE_SECONDARY,
  17. } from '../../app/src/infrastructure/mongodb.mjs'
  18. import { scriptRunner } from '../lib/ScriptRunner.mjs'
  19. async function main() {
  20. const dryRun = process.argv.includes('--dry-run')
  21. const now = new Date()
  22. const cursor = db.oauthAccessTokens.find(
  23. {
  24. type: 'pat',
  25. accessTokenExpiresAt: { $lt: now },
  26. 'lastNotifiedAt.expired': { $exists: false },
  27. notificationsSuppressedAt: { $exists: false },
  28. },
  29. {
  30. projection: { _id: 1 },
  31. readPreference: READ_PREFERENCE_SECONDARY,
  32. }
  33. )
  34. let matched = 0
  35. for await (const doc of cursor) {
  36. if (!dryRun) {
  37. await db.oauthAccessTokens.updateOne(
  38. { _id: doc._id },
  39. { $set: { notificationsSuppressedAt: now } }
  40. )
  41. }
  42. matched++
  43. }
  44. if (dryRun) {
  45. logger.info(
  46. { matched },
  47. 'dry run: expired-token notifications would be suppressed'
  48. )
  49. } else {
  50. logger.info(
  51. { suppressed: matched },
  52. 'expired-token notifications suppressed'
  53. )
  54. }
  55. }
  56. try {
  57. await scriptRunner(main)
  58. process.exit(0)
  59. } catch (error) {
  60. logger.error(
  61. { err: error },
  62. 'backfill_suppress_expired_token_notifications failed'
  63. )
  64. process.exit(1)
  65. }