router.js 38 KB

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