router.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203
  1. const AdminController = require('./Features/ServerAdmin/AdminController')
  2. const ErrorController = require('./Features/Errors/ErrorController')
  3. const ProjectController = require('./Features/Project/ProjectController')
  4. const ProjectApiController = require('./Features/Project/ProjectApiController')
  5. const SpellingController = require('./Features/Spelling/SpellingController')
  6. const EditorRouter = require('./Features/Editor/EditorRouter')
  7. const Settings = require('@overleaf/settings')
  8. const TpdsController = require('./Features/ThirdPartyDataStore/TpdsController')
  9. const SubscriptionRouter = require('./Features/Subscription/SubscriptionRouter')
  10. const UploadsRouter = require('./Features/Uploads/UploadsRouter')
  11. const metrics = require('@overleaf/metrics')
  12. const ReferalController = require('./Features/Referal/ReferalController')
  13. const AuthenticationController = require('./Features/Authentication/AuthenticationController')
  14. const SessionManager = require('./Features/Authentication/SessionManager')
  15. const TagsController = require('./Features/Tags/TagsController')
  16. const NotificationsController = require('./Features/Notifications/NotificationsController')
  17. const CollaboratorsRouter = require('./Features/Collaborators/CollaboratorsRouter')
  18. const UserInfoController = require('./Features/User/UserInfoController')
  19. const UserController = require('./Features/User/UserController')
  20. const UserEmailsController = require('./Features/User/UserEmailsController')
  21. const UserPagesController = require('./Features/User/UserPagesController')
  22. const DocumentController = require('./Features/Documents/DocumentController')
  23. const CompileManager = require('./Features/Compile/CompileManager')
  24. const CompileController = require('./Features/Compile/CompileController')
  25. const ClsiCookieManager = require('./Features/Compile/ClsiCookieManager')(
  26. Settings.apis.clsi != null ? Settings.apis.clsi.backendGroupName : undefined
  27. )
  28. const HealthCheckController = require('./Features/HealthCheck/HealthCheckController')
  29. const ProjectDownloadsController = require('./Features/Downloads/ProjectDownloadsController')
  30. const FileStoreController = require('./Features/FileStore/FileStoreController')
  31. const HistoryController = require('./Features/History/HistoryController')
  32. const ExportsController = require('./Features/Exports/ExportsController')
  33. const PasswordResetRouter = require('./Features/PasswordReset/PasswordResetRouter')
  34. const StaticPagesRouter = require('./Features/StaticPages/StaticPagesRouter')
  35. const ChatController = require('./Features/Chat/ChatController')
  36. const Modules = require('./infrastructure/Modules')
  37. const RateLimiterMiddleware = require('./Features/Security/RateLimiterMiddleware')
  38. const InactiveProjectController = require('./Features/InactiveData/InactiveProjectController')
  39. const ContactRouter = require('./Features/Contacts/ContactRouter')
  40. const ReferencesController = require('./Features/References/ReferencesController')
  41. const AuthorizationMiddleware = require('./Features/Authorization/AuthorizationMiddleware')
  42. const BetaProgramController = require('./Features/BetaProgram/BetaProgramController')
  43. const AnalyticsRouter = require('./Features/Analytics/AnalyticsRouter')
  44. const MetaController = require('./Features/Metadata/MetaController')
  45. const TokenAccessController = require('./Features/TokenAccess/TokenAccessController')
  46. const Features = require('./infrastructure/Features')
  47. const LinkedFilesRouter = require('./Features/LinkedFiles/LinkedFilesRouter')
  48. const TemplatesRouter = require('./Features/Templates/TemplatesRouter')
  49. const InstitutionsController = require('./Features/Institutions/InstitutionsController')
  50. const UserMembershipRouter = require('./Features/UserMembership/UserMembershipRouter')
  51. const SystemMessageController = require('./Features/SystemMessages/SystemMessageController')
  52. const AnalyticsRegistrationSourceMiddleware = require('./Features/Analytics/AnalyticsRegistrationSourceMiddleware')
  53. const AnalyticsUTMTrackingMiddleware = require('./Features/Analytics/AnalyticsUTMTrackingMiddleware')
  54. const SplitTestMiddleware = require('./Features/SplitTests/SplitTestMiddleware')
  55. const { Joi, validate } = require('./infrastructure/Validation')
  56. const {
  57. renderUnsupportedBrowserPage,
  58. unsupportedBrowserMiddleware,
  59. } = require('./infrastructure/UnsupportedBrowserMiddleware')
  60. const logger = require('@overleaf/logger')
  61. const _ = require('underscore')
  62. const { expressify } = require('./util/promises')
  63. module.exports = { initialize }
  64. function initialize(webRouter, privateApiRouter, publicApiRouter) {
  65. webRouter.use(unsupportedBrowserMiddleware)
  66. if (!Settings.allowPublicAccess) {
  67. webRouter.all('*', AuthenticationController.requireGlobalLogin)
  68. }
  69. webRouter.get('*', AnalyticsRegistrationSourceMiddleware.setInbound())
  70. webRouter.get('*', AnalyticsUTMTrackingMiddleware.recordUTMTags())
  71. webRouter.get(
  72. '*',
  73. expressify(
  74. SplitTestMiddleware.loadAssignmentsInLocals(['unified-navigation'])
  75. )
  76. )
  77. webRouter.get('/login', UserPagesController.loginPage)
  78. AuthenticationController.addEndpointToLoginWhitelist('/login')
  79. webRouter.post('/login', AuthenticationController.passportLogin)
  80. if (Settings.enableLegacyLogin) {
  81. AuthenticationController.addEndpointToLoginWhitelist('/login/legacy')
  82. webRouter.get('/login/legacy', UserPagesController.loginPage)
  83. webRouter.post('/login/legacy', AuthenticationController.passportLogin)
  84. }
  85. webRouter.get(
  86. '/read-only/one-time-login',
  87. UserPagesController.oneTimeLoginPage
  88. )
  89. AuthenticationController.addEndpointToLoginWhitelist(
  90. '/read-only/one-time-login'
  91. )
  92. webRouter.get('/logout', UserPagesController.logoutPage)
  93. webRouter.post('/logout', UserController.logout)
  94. webRouter.get('/restricted', AuthorizationMiddleware.restricted)
  95. if (Features.hasFeature('registration-page')) {
  96. webRouter.get('/register', UserPagesController.registerPage)
  97. AuthenticationController.addEndpointToLoginWhitelist('/register')
  98. }
  99. EditorRouter.apply(webRouter, privateApiRouter)
  100. CollaboratorsRouter.apply(webRouter, privateApiRouter)
  101. SubscriptionRouter.apply(webRouter, privateApiRouter, publicApiRouter)
  102. UploadsRouter.apply(webRouter, privateApiRouter)
  103. PasswordResetRouter.apply(webRouter, privateApiRouter)
  104. StaticPagesRouter.apply(webRouter, privateApiRouter)
  105. ContactRouter.apply(webRouter, privateApiRouter)
  106. AnalyticsRouter.apply(webRouter, privateApiRouter, publicApiRouter)
  107. LinkedFilesRouter.apply(webRouter, privateApiRouter, publicApiRouter)
  108. TemplatesRouter.apply(webRouter)
  109. UserMembershipRouter.apply(webRouter)
  110. Modules.applyRouter(webRouter, privateApiRouter, publicApiRouter)
  111. if (Settings.enableSubscriptions) {
  112. webRouter.get(
  113. '/user/bonus',
  114. AuthenticationController.requireLogin(),
  115. ReferalController.bonus
  116. )
  117. }
  118. // .getMessages will generate an empty response for anonymous users.
  119. webRouter.get('/system/messages', SystemMessageController.getMessages)
  120. webRouter.get(
  121. '/user/settings',
  122. AuthenticationController.requireLogin(),
  123. UserPagesController.settingsPage
  124. )
  125. webRouter.post(
  126. '/user/settings',
  127. AuthenticationController.requireLogin(),
  128. UserController.updateUserSettings
  129. )
  130. webRouter.post(
  131. '/user/password/update',
  132. AuthenticationController.requireLogin(),
  133. RateLimiterMiddleware.rateLimit({
  134. endpointName: 'change-password',
  135. maxRequests: 10,
  136. timeInterval: 60,
  137. }),
  138. UserController.changePassword
  139. )
  140. webRouter.get(
  141. '/user/emails',
  142. AuthenticationController.requireLogin(),
  143. UserController.promises.ensureAffiliationMiddleware,
  144. UserEmailsController.list
  145. )
  146. webRouter.get('/user/emails/confirm', UserEmailsController.showConfirm)
  147. webRouter.post(
  148. '/user/emails/confirm',
  149. RateLimiterMiddleware.rateLimit({
  150. endpointName: 'confirm-email',
  151. maxRequests: 10,
  152. timeInterval: 60,
  153. }),
  154. UserEmailsController.confirm
  155. )
  156. webRouter.post(
  157. '/user/emails/resend_confirmation',
  158. AuthenticationController.requireLogin(),
  159. RateLimiterMiddleware.rateLimit({
  160. endpointName: 'resend-confirmation',
  161. maxRequests: 10,
  162. timeInterval: 60,
  163. }),
  164. UserEmailsController.resendConfirmation
  165. )
  166. if (Features.hasFeature('affiliations')) {
  167. webRouter.post(
  168. '/user/emails',
  169. AuthenticationController.requireLogin(),
  170. RateLimiterMiddleware.rateLimit({
  171. endpointName: 'add-email',
  172. maxRequests: 10,
  173. timeInterval: 60,
  174. }),
  175. UserEmailsController.add
  176. )
  177. webRouter.post(
  178. '/user/emails/delete',
  179. AuthenticationController.requireLogin(),
  180. RateLimiterMiddleware.rateLimit({
  181. endpointName: 'delete-email',
  182. maxRequests: 10,
  183. timeInterval: 60,
  184. }),
  185. UserEmailsController.remove
  186. )
  187. webRouter.post(
  188. '/user/emails/default',
  189. AuthenticationController.requireLogin(),
  190. UserEmailsController.setDefault
  191. )
  192. webRouter.post(
  193. '/user/emails/endorse',
  194. AuthenticationController.requireLogin(),
  195. RateLimiterMiddleware.rateLimit({
  196. endpointName: 'endorse-email',
  197. maxRequests: 30,
  198. timeInterval: 60,
  199. }),
  200. UserEmailsController.endorse
  201. )
  202. }
  203. webRouter.get(
  204. '/user/sessions',
  205. AuthenticationController.requireLogin(),
  206. UserPagesController.sessionsPage
  207. )
  208. webRouter.post(
  209. '/user/sessions/clear',
  210. AuthenticationController.requireLogin(),
  211. UserController.clearSessions
  212. )
  213. webRouter.delete(
  214. '/user/newsletter/unsubscribe',
  215. AuthenticationController.requireLogin(),
  216. UserController.unsubscribe
  217. )
  218. webRouter.post(
  219. '/user/delete',
  220. RateLimiterMiddleware.rateLimit({
  221. endpointName: 'delete-user',
  222. maxRequests: 10,
  223. timeInterval: 60,
  224. }),
  225. AuthenticationController.requireLogin(),
  226. UserController.tryDeleteUser
  227. )
  228. webRouter.get(
  229. '/user/personal_info',
  230. AuthenticationController.requireLogin(),
  231. UserInfoController.getLoggedInUsersPersonalInfo
  232. )
  233. privateApiRouter.get(
  234. '/user/:user_id/personal_info',
  235. AuthenticationController.requirePrivateApiAuth(),
  236. UserInfoController.getPersonalInfo
  237. )
  238. webRouter.get(
  239. '/user/reconfirm',
  240. UserPagesController.renderReconfirmAccountPage
  241. )
  242. // for /user/reconfirm POST, see password router
  243. webRouter.get(
  244. '/user/tpds/queues',
  245. AuthenticationController.requireLogin(),
  246. TpdsController.getQueues
  247. )
  248. webRouter.get(
  249. '/user/projects',
  250. AuthenticationController.requireLogin(),
  251. ProjectController.userProjectsJson
  252. )
  253. webRouter.get(
  254. '/project/:Project_id/entities',
  255. AuthenticationController.requireLogin(),
  256. AuthorizationMiddleware.ensureUserCanReadProject,
  257. ProjectController.projectEntitiesJson
  258. )
  259. webRouter.get(
  260. '/project',
  261. AuthenticationController.requireLogin(),
  262. RateLimiterMiddleware.rateLimit({
  263. endpointName: 'open-dashboard',
  264. maxRequests: 30,
  265. timeInterval: 60,
  266. }),
  267. ProjectController.projectListPage
  268. )
  269. webRouter.post(
  270. '/project/new',
  271. AuthenticationController.requireLogin(),
  272. RateLimiterMiddleware.rateLimit({
  273. endpointName: 'create-project',
  274. maxRequests: 20,
  275. timeInterval: 60,
  276. }),
  277. ProjectController.newProject
  278. )
  279. for (const route of [
  280. // Keep the old route for continuous metrics
  281. '/Project/:Project_id',
  282. // New route for pdf-detach
  283. '/Project/:Project_id/:detachRole(detacher|detached)',
  284. ]) {
  285. webRouter.get(
  286. route,
  287. RateLimiterMiddleware.rateLimit({
  288. endpointName: 'open-project',
  289. params: ['Project_id'],
  290. maxRequests: 15,
  291. timeInterval: 60,
  292. }),
  293. AuthenticationController.validateUserSession(),
  294. AuthorizationMiddleware.ensureUserCanReadProject,
  295. ProjectController.loadEditor
  296. )
  297. }
  298. webRouter.head(
  299. '/Project/:Project_id/file/:File_id',
  300. AuthorizationMiddleware.ensureUserCanReadProject,
  301. FileStoreController.getFileHead
  302. )
  303. webRouter.get(
  304. '/Project/:Project_id/file/:File_id',
  305. AuthorizationMiddleware.ensureUserCanReadProject,
  306. FileStoreController.getFile
  307. )
  308. webRouter.post(
  309. '/project/:Project_id/settings',
  310. validate({ body: Joi.object() }),
  311. AuthorizationMiddleware.ensureUserCanWriteProjectSettings,
  312. ProjectController.updateProjectSettings
  313. )
  314. webRouter.post(
  315. '/project/:Project_id/settings/admin',
  316. AuthenticationController.requireLogin(),
  317. AuthorizationMiddleware.ensureUserCanAdminProject,
  318. ProjectController.updateProjectAdminSettings
  319. )
  320. webRouter.post(
  321. '/project/:Project_id/compile',
  322. RateLimiterMiddleware.rateLimit({
  323. endpointName: 'compile-project-http',
  324. params: ['Project_id'],
  325. maxRequests: 800,
  326. timeInterval: 60 * 60,
  327. }),
  328. AuthorizationMiddleware.ensureUserCanReadProject,
  329. CompileController.compile
  330. )
  331. webRouter.post(
  332. '/project/:Project_id/compile/stop',
  333. AuthorizationMiddleware.ensureUserCanReadProject,
  334. CompileController.stopCompile
  335. )
  336. // LEGACY: Used by the web download buttons, adds filename header, TODO: remove at some future date
  337. webRouter.get(
  338. '/project/:Project_id/output/output.pdf',
  339. AuthorizationMiddleware.ensureUserCanReadProject,
  340. CompileController.downloadPdf
  341. )
  342. // PDF Download button
  343. webRouter.get(
  344. /^\/download\/project\/([^/]*)\/output\/output\.pdf$/,
  345. function (req, res, next) {
  346. const params = { Project_id: req.params[0] }
  347. req.params = params
  348. next()
  349. },
  350. AuthorizationMiddleware.ensureUserCanReadProject,
  351. CompileController.downloadPdf
  352. )
  353. // PDF Download button for specific build
  354. webRouter.get(
  355. /^\/download\/project\/([^/]*)\/build\/([0-9a-f-]+)\/output\/output\.pdf$/,
  356. function (req, res, next) {
  357. const params = {
  358. Project_id: req.params[0],
  359. build_id: req.params[1],
  360. }
  361. req.params = params
  362. next()
  363. },
  364. AuthorizationMiddleware.ensureUserCanReadProject,
  365. CompileController.downloadPdf
  366. )
  367. // Align with limits defined in CompileController.downloadPdf
  368. const rateLimiterMiddlewareOutputFiles = RateLimiterMiddleware.rateLimit({
  369. endpointName: 'misc-output-download',
  370. params: ['Project_id'],
  371. maxRequests: 1000,
  372. timeInterval: 60 * 60,
  373. })
  374. // Used by the pdf viewers
  375. webRouter.get(
  376. /^\/project\/([^/]*)\/output\/(.*)$/,
  377. function (req, res, next) {
  378. const params = {
  379. Project_id: req.params[0],
  380. file: req.params[1],
  381. }
  382. req.params = params
  383. next()
  384. },
  385. rateLimiterMiddlewareOutputFiles,
  386. AuthorizationMiddleware.ensureUserCanReadProject,
  387. CompileController.getFileFromClsi
  388. )
  389. // direct url access to output files for a specific build (query string not required)
  390. webRouter.get(
  391. /^\/project\/([^/]*)\/build\/([0-9a-f-]+)\/output\/(.*)$/,
  392. function (req, res, next) {
  393. const params = {
  394. Project_id: req.params[0],
  395. build_id: req.params[1],
  396. file: req.params[2],
  397. }
  398. req.params = params
  399. next()
  400. },
  401. rateLimiterMiddlewareOutputFiles,
  402. AuthorizationMiddleware.ensureUserCanReadProject,
  403. CompileController.getFileFromClsi
  404. )
  405. // direct url access to output files for user but no build, to retrieve files when build fails
  406. webRouter.get(
  407. /^\/project\/([^/]*)\/user\/([0-9a-f-]+)\/output\/(.*)$/,
  408. function (req, res, next) {
  409. const params = {
  410. Project_id: req.params[0],
  411. user_id: req.params[1],
  412. file: req.params[2],
  413. }
  414. req.params = params
  415. next()
  416. },
  417. rateLimiterMiddlewareOutputFiles,
  418. AuthorizationMiddleware.ensureUserCanReadProject,
  419. CompileController.getFileFromClsi
  420. )
  421. // direct url access to output files for a specific user and build (query string not required)
  422. webRouter.get(
  423. /^\/project\/([^/]*)\/user\/([0-9a-f]+)\/build\/([0-9a-f-]+)\/output\/(.*)$/,
  424. function (req, res, next) {
  425. const params = {
  426. Project_id: req.params[0],
  427. user_id: req.params[1],
  428. build_id: req.params[2],
  429. file: req.params[3],
  430. }
  431. req.params = params
  432. next()
  433. },
  434. rateLimiterMiddlewareOutputFiles,
  435. AuthorizationMiddleware.ensureUserCanReadProject,
  436. CompileController.getFileFromClsi
  437. )
  438. webRouter.delete(
  439. '/project/:Project_id/output',
  440. validate({ query: { clsiserverid: Joi.string() } }),
  441. AuthorizationMiddleware.ensureUserCanReadProject,
  442. CompileController.deleteAuxFiles
  443. )
  444. webRouter.get(
  445. '/project/:Project_id/sync/code',
  446. AuthorizationMiddleware.ensureUserCanReadProject,
  447. CompileController.proxySyncCode
  448. )
  449. webRouter.get(
  450. '/project/:Project_id/sync/pdf',
  451. AuthorizationMiddleware.ensureUserCanReadProject,
  452. CompileController.proxySyncPdf
  453. )
  454. webRouter.get(
  455. '/project/:Project_id/wordcount',
  456. validate({ query: { clsiserverid: Joi.string() } }),
  457. AuthorizationMiddleware.ensureUserCanReadProject,
  458. CompileController.wordCount
  459. )
  460. webRouter.post(
  461. '/Project/:Project_id/archive',
  462. AuthorizationMiddleware.ensureUserCanReadProject,
  463. ProjectController.archiveProject
  464. )
  465. webRouter.delete(
  466. '/Project/:Project_id/archive',
  467. AuthorizationMiddleware.ensureUserCanReadProject,
  468. ProjectController.unarchiveProject
  469. )
  470. webRouter.post(
  471. '/project/:project_id/trash',
  472. AuthorizationMiddleware.ensureUserCanReadProject,
  473. ProjectController.trashProject
  474. )
  475. webRouter.delete(
  476. '/project/:project_id/trash',
  477. AuthorizationMiddleware.ensureUserCanReadProject,
  478. ProjectController.untrashProject
  479. )
  480. webRouter.delete(
  481. '/Project/:Project_id',
  482. AuthenticationController.requireLogin(),
  483. AuthorizationMiddleware.ensureUserCanAdminProject,
  484. ProjectController.deleteProject
  485. )
  486. webRouter.post(
  487. '/Project/:Project_id/restore',
  488. AuthenticationController.requireLogin(),
  489. AuthorizationMiddleware.ensureUserCanAdminProject,
  490. ProjectController.restoreProject
  491. )
  492. webRouter.post(
  493. '/Project/:Project_id/clone',
  494. AuthorizationMiddleware.ensureUserCanReadProject,
  495. ProjectController.cloneProject
  496. )
  497. webRouter.post(
  498. '/project/:Project_id/rename',
  499. AuthenticationController.requireLogin(),
  500. AuthorizationMiddleware.ensureUserCanAdminProject,
  501. ProjectController.renameProject
  502. )
  503. webRouter.get(
  504. '/project/:Project_id/updates',
  505. AuthorizationMiddleware.ensureUserCanReadProject,
  506. HistoryController.selectHistoryApi,
  507. HistoryController.proxyToHistoryApiAndInjectUserDetails
  508. )
  509. webRouter.get(
  510. '/project/:Project_id/doc/:doc_id/diff',
  511. AuthorizationMiddleware.ensureUserCanReadProject,
  512. HistoryController.selectHistoryApi,
  513. HistoryController.proxyToHistoryApi
  514. )
  515. webRouter.get(
  516. '/project/:Project_id/diff',
  517. AuthorizationMiddleware.ensureUserCanReadProject,
  518. HistoryController.selectHistoryApi,
  519. HistoryController.proxyToHistoryApiAndInjectUserDetails
  520. )
  521. webRouter.get(
  522. '/project/:Project_id/filetree/diff',
  523. AuthorizationMiddleware.ensureUserCanReadProject,
  524. HistoryController.selectHistoryApi,
  525. HistoryController.proxyToHistoryApi
  526. )
  527. webRouter.post(
  528. '/project/:Project_id/doc/:doc_id/version/:version_id/restore',
  529. AuthorizationMiddleware.ensureUserCanWriteProjectContent,
  530. HistoryController.selectHistoryApi,
  531. HistoryController.proxyToHistoryApi
  532. )
  533. webRouter.post(
  534. '/project/:project_id/doc/:doc_id/restore',
  535. AuthorizationMiddleware.ensureUserCanWriteProjectContent,
  536. HistoryController.restoreDocFromDeletedDoc
  537. )
  538. webRouter.post(
  539. '/project/:project_id/restore_file',
  540. AuthorizationMiddleware.ensureUserCanWriteProjectContent,
  541. HistoryController.restoreFileFromV2
  542. )
  543. webRouter.get(
  544. '/project/:project_id/version/:version/zip',
  545. RateLimiterMiddleware.rateLimit({
  546. endpointName: 'download-project-revision',
  547. maxRequests: 30,
  548. timeInterval: 60 * 60,
  549. }),
  550. AuthorizationMiddleware.ensureUserCanReadProject,
  551. HistoryController.downloadZipOfVersion
  552. )
  553. privateApiRouter.post(
  554. '/project/:Project_id/history/resync',
  555. AuthenticationController.requirePrivateApiAuth(),
  556. HistoryController.resyncProjectHistory
  557. )
  558. webRouter.get(
  559. '/project/:Project_id/labels',
  560. AuthorizationMiddleware.ensureUserCanReadProject,
  561. HistoryController.selectHistoryApi,
  562. HistoryController.ensureProjectHistoryEnabled,
  563. HistoryController.getLabels
  564. )
  565. webRouter.post(
  566. '/project/:Project_id/labels',
  567. AuthorizationMiddleware.ensureUserCanWriteProjectContent,
  568. HistoryController.selectHistoryApi,
  569. HistoryController.ensureProjectHistoryEnabled,
  570. HistoryController.createLabel
  571. )
  572. webRouter.delete(
  573. '/project/:Project_id/labels/:label_id',
  574. AuthorizationMiddleware.ensureUserCanWriteProjectContent,
  575. HistoryController.selectHistoryApi,
  576. HistoryController.ensureProjectHistoryEnabled,
  577. HistoryController.deleteLabel
  578. )
  579. webRouter.post(
  580. '/project/:project_id/export/:brand_variation_id',
  581. AuthorizationMiddleware.ensureUserCanWriteProjectContent,
  582. ExportsController.exportProject
  583. )
  584. webRouter.get(
  585. '/project/:project_id/export/:export_id',
  586. AuthorizationMiddleware.ensureUserCanWriteProjectContent,
  587. ExportsController.exportStatus
  588. )
  589. webRouter.get(
  590. '/project/:project_id/export/:export_id/:type',
  591. AuthorizationMiddleware.ensureUserCanWriteProjectContent,
  592. ExportsController.exportDownload
  593. )
  594. webRouter.get(
  595. '/Project/:Project_id/download/zip',
  596. AuthorizationMiddleware.ensureUserCanReadProject,
  597. ProjectDownloadsController.downloadProject
  598. )
  599. webRouter.get(
  600. '/project/download/zip',
  601. AuthorizationMiddleware.ensureUserCanReadMultipleProjects,
  602. ProjectDownloadsController.downloadMultipleProjects
  603. )
  604. webRouter.get(
  605. '/project/:project_id/metadata',
  606. AuthorizationMiddleware.ensureUserCanReadProject,
  607. Settings.allowAnonymousReadAndWriteSharing
  608. ? (req, res, next) => {
  609. next()
  610. }
  611. : AuthenticationController.requireLogin(),
  612. MetaController.getMetadata
  613. )
  614. webRouter.post(
  615. '/project/:project_id/doc/:doc_id/metadata',
  616. AuthorizationMiddleware.ensureUserCanReadProject,
  617. Settings.allowAnonymousReadAndWriteSharing
  618. ? (req, res, next) => {
  619. next()
  620. }
  621. : AuthenticationController.requireLogin(),
  622. MetaController.broadcastMetadataForDoc
  623. )
  624. privateApiRouter.post(
  625. '/internal/expire-deleted-projects-after-duration',
  626. AuthenticationController.requirePrivateApiAuth(),
  627. ProjectController.expireDeletedProjectsAfterDuration
  628. )
  629. privateApiRouter.post(
  630. '/internal/expire-deleted-users-after-duration',
  631. AuthenticationController.requirePrivateApiAuth(),
  632. UserController.expireDeletedUsersAfterDuration
  633. )
  634. privateApiRouter.post(
  635. '/internal/project/:projectId/expire-deleted-project',
  636. AuthenticationController.requirePrivateApiAuth(),
  637. ProjectController.expireDeletedProject
  638. )
  639. privateApiRouter.post(
  640. '/internal/users/:userId/expire',
  641. AuthenticationController.requirePrivateApiAuth(),
  642. UserController.expireDeletedUser
  643. )
  644. privateApiRouter.get(
  645. '/user/:userId/tag',
  646. AuthenticationController.requirePrivateApiAuth(),
  647. TagsController.apiGetAllTags
  648. )
  649. webRouter.get(
  650. '/tag',
  651. AuthenticationController.requireLogin(),
  652. TagsController.getAllTags
  653. )
  654. webRouter.post(
  655. '/tag',
  656. AuthenticationController.requireLogin(),
  657. RateLimiterMiddleware.rateLimit({
  658. endpointName: 'create-tag',
  659. maxRequests: 30,
  660. timeInterval: 60,
  661. }),
  662. TagsController.createTag
  663. )
  664. webRouter.post(
  665. '/tag/:tagId/rename',
  666. AuthenticationController.requireLogin(),
  667. RateLimiterMiddleware.rateLimit({
  668. endpointName: 'rename-tag',
  669. maxRequests: 30,
  670. timeInterval: 60,
  671. }),
  672. TagsController.renameTag
  673. )
  674. webRouter.delete(
  675. '/tag/:tagId',
  676. AuthenticationController.requireLogin(),
  677. RateLimiterMiddleware.rateLimit({
  678. endpointName: 'delete-tag',
  679. maxRequests: 30,
  680. timeInterval: 60,
  681. }),
  682. TagsController.deleteTag
  683. )
  684. webRouter.post(
  685. '/tag/:tagId/project/:projectId',
  686. AuthenticationController.requireLogin(),
  687. RateLimiterMiddleware.rateLimit({
  688. endpointName: 'add-project-to-tag',
  689. maxRequests: 30,
  690. timeInterval: 60,
  691. }),
  692. TagsController.addProjectToTag
  693. )
  694. webRouter.delete(
  695. '/tag/:tagId/project/:projectId',
  696. AuthenticationController.requireLogin(),
  697. RateLimiterMiddleware.rateLimit({
  698. endpointName: 'remove-project-from-tag',
  699. maxRequests: 30,
  700. timeInterval: 60,
  701. }),
  702. TagsController.removeProjectFromTag
  703. )
  704. webRouter.get(
  705. '/notifications',
  706. AuthenticationController.requireLogin(),
  707. NotificationsController.getAllUnreadNotifications
  708. )
  709. webRouter.delete(
  710. '/notifications/:notificationId',
  711. AuthenticationController.requireLogin(),
  712. NotificationsController.markNotificationAsRead
  713. )
  714. // Deprecated in favour of /internal/project/:project_id but still used by versioning
  715. privateApiRouter.get(
  716. '/project/:project_id/details',
  717. AuthenticationController.requirePrivateApiAuth(),
  718. ProjectApiController.getProjectDetails
  719. )
  720. // New 'stable' /internal API end points
  721. privateApiRouter.get(
  722. '/internal/project/:project_id',
  723. AuthenticationController.requirePrivateApiAuth(),
  724. ProjectApiController.getProjectDetails
  725. )
  726. privateApiRouter.get(
  727. '/internal/project/:Project_id/zip',
  728. AuthenticationController.requirePrivateApiAuth(),
  729. ProjectDownloadsController.downloadProject
  730. )
  731. privateApiRouter.get(
  732. '/internal/project/:project_id/compile/pdf',
  733. AuthenticationController.requirePrivateApiAuth(),
  734. CompileController.compileAndDownloadPdf
  735. )
  736. privateApiRouter.post(
  737. '/internal/deactivateOldProjects',
  738. AuthenticationController.requirePrivateApiAuth(),
  739. InactiveProjectController.deactivateOldProjects
  740. )
  741. privateApiRouter.post(
  742. '/internal/project/:project_id/deactivate',
  743. AuthenticationController.requirePrivateApiAuth(),
  744. InactiveProjectController.deactivateProject
  745. )
  746. privateApiRouter.get(
  747. /^\/internal\/project\/([^/]*)\/output\/(.*)$/,
  748. function (req, res, next) {
  749. const params = {
  750. Project_id: req.params[0],
  751. file: req.params[1],
  752. }
  753. req.params = params
  754. next()
  755. },
  756. AuthenticationController.requirePrivateApiAuth(),
  757. CompileController.getFileFromClsi
  758. )
  759. privateApiRouter.get(
  760. '/project/:Project_id/doc/:doc_id',
  761. AuthenticationController.requirePrivateApiAuth(),
  762. DocumentController.getDocument
  763. )
  764. privateApiRouter.post(
  765. '/project/:Project_id/doc/:doc_id',
  766. AuthenticationController.requirePrivateApiAuth(),
  767. DocumentController.setDocument
  768. )
  769. privateApiRouter.post(
  770. '/user/:user_id/update/*',
  771. AuthenticationController.requirePrivateApiAuth(),
  772. TpdsController.mergeUpdate
  773. )
  774. privateApiRouter.delete(
  775. '/user/:user_id/update/*',
  776. AuthenticationController.requirePrivateApiAuth(),
  777. TpdsController.deleteUpdate
  778. )
  779. privateApiRouter.post(
  780. '/project/:project_id/contents/*',
  781. AuthenticationController.requirePrivateApiAuth(),
  782. TpdsController.updateProjectContents
  783. )
  784. privateApiRouter.delete(
  785. '/project/:project_id/contents/*',
  786. AuthenticationController.requirePrivateApiAuth(),
  787. TpdsController.deleteProjectContents
  788. )
  789. webRouter.post(
  790. '/spelling/check',
  791. AuthenticationController.requireLogin(),
  792. SpellingController.proxyRequestToSpellingApi
  793. )
  794. webRouter.post(
  795. '/spelling/learn',
  796. validate({
  797. body: Joi.object({
  798. word: Joi.string().required(),
  799. }),
  800. }),
  801. AuthenticationController.requireLogin(),
  802. SpellingController.learn
  803. )
  804. webRouter.get(
  805. '/project/:project_id/messages',
  806. AuthorizationMiddleware.blockRestrictedUserFromProject,
  807. AuthorizationMiddleware.ensureUserCanReadProject,
  808. ChatController.getMessages
  809. )
  810. webRouter.post(
  811. '/project/:project_id/messages',
  812. AuthorizationMiddleware.blockRestrictedUserFromProject,
  813. AuthorizationMiddleware.ensureUserCanReadProject,
  814. RateLimiterMiddleware.rateLimit({
  815. endpointName: 'send-chat-message',
  816. maxRequests: 100,
  817. timeInterval: 60,
  818. }),
  819. ChatController.sendMessage
  820. )
  821. webRouter.post(
  822. '/project/:Project_id/references/index',
  823. AuthorizationMiddleware.ensureUserCanReadProject,
  824. RateLimiterMiddleware.rateLimit({
  825. endpointName: 'index-project-references',
  826. maxRequests: 30,
  827. timeInterval: 60,
  828. }),
  829. ReferencesController.index
  830. )
  831. webRouter.post(
  832. '/project/:Project_id/references/indexAll',
  833. AuthorizationMiddleware.ensureUserCanReadProject,
  834. RateLimiterMiddleware.rateLimit({
  835. endpointName: 'index-all-project-references',
  836. maxRequests: 30,
  837. timeInterval: 60,
  838. }),
  839. ReferencesController.indexAll
  840. )
  841. // disable beta program while v2 is in beta
  842. webRouter.get(
  843. '/beta/participate',
  844. AuthenticationController.requireLogin(),
  845. BetaProgramController.optInPage
  846. )
  847. webRouter.post(
  848. '/beta/opt-in',
  849. AuthenticationController.requireLogin(),
  850. BetaProgramController.optIn
  851. )
  852. webRouter.post(
  853. '/beta/opt-out',
  854. AuthenticationController.requireLogin(),
  855. BetaProgramController.optOut
  856. )
  857. // New "api" endpoints. Started as a way for v1 to call over to v2 (for
  858. // long-term features, as opposed to the nominally temporary ones in the
  859. // overleaf-integration module), but may expand beyond that role.
  860. publicApiRouter.post(
  861. '/api/clsi/compile/:submission_id',
  862. AuthenticationController.requirePrivateApiAuth(),
  863. CompileController.compileSubmission
  864. )
  865. publicApiRouter.get(
  866. /^\/api\/clsi\/compile\/([^/]*)\/build\/([0-9a-f-]+)\/output\/(.*)$/,
  867. function (req, res, next) {
  868. const params = {
  869. submission_id: req.params[0],
  870. build_id: req.params[1],
  871. file: req.params[2],
  872. }
  873. req.params = params
  874. next()
  875. },
  876. AuthenticationController.requirePrivateApiAuth(),
  877. CompileController.getFileFromClsiWithoutUser
  878. )
  879. publicApiRouter.post(
  880. '/api/institutions/confirm_university_domain',
  881. RateLimiterMiddleware.rateLimit({
  882. endpointName: 'confirm-university-domain',
  883. maxRequests: 1,
  884. timeInterval: 60,
  885. }),
  886. AuthenticationController.requirePrivateApiAuth(),
  887. InstitutionsController.confirmDomain
  888. )
  889. webRouter.get('/chrome', function (req, res, next) {
  890. // Match v1 behaviour - this is used for a Chrome web app
  891. if (SessionManager.isUserLoggedIn(req.session)) {
  892. res.redirect('/project')
  893. } else {
  894. res.redirect('/register')
  895. }
  896. })
  897. // Admin Stuff
  898. webRouter.get(
  899. '/admin',
  900. AuthorizationMiddleware.ensureUserIsSiteAdmin,
  901. AdminController.index
  902. )
  903. webRouter.get(
  904. '/admin/user',
  905. AuthorizationMiddleware.ensureUserIsSiteAdmin,
  906. (req, res) => res.redirect('/admin/register')
  907. ) // this gets removed by admin-panel addon
  908. webRouter.get(
  909. '/admin/register',
  910. AuthorizationMiddleware.ensureUserIsSiteAdmin,
  911. AdminController.registerNewUser
  912. )
  913. webRouter.post(
  914. '/admin/register',
  915. AuthorizationMiddleware.ensureUserIsSiteAdmin,
  916. UserController.register
  917. )
  918. if (!Features.hasFeature('saas')) {
  919. webRouter.post(
  920. '/admin/openEditor',
  921. AuthorizationMiddleware.ensureUserIsSiteAdmin,
  922. AdminController.openEditor
  923. )
  924. webRouter.post(
  925. '/admin/closeEditor',
  926. AuthorizationMiddleware.ensureUserIsSiteAdmin,
  927. AdminController.closeEditor
  928. )
  929. webRouter.post(
  930. '/admin/disconnectAllUsers',
  931. AuthorizationMiddleware.ensureUserIsSiteAdmin,
  932. AdminController.disconnectAllUsers
  933. )
  934. }
  935. webRouter.post(
  936. '/admin/flushProjectToTpds',
  937. AuthorizationMiddleware.ensureUserIsSiteAdmin,
  938. AdminController.flushProjectToTpds
  939. )
  940. webRouter.post(
  941. '/admin/pollDropboxForUser',
  942. AuthorizationMiddleware.ensureUserIsSiteAdmin,
  943. AdminController.pollDropboxForUser
  944. )
  945. webRouter.post(
  946. '/admin/messages',
  947. AuthorizationMiddleware.ensureUserIsSiteAdmin,
  948. AdminController.createMessage
  949. )
  950. webRouter.post(
  951. '/admin/messages/clear',
  952. AuthorizationMiddleware.ensureUserIsSiteAdmin,
  953. AdminController.clearMessages
  954. )
  955. webRouter.post(
  956. '/admin/unregisterServiceWorker',
  957. AuthorizationMiddleware.ensureUserIsSiteAdmin,
  958. AdminController.unregisterServiceWorker
  959. )
  960. privateApiRouter.get('/perfTest', (req, res) => res.send('hello'))
  961. publicApiRouter.get('/status', (req, res) => {
  962. if (!Settings.siteIsOpen) {
  963. res.send('web site is closed (web)')
  964. } else if (!Settings.editorIsOpen) {
  965. res.send('web editor is closed (web)')
  966. } else {
  967. res.send('web sharelatex is alive (web)')
  968. }
  969. })
  970. privateApiRouter.get('/status', (req, res) =>
  971. res.send('web sharelatex is alive (api)')
  972. )
  973. // used by kubernetes health-check and acceptance tests
  974. webRouter.get('/dev/csrf', (req, res) => res.send(res.locals.csrfToken))
  975. publicApiRouter.get(
  976. '/health_check',
  977. HealthCheckController.checkActiveHandles,
  978. HealthCheckController.check
  979. )
  980. privateApiRouter.get(
  981. '/health_check',
  982. HealthCheckController.checkActiveHandles,
  983. HealthCheckController.checkApi
  984. )
  985. publicApiRouter.get(
  986. '/health_check/api',
  987. HealthCheckController.checkActiveHandles,
  988. HealthCheckController.checkApi
  989. )
  990. privateApiRouter.get(
  991. '/health_check/api',
  992. HealthCheckController.checkActiveHandles,
  993. HealthCheckController.checkApi
  994. )
  995. publicApiRouter.get(
  996. '/health_check/full',
  997. HealthCheckController.checkActiveHandles,
  998. HealthCheckController.check
  999. )
  1000. privateApiRouter.get(
  1001. '/health_check/full',
  1002. HealthCheckController.checkActiveHandles,
  1003. HealthCheckController.check
  1004. )
  1005. publicApiRouter.get('/health_check/redis', HealthCheckController.checkRedis)
  1006. privateApiRouter.get('/health_check/redis', HealthCheckController.checkRedis)
  1007. publicApiRouter.get('/health_check/mongo', HealthCheckController.checkMongo)
  1008. privateApiRouter.get('/health_check/mongo', HealthCheckController.checkMongo)
  1009. webRouter.get(
  1010. '/status/compiler/:Project_id',
  1011. RateLimiterMiddleware.rateLimit({
  1012. endpointName: 'status-compiler',
  1013. maxRequests: 10,
  1014. timeInterval: 60,
  1015. }),
  1016. AuthorizationMiddleware.ensureUserCanReadProject,
  1017. function (req, res) {
  1018. const projectId = req.params.Project_id
  1019. const sendRes = _.once(function (statusCode, message) {
  1020. res.status(statusCode)
  1021. res.send(message)
  1022. ClsiCookieManager.clearServerId(projectId)
  1023. }) // force every compile to a new server
  1024. // set a timeout
  1025. let handler = setTimeout(function () {
  1026. sendRes(500, 'Compiler timed out')
  1027. handler = null
  1028. }, 10000)
  1029. // use a valid user id for testing
  1030. const testUserId = '123456789012345678901234'
  1031. // run the compile
  1032. CompileManager.compile(
  1033. projectId,
  1034. testUserId,
  1035. {},
  1036. function (error, status) {
  1037. if (handler) {
  1038. clearTimeout(handler)
  1039. }
  1040. if (error) {
  1041. sendRes(500, `Compiler returned error ${error.message}`)
  1042. } else if (status === 'success') {
  1043. sendRes(200, 'Compiler returned in less than 10 seconds')
  1044. } else {
  1045. sendRes(500, `Compiler returned failure ${status}`)
  1046. }
  1047. }
  1048. )
  1049. }
  1050. )
  1051. webRouter.get('/no-cache', function (req, res, next) {
  1052. res.header('Cache-Control', 'max-age=0')
  1053. res.sendStatus(404)
  1054. })
  1055. webRouter.get('/oops-express', (req, res, next) =>
  1056. next(new Error('Test error'))
  1057. )
  1058. webRouter.get('/oops-internal', function (req, res, next) {
  1059. throw new Error('Test error')
  1060. })
  1061. webRouter.get('/oops-mongo', (req, res, next) =>
  1062. require('./models/Project').Project.findOne({}, function () {
  1063. throw new Error('Test error')
  1064. })
  1065. )
  1066. privateApiRouter.get('/opps-small', function (req, res, next) {
  1067. logger.err('test error occured')
  1068. res.sendStatus(200)
  1069. })
  1070. webRouter.post('/error/client', function (req, res, next) {
  1071. logger.warn(
  1072. { err: req.body.error, meta: req.body.meta },
  1073. 'client side error'
  1074. )
  1075. metrics.inc('client-side-error')
  1076. res.sendStatus(204)
  1077. })
  1078. webRouter.get(
  1079. `/read/:token(${TokenAccessController.READ_ONLY_TOKEN_PATTERN})`,
  1080. RateLimiterMiddleware.rateLimit({
  1081. endpointName: 'read-only-token',
  1082. maxRequests: 15,
  1083. timeInterval: 60,
  1084. }),
  1085. AnalyticsRegistrationSourceMiddleware.setSource(
  1086. 'collaboration',
  1087. 'link-sharing'
  1088. ),
  1089. TokenAccessController.tokenAccessPage,
  1090. AnalyticsRegistrationSourceMiddleware.clearSource()
  1091. )
  1092. webRouter.get(
  1093. `/:token(${TokenAccessController.READ_AND_WRITE_TOKEN_PATTERN})`,
  1094. RateLimiterMiddleware.rateLimit({
  1095. endpointName: 'read-and-write-token',
  1096. maxRequests: 15,
  1097. timeInterval: 60,
  1098. }),
  1099. AnalyticsRegistrationSourceMiddleware.setSource(
  1100. 'collaboration',
  1101. 'link-sharing'
  1102. ),
  1103. TokenAccessController.tokenAccessPage,
  1104. AnalyticsRegistrationSourceMiddleware.clearSource()
  1105. )
  1106. webRouter.post(
  1107. `/:token(${TokenAccessController.READ_AND_WRITE_TOKEN_PATTERN})/grant`,
  1108. RateLimiterMiddleware.rateLimit({
  1109. endpointName: 'grant-token-access-read-write',
  1110. maxRequests: 10,
  1111. timeInterval: 60,
  1112. }),
  1113. TokenAccessController.grantTokenAccessReadAndWrite
  1114. )
  1115. webRouter.post(
  1116. `/read/:token(${TokenAccessController.READ_ONLY_TOKEN_PATTERN})/grant`,
  1117. RateLimiterMiddleware.rateLimit({
  1118. endpointName: 'grant-token-access-read-only',
  1119. maxRequests: 10,
  1120. timeInterval: 60,
  1121. }),
  1122. TokenAccessController.grantTokenAccessReadOnly
  1123. )
  1124. webRouter.get('/unsupported-browser', renderUnsupportedBrowserPage)
  1125. webRouter.get('*', ErrorController.notFound)
  1126. }