delete_dangling_file_refs.js 3.2 KB

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