2014-03-05 11:31:52 -05:00
|
|
|
logger = require "logger-sharelatex"
|
|
|
|
request = require "request"
|
|
|
|
settings = require "settings-sharelatex"
|
2014-03-11 14:01:14 -04:00
|
|
|
AuthenticationController = require "../Authentication/AuthenticationController"
|
2018-03-15 12:57:39 -04:00
|
|
|
Errors = require "../Errors/Errors"
|
|
|
|
HistoryManager = require "./HistoryManager"
|
2017-11-03 12:44:37 -04:00
|
|
|
ProjectDetailsHandler = require "../Project/ProjectDetailsHandler"
|
2018-03-07 06:02:29 -05:00
|
|
|
ProjectEntityUpdateHandler = require "../Project/ProjectEntityUpdateHandler"
|
2018-03-08 12:24:54 -05:00
|
|
|
RestoreManager = require "./RestoreManager"
|
2014-03-05 11:31:52 -05:00
|
|
|
|
2016-12-09 10:43:08 -05:00
|
|
|
module.exports = HistoryController =
|
2017-11-03 12:44:37 -04:00
|
|
|
selectHistoryApi: (req, res, next = (error) ->) ->
|
|
|
|
project_id = req.params?.Project_id
|
|
|
|
# find out which type of history service this project uses
|
|
|
|
ProjectDetailsHandler.getDetails project_id, (err, project) ->
|
|
|
|
return next(err) if err?
|
2017-12-14 10:38:20 -05:00
|
|
|
history = project.overleaf?.history
|
|
|
|
if history?.id? and history?.display
|
2017-11-03 12:44:37 -04:00
|
|
|
req.useProjectHistory = true
|
|
|
|
else
|
|
|
|
req.useProjectHistory = false
|
|
|
|
next()
|
|
|
|
|
2018-07-20 10:36:03 -04:00
|
|
|
ensureProjectHistoryEnabled: (req, res, next = (error) ->) ->
|
|
|
|
if req.useProjectHistory?
|
|
|
|
next()
|
|
|
|
else
|
|
|
|
logger.log {project_id}, "project history not enabled"
|
|
|
|
res.sendStatus(404)
|
|
|
|
|
2016-12-09 10:43:08 -05:00
|
|
|
proxyToHistoryApi: (req, res, next = (error) ->) ->
|
2016-09-05 10:58:31 -04:00
|
|
|
user_id = AuthenticationController.getLoggedInUserId req
|
2017-11-03 12:44:37 -04:00
|
|
|
url = HistoryController.buildHistoryServiceUrl(req.useProjectHistory) + req.url
|
2017-10-11 06:18:34 -04:00
|
|
|
|
2017-11-03 12:44:37 -04:00
|
|
|
logger.log url: url, "proxying to history api"
|
2016-09-05 10:58:31 -04:00
|
|
|
getReq = request(
|
|
|
|
url: url
|
|
|
|
method: req.method
|
|
|
|
headers:
|
|
|
|
"X-User-Id": user_id
|
|
|
|
)
|
|
|
|
getReq.pipe(res)
|
|
|
|
getReq.on "error", (error) ->
|
2017-11-03 12:44:37 -04:00
|
|
|
logger.error url: url, err: error, "history API error"
|
2016-09-05 10:58:31 -04:00
|
|
|
next(error)
|
2017-10-11 06:18:34 -04:00
|
|
|
|
2018-01-11 06:31:41 -05:00
|
|
|
proxyToHistoryApiAndInjectUserDetails: (req, res, next = (error) ->) ->
|
|
|
|
user_id = AuthenticationController.getLoggedInUserId req
|
|
|
|
url = HistoryController.buildHistoryServiceUrl(req.useProjectHistory) + req.url
|
|
|
|
logger.log url: url, "proxying to history api"
|
2018-07-20 10:36:03 -04:00
|
|
|
HistoryController._makeRequest {
|
2018-01-11 06:31:41 -05:00
|
|
|
url: url
|
|
|
|
method: req.method
|
|
|
|
json: true
|
|
|
|
headers:
|
|
|
|
"X-User-Id": user_id
|
2018-07-20 10:36:03 -04:00
|
|
|
}, (error, body) ->
|
2018-01-11 06:31:41 -05:00
|
|
|
return next(error) if error?
|
2018-07-20 10:36:03 -04:00
|
|
|
HistoryManager.injectUserDetails body, (error, data) ->
|
|
|
|
return next(error) if error?
|
|
|
|
res.json data
|
2018-01-11 06:31:41 -05:00
|
|
|
|
2017-11-03 12:44:37 -04:00
|
|
|
buildHistoryServiceUrl: (useProjectHistory) ->
|
|
|
|
# choose a history service, either document-level (trackchanges)
|
|
|
|
# or project-level (project_history)
|
2017-12-14 10:38:20 -05:00
|
|
|
if useProjectHistory
|
2017-10-11 06:18:34 -04:00
|
|
|
return settings.apis.project_history.url
|
|
|
|
else
|
|
|
|
return settings.apis.trackchanges.url
|
2018-02-28 12:31:26 -05:00
|
|
|
|
2018-03-07 06:19:47 -05:00
|
|
|
resyncProjectHistory: (req, res, next = (error) ->) ->
|
2018-02-28 12:31:26 -05:00
|
|
|
project_id = req.params.Project_id
|
2018-03-07 06:19:47 -05:00
|
|
|
ProjectEntityUpdateHandler.resyncProjectHistory project_id, (error) ->
|
2018-03-15 12:57:39 -04:00
|
|
|
return res.sendStatus(404) if error instanceof Errors.ProjectHistoryDisabledError
|
2018-02-28 12:31:26 -05:00
|
|
|
return next(error) if error?
|
|
|
|
res.sendStatus 204
|
2018-03-08 12:24:54 -05:00
|
|
|
|
2018-03-16 08:21:07 -04:00
|
|
|
restoreFileFromV2: (req, res, next) ->
|
2018-03-08 12:24:54 -05:00
|
|
|
{project_id} = req.params
|
|
|
|
{version, pathname} = req.body
|
|
|
|
user_id = AuthenticationController.getLoggedInUserId req
|
2018-03-16 08:21:07 -04:00
|
|
|
logger.log {project_id, version, pathname}, "restoring file from v2"
|
|
|
|
RestoreManager.restoreFileFromV2 user_id, project_id, version, pathname, (error, entity) ->
|
2018-03-08 12:24:54 -05:00
|
|
|
return next(error) if error?
|
2018-03-12 07:47:56 -04:00
|
|
|
res.json {
|
|
|
|
type: entity.type,
|
|
|
|
id: entity._id
|
|
|
|
}
|
2018-03-16 08:21:07 -04:00
|
|
|
|
|
|
|
restoreDocFromDeletedDoc: (req, res, next) ->
|
|
|
|
{project_id, doc_id} = req.params
|
|
|
|
{name} = req.body
|
|
|
|
user_id = AuthenticationController.getLoggedInUserId(req)
|
|
|
|
if !name?
|
|
|
|
return res.sendStatus 400 # Malformed request
|
|
|
|
logger.log {project_id, doc_id, user_id}, "restoring doc from v1 deleted doc"
|
|
|
|
RestoreManager.restoreDocFromDeletedDoc user_id, project_id, doc_id, name, (err, doc) =>
|
|
|
|
return next(error) if error?
|
|
|
|
res.json {
|
|
|
|
doc_id: doc._id
|
|
|
|
}
|
|
|
|
|
2018-07-20 10:36:03 -04:00
|
|
|
getLabels: (req, res, next) ->
|
|
|
|
project_id = req.params.Project_id
|
|
|
|
user_id = AuthenticationController.getLoggedInUserId(req)
|
|
|
|
HistoryController._makeRequest {
|
|
|
|
method: "GET"
|
|
|
|
url: "#{settings.apis.project_history.url}/project/#{project_id}/labels"
|
|
|
|
json: true
|
|
|
|
}, (error, labels) ->
|
|
|
|
return next(error) if error?
|
|
|
|
res.json labels
|
|
|
|
|
|
|
|
createLabel: (req, res, next) ->
|
|
|
|
project_id = req.params.Project_id
|
|
|
|
{comment, version} = req.body
|
|
|
|
user_id = AuthenticationController.getLoggedInUserId(req)
|
|
|
|
HistoryController._makeRequest {
|
|
|
|
method: "POST"
|
|
|
|
url: "#{settings.apis.project_history.url}/project/#{project_id}/user/#{user_id}/labels"
|
|
|
|
json: {comment, version}
|
|
|
|
}, (error, label) ->
|
|
|
|
return next(error) if error?
|
|
|
|
res.json label
|
|
|
|
|
|
|
|
deleteLabel: (req, res, next) ->
|
|
|
|
project_id = req.params.Project_id
|
|
|
|
label_id = req.params.label_id
|
|
|
|
user_id = AuthenticationController.getLoggedInUserId(req)
|
|
|
|
HistoryController._makeRequest {
|
|
|
|
method: "DELETE"
|
|
|
|
url: "#{settings.apis.project_history.url}/project/#{project_id}/user/#{user_id}/labels/#{label_id}"
|
|
|
|
}, (error) ->
|
|
|
|
return next(error) if error?
|
|
|
|
res.sendStatus 204
|
|
|
|
|
|
|
|
_makeRequest: (options, callback) ->
|
|
|
|
request options, (error, response, body) ->
|
|
|
|
return callback(error) if error?
|
|
|
|
if 200 <= response.statusCode < 300
|
|
|
|
callback(null, body)
|
|
|
|
else
|
|
|
|
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)
|
2018-12-05 07:01:25 -05:00
|
|
|
|
|
|
|
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)
|