mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #1217 from sharelatex/ja-download-zip-version
Add option to download zip file of version in history GitOrigin-RevId: 11ffb9a3305e1e5e0492fccf2be41be7beb67d68
This commit is contained in:
parent
28c934e8ff
commit
e603afe106
7 changed files with 110 additions and 6 deletions
|
@ -143,3 +143,37 @@ module.exports = HistoryController =
|
|||
error = new Error("history api responded with non-success code: #{response.statusCode}")
|
||||
logger.error err: error, "project-history api responded with non-success code: #{response.statusCode}"
|
||||
callback(error)
|
||||
|
||||
downloadZipOfVersion: (req, res, next) ->
|
||||
{project_id, version} = req.params
|
||||
logger.log {project_id, version}, "got request for zip file at version"
|
||||
ProjectDetailsHandler.getDetails project_id, (err, project) ->
|
||||
return next(err) if err?
|
||||
v1_id = project.overleaf?.history?.id
|
||||
if !v1_id?
|
||||
logger.err {project_id, version}, 'got request for zip version of non-v1 history project'
|
||||
return res.sendStatus(402)
|
||||
|
||||
url = "#{settings.apis.v1_history.url}/projects/#{v1_id}/version/#{version}/zip"
|
||||
logger.log {project_id, v1_id, version, url}, "proxying to history api"
|
||||
getReq = request(
|
||||
url: url
|
||||
auth:
|
||||
user: settings.apis.v1_history.user
|
||||
pass: settings.apis.v1_history.pass
|
||||
sendImmediately: true
|
||||
)
|
||||
getReq.on 'response', (response) ->
|
||||
# pipe also proxies the headers, but we want to customize these ones
|
||||
delete response.headers['content-disposition']
|
||||
delete response.headers['content-type']
|
||||
res.status response.statusCode
|
||||
res.setContentDisposition(
|
||||
'attachment',
|
||||
{filename: "#{project.name} (Version #{version}).zip"}
|
||||
)
|
||||
res.contentType('application/zip')
|
||||
getReq.pipe(res)
|
||||
getReq.on "error", (err) ->
|
||||
logger.error {err, project_id, v1_id, version}, "history API error"
|
||||
next(error)
|
|
@ -242,6 +242,7 @@ module.exports = class Router
|
|||
webRouter.post "/project/:Project_id/doc/:doc_id/version/:version_id/restore", AuthorizationMiddlewear.ensureUserCanWriteProjectContent, HistoryController.selectHistoryApi, HistoryController.proxyToHistoryApi
|
||||
webRouter.post '/project/:project_id/doc/:doc_id/restore', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, HistoryController.restoreDocFromDeletedDoc
|
||||
webRouter.post "/project/:project_id/restore_file", AuthorizationMiddlewear.ensureUserCanWriteProjectContent, HistoryController.restoreFileFromV2
|
||||
webRouter.get "/project/:project_id/version/:version/zip", AuthorizationMiddlewear.ensureUserCanReadProject, HistoryController.downloadZipOfVersion
|
||||
privateApiRouter.post "/project/:Project_id/history/resync", AuthenticationController.httpAuth, HistoryController.resyncProjectHistory
|
||||
|
||||
webRouter.get "/project/:Project_id/labels", AuthorizationMiddlewear.ensureUserCanReadProject, HistoryController.selectHistoryApi, HistoryController.ensureProjectHistoryEnabled, HistoryController.getLabels
|
||||
|
|
|
@ -30,12 +30,13 @@
|
|||
i.fa.fa-exchange
|
||||
| #{translate("compare_to_another_version")}
|
||||
|
||||
button.history-toolbar-btn-danger.pull-right(
|
||||
tooltip-html="'<p>This feature is still in progress. Please use copy and paste to restore your files.</p><p>More restore options are coming soon.</p>'"
|
||||
tooltip-placement="bottom"
|
||||
a.history-toolbar-btn-danger.pull-right(
|
||||
ng-hide="history.loadingFileTree || history.selection.updates.length == 0"
|
||||
ng-href="/project/{{ project_id }}/version/{{ history.selection.updates[0].toV }}/zip"
|
||||
target="_blank"
|
||||
)
|
||||
i.fa.fa-undo
|
||||
| #{translate("restore_to_before_these_changes")}
|
||||
i.fa.fa-download
|
||||
| #{translate("download_project_at_this_version")}
|
||||
|
||||
.history-toolbar-entries-list(
|
||||
ng-if="!history.error"
|
||||
|
|
|
@ -171,6 +171,8 @@ module.exports = settings =
|
|||
pass: v1Api.pass
|
||||
v1_history:
|
||||
url: "http://#{process.env['V1_HISTORY_HOST'] or "localhost"}:3100/api"
|
||||
user: 'staging'
|
||||
pass: 'password'
|
||||
|
||||
templates:
|
||||
user_id: process.env.TEMPLATES_USER_ID or "5395eb7aad1f29a88756c7f2"
|
||||
|
|
|
@ -44,7 +44,6 @@
|
|||
}
|
||||
.history-toolbar-btn-danger {
|
||||
.btn-danger;
|
||||
opacity: 0.65;
|
||||
}
|
||||
.history-toolbar-entries-list {
|
||||
flex: 0 0 @changesListWidth;
|
||||
|
|
47
services/web/test/acceptance/coffee/HistoryTests.coffee
Normal file
47
services/web/test/acceptance/coffee/HistoryTests.coffee
Normal file
|
@ -0,0 +1,47 @@
|
|||
{expect} = require 'chai'
|
||||
|
||||
{db, ObjectId} = require("../../../app/js/infrastructure/mongojs")
|
||||
MockV1HistoryApi = require './helpers/MockV1HistoryApi'
|
||||
User = require './helpers/User'
|
||||
|
||||
describe 'History', ->
|
||||
beforeEach (done) ->
|
||||
@owner = new User()
|
||||
@owner.login done
|
||||
|
||||
describe 'zip download of version', ->
|
||||
it 'should stream the zip file of a version', (done) ->
|
||||
@owner.createProject 'example-project', (error, @project_id) =>
|
||||
return done(error) if error?
|
||||
@v1_history_id = 42
|
||||
db.projects.update {
|
||||
_id: ObjectId(@project_id)
|
||||
}, {
|
||||
$set: {
|
||||
'overleaf.history.id': @v1_history_id
|
||||
}
|
||||
}, (error) =>
|
||||
return done(error) if error?
|
||||
@owner.request "/project/#{@project_id}/version/42/zip", (error, response, body) =>
|
||||
return done(error) if error?
|
||||
expect(response.statusCode).to.equal 200
|
||||
expect(response.headers['content-type']).to.equal 'application/zip'
|
||||
expect(response.headers['content-disposition']).to.equal 'attachment; filename="example-project%20(Version%2042).zip"'
|
||||
expect(body).to.equal "Mock zip for #{@v1_history_id} at version 42"
|
||||
done()
|
||||
|
||||
it 'should return 402 for non-v2-history project', (done) ->
|
||||
@owner.createProject 'non-v2-project', (error, @project_id) =>
|
||||
return done(error) if error?
|
||||
db.projects.update {
|
||||
_id: ObjectId(@project_id)
|
||||
}, {
|
||||
$unset: {
|
||||
'overleaf.history.id': true
|
||||
}
|
||||
}, (error) =>
|
||||
return done(error) if error?
|
||||
@owner.request "/project/#{@project_id}/version/42/zip", (error, response, body) =>
|
||||
return done(error) if error?
|
||||
expect(response.statusCode).to.equal 402
|
||||
done()
|
|
@ -0,0 +1,20 @@
|
|||
_ = require 'lodash'
|
||||
express = require 'express'
|
||||
bodyParser = require "body-parser"
|
||||
app = express()
|
||||
{ObjectId} = require 'mongojs'
|
||||
|
||||
module.exports = MockV1HistoryApi =
|
||||
run: () ->
|
||||
app.get "/api/projects/:project_id/version/:version/zip", (req, res, next) =>
|
||||
res.header('content-disposition', 'attachment; name=project.zip')
|
||||
res.header('content-type', 'application/octet-stream')
|
||||
res.send "Mock zip for #{req.params.project_id} at version #{req.params.version}"
|
||||
|
||||
app.listen 3100, (error) ->
|
||||
throw error if error?
|
||||
.on "error", (error) ->
|
||||
console.error "error starting MockV1HistoryApi:", error.message
|
||||
process.exit(1)
|
||||
|
||||
MockV1HistoryApi.run()
|
Loading…
Reference in a new issue