misc
This commit is contained in:
@@ -1,24 +1,12 @@
|
||||
const rateLimitMap = new Map();
|
||||
|
||||
function checkRateLimit(userId, limit = 5, windowMs = 1000) {
|
||||
const now = Date.now();
|
||||
const key = userId;
|
||||
const entry = rateLimitMap.get(key);
|
||||
|
||||
if (!entry || now > entry.resetTime) {
|
||||
rateLimitMap.set(key, { count: 1, resetTime: now + windowMs });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (entry.count >= limit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
entry.count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
import { getSubsiteByHost } from '../../utils/subsites.js';
|
||||
import {
|
||||
checkRateLimit,
|
||||
recordRequest,
|
||||
getCookieId,
|
||||
generateNonce,
|
||||
createNonceCookie,
|
||||
getClientIp,
|
||||
} from '../../utils/rateLimit.js';
|
||||
|
||||
export async function GET({ request }) {
|
||||
const host = request.headers.get('host');
|
||||
@@ -28,27 +16,51 @@ export async function GET({ request }) {
|
||||
return new Response('Not found', { status: 404 });
|
||||
}
|
||||
|
||||
let userId = request.headers.get('cookie')?.split(';').find(c => c.trim().startsWith('nonce='))?.split('=')[1];
|
||||
const hadCookie = !!userId;
|
||||
if (!userId) {
|
||||
userId = crypto.randomUUID();
|
||||
// Rate limit check (5 requests per second, flood protection at 30/10s)
|
||||
const rateCheck = checkRateLimit(request, {
|
||||
limit: 5,
|
||||
windowMs: 1000,
|
||||
burstLimit: 30,
|
||||
burstWindowMs: 10_000,
|
||||
});
|
||||
|
||||
let cookieId = getCookieId(request);
|
||||
const hadCookie = !!cookieId;
|
||||
if (!cookieId) {
|
||||
cookieId = generateNonce();
|
||||
}
|
||||
|
||||
if (!checkRateLimit(userId)) {
|
||||
const response = new Response(JSON.stringify({ error: 'Rate limit exceeded' }), { status: 429, headers: { 'Content-Type': 'application/json' } });
|
||||
if (!rateCheck.allowed) {
|
||||
const errorMsg = rateCheck.isFlooding
|
||||
? { error: 'Too many requests - please slow down' }
|
||||
: { error: 'Rate limit exceeded' };
|
||||
const response = new Response(JSON.stringify(errorMsg), {
|
||||
status: 429,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Retry-After': '1',
|
||||
},
|
||||
});
|
||||
if (!hadCookie) {
|
||||
response.headers.set('Set-Cookie', `nonce=${userId}; HttpOnly; Path=/; Max-Age=31536000`);
|
||||
response.headers.set('Set-Cookie', createNonceCookie(cookieId));
|
||||
}
|
||||
console.log(`[search] rate limited: ip=${rateCheck.ip} flooding=${rateCheck.isFlooding}`);
|
||||
return response;
|
||||
}
|
||||
|
||||
// Record the request for rate limiting
|
||||
recordRequest(request, 1000);
|
||||
|
||||
const TMDB_API_KEY = import.meta.env.TMDB_API_KEY;
|
||||
|
||||
if (!TMDB_API_KEY) {
|
||||
console.error('TMDB_API_KEY not set');
|
||||
const response = new Response(JSON.stringify([]), { status: 500, headers: { 'Content-Type': 'application/json' } });
|
||||
const response = new Response(JSON.stringify([]), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
if (!hadCookie) {
|
||||
response.headers.set('Set-Cookie', `nonce=${userId}; HttpOnly; Path=/; Max-Age=31536000`);
|
||||
response.headers.set('Set-Cookie', createNonceCookie(cookieId));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
@@ -57,17 +69,23 @@ export async function GET({ request }) {
|
||||
const q = url.searchParams.get('q');
|
||||
|
||||
if (!q || typeof q !== 'string' || !q.trim()) {
|
||||
const response = new Response(JSON.stringify([]), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
||||
const response = new Response(JSON.stringify([]), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
if (!hadCookie) {
|
||||
response.headers.set('Set-Cookie', `nonce=${userId}; HttpOnly; Path=/; Max-Age=31536000`);
|
||||
response.headers.set('Set-Cookie', createNonceCookie(cookieId));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
if (q.length > 100) {
|
||||
const response = new Response(JSON.stringify([]), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
||||
const response = new Response(JSON.stringify([]), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
if (!hadCookie) {
|
||||
response.headers.set('Set-Cookie', `nonce=${userId}; HttpOnly; Path=/; Max-Age=31536000`);
|
||||
response.headers.set('Set-Cookie', createNonceCookie(cookieId));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
@@ -96,16 +114,21 @@ export async function GET({ request }) {
|
||||
overview: item.overview,
|
||||
poster_path: item.poster_path,
|
||||
}));
|
||||
const response = new Response(JSON.stringify(filtered), { headers: { 'Content-Type': 'application/json' } });
|
||||
const response = new Response(JSON.stringify(filtered), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
if (!hadCookie) {
|
||||
response.headers.set('Set-Cookie', `nonce=${userId}; HttpOnly; Path=/; Max-Age=31536000`);
|
||||
response.headers.set('Set-Cookie', createNonceCookie(cookieId));
|
||||
}
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('Error fetching suggestions:', error);
|
||||
const response = new Response(JSON.stringify([]), { status: 500, headers: { 'Content-Type': 'application/json' } });
|
||||
const response = new Response(JSON.stringify([]), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
if (!hadCookie) {
|
||||
response.headers.set('Set-Cookie', `nonce=${userId}; HttpOnly; Path=/; Max-Age=31536000`);
|
||||
response.headers.set('Set-Cookie', createNonceCookie(cookieId));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user