InactiveProjectManager.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. const OError = require('@overleaf/o-error')
  2. const logger = require('@overleaf/logger')
  3. const DocstoreManager = require('../Docstore/DocstoreManager')
  4. const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
  5. const ProjectGetter = require('../Project/ProjectGetter')
  6. const ProjectUpdateHandler = require('../Project/ProjectUpdateHandler')
  7. const { Project } = require('../../models/Project')
  8. const { ObjectId } = require('mongodb-legacy')
  9. const Modules = require('../../infrastructure/Modules')
  10. const { READ_PREFERENCE_SECONDARY } = require('../../infrastructure/mongodb')
  11. const { callbackifyAll } = require('@overleaf/promise-utils')
  12. const Metrics = require('@overleaf/metrics')
  13. const MILISECONDS_IN_DAY = 86400000
  14. const InactiveProjectManager = {
  15. async reactivateProjectIfRequired(projectId) {
  16. let project
  17. try {
  18. project = await ProjectGetter.promises.getProject(projectId, {
  19. active: true,
  20. })
  21. } catch (err) {
  22. OError.tag(err, 'error getting project', {
  23. project_id: projectId,
  24. })
  25. throw err
  26. }
  27. logger.debug(
  28. { projectId, active: project.active },
  29. 'seeing if need to reactivate project'
  30. )
  31. if (project.active) {
  32. return
  33. }
  34. try {
  35. await DocstoreManager.promises.unarchiveProject(projectId)
  36. } catch (err) {
  37. OError.tag(err, 'error reactivating project in docstore', {
  38. project_id: projectId,
  39. })
  40. throw err
  41. }
  42. await ProjectUpdateHandler.promises.markAsActive(projectId)
  43. },
  44. async deactivateOldProjects(limit, daysOld) {
  45. if (limit == null) {
  46. limit = 10
  47. }
  48. if (daysOld == null) {
  49. daysOld = 360
  50. }
  51. const oldProjectDate = new Date() - MILISECONDS_IN_DAY * daysOld
  52. let projects
  53. try {
  54. // use $not $gt to catch non-opened projects where lastOpened is null
  55. projects = await Project.find({
  56. lastOpened: { $not: { $gt: oldProjectDate } },
  57. })
  58. .where('_id')
  59. .lt(ObjectId.createFromTime(oldProjectDate / 1000))
  60. .where('active')
  61. .equals(true)
  62. .select('_id')
  63. .limit(limit)
  64. .read(READ_PREFERENCE_SECONDARY)
  65. .exec()
  66. } catch (err) {
  67. logger.err({ err }, 'could not get projects for deactivating')
  68. }
  69. logger.debug(
  70. { numberOfProjects: projects && projects.length },
  71. 'deactivating projects'
  72. )
  73. for (const project of projects) {
  74. try {
  75. await InactiveProjectManager.deactivateProject(project._id)
  76. } catch (err) {
  77. logger.err(
  78. { projectId: project._id, err },
  79. 'unable to deactivate project'
  80. )
  81. }
  82. }
  83. return projects
  84. },
  85. async deactivateProject(projectId) {
  86. logger.debug({ projectId }, 'deactivating inactive project')
  87. // ensure project is removed from document updater (also flushes updates to history)
  88. try {
  89. await DocumentUpdaterHandler.promises.flushProjectToMongoAndDelete(
  90. projectId
  91. )
  92. } catch (err) {
  93. logger.warn(
  94. { err, projectId },
  95. 'error flushing project to mongo when archiving'
  96. )
  97. Metrics.inc('inactive-project', 1, {
  98. method: 'archive',
  99. status: 'flush-error',
  100. })
  101. throw err
  102. }
  103. await Modules.promises.hooks.fire('deactivateProject', projectId)
  104. // now archive the project and mark it as inactive
  105. try {
  106. await DocstoreManager.promises.archiveProject(projectId)
  107. await ProjectUpdateHandler.promises.markAsInactive(projectId)
  108. } catch (err) {
  109. logger.warn({ err, projectId }, 'error deactivating project')
  110. throw err
  111. }
  112. },
  113. }
  114. module.exports = {
  115. ...callbackifyAll(InactiveProjectManager),
  116. promises: InactiveProjectManager,
  117. }