HttpController.coffee 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. DocumentManager = require "./DocumentManager"
  2. HistoryManager = require "./HistoryManager"
  3. ProjectManager = require "./ProjectManager"
  4. Errors = require "./Errors"
  5. logger = require "logger-sharelatex"
  6. Metrics = require "./Metrics"
  7. TWO_MEGABYTES = 2 * 1024 * 1024
  8. module.exports = HttpController =
  9. getDoc: (req, res, next = (error) ->) ->
  10. doc_id = req.params.doc_id
  11. project_id = req.params.project_id
  12. logger.log project_id: project_id, doc_id: doc_id, "getting doc via http"
  13. timer = new Metrics.Timer("http.getDoc")
  14. if req.query?.fromVersion?
  15. fromVersion = parseInt(req.query.fromVersion, 10)
  16. else
  17. fromVersion = -1
  18. DocumentManager.getDocAndRecentOpsWithLock project_id, doc_id, fromVersion, (error, lines, version, ops, ranges, pathname) ->
  19. timer.done()
  20. return next(error) if error?
  21. logger.log project_id: project_id, doc_id: doc_id, "got doc via http"
  22. if !lines? or !version?
  23. return next(new Errors.NotFoundError("document not found"))
  24. res.send JSON.stringify
  25. id: doc_id
  26. lines: lines
  27. version: version
  28. ops: ops
  29. ranges: ranges
  30. pathname: pathname
  31. _getTotalSizeOfLines: (lines) ->
  32. size = 0
  33. for line in lines
  34. size += (line.length + 1)
  35. return size
  36. getProjectDocsAndFlushIfOld: (req, res, next = (error) ->) ->
  37. project_id = req.params.project_id
  38. projectStateHash = req.query?.state
  39. # exclude is string of existing docs "id:version,id:version,..."
  40. excludeItems = req.query?.exclude?.split(',') or []
  41. logger.log project_id: project_id, exclude: excludeItems, "getting docs via http"
  42. timer = new Metrics.Timer("http.getAllDocs")
  43. excludeVersions = {}
  44. for item in excludeItems
  45. [id,version] = item?.split(':')
  46. excludeVersions[id] = version
  47. logger.log {project_id: project_id, projectStateHash: projectStateHash, excludeVersions: excludeVersions}, "excluding versions"
  48. ProjectManager.getProjectDocsAndFlushIfOld project_id, projectStateHash, excludeVersions, (error, result) ->
  49. timer.done()
  50. if error instanceof Errors.ProjectStateChangedError
  51. res.send 409 # conflict
  52. else if error?
  53. return next(error)
  54. else
  55. logger.log project_id: project_id, result: ("#{doc._id}:#{doc.v}" for doc in result), "got docs via http"
  56. res.send result
  57. clearProjectState: (req, res, next = (error) ->) ->
  58. project_id = req.params.project_id
  59. timer = new Metrics.Timer("http.clearProjectState")
  60. logger.log project_id: project_id, "clearing project state via http"
  61. ProjectManager.clearProjectState project_id, (error) ->
  62. timer.done()
  63. if error?
  64. return next(error)
  65. else
  66. res.send 200
  67. setDoc: (req, res, next = (error) ->) ->
  68. doc_id = req.params.doc_id
  69. project_id = req.params.project_id
  70. {lines, source, user_id, undoing} = req.body
  71. lineSize = HttpController._getTotalSizeOfLines(lines)
  72. if lineSize > TWO_MEGABYTES
  73. logger.log {project_id, doc_id, source, lineSize, user_id}, "document too large, returning 406 response"
  74. return res.send 406
  75. logger.log {project_id, doc_id, lines, source, user_id, undoing}, "setting doc via http"
  76. timer = new Metrics.Timer("http.setDoc")
  77. DocumentManager.setDocWithLock project_id, doc_id, lines, source, user_id, undoing, (error) ->
  78. timer.done()
  79. return next(error) if error?
  80. logger.log project_id: project_id, doc_id: doc_id, "set doc via http"
  81. res.send 204 # No Content
  82. flushDocIfLoaded: (req, res, next = (error) ->) ->
  83. doc_id = req.params.doc_id
  84. project_id = req.params.project_id
  85. logger.log project_id: project_id, doc_id: doc_id, "flushing doc via http"
  86. timer = new Metrics.Timer("http.flushDoc")
  87. DocumentManager.flushDocIfLoadedWithLock project_id, doc_id, (error) ->
  88. timer.done()
  89. return next(error) if error?
  90. logger.log project_id: project_id, doc_id: doc_id, "flushed doc via http"
  91. res.send 204 # No Content
  92. flushAndDeleteDoc: (req, res, next = (error) ->) ->
  93. doc_id = req.params.doc_id
  94. project_id = req.params.project_id
  95. logger.log project_id: project_id, doc_id: doc_id, "deleting doc via http"
  96. timer = new Metrics.Timer("http.deleteDoc")
  97. DocumentManager.flushAndDeleteDocWithLock project_id, doc_id, (error) ->
  98. timer.done()
  99. # There is no harm in flushing project history if the previous call
  100. # failed and sometimes it is required
  101. HistoryManager.flushProjectChangesAsync project_id
  102. return next(error) if error?
  103. logger.log project_id: project_id, doc_id: doc_id, "deleted doc via http"
  104. res.send 204 # No Content
  105. flushProject: (req, res, next = (error) ->) ->
  106. project_id = req.params.project_id
  107. logger.log project_id: project_id, "flushing project via http"
  108. timer = new Metrics.Timer("http.flushProject")
  109. ProjectManager.flushProjectWithLocks project_id, (error) ->
  110. timer.done()
  111. return next(error) if error?
  112. logger.log project_id: project_id, "flushed project via http"
  113. res.send 204 # No Content
  114. deleteProject: (req, res, next = (error) ->) ->
  115. project_id = req.params.project_id
  116. logger.log project_id: project_id, "deleting project via http"
  117. timer = new Metrics.Timer("http.deleteProject")
  118. ProjectManager.flushAndDeleteProjectWithLocks project_id, (error) ->
  119. timer.done()
  120. return next(error) if error?
  121. logger.log project_id: project_id, "deleted project via http"
  122. res.send 204 # No Content
  123. acceptChanges: (req, res, next = (error) ->) ->
  124. {project_id, doc_id} = req.params
  125. change_ids = req.body?.change_ids
  126. if !change_ids?
  127. change_ids = [ req.params.change_id ]
  128. logger.log {project_id, doc_id}, "accepting #{ change_ids.length } changes via http"
  129. timer = new Metrics.Timer("http.acceptChanges")
  130. DocumentManager.acceptChangesWithLock project_id, doc_id, change_ids, (error) ->
  131. timer.done()
  132. return next(error) if error?
  133. logger.log {project_id, doc_id}, "accepted #{ change_ids.length } changes via http"
  134. res.send 204 # No Content
  135. deleteComment: (req, res, next = (error) ->) ->
  136. {project_id, doc_id, comment_id} = req.params
  137. logger.log {project_id, doc_id, comment_id}, "deleting comment via http"
  138. timer = new Metrics.Timer("http.deleteComment")
  139. DocumentManager.deleteCommentWithLock project_id, doc_id, comment_id, (error) ->
  140. timer.done()
  141. return next(error) if error?
  142. logger.log {project_id, doc_id, comment_id}, "deleted comment via http"
  143. res.send 204 # No Content
  144. updateProject: (req, res, next = (error) ->) ->
  145. timer = new Metrics.Timer("http.updateProject")
  146. project_id = req.params.project_id
  147. {userId, docUpdates, fileUpdates, version} = req.body
  148. logger.log {project_id, docUpdates, fileUpdates, version}, "updating project via http"
  149. ProjectManager.updateProjectWithLocks project_id, userId, docUpdates, fileUpdates, version, (error) ->
  150. timer.done()
  151. return next(error) if error?
  152. logger.log project_id: project_id, "updated project via http"
  153. res.send 204 # No Content
  154. resyncProjectHistory: (req, res, next = (error) ->) ->
  155. project_id = req.params.project_id
  156. {docs, files} = req.body
  157. logger.log {project_id, docs, files}, "queuing project history resync via http"
  158. HistoryManager.resyncProjectHistory project_id, docs, files, (error) ->
  159. return next(error) if error?
  160. logger.log {project_id}, "queued project history resync via http"
  161. res.send 204