mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-29 16:13:42 -05:00
Merge pull request #1332 from sharelatex/ew-block-while-project-importing
block loading project while importing GitOrigin-RevId: b96589e5cae9faf269abefd4ea732992a350e68c
This commit is contained in:
parent
258ce15d91
commit
985f0a97bc
9 changed files with 143 additions and 15 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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+)$/
|
||||||
|
|
|
@ -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
|
||||||
|
|
20
services/web/app/views/project/importing.pug
Normal file
20
services/web/app/views/project/importing.pug
Normal 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 .
|
|
@ -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',
|
||||||
|
|
21
services/web/public/src/main/importing.js
Normal file
21
services/web/public/src/main/importing.js
Normal 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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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, {
|
||||||
|
|
Loading…
Reference in a new issue