mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-30 03:53:05 -05:00
Differentiate between timeouts, server errors, and LaTeX failures in client
This commit is contained in:
parent
6c5a2c2740
commit
7fed2155be
8 changed files with 112 additions and 26 deletions
|
@ -4,8 +4,22 @@ CompileManager = require("./CompileManager")
|
||||||
logger = require "logger-sharelatex"
|
logger = require "logger-sharelatex"
|
||||||
request = require "request"
|
request = require "request"
|
||||||
Settings = require "settings-sharelatex"
|
Settings = require "settings-sharelatex"
|
||||||
|
AuthenticationController = require "../Authentication/AuthenticationController"
|
||||||
|
|
||||||
module.exports = CompileController =
|
module.exports = CompileController =
|
||||||
|
compile: (req, res, next = (error) ->) ->
|
||||||
|
project_id = req.params.Project_id
|
||||||
|
isAutoCompile = !!req.query?.auto_compile
|
||||||
|
AuthenticationController.getLoggedInUserId req, (error, user_id) ->
|
||||||
|
return next(error) if error?
|
||||||
|
CompileManager.compile project_id, user_id, { isAutoCompile }, (error, status, outputFiles) ->
|
||||||
|
return next(error) if error?
|
||||||
|
res.contentType("application/json")
|
||||||
|
res.send 200, JSON.stringify {
|
||||||
|
status: status
|
||||||
|
outputFiles: outputFiles
|
||||||
|
}
|
||||||
|
|
||||||
downloadPdf: (req, res, next = (error) ->)->
|
downloadPdf: (req, res, next = (error) ->)->
|
||||||
Metrics.inc "pdf-downloads"
|
Metrics.inc "pdf-downloads"
|
||||||
project_id = req.params.Project_id
|
project_id = req.params.Project_id
|
||||||
|
|
|
@ -19,8 +19,7 @@ module.exports = CompileManager =
|
||||||
|
|
||||||
@_checkIfAutoCompileLimitHasBeenHit opt.isAutoCompile, (err, canCompile)->
|
@_checkIfAutoCompileLimitHasBeenHit opt.isAutoCompile, (err, canCompile)->
|
||||||
if !canCompile
|
if !canCompile
|
||||||
err = {rateLimitHit:true}
|
return callback null, "autocompile-backoff", []
|
||||||
return callback(err)
|
|
||||||
logger.log project_id: project_id, user_id: user_id, "compiling project"
|
logger.log project_id: project_id, user_id: user_id, "compiling project"
|
||||||
CompileManager._checkIfRecentlyCompiled project_id, user_id, (error, recentlyCompiled) ->
|
CompileManager._checkIfRecentlyCompiled project_id, user_id, (error, recentlyCompiled) ->
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
|
|
|
@ -104,6 +104,7 @@ module.exports = class Router
|
||||||
app.get '/Project/:Project_id', SecurityManager.requestCanAccessProject, ProjectController.loadEditor
|
app.get '/Project/:Project_id', SecurityManager.requestCanAccessProject, ProjectController.loadEditor
|
||||||
app.get '/Project/:Project_id/file/:File_id', SecurityManager.requestCanAccessProject, FileStoreController.getFile
|
app.get '/Project/:Project_id/file/:File_id', SecurityManager.requestCanAccessProject, FileStoreController.getFile
|
||||||
|
|
||||||
|
app.post '/project/:Project_id/compile', SecurityManager.requestCanAccessProject, CompileController.compile
|
||||||
app.get '/Project/:Project_id/output/output.pdf', SecurityManager.requestCanAccessProject, CompileController.downloadPdf
|
app.get '/Project/:Project_id/output/output.pdf', SecurityManager.requestCanAccessProject, CompileController.downloadPdf
|
||||||
app.get /^\/project\/([^\/]*)\/output\/(.*)$/,
|
app.get /^\/project\/([^\/]*)\/output\/(.*)$/,
|
||||||
((req, res, next) ->
|
((req, res, next) ->
|
||||||
|
@ -320,6 +321,7 @@ module.exports = class Router
|
||||||
AuthorizationManager.ensureClientCanAdminProject client, (error, project_id) =>
|
AuthorizationManager.ensureClientCanAdminProject client, (error, project_id) =>
|
||||||
EditorController.setPublicAccessLevel(project_id, newAccessLevel, callback)
|
EditorController.setPublicAccessLevel(project_id, newAccessLevel, callback)
|
||||||
|
|
||||||
|
# Deprecated and can be removed after deploying.
|
||||||
client.on 'pdfProject', (opts, callback)->
|
client.on 'pdfProject', (opts, callback)->
|
||||||
AuthorizationManager.ensureClientCanViewProject client, (error, project_id) =>
|
AuthorizationManager.ensureClientCanViewProject client, (error, project_id) =>
|
||||||
CompileManager.compile project_id, user._id, opts, (error, status, outputFiles) ->
|
CompileManager.compile project_id, user._id, opts, (error, status, outputFiles) ->
|
||||||
|
|
|
@ -234,14 +234,19 @@
|
||||||
|
|
||||||
script(type="text/template")#compileErrorTemplate
|
script(type="text/template")#compileErrorTemplate
|
||||||
li.alert.alert-error
|
li.alert.alert-error
|
||||||
strong Compile Error:
|
strong Server Error.
|
||||||
span Sorry, something went wrong and the project could not be compiled. This may be due to our compiler being overloaded or an incompatibility with the project. Please try again in a few moments and if the problem continues let us know via the feedback tab at the top.
|
span Sorry, something went wrong and the project could not be compiled. Please try again in a few moments and if the problem continues please contact support.
|
||||||
|
|
||||||
script(type="text/template")#compileFailedTemplate
|
script(type="text/template")#compileFailedTemplate
|
||||||
li.alert.alert-error
|
li.alert.alert-error
|
||||||
strong Ooops, your LaTeX code couldn't compile for some reason. Please check these errors for details:
|
strong Ooops, your LaTeX code couldn't compile for some reason. Please check the errors below for details, or view the raw log.
|
||||||
|
|
||||||
|
|
||||||
|
script(type="text/template")#compileTimeoutTemplate
|
||||||
|
li.alert.alert-error
|
||||||
|
strong Timed out.
|
||||||
|
span Sorry, your compile was taking too long and timed out.
|
||||||
|
| This may be due to a large number of high-res images, or lots of complicated diagrams.
|
||||||
|
| Please try to make your document simpler, or contact support for help.
|
||||||
script(type="text/template")#compileLogEntryTemplate
|
script(type="text/template")#compileLogEntryTemplate
|
||||||
li.alert.clickable(class="alert-{{ type }}")
|
li.alert.clickable(class="alert-{{ type }}")
|
||||||
strong {{ title }}:
|
strong {{ title }}:
|
||||||
|
|
|
@ -37,6 +37,7 @@ define [
|
||||||
compileSuccess: $('#compileSuccessTemplate').html()
|
compileSuccess: $('#compileSuccessTemplate').html()
|
||||||
compileFailed: $('#compileFailedTemplate').html()
|
compileFailed: $('#compileFailedTemplate').html()
|
||||||
compileError: $('#compileErrorTemplate').html()
|
compileError: $('#compileErrorTemplate').html()
|
||||||
|
compileTimeout: $('#compileTimeoutTemplate').html()
|
||||||
outputFileLink: $('#outputFileLinkTemplate').html()
|
outputFileLink: $('#outputFileLinkTemplate').html()
|
||||||
|
|
||||||
events:
|
events:
|
||||||
|
@ -79,7 +80,7 @@ define [
|
||||||
@pdfView.onResize?()
|
@pdfView.onResize?()
|
||||||
|
|
||||||
updateLog: (options) ->
|
updateLog: (options) ->
|
||||||
{pdfExists, logExists, compileErrors, rawLog} = options
|
{pdfExists, logExists, compileErrors, rawLog, timedOut, systemError} = options
|
||||||
|
|
||||||
if @errorViews?
|
if @errorViews?
|
||||||
for errorView in @errorViews
|
for errorView in @errorViews
|
||||||
|
@ -110,11 +111,12 @@ define [
|
||||||
|
|
||||||
@$("#showLog").html(logButtonHtml)
|
@$("#showLog").html(logButtonHtml)
|
||||||
|
|
||||||
if !pdfExists
|
if timedOut
|
||||||
if !compileErrors?
|
errorLogs.prepend($(@templates.compileTimeout))
|
||||||
errorLogs.prepend($(@templates.compileError))
|
else if systemError
|
||||||
else
|
errorLogs.prepend($(@templates.compileError))
|
||||||
errorLogs.prepend($(@templates.compileFailed))
|
else if !pdfExists
|
||||||
|
errorLogs.prepend($(@templates.compileFailed))
|
||||||
else if pdfExists && compileErrors.all.length == 0
|
else if pdfExists && compileErrors.all.length == 0
|
||||||
errorLogs.prepend($(@templates.compileSuccess))
|
errorLogs.prepend($(@templates.compileSuccess))
|
||||||
|
|
||||||
|
|
|
@ -124,7 +124,7 @@ define [
|
||||||
@ide.on "afterJoinProject", () =>
|
@ide.on "afterJoinProject", () =>
|
||||||
@_refreshPdfWhenProjectIsLoaded(opts)
|
@_refreshPdfWhenProjectIsLoaded(opts)
|
||||||
|
|
||||||
_refreshPdfWhenProjectIsLoaded: (opts) ->
|
_refreshPdfWhenProjectIsLoaded: (opts = {}) ->
|
||||||
doneCompiling = _.once =>
|
doneCompiling = _.once =>
|
||||||
@compiling = false
|
@compiling = false
|
||||||
@view.doneCompiling()
|
@view.doneCompiling()
|
||||||
|
@ -143,17 +143,23 @@ define [
|
||||||
@view.onCompiling()
|
@view.onCompiling()
|
||||||
@syncButtonsView?.hide()
|
@syncButtonsView?.hide()
|
||||||
@compiling = true
|
@compiling = true
|
||||||
@ide.socket.emit "pdfProject", opts, (err, pdfExists, outputFiles) =>
|
@_doCompile opts.isAutoCompile, (error, status, outputFiles) =>
|
||||||
@compiling = false
|
@compiling = false
|
||||||
doneCompiling()
|
doneCompiling()
|
||||||
|
|
||||||
if err? and err.rateLimitHit
|
if error?
|
||||||
|
@view.updateLog(systemError: true)
|
||||||
|
@view.unsetPdf()
|
||||||
|
@view.showLog()
|
||||||
|
else if status == "timedout"
|
||||||
|
@view.updateLog(timedOut: true)
|
||||||
|
@view.unsetPdf()
|
||||||
|
@view.showLog()
|
||||||
|
else if status == "autocompile-backoff"
|
||||||
@view.showBeforeCompile()
|
@view.showBeforeCompile()
|
||||||
else
|
else
|
||||||
if err?
|
pdfExists = (status == "success")
|
||||||
@view.updateLog(pdfExists: false, logExists: false)
|
@fetchLogAndUpdateView(pdfExists)
|
||||||
else
|
|
||||||
@fetchLogAndUpdateView(pdfExists)
|
|
||||||
|
|
||||||
if pdfExists
|
if pdfExists
|
||||||
@view.setPdf("/project/#{@ide.project_id}/output/output.pdf?cache_bust=#{Date.now()}")
|
@view.setPdf("/project/#{@ide.project_id}/output/output.pdf?cache_bust=#{Date.now()}")
|
||||||
|
@ -166,6 +172,22 @@ define [
|
||||||
if outputFiles?
|
if outputFiles?
|
||||||
@view.showOutputFileDownloadLinks(outputFiles)
|
@view.showOutputFileDownloadLinks(outputFiles)
|
||||||
|
|
||||||
|
_doCompile: (isAutoCompile, callback = (error, status, outputFiles) ->) ->
|
||||||
|
url = "/project/#{@ide.project_id}/compile"
|
||||||
|
if isAutoCompile
|
||||||
|
url += "?auto_compile=true"
|
||||||
|
$.ajax(
|
||||||
|
url: url
|
||||||
|
type: "POST"
|
||||||
|
headers:
|
||||||
|
"X-CSRF-Token": window.csrfToken
|
||||||
|
dataType: 'json'
|
||||||
|
success: (body, status, response) ->
|
||||||
|
callback null, body.status, body.outputFiles
|
||||||
|
error: (error) ->
|
||||||
|
callback error
|
||||||
|
)
|
||||||
|
|
||||||
fetchLogAndUpdateView: (pdfExists) ->
|
fetchLogAndUpdateView: (pdfExists) ->
|
||||||
$.ajax(
|
$.ajax(
|
||||||
url: "/project/#{@ide.project_id}/output/output.log"
|
url: "/project/#{@ide.project_id}/output/output.log"
|
||||||
|
|
|
@ -21,11 +21,58 @@ describe "CompileController", ->
|
||||||
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
|
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
|
||||||
"../../infrastructure/Metrics": @Metrics = { inc: sinon.stub() }
|
"../../infrastructure/Metrics": @Metrics = { inc: sinon.stub() }
|
||||||
"./CompileManager":@CompileManager
|
"./CompileManager":@CompileManager
|
||||||
|
"../Authentication/AuthenticationController": @AuthenticationController = {}
|
||||||
@project_id = "project-id"
|
@project_id = "project-id"
|
||||||
@next = sinon.stub()
|
@next = sinon.stub()
|
||||||
@req = new MockRequest()
|
@req = new MockRequest()
|
||||||
@res = new MockResponse()
|
@res = new MockResponse()
|
||||||
|
|
||||||
|
describe "compile", ->
|
||||||
|
describe "when not an auto compile", ->
|
||||||
|
beforeEach ->
|
||||||
|
@req.params =
|
||||||
|
Project_id: @project_id
|
||||||
|
@AuthenticationController.getLoggedInUserId = sinon.stub().callsArgWith(1, null, @user_id = "mock-user-id")
|
||||||
|
@CompileManager.compile = sinon.stub().callsArgWith(3, null, @status = "success", @outputFiles = ["mock-output-files"])
|
||||||
|
@CompileController.compile @req, @res, @next
|
||||||
|
|
||||||
|
it "should look up the user id", ->
|
||||||
|
@AuthenticationController.getLoggedInUserId
|
||||||
|
.calledWith(@req)
|
||||||
|
.should.equal true
|
||||||
|
|
||||||
|
it "should do the compile without the auto compile flag", ->
|
||||||
|
@CompileManager.compile
|
||||||
|
.calledWith(@project_id, @user_id, { isAutoCompile: false })
|
||||||
|
.should.equal true
|
||||||
|
|
||||||
|
it "should set the content-type of the response to application/json", ->
|
||||||
|
@res.contentType
|
||||||
|
.calledWith("application/json")
|
||||||
|
.should.equal true
|
||||||
|
|
||||||
|
it "should send a successful response reporting the status and files", ->
|
||||||
|
@res.statusCode.should.equal 200
|
||||||
|
@res.body.should.equal JSON.stringify({
|
||||||
|
status: @status
|
||||||
|
outputFiles: @outputFiles
|
||||||
|
})
|
||||||
|
|
||||||
|
describe "when an auto compile", ->
|
||||||
|
beforeEach ->
|
||||||
|
@req.params =
|
||||||
|
Project_id: @project_id
|
||||||
|
@req.query =
|
||||||
|
auto_compile: "true"
|
||||||
|
@AuthenticationController.getLoggedInUserId = sinon.stub().callsArgWith(1, null, @user_id = "mock-user-id")
|
||||||
|
@CompileManager.compile = sinon.stub().callsArgWith(3, null, @status = "success", @outputFiles = ["mock-output-files"])
|
||||||
|
@CompileController.compile @req, @res, @next
|
||||||
|
|
||||||
|
it "should do the compile with the auto compile flag", ->
|
||||||
|
@CompileManager.compile
|
||||||
|
.calledWith(@project_id, @user_id, { isAutoCompile: true })
|
||||||
|
.should.equal true
|
||||||
|
|
||||||
describe "downloadPdf", ->
|
describe "downloadPdf", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@req.params =
|
@req.params =
|
||||||
|
|
|
@ -90,13 +90,8 @@ describe "CompileManager", ->
|
||||||
describe "should check the rate limit", ->
|
describe "should check the rate limit", ->
|
||||||
it "should return", (done)->
|
it "should return", (done)->
|
||||||
@CompileManager._checkIfAutoCompileLimitHasBeenHit = sinon.stub().callsArgWith(1, null, false)
|
@CompileManager._checkIfAutoCompileLimitHasBeenHit = sinon.stub().callsArgWith(1, null, false)
|
||||||
@CompileManager.compile @project_id, @user_id, {}, (err)->
|
@CompileManager.compile @project_id, @user_id, {}, (err, status)->
|
||||||
done()
|
status.should.equal "autocompile-backoff"
|
||||||
|
|
||||||
it "should not error if the autocompile limit has not been hit", (done)->
|
|
||||||
@CompileManager._checkIfAutoCompileLimitHasBeenHit = sinon.stub().callsArgWith(1, null, true)
|
|
||||||
@CompileManager.compile @project_id, @user_id, {}, (err)->
|
|
||||||
assert.equal null, err
|
|
||||||
done()
|
done()
|
||||||
|
|
||||||
describe "getLogLines", ->
|
describe "getLogLines", ->
|
||||||
|
|
Loading…
Reference in a new issue