| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- /**
- * This script cleans up active projects that were expired. It's meant to be used once.
- *
- * See https://github.com/overleaf/internal/issues/4532
- */
- const { db, waitForDb } = require('../app/src/infrastructure/mongodb')
- const Errors = require('../app/src/Features/Errors/Errors')
- const FileStoreHandler = require('../app/src/Features/FileStore/FileStoreHandler')
- const ProjectEntityMongoUpdateHandler = require('../app/src/Features/Project/ProjectEntityMongoUpdateHandler')
- const { batchedUpdate } = require('./helpers/batchedUpdate')
- const DRY_RUN = process.env.DRY_RUN !== 'false'
- waitForDb()
- .then(main)
- .then(() => {
- process.exitCode = 0
- })
- .catch(error => {
- console.error({ error })
- process.exitCode = 1
- })
- .finally(() => {
- process.exit()
- })
- async function main() {
- await batchedUpdate(
- 'deletedProjects',
- { project: null },
- processDeletedProjects,
- {
- 'deleterData.deletedProjectId': 1,
- }
- )
- if (DRY_RUN) {
- console.log(
- '\nThis was a dry run. Re-run with DRY_RUN=false to delete broken refs and projects.'
- )
- }
- }
- async function processDeletedProjects(collection, deletedProjects) {
- const projectIds = deletedProjects.map(x => x.deleterData.deletedProjectId)
- const activeProjects = await db.projects
- .find(
- { _id: { $in: projectIds } },
- { projection: { _id: 1, rootFolder: 1 } }
- )
- .toArray()
- for (const activeProject of activeProjects) {
- await cleanupProject(activeProject)
- }
- }
- async function cleanupProject(project) {
- const { docIds, fileIds } = findRefsInFolder(project.rootFolder[0])
- const badDocIds = []
- const badFileIds = []
- for (const docId of docIds) {
- if (!(await docExists(docId))) {
- badDocIds.push(docId)
- }
- }
- for (const fileId of fileIds) {
- if (!(await fileExists(project._id, fileId))) {
- badFileIds.push(fileId)
- }
- }
- if (badDocIds.length === 0 && badFileIds.length === 0) {
- return
- }
- if (
- badDocIds.length === docIds.length &&
- badFileIds.length === fileIds.length
- ) {
- console.log(`Deleting project ${project._id}. All refs are bad.`)
- if (!DRY_RUN) {
- await db.projects.removeOne({ _id: project._id })
- }
- return
- }
- console.log(`Cleaning project ${project._id}:`)
- for (const docId of badDocIds) {
- console.log(` * Deleting bad doc ref ${docId}`)
- if (!DRY_RUN) {
- await ProjectEntityMongoUpdateHandler.promises.deleteEntity(
- project._id,
- docId,
- 'doc'
- )
- }
- }
- for (const fileId of badFileIds) {
- console.log(` * Deleting bad file ref ${fileId}`)
- if (!DRY_RUN) {
- await ProjectEntityMongoUpdateHandler.promises.deleteEntity(
- project._id,
- fileId,
- 'file'
- )
- }
- }
- }
- function findRefsInFolder(folder) {
- let docIds = folder.docs.map(doc => doc._id)
- let fileIds = folder.fileRefs.map(file => file._id)
- for (const subfolder of folder.folders) {
- const subrefs = findRefsInFolder(subfolder)
- docIds = docIds.concat(subrefs.docIds)
- fileIds = fileIds.concat(subrefs.fileIds)
- }
- return { docIds, fileIds }
- }
- async function docExists(docId) {
- const doc = await db.docs.findOne({ _id: docId })
- return doc != null
- }
- async function fileExists(projectId, fileId) {
- try {
- // Getting the file size to avoid downloading the whole file
- await FileStoreHandler.promises.getFileSize(projectId, fileId)
- } catch (err) {
- if (err instanceof Errors.NotFoundError) {
- return false
- }
- throw err
- }
- return true
- }
|