diff --git a/src/app/api/search/route.ts b/src/app/api/search/route.ts index d5b725d..ec4d4de 100644 --- a/src/app/api/search/route.ts +++ b/src/app/api/search/route.ts @@ -1,8 +1,7 @@ import { NextResponse } from 'next/server'; -import { getAdultApiSites, getAvailableApiSites, getCacheTime } from '@/lib/config'; +import { getCacheTime, getFilteredApiSites } from '@/lib/config'; import { addCorsHeaders, handleOptionsRequest } from '@/lib/cors'; -import { getStorage } from '@/lib/db'; import { searchFromApi } from '@/lib/downstream'; export const runtime = 'edge'; @@ -15,8 +14,15 @@ export async function OPTIONS() { export async function GET(request: Request) { const { searchParams } = new URL(request.url); const query = searchParams.get('q'); - const includeAdult = searchParams.get('include_adult') === 'true'; - const userName = searchParams.get('user'); // 用于获取用户设置 + + // 从 Authorization header 或 query parameter 获取用户名 + let userName: string | undefined = searchParams.get('user') || undefined; + if (!userName) { + const authHeader = request.headers.get('Authorization'); + if (authHeader && authHeader.startsWith('Bearer ')) { + userName = authHeader.substring(7); + } + } if (!query) { const cacheTime = await getCacheTime(); @@ -37,51 +43,34 @@ export async function GET(request: Request) { } try { - // 获取用户设置以确定是否需要过滤成人内容 - let shouldFilterAdult = true; // 默认过滤成人内容 + // 使用新的动态过滤方法,根据用户设置自动过滤成人内容源 + const availableSites = await getFilteredApiSites(userName); - if (userName) { - const storage = getStorage(); - const userSettings = await storage.getUserSettings(userName); - shouldFilterAdult = userSettings?.filter_adult_content !== false; + if (!availableSites || availableSites.length === 0) { + const cacheTime = await getCacheTime(); + const response = NextResponse.json({ + regular_results: [], + adult_results: [] + }, { + headers: { + 'Cache-Control': `public, max-age=${cacheTime}, s-maxage=${cacheTime}`, + 'CDN-Cache-Control': `public, s-maxage=${cacheTime}`, + 'Vercel-CDN-Cache-Control': `public, s-maxage=${cacheTime}`, + }, + }); + return addCorsHeaders(response); } - // 获取所有可用的API站点(不包含成人内容) - const regularSites = await getAvailableApiSites(); - - if (!regularSites || regularSites.length === 0) { - const cacheTime = await getCacheTime(); - const response = NextResponse.json({ - regular_results: [], - adult_results: [] - }, { - headers: { - 'Cache-Control': `public, max-age=${cacheTime}, s-maxage=${cacheTime}`, - 'CDN-Cache-Control': `public, s-maxage=${cacheTime}`, - 'Vercel-CDN-Cache-Control': `public, s-maxage=${cacheTime}`, - }, - }); - return addCorsHeaders(response); - } - - // 搜索常规(非成人)内容 - const regularSearchPromises = regularSites.map((site) => searchFromApi(site, query)); - const regularResults = (await Promise.all(regularSearchPromises)).flat(); - - let adultResults: unknown[] = []; - - // 如果用户设置允许且明确请求包含成人内容,则搜索成人资源站 - if (!shouldFilterAdult && includeAdult) { - const adultSites = await getAdultApiSites(); - const adultSearchPromises = adultSites.map((site) => searchFromApi(site, query)); - adultResults = (await Promise.all(adultSearchPromises)).flat(); - } + // 搜索所有可用的资源站(已根据用户设置动态过滤) + const searchPromises = availableSites.map((site) => searchFromApi(site, query)); + const searchResults = (await Promise.all(searchPromises)).flat(); + // 所有结果都作为常规结果返回,因为成人内容源已经在源头被过滤掉了 const cacheTime = await getCacheTime(); const response = NextResponse.json( { - regular_results: regularResults, - adult_results: adultResults + regular_results: searchResults, + adult_results: [] // 始终为空,因为成人内容在源头就被过滤了 }, { headers: { diff --git a/src/app/search/page.tsx b/src/app/search/page.tsx index b4e8395..281df4f 100644 --- a/src/app/search/page.tsx +++ b/src/app/search/page.tsx @@ -179,8 +179,9 @@ function SearchPageClient() { headers['Authorization'] = `Bearer ${authInfo.username}`; } + // 简化的搜索请求 - 成人内容过滤现在在API层面自动处理 const response = await fetch( - `/api/search?q=${encodeURIComponent(query.trim())}`, + `/api/search?q=${encodeURIComponent(query.trim())}`, { headers } ); const data = await response.json(); diff --git a/src/lib/config.ts b/src/lib/config.ts index 130233c..b3e33ad 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -1,8 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any, no-console, @typescript-eslint/no-non-null-assertion */ -import { getStorage } from '@/lib/db'; - import { AdminConfig } from './admin.types'; +import { getStorage } from './db'; import runtimeConfig from './runtime'; export interface ApiSite { @@ -423,6 +422,52 @@ export async function getAvailableApiSites(filterAdult = false): Promise { + const config = await getConfig(); + + // 防御性检查:确保 SourceConfig 存在且为数组 + if (!config.SourceConfig || !Array.isArray(config.SourceConfig)) { + console.warn('SourceConfig is missing or not an array, returning empty array'); + return []; + } + + // 默认过滤成人内容 + let shouldFilterAdult = true; + + // 如果提供了用户名,获取用户设置 + if (userName) { + try { + const storage = getStorage(); + const userSettings = await storage.getUserSettings(userName); + shouldFilterAdult = userSettings?.filter_adult_content !== false; // 默认为 true + } catch (error) { + // 获取用户设置失败时,默认过滤成人内容 + console.warn('Failed to get user settings, using default filter:', error); + } + } + + // 防御性处理:为每个源确保 is_adult 字段存在 + let sites = config.SourceConfig + .filter((s) => !s.disabled) + .map((s) => ({ + ...s, + is_adult: s.is_adult === true // 严格检查,只有明确为 true 的才是成人内容 + })); + + // 根据用户设置动态过滤成人内容源 + if (shouldFilterAdult) { + sites = sites.filter((s) => !s.is_adult); + } + + return sites.map((s) => ({ + key: s.key, + name: s.name, + api: s.api, + detail: s.detail, + })); +} + // 获取成人内容资源站 export async function getAdultApiSites(): Promise { const config = await getConfig();