mirror of
https://github.com/imputnet/cobalt.git
synced 2025-02-17 19:00:13 +00:00
api: use zod for request schema validation
This commit is contained in:
parent
b510cbf9e0
commit
f32f624916
|
@ -40,7 +40,8 @@
|
||||||
"set-cookie-parser": "2.6.0",
|
"set-cookie-parser": "2.6.0",
|
||||||
"undici": "^5.19.1",
|
"undici": "^5.19.1",
|
||||||
"url-pattern": "1.0.3",
|
"url-pattern": "1.0.3",
|
||||||
"youtubei.js": "^10.3.0"
|
"youtubei.js": "^10.3.0",
|
||||||
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"freebind": "^0.2.2"
|
"freebind": "^0.2.2"
|
||||||
|
|
|
@ -138,8 +138,8 @@ export function runAPI(express, app, __dirname) {
|
||||||
request.youtubeDubLang = lang;
|
request.youtubeDubLang = lang;
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizedRequest = normalizeRequest(request);
|
const { success, data: normalizedRequest } = await normalizeRequest(request);
|
||||||
if (!normalizedRequest) {
|
if (!success) {
|
||||||
return fail('ErrorCantProcess');
|
return fail('ErrorCantProcess');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@ import match from "../processing/match.js";
|
||||||
import { extract } from "../processing/url.js";
|
import { extract } from "../processing/url.js";
|
||||||
|
|
||||||
export async function runTest(url, params, expect) {
|
export async function runTest(url, params, expect) {
|
||||||
const normalized = normalizeRequest({ url, ...params });
|
const { success, data: normalized } = await normalizeRequest({ url, ...params });
|
||||||
if (!normalized) {
|
if (!success) {
|
||||||
throw "invalid request";
|
throw "invalid request";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,7 @@
|
||||||
import ipaddr from "ipaddr.js";
|
import ipaddr from "ipaddr.js";
|
||||||
|
|
||||||
import { normalizeURL } from "./url.js";
|
|
||||||
import { createStream } from "../stream/manage.js";
|
import { createStream } from "../stream/manage.js";
|
||||||
import { verifyLanguageCode } from "../misc/utils.js";
|
import { apiSchema } from "./schema.js";
|
||||||
|
|
||||||
const apiRequest = {
|
|
||||||
option: {
|
|
||||||
audioFormat: ["best", "mp3", "ogg", "wav", "opus"],
|
|
||||||
downloadMode: ["auto", "audio", "mute"],
|
|
||||||
filenameStyle: ["classic", "pretty", "basic", "nerdy"],
|
|
||||||
videoQuality: ["max", "4320", "2160", "1440", "1080", "720", "480", "360", "240", "144"],
|
|
||||||
youtubeVideoCodec: ["h264", "av1", "vp9"],
|
|
||||||
},
|
|
||||||
boolean: [
|
|
||||||
"disableMetadata",
|
|
||||||
"tiktokFullAudio",
|
|
||||||
"tiktokH265",
|
|
||||||
"twitterGif",
|
|
||||||
"youtubeDubBrowserLang",
|
|
||||||
"youtubeDubLang"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createResponse(responseType, responseData) {
|
export function createResponse(responseType, responseData) {
|
||||||
const internalError = (code) => {
|
const internalError = (code) => {
|
||||||
|
@ -91,49 +72,7 @@ export function createResponse(responseType, responseData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeRequest(request) {
|
export function normalizeRequest(request) {
|
||||||
try {
|
return apiSchema.safeParseAsync(request).catch(() => ({ success: false }));
|
||||||
let template = {
|
|
||||||
audioFormat: "mp3",
|
|
||||||
url: normalizeURL(decodeURIComponent(request.url)),
|
|
||||||
youtubeVideoCodec: "h264",
|
|
||||||
videoQuality: "720",
|
|
||||||
filenameStyle: "classic",
|
|
||||||
downloadMode: "auto",
|
|
||||||
tiktokFullAudio: false,
|
|
||||||
disableMetadata: false,
|
|
||||||
youtubeDubBrowserLang: false,
|
|
||||||
youtubeDubLang: false,
|
|
||||||
twitterGif: false,
|
|
||||||
tiktokH265: false
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestKeys = Object.keys(request);
|
|
||||||
const templateKeys = Object.keys(template);
|
|
||||||
|
|
||||||
if (requestKeys.length > templateKeys.length + 1 || !request.url) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const i in requestKeys) {
|
|
||||||
const key = requestKeys[i];
|
|
||||||
const item = request[key];
|
|
||||||
|
|
||||||
if (String(key) !== "url" && templateKeys.includes(key)) {
|
|
||||||
if (apiRequest.boolean.includes(key)) {
|
|
||||||
template[key] = !!item;
|
|
||||||
} else if (apiRequest.option[key] && apiRequest.option[key].includes(item)) {
|
|
||||||
template[key] = String(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (template.youtubeDubBrowserLang)
|
|
||||||
template.youtubeDubLang = verifyLanguageCode(request.youtubeDubLang);
|
|
||||||
|
|
||||||
return template
|
|
||||||
} catch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getIP(req) {
|
export function getIP(req) {
|
||||||
|
|
42
api/src/processing/schema.js
Normal file
42
api/src/processing/schema.js
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { normalizeURL } from "./url.js";
|
||||||
|
import { verifyLanguageCode } from "../misc/utils.js";
|
||||||
|
|
||||||
|
export const apiSchema = z.object({
|
||||||
|
url: z.string()
|
||||||
|
.min(1)
|
||||||
|
.transform((url) => normalizeURL(decodeURIComponent(url))),
|
||||||
|
|
||||||
|
audioFormat: z.enum(
|
||||||
|
["best", "mp3", "ogg", "wav", "opus"]
|
||||||
|
).default("mp3"),
|
||||||
|
|
||||||
|
downloadMode: z.enum(
|
||||||
|
["auto", "audio", "mute"]
|
||||||
|
).default("auto"),
|
||||||
|
|
||||||
|
filenameStyle: z.enum(
|
||||||
|
["classic", "pretty", "basic", "nerdy"]
|
||||||
|
).default("classic"),
|
||||||
|
|
||||||
|
youtubeVideoCodec: z.enum(
|
||||||
|
["h264", "av1", "vp9"]
|
||||||
|
).default("h264"),
|
||||||
|
|
||||||
|
videoQuality: z.enum([
|
||||||
|
"max", "4320", "2160", "1440", "1080", "720", "480", "360", "240", "144"
|
||||||
|
]).default("720"),
|
||||||
|
|
||||||
|
youtubeDubLang: z.string()
|
||||||
|
.length(2)
|
||||||
|
.transform(verifyLanguageCode)
|
||||||
|
.optional(),
|
||||||
|
|
||||||
|
disableMetadata: z.boolean().default(false),
|
||||||
|
tiktokFullAudio: z.boolean().default(false),
|
||||||
|
tiktokH265: z.boolean().default(false),
|
||||||
|
twitterGif: z.boolean().default(false),
|
||||||
|
youtubeDubBrowserLang: z.boolean().default(false),
|
||||||
|
})
|
||||||
|
.strict();
|
|
@ -34,8 +34,10 @@ for (let i in services) {
|
||||||
let params = {...{url: test.url}, ...test.params};
|
let params = {...{url: test.url}, ...test.params};
|
||||||
console.log(params);
|
console.log(params);
|
||||||
|
|
||||||
let chck = normalizeRequest(params);
|
let chck = await normalizeRequest(params);
|
||||||
if (chck) {
|
if (chck.success) {
|
||||||
|
chck = chck.data;
|
||||||
|
|
||||||
const parsed = extract(chck.url);
|
const parsed = extract(chck.url);
|
||||||
if (parsed === null) {
|
if (parsed === null) {
|
||||||
throw `Invalid URL: ${chck.url}`
|
throw `Invalid URL: ${chck.url}`
|
||||||
|
|
|
@ -61,6 +61,9 @@ importers:
|
||||||
youtubei.js:
|
youtubei.js:
|
||||||
specifier: ^10.3.0
|
specifier: ^10.3.0
|
||||||
version: 10.3.0
|
version: 10.3.0
|
||||||
|
zod:
|
||||||
|
specifier: ^3.23.8
|
||||||
|
version: 3.23.8
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
freebind:
|
freebind:
|
||||||
specifier: ^0.2.2
|
specifier: ^0.2.2
|
||||||
|
@ -1834,6 +1837,9 @@ packages:
|
||||||
youtubei.js@10.3.0:
|
youtubei.js@10.3.0:
|
||||||
resolution: {integrity: sha512-tLmeJCECK2xF2hZZtF2nEqirdKVNLFSDpa0LhTaXY3tngtL7doQXyy7M2CLueramDTlmCnFaW+rctHirTPFaRQ==}
|
resolution: {integrity: sha512-tLmeJCECK2xF2hZZtF2nEqirdKVNLFSDpa0LhTaXY3tngtL7doQXyy7M2CLueramDTlmCnFaW+rctHirTPFaRQ==}
|
||||||
|
|
||||||
|
zod@3.23.8:
|
||||||
|
resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==}
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
'@ampproject/remapping@2.3.0':
|
'@ampproject/remapping@2.3.0':
|
||||||
|
@ -3405,3 +3411,5 @@ snapshots:
|
||||||
jintr: 2.1.1
|
jintr: 2.1.1
|
||||||
tslib: 2.6.3
|
tslib: 2.6.3
|
||||||
undici: 5.28.4
|
undici: 5.28.4
|
||||||
|
|
||||||
|
zod@3.23.8: {}
|
||||||
|
|
Loading…
Reference in a new issue