unset_allow_downgrade.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. const { promisify } = require('util')
  2. const { ObjectId } = require('mongodb')
  3. const {
  4. db,
  5. waitForDb,
  6. READ_PREFERENCE_SECONDARY,
  7. } = require('../../app/src/infrastructure/mongodb')
  8. const sleep = promisify(setTimeout)
  9. const _ = require('lodash')
  10. const NOW_IN_S = Date.now() / 1000
  11. const ONE_WEEK_IN_S = 60 * 60 * 24 * 7
  12. const TEN_SECONDS = 10 * 1000
  13. function getSecondsFromObjectId(id) {
  14. return id.getTimestamp().getTime() / 1000
  15. }
  16. async function main(options) {
  17. if (!options) {
  18. options = {}
  19. }
  20. _.defaults(options, {
  21. projectId: process.env.PROJECT_ID,
  22. dryRun: process.env.DRY_RUN !== 'false',
  23. verboseLogging: process.env.VERBOSE_LOGGING === 'true',
  24. firstProjectId: process.env.FIRST_PROJECT_ID
  25. ? ObjectId(process.env.FIRST_PROJECT_ID)
  26. : ObjectId('4b3d3b3d0000000000000000'), // timestamped to 2010-01-01T00:01:01.000Z
  27. incrementByS: parseInt(process.env.INCREMENT_BY_S, 10) || ONE_WEEK_IN_S,
  28. batchSize: parseInt(process.env.BATCH_SIZE, 10) || 1000,
  29. stopAtS: parseInt(process.env.STOP_AT_S, 10) || NOW_IN_S,
  30. letUserDoubleCheckInputsFor:
  31. parseInt(process.env.LET_USER_DOUBLE_CHECK_INPUTS_FOR, 10) || TEN_SECONDS,
  32. })
  33. if (options.projectId) {
  34. await waitForDb()
  35. const { modifiedCount } = await db.projects.updateOne(
  36. {
  37. _id: ObjectId(options.projectId),
  38. 'overleaf.history.allowDowngrade': true,
  39. },
  40. { $unset: { 'overleaf.history.allowDowngrade': 1 } }
  41. )
  42. console.log(`modifiedCount: ${modifiedCount}`)
  43. process.exit(0)
  44. }
  45. await letUserDoubleCheckInputs(options)
  46. await waitForDb()
  47. let startId = options.firstProjectId
  48. let totalProcessed = 0
  49. while (getSecondsFromObjectId(startId) <= options.stopAtS) {
  50. let batchProcessed = 0
  51. const end = getSecondsFromObjectId(startId) + options.incrementByS
  52. let endId = ObjectId.createFromTime(end)
  53. const query = {
  54. _id: {
  55. // include edge
  56. $gte: startId,
  57. // exclude edge
  58. $lt: endId,
  59. },
  60. 'overleaf.history.allowDowngrade': true,
  61. }
  62. const projects = await db.projects
  63. .find(query, { readPreference: READ_PREFERENCE_SECONDARY })
  64. .project({ _id: 1 })
  65. .limit(options.batchSize)
  66. .toArray()
  67. if (projects.length) {
  68. const projectIds = projects.map(project => project._id)
  69. if (options.verboseLogging) {
  70. console.log(
  71. `Processing projects with ids: ${JSON.stringify(projectIds)}`
  72. )
  73. } else {
  74. console.log(`Processing ${projects.length} projects`)
  75. }
  76. if (!options.dryRun) {
  77. await db.projects.updateMany(
  78. { _id: { $in: projectIds } },
  79. { $unset: { 'overleaf.history.allowDowngrade': 1 } }
  80. )
  81. } else {
  82. console.log(
  83. `skipping update of ${projectIds.length} projects in dry-run mode`
  84. )
  85. }
  86. totalProcessed += projectIds.length
  87. batchProcessed += projectIds.length
  88. if (projects.length === options.batchSize) {
  89. endId = projects[projects.length - 1]._id
  90. }
  91. }
  92. console.error(
  93. `Processed ${batchProcessed} from ${startId} until ${endId} (${totalProcessed} processed in total)`
  94. )
  95. startId = endId
  96. }
  97. }
  98. async function letUserDoubleCheckInputs(options) {
  99. console.error('Options:', JSON.stringify(options, null, 2))
  100. console.error(
  101. 'Waiting for you to double check inputs for',
  102. options.letUserDoubleCheckInputsFor,
  103. 'ms'
  104. )
  105. await sleep(options.letUserDoubleCheckInputsFor)
  106. }
  107. module.exports = main
  108. if (require.main === module) {
  109. main()
  110. .then(() => {
  111. console.error('Done.')
  112. process.exit(0)
  113. })
  114. .catch(error => {
  115. console.error({ error })
  116. process.exit(1)
  117. })
  118. }