ProjectGetter.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  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. /**
  74. * @return {Promise<any>}
  75. */
  76. async findAllUsersProjects(userId, fields) {
  77. const CollaboratorsGetter = require('../Collaborators/CollaboratorsGetter')
  78. const ownedProjects = await Project.find(
  79. { owner_ref: userId },
  80. fields
  81. ).exec()
  82. const projects =
  83. await CollaboratorsGetter.promises.getProjectsUserIsMemberOf(
  84. userId,
  85. fields
  86. )
  87. const result = {
  88. owned: ownedProjects || [],
  89. readAndWrite: projects.readAndWrite || [],
  90. readOnly: projects.readOnly || [],
  91. tokenReadAndWrite: projects.tokenReadAndWrite || [],
  92. tokenReadOnly: projects.tokenReadOnly || [],
  93. review: projects.review || [],
  94. }
  95. // Remove duplicate projects. The order of result values is determined by the order they occur.
  96. const tempAddedProjectsIds = new Set()
  97. const filteredProjects = Object.entries(result).reduce((prev, current) => {
  98. const [key, projects] = current
  99. prev[key] = []
  100. projects.forEach(project => {
  101. const projectId = project._id.toString()
  102. if (!tempAddedProjectsIds.has(projectId)) {
  103. prev[key].push(project)
  104. tempAddedProjectsIds.add(projectId)
  105. }
  106. })
  107. return prev
  108. }, {})
  109. return filteredProjects
  110. },
  111. /**
  112. * Return all projects with the given name that belong to the given user.
  113. *
  114. * Projects include the user's own projects as well as collaborations with
  115. * read/write access.
  116. */
  117. async findUsersProjectsByName(userId, projectName) {
  118. const allProjects = await ProjectGetter.findAllUsersProjects(
  119. userId,
  120. 'name archived trashed'
  121. )
  122. const { owned, readAndWrite } = allProjects
  123. const projects = owned.concat(readAndWrite)
  124. const lowerCasedProjectName = projectName.toLowerCase()
  125. return projects.filter(
  126. project => project.name.toLowerCase() === lowerCasedProjectName
  127. )
  128. },
  129. async getUsersDeletedProjects(userId) {
  130. return await DeletedProject.find({
  131. 'deleterData.deletedProjectOwnerId': userId,
  132. }).exec()
  133. },
  134. }
  135. module.exports = {
  136. ...callbackifyAll(ProjectGetter),
  137. promises: ProjectGetter,
  138. }