feat: add pagination and strict query validation for workspace/project listings

This commit is contained in:
Yusuf İpek
2026-02-24 18:07:04 +03:00
parent 9f32aeafe6
commit 644b239ab3
6 changed files with 236 additions and 65 deletions
+16 -2
View File
@@ -12,11 +12,25 @@ export async function GET(request: NextRequest, { params }: RouteParams) {
try {
const session = await auth();
const { projectId } = await params;
const MAX_LIMIT = 100;
const MAX_OFFSET = 10000;
// Parse pagination params
const searchParams = request.nextUrl.searchParams;
const limit = Math.min(parseInt(searchParams.get('limit') || '20'), 100);
const offset = Math.max(0, parseInt(searchParams.get('offset') || '0'));
const limitParam = searchParams.get('limit');
const offsetParam = searchParams.get('offset');
const limitRaw = limitParam === null ? 20 : Number(limitParam);
if (!Number.isSafeInteger(limitRaw) || limitRaw < 1 || limitRaw > MAX_LIMIT) {
return apiErrors.badRequest('Invalid limit. Must be a positive integer between 1 and 100.');
}
const offset = offsetParam === null ? 0 : Number(offsetParam);
if (!Number.isSafeInteger(offset) || offset < 0 || offset > MAX_OFFSET) {
return apiErrors.badRequest('Invalid offset. Must be a non-negative integer up to 10000.');
}
const limit = limitRaw;
const project = await db.project.findUnique({
where: { id: projectId },
+21 -2
View File
@@ -10,16 +10,35 @@ import { DEFAULT_COMMENT_TAGS } from '@/lib/comment-tags';
export async function GET(request: NextRequest) {
try {
const session = await auth();
const MAX_LIMIT = 100;
const MAX_PAGE = 1000;
const MAX_OFFSET = 10000;
if (!session?.user?.id) {
return apiErrors.unauthorized();
}
const { searchParams } = new URL(request.url);
const page = parseInt(searchParams.get('page') || '1');
const limit = parseInt(searchParams.get('limit') || '10');
const pageParam = searchParams.get('page');
const limitParam = searchParams.get('limit');
const workspaceId = searchParams.get('workspaceId');
const pageRaw = pageParam === null ? 1 : Number(pageParam);
if (!Number.isSafeInteger(pageRaw) || pageRaw < 1 || pageRaw > MAX_PAGE) {
return apiErrors.badRequest('Invalid page. Must be a positive integer.');
}
const limitRaw = limitParam === null ? 10 : Number(limitParam);
if (!Number.isSafeInteger(limitRaw) || limitRaw < 1 || limitRaw > MAX_LIMIT) {
return apiErrors.badRequest('Invalid limit. Must be a positive integer between 1 and 100.');
}
const page = pageRaw;
const limit = limitRaw;
const skip = (page - 1) * limit;
if (!Number.isSafeInteger(skip) || skip > MAX_OFFSET) {
return apiErrors.badRequest('Invalid page range. Offset must be 10000 or less.');
}
// Build base filter: user is owner OR a member
const baseFilter: Record<string, unknown> = {