| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- const { callbackify } = require('util')
- const fetch = require('node-fetch')
- const settings = require('@overleaf/settings')
- const OError = require('@overleaf/o-error')
- const UserGetter = require('../User/UserGetter')
- async function initializeProject(projectId) {
- const response = await fetch(`${settings.apis.project_history.url}/project`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- Accept: 'application/json',
- },
- body: JSON.stringify({ historyId: projectId.toString() }),
- })
- if (!response.ok) {
- throw new OError('failed to initialize project history', {
- statusCode: response.status,
- })
- }
- const body = await response.json()
- const historyId = body && body.project && body.project.id
- if (!historyId) {
- throw new OError('project-history did not provide an id', { body })
- }
- return historyId
- }
- async function flushProject(projectId) {
- const response = await fetch(
- `${settings.apis.project_history.url}/project/${projectId}/flush`,
- { method: 'POST' }
- )
- if (!response.ok) {
- throw new OError('failed to flush project to project history', {
- projectId,
- statusCode: response.status,
- })
- }
- }
- async function deleteProjectHistory(projectId) {
- const response = await fetch(
- `${settings.apis.project_history.url}/project/${projectId}`,
- { method: 'DELETE' }
- )
- if (!response.ok) {
- throw new OError('failed to delete project history', {
- projectId,
- statusCode: response.status,
- })
- }
- }
- async function resyncProject(projectId, options = {}) {
- const body = {}
- if (options.force) {
- body.force = options.force
- }
- if (options.origin) {
- body.origin = options.origin
- }
- const response = await fetch(
- `${settings.apis.project_history.url}/project/${projectId}/resync`,
- {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(body),
- signal: AbortSignal.timeout(6 * 60 * 1000),
- }
- )
- if (!response.ok) {
- throw new OError('failed to resync project history', {
- projectId,
- statusCode: response.status,
- })
- }
- }
- async function deleteProject(projectId, historyId) {
- const tasks = []
- tasks.push(_deleteProjectInProjectHistory(projectId))
- if (historyId != null) {
- tasks.push(_deleteProjectInFullProjectHistory(historyId))
- }
- await Promise.all(tasks)
- }
- async function _deleteProjectInProjectHistory(projectId) {
- const response = await fetch(
- `${settings.apis.project_history.url}/project/${projectId}`,
- { method: 'DELETE' }
- )
- if (!response.ok) {
- throw new OError('failed to clear project history in project-history', {
- projectId,
- statusCode: response.status,
- })
- }
- }
- async function _deleteProjectInFullProjectHistory(historyId) {
- const response = await fetch(
- `${settings.apis.v1_history.url}/projects/${historyId}`,
- {
- method: 'DELETE',
- headers: {
- Authorization:
- 'Basic ' +
- Buffer.from(
- `${settings.apis.v1_history.user}:${settings.apis.v1_history.pass}`
- ).toString('base64'),
- },
- }
- )
- if (!response.ok) {
- throw new OError('failed to clear project history', {
- historyId,
- statusCode: response.status,
- })
- }
- }
- 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()
- const entries = Array.isArray(data.diff)
- ? data.diff
- : Array.isArray(data.updates)
- ? data.updates
- : []
- for (const entry of entries) {
- for (const user of (entry.meta && entry.meta.users) || []) {
- if (typeof user === 'string') {
- userIds.add(user)
- } else if (typeof user === 'number') {
- v1UserIds.add(user)
- }
- }
- }
- 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)
- }
- for (const entry of entries) {
- if (entry.meta != null) {
- entry.meta.users = ((entry.meta && entry.meta.users) || []).map(user => {
- if (typeof user === 'string' || typeof user === 'number') {
- return users[user]
- } else {
- return user
- }
- })
- }
- }
- return data
- }
- function _userView(user) {
- const { _id, first_name: firstName, last_name: lastName, email } = user
- return { first_name: firstName, last_name: lastName, email, id: _id }
- }
- module.exports = {
- initializeProject: callbackify(initializeProject),
- flushProject: callbackify(flushProject),
- resyncProject: callbackify(resyncProject),
- deleteProject: callbackify(deleteProject),
- deleteProjectHistory: callbackify(deleteProjectHistory),
- injectUserDetails: callbackify(injectUserDetails),
- promises: {
- initializeProject,
- flushProject,
- resyncProject,
- deleteProject,
- injectUserDetails,
- deleteProjectHistory,
- },
- }
|