unset_allow_downgrade.js 3.6 KB

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