Merge pull request #1332 from sharelatex/ew-block-while-project-importing

block loading project while importing

GitOrigin-RevId: b96589e5cae9faf269abefd4ea732992a350e68c
This commit is contained in:
Ersun Warncke 2019-01-03 13:56:24 -04:00 committed by sharelatex
parent 258ce15d91
commit 985f0a97bc
9 changed files with 143 additions and 15 deletions

View file

@ -277,8 +277,16 @@ module.exports = ProjectController =
project: (cb)-> project: (cb)->
ProjectGetter.getProject( ProjectGetter.getProject(
project_id, project_id,
{ name: 1, lastUpdated: 1, track_changes: 1, owner_ref: 1, brandVariationId: 1, overleaf: 1 }, { name: 1, lastUpdated: 1, track_changes: 1, owner_ref: 1, brandVariationId: 1, overleaf: 1, tokens: 1 },
cb (err, project) ->
return cb(err) if err?
return cb(null, project) unless project.overleaf?.id? and project.tokens?.readAndWrite? and Settings.projectImportingCheckMaxCreateDelta?
createDelta = (new Date().getTime() - new Date(project._id.getTimestamp()).getTime()) / 1000
return cb(null, project) unless createDelta < Settings.projectImportingCheckMaxCreateDelta
TokenAccessHandler.getV1DocInfo project.tokens.readAndWrite, null, (err, doc_info) ->
return next err if err?
project.exporting = doc_info.exporting
cb(null, project)
) )
user: (cb)-> user: (cb)->
if !user_id? if !user_id?
@ -327,6 +335,11 @@ module.exports = ProjectController =
if !privilegeLevel? or privilegeLevel == PrivilegeLevels.NONE if !privilegeLevel? or privilegeLevel == PrivilegeLevels.NONE
return res.sendStatus 401 return res.sendStatus 401
if project.exporting
res.render 'project/importing',
bodyClasses: ["editor"]
return
if subscription? and subscription.freeTrial? and subscription.freeTrial.expiresAt? if subscription? and subscription.freeTrial? and subscription.freeTrial.expiresAt?
allowedFreeTrial = !!subscription.freeTrial.allowed || true allowedFreeTrial = !!subscription.freeTrial.allowed || true

View file

@ -126,14 +126,20 @@ module.exports = TokenAccessHandler =
return callback(null, { return callback(null, {
exists: true exists: true
exported: false exported: false
exporting: false
}) unless Settings.apis?.v1? }) unless Settings.apis?.v1?
if v2UserId?
UserGetter.getUser v2UserId, { overleaf: 1 }, (err, user) -> UserGetter.getUser v2UserId, { overleaf: 1 }, (err, user) ->
return callback(err) if err? return callback(err) if err?
v1UserId = user.overleaf?.id v1UserId = user.overleaf?.id
V1Api.request { url: "/api/v1/sharelatex/users/#{v1UserId}/docs/#{token}/info" }, (err, response, body) -> V1Api.request { url: "/api/v1/sharelatex/users/#{v1UserId}/docs/#{token}/info" }, (err, response, body) ->
return callback err if err? return callback err if err?
callback null, body callback null, body
else
V1Api.request { url: "/api/v1/sharelatex/docs/#{token}/info" }, (err, response, body) ->
return callback err if err?
callback null, body
module.exports.READ_AND_WRITE_TOKEN_REGEX = /^(\d+)(\w+)$/ module.exports.READ_AND_WRITE_TOKEN_REGEX = /^(\d+)(\w+)$/
module.exports.READ_AND_WRITE_URL_REGEX = /^\/(\d+)(\w+)$/ module.exports.READ_AND_WRITE_URL_REGEX = /^\/(\d+)(\w+)$/

View file

