overleaf/services/project-history/app/js/WebApiManager.js
Antoine Clausse 7f48c67512 Add prefer-node-protocol ESLint rule (#21532)
* Add `unicorn/prefer-node-protocol`

* Fix `unicorn/prefer-node-protocol` ESLint errors

* Run `npm run format:fix`

* Add sandboxed-module sourceTransformers in mocha setups

Fix `no such file or directory, open 'node:fs'` in `sandboxed-module`

* Remove `node:` in the SandboxedModule requires

* Fix new linting errors with `node:`

GitOrigin-RevId: 68f6e31e2191fcff4cb8058dd0a6914c14f59926
2024-11-11 09:04:51 +00:00

109 lines
2.9 KiB
JavaScript

import { callbackify } from 'node:util'
import { setTimeout } from 'node:timers/promises'
import logger from '@overleaf/logger'
import Metrics from '@overleaf/metrics'
import Settings from '@overleaf/settings'
import {
fetchNothing,
fetchJson,
RequestFailedError,
} from '@overleaf/fetch-utils'
import * as Errors from './Errors.js'
import * as RedisManager from './RedisManager.js'
let RETRY_TIMEOUT_MS = 5000
async function getHistoryId(projectId) {
Metrics.inc('history_id_cache_requests_total')
const cachedHistoryId =
await RedisManager.promises.getCachedHistoryId(projectId)
if (cachedHistoryId) {
Metrics.inc('history_id_cache_hits_total')
return cachedHistoryId
} else {
const project = await _getProjectDetails(projectId)
const historyId =
project.overleaf &&
project.overleaf.history &&
project.overleaf.history.id
if (historyId != null) {
await RedisManager.promises.setCachedHistoryId(projectId, historyId)
}
return historyId
}
}
async function requestResync(projectId, opts = {}) {
try {
const body = {}
if (opts.historyRangesMigration) {
body.historyRangesMigration = opts.historyRangesMigration
}
await fetchNothing(
`${Settings.apis.web.url}/project/${projectId}/history/resync`,
{
method: 'POST',
signal: AbortSignal.timeout(6 * 60000),
basicAuth: {
user: Settings.apis.web.user,
password: Settings.apis.web.pass,
},
json: body,
}
)
} catch (err) {
if (err instanceof RequestFailedError && err.response.status === 404) {
throw new Errors.NotFoundError('got a 404 from web api').withCause(err)
} else {
throw err
}
}
}
async function _getProjectDetails(projectId, callback) {
logger.debug({ projectId }, 'getting project details from web')
let attempts = 0
while (true) {
attempts += 1
try {
return await fetchJson(
`${Settings.apis.web.url}/project/${projectId}/details`,
{
signal: AbortSignal.timeout(16000),
basicAuth: {
user: Settings.apis.web.user,
password: Settings.apis.web.pass,
},
}
)
} catch (err) {
if (err instanceof RequestFailedError && err.response.status === 404) {
throw new Errors.NotFoundError('got a 404 from web api').withCause(err)
} else if (attempts < 2) {
// retry after 5 seconds
await setTimeout(RETRY_TIMEOUT_MS)
} else {
throw err
}
}
}
}
/**
* Adjust the retry timeout in tests
*/
export async function setRetryTimeoutMs(timeoutMs) {
RETRY_TIMEOUT_MS = timeoutMs
}
// EXPORTS
const getHistoryIdCb = callbackify(getHistoryId)
const requestResyncCb = callbackify(requestResync)
export { getHistoryIdCb as getHistoryId, requestResyncCb as requestResync }
export const promises = {
getHistoryId,
requestResync,
}