Merge pull request #36767 from overleaf/oa-express-types

[web] Express req/res/next types

GitOrigin-RevId: 30b4822e2ee55976331d049f8109f7098914973c
This commit is contained in:
Olzhas Askar
2026-09-17 12:02:38 +00:00
committed by Copybot
parent e9206d2b95
commit bb7dfcd588
14 changed files with 225 additions and 165 deletions
@@ -26,9 +26,9 @@ const {
*/
function useCapabilities() {
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
const middleware = async function (req, res, next) {
// attach the user's capabilities to the request object
@@ -36,7 +36,7 @@ function useCapabilities() {
// provide a function to assert that a capability is present
/** @param {any} capability */
req.assertPermission = capability => {
if (!req.capabilitySet.has(capability)) {
if (!req.capabilitySet?.has(capability)) {
throw new ForbiddenError(
`user does not have permission for ${capability}`
)
@@ -64,15 +64,17 @@ function useCapabilities() {
const combinedGroupPolicy = combineGroupPolicies(groupPolicies)
// attach the new capabilities to the request object
for (const cap of getUserCapabilities(combinedGroupPolicy)) {
req.capabilitySet.add(cap)
req.capabilitySet?.add(cap)
}
// also attach the user's restrictions (the capabilities they don't have)
req.userRestrictions = getUserRestrictions(combinedGroupPolicy)
// attach allowed properties to the request object
const allowedProperties = combineAllowedProperties(results)
/** @type {Record<string, any>} */
const reqRecord = req
for (const [prop, value] of Object.entries(allowedProperties)) {
req[prop] = value
reqRecord[prop] = value
}
}
next()
@@ -10,6 +10,12 @@ import Errors from '../Errors/Errors.js'
import { preparePlainTextResponse } from '../../infrastructure/Response.mjs'
import { z, zz, parseReq } from '../../infrastructure/Validation.mjs'
/**
* @typedef {import('express').Request} Request
* @typedef {import('express').Response} Response
* @typedef {import('express').NextFunction} NextFunction
*/
const getFileSchema = z.object({
params: z.strictObject({
Project_id: zz.objectId(),
@@ -28,8 +34,8 @@ const getFileHeadSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {Request} req
* @param {Response} res
*/
async function getFile(req, res) {
const { params, query } = parseReq(req, getFileSchema, { logOnly: true })
@@ -125,8 +131,8 @@ async function getFile(req, res) {
}
/**
* @param {any} req
* @param {any} res
* @param {Request} req
* @param {Response} res
*/
async function getFileHead(req, res) {
const { params } = parseReq(req, getFileHeadSchema, { logOnly: true })
@@ -35,6 +35,12 @@ import { z, zz, parseReq } from '../../infrastructure/Validation.mjs'
/** @type {any} */
import ProjectAuditLogHandler from '../Project/ProjectAuditLogHandler.mjs'
/**
* @typedef {import('express').Request} Request
* @typedef {import('express').Response} Response
* @typedef {import('express').NextFunction} NextFunction
*/
// Number of seconds after which the browser should send a request to revalidate
// blobs
const REVALIDATE_BLOB_AFTER_SECONDS = 86400 // 1 day
@@ -46,16 +52,16 @@ const STALE_WHILE_REVALIDATE_SECONDS = 365 * 86400 // 1 year
const MAX_HISTORY_ZIP_ATTEMPTS = 40
/**
* @param {any} req
* @param {any} res
* @param {Request} req
* @param {Response} res
*/
async function getBlob(req, res) {
await requestBlob('GET', req, res)
}
/**
* @param {any} req
* @param {any} res
* @param {Request} req
* @param {Response} res
*/
async function headBlob(req, res) {
await requestBlob('HEAD', req, res)
@@ -73,8 +79,8 @@ const requestBlobSchema = z.object({
/**
* @param {any} method
* @param {any} req
* @param {any} res
* @param {Request} req
* @param {Response} res
*/
async function requestBlob(method, req, res) {
const { params } = parseReq(req, requestBlobSchema)
@@ -124,7 +130,7 @@ async function requestBlob(method, req, res) {
}
/**
* @param {any} res
* @param {Response} res
* @param {any} etag
*/
function setBlobCacheHeaders(res, etag) {
@@ -159,9 +165,9 @@ const proxyToHistoryApiFallbackSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function proxyToHistoryApi(req, res, next) {
parseReq(req, proxyToHistoryApiSchema, {
@@ -210,9 +216,9 @@ const proxyToHistoryApiAndInjectUserDetailsFallbackSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function proxyToHistoryApiAndInjectUserDetails(req, res, next) {
parseReq(req, proxyToHistoryApiAndInjectUserDetailsSchema, {
@@ -251,9 +257,9 @@ const resyncProjectHistoryFallbackSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function resyncProjectHistory(req, res, next) {
// increase timeout to 6 minutes
@@ -306,9 +312,9 @@ const restoreFileFromV2FallbackSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function restoreFileFromV2(req, res, next) {
const { params, body } = parseReq(req, restoreFileFromV2Schema, {
@@ -367,9 +373,9 @@ const revertFileFallbackSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function revertFile(req, res, next) {
const { params, body } = parseReq(req, revertFileSchema, {
@@ -427,9 +433,9 @@ const revertProjectFallbackSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function revertProject(req, res, next) {
const { params, body } = parseReq(req, revertProjectSchema, {
@@ -475,9 +481,9 @@ const getLabelsFallbackSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function getLabels(req, res, next) {
const { params } = parseReq(req, getLabelsSchema, {
@@ -516,9 +522,9 @@ const createLabelFallbackSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function createLabel(req, res, next) {
const { params, body } = parseReq(req, createLabelSchema, {
@@ -636,9 +642,9 @@ const deleteLabelFallbackSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function deleteLabel(req, res, next) {
const { params } = parseReq(req, deleteLabelSchema, {
@@ -672,9 +678,9 @@ const downloadZipOfVersionSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function downloadZipOfVersion(req, res, next) {
const { params } = parseReq(req, downloadZipOfVersionSchema)
@@ -718,8 +724,8 @@ async function downloadZipOfVersion(req, res, next) {
* @param {any} v1ProjectId
* @param {any} version
* @param {any} name
* @param {any} req
* @param {any} res
* @param {Request} req
* @param {Response} res
*/
async function _pipeHistoryZipToResponse(v1ProjectId, version, name, req, res) {
if (req.destroyed) {
@@ -837,9 +843,9 @@ const getLatestHistorySchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function getLatestHistory(req, res, next) {
const { params } = parseReq(req, getLatestHistorySchema)
@@ -859,9 +865,9 @@ const getChangesSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function getChanges(req, res, next) {
const { params, query } = parseReq(req, getChangesSchema)
@@ -131,7 +131,7 @@ const _buildPortalTemplatesList = affiliations => {
}
/**
* @param {any} req
* @param {import("express").Request} req
*/
function cleanupSession(req) {
// cleanup redirects at the end of the redirect chain
@@ -4,6 +4,12 @@ import SessionManager from '../Authentication/SessionManager.mjs'
import LearnedWordsManager from './LearnedWordsManager.mjs'
import { z, parseReq } from '../../infrastructure/Validation.mjs'
/**
* @typedef {import('express').Request} Request
* @typedef {import('express').Response} Response
* @typedef {import('express').NextFunction} NextFunction
*/
const learnSchema = z.object({
body: z.strictObject({
word: z.string().min(1),
@@ -32,9 +38,9 @@ const unlearnFallbackSchema = z.object({
export default {
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
learn(req, res, next) {
const { body } = parseReq(req, learnSchema, {
@@ -53,9 +59,9 @@ export default {
},
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
unlearn(req, res, next) {
const { body } = parseReq(req, unlearnSchema, {
@@ -41,6 +41,9 @@ const SUBSCRIPTION_PAUSED_REDIRECT_PATH =
* @typedef {import('../../../../types/subscription/currency').CurrencyCode} CurrencyCode
* @typedef {import('./PaymentProviderEntities.mjs').PaymentProviderSubscription} PaymentProviderSubscription
* @typedef {import('../../../../types/subscription/plan').Plan} Plan
* @typedef {import('express').Request} Request
* @typedef {import('express').Response} Response
* @typedef {import('express').NextFunction} NextFunction
*/
/**
@@ -161,8 +164,8 @@ const userSubscriptionPageSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {Request} req
* @param {Response} res
*/
async function userSubscriptionPage(req, res) {
const { query } = parseReq(req, userSubscriptionPageSchema, {
@@ -378,8 +381,8 @@ const successfulSubscriptionFallbackSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {Request} req
* @param {Response} res
*/
async function successfulSubscription(req, res) {
const { query } = parseReq(req, successfulSubscriptionSchema, {
@@ -431,9 +434,9 @@ const pauseSubscriptionSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function pauseSubscription(req, res, next) {
const user = SessionManager.getSessionUser(req.session)
@@ -487,9 +490,9 @@ async function pauseSubscription(req, res, next) {
}
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function resumeSubscription(req, res, next) {
const user = SessionManager.getSessionUser(req.session)
@@ -508,9 +511,9 @@ async function resumeSubscription(req, res, next) {
}
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function cancelSubscription(req, res, next) {
const user = SessionManager.getSessionUser(req.session)
@@ -527,12 +530,10 @@ async function cancelSubscription(req, res, next) {
}
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @returns {Promise<void>}
* @param {Request} req
* @param {Response} res
*/
async function canceledSubscription(req, res, next) {
async function canceledSubscription(req, res) {
return res.render('subscriptions/canceled-subscription-react', {
title: 'subscription_canceled',
user: sanitizeSessionUserForFrontEnd(
@@ -542,9 +543,9 @@ async function canceledSubscription(req, res, next) {
}
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
function cancelV1Subscription(req, res, next) {
const userId = SessionManager.getLoggedInUserId(req.session)
@@ -570,8 +571,8 @@ const previewAddonPurchaseSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {Request} req
* @param {Response} res
*/
async function previewAddonPurchase(req, res) {
const { params } = parseReq(req, previewAddonPurchaseSchema, {
@@ -589,11 +590,10 @@ async function previewAddonPurchase(req, res) {
}
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
*/
async function purchaseAddon(req, res, next) {
async function purchaseAddon(req, res) {
return res.sendStatus(404)
}
@@ -604,9 +604,9 @@ const removeAddonSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function removeAddon(req, res, next) {
const user = SessionManager.getSessionUser(req.session)
@@ -662,8 +662,8 @@ const reactivateAddonSchema = z.object({
* Reactivate an add-on pending cancellation
*
* This "cancels" the cancellation.
* @param {any} req
* @param {any} res
* @param {Request} req
* @param {Response} res
*/
async function reactivateAddon(req, res) {
const user = SessionManager.getSessionUser(req.session)
@@ -701,11 +701,10 @@ const previewSubscriptionSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
*/
async function previewSubscription(req, res, next) {
async function previewSubscription(req, res) {
const { query } = parseReq(req, previewSubscriptionSchema, {
logOnly: true,
})
@@ -768,9 +767,9 @@ async function previewSubscription(req, res, next) {
}
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
function cancelPendingSubscriptionChange(req, res, next) {
const user = SessionManager.getSessionUser(req.session)
@@ -794,9 +793,9 @@ function cancelPendingSubscriptionChange(req, res, next) {
}
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function updateAccountEmailAddress(req, res, next) {
const user = SessionManager.getSessionUser(req.session)
@@ -813,9 +812,9 @@ async function updateAccountEmailAddress(req, res, next) {
}
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
function reactivateSubscription(req, res, next) {
const user = SessionManager.getSessionUser(req.session)
@@ -859,9 +858,9 @@ const recurlyCallbackSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
function recurlyCallback(req, res, next) {
const { body } = parseReq(req, recurlyCallbackSchema, { logOnly: true })
@@ -914,8 +913,8 @@ function recurlyCallback(req, res, next) {
}
/**
* @param {any} req
* @param {any} res
* @param {Request} req
* @param {Response} res
*/
async function extendTrial(req, res) {
const user = SessionManager.getSessionUser(req.session)
@@ -943,9 +942,9 @@ async function extendTrial(req, res) {
}
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
function recurlyNotificationParser(req, res, next) {
let xml = ''
@@ -975,8 +974,8 @@ const refreshUserFeaturesSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {Request} req
* @param {Response} res
*/
async function refreshUserFeatures(req, res) {
const { params } = parseReq(req, refreshUserFeaturesSchema, {
@@ -1003,8 +1002,8 @@ const getRecommendedCurrencySchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {Request} req
* @param {Response} res
* @returns {Promise<{currency: CurrencyCode, recommendedCurrency: CurrencyCode, countryCode: string|undefined}>}
*/
async function getRecommendedCurrency(req, res) {
@@ -1049,8 +1048,8 @@ const getLatamCountryBannerDetailsSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {Request} req
* @param {Response} res
*/
async function getLatamCountryBannerDetails(req, res) {
const { query } = parseReq(req, getLatamCountryBannerDetailsSchema, {
@@ -236,7 +236,7 @@ async function syncSubscription(recurlySubscription, requesterData) {
/**
* attempt to collect past due invoice for customer. Only do that when a) the
* customer is using Paypal and b) there is only one past due invoice.
* This is used because Recurly doesn't always attempt collection of paast due
* This is used because Recurly doesn't always attempt collection of past due
* invoices after Paypal billing info were updated.
*
* @param {any} recurlyAccountCode
@@ -19,6 +19,12 @@ import UserAuditLogHandler from '../User/UserAuditLogHandler.mjs'
import { sanitizeSessionUserForFrontEnd } from '../../infrastructure/FrontEndUser.mjs'
import { z, zz, parseReq } from '../../infrastructure/Validation.mjs'
/**
* @typedef {import('express').Request} Request
* @typedef {import('express').Response} Response
* @typedef {import('express').NextFunction} NextFunction
*/
const rateLimiters = {
resendGroupInvite: new RateLimiter('resend-group-invite', {
points: 1,
@@ -33,9 +39,9 @@ const createInviteSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function createInvite(req, res, next) {
const { body } = parseReq(req, createInviteSchema, { logOnly: true })
@@ -96,9 +102,9 @@ const viewInviteSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function viewInvite(req, res, next) {
const { params, query } = parseReq(req, viewInviteSchema, {
@@ -220,9 +226,9 @@ async function viewInvite(req, res, next) {
}
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function viewInvites(req, res, next) {
const user = SessionManager.getSessionUser(req.session)
@@ -246,9 +252,9 @@ const acceptInviteSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function acceptInvite(req, res, next) {
const { params } = parseReq(req, acceptInviteSchema, { logOnly: true })
@@ -294,9 +300,9 @@ const revokeInviteSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
function revokeInvite(req, res, next) {
const { params } = parseReq(req, revokeInviteSchema, { logOnly: true })
@@ -331,9 +337,9 @@ const resendInviteSchema = z.object({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async function resendInvite(req, res, next) {
const { body } = parseReq(req, resendInviteSchema, { logOnly: true })
@@ -23,6 +23,12 @@ import AnalyticsManager from '../Analytics/AnalyticsManager.mjs'
import { multer, multerUploadHandler } from '../../infrastructure/Multer.mjs'
import { parseReq, z, zz } from '../../infrastructure/Validation.mjs'
/**
* @typedef {import('express').Request} Request
* @typedef {import('express').Response} Response
* @typedef {import('express').NextFunction} NextFunction
*/
const uploadMetaTypeSchema = z.string().optional()
const uploadProjectSchema = z.object({
@@ -77,11 +83,10 @@ const upload = multerUploadHandler({
})
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
*/
function uploadProject(req, res, next) {
function uploadProject(req, res) {
const timer = new metrics.Timer('project-upload')
const userId = SessionManager.getLoggedInUserId(req.session)
const { body, file } = parseReq(req, uploadProjectSchema, {
@@ -120,11 +125,10 @@ function uploadProject(req, res, next) {
}
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
*/
async function uploadFile(req, res, next) {
async function uploadFile(req, res) {
const timer = new metrics.Timer('file-upload')
const { params, query, body, file } = parseReq(req, uploadFileSchema, {
logOnly: true,
@@ -226,11 +230,10 @@ async function uploadFile(req, res, next) {
}
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
*/
async function importDocument(req, res, next) {
async function importDocument(req, res) {
const userId = SessionManager.getLoggedInUserId(req.session)
const { query, body, file } = parseReq(req, importDocumentSchema, {
logOnly: true,
@@ -313,9 +316,9 @@ async function importDocument(req, res, next) {
}
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
function multerMiddleware(req, res, next) {
if (upload == null) {
@@ -17,6 +17,12 @@ import {
} from '../../infrastructure/Validation.mjs'
import AdminAuthorizationHelper from '../Helpers/AdminAuthorizationHelper.mjs'
/**
* @typedef {import('express').Request} Request
* @typedef {import('express').Response} Response
* @typedef {import('express').NextFunction} NextFunction
*/
const { useAdminCapabilities, useNonAdminDomainCapabilities } =
AdminAuthorizationHelper
// set of middleware arrays or functions that checks user access to an entity
@@ -310,9 +316,9 @@ const requireGraphAccessSchema = z.object({
// - the entity name is in `req.query.resource_type` and is used to find the
// require middleware depending on the entity name
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
function requireGraphAccess(req, res, next) {
const { query, params } = parseReq(req, requireGraphAccessSchema, {
@@ -3,7 +3,10 @@
import Settings from '@overleaf/settings'
/**
* @import { HttpPermissionsPolicy } from './types'
* @typedef {import('express').Request} Request
* @typedef {import('express').Response} Response
* @typedef {import('express').NextFunction} NextFunction
* @typedef {import('./types').HttpPermissionsPolicy} HttpPermissionsPolicy
*/
class HttpPermissionsPolicyMiddleware {
@@ -73,16 +76,17 @@ class HttpPermissionsPolicyMiddleware {
}
/**
* @param {any} req
* @param {any} res
* @param {any} next
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
middleware(req, res, next) {
if (this.policy && Settings.useHttpPermissionsPolicy) {
const policy = this.policy
if (policy && Settings.useHttpPermissionsPolicy) {
const originalRender = res.render
res.render = (/** @type {any} */ ...args) => {
res.setHeader('Permissions-Policy', this.policy)
res.setHeader('Permissions-Policy', policy)
originalRender.apply(res, args)
}
}
@@ -3,8 +3,10 @@ import { UserFeatureUsage } from '../../models/UserFeatureUsage.mjs'
import { TooManyRequestsError } from '../../Features/Errors/Errors.js'
import AnalyticsManager from '../../Features/Analytics/AnalyticsManager.mjs'
import UserAuditLogHandler from '../../Features/User/UserAuditLogHandler.mjs'
/** @typedef {{usage?: number | null, periodStart?: Date | null}} FeatureUsage */
/** @typedef {{remainingTokens?: number | null, periodStart?: Date | null}} RemainingTokens */
/** @typedef {import('express').Response} Response */
const PERIOD = 24 // hours
const PERIOD_IN_MILLISECONDS = PERIOD * 60 * 60 * 1000
@@ -60,7 +62,7 @@ export default class TokenUsageRateLimiter {
/**
* @param {any} userId
* @param {any} res
* @param {Response} res
* @param {any} amount
* @param {{ auditLogTool?: string }} [options] - if `auditLogTool` is set, an `ai-quota-breach` audit log entry is written with `{ tool }` in the info payload when this recording crosses or sits past the allowance
*/
+7
View File
@@ -1,6 +1,7 @@
import 'express'
import OAuth2Server from '@node-oauth/oauth2-server'
import type SessionData from 'express-session'
import type { Capability } from '../../capabilities'
// Request-scoped logger attached by @overleaf/metrics http.monitor() middleware.
// See libraries/metrics/http.js RequestLogger class.
@@ -20,11 +21,17 @@ declare module 'express' {
oauth_user?: OAuth2Server.User
logger: RequestLogger
i18n: {
language: string
translate(
key: string,
vars?: Record<string, any>,
components?: any
): string
}
// Set by PermissionMiddleware
managedBy?: any
isManagedGroupAdmin?: boolean
capabilitySet?: Set<Capability>
assertPermission(capability: string): void
}
}
+13
View File
@@ -0,0 +1,13 @@
import 'express'
declare module 'express' {
// eslint-disable-next-line no-unused-vars
interface Response {
// Set per-request by the addSetContentDisposition middleware in
// app/src/infrastructure/ExpressLocals.mjs (web/private-api/public-api routers only).
setContentDisposition(
type: 'attachment' | 'inline',
opts: { filename: string }
): void
}
}