mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-27 02:51:57 +02:00
Merge pull request #28380 from overleaf/td-zod-history
Migrate history-related web back end to Zod GitOrigin-RevId: 38ed56927bee4f5670604d7178b096e382c8cb65
This commit is contained in:
@@ -25,6 +25,7 @@ import ProjectEntityUpdateHandler from '../Project/ProjectEntityUpdateHandler.js
|
||||
import RestoreManager from './RestoreManager.mjs'
|
||||
import { prepareZipAttachment } from '../../infrastructure/Response.js'
|
||||
import Features from '../../infrastructure/Features.js'
|
||||
import { z, zz, validateReq } from '../../infrastructure/Validation.js'
|
||||
|
||||
// Number of seconds after which the browser should send a request to revalidate
|
||||
// blobs
|
||||
@@ -44,8 +45,19 @@ async function headBlob(req, res) {
|
||||
await requestBlob('HEAD', req, res)
|
||||
}
|
||||
|
||||
const requestBlobSchema = z.object({
|
||||
params: z.object({
|
||||
project_id: zz.coercedObjectId(),
|
||||
hash: zz.hex().length(40),
|
||||
}),
|
||||
query: z.object({
|
||||
fallback: zz.coercedObjectId().optional(),
|
||||
}),
|
||||
})
|
||||
|
||||
async function requestBlob(method, req, res) {
|
||||
const { project_id: projectId, hash } = req.params
|
||||
const { params } = validateReq(req, requestBlobSchema)
|
||||
const { project_id: projectId, hash } = params
|
||||
|
||||
// Handle conditional GET request
|
||||
if (req.get('If-None-Match') === hash) {
|
||||
@@ -461,17 +473,35 @@ async function _pipeHistoryZipToResponse(v1ProjectId, version, name, req, res) {
|
||||
}
|
||||
}
|
||||
|
||||
const getLatestHistorySchema = z.object({
|
||||
params: z.object({
|
||||
project_id: zz.objectId(),
|
||||
}),
|
||||
})
|
||||
|
||||
async function getLatestHistory(req, res, next) {
|
||||
const projectId = req.params.project_id
|
||||
const { params } = validateReq(req, getLatestHistorySchema)
|
||||
const projectId = params.project_id
|
||||
const history = await HistoryManager.promises.getLatestHistory(projectId)
|
||||
res.json(history)
|
||||
}
|
||||
|
||||
const getChangesSchema = z.object({
|
||||
params: z.object({
|
||||
project_id: zz.objectId(),
|
||||
}),
|
||||
query: z.object({
|
||||
since: z.coerce.number().int().min(0).optional(),
|
||||
paginated: z.stringbool().optional(),
|
||||
}),
|
||||
})
|
||||
|
||||
async function getChanges(req, res, next) {
|
||||
const projectId = req.params.project_id
|
||||
let since = req.query.since
|
||||
const { params, query } = validateReq(req, getChangesSchema)
|
||||
const projectId = params.project_id
|
||||
let since = query.since
|
||||
// TODO: Transition flag; remove after a while
|
||||
const paginated = req.query.paginated === 'true'
|
||||
const paginated = query.paginated
|
||||
|
||||
if (paginated) {
|
||||
const changes = await HistoryManager.promises.getChanges(projectId, {
|
||||
|
||||
@@ -280,8 +280,8 @@ async function getLatestHistory(projectId) {
|
||||
* Get history changes since a given version
|
||||
*
|
||||
* @param {string} projectId
|
||||
* @param {object} opts
|
||||
* @param {number} opts.since - The start version of changes to get
|
||||
* @param {object} [opts]
|
||||
* @param {number} [opts.since] - The start version of changes to get
|
||||
*/
|
||||
async function getChanges(projectId, opts = {}) {
|
||||
const historyId = await getHistoryId(projectId)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// @ts-check
|
||||
|
||||
import Settings from '@overleaf/settings'
|
||||
import { Joi, validate } from '../../infrastructure/Validation.js'
|
||||
import { RateLimiter } from '../../infrastructure/RateLimiter.js'
|
||||
import AuthenticationController from '../Authentication/AuthenticationController.js'
|
||||
import AuthorizationMiddleware from '../Authorization/AuthorizationMiddleware.mjs'
|
||||
@@ -29,30 +28,12 @@ function apply(webRouter, privateApiRouter) {
|
||||
|
||||
webRouter.head(
|
||||
'/project/:project_id/blob/:hash',
|
||||
validate({
|
||||
params: Joi.object({
|
||||
project_id: Joi.objectId().required(),
|
||||
hash: Joi.string().required().hex().length(40),
|
||||
}),
|
||||
query: Joi.object({
|
||||
fallback: Joi.objectId().optional(),
|
||||
}),
|
||||
}),
|
||||
RateLimiterMiddleware.rateLimit(rateLimiters.getProjectBlob),
|
||||
AuthorizationMiddleware.ensureUserCanReadProject,
|
||||
HistoryController.headBlob
|
||||
)
|
||||
webRouter.get(
|
||||
'/project/:project_id/blob/:hash',
|
||||
validate({
|
||||
params: Joi.object({
|
||||
project_id: Joi.objectId().required(),
|
||||
hash: Joi.string().required().hex().length(40),
|
||||
}),
|
||||
query: Joi.object({
|
||||
fallback: Joi.objectId().optional(),
|
||||
}),
|
||||
}),
|
||||
RateLimiterMiddleware.rateLimit(rateLimiters.getProjectBlob),
|
||||
AuthorizationMiddleware.ensureUserCanReadProject,
|
||||
HistoryController.getBlob
|
||||
@@ -151,25 +132,12 @@ function apply(webRouter, privateApiRouter) {
|
||||
|
||||
webRouter.get(
|
||||
'/project/:project_id/latest/history',
|
||||
validate({
|
||||
params: Joi.object({
|
||||
project_id: Joi.objectId().required(),
|
||||
}),
|
||||
}),
|
||||
AuthorizationMiddleware.blockRestrictedUserFromProject,
|
||||
AuthorizationMiddleware.ensureUserCanReadProject,
|
||||
HistoryController.getLatestHistory
|
||||
)
|
||||
webRouter.get(
|
||||
'/project/:project_id/changes',
|
||||
validate({
|
||||
params: Joi.object({
|
||||
project_id: Joi.objectId().required(),
|
||||
}),
|
||||
query: Joi.object({
|
||||
since: Joi.number().integer().min(0).optional(),
|
||||
}),
|
||||
}),
|
||||
AuthorizationMiddleware.blockRestrictedUserFromProject,
|
||||
AuthorizationMiddleware.ensureUserCanReadProject,
|
||||
HistoryController.getChanges
|
||||
|
||||
@@ -48,6 +48,7 @@ const zz = {
|
||||
.string()
|
||||
.refine(ObjectId.isValid, { message: 'invalid Mongo ObjectId' })
|
||||
.transform(val => new ObjectId(val)),
|
||||
hex: () => z.string().regex(/^[0-9a-f]*$/),
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user