ProjectGetter.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. const { db } = require('../../infrastructure/mongodb')
  2. const { normalizeQuery } = require('../Helpers/Mongo')
  3. const OError = require('@overleaf/o-error')
  4. const { Project } = require('../../models/Project')
  5. const LockManager = require('../../infrastructure/LockManager')
  6. const { DeletedProject } = require('../../models/DeletedProject')
  7. const { callbackifyAll } = require('@overleaf/promise-utils')
  8. const ProjectGetter = {
  9. EXCLUDE_DEPTH: 8,
  10. async getProjectWithoutDocLines(projectId) {
  11. const excludes = {}
  12. for (let i = 1; i <= ProjectGetter.EXCLUDE_DEPTH; i++) {
  13. excludes[`rootFolder${Array(i).join('.folders')}.docs.lines`] = 0
  14. }
  15. return await ProjectGetter.getProject(projectId, excludes)
  16. },
  17. async getProjectWithOnlyFolders(projectId) {
  18. const excludes = {}
  19. for (let i = 1; i <= ProjectGetter.EXCLUDE_DEPTH; i++) {
  20. excludes[`rootFolder${Array(i).join('.folders')}.docs`] = 0
  21. excludes[`rootFolder${Array(i).join('.folders')}.fileRefs`] = 0
  22. }
  23. return await ProjectGetter.getProject(projectId, excludes)
  24. },
  25. async getProject(projectId, projection = {}) {
  26. if (projectId == null) {
  27. throw new Error('no project id provided')
  28. }
  29. if (typeof projection !== 'object') {
  30. throw new Error('projection is not an object')
  31. }
  32. if (projection.rootFolder || Object.keys(projection).length === 0) {
  33. const ProjectEntityMongoUpdateHandler = require('./ProjectEntityMongoUpdateHandler')
  34. return await LockManager.promises.runWithLock(
  35. ProjectEntityMongoUpdateHandler.LOCK_NAMESPACE,
  36. projectId,
  37. () => ProjectGetter.getProjectWithoutLock(projectId, projection)
  38. )
  39. } else {
  40. return await ProjectGetter.getProjectWithoutLock(projectId, projection)
  41. }
  42. },
  43. async getProjectWithoutLock(projectId, projection = {}) {
  44. if (projectId == null) {
  45. throw new Error('no project id provided')
  46. }
  47. if (typeof projection !== 'object') {
  48. throw new Error('projection is not an object')
  49. }
  50. const query = normalizeQuery(projectId)
  51. let project
  52. try {
  53. project = await db.projects.findOne(query, { projection })
  54. } catch (error) {
  55. OError.tag(error, 'error getting project', {
  56. query,
  57. projection,
  58. })
  59. throw error
  60. }
  61. return project
  62. },
  63. async getProjectIdByReadAndWriteToken(token) {
  64. const project = await Project.findOne(
  65. { 'tokens.readAndWrite': token },
  66. { _id: 1 }
  67. ).exec()
  68. if (project == null) {
  69. return
  70. }
  71. return project._id
  72. },
  73. async findAllUsersProjects(userId, fields) {
  74. const CollaboratorsGetter = require('../Collaborators/CollaboratorsGetter')
  75. const ownedProjects = await Project.find(
  76. { owner_ref: userId },
  77. fields
  78. ).exec()
  79. const projects =
  80. await CollaboratorsGetter.promises.getProjectsUserIsMemberOf(
  81. userId,
  82. fields
  83. )
  84. const result = {
  85. owned: ownedProjects || [],
  86. readAndWrite: projects.readAndWrite || [],
  87. readOnly: projects.readOnly || [],
  88. tokenReadAndWrite: projects.tokenReadAndWrite || [],
  89. tokenReadOnly: projects.tokenReadOnly || [],
  90. review: projects.review || [],
  91. }
  92. // Remove duplicate projects. The order of result values is determined by the order they occur.
  93. const tempAddedProjectsIds = new Set()
  94. const filteredProjects = Object.entries(result).reduce((prev, current) => {
  95. const [key, projects] = current
  96. prev[key] = []
  97. projects.forEach(project => {
  98. const projectId = project._id.toString()
  99. if (!tempAddedProjectsIds.has(projectId)) {
  100. prev[key].push(project)
  101. tempAddedProjectsIds.add(projectId)
  102. }
  103. })
  104. return prev
  105. }, {})
  106. return filteredProjects
  107. },
  108. /**
  109. * Return all projects with the given name that belong to the given user.
  110. *
  111. * Projects include the user's own projects as well as collaborations with
  112. * read/write access.
  113. */
  114. async findUsersProjectsByName(userId, projectName) {
  115. const allProjects = await ProjectGetter.findAllUsersProjects(
  116. userId,
  117. 'name archived trashed'
  118. )
  119. const { owned, readAndWrite } = allProjects
  120. const projects = owned.concat(readAndWrite)
  121. const lowerCasedProjectName = projectName.toLowerCase()
  122. return projects.filter(
  123. project => project.name.toLowerCase() === lowerCasedProjectName
  124. )
  125. },
  126. async getUsersDeletedProjects(userId) {
  127. return await DeletedProject.find({
  128. 'deleterData.deletedProjectOwnerId': userId,
  129. }).exec()
  130. },
  131. async getHistoryId(projectId) {
  132. const project = await this.getProject(projectId, {
  133. 'overleaf.history.id': 1,
  134. })
  135. const historyId = project?.overleaf?.history?.id
  136. if (!historyId) {
  137. throw new OError('project does not have a history id', { projectId })
  138. }
  139. return historyId
  140. },
  141. }
  142. module.exports = {
  143. ...callbackifyAll(ProjectGetter),
  144. promises: ProjectGetter,
  145. }