@ -172,7 +172,7 @@ module.exports = class Router
webRouter.get '/Project/:Project_id', RateLimiterMiddlewear.rateLimit({ webRouter.get '/Project/:Project_id', RateLimiterMiddlewear.rateLimit({
endpointName: "open-project" endpointName: "open-project"
params: ["Project_id"] params: ["Project_id"]
maxRequests: 10 maxRequests: 15
timeInterval: 60 timeInterval: 60
}), AuthorizationMiddlewear.ensureUserCanReadProject, ProjectController.loadEditor }), AuthorizationMiddlewear.ensureUserCanReadProject, ProjectController.loadEditor
webRouter.get '/Project/:Project_id/file/:File_id', AuthorizationMiddlewear.ensureUserCanReadProject, FileStoreController.getFile webRouter.get '/Project/:Project_id/file/:File_id', AuthorizationMiddlewear.ensureUserCanReadProject, FileStoreController.getFile
@ -439,7 +439,7 @@ module.exports = class Router
webRouter.get '/read/:read_only_token([a-z]+)', webRouter.get '/read/:read_only_token([a-z]+)',
RateLimiterMiddlewear.rateLimit({ RateLimiterMiddlewear.rateLimit({
endpointName: 'read-only-token', endpointName: 'read-only-token',
maxRequests: 10, maxRequests: 15,
timeInterval: 60 timeInterval: 60
}), }),
TokenAccessController.readOnlyToken TokenAccessController.readOnlyToken
@ -447,7 +447,7 @@ module.exports = class Router
webRouter.get '/:read_and_write_token([0-9]+[a-z]+)', webRouter.get '/:read_and_write_token([0-9]+[a-z]+)',
RateLimiterMiddlewear.rateLimit({ RateLimiterMiddlewear.rateLimit({
endpointName: 'read-and-write-token', endpointName: 'read-and-write-token',
maxRequests: 10, maxRequests: 15,
timeInterval: 60 timeInterval: 60
}), }),
TokenAccessController.readAndWriteToken TokenAccessController.readAndWriteToken

View file

@ -0,0 +1,20 @@
extends ../layout
block vars
- var suppressNavbar = true
- var suppressFooter = true
- var suppressSystemMessages = true
- metadata.robotsNoindexNofollow = true
block content
.editor(ng-controller="ImportingController").full-size
.loading-screen
.loading-screen-brand-container
.loading-screen-brand(
style="height: 20%;"
ng-style="{ 'height': state.load_progress + '%' }"
)
h3.loading-screen-label #{translate("importing")}
span.loading-screen-ellip .
span.loading-screen-ellip .
span.loading-screen-ellip .

View file

@ -37,6 +37,7 @@ define([
'main/keys', 'main/keys',
'main/cms/blog', 'main/cms/blog',
'main/cms/index', 'main/cms/index',
'main/importing',
'analytics/AbTestingManager', 'analytics/AbTestingManager',
'directives/asyncForm', 'directives/asyncForm',
'directives/stopPropagation', 'directives/stopPropagation',

View file

@ -0,0 +1,21 @@
define(['base'], function(App) {
App.controller('ImportingController', function(
$interval,
$scope,
$timeout,
$window
) {
$interval(function() {
$scope.state.load_progress += 5
if ($scope.state.load_progress > 100) {
$scope.state.load_progress = 20
}
}, 500)
$timeout(function() {
$window.location.reload()
}, 5000)
$scope.state = {
load_progress: 20
}
})
})

View file

@ -1,5 +1,6 @@
expect = require("chai").expect expect = require("chai").expect
async = require("async") async = require("async")
MockV1Api = require "./helpers/MockV1Api"
User = require "./helpers/User" User = require "./helpers/User"
request = require "./helpers/request" request = require "./helpers/request"
settings = require "settings-sharelatex" settings = require "settings-sharelatex"
@ -441,3 +442,48 @@ describe 'TokenAccess', ->
'/sign_in_to_v1?return_to=/read/abcd' '/sign_in_to_v1?return_to=/read/abcd'
) )
, done) , done)
describe 'importing v1 project', ->
before (done) ->
settings.projectImportingCheckMaxCreateDelta = 3600
settings.overleaf =
host: 'http://localhost:5000'
@owner.createProject "token-rw-test#{Math.random()}", (err, project_id) =>
return done(err) if err?
@project_id = project_id
@owner.makeTokenBased @project_id, (err) =>
return done(err) if err?
db.projects.update {_id: ObjectId(project_id)}, $set: overleaf: id: 1234, (err) =>
return done(err) if err?
@owner.getProject @project_id, (err, project) =>
return done(err) if err?
@tokens = project.tokens
MockV1Api.setDocInfo @tokens.readAndWrite, exporting: true
MockV1Api.setDocInfo @tokens.readOnly, exporting: true
done()
after ->
delete settings.projectImportingCheckMaxCreateDelta
delete settings.overleaf
it 'should show importing page for read and write token', (done) ->
try_read_and_write_token_access(@owner, @tokens.readAndWrite, (response, body) =>
expect(response.statusCode).to.equal 200
expect(body).to.include('ImportingController')
, done)
it 'should show importing page for read only token', (done) ->
try_read_only_token_access(@owner, @tokens.readOnly, (response, body) =>
expect(response.statusCode).to.equal 200
expect(body).to.include('ImportingController')
, done)
describe 'when importing check not configured', ->
before ->
delete settings.projectImportingCheckMaxCreateDelta
it 'should load editor', (done) ->
try_read_and_write_token_access(@owner, @tokens.readAndWrite, (response, body) =>
expect(response.statusCode).to.equal 200
expect(body).to.include('IdeController')
, done)

View file

@ -42,6 +42,10 @@ module.exports = MockV1Api =
setAffiliations: (affiliations) -> @affiliations = affiliations setAffiliations: (affiliations) -> @affiliations = affiliations
doc_info: {}
setDocInfo: (token, info) -> @doc_info[token] = info
run: () -> run: () ->
app.get "/api/v1/sharelatex/users/:v1_user_id/plan_code", (req, res, next) => app.get "/api/v1/sharelatex/users/:v1_user_id/plan_code", (req, res, next) =>
user = @users[req.params.v1_user_id] user = @users[req.params.v1_user_id]
@ -136,16 +140,20 @@ module.exports = MockV1Api =
else else
res.status(404).json {} res.status(404).json {}
app.listen 5000, (error) ->
throw error if error?
.on "error", (error) ->
console.error "error starting MockV1Api:", error.message
process.exit(1)
app.get '/api/v1/sharelatex/docs/:token/is_published', (req, res, next) => app.get '/api/v1/sharelatex/docs/:token/is_published', (req, res, next) =>
res.json { allow: true } res.json { allow: true }
app.get '/api/v1/sharelatex/users/:user_id/docs/:token/info', (req, res, next) => app.get '/api/v1/sharelatex/users/:user_id/docs/:token/info', (req, res, next) =>
res.json { exported: false } res.json { exported: false }
app.get '/api/v1/sharelatex/docs/:token/info', (req, res, next) =>
return res.json @doc_info[req.params.token] if @doc_info[req.params.token]?
res.json { exporting: false }
app.listen 5000, (error) ->
throw error if error?
.on "error", (error) ->
console.error "error starting MockV1Api:", error.message
process.exit(1)
MockV1Api.run() MockV1Api.run()

View file

@ -541,6 +541,7 @@ describe "TokenAccessHandler", ->
expect(@callback.calledWith null, { expect(@callback.calledWith null, {
exists: true exists: true
exported: false exported: false
exporting: false
}).to.equal true }).to.equal true
describe 'when v1 api is set', -> describe 'when v1 api is set', ->
@ -579,6 +580,18 @@ describe "TokenAccessHandler", ->
expect(@V1Api.request.calledWith { url: "/api/v1/sharelatex/users/#{@v1UserId}/docs/#{@token}/info" }).to.equal true expect(@V1Api.request.calledWith { url: "/api/v1/sharelatex/users/#{@v1UserId}/docs/#{@token}/info" }).to.equal true
expect(@callback.calledWith null, 'mock-data').to.equal true expect(@callback.calledWith null, 'mock-data').to.equal true
describe 'when user id arg is null', ->
beforeEach ->
@v2UserId = null
@UserGetter.getUser = sinon.stub()
@V1Api.request = sinon.stub().callsArgWith(1, null, null, 'mock-data')
@TokenAccessHandler.getV1DocInfo @token, @v2UserId, @callback
it 'should get info without user', ->
expect(@UserGetter.getUser.called).to.equal false
expect(@V1Api.request.calledWith { url: "/api/v1/sharelatex/docs/#{@token}/info" }).to.equal true
expect(@callback.calledWith null, 'mock-data').to.equal true
describe 'on V1Api.request error', -> describe 'on V1Api.request error', ->
beforeEach -> beforeEach ->
@UserGetter.getUser = sinon.stub().yields(null, { @UserGetter.getUser = sinon.stub().yields(null, {