diff --git a/common/models/rest-api.ts b/common/models/rest-api.ts index f43868247..49db977e4 100644 --- a/common/models/rest-api.ts +++ b/common/models/rest-api.ts @@ -3,7 +3,12 @@ import { ServerMessageEvent } from "./messages"; import { BehaviorOption, QueueMode, RoomSettings, RoomUserInfo, Visibility } from "./types"; import { QueueItem, Video, VideoId } from "./video"; import type { Category } from "sponsorblock-api"; -import { OttApiRequestRoomCreateSchema, OttApiRequestVoteSchema } from "./zod-schemas"; +import { + OttApiRequestRoomCreateSchema, + OttApiRequestVoteSchema, + OttApiRequestAddToQueueSchema, + OttApiRequestRemoveFromQueueSchema, +} from "./zod-schemas"; import { z } from "zod"; export type OttResponseBody = @@ -68,16 +73,9 @@ export interface OttApiRequestUndo { event: ServerMessageEvent; } -export type OttApiRequestAddToQueue = - | { - videos: VideoId[]; - } - | VideoId - | { - url: string; - }; +export type OttApiRequestAddToQueue = z.infer; -export type OttApiRequestRemoveFromQueue = VideoId; +export type OttApiRequestRemoveFromQueue = z.infer; export type OttApiResponseAddPreview = { result: Video[]; diff --git a/common/models/zod-schemas.ts b/common/models/zod-schemas.ts index 0486373d9..d1bc71ce4 100644 --- a/common/models/zod-schemas.ts +++ b/common/models/zod-schemas.ts @@ -1,7 +1,6 @@ import { ALL_VIDEO_SERVICES, ROOM_NAME_REGEX } from "ott-common/constants"; import { Visibility, QueueMode } from "ott-common/models/types"; -import { VideoService } from "./video"; -import { string, z } from "zod"; +import { z } from "zod"; // These strings are not allowed to be used as room names. const RESERVED_ROOM_NAMES = ["list", "create", "generate"]; @@ -28,3 +27,17 @@ const VideoIdSchema = z.object({ export const OttApiRequestVoteSchema = z.object({ ...VideoIdSchema.shape, }); + +export const OttApiRequestAddToQueueSchema = z.union([ + z.object({ + videos: z.array(VideoIdSchema), + }), + VideoIdSchema, + z.object({ + url: z.string(), + }), +]); + +export const OttApiRequestRemoveFromQueueSchema = z.object({ + ...VideoIdSchema.shape, +}); diff --git a/server/api/room.ts b/server/api/room.ts index d626e5a9a..cc5f5cdbf 100644 --- a/server/api/room.ts +++ b/server/api/room.ts @@ -35,6 +35,8 @@ import { conf } from "../ott-config"; import { OttApiRequestRoomCreateSchema, OttApiRequestVoteSchema, + OttApiRequestAddToQueueSchema, + OttApiRequestRemoveFromQueueSchema, } from "ott-common/models/zod-schemas"; import { ZodError } from "zod"; import { fromZodError } from "zod-validation-error"; @@ -350,9 +352,10 @@ const addToQueue: RequestHandler< OttResponseBody, OttApiRequestAddToQueue > = async (req, res) => { + const body = OttApiRequestAddToQueueSchema.parse(req.body); let points = 5; - if ("videos" in req.body) { - points = 3 * req.body.videos.length; + if ("videos" in body) { + points = 3 * body.videos.length; } if (!(await consumeRateLimitPoints(res, req.ip, points))) { return; @@ -360,27 +363,26 @@ const addToQueue: RequestHandler< const room = (await roommanager.getRoom(req.params.name)).unwrap(); let roomRequest: AddRequest; - if ("videos" in req.body) { + if ("videos" in body) { roomRequest = { type: RoomRequestType.AddRequest, - videos: req.body.videos, + videos: body.videos, }; - } else if ("url" in req.body) { + } else if ("url" in body) { roomRequest = { type: RoomRequestType.AddRequest, - url: req.body.url, + url: body.url, }; - } else if ("service" in req.body && "id" in req.body) { + } else { roomRequest = { type: RoomRequestType.AddRequest, video: { - service: req.body.service, - id: req.body.id, + service: body.service, + id: body.id, }, }; - } else { - throw new BadApiArgumentException("service,id", "missing"); } + await room.processUnauthorizedRequest(roomRequest, { token: req.token! }); res.json({ success: true, @@ -392,26 +394,23 @@ const removeFromQueue: RequestHandler< OttResponseBody, OttApiRequestRemoveFromQueue > = async (req, res) => { + const body = OttApiRequestRemoveFromQueueSchema.parse(req.body); let points = 5; if (!(await consumeRateLimitPoints(res, req.ip, points))) { return; } const room = (await roommanager.getRoom(req.params.name)).unwrap(); - if (req.body.service && req.body.id) { - await room.processUnauthorizedRequest( - { - type: RoomRequestType.RemoveRequest, - video: { service: req.body.service, id: req.body.id }, - }, - { token: req.token! } - ); - res.json({ - success: true, - }); - } else { - throw new BadApiArgumentException("service,id", "missing"); - } + await room.processUnauthorizedRequest( + { + type: RoomRequestType.RemoveRequest, + video: { service: body.service, id: body.id }, + }, + { token: req.token! } + ); + res.json({ + success: true, + }); }; const errorHandler: ErrorRequestHandler = (err: Error, req, res) => {