diff --git a/services/web/app/coffee/Features/Compile/CompileController.coffee b/services/web/app/coffee/Features/Compile/CompileController.coffee index 5a6538cc8b..09b4bf51ba 100644 --- a/services/web/app/coffee/Features/Compile/CompileController.coffee +++ b/services/web/app/coffee/Features/Compile/CompileController.coffee @@ -4,8 +4,22 @@ CompileManager = require("./CompileManager") logger = require "logger-sharelatex" request = require "request" Settings = require "settings-sharelatex" +AuthenticationController = require "../Authentication/AuthenticationController" 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) ->)-> Metrics.inc "pdf-downloads" project_id = req.params.Project_id diff --git a/services/web/app/coffee/Features/Compile/CompileManager.coffee b/services/web/app/coffee/Features/Compile/CompileManager.coffee index 012a5dc069..0bef4b9796 100644 --- a/services/web/app/coffee/Features/Compile/CompileManager.coffee +++ b/services/web/app/coffee/Features/Compile/CompileManager.coffee @@ -19,8 +19,7 @@ module.exports = CompileManager = @_checkIfAutoCompileLimitHasBeenHit opt.isAutoCompile, (err, canCompile)-> if !canCompile - err = {rateLimitHit:true} - return callback(err) + return callback null, "autocompile-backoff", [] logger.log project_id: project_id, user_id: user_id, "compiling project" CompileManager._checkIfRecentlyCompiled project_id, user_id, (error, recentlyCompiled) -> return callback(error) if error? diff --git a/services/web/app/coffee/router.coffee b/services/web/app/coffee/router.coffee index 649904c171..bc95f6fe19 100644 --- a/services/web/app/coffee/router.coffee +++ b/services/web/app/coffee/router.coffee @@ -104,6 +104,7 @@ module.exports = class Router app.get '/Project/:Project_id', SecurityManager.requestCanAccessProject, ProjectController.loadEditor 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\/([^\/]*)\/output\/(.*)$/, ((req, res, next) -> @@ -320,6 +321,7 @@ module.exports = class Router AuthorizationManager.ensureClientCanAdminProject client, (error, project_id) => EditorController.setPublicAccessLevel(project_id, newAccessLevel, callback) + # Deprecated and can be removed after deploying. client.on 'pdfProject', (opts, callback)-> AuthorizationManager.ensureClientCanViewProject client, (error, project_id) => CompileManager.compile project_id, user._id, opts, (error, status, outputFiles) -> diff --git a/services/web/app/views/templates.jade b/services/web/app/views/templates.jade index 104ddc07e9..fd390f5922 100644 --- a/services/web/app/views/templates.jade +++ b/services/web/app/views/templates.jade @@ -234,14 +234,19 @@ script(type="text/template")#compileErrorTemplate li.alert.alert-error - strong Compile 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. + strong Server Error. + 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 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 li.alert.clickable(class="alert-{{ type }}") strong {{ title }}: diff --git a/services/web/public/coffee/pdf/CompiledView.coffee b/services/web/public/coffee/pdf/CompiledView.coffee index 4f0cd4c242..dd145984fc 100644 --- a/services/web/public/coffee/pdf/CompiledView.coffee +++ b/services/web/public/coffee/pdf/CompiledView.coffee @@ -37,6 +37,7 @@ define [ compileSuccess: $('#compileSuccessTemplate').html() compileFailed: $('#compileFailedTemplate').html() compileError: $('#compileErrorTemplate').html() + compileTimeout: $('#compileTimeoutTemplate').html() outputFileLink: $('#outputFileLinkTemplate').html() events: @@ -79,7 +80,7 @@ define [ @pdfView.onResize?() updateLog: (options) -> - {pdfExists, logExists, compileErrors, rawLog} = options + {pdfExists, logExists, compileErrors, rawLog, timedOut, systemError} = options if @errorViews? for errorView in @errorViews @@ -110,11 +111,12 @@ define [ @$("#showLog").html(logButtonHtml) - if !pdfExists - if !compileErrors? - errorLogs.prepend($(@templates.compileError)) - else - errorLogs.prepend($(@templates.compileFailed)) + if timedOut + errorLogs.prepend($(@templates.compileTimeout)) + else if systemError + errorLogs.prepend($(@templates.compileError)) + else if !pdfExists + errorLogs.prepend($(@templates.compileFailed)) else if pdfExists && compileErrors.all.length == 0 errorLogs.prepend($(@templates.compileSuccess)) diff --git a/services/web/public/coffee/pdf/PdfManager.coffee b/services/web/public/coffee/pdf/PdfManager.coffee index c9c74c7f90..d7990ed12f 100644 --- a/services/web/public/coffee/pdf/PdfManager.coffee +++ b/services/web/public/coffee/pdf/PdfManager.coffee @@ -124,7 +124,7 @@ define [ @ide.on "afterJoinProject", () => @_refreshPdfWhenProjectIsLoaded(opts) - _refreshPdfWhenProjectIsLoaded: (opts) -> + _refreshPdfWhenProjectIsLoaded: (opts = {}) -> doneCompiling = _.once => @compiling = false @view.doneCompiling() @@ -143,17 +143,23 @@ define [ @view.onCompiling() @syncButtonsView?.hide() @compiling = true - @ide.socket.emit "pdfProject", opts, (err, pdfExists, outputFiles) => + @_doCompile opts.isAutoCompile, (error, status, outputFiles) => @compiling = false 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() else - if err? - @view.updateLog(pdfExists: false, logExists: false) - else - @fetchLogAndUpdateView(pdfExists) + pdfExists = (status == "success") + @fetchLogAndUpdateView(pdfExists) if pdfExists @view.setPdf("/project/#{@ide.project_id}/output/output.pdf?cache_bust=#{Date.now()}") @@ -166,6 +172,22 @@ define [ if 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) -> $.ajax( url: "/project/#{@ide.project_id}/output/output.log" diff --git a/services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee b/services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee index 94b65cb2d4..f6c0712819 100644 --- a/services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee @@ -21,11 +21,58 @@ describe "CompileController", -> "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } "../../infrastructure/Metrics": @Metrics = { inc: sinon.stub() } "./CompileManager":@CompileManager + "../Authentication/AuthenticationController": @AuthenticationController = {} @project_id = "project-id" @next = sinon.stub() @req = new MockRequest() @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", -> beforeEach -> @req.params = diff --git a/services/web/test/UnitTests/coffee/Compile/CompileManagerTests.coffee b/services/web/test/UnitTests/coffee/Compile/CompileManagerTests.coffee index cc9bdfba3f..f1b963a8c5 100644 --- a/services/web/test/UnitTests/coffee/Compile/CompileManagerTests.coffee +++ b/services/web/test/UnitTests/coffee/Compile/CompileManagerTests.coffee @@ -90,13 +90,8 @@ describe "CompileManager", -> describe "should check the rate limit", -> it "should return", (done)-> @CompileManager._checkIfAutoCompileLimitHasBeenHit = sinon.stub().callsArgWith(1, null, false) - @CompileManager.compile @project_id, @user_id, {}, (err)-> - done() - - 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 + @CompileManager.compile @project_id, @user_id, {}, (err, status)-> + status.should.equal "autocompile-backoff" done() describe "getLogLines", ->