perf(webapp): clamp list-endpoint page size to 100 (#4360)
## Summary Several list endpoints accepted an unbounded page size (`perPage` / `per_page` / `pageSize`). An unbounded page lets one request pull an arbitrarily large result set and do a proportional amount of work, which is a poor default for a shared API. This clamps the page size to 100 on every list endpoint that was uncapped, matching the existing cap on `api.v1.runs` and `api.v1.sessions`. Clamping rather than rejecting keeps existing clients working: a request for a larger page returns up to 100 items and offset pagination continues from there. ## Endpoints capped - `api.v1.schedules` (`perPage`) - `api.v1.queues` (`perPage`) - `resources.…versions` (`per_page`) - `resources.…queues` (`per_page`) - `admin.api.v1.…engine.report` (`per_page`) - `admin.api.v1.llm-models` (`pageSize`) Already capped, left as-is: `api.v1.runs`, `api.v1.sessions`, `api.v1.deployments`.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
area: webapp
|
||||
type: improvement
|
||||
---
|
||||
|
||||
List API endpoints now clamp the page size to a maximum of 100. Requests asking for a larger page size return up to 100 items and keep paginating, rather than pulling an unbounded page.
|
||||
@@ -13,7 +13,12 @@ const ParamsSchema = z.object({
|
||||
const SearchParamsSchema = z.object({
|
||||
verbose: z.string().default("0"),
|
||||
page: z.coerce.number().optional(),
|
||||
per_page: z.coerce.number().optional(),
|
||||
per_page: z.coerce
|
||||
.number()
|
||||
.int()
|
||||
.positive()
|
||||
.transform((n) => Math.min(n, 100))
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export async function loader({ request, params }: LoaderFunctionArgs) {
|
||||
|
||||
@@ -9,7 +9,10 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
||||
|
||||
const url = new URL(request.url);
|
||||
const page = parseInt(url.searchParams.get("page") ?? "1");
|
||||
const pageSize = parseInt(url.searchParams.get("pageSize") ?? "50");
|
||||
const pageSize = Math.max(
|
||||
1,
|
||||
Math.min(parseInt(url.searchParams.get("pageSize") ?? "50") || 50, 100)
|
||||
);
|
||||
|
||||
const [models, total] = await Promise.all([
|
||||
prisma.llmModel.findMany({
|
||||
|
||||
@@ -12,7 +12,12 @@ import { ServiceValidationError } from "~/v3/services/baseService.server";
|
||||
|
||||
const SearchParamsSchema = z.object({
|
||||
page: z.coerce.number().int().positive().optional(),
|
||||
perPage: z.coerce.number().int().positive().optional(),
|
||||
perPage: z.coerce
|
||||
.number()
|
||||
.int()
|
||||
.positive()
|
||||
.transform((n) => Math.min(n, 100))
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export const loader = createLoaderApiRoute(
|
||||
|
||||
@@ -12,7 +12,12 @@ import { UpsertTaskScheduleService } from "~/v3/services/upsertTaskSchedule.serv
|
||||
|
||||
const SearchParamsSchema = z.object({
|
||||
page: z.coerce.number().int().positive().optional(),
|
||||
perPage: z.coerce.number().int().positive().optional(),
|
||||
perPage: z.coerce
|
||||
.number()
|
||||
.int()
|
||||
.positive()
|
||||
.transform((n) => Math.min(n, 100))
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export async function action({ request }: ActionFunctionArgs) {
|
||||
|
||||
+5
-1
@@ -9,7 +9,11 @@ import { EnvironmentParamSchema } from "~/utils/pathBuilder";
|
||||
const SearchParamsSchema = z.object({
|
||||
query: z.string().optional(),
|
||||
page: z.coerce.number().min(1).default(1),
|
||||
per_page: z.coerce.number().min(1).default(20),
|
||||
per_page: z.coerce
|
||||
.number()
|
||||
.min(1)
|
||||
.transform((n) => Math.min(n, 100))
|
||||
.default(20),
|
||||
type: z.enum(["task", "custom"]).optional(),
|
||||
});
|
||||
|
||||
|
||||
+5
-1
@@ -8,7 +8,11 @@ import { EnvironmentParamSchema } from "~/utils/pathBuilder";
|
||||
|
||||
const SearchParamsSchema = z.object({
|
||||
query: z.string().optional(),
|
||||
per_page: z.coerce.number().min(1).default(25),
|
||||
per_page: z.coerce
|
||||
.number()
|
||||
.min(1)
|
||||
.transform((n) => Math.min(n, 100))
|
||||
.default(25),
|
||||
});
|
||||
|
||||
export async function loader({ request, params }: LoaderFunctionArgs) {
|
||||
|
||||
Reference in New Issue
Block a user