HttpController.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. /*
  2. * decaffeinate suggestions:
  3. * DS101: Remove unnecessary use of Array.from
  4. * DS102: Remove unnecessary code created because of implicit returns
  5. * DS207: Consider shorter variations of null checks
  6. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  7. */
  8. let HttpController;
  9. const DocManager = require("./DocManager");
  10. const logger = require("logger-sharelatex");
  11. const DocArchive = require("./DocArchiveManager");
  12. const HealthChecker = require("./HealthChecker");
  13. const Settings = require("settings-sharelatex");
  14. module.exports = (HttpController = {
  15. getDoc(req, res, next) {
  16. if (next == null) { next = function(error) {}; }
  17. const {
  18. project_id
  19. } = req.params;
  20. const {
  21. doc_id
  22. } = req.params;
  23. const include_deleted = (req.query != null ? req.query.include_deleted : undefined) === "true";
  24. logger.log({project_id, doc_id}, "getting doc");
  25. return DocManager.getFullDoc(project_id, doc_id, function(error, doc) {
  26. if (error != null) { return next(error); }
  27. logger.log({doc_id, project_id}, "got doc");
  28. if ((doc == null)) {
  29. return res.send(404);
  30. } else if (doc.deleted && !include_deleted) {
  31. return res.send(404);
  32. } else {
  33. return res.json(HttpController._buildDocView(doc));
  34. }
  35. });
  36. },
  37. getRawDoc(req, res, next){
  38. if (next == null) { next = function(error){}; }
  39. const {
  40. project_id
  41. } = req.params;
  42. const {
  43. doc_id
  44. } = req.params;
  45. logger.log({project_id, doc_id}, "getting raw doc");
  46. return DocManager.getDocLines(project_id, doc_id, function(error, doc) {
  47. if (error != null) { return next(error); }
  48. if ((doc == null)) {
  49. return res.send(404);
  50. } else {
  51. res.setHeader('content-type', 'text/plain');
  52. return res.send(HttpController._buildRawDocView(doc));
  53. }
  54. });
  55. },
  56. getAllDocs(req, res, next) {
  57. if (next == null) { next = function(error) {}; }
  58. const {
  59. project_id
  60. } = req.params;
  61. logger.log({project_id}, "getting all docs");
  62. return DocManager.getAllNonDeletedDocs(project_id, {lines: true, rev: true}, function(error, docs) {
  63. if (docs == null) { docs = []; }
  64. if (error != null) { return next(error); }
  65. return res.json(HttpController._buildDocsArrayView(project_id, docs));
  66. });
  67. },
  68. getAllRanges(req, res, next) {
  69. if (next == null) { next = function(error) {}; }
  70. const {
  71. project_id
  72. } = req.params;
  73. logger.log({project_id}, "getting all ranges");
  74. return DocManager.getAllNonDeletedDocs(project_id, {ranges: true}, function(error, docs) {
  75. if (docs == null) { docs = []; }
  76. if (error != null) { return next(error); }
  77. return res.json(HttpController._buildDocsArrayView(project_id, docs));
  78. });
  79. },
  80. updateDoc(req, res, next) {
  81. if (next == null) { next = function(error) {}; }
  82. const {
  83. project_id
  84. } = req.params;
  85. const {
  86. doc_id
  87. } = req.params;
  88. const lines = req.body != null ? req.body.lines : undefined;
  89. const version = req.body != null ? req.body.version : undefined;
  90. const ranges = req.body != null ? req.body.ranges : undefined;
  91. if ((lines == null) || !(lines instanceof Array)) {
  92. logger.error({project_id, doc_id}, "no doc lines provided");
  93. res.send(400); // Bad Request
  94. return;
  95. }
  96. if ((version == null) || (typeof version === !"number")) {
  97. logger.error({project_id, doc_id}, "no doc version provided");
  98. res.send(400); // Bad Request
  99. return;
  100. }
  101. if ((ranges == null)) {
  102. logger.error({project_id, doc_id}, "no doc ranges provided");
  103. res.send(400); // Bad Request
  104. return;
  105. }
  106. const bodyLength = lines.reduce(
  107. (len, line) => line.length + len,
  108. 0
  109. );
  110. if (bodyLength > Settings.max_doc_length) {
  111. logger.error({project_id, doc_id, bodyLength}, "document body too large");
  112. res.status(413).send("document body too large");
  113. return;
  114. }
  115. logger.log({project_id, doc_id}, "got http request to update doc");
  116. return DocManager.updateDoc(project_id, doc_id, lines, version, ranges, function(error, modified, rev) {
  117. if (error != null) { return next(error); }
  118. return res.json({
  119. modified,
  120. rev
  121. });
  122. });
  123. },
  124. deleteDoc(req, res, next) {
  125. if (next == null) { next = function(error) {}; }
  126. const {
  127. project_id
  128. } = req.params;
  129. const {
  130. doc_id
  131. } = req.params;
  132. logger.log({project_id, doc_id}, "deleting doc");
  133. return DocManager.deleteDoc(project_id, doc_id, function(error) {
  134. if (error != null) { return next(error); }
  135. return res.send(204);
  136. });
  137. },
  138. _buildDocView(doc) {
  139. const doc_view = { _id: (doc._id != null ? doc._id.toString() : undefined) };
  140. for (let attribute of ["lines", "rev", "version", "ranges", "deleted"]) {
  141. if (doc[attribute] != null) {
  142. doc_view[attribute] = doc[attribute];
  143. }
  144. }
  145. return doc_view;
  146. },
  147. _buildRawDocView(doc){
  148. return ((doc != null ? doc.lines : undefined) || []).join("\n");
  149. },
  150. _buildDocsArrayView(project_id, docs) {
  151. const docViews = [];
  152. for (let doc of Array.from(docs)) {
  153. if (doc != null) { // There can end up being null docs for some reason :( (probably a race condition)
  154. docViews.push(HttpController._buildDocView(doc));
  155. } else {
  156. logger.error({err: new Error("null doc"), project_id}, "encountered null doc");
  157. }
  158. }
  159. return docViews;
  160. },
  161. archiveAllDocs(req, res, next) {
  162. if (next == null) { next = function(error) {}; }
  163. const {
  164. project_id
  165. } = req.params;
  166. logger.log({project_id}, "archiving all docs");
  167. return DocArchive.archiveAllDocs(project_id, function(error) {
  168. if (error != null) { return next(error); }
  169. return res.send(204);
  170. });
  171. },
  172. unArchiveAllDocs(req, res, next) {
  173. if (next == null) { next = function(error) {}; }
  174. const {
  175. project_id
  176. } = req.params;
  177. logger.log({project_id}, "unarchiving all docs");
  178. return DocArchive.unArchiveAllDocs(project_id, function(error) {
  179. if (error != null) { return next(error); }
  180. return res.send(200);
  181. });
  182. },
  183. destroyAllDocs(req, res, next) {
  184. if (next == null) { next = function(error) {}; }
  185. const {
  186. project_id
  187. } = req.params;
  188. logger.log({project_id}, "destroying all docs");
  189. return DocArchive.destroyAllDocs(project_id, function(error) {
  190. if (error != null) { return next(error); }
  191. return res.send(204);
  192. });
  193. },
  194. healthCheck(req, res){
  195. return HealthChecker.check(function(err){
  196. if (err != null) {
  197. logger.err({err}, "error performing health check");
  198. return res.send(500);
  199. } else {
  200. return res.send(200);
  201. }
  202. });
  203. }
  204. });