app.coffee 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. CompileController = require "./app/js/CompileController"
  2. Settings = require "settings-sharelatex"
  3. logger = require "logger-sharelatex"
  4. logger.initialize("clsi")
  5. smokeTest = require "smoke-test-sharelatex"
  6. Path = require "path"
  7. fs = require "fs"
  8. Metrics = require "metrics-sharelatex"
  9. Metrics.initialize("clsi")
  10. Metrics.open_sockets.monitor(logger)
  11. ProjectPersistenceManager = require "./app/js/ProjectPersistenceManager"
  12. OutputCacheManager = require "./app/js/OutputCacheManager"
  13. require("./app/js/db").sync()
  14. express = require "express"
  15. bodyParser = require "body-parser"
  16. app = express()
  17. app.use Metrics.http.monitor(logger)
  18. # Compile requests can take longer than the default two
  19. # minutes (including file download time), so bump up the
  20. # timeout a bit.
  21. TIMEOUT = 6 * 60 * 1000
  22. app.use (req, res, next) ->
  23. req.setTimeout TIMEOUT
  24. res.setTimeout TIMEOUT
  25. next()
  26. app.post "/project/:project_id/compile", bodyParser.json(limit: "5mb"), CompileController.compile
  27. app.delete "/project/:project_id", CompileController.clearCache
  28. app.get "/project/:project_id/sync/code", CompileController.syncFromCode
  29. app.get "/project/:project_id/sync/pdf", CompileController.syncFromPdf
  30. ForbidSymlinks = require "./app/js/StaticServerForbidSymlinks"
  31. # create a static server which does not allow access to any symlinks
  32. # avoids possible mismatch of root directory between middleware check
  33. # and serving the files
  34. staticServer = ForbidSymlinks express.static, Settings.path.compilesDir, setHeaders: (res, path, stat) ->
  35. if Path.basename(path) == "output.pdf"
  36. res.set("Content-Type", "application/pdf")
  37. # Calculate an etag in the same way as nginx
  38. # https://github.com/tj/send/issues/65
  39. etag = (path, stat) ->
  40. '"' + Math.ceil(+stat.mtime / 1000).toString(16) +
  41. '-' + Number(stat.size).toString(16) + '"'
  42. res.set("Etag", etag(path, stat))
  43. else
  44. # Force plain treatment of other file types to prevent hosting of HTTP/JS files
  45. # that could be used in same-origin/XSS attacks.
  46. res.set("Content-Type", "text/plain")
  47. app.get "/project/:project_id/output/*", (req, res, next) ->
  48. if req.query?.build? && req.query.build.match(OutputCacheManager.BUILD_REGEX)
  49. # for specific build get the path from the OutputCacheManager (e.g. .clsi/buildId)
  50. req.url = "/#{req.params.project_id}/" + OutputCacheManager.path(req.query.build, "/#{req.params[0]}")
  51. else
  52. req.url = "/#{req.params.project_id}/#{req.params[0]}"
  53. staticServer(req, res, next)
  54. app.get "/status", (req, res, next) ->
  55. res.send "CLSI is alive\n"
  56. resCacher =
  57. contentType:(@setContentType)->
  58. send:(@code, @body)->
  59. #default the server to be down
  60. code:500
  61. body:{}
  62. setContentType:"application/json"
  63. if Settings.smokeTest
  64. do runSmokeTest = ->
  65. logger.log("running smoke tests")
  66. smokeTest.run(require.resolve(__dirname + "/test/smoke/js/SmokeTests.js"))({}, resCacher)
  67. setTimeout(runSmokeTest, 20 * 1000)
  68. app.get "/health_check", (req, res)->
  69. res.contentType(resCacher?.setContentType)
  70. res.send resCacher?.code, resCacher?.body
  71. profiler = require "v8-profiler"
  72. app.get "/profile", (req, res) ->
  73. time = parseInt(req.query.time || "1000")
  74. profiler.startProfiling("test")
  75. setTimeout () ->
  76. profile = profiler.stopProfiling("test")
  77. res.json(profile)
  78. , time
  79. app.get "/heapdump", (req, res)->
  80. require('heapdump').writeSnapshot '/tmp/' + Date.now() + '.clsi.heapsnapshot', (err, filename)->
  81. res.send filename
  82. app.use (error, req, res, next) ->
  83. logger.error err: error, "server error"
  84. res.send error?.statusCode || 500
  85. app.listen port = (Settings.internal?.clsi?.port or 3013), host = (Settings.internal?.clsi?.host or "localhost"), (error) ->
  86. logger.info "CLSI starting up, listening on #{host}:#{port}"
  87. setInterval () ->
  88. ProjectPersistenceManager.clearExpiredProjects()
  89. , tenMinutes = 10 * 60 * 1000