Files
Verso/services/web/app/src/Features/Subscription/V1SubscriptionManager.mjs
T
7c70b749d4 [monorepo] remove PII and variables from error messages (#31508)
* [monorepo] remove PII and variables from error messages

Exclusions:
- scripts
- tests
- fuzzing
- SplitTestManager (messages are sent to admin frontend)
- Group setup (we may want an error per unique tuple)
- sharejs (unused types; text type errors are shadowed already)
- history-v1 error messages that are used by the ErrorRecorder
- errors that flag issues with configuration/call signatures

I've used these search terms for finding unwanted error messages:
- new Error(`
- new Error\(\n\s+` (regex search)
- new OError(`
- new OError\(\n\s+` (regex search)

* [web] throw NotFoundError from ProjectLocator

* [github-sync] fix OError.tag call in script

Co-authored-by: Jessica Lawshe <jessica.lawshe@overleaf.com>

* [templates] revert changes to test client

---------

Co-authored-by: Jessica Lawshe <jessica.lawshe@overleaf.com>
GitOrigin-RevId: 736857a4fc5d9bfb0f8cb03e0f004eda87e5a220
2026-02-17 09:05:04 +00:00

127 lines
3.4 KiB
JavaScript

import UserGetter from '../User/UserGetter.mjs'
import request from 'requestretry'
import settings from '@overleaf/settings'
import { V1ConnectionError, NotFoundError } from '../Errors/Errors.js'
import { promisify } from '@overleaf/promise-utils'
import OError from '@overleaf/o-error'
const V1SubscriptionManager = {
cancelV1Subscription(userId, callback) {
V1SubscriptionManager._v1Request(
userId,
{
method: 'DELETE',
url(v1Id) {
return `/api/v1/overleaf/users/${v1Id}/subscription`
},
},
callback
)
},
v1IdForUser(userId, callback) {
UserGetter.getUser(userId, { 'overleaf.id': 1 }, function (err, user) {
if (err) {
return callback(err)
}
const v1Id = user?.overleaf?.id
callback(null, v1Id)
})
},
// v1 accounts created before migration to v2 had github and mendeley for free
// but these are now paid-for features for new accounts (v1id > cutoff)
getGrandfatheredFeaturesForV1User(v1Id) {
const cutoff = settings.v1GrandfatheredFeaturesUidCutoff
if (!cutoff) {
return {}
}
if (!v1Id) {
return {}
}
if (v1Id < cutoff) {
return settings.v1GrandfatheredFeatures || {}
} else {
return {}
}
},
_v1Request(userId, options, callback) {
if (!settings.apis.v1.url) {
return callback(null, null)
}
V1SubscriptionManager.v1IdForUser(userId, function (err, v1Id) {
if (err) {
return callback(err)
}
if (!v1Id) {
return callback(null, null, null)
}
const url = options.url(v1Id)
const requestOptions = {
baseUrl: settings.apis.v1.url,
url,
method: options.method,
auth: {
user: settings.apis.v1.user,
pass: settings.apis.v1.pass,
sendImmediately: true,
},
json: true,
timeout: settings.apis.v1.timeout,
}
if (options.method === 'GET') {
requestOptions.maxAttempts = 3
requestOptions.retryDelay = 500
} else {
requestOptions.maxAttempts = 0
}
request(requestOptions, function (error, response, body) {
if (error) {
return callback(
new V1ConnectionError({
message: 'no v1 connection',
info: { url },
}).withCause(error)
)
}
if (response && response.statusCode >= 500) {
return callback(
new V1ConnectionError({
message: 'error from v1',
info: {
status: response.statusCode,
body,
},
})
)
}
if (response.statusCode >= 200 && response.statusCode < 300) {
return callback(null, body, v1Id)
} else {
if (response.statusCode === 404) {
return callback(new NotFoundError(`v1 user not found: ${userId}`))
} else {
return callback(
new OError('non-success code from v1', {
url: options.url(v1Id),
method: options.method,
statusCode: response.statusCode,
})
)
}
}
})
})
},
}
V1SubscriptionManager.promises = {
cancelV1Subscription: promisify(V1SubscriptionManager.cancelV1Subscription),
v1IdForUser: promisify(V1SubscriptionManager.v1IdForUser),
}
export default V1SubscriptionManager