HttpErrorHandler.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. const logger = require('logger-sharelatex')
  2. const Settings = require('@overleaf/settings')
  3. function renderJSONError(res, message, info = {}) {
  4. if (info.message) {
  5. logger.warn(
  6. info,
  7. `http error info shouldn't contain a 'message' field, will be overridden`
  8. )
  9. }
  10. if (message != null) {
  11. res.json({ ...info, message })
  12. } else {
  13. res.json(info)
  14. }
  15. }
  16. function handleGeneric500Error(req, res, statusCode, message) {
  17. res.status(statusCode)
  18. switch (req.accepts(['html', 'json'])) {
  19. case 'html':
  20. return res.render('general/500', { title: 'Server Error' })
  21. case 'json':
  22. return renderJSONError(res, message)
  23. default:
  24. return res.send('internal server error')
  25. }
  26. }
  27. function handleGeneric400Error(req, res, statusCode, message, info = {}) {
  28. res.status(statusCode)
  29. switch (req.accepts(['html', 'json'])) {
  30. case 'html':
  31. return res.render('general/400', {
  32. title: 'Client Error',
  33. message: message,
  34. })
  35. case 'json':
  36. return renderJSONError(res, message, info)
  37. default:
  38. return res.send('client error')
  39. }
  40. }
  41. let HttpErrorHandler
  42. module.exports = HttpErrorHandler = {
  43. handleErrorByStatusCode(req, res, error, statusCode) {
  44. const is400Error = statusCode >= 400 && statusCode < 500
  45. const is500Error = statusCode >= 500 && statusCode < 600
  46. if (is400Error) {
  47. logger.warn(error)
  48. } else if (is500Error) {
  49. logger.error(error)
  50. }
  51. if (statusCode === 403) {
  52. HttpErrorHandler.forbidden(req, res)
  53. } else if (statusCode === 404) {
  54. HttpErrorHandler.notFound(req, res)
  55. } else if (statusCode === 409) {
  56. HttpErrorHandler.conflict(req, res, '')
  57. } else if (statusCode === 422) {
  58. HttpErrorHandler.unprocessableEntity(req, res)
  59. } else if (is400Error) {
  60. handleGeneric400Error(req, res, statusCode)
  61. } else if (is500Error) {
  62. handleGeneric500Error(req, res, statusCode)
  63. } else {
  64. logger.error(
  65. { err: error, statusCode },
  66. `unable to handle error with status code ${statusCode}`
  67. )
  68. res.sendStatus(500)
  69. }
  70. },
  71. badRequest(req, res, message, info = {}) {
  72. handleGeneric400Error(req, res, 400, message, info)
  73. },
  74. conflict(req, res, message, info = {}) {
  75. res.status(409)
  76. switch (req.accepts(['html', 'json'])) {
  77. case 'html':
  78. return res.render('general/400', {
  79. title: 'Client Error',
  80. message: message,
  81. })
  82. case 'json':
  83. return renderJSONError(res, message, info)
  84. default:
  85. return res.send('conflict')
  86. }
  87. },
  88. forbidden(req, res, message = 'restricted', info = {}) {
  89. res.status(403)
  90. switch (req.accepts(['html', 'json'])) {
  91. case 'html':
  92. return res.render('user/restricted', { title: 'restricted' })
  93. case 'json':
  94. return renderJSONError(res, message, info)
  95. default:
  96. return res.send('restricted')
  97. }
  98. },
  99. notFound(req, res, message = 'not found', info = {}) {
  100. res.status(404)
  101. switch (req.accepts(['html', 'json'])) {
  102. case 'html':
  103. return res.render('general/404', { title: 'page_not_found' })
  104. case 'json':
  105. return renderJSONError(res, message, info)
  106. default:
  107. return res.send('not found')
  108. }
  109. },
  110. unprocessableEntity(req, res, message = 'unprocessable entity', info = {}) {
  111. res.status(422)
  112. switch (req.accepts(['html', 'json'])) {
  113. case 'html':
  114. return res.render('general/400', {
  115. title: 'Client Error',
  116. message: message,
  117. })
  118. case 'json':
  119. return renderJSONError(res, message, info)
  120. default:
  121. return res.send('unprocessable entity')
  122. }
  123. },
  124. legacyInternal(req, res, message, error) {
  125. logger.error(error)
  126. handleGeneric500Error(req, res, 500, message)
  127. },
  128. maintenance(req, res) {
  129. // load balancer health checks require a success response for /
  130. if (req.url === '/') {
  131. res.status(200)
  132. } else {
  133. res.status(503)
  134. }
  135. let message = `${Settings.appName} is currently down for maintenance.`
  136. if (Settings.statusPageUrl) {
  137. message += ` Please check https://${Settings.statusPageUrl} for updates.`
  138. }
  139. switch (req.accepts(['html', 'json'])) {
  140. case 'html':
  141. return res.render('general/closed', { title: 'maintenance' })
  142. case 'json':
  143. return renderJSONError(res, message, {})
  144. default:
  145. return res.send(message)
  146. }
  147. },
  148. }