2020-03-03 08:59:09 -05:00
|
|
|
const { callbackify } = require('util')
|
|
|
|
const request = require('request-promise-native')
|
2021-07-07 05:38:56 -04:00
|
|
|
const settings = require('@overleaf/settings')
|
2020-03-03 08:59:09 -05:00
|
|
|
const OError = require('@overleaf/o-error')
|
2019-05-29 05:21:06 -04:00
|
|
|
const UserGetter = require('../User/UserGetter')
|
|
|
|
|
2020-03-03 08:59:09 -05:00
|
|
|
module.exports = {
|
|
|
|
initializeProject: callbackify(initializeProject),
|
|
|
|
flushProject: callbackify(flushProject),
|
|
|
|
resyncProject: callbackify(resyncProject),
|
2020-03-04 04:38:40 -05:00
|
|
|
deleteProject: callbackify(deleteProject),
|
2020-03-03 08:59:09 -05:00
|
|
|
injectUserDetails: callbackify(injectUserDetails),
|
|
|
|
promises: {
|
|
|
|
initializeProject,
|
|
|
|
flushProject,
|
|
|
|
resyncProject,
|
2020-03-04 04:38:40 -05:00
|
|
|
deleteProject,
|
2021-04-27 03:52:58 -04:00
|
|
|
injectUserDetails,
|
|
|
|
},
|
2020-03-03 08:59:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
async function initializeProject() {
|
|
|
|
if (
|
|
|
|
!(
|
|
|
|
settings.apis.project_history &&
|
|
|
|
settings.apis.project_history.initializeHistoryForNewProjects
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
const body = await request.post({
|
2021-04-27 03:52:58 -04:00
|
|
|
url: `${settings.apis.project_history.url}/project`,
|
2020-03-03 08:59:09 -05:00
|
|
|
})
|
|
|
|
const project = JSON.parse(body)
|
|
|
|
const overleafId = project && project.project && project.project.id
|
|
|
|
if (!overleafId) {
|
|
|
|
throw new Error('project-history did not provide an id', project)
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
2020-03-03 08:59:09 -05:00
|
|
|
return { overleaf_id: overleafId }
|
|
|
|
} catch (err) {
|
2020-08-11 05:28:29 -04:00
|
|
|
throw OError.tag(err, 'failed to initialize project history')
|
2020-03-03 08:59:09 -05:00
|
|
|
}
|
|
|
|
}
|
2019-05-29 05:21:06 -04:00
|
|
|
|
2020-03-03 08:59:09 -05:00
|
|
|
async function flushProject(projectId) {
|
|
|
|
try {
|
|
|
|
await request.post({
|
2021-04-27 03:52:58 -04:00
|
|
|
url: `${settings.apis.project_history.url}/project/${projectId}/flush`,
|
2020-03-03 08:59:09 -05:00
|
|
|
})
|
|
|
|
} catch (err) {
|
2020-08-11 05:28:29 -04:00
|
|
|
throw OError.tag(err, 'failed to flush project to project history', {
|
2021-04-27 03:52:58 -04:00
|
|
|
projectId,
|
2020-08-11 05:28:29 -04:00
|
|
|
})
|
2020-03-03 08:59:09 -05:00
|
|
|
}
|
|
|
|
}
|
2019-05-29 05:21:06 -04:00
|
|
|
|
2020-03-03 08:59:09 -05:00
|
|
|
async function resyncProject(projectId) {
|
|
|
|
try {
|
|
|
|
await request.post({
|
2021-04-27 03:52:58 -04:00
|
|
|
url: `${settings.apis.project_history.url}/project/${projectId}/resync`,
|
2020-03-03 08:59:09 -05:00
|
|
|
})
|
|
|
|
} catch (err) {
|
2021-01-14 08:56:36 -05:00
|
|
|
throw OError.tag(err, 'failed to resync project history', { projectId })
|
2020-03-03 08:59:09 -05:00
|
|
|
}
|
|
|
|
}
|
2019-05-29 05:21:06 -04:00
|
|
|
|
2021-01-14 08:56:36 -05:00
|
|
|
async function deleteProject(projectId, historyId) {
|
2020-03-04 04:38:40 -05:00
|
|
|
try {
|
2021-01-14 08:56:36 -05:00
|
|
|
const tasks = [
|
|
|
|
request.delete(
|
|
|
|
`${settings.apis.project_history.url}/project/${projectId}`
|
2021-04-27 03:52:58 -04:00
|
|
|
),
|
2021-01-14 08:56:36 -05:00
|
|
|
]
|
|
|
|
if (historyId != null) {
|
|
|
|
tasks.push(
|
|
|
|
request.delete({
|
|
|
|
url: `${settings.apis.v1_history.url}/projects/${historyId}`,
|
|
|
|
auth: {
|
|
|
|
user: settings.apis.v1_history.user,
|
2021-04-27 03:52:58 -04:00
|
|
|
pass: settings.apis.v1_history.pass,
|
|
|
|
},
|
2021-01-14 08:56:36 -05:00
|
|
|
})
|
|
|
|
)
|
|
|
|
}
|
|
|
|
await Promise.all(tasks)
|
2020-03-04 04:38:40 -05:00
|
|
|
} catch (err) {
|
2021-01-14 08:56:36 -05:00
|
|
|
throw OError.tag(err, 'failed to clear project history', {
|
|
|
|
projectId,
|
2021-04-27 03:52:58 -04:00
|
|
|
historyId,
|
2021-01-14 08:56:36 -05:00
|
|
|
})
|
2020-03-04 04:38:40 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-03 08:59:09 -05:00
|
|
|
async function injectUserDetails(data) {
|
|
|
|
// data can be either:
|
|
|
|
// {
|
|
|
|
// diff: [{
|
|
|
|
// i: "foo",
|
|
|
|
// meta: {
|
|
|
|
// users: ["user_id", v1_user_id, ...]
|
|
|
|
// ...
|
|
|
|
// }
|
|
|
|
// }, ...]
|
|
|
|
// }
|
|
|
|
// or
|
|
|
|
// {
|
|
|
|
// updates: [{
|
|
|
|
// pathnames: ["main.tex"]
|
|
|
|
// meta: {
|
|
|
|
// users: ["user_id", v1_user_id, ...]
|
|
|
|
// ...
|
|
|
|
// },
|
|
|
|
// ...
|
|
|
|
// }, ...]
|
|
|
|
// }
|
|
|
|
// Either way, the top level key points to an array of objects with a meta.users property
|
|
|
|
// that we need to replace user_ids with populated user objects.
|
|
|
|
// Note that some entries in the users arrays may be v1 ids returned by the v1 history
|
|
|
|
// service. v1 ids will be `numbers`
|
|
|
|
let userIds = new Set()
|
|
|
|
let v1UserIds = new Set()
|
2020-03-05 11:36:12 -05:00
|
|
|
const entries = Array.isArray(data.diff)
|
|
|
|
? data.diff
|
|
|
|
: Array.isArray(data.updates)
|
2020-12-15 05:23:54 -05:00
|
|
|
? data.updates
|
|
|
|
: []
|
2020-03-05 11:36:12 -05:00
|
|
|
for (const entry of entries) {
|
2020-03-03 08:59:09 -05:00
|
|
|
for (const user of (entry.meta && entry.meta.users) || []) {
|
|
|
|
if (typeof user === 'string') {
|
|
|
|
userIds.add(user)
|
|
|
|
} else if (typeof user === 'number') {
|
|
|
|
v1UserIds.add(user)
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
}
|
2020-03-03 08:59:09 -05:00
|
|
|
}
|
2019-05-29 05:21:06 -04:00
|
|
|
|
2020-03-03 08:59:09 -05:00
|
|
|
userIds = Array.from(userIds)
|
|
|
|
v1UserIds = Array.from(v1UserIds)
|
|
|
|
const projection = { first_name: 1, last_name: 1, email: 1 }
|
|
|
|
const usersArray = await UserGetter.promises.getUsers(userIds, projection)
|
|
|
|
const users = {}
|
|
|
|
for (const user of usersArray) {
|
|
|
|
users[user._id.toString()] = _userView(user)
|
|
|
|
}
|
|
|
|
projection.overleaf = 1
|
|
|
|
const v1IdentifiedUsersArray = await UserGetter.promises.getUsersByV1Ids(
|
|
|
|
v1UserIds,
|
|
|
|
projection
|
|
|
|
)
|
|
|
|
for (const user of v1IdentifiedUsersArray) {
|
|
|
|
users[user.overleaf.id] = _userView(user)
|
|
|
|
}
|
2020-03-05 11:36:12 -05:00
|
|
|
for (const entry of entries) {
|
2020-03-03 08:59:09 -05:00
|
|
|
if (entry.meta != null) {
|
|
|
|
entry.meta.users = ((entry.meta && entry.meta.users) || []).map(user => {
|
|
|
|
if (typeof user === 'string' || typeof user === 'number') {
|
|
|
|
return users[user]
|
2019-05-29 05:21:06 -04:00
|
|
|
} else {
|
2020-03-03 08:59:09 -05:00
|
|
|
return user
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
2020-03-03 08:59:09 -05:00
|
|
|
})
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
}
|
2020-03-03 08:59:09 -05:00
|
|
|
return data
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
2019-10-23 08:24:01 -04:00
|
|
|
|
2020-03-03 08:59:09 -05:00
|
|
|
function _userView(user) {
|
|
|
|
const { _id, first_name: firstName, last_name: lastName, email } = user
|
|
|
|
return { first_name: firstName, last_name: lastName, email, id: _id }
|
|
|
|
}
|