delete_dangling_comments.mjs 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. // @ts-check
  2. import minimist from 'minimist'
  3. import ChatApiHandler from '../app/src/Features/Chat/ChatApiHandler.mjs'
  4. import DocumentUpdaterHandler from '../app/src/Features/DocumentUpdater/DocumentUpdaterHandler.mjs'
  5. import DocstoreManager from '../app/src/Features/Docstore/DocstoreManager.mjs'
  6. import HistoryManager from '../app/src/Features/History/HistoryManager.mjs'
  7. import { db, ObjectId } from '../app/src/infrastructure/mongodb.mjs'
  8. const OPTS = parseArgs()
  9. function usage() {
  10. console.error(
  11. 'Usage: node delete_dangling_comments.mjs [--commit] PROJECT_ID...'
  12. )
  13. }
  14. function parseArgs() {
  15. const args = minimist(process.argv.slice(2), {
  16. boolean: ['commit'],
  17. })
  18. if (args._.length === 0) {
  19. usage()
  20. process.exit(0)
  21. }
  22. return {
  23. projectIds: args._,
  24. commit: args.commit,
  25. }
  26. }
  27. /**
  28. * @param {any} projectId
  29. */
  30. async function processProject(projectId) {
  31. console.log(`Processing project ${projectId}...`)
  32. await DocumentUpdaterHandler.promises.flushProjectToMongoAndDelete(projectId)
  33. const docRanges = await DocstoreManager.promises.getAllRanges(projectId)
  34. const threads = await ChatApiHandler.promises.getThreads(projectId)
  35. const threadIds = new Set(Object.keys(threads))
  36. let commentsDeleted = 0
  37. for (const doc of docRanges) {
  38. const commentsDeletedInDoc = await processDoc(projectId, doc, threadIds)
  39. commentsDeleted += commentsDeletedInDoc
  40. }
  41. if (OPTS.commit) {
  42. console.log(`${commentsDeleted} comments deleted`)
  43. if (commentsDeleted > 0) {
  44. console.log(`Resyncing history for project ${projectId}`)
  45. await HistoryManager.promises.resyncProject(projectId)
  46. }
  47. }
  48. }
  49. /**
  50. * @param {any} projectId
  51. * @param {any} doc
  52. * @param {any} threadIds
  53. */
  54. async function processDoc(projectId, doc, threadIds) {
  55. let commentsDeleted = 0
  56. for (const comment of doc.ranges?.comments ?? []) {
  57. const threadId = comment.op.t
  58. if (!threadIds.has(threadId)) {
  59. if (OPTS.commit) {
  60. console.log(`Deleting dangling comment ${comment.op.t}...`)
  61. await deleteComment(doc._id, threadId)
  62. commentsDeleted += 1
  63. } else {
  64. console.log(`Would delete dangling comment ${comment.op.t}...`)
  65. }
  66. }
  67. }
  68. return commentsDeleted
  69. }
  70. /**
  71. * @param {any} docId
  72. * @param {any} threadId
  73. */
  74. async function deleteComment(docId, threadId) {
  75. await db.docs.updateOne(
  76. { _id: new ObjectId(docId) },
  77. {
  78. $pull: { 'ranges.comments': { 'op.t': new ObjectId(threadId) } },
  79. }
  80. )
  81. }
  82. // Main loop
  83. for (const projectId of OPTS.projectIds) {
  84. await processProject(projectId)
  85. }
  86. if (!OPTS.commit) {
  87. console.log('This was a dry run. Rerun with --commit to apply changes')
  88. }
  89. process.exit(0)