HttpController.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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, deletedAt: true },
  115. function (error, docs) {
  116. if (error) {
  117. return next(error)
  118. }
  119. res.json(
  120. docs.map(doc => {
  121. return {
  122. _id: doc._id.toString(),
  123. name: doc.name,
  124. deletedAt: doc.deletedAt,
  125. }
  126. })
  127. )
  128. }
  129. )
  130. },
  131. getAllRanges(req, res, next) {
  132. if (next == null) {
  133. next = function (error) {}
  134. }
  135. const { project_id } = req.params
  136. logger.log({ project_id }, 'getting all ranges')
  137. return DocManager.getAllNonDeletedDocs(
  138. project_id,
  139. { ranges: true },
  140. function (error, docs) {
  141. if (docs == null) {
  142. docs = []
  143. }
  144. if (error != null) {
  145. return next(error)
  146. }
  147. return res.json(HttpController._buildDocsArrayView(project_id, docs))
  148. }
  149. )
  150. },
  151. updateDoc(req, res, next) {
  152. if (next == null) {
  153. next = function (error) {}
  154. }
  155. const { project_id } = req.params
  156. const { doc_id } = req.params
  157. const lines = req.body != null ? req.body.lines : undefined
  158. const version = req.body != null ? req.body.version : undefined
  159. const ranges = req.body != null ? req.body.ranges : undefined
  160. if (lines == null || !(lines instanceof Array)) {
  161. logger.error({ project_id, doc_id }, 'no doc lines provided')
  162. res.sendStatus(400) // Bad Request
  163. return
  164. }
  165. if (version == null || typeof version === !'number') {
  166. logger.error({ project_id, doc_id }, 'no doc version provided')
  167. res.sendStatus(400) // Bad Request
  168. return
  169. }
  170. if (ranges == null) {
  171. logger.error({ project_id, doc_id }, 'no doc ranges provided')
  172. res.sendStatus(400) // Bad Request
  173. return
  174. }
  175. const bodyLength = lines.reduce((len, line) => line.length + len, 0)
  176. if (bodyLength > Settings.max_doc_length) {
  177. logger.error(
  178. { project_id, doc_id, bodyLength },
  179. 'document body too large'
  180. )
  181. res.status(413).send('document body too large')
  182. return
  183. }
  184. logger.log({ project_id, doc_id }, 'got http request to update doc')
  185. return DocManager.updateDoc(
  186. project_id,
  187. doc_id,
  188. lines,
  189. version,
  190. ranges,
  191. function (error, modified, rev) {
  192. if (error != null) {
  193. return next(error)
  194. }
  195. return res.json({
  196. modified,
  197. rev,
  198. })
  199. }
  200. )
  201. },
  202. patchDoc(req, res, next) {
  203. const { project_id, doc_id } = req.params
  204. logger.log({ project_id, doc_id }, 'patching doc')
  205. const allowedFields = ['deleted', 'deletedAt', 'name']
  206. const meta = {}
  207. Object.entries(req.body).forEach(([field, value]) => {
  208. if (allowedFields.includes(field)) {
  209. meta[field] = value
  210. } else {
  211. logger.fatal({ field }, 'joi validation for pathDoc is broken')
  212. }
  213. })
  214. DocManager.patchDoc(project_id, doc_id, meta, function (error) {
  215. if (error) {
  216. return next(error)
  217. }
  218. res.sendStatus(204)
  219. })
  220. },
  221. _buildDocView(doc) {
  222. const doc_view = { _id: doc._id != null ? doc._id.toString() : undefined }
  223. for (const attribute of ['lines', 'rev', 'version', 'ranges', 'deleted']) {
  224. if (doc[attribute] != null) {
  225. doc_view[attribute] = doc[attribute]
  226. }
  227. }
  228. return doc_view
  229. },
  230. _buildRawDocView(doc) {
  231. return ((doc != null ? doc.lines : undefined) || []).join('\n')
  232. },
  233. _buildDocsArrayView(project_id, docs) {
  234. const docViews = []
  235. for (const doc of Array.from(docs)) {
  236. if (doc != null) {
  237. // There can end up being null docs for some reason :( (probably a race condition)
  238. docViews.push(HttpController._buildDocView(doc))
  239. } else {
  240. logger.error(
  241. { err: new Error('null doc'), project_id },
  242. 'encountered null doc'
  243. )
  244. }
  245. }
  246. return docViews
  247. },
  248. archiveAllDocs(req, res, next) {
  249. if (next == null) {
  250. next = function (error) {}
  251. }
  252. const { project_id } = req.params
  253. logger.log({ project_id }, 'archiving all docs')
  254. return DocArchive.archiveAllDocs(project_id, function (error) {
  255. if (error != null) {
  256. return next(error)
  257. }
  258. return res.sendStatus(204)
  259. })
  260. },
  261. archiveDoc(req, res, next) {
  262. const { project_id, doc_id } = req.params
  263. logger.log({ project_id, doc_id }, 'archiving a doc')
  264. DocArchive.archiveDocById(project_id, doc_id, function (error) {
  265. if (error) {
  266. return next(error)
  267. }
  268. res.sendStatus(204)
  269. })
  270. },
  271. unArchiveAllDocs(req, res, next) {
  272. if (next == null) {
  273. next = function (error) {}
  274. }
  275. const { project_id } = req.params
  276. logger.log({ project_id }, 'unarchiving all docs')
  277. return DocArchive.unArchiveAllDocs(project_id, function (error) {
  278. if (error != null) {
  279. return next(error)
  280. }
  281. return res.sendStatus(200)
  282. })
  283. },
  284. destroyAllDocs(req, res, next) {
  285. if (next == null) {
  286. next = function (error) {}
  287. }
  288. const { project_id } = req.params
  289. logger.log({ project_id }, 'destroying all docs')
  290. return DocArchive.destroyAllDocs(project_id, function (error) {
  291. if (error != null) {
  292. return next(error)
  293. }
  294. return res.sendStatus(204)
  295. })
  296. },
  297. healthCheck(req, res) {
  298. return HealthChecker.check(function (err) {
  299. if (err != null) {
  300. logger.err({ err }, 'error performing health check')
  301. return res.sendStatus(500)
  302. } else {
  303. return res.sendStatus(200)
  304. }
  305. })
  306. },
  307. }