refactor: add SubNav layout and per-subsite nav placeholders; switch Base to use SubNav
This commit is contained in:
112
src/pages/api/search.js
Normal file
112
src/pages/api/search.js
Normal file
@@ -0,0 +1,112 @@
|
||||
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';
|
||||
|
||||
export async function GET({ request }) {
|
||||
const host = request.headers.get('host');
|
||||
const subsite = getSubsiteByHost(host);
|
||||
|
||||
if (!subsite || subsite.short !== 'req') {
|
||||
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();
|
||||
}
|
||||
|
||||
if (!checkRateLimit(userId)) {
|
||||
const response = new Response(JSON.stringify({ error: 'Rate limit exceeded' }), { status: 429, headers: { 'Content-Type': 'application/json' } });
|
||||
if (!hadCookie) {
|
||||
response.headers.set('Set-Cookie', `nonce=${userId}; HttpOnly; Path=/; Max-Age=31536000`);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
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' } });
|
||||
if (!hadCookie) {
|
||||
response.headers.set('Set-Cookie', `nonce=${userId}; HttpOnly; Path=/; Max-Age=31536000`);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
const url = new URL(request.url);
|
||||
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' } });
|
||||
if (!hadCookie) {
|
||||
response.headers.set('Set-Cookie', `nonce=${userId}; HttpOnly; Path=/; Max-Age=31536000`);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
if (q.length > 100) {
|
||||
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`);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
try {
|
||||
const apiResponse = await fetch(`https://api.themoviedb.org/3/search/multi?api_key=${TMDB_API_KEY}&query=${encodeURIComponent(q)}`);
|
||||
if (!apiResponse.ok) {
|
||||
throw new Error(`TMDB API error: ${apiResponse.status}`);
|
||||
}
|
||||
const data = await apiResponse.json();
|
||||
const seen = new Set();
|
||||
const filtered = data.results
|
||||
.filter(item => {
|
||||
if (item.media_type !== 'movie' && item.media_type !== 'tv') return false;
|
||||
const key = `${item.media_type}-${item.title || item.name}-${item.release_date?.split('-')[0] || item.first_air_date?.split('-')[0] || ''}`;
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
})
|
||||
.slice(0, 10) // Limit to 10 suggestions
|
||||
.map(item => ({
|
||||
label: item.title || item.name,
|
||||
value: item.title || item.name,
|
||||
year: item.release_date?.split('-')[0] || item.first_air_date?.split('-')[0],
|
||||
mediaType: item.media_type,
|
||||
overview: item.overview,
|
||||
poster_path: item.poster_path,
|
||||
}));
|
||||
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`);
|
||||
}
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('Error fetching suggestions:', error);
|
||||
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`);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user