diff --git a/server/routes/api/__snapshots__/views.test.ts.snap b/server/routes/api/views/__snapshots__/views.test.ts.snap similarity index 100% rename from server/routes/api/__snapshots__/views.test.ts.snap rename to server/routes/api/views/__snapshots__/views.test.ts.snap diff --git a/server/routes/api/views/index.ts b/server/routes/api/views/index.ts new file mode 100644 index 000000000..2cf613113 --- /dev/null +++ b/server/routes/api/views/index.ts @@ -0,0 +1 @@ +export { default } from "./views"; diff --git a/server/routes/api/views/schema.ts b/server/routes/api/views/schema.ts new file mode 100644 index 000000000..a76a689f8 --- /dev/null +++ b/server/routes/api/views/schema.ts @@ -0,0 +1,23 @@ +import z from "zod"; +import BaseSchema from "../BaseSchema"; + +export const ViewsListSchema = BaseSchema.extend({ + body: z.object({ + /** Id of the document to retrieve the views for */ + documentId: z.string().uuid(), + + /** Whether to include views by suspended users */ + includeSuspended: z.boolean().default(false), + }), +}); + +export type ViewsListReq = z.infer; + +export const ViewsCreateSchema = BaseSchema.extend({ + body: z.object({ + /** Id of the document to create the view for */ + documentId: z.string().uuid(), + }), +}); + +export type ViewsCreateReq = z.infer; diff --git a/server/routes/api/views.test.ts b/server/routes/api/views/views.test.ts similarity index 100% rename from server/routes/api/views.test.ts rename to server/routes/api/views/views.test.ts diff --git a/server/routes/api/views.ts b/server/routes/api/views/views.ts similarity index 63% rename from server/routes/api/views.ts rename to server/routes/api/views/views.ts index 613f2bb46..8a4a15f24 100644 --- a/server/routes/api/views.ts +++ b/server/routes/api/views/views.ts @@ -1,40 +1,45 @@ import Router from "koa-router"; import auth from "@server/middlewares/authentication"; import { rateLimiter } from "@server/middlewares/rateLimiter"; +import validate from "@server/middlewares/validate"; import { View, Document, Event } from "@server/models"; import { authorize } from "@server/policies"; import { presentView } from "@server/presenters"; import { APIContext } from "@server/types"; import { RateLimiterStrategy } from "@server/utils/RateLimiter"; -import { assertUuid } from "@server/validation"; +import * as T from "./schema"; const router = new Router(); -router.post("views.list", auth(), async (ctx: APIContext) => { - const { documentId, includeSuspended = false } = ctx.request.body; - assertUuid(documentId, "documentId is required"); +router.post( + "views.list", + auth(), + validate(T.ViewsListSchema), + async (ctx: APIContext) => { + const { documentId, includeSuspended } = ctx.input.body; + const { user } = ctx.state.auth; - const { user } = ctx.state.auth; - const document = await Document.findByPk(documentId, { - userId: user.id, - }); - authorize(user, "read", document); - const views = await View.findByDocument(documentId, { includeSuspended }); + const document = await Document.findByPk(documentId, { + userId: user.id, + }); + authorize(user, "read", document); + const views = await View.findByDocument(documentId, { includeSuspended }); - ctx.body = { - data: views.map(presentView), - }; -}); + ctx.body = { + data: views.map(presentView), + }; + } +); router.post( "views.create", auth(), rateLimiter(RateLimiterStrategy.OneThousandPerHour), - async (ctx: APIContext) => { - const { documentId } = ctx.request.body; - assertUuid(documentId, "documentId is required"); - + validate(T.ViewsCreateSchema), + async (ctx: APIContext) => { + const { documentId } = ctx.input.body; const { user } = ctx.state.auth; + const document = await Document.findByPk(documentId, { userId: user.id, });