clear_sessions_2fa.mjs 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import { promisify, promiseMapWithLimit } from '@overleaf/promise-utils'
  2. import UserSessionsRedis from '../app/src/Features/User/UserSessionsRedis.js'
  3. import minimist from 'minimist'
  4. import { scriptRunner } from './lib/ScriptRunner.mjs'
  5. const rClient = UserSessionsRedis.client()
  6. const args = minimist(process.argv.slice(2))
  7. const CURSOR = args.cursor
  8. const COMMIT = args.commit === 'true'
  9. const CONCURRENCY = parseInt(args.concurrency, 10) || 50
  10. const LOG_EVERY_IN_S = parseInt(args['log-every-in-s'], 10) || 5
  11. function shouldDelete(session) {
  12. if (session.twoFactorAuthenticationPendingUser) {
  13. // twoFactorAuthenticationPendingUserId migration
  14. return true
  15. }
  16. // default: keep
  17. return false
  18. }
  19. async function processSession(key) {
  20. if (!key || !key.startsWith('sess:')) {
  21. throw new Error(`unexpected session key: ${key}`)
  22. }
  23. const blob = await rClient.get(key)
  24. if (!blob) return false // expired or deleted
  25. const session = JSON.parse(blob)
  26. if (shouldDelete(session)) {
  27. const deleteLabel = COMMIT ? 'delete' : 'would delete'
  28. console.warn(deleteLabel, key)
  29. if (COMMIT) {
  30. await rClient.del(key)
  31. }
  32. return true
  33. }
  34. return false
  35. }
  36. async function main() {
  37. console.warn({ COMMIT, CONCURRENCY, CURSOR, LOG_EVERY_IN_S })
  38. console.warn('starting in 10s')
  39. await promisify(setTimeout)(10_000)
  40. let processed = 0
  41. let deleted = 0
  42. function logProgress() {
  43. const deletedLabel = COMMIT ? 'deleted' : 'would have deleted'
  44. console.log(
  45. `processed ${processed} | ${deletedLabel} ${deleted} | cursor ${cursor}`
  46. )
  47. }
  48. let cursor = CURSOR
  49. let lastLog = 0
  50. while (cursor !== '0') {
  51. let keys
  52. ;[cursor, keys] = await rClient.scan(cursor || 0, 'MATCH', 'sess:*')
  53. const results = await promiseMapWithLimit(CONCURRENCY, keys, processSession)
  54. processed += keys.length
  55. for (const r of results) {
  56. if (r) deleted++
  57. }
  58. if (Date.now() - lastLog >= LOG_EVERY_IN_S * 1000) {
  59. logProgress()
  60. lastLog = Date.now()
  61. }
  62. }
  63. logProgress()
  64. console.log('Done.')
  65. await rClient.disconnect()
  66. }
  67. try {
  68. await scriptRunner(main)
  69. } catch (error) {
  70. console.error(error)
  71. process.exit(1)
  72. }