diff --git a/src/core.ts b/src/core.ts index c69ec87..ca9d87d 100644 --- a/src/core.ts +++ b/src/core.ts @@ -1,3 +1,4 @@ +import { logToCF } from "./utils/httpClient"; import type { Platform, PlatformSearchResult, StreamProgress, StreamResult } from "./types"; import platformsGal from "./platforms/gal"; import platformsPatch from "./platforms/patch"; @@ -22,6 +23,11 @@ export async function handleSearchRequestStream( writer: WritableStreamDefaultWriter, zypassword: string = "" // 添加 zypassword 参数 ): Promise { + // 记录搜索关键词 + logToCF({ + message: `搜索关键词: ${game}`, + level: "info", + }); const encoder = new TextEncoder(); const total = platforms.length; let completed = 0; @@ -38,6 +44,13 @@ export async function handleSearchRequestStream( const progress: StreamProgress = { completed, total }; if (result.count > 0 || result.error) { + if (result.error) { + // 记录平台错误 + logToCF({ + message: `平台 ${result.name} 搜索错误: ${result.error}`, + level: "error", + }); + } const streamResult: StreamResult = { name: result.name, color: result.error ? 'red' : platform.color, @@ -53,6 +66,11 @@ export async function handleSearchRequestStream( completed++; // 记录平台内部的未知错误 console.error(`Error searching platform ${platform.name}:`, e); + // 记录平台内部的未知错误 + logToCF({ + message: `平台 ${platform.name} 内部错误: ${e instanceof Error ? e.message : String(e)}`, + level: "error", + }); const progress: StreamProgress = { completed, total }; await writer.write(encoder.encode(formatStreamEvent({ progress }))); } diff --git a/src/index.ts b/src/index.ts index 40948c8..0c7e187 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,5 @@ import { handleSearchRequestStream, PLATFORMS_GAL, PLATFORMS_PATCH } from "./core"; -import { checkRateLimit } from "./ratelimit"; - export interface Env { - RATE_LIMIT_KV: KVNamespace; } const corsHeaders = { @@ -17,20 +14,6 @@ async function handleSearch(request: Request, env: Env, ctx: ExecutionContext, p const game = formData.get("game") as string; const zypassword = formData.get("zypassword") as string || ""; // 获取 zypassword - if (env.RATE_LIMIT_KV) { - const ip = request.headers.get("CF-Connecting-IP") || "unknown"; - const { allowed, retryAfter } = await checkRateLimit(ip, env.RATE_LIMIT_KV); - - if (!allowed) { - return new Response( - JSON.stringify({ error: `搜索过于频繁, 请 ${retryAfter} 秒后再试` }), - { - status: 429, - headers: { "Content-Type": "application/json", ...corsHeaders }, - } - ); - } - } if (!game || typeof game !== 'string') { return new Response(JSON.stringify({ error: "Game name is required" }), { diff --git a/src/platforms/gal/ACGYingYingGuai.ts b/src/platforms/gal/ACGYingYingGuai.ts index de9b880..507e084 100644 --- a/src/platforms/gal/ACGYingYingGuai.ts +++ b/src/platforms/gal/ACGYingYingGuai.ts @@ -17,7 +17,7 @@ async function searchACGYingYingGuai(game: string): Promise { const response = await fetchClient(url); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const html = await response.text(); diff --git a/src/platforms/gal/DaoHeGal.ts b/src/platforms/gal/DaoHeGal.ts index d4f591c..37e6691 100644 --- a/src/platforms/gal/DaoHeGal.ts +++ b/src/platforms/gal/DaoHeGal.ts @@ -31,7 +31,7 @@ async function searchDaoHeGal(game: string): Promise { const response = await fetchClient(url); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const data = await response.json() as DaoHeGalResponse; diff --git a/src/platforms/gal/FuFuACG.ts b/src/platforms/gal/FuFuACG.ts index e6ab779..43f1d89 100644 --- a/src/platforms/gal/FuFuACG.ts +++ b/src/platforms/gal/FuFuACG.ts @@ -31,7 +31,7 @@ async function searchFuFuACG(game: string): Promise { }); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const data = await response.json() as FuFuACGResponse; diff --git a/src/platforms/gal/GGBases.ts b/src/platforms/gal/GGBases.ts index f13ee7f..232abaa 100644 --- a/src/platforms/gal/GGBases.ts +++ b/src/platforms/gal/GGBases.ts @@ -19,7 +19,7 @@ async function searchGGBases(game: string): Promise { const response = await fetchClient(url); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const html = await response.text(); diff --git a/src/platforms/gal/GalTuShuGuan.ts b/src/platforms/gal/GalTuShuGuan.ts index c48d902..5f60bb9 100644 --- a/src/platforms/gal/GalTuShuGuan.ts +++ b/src/platforms/gal/GalTuShuGuan.ts @@ -35,7 +35,7 @@ async function searchGalTuShuGuan(game: string): Promise { const response = await fetchClient(url); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const data = await response.json() as GalTuShuGuanResponse; diff --git a/src/platforms/gal/GalgameX.ts b/src/platforms/gal/GalgameX.ts index f402a35..a7e63cb 100644 --- a/src/platforms/gal/GalgameX.ts +++ b/src/platforms/gal/GalgameX.ts @@ -48,7 +48,7 @@ async function searchGalgameX(game: string): Promise { }); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const data = await response.json() as GalgameXResponse; diff --git a/src/platforms/gal/Hikarinagi.ts b/src/platforms/gal/Hikarinagi.ts index 8464e55..28f0b88 100644 --- a/src/platforms/gal/Hikarinagi.ts +++ b/src/platforms/gal/Hikarinagi.ts @@ -17,7 +17,7 @@ async function searchHikarinagi(game: string): Promise { const response = await fetchClient(url); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const html = await response.text(); diff --git a/src/platforms/gal/JiMengACG.ts b/src/platforms/gal/JiMengACG.ts index af8bd47..ef1c8a4 100644 --- a/src/platforms/gal/JiMengACG.ts +++ b/src/platforms/gal/JiMengACG.ts @@ -31,7 +31,7 @@ async function searchJiMengACG(game: string): Promise { const response = await fetchClient(url); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const data = await response.json() as JiMengACGResponse; diff --git a/src/platforms/gal/Koyso.ts b/src/platforms/gal/Koyso.ts index bc5a312..1ece96e 100644 --- a/src/platforms/gal/Koyso.ts +++ b/src/platforms/gal/Koyso.ts @@ -3,7 +3,7 @@ import type { Platform, PlatformSearchResult, SearchResultItem } from "../../typ const API_URL = "https://koyso.to/"; const BASE_URL = "https://koyso.to"; -const REGEX = /.*?(?.+?)<\/span>/gs; +const REGEX = /.*?(?.+?)<\/span>/gs; async function searchKoyso(game: string): Promise { const searchResult: PlatformSearchResult = { @@ -18,12 +18,12 @@ async function searchKoyso(game: string): Promise { const response = await fetchClient(url); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const html = await response.text(); // --- DEBUGGING: Print the full HTML content --- - // console.log("Koyso API HTML Response:", html.substring(0, 1000)); // Print first 1000 chars + console.log("Koyso API HTML Response:", html); // --- END DEBUGGING --- const matches = html.matchAll(REGEX); @@ -33,7 +33,7 @@ async function searchKoyso(game: string): Promise { if (match.groups?.NAME && match.groups?.URL) { items.push({ name: match.groups.NAME.trim(), - url: BASE_URL + encodeURIComponent(match.groups.URL), + url: BASE_URL + match.groups.URL, }); } } diff --git a/src/platforms/gal/KunGalgame.ts b/src/platforms/gal/KunGalgame.ts index 225b783..f2e108d 100644 --- a/src/platforms/gal/KunGalgame.ts +++ b/src/platforms/gal/KunGalgame.ts @@ -28,7 +28,7 @@ async function searchKunGalgame(game: string): Promise { const response = await fetchClient(url); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const data = await response.json() as KunGalgameItem[]; diff --git a/src/platforms/gal/LiangZiACG.ts b/src/platforms/gal/LiangZiACG.ts index b8c8e00..234574c 100644 --- a/src/platforms/gal/LiangZiACG.ts +++ b/src/platforms/gal/LiangZiACG.ts @@ -17,7 +17,7 @@ async function searchLiangZiACG(game: string): Promise { const response = await fetchClient(url); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const html = await response.text(); diff --git a/src/platforms/gal/MaoMaoWangPan.ts b/src/platforms/gal/MaoMaoWangPan.ts index 6b0490a..9956cf2 100644 --- a/src/platforms/gal/MaoMaoWangPan.ts +++ b/src/platforms/gal/MaoMaoWangPan.ts @@ -43,13 +43,13 @@ async function searchMaoMaoWangPan(game: string): Promise }); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const data = await response.json() as MaoMaoResponse; if (data.message !== "success") { - throw new Error(`API returned an error: ${data.message}`); + throw new Error(`${data.message}`); } const items: SearchResultItem[] = data.data.content diff --git a/src/platforms/gal/MiaoYuanLingYu.ts b/src/platforms/gal/MiaoYuanLingYu.ts index 3504a2f..f5ff9b3 100644 --- a/src/platforms/gal/MiaoYuanLingYu.ts +++ b/src/platforms/gal/MiaoYuanLingYu.ts @@ -18,7 +18,7 @@ async function searchMiaoYuanLingYu(game: string): Promise const response = await fetchClient(url); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const html = await response.text(); diff --git a/src/platforms/gal/Nysoure.ts b/src/platforms/gal/Nysoure.ts index 0cf80ba..1b7dd5a 100644 --- a/src/platforms/gal/Nysoure.ts +++ b/src/platforms/gal/Nysoure.ts @@ -28,7 +28,7 @@ async function searchNysoure(game: string): Promise { const response = await fetchClient(url); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const data = await response.json() as NysoureResponse; diff --git a/src/platforms/gal/QingJiACG.ts b/src/platforms/gal/QingJiACG.ts index 2ec160c..106c31b 100644 --- a/src/platforms/gal/QingJiACG.ts +++ b/src/platforms/gal/QingJiACG.ts @@ -18,7 +18,7 @@ async function searchQingJiACG(game: string): Promise { const response = await fetchClient(url); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const html = await response.text(); diff --git a/src/platforms/gal/ShenShiTianTang.ts b/src/platforms/gal/ShenShiTianTang.ts index 780f944..e7a5a57 100644 --- a/src/platforms/gal/ShenShiTianTang.ts +++ b/src/platforms/gal/ShenShiTianTang.ts @@ -17,7 +17,7 @@ async function searchShenShiTianTang(game: string): Promise { }); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const data = await response.json() as { galgames: { name: string; uniqueId: string }[] }; diff --git a/src/platforms/gal/VikaACG.ts b/src/platforms/gal/VikaACG.ts index b67c674..f9e3ce4 100644 --- a/src/platforms/gal/VikaACG.ts +++ b/src/platforms/gal/VikaACG.ts @@ -39,7 +39,7 @@ async function searchVikaACG(game: string): Promise { }); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const rawText = await response.text(); diff --git a/src/platforms/gal/WeiZhiYunPan.ts b/src/platforms/gal/WeiZhiYunPan.ts index 04d6b23..3d3f4a2 100644 --- a/src/platforms/gal/WeiZhiYunPan.ts +++ b/src/platforms/gal/WeiZhiYunPan.ts @@ -43,13 +43,13 @@ async function searchWeiZhiYunPan(game: string): Promise { }); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const data = await response.json() as WeiZhiYunPanResponse; if (data.message !== "success") { - throw new Error(`API returned an error: ${data.message}`); + throw new Error(`${data.message}`); } const items: SearchResultItem[] = data.data.content.map(item => ({ diff --git a/src/platforms/gal/YouYuDeloli.ts b/src/platforms/gal/YouYuDeloli.ts index c6e2e52..ca8da51 100644 --- a/src/platforms/gal/YouYuDeloli.ts +++ b/src/platforms/gal/YouYuDeloli.ts @@ -17,7 +17,7 @@ async function searchYouYuDeloli(game: string): Promise { const response = await fetchClient(url); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const html = await response.text(); diff --git a/src/platforms/gal/ZeroFive.ts b/src/platforms/gal/ZeroFive.ts index dda6cbe..1917f5c 100644 --- a/src/platforms/gal/ZeroFive.ts +++ b/src/platforms/gal/ZeroFive.ts @@ -43,13 +43,13 @@ async function searchZeroFive(game: string): Promise { }); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const data = await response.json() as ZeroFiveResponse; if (data.message !== "success") { - throw new Error(`API returned an error: ${data.message}`); + throw new Error(`${data.message}`); } const items: SearchResultItem[] = data.data.content.map(item => ({ diff --git a/src/platforms/gal/ZhenHongXiaoZhan.ts b/src/platforms/gal/ZhenHongXiaoZhan.ts index a19e7e9..779ea10 100644 --- a/src/platforms/gal/ZhenHongXiaoZhan.ts +++ b/src/platforms/gal/ZhenHongXiaoZhan.ts @@ -18,7 +18,7 @@ async function searchZhenHongXiaoZhan(game: string): Promise ({ diff --git a/src/platforms/gal/ZiYuanShe.ts b/src/platforms/gal/ZiYuanShe.ts index 53b8c92..0f7fe30 100644 --- a/src/platforms/gal/ZiYuanShe.ts +++ b/src/platforms/gal/ZiYuanShe.ts @@ -43,13 +43,17 @@ async function searchZiYuanShe(game: string, zypassword: string = ""): Promise

({ diff --git a/src/platforms/gal/xxacg.ts b/src/platforms/gal/xxacg.ts index 1ed1ce8..c5f2314 100644 --- a/src/platforms/gal/xxacg.ts +++ b/src/platforms/gal/xxacg.ts @@ -22,7 +22,7 @@ async function searchXxacg(game: string): Promise { const response = await fetchClient(url); if (!response.ok) { - throw new Error(`Search API response status code is ${response.status}`); + throw new Error(`Search 资源平台 SearchAPI 响应异常状态码 ${response.status}`); } html = await response.text(); diff --git a/src/platforms/patch/KunGalgameBuDing.ts b/src/platforms/patch/KunGalgameBuDing.ts index e04c788..1bd3532 100644 --- a/src/platforms/patch/KunGalgameBuDing.ts +++ b/src/platforms/patch/KunGalgameBuDing.ts @@ -41,7 +41,7 @@ async function searchKunGalgameBuDing(game: string): Promise { const response = await fetchClient(url); if (!response.ok) { - throw new Error(`API response status code is ${response.status}`); + throw new Error(`资源平台 SearchAPI 响应异常状态码 ${response.status}`); } const data = await response.json() as TwoDFanResponse; diff --git a/src/ratelimit.ts b/src/ratelimit.ts deleted file mode 100644 index 7220b9c..0000000 --- a/src/ratelimit.ts +++ /dev/null @@ -1,36 +0,0 @@ -// --- 速率限制常量 --- -const SEARCH_INTERVAL_SECONDS = 15; -// KV 条目将在此秒数后自动过期,以防止存储膨胀 -const IP_ENTRY_TTL_SECONDS = 60; - -/** - * 检查给定 IP 地址是否超出了速率限制。 - * @param ip 客户端的 IP 地址。 - * @param kvNamespace 用于存储 IP 时间戳的 KV 命名空间。 - * @returns 返回一个对象,包含是否允许请求以及剩余的等待秒数。 - */ -export async function checkRateLimit( - ip: string, - kvNamespace: KVNamespace -): Promise<{ allowed: boolean; retryAfter: number }> { - const currentTime = Math.floor(Date.now() / 1000); - const lastSearchTimeStr = await kvNamespace.get(ip); - const lastSearchTime = lastSearchTimeStr ? parseInt(lastSearchTimeStr, 10) : 0; - - if (lastSearchTime && (currentTime - lastSearchTime) < SEARCH_INTERVAL_SECONDS) { - return { - allowed: false, - retryAfter: SEARCH_INTERVAL_SECONDS - (currentTime - lastSearchTime), - }; - } - - // 更新 IP 的最后搜索时间,并设置 TTL - await kvNamespace.put(ip, currentTime.toString(), { - expirationTtl: IP_ENTRY_TTL_SECONDS, - }); - - return { - allowed: true, - retryAfter: 0, - }; -} \ No newline at end of file diff --git a/src/utils/httpClient.ts b/src/utils/httpClient.ts index 3f97360..7a5c81e 100644 --- a/src/utils/httpClient.ts +++ b/src/utils/httpClient.ts @@ -32,10 +32,27 @@ export async function fetchClient( return response; } catch (error) { if (error instanceof Error && error.name === 'AbortError') { - throw new Error(`Request timed out after ${TIMEOUT_SECONDS} seconds`); + throw new Error(`资源平台 SearchAPI 请求超时`); } throw error; } finally { clearTimeout(timeoutId); } +} +/** + * 向 Cloudflare 发送日志。 + * @param data 要记录的数据对象。 + */ +export async function logToCF(data: object) { + try { + await fetch("https://log.gal.homes", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + } catch (error) { + console.error("Failed to log to Cloudflare:", error); + } } \ No newline at end of file diff --git a/wrangler.toml b/wrangler.toml index 2149a8b..adc956e 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -2,10 +2,5 @@ name = "searchgal-worker" main = "src/index.ts" compatibility_date = "2023-10-30" -[vars] -# 如果有需要,可以在这里添加环境变量 - -# --- KV 命名空间绑定 --- -[[kv_namespaces]] -binding = "RATE_LIMIT_KV" -id = "7ed4c4f36baf419bb9ed54538a61f473" +[observability.logs] +enabled = true