delete_dangling_file_refs.mjs 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /**
  2. * This script deletes dangling doc and file refs in projects
  3. */
  4. import minimist from 'minimist'
  5. import mongodb from 'mongodb-legacy'
  6. import { db } from '../app/src/infrastructure/mongodb.js'
  7. import Errors from '../app/src/Features/Errors/Errors.js'
  8. import FileStoreHandler from '../app/src/Features/FileStore/FileStoreHandler.js'
  9. import ProjectEntityMongoUpdateHandler from '../app/src/Features/Project/ProjectEntityMongoUpdateHandler.js'
  10. import { iterablePaths } from '../app/src/Features/Project/IterablePath.js'
  11. const { ObjectId } = mongodb
  12. const OPTIONS = parseArgs()
  13. function parseArgs() {
  14. const argv = minimist(process.argv.slice(2), {
  15. boolean: ['dry-run'],
  16. default: { 'dry-run': true },
  17. })
  18. const dryRun = argv['dry-run']
  19. const projectIds = argv._
  20. if (projectIds.length === 0) {
  21. console.log(`Usage: ${process.argv[1]} [--no-dry-run] PROJECT_ID ...`)
  22. process.exit(0)
  23. }
  24. return { projectIds, dryRun }
  25. }
  26. async function main() {
  27. const projects = await getProjects()
  28. for (const project of projects) {
  29. await processProject(project)
  30. }
  31. if (OPTIONS.dryRun) {
  32. console.log(
  33. '\nThis was a dry run. Re-run with --no-dry-run to delete broken refs.'
  34. )
  35. }
  36. }
  37. async function getProjects() {
  38. const projectIds = OPTIONS.projectIds.map(id => new ObjectId(id))
  39. const projects = await db.projects
  40. .find(
  41. { _id: { $in: projectIds } },
  42. { projection: { _id: 1, rootFolder: 1 } }
  43. )
  44. .toArray()
  45. return projects
  46. }
  47. async function processProject(project) {
  48. console.log(`Processing project ${project._id}`)
  49. const { docIds, fileIds } = findRefsInFolder(project.rootFolder[0])
  50. for (const docId of docIds) {
  51. if (!(await docExists(docId))) {
  52. await deleteDoc(project._id, docId)
  53. }
  54. }
  55. for (const fileId of fileIds) {
  56. if (!(await fileExists(project._id, fileId))) {
  57. await deleteFile(project._id, fileId)
  58. }
  59. }
  60. }
  61. function findRefsInFolder(folder) {
  62. let docIds = folder.docs.map(doc => doc._id)
  63. let fileIds = folder.fileRefs.map(file => file._id)
  64. for (const subfolder of iterablePaths(folder, 'folders')) {
  65. const subrefs = findRefsInFolder(subfolder)
  66. docIds = docIds.concat(subrefs.docIds)
  67. fileIds = fileIds.concat(subrefs.fileIds)
  68. }
  69. return { docIds, fileIds }
  70. }
  71. async function docExists(docId) {
  72. const doc = await db.docs.findOne({ _id: docId })
  73. return doc != null
  74. }
  75. async function fileExists(projectId, fileId) {
  76. try {
  77. // Getting the file size to avoid downloading the whole file
  78. await FileStoreHandler.promises.getFileSize(projectId, fileId)
  79. } catch (err) {
  80. if (err instanceof Errors.NotFoundError) {
  81. return false
  82. }
  83. throw err
  84. }
  85. return true
  86. }
  87. async function deleteDoc(projectId, docId) {
  88. console.log(` * Deleting bad doc ref ${docId}`)
  89. if (!OPTIONS.dryRun) {
  90. await ProjectEntityMongoUpdateHandler.promises.deleteEntity(
  91. projectId,
  92. docId,
  93. 'doc',
  94. null // unset lastUpdatedBy
  95. )
  96. }
  97. }
  98. async function deleteFile(projectId, fileId) {
  99. console.log(` * Deleting bad file ref ${fileId}`)
  100. if (!OPTIONS.dryRun) {
  101. await ProjectEntityMongoUpdateHandler.promises.deleteEntity(
  102. projectId,
  103. fileId,
  104. 'file',
  105. null // unset lastUpdatedBy
  106. )
  107. }
  108. }
  109. try {
  110. await main()
  111. process.exit(0)
  112. } catch (error) {
  113. console.error({ error })
  114. process.exit(1)
  115. }