HttpController.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  4. valid-typeof,
  5. */
  6. // TODO: This file was created by bulk-decaffeinate.
  7. // Fix any style issues and re-enable lint.
  8. /*
  9. * decaffeinate suggestions:
  10. * DS101: Remove unnecessary use of Array.from
  11. * DS102: Remove unnecessary code created because of implicit returns
  12. * DS207: Consider shorter variations of null checks
  13. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  14. */
  15. let HttpController
  16. const DocManager = require('./DocManager')
  17. const logger = require('logger-sharelatex')
  18. const DocArchive = require('./DocArchiveManager')
  19. const HealthChecker = require('./HealthChecker')
  20. const Settings = require('@overleaf/settings')
  21. module.exports = HttpController = {
  22. getDoc(req, res, next) {
  23. if (next == null) {
  24. next = function (error) {}
  25. }
  26. const { project_id } = req.params
  27. const { doc_id } = req.params
  28. const include_deleted =
  29. (req.query != null ? req.query.include_deleted : undefined) === 'true'
  30. logger.log({ project_id, doc_id }, 'getting doc')
  31. return DocManager.getFullDoc(project_id, doc_id, function (error, doc) {
  32. if (error != null) {
  33. return next(error)
  34. }
  35. logger.log({ doc_id, project_id }, 'got doc')
  36. if (doc == null) {
  37. return res.sendStatus(404)
  38. } else if (doc.deleted && !include_deleted) {
  39. return res.sendStatus(404)
  40. } else {
  41. return res.json(HttpController._buildDocView(doc))
  42. }
  43. })
  44. },
  45. peekDoc(req, res, next) {
  46. const { project_id } = req.params
  47. const { doc_id } = req.params
  48. logger.log({ project_id, doc_id }, 'peeking doc')
  49. DocManager.peekDoc(project_id, doc_id, function (error, doc) {
  50. if (error) {
  51. return next(error)
  52. }
  53. if (doc == null) {
  54. return res.sendStatus(404)
  55. } else {
  56. res.setHeader('x-doc-status', doc.inS3 ? 'archived' : 'active')
  57. return res.json(HttpController._buildDocView(doc))
  58. }
  59. })
  60. },
  61. isDocDeleted(req, res, next) {
  62. const { doc_id: docId, project_id: projectId } = req.params
  63. DocManager.isDocDeleted(projectId, docId, function (error, deleted) {
  64. if (error) {
  65. return next(error)
  66. }
  67. res.json({ deleted })
  68. })
  69. },
  70. getRawDoc(req, res, next) {
  71. if (next == null) {
  72. next = function (error) {}
  73. }
  74. const { project_id } = req.params
  75. const { doc_id } = req.params
  76. logger.log({ project_id, doc_id }, 'getting raw doc')
  77. return DocManager.getDocLines(project_id, doc_id, function (error, doc) {
  78. if (error != null) {
  79. return next(error)
  80. }
  81. if (doc == null) {
  82. return res.sendStatus(404)
  83. } else {
  84. res.setHeader('content-type', 'text/plain')
  85. return res.send(HttpController._buildRawDocView(doc))
  86. }
  87. })
  88. },
  89. getAllDocs(req, res, next) {
  90. if (next == null) {
  91. next = function (error) {}
  92. }
  93. const { project_id } = req.params
  94. logger.log({ project_id }, 'getting all docs')
  95. return DocManager.getAllNonDeletedDocs(
  96. project_id,
  97. { lines: true, rev: true },
  98. function (error, docs) {
  99. if (docs == null) {
  100. docs = []
  101. }
  102. if (error != null) {
  103. return next(error)
  104. }
  105. return res.json(HttpController._buildDocsArrayView(project_id, docs))
  106. }
  107. )
  108. },
  109. getAllDeletedDocs(req, res, next) {
  110. const { project_id } = req.params
  111. logger.log({ project_id }, 'getting all deleted docs')
  112. DocManager.getAllDeletedDocs(
  113. project_id,
  114. { name: true },
  115. function (error, docs) {
  116. if (error) {
  117. return next(error)
  118. }
  119. res.json(
  120. docs.map(doc => {
  121. return { _id: doc._id.toString(), name: doc.name }
  122. })
  123. )
  124. }
  125. )
  126. },
  127. getAllRanges(req, res, next) {
  128. if (next == null) {
  129. next = function (error) {}
  130. }
  131. const { project_id } = req.params
  132. logger.log({ project_id }, 'getting all ranges')
  133. return DocManager.getAllNonDeletedDocs(
  134. project_id,
  135. { ranges: true },
  136. function (error, docs) {
  137. if (docs == null) {
  138. docs = []
  139. }
  140. if (error != null) {
  141. return next(error)
  142. }
  143. return res.json(HttpController._buildDocsArrayView(project_id, docs))
  144. }
  145. )
  146. },
  147. updateDoc(req, res, next) {
  148. if (next == null) {
  149. next = function (error) {}
  150. }
  151. const { project_id } = req.params
  152. const { doc_id } = req.params
  153. const lines = req.body != null ? req.body.lines : undefined
  154. const version = req.body != null ? req.body.version : undefined
  155. const ranges = req.body != null ? req.body.ranges : undefined
  156. if (lines == null || !(lines instanceof Array)) {
  157. logger.error({ project_id, doc_id }, 'no doc lines provided')
  158. res.sendStatus(400) // Bad Request
  159. return
  160. }
  161. if (version == null || typeof version === !'number') {
  162. logger.error({ project_id, doc_id }, 'no doc version provided')
  163. res.sendStatus(400) // Bad Request
  164. return
  165. }
  166. if (ranges == null) {
  167. logger.error({ project_id, doc_id }, 'no doc ranges provided')
  168. res.sendStatus(400) // Bad Request
  169. return
  170. }
  171. const bodyLength = lines.reduce((len, line) => line.length + len, 0)
  172. if (bodyLength > Settings.max_doc_length) {
  173. logger.error(
  174. { project_id, doc_id, bodyLength },
  175. 'document body too large'
  176. )
  177. res.status(413).send('document body too large')
  178. return
  179. }
  180. logger.log({ project_id, doc_id }, 'got http request to update doc')
  181. return DocManager.updateDoc(
  182. project_id,
  183. doc_id,
  184. lines,
  185. version,
  186. ranges,
  187. function (error, modified, rev) {
  188. if (error != null) {
  189. return next(error)
  190. }
  191. return res.json({
  192. modified,
  193. rev,
  194. })
  195. }
  196. )
  197. },
  198. patchDoc(req, res, next) {
  199. const { project_id, doc_id } = req.params
  200. logger.log({ project_id, doc_id }, 'patching doc')
  201. const allowedFields = ['deleted', 'deletedAt', 'name']
  202. const meta = {}
  203. Object.entries(req.body).forEach(([field, value]) => {
  204. if (allowedFields.includes(field)) {
  205. meta[field] = value
  206. } else {
  207. logger.fatal({ field }, 'joi validation for pathDoc is broken')
  208. }
  209. })
  210. DocManager.patchDoc(project_id, doc_id, meta, function (error) {
  211. if (error) {
  212. return next(error)
  213. }
  214. res.sendStatus(204)
  215. })
  216. },
  217. _buildDocView(doc) {
  218. const doc_view = { _id: doc._id != null ? doc._id.toString() : undefined }
  219. for (const attribute of ['lines', 'rev', 'version', 'ranges', 'deleted']) {
  220. if (doc[attribute] != null) {
  221. doc_view[attribute] = doc[attribute]
  222. }
  223. }
  224. return doc_view
  225. },
  226. _buildRawDocView(doc) {
  227. return ((doc != null ? doc.lines : undefined) || []).join('\n')
  228. },
  229. _buildDocsArrayView(project_id, docs) {
  230. const docViews = []
  231. for (const doc of Array.from(docs)) {
  232. if (doc != null) {
  233. // There can end up being null docs for some reason :( (probably a race condition)
  234. docViews.push(HttpController._buildDocView(doc))
  235. } else {
  236. logger.error(
  237. { err: new Error('null doc'), project_id },
  238. 'encountered null doc'
  239. )
  240. }
  241. }
  242. return docViews
  243. },
  244. archiveAllDocs(req, res, next) {
  245. if (next == null) {
  246. next = function (error) {}
  247. }
  248. const { project_id } = req.params
  249. logger.log({ project_id }, 'archiving all docs')
  250. return DocArchive.archiveAllDocs(project_id, function (error) {
  251. if (error != null) {
  252. return next(error)
  253. }
  254. return res.sendStatus(204)
  255. })
  256. },
  257. archiveDoc(req, res, next) {
  258. const { project_id, doc_id } = req.params
  259. logger.log({ project_id, doc_id }, 'archiving a doc')
  260. DocArchive.archiveDocById(project_id, doc_id, function (error) {
  261. if (error) {
  262. return next(error)
  263. }
  264. res.sendStatus(204)
  265. })
  266. },
  267. unArchiveAllDocs(req, res, next) {
  268. if (next == null) {
  269. next = function (error) {}
  270. }
  271. const { project_id } = req.params
  272. logger.log({ project_id }, 'unarchiving all docs')
  273. return DocArchive.unArchiveAllDocs(project_id, function (error) {
  274. if (error != null) {
  275. return next(error)
  276. }
  277. return res.sendStatus(200)
  278. })
  279. },
  280. destroyAllDocs(req, res, next) {
  281. if (next == null) {
  282. next = function (error) {}
  283. }
  284. const { project_id } = req.params
  285. logger.log({ project_id }, 'destroying all docs')
  286. return DocArchive.destroyAllDocs(project_id, function (error) {
  287. if (error != null) {
  288. return next(error)
  289. }
  290. return res.sendStatus(204)
  291. })
  292. },
  293. healthCheck(req, res) {
  294. return HealthChecker.check(function (err) {
  295. if (err != null) {
  296. logger.err({ err }, 'error performing health check')
  297. return res.sendStatus(500)
  298. } else {
  299. return res.sendStatus(200)
  300. }
  301. })
  302. },
  303. }