WebApiManager.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import { promisify } from 'util'
  2. import request from 'requestretry' // allow retry on error https://github.com/FGRibreau/node-request-retry
  3. import logger from '@overleaf/logger'
  4. import Metrics from '@overleaf/metrics'
  5. import OError from '@overleaf/o-error'
  6. import Settings from '@overleaf/settings'
  7. import * as Errors from './Errors.js'
  8. import * as RedisManager from './RedisManager.js'
  9. // Don't let HTTP calls hang for a long time
  10. const DEFAULT_MAX_HTTP_REQUEST_LENGTH = 16000 // 16 seconds
  11. export function getHistoryId(projectId, callback) {
  12. Metrics.inc('history_id_cache_requests_total')
  13. RedisManager.getCachedHistoryId(projectId, (err, cachedHistoryId) => {
  14. if (err) return callback(err)
  15. if (cachedHistoryId) {
  16. Metrics.inc('history_id_cache_hits_total')
  17. callback(null, cachedHistoryId, true)
  18. } else {
  19. _getProjectDetails(projectId, function (error, project) {
  20. if (error) {
  21. return callback(error)
  22. }
  23. const historyId =
  24. project.overleaf &&
  25. project.overleaf.history &&
  26. project.overleaf.history.id
  27. if (historyId != null) {
  28. RedisManager.setCachedHistoryId(projectId, historyId, err => {
  29. if (err) return callback(err)
  30. callback(null, historyId, false)
  31. })
  32. } else {
  33. callback(null, historyId, false)
  34. }
  35. })
  36. }
  37. })
  38. }
  39. export function requestResync(projectId, callback) {
  40. const path = `/project/${projectId}/history/resync`
  41. _sendRequest(
  42. { path, timeout: 6 * 60000, maxAttempts: 1, method: 'POST' },
  43. callback
  44. )
  45. }
  46. function _getProjectDetails(projectId, callback) {
  47. const path = `/project/${projectId}/details`
  48. logger.debug({ projectId }, 'getting project details from web')
  49. _sendRequest({ path, json: true }, callback)
  50. }
  51. function _sendRequest(options, callback) {
  52. const url = `${Settings.apis.web.url}${options.path}`
  53. request(
  54. {
  55. method: options.method || 'GET',
  56. url,
  57. json: options.json || false,
  58. timeout: options.timeout || DEFAULT_MAX_HTTP_REQUEST_LENGTH,
  59. maxAttempts: options.maxAttempts || 2, // for node-request-retry
  60. auth: {
  61. user: Settings.apis.web.user,
  62. pass: Settings.apis.web.pass,
  63. sendImmediately: true,
  64. },
  65. },
  66. function (error, res, body) {
  67. if (error != null) {
  68. return callback(OError.tag(error))
  69. }
  70. if (res.statusCode === 404) {
  71. logger.debug({ url }, 'got 404 from web api')
  72. error = new Errors.NotFoundError('got a 404 from web api')
  73. return callback(error)
  74. }
  75. if (res.statusCode >= 200 && res.statusCode < 300) {
  76. callback(null, body)
  77. } else {
  78. error = new OError(
  79. `web returned a non-success status code: ${res.statusCode} (attempts: ${res.attempts})`,
  80. { url, res }
  81. )
  82. callback(error)
  83. }
  84. }
  85. )
  86. }
  87. export const promises = {
  88. getHistoryId: promisify(getHistoryId),
  89. requestResync: promisify(requestResync),
  90. }