fix(api): add rate limiting, file size validation, and timeout handling

- Add Content-Length header check for early file size validation on audio upload
- Add rate limiting (60 req/min) to public watch endpoint
- Add 10-second timeout with AbortController for YouTube and Vimeo oEmbed requests
- Add automatic rate limit cleanup interval for self-hosted servers
- Fix null check for comment.replies in video page content
- Add checkWorkspaceAccess helper for workspace authorization
This commit is contained in:
Yusuf İpek
2026-02-14 15:59:30 +03:00
parent 413fc9cec6
commit 20005f1a15
8 changed files with 68 additions and 4 deletions
+10
View File
@@ -10,6 +10,15 @@ const ALLOWED_TYPES = ['audio/webm', 'audio/ogg', 'audio/mp4', 'audio/mpeg', 'au
export async function POST(request: Request) {
try {
// Check Content-Length header BEFORE loading the file
const contentLength = request.headers.get('content-length');
if (contentLength) {
const fileSize = parseInt(contentLength, 10);
if (isNaN(fileSize) || fileSize > MAX_FILE_SIZE) {
return apiErrors.badRequest('File too large. Maximum size is 10MB.');
}
}
// Rate limit
const limited = await rateLimit(request, 'voice-upload');
if (limited) return limited;
@@ -27,6 +36,7 @@ export async function POST(request: Request) {
return apiErrors.badRequest('No audio file provided');
}
// Double-check file size (defense in depth - Content-Length can be spoofed)
if (file.size > MAX_FILE_SIZE) {
return apiErrors.badRequest('File too large. Maximum size is 10MB.');
}
+5
View File
@@ -2,12 +2,17 @@ import { NextRequest } from 'next/server';
import { db } from '@/lib/db';
import { auth, checkProjectAccess } from '@/lib/auth';
import { apiErrors, successResponse, withCacheControl } from '@/lib/api-response';
import { rateLimit } from '@/lib/rate-limit';
type RouteParams = { params: Promise<{ videoId: string }> };
// GET /api/watch/[videoId] - Public watch endpoint (no projectId needed)
export async function GET(request: NextRequest, { params }: RouteParams) {
try {
// Rate limit: 60 requests per minute per IP for public watch endpoint
const limited = await rateLimit(request, 'watch', { windowMs: 60 * 1000, maxRequests: 60 });
if (limited) return limited;
const session = await auth();
const { videoId } = await params;