chore: clean up lint/type issues and tighten comment media URL validation

This commit is contained in:
Yusuf İpek
2026-02-23 16:06:43 +03:00
parent 609b33bbcb
commit d15e5192ac
19 changed files with 61 additions and 86 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ import { NextRequest } from 'next/server';
import { db } from '@/lib/db';
import bcrypt from 'bcryptjs';
import { checkRateLimit, getClientIp, rateLimitHeaders, RATE_LIMIT_CONFIGS } from '@/lib/rate-limit';
import { apiErrors, successResponse, ErrorCode, withCacheControl } from '@/lib/api-response';
import { apiErrors, successResponse, withCacheControl } from '@/lib/api-response';
export async function POST(request: NextRequest) {
try {
+2 -1
View File
@@ -107,7 +107,8 @@ export async function GET(request: NextRequest, { params }: RouteParams) {
}
// Strip internal project data from response
const { version: _version, ...commentData } = comment;
const commentData = { ...comment } as Omit<typeof comment, 'version'> & { version?: unknown };
delete commentData.version;
const response = successResponse(commentData);
return withCacheControl(response, 'private, no-cache');
} catch (error) {
+6 -12
View File
@@ -1,12 +1,13 @@
import { NextRequest } from 'next/server';
import { db } from '@/lib/db';
import { auth } from '@/lib/auth';
import { validateOptionalUrl } from '@/lib/validation';
import { rateLimit } from '@/lib/rate-limit';
import { notifyProjectOwner } from '@/lib/notifications';
import { apiErrors, successResponse, withCacheControl } from '@/lib/api-response';
type RouteParams = { params: Promise<{ versionId: string }> };
const SAFE_IMAGE_PATH = /^\/api\/upload\/image\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.[a-z0-9]+$/i;
const SAFE_AUDIO_PATH = /^\/api\/upload\/audio\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.[a-z0-9]+$/i;
// GET /api/versions/[versionId]/comments
export async function GET(request: NextRequest, { params }: RouteParams) {
@@ -218,19 +219,12 @@ export async function POST(request: NextRequest, { params }: RouteParams) {
return apiErrors.badRequest('Guest name is required for guest comments');
}
// Validate voice URL uses safe scheme (allow internal /api/ paths)
if (voiceUrl && !voiceUrl.startsWith('/api/')) {
const voiceUrlError = validateOptionalUrl(voiceUrl, 'Voice URL');
if (voiceUrlError) {
return apiErrors.badRequest(voiceUrlError);
}
if (voiceUrl && !SAFE_AUDIO_PATH.test(voiceUrl)) {
return apiErrors.badRequest('Voice URL must reference an uploaded audio file');
}
if (imageUrl && !imageUrl.startsWith('/api/')) {
const imageUrlError = validateOptionalUrl(imageUrl, 'Image URL');
if (imageUrlError) {
return apiErrors.badRequest(imageUrlError);
}
if (imageUrl && !SAFE_IMAGE_PATH.test(imageUrl)) {
return apiErrors.badRequest('Image URL must reference an uploaded image file');
}
const comment = await db.comment.create({