TpdsUpdateHandler.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. const { promisify } = require('util')
  2. const UpdateMerger = require('./UpdateMerger')
  3. const logger = require('@overleaf/logger')
  4. const NotificationsBuilder = require('../Notifications/NotificationsBuilder')
  5. const ProjectCreationHandler = require('../Project/ProjectCreationHandler')
  6. const ProjectDeleter = require('../Project/ProjectDeleter')
  7. const ProjectGetter = require('../Project/ProjectGetter')
  8. const ProjectHelper = require('../Project/ProjectHelper')
  9. const ProjectRootDocManager = require('../Project/ProjectRootDocManager')
  10. const FileTypeManager = require('../Uploads/FileTypeManager')
  11. const CooldownManager = require('../Cooldown/CooldownManager')
  12. const Errors = require('../Errors/Errors')
  13. const Modules = require('../../infrastructure/Modules')
  14. const {
  15. BackgroundTaskTracker,
  16. } = require('../../infrastructure/GracefulShutdown')
  17. const ROOT_DOC_TIMEOUT_LENGTH = 30 * 1000
  18. const rootDocResets = new BackgroundTaskTracker('root doc resets')
  19. function newUpdate(userId, projectName, path, updateRequest, source, callback) {
  20. getOrCreateProject(userId, projectName, (err, project) => {
  21. if (err) {
  22. return callback(err)
  23. }
  24. if (project == null) {
  25. return callback()
  26. }
  27. CooldownManager.isProjectOnCooldown(
  28. project._id,
  29. (err, projectIsOnCooldown) => {
  30. if (err) {
  31. return callback(err)
  32. }
  33. if (projectIsOnCooldown) {
  34. return callback(
  35. new Errors.TooManyRequestsError('project on cooldown')
  36. )
  37. }
  38. FileTypeManager.shouldIgnore(path, (err, shouldIgnore) => {
  39. if (err) {
  40. return callback(err)
  41. }
  42. if (shouldIgnore) {
  43. return callback()
  44. }
  45. UpdateMerger.mergeUpdate(
  46. userId,
  47. project._id,
  48. path,
  49. updateRequest,
  50. source,
  51. callback
  52. )
  53. })
  54. }
  55. )
  56. })
  57. }
  58. function deleteUpdate(userId, projectName, path, source, callback) {
  59. logger.debug({ userId, filePath: path }, 'handling delete update from tpds')
  60. ProjectGetter.findUsersProjectsByName(
  61. userId,
  62. projectName,
  63. (err, projects) => {
  64. if (err) {
  65. return callback(err)
  66. }
  67. const activeProjects = projects.filter(
  68. project => !ProjectHelper.isArchivedOrTrashed(project, userId)
  69. )
  70. if (activeProjects.length === 0) {
  71. logger.debug(
  72. { userId, filePath: path, projectName },
  73. 'project not found from tpds update, ignoring folder or project'
  74. )
  75. return callback()
  76. }
  77. if (projects.length > 1) {
  78. // There is more than one project with that name, and one of them is
  79. // active (previous condition)
  80. return handleDuplicateProjects(userId, projectName, callback)
  81. }
  82. const project = activeProjects[0]
  83. if (path === '/') {
  84. logger.debug(
  85. { userId, filePath: path, projectName, project_id: project._id },
  86. 'project found for delete update, path is root so marking project as deleted'
  87. )
  88. ProjectDeleter.markAsDeletedByExternalSource(project._id, callback)
  89. } else {
  90. UpdateMerger.deleteUpdate(userId, project._id, path, source, err => {
  91. callback(err)
  92. })
  93. }
  94. }
  95. )
  96. }
  97. function getOrCreateProject(userId, projectName, callback) {
  98. ProjectGetter.findUsersProjectsByName(
  99. userId,
  100. projectName,
  101. (err, projects) => {
  102. if (err) {
  103. return callback(err)
  104. }
  105. if (projects.length === 0) {
  106. // No project with that name -- active, archived or trashed -- has been
  107. // found. Create one.
  108. return ProjectCreationHandler.createBlankProject(
  109. userId,
  110. projectName,
  111. (err, project) => {
  112. // have a crack at setting the root doc after a while, on creation
  113. // we won't have it yet, but should have been sent it it within 30
  114. // seconds
  115. rootDocResets.add()
  116. setTimeout(() => {
  117. ProjectRootDocManager.setRootDocAutomatically(project._id, () => {
  118. rootDocResets.done()
  119. })
  120. }, ROOT_DOC_TIMEOUT_LENGTH)
  121. callback(err, project)
  122. }
  123. )
  124. }
  125. const activeProjects = projects.filter(
  126. project => !ProjectHelper.isArchivedOrTrashed(project, userId)
  127. )
  128. if (activeProjects.length === 0) {
  129. // All projects with that name are archived or trashed. Ignore.
  130. return callback(null, null)
  131. }
  132. if (projects.length > 1) {
  133. // There is more than one project with that name, and one of them is
  134. // active (previous condition)
  135. return handleDuplicateProjects(userId, projectName, err => {
  136. if (err) {
  137. return callback(err)
  138. }
  139. callback(null, null)
  140. })
  141. }
  142. callback(err, activeProjects[0])
  143. }
  144. )
  145. }
  146. function handleDuplicateProjects(userId, projectName, callback) {
  147. Modules.hooks.fire('removeDropbox', userId, 'duplicate-projects', err => {
  148. if (err) {
  149. return callback(err)
  150. }
  151. NotificationsBuilder.dropboxDuplicateProjectNames(userId).create(
  152. projectName,
  153. callback
  154. )
  155. })
  156. }
  157. module.exports = {
  158. newUpdate,
  159. deleteUpdate,
  160. promises: {
  161. newUpdate: promisify(newUpdate),
  162. deleteUpdate: promisify(deleteUpdate),
  163. },
  164. }