Router.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. import OError from '@overleaf/o-error'
  2. import * as HttpController from './HttpController.js'
  3. import { Joi, validate } from './Validation.js'
  4. export function initialize(app) {
  5. app.use(
  6. validate({
  7. params: Joi.object({
  8. project_id: Joi.string().regex(/^[0-9a-f]{24}$/),
  9. user_id: Joi.string().regex(/^[0-9a-f]{24}$/),
  10. label_id: Joi.string().regex(/^[0-9a-f]{24}$/),
  11. version: Joi.number().integer(),
  12. }),
  13. })
  14. )
  15. // use an extended timeout on all endpoints, to allow for long requests to history-v1
  16. app.use(longerTimeout)
  17. app.post('/project', HttpController.initializeProject)
  18. app.delete('/project/:project_id', HttpController.deleteProject)
  19. app.get('/project/:project_id/snapshot', HttpController.getLatestSnapshot)
  20. app.get(
  21. '/project/:project_id/diff',
  22. validate({
  23. query: {
  24. pathname: Joi.string().required(),
  25. from: Joi.number().integer().required(),
  26. to: Joi.number().integer().required(),
  27. },
  28. }),
  29. HttpController.getDiff
  30. )
  31. app.get(
  32. '/project/:project_id/filetree/diff',
  33. validate({
  34. query: {
  35. from: Joi.number().integer().required(),
  36. to: Joi.number().integer().required(),
  37. },
  38. }),
  39. HttpController.getFileTreeDiff
  40. )
  41. app.get(
  42. '/project/:project_id/updates',
  43. validate({
  44. query: {
  45. before: Joi.number().integer(),
  46. min_count: Joi.number().integer(),
  47. },
  48. }),
  49. HttpController.getUpdates
  50. )
  51. app.get(
  52. '/project/:project_id/changes-in-chunk',
  53. validate({
  54. query: {
  55. since: Joi.number().integer().min(0),
  56. },
  57. }),
  58. HttpController.getChangesInChunkSince
  59. )
  60. app.get('/project/:project_id/version', HttpController.latestVersion)
  61. app.post(
  62. '/project/:project_id/flush',
  63. validate({
  64. query: {
  65. debug: Joi.boolean().default(false),
  66. bisect: Joi.boolean().default(false),
  67. },
  68. }),
  69. HttpController.flushProject
  70. )
  71. app.post(
  72. '/project/:project_id/resync',
  73. validate({
  74. query: {
  75. force: Joi.boolean().default(false),
  76. },
  77. body: {
  78. force: Joi.boolean().default(false),
  79. origin: Joi.object({
  80. kind: Joi.string().required(),
  81. }),
  82. historyRangesMigration: Joi.string()
  83. .optional()
  84. .valid('forwards', 'backwards'),
  85. },
  86. }),
  87. HttpController.resyncProject
  88. )
  89. app.get(
  90. '/project/:project_id/dump',
  91. validate({
  92. query: {
  93. count: Joi.number().integer(),
  94. },
  95. }),
  96. HttpController.dumpProject
  97. )
  98. app.get('/project/:project_id/labels', HttpController.getLabels)
  99. app.post(
  100. '/project/:project_id/labels',
  101. validate({
  102. body: {
  103. version: Joi.number().integer().required(),
  104. comment: Joi.string().required(),
  105. created_at: Joi.string(),
  106. validate_exists: Joi.boolean().default(true),
  107. user_id: Joi.string().allow(null),
  108. },
  109. }),
  110. HttpController.createLabel
  111. )
  112. app.delete(
  113. '/project/:project_id/user/:user_id/labels/:label_id',
  114. validate({
  115. params: Joi.object({
  116. project_id: Joi.string().regex(/^[0-9a-f]{24}$/),
  117. user_id: Joi.string().regex(/^[0-9a-f]{24}$/),
  118. label_id: Joi.string().regex(/^[0-9a-f]{24}$/),
  119. }),
  120. }),
  121. HttpController.deleteLabelForUser
  122. )
  123. app.delete(
  124. '/project/:project_id/labels/:label_id',
  125. validate({
  126. params: Joi.object({
  127. project_id: Joi.string().regex(/^[0-9a-f]{24}$/),
  128. label_id: Joi.string().regex(/^[0-9a-f]{24}$/),
  129. }),
  130. }),
  131. HttpController.deleteLabel
  132. )
  133. app.post(
  134. '/user/:from_user/labels/transfer/:to_user',
  135. HttpController.transferLabels
  136. )
  137. app.get(
  138. '/project/:project_id/version/:version/:pathname',
  139. HttpController.getFileSnapshot
  140. )
  141. app.get(
  142. '/project/:project_id/ranges/version/:version/:pathname',
  143. HttpController.getRangesSnapshot
  144. )
  145. app.get(
  146. '/project/:project_id/metadata/version/:version/:pathname',
  147. HttpController.getFileMetadataSnapshot
  148. )
  149. app.get(
  150. '/project/:project_id/version/:version',
  151. HttpController.getProjectSnapshot
  152. )
  153. app.get(
  154. '/project/:project_id/paths/version/:version',
  155. HttpController.getPathsAtVersion
  156. )
  157. app.post(
  158. '/project/:project_id/force',
  159. validate({
  160. query: {
  161. clear: Joi.boolean().default(false),
  162. },
  163. }),
  164. HttpController.forceDebugProject
  165. )
  166. app.get('/project/:history_id/blob/:hash', HttpController.getProjectBlob)
  167. app.get('/status/failures', HttpController.getFailures)
  168. app.get('/status/queue', HttpController.getQueueCounts)
  169. app.post(
  170. '/retry/failures',
  171. validate({
  172. query: {
  173. failureType: Joi.string().valid('soft', 'hard'),
  174. // bail out after this time limit
  175. timeout: Joi.number().integer().default(300),
  176. // maximum number of projects to check
  177. limit: Joi.number().integer().default(100),
  178. callbackUrl: Joi.string(),
  179. },
  180. }),
  181. HttpController.retryFailures
  182. )
  183. app.post(
  184. '/flush/old',
  185. validate({
  186. query: {
  187. // flush projects with queued ops older than this
  188. maxAge: Joi.number()
  189. .integer()
  190. .default(6 * 3600),
  191. // pause this amount of time between checking queues
  192. queueDelay: Joi.number().integer().default(100),
  193. // maximum number of queues to check
  194. limit: Joi.number().integer().default(1000),
  195. // maximum amount of time allowed
  196. timeout: Joi.number()
  197. .integer()
  198. .default(60 * 1000),
  199. // whether to run in the background
  200. background: Joi.boolean().falsy('0').truthy('1').default(false),
  201. },
  202. }),
  203. HttpController.flushOld
  204. )
  205. app.get('/status', (req, res, next) => res.send('project-history is up'))
  206. app.get('/oops', function (req, res, next) {
  207. throw new OError('dummy test error')
  208. })
  209. app.get('/check_lock', HttpController.checkLock)
  210. app.get('/health_check', HttpController.healthCheck)
  211. }
  212. function longerTimeout(req, res, next) {
  213. res.setTimeout(6 * 60 * 1000)
  214. next()
  215. }