From 9e24d85cd0929359d02220557cb07e898b8d4c7b Mon Sep 17 00:00:00 2001 From: shumengya Date: Wed, 24 Jun 2026 22:10:22 +0800 Subject: [PATCH] chore: sync local changes to Gitea --- README.md | 40 ++++++-------- README.zh.md | 42 ++++++--------- envs.js | 8 +-- index.d.ts | 4 +- mcp/cf-search-mcp.js | 8 +-- package.json | 2 - screenshot.png | Bin 49782 -> 0 bytes utils/dev.js | 2 - utils/getHTML.js | 118 +++++------------------------------------- utils/searchBrave.js | 70 ------------------------- utils/searchGoogle.js | 34 ------------ worker.js | 50 ++++++++++-------- wrangler.toml | 34 +----------- 13 files changed, 81 insertions(+), 331 deletions(-) delete mode 100644 screenshot.png delete mode 100644 utils/searchBrave.js delete mode 100644 utils/searchGoogle.js diff --git a/README.md b/README.md index 28f69ed..22c7277 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ English | [中文](./README.zh.md) ## Features -- 🔍 **Multi-engine Aggregation** - Use multiple search engines at the same time (Google, Brave, DuckDuckGo, Bing) +- 🔍 **Multi-engine Aggregation** - Use DuckDuckGo and Bing at the same time - 🤖 **AI Enhanced (MCP)** - Native support for Model Context Protocol, one-click search tool integration for **OpenClaw** / **Claude Code** / **Codex** - ⚡ **Parallel Search** - All search engines are requested concurrently for faster results - 🛡️ **Fault Tolerance** - Failure of a single engine does not affect others; unresponsive engines are automatically marked @@ -132,7 +132,7 @@ Search using query parameters: curl "https://$YOUR-DOMAIN/search?q=cloudflare" # Specify search engines -curl "https://$YOUR-DOMAIN/search?q=cloudflare&engines=google,brave" +curl "https://$YOUR-DOMAIN/search?q=cloudflare&engines=duckduckgo,bing" # Use token authentication (if TOKEN env var is configured) curl "https://$YOUR-DOMAIN/search?q=cloudflare&token=$YOUR-TOKEN" @@ -145,7 +145,7 @@ Submit search by POST form: ```bash curl -X POST "https://$YOUR-DOMAIN/search" \ -d "q=cloudflare" \ - -d "engines=google,brave" + -d "engines=duckduckgo,bing" -d "token=$YOUR-TOKEN" # if TOKEN env var is configured ``` @@ -160,13 +160,11 @@ Used to execute search queries and return aggregated results. | Parameter | Type | Required | Description | Example | | ------------- | -------- | -------- | ---------------------------------------------------------- | ---------------- | | `q` / `query` | `string` | yes | Search keyword | `cloudflare` | -| `engines` | `string` | no | Specify search engines, separated by commas | `google,brave` | +| `engines` | `string` | no | Specify search engines, separated by commas | `duckduckgo,bing` | | `token` | `string` | no/yes | Access token (required when `TOKEN` env var is configured) | `$YOUR-TOKEN` | **Supported Search Engines**: -- `google` - Google Search (requires API Key configuration) -- `brave` - Brave Search - `duckduckgo` - DuckDuckGo Search - `bing` - Bing Search @@ -178,6 +176,8 @@ Used to execute search queries and return aggregated results. number_of_results: number; // Total number of results enabled_engines: string[]; // Enabled search engine list unresponsive_engines: string[]; // Unresponsive search engine list + empty_engines: string[]; // Engines that responded with no results + engine_errors: Record; // Error message by engine results: Array<{ title: string; // Result title description: string; // Result description @@ -191,12 +191,12 @@ Used to execute search queries and return aggregated results. ```bash # GET request -curl "https://$YOUR-DOMAIN/search?q=cloudflare&engines=google,brave" +curl "https://$YOUR-DOMAIN/search?q=cloudflare&engines=duckduckgo,bing" # POST request curl -X POST "https://$YOUR-DOMAIN/search" \ -H "Content-Type: application/x-www-form-urlencoded" \ - -d "q=cloudflare&engines=google,brave" + -d "q=cloudflare&engines=duckduckgo,bing" ``` #### Response Example @@ -205,20 +205,22 @@ curl -X POST "https://$YOUR-DOMAIN/search" \ { "query": "cloudflare", "number_of_results": 15, - "enabled_engines": ["google", "brave", "duckduckgo"], + "enabled_engines": ["duckduckgo", "bing"], "unresponsive_engines": [], + "empty_engines": [], + "engine_errors": {}, "results": [ { "title": "Cloudflare - The Web Performance & Security Company", "description": "Cloudflare is on a mission to help build a better Internet...", "url": "https://www.cloudflare.com/", - "engine": "google" + "engine": "duckduckgo" }, { "title": "Cloudflare Workers", "description": "Deploy serverless code instantly across the globe...", "url": "https://workers.cloudflare.com/", - "engine": "brave" + "engine": "bing" } ] } @@ -230,9 +232,7 @@ curl -X POST "https://$YOUR-DOMAIN/search" \ | Engine | Description | Configuration Required | Enabled by Default | | -------------- | ---------------------------- | ------------------------------- | ------------------ | -| **Google** | Google Custom Search API | Requires `GOOGLE_API_KEY` and `GOOGLE_CX` | yes | -| **Brave** | Brave Search API | - | yes | -| **DuckDuckGo** | DuckDuckGo Instant Answer API | - | yes | +| **DuckDuckGo** | DuckDuckGo HTML search | - | yes | | **Bing** | Bing Search | - | no (unstable results) | ### Basic Working Approach @@ -249,13 +249,10 @@ curl -X POST "https://$YOUR-DOMAIN/search" \ | Variable Name | Type | Default | Description | | ------------------ | -------- | -------- | --------------------------------------------------- | | `DEFAULT_TIMEOUT` | `string` | `"3000"` | Timeout per search engine request (milliseconds) | -| `GOOGLE_API_KEY` | `string` | `null` | https://console.cloud.google.com/apis/credentials | -| `GOOGLE_CX` | `string` | `null` | https://programmablesearchengine.google.com/ | | `TOKEN` | `string` | `null` | Access token. Enables auth when configured to prevent abuse | **Notes**: -- Google Custom Search API free tier is limited to 100 requests per day - After `TOKEN` is configured, all requests must provide a valid token ### Configuration Methods @@ -266,8 +263,6 @@ Edit the `[vars]` section in `wrangler.toml`: ```toml [vars] -GOOGLE_API_KEY = "your-google-api-key" -GOOGLE_CX = "your-google-custom-search-cx" DEFAULT_TIMEOUT = "3000" TOKEN = "your-secret-token-here" ``` @@ -286,7 +281,7 @@ Build your own aggregated search API and combine results from multiple search en ```javascript const response = await fetch( - "https://$YOUR-DOMAIN/search?q=javascript&engines=google,brave", + "https://$YOUR-DOMAIN/search?q=javascript&engines=duckduckgo,bing", ); const data = await response.json(); console.log(`Found ${data.number_of_results} results`); @@ -311,7 +306,7 @@ async function search(query) { Collect results from multiple search engines for comparative analysis: ```javascript -const engines = ["google", "brave", "duckduckgo"]; +const engines = ["duckduckgo", "bing"]; const results = await fetch( `https://$YOUR-DOMAIN/search?q=AI&engines=${engines.join(",")}`, ); @@ -339,7 +334,6 @@ With MCP (Model Context Protocol), AI assistants can directly call your search s - In Worker settings, click **Triggers** > **Add Custom Domain** to add a custom domain 2. **Search Engine Limits** - - Google API free tier is limited to 100 requests per day - Other search engines generally do not have strict limits, but please use responsibly - Frequent requests may cause temporary rate limiting @@ -380,7 +374,6 @@ A: Possible reasons: - Search engine API is temporarily unavailable or timed out - No relevant results for the search keyword - Search engine has rate-limited access -- Google requires API Key configuration before use You can check the `unresponsive_engines` field in the response to see which engines did not respond. @@ -427,7 +420,6 @@ Issues and Pull Requests are welcome! - [Project GitHub](https://github.com/Yrobot/cloudflare-search) - [Cloudflare Workers Docs](https://developers.cloudflare.com/workers/) -- [Google Custom Search API](https://developers.google.com/custom-search/v1/overview) ## Support the Project diff --git a/README.zh.md b/README.zh.md index d1195ea..b172f7c 100644 --- a/README.zh.md +++ b/README.zh.md @@ -10,7 +10,7 @@ ## 特性 -- 🔍 **多引擎聚合** - 同时使用多个搜索引擎(Google、Brave、DuckDuckGo、Bing) +- 🔍 **多引擎聚合** - 同时使用 DuckDuckGo 和 Bing 搜索引擎 - 🤖 **AI 增强 (MCP)** - 原生支持 Model Context Protocol,一键为 **OpenClaw** / **Claude Code** / **Codex** 添加搜索工具 - ⚡ **并行搜索** - 所有搜索引擎同时请求,快速返回结果 - 🛡️ **容错机制** - 单个引擎失败不影响其他引擎,自动标记无响应引擎 @@ -134,7 +134,7 @@ https://$YOUR-DOMAIN/ curl "https://$YOUR-DOMAIN/search?q=cloudflare" # 指定搜索引擎 -curl "https://$YOUR-DOMAIN/search?q=cloudflare&engines=google,brave" +curl "https://$YOUR-DOMAIN/search?q=cloudflare&engines=duckduckgo,bing" # 使用 token 鉴权(如果配置了 TOKEN 环境变量) curl "https://$YOUR-DOMAIN/search?q=cloudflare&token=$YOUR-TOKEN" @@ -147,7 +147,7 @@ curl "https://$YOUR-DOMAIN/search?q=cloudflare&token=$YOUR-TOKEN" ```bash curl -X POST "https://$YOUR-DOMAIN/search" \ -d "q=cloudflare" \ - -d "engines=google,brave" + -d "engines=duckduckgo,bing" -d "token=$YOUR-TOKEN" # 如果配置了 TOKEN 环境变量 ``` @@ -162,13 +162,11 @@ curl -X POST "https://$YOUR-DOMAIN/search" \ | 参数 | 类型 | 必填 | 说明 | 示例 | | ------------- | -------- | ------ | ----------------------------------------- | -------------- | | `q` / `query` | `string` | yes | 搜索关键词 | `cloudflare` | -| `engines` | `string` | no | 指定搜索引擎,多个用逗号分隔 | `google,brave` | -| `token` | `string` | no/yse | 访问令牌(当配置了 TOKEN 环境变量时必填) | `$YOUR-TOKEN` | +| `engines` | `string` | no | 指定搜索引擎,多个用逗号分隔 | `duckduckgo,bing` | +| `token` | `string` | no/yes | 访问令牌(当配置了 TOKEN 环境变量时必填) | `$YOUR-TOKEN` | **支持的搜索引擎**: -- `google` - Google 搜索(需要配置 API Key) -- `brave` - Brave 搜索 - `duckduckgo` - DuckDuckGo 搜索 - `bing` - Bing 搜索 @@ -180,6 +178,8 @@ curl -X POST "https://$YOUR-DOMAIN/search" \ number_of_results: number; // 结果总数 enabled_engines: string[]; // 启用的搜索引擎列表 unresponsive_engines: string[]; // 无响应的搜索引擎列表 + empty_engines: string[]; // 响应成功但无结果的搜索引擎列表 + engine_errors: Record; // 各搜索引擎错误信息 results: Array<{ title: string; // 结果标题 description: string; // 结果描述 @@ -193,12 +193,12 @@ curl -X POST "https://$YOUR-DOMAIN/search" \ ```bash # GET 请求 -curl "https://$YOUR-DOMAIN/search?q=cloudflare&engines=google,brave" +curl "https://$YOUR-DOMAIN/search?q=cloudflare&engines=duckduckgo,bing" # POST 请求 curl -X POST "https://$YOUR-DOMAIN/search" \ -H "Content-Type: application/x-www-form-urlencoded" \ - -d "q=cloudflare&engines=google,brave" + -d "q=cloudflare&engines=duckduckgo,bing" ``` #### 响应示例 @@ -207,20 +207,22 @@ curl -X POST "https://$YOUR-DOMAIN/search" \ { "query": "cloudflare", "number_of_results": 15, - "enabled_engines": ["google", "brave", "duckduckgo"], + "enabled_engines": ["duckduckgo", "bing"], "unresponsive_engines": [], + "empty_engines": [], + "engine_errors": {}, "results": [ { "title": "Cloudflare - The Web Performance & Security Company", "description": "Cloudflare is on a mission to help build a better Internet...", "url": "https://www.cloudflare.com/", - "engine": "google" + "engine": "duckduckgo" }, { "title": "Cloudflare Workers", "description": "Deploy serverless code instantly across the globe...", "url": "https://workers.cloudflare.com/", - "engine": "brave" + "engine": "bing" } ] } @@ -232,9 +234,7 @@ curl -X POST "https://$YOUR-DOMAIN/search" \ | 引擎 | 说明 | 是否需要配置 | 默认启用 | | -------------- | ------------------------ | ----------------------------- | --------------- | -| **Google** | Google Custom Search API | 需要 GOOGLE_API_KEY GOOGLE_CX | yes | -| **Brave** | Brave Search API | - | yes | -| **DuckDuckGo** | DuckDuckGo 即时答案 API | - | yes | +| **DuckDuckGo** | DuckDuckGo HTML 搜索 | - | yes | | **Bing** | Bing 搜索 | - | no (结果不稳定) | ### 基本工作方案 @@ -251,13 +251,10 @@ curl -X POST "https://$YOUR-DOMAIN/search" \ | 变量名 | 类型 | 默认值 | 说明 | | ----------------- | -------- | -------- | ------------------------------------------------- | | `DEFAULT_TIMEOUT` | `string` | `"3000"` | 单个搜索引擎的超时时间(毫秒) | -| `GOOGLE_API_KEY` | `string` | `null` | https://console.cloud.google.com/apis/credentials | -| `GOOGLE_CX` | `string` | `null` | https://programmablesearchengine.google.com/ | | `TOKEN` | `string` | `null` | 访问令牌,配置后启用鉴权,保护服务不被滥用 | **注意**: -- Google Custom Search API 免费版每天限制 100 次请求 - `TOKEN` 配置后,所有请求都需要提供有效的 token ### 配置方式 @@ -268,8 +265,6 @@ curl -X POST "https://$YOUR-DOMAIN/search" \ ```toml [vars] -GOOGLE_API_KEY = "your-google-api-key" -GOOGLE_CX = "your-google-custom-search-cx" DEFAULT_TIMEOUT = "3000" TOKEN = "your-secret-token-here" ``` @@ -288,7 +283,7 @@ TOKEN = "your-secret-token-here" ```javascript const response = await fetch( - "https://$YOUR-DOMAIN/search?q=javascript&engines=google,brave", + "https://$YOUR-DOMAIN/search?q=javascript&engines=duckduckgo,bing", ); const data = await response.json(); console.log(`找到 ${data.number_of_results} 个结果`); @@ -313,7 +308,7 @@ async function search(query) { 收集多个搜索引擎的结果进行对比分析: ```javascript -const engines = ["google", "brave", "duckduckgo"]; +const engines = ["duckduckgo", "bing"]; const results = await fetch( `https://$YOUR-DOMAIN/search?q=AI&engines=${engines.join(",")}`, ); @@ -341,7 +336,6 @@ const byEngine = data.results.reduce((acc, result) => { - 在 Worker 设置中点击 **Triggers** > **Add Custom Domain** 添加自定义域名 2. **搜索引擎限制** - - Google API 免费版每天限制 100 次请求 - 其他搜索引擎一般没有严格限制,但请合理使用 - 频繁请求可能导致被临时限制访问 @@ -382,7 +376,6 @@ A: 可能原因: - 搜索引擎 API 临时不可用或响应超时 - 搜索关键词没有相关结果 - 搜索引擎限制了访问频率 -- Google 需要配置 API Key 才能使用 可以查看返回的 `unresponsive_engines` 字段了解哪些引擎没有响应。 @@ -429,7 +422,6 @@ A: 建议配置 `TOKEN` 环境变量启用鉴权: - [项目 GitHub](https://github.com/Yrobot/cloudflare-search) - [Cloudflare Workers 文档](https://developers.cloudflare.com/workers/) -- [Google Custom Search API](https://developers.google.com/custom-search/v1/overview) ## 支持一下 diff --git a/envs.js b/envs.js index 8ab2844..a8fb7a4 100644 --- a/envs.js +++ b/envs.js @@ -3,20 +3,14 @@ export const env = { DEFAULT_TIMEOUT: "3000", // 支持的搜索引擎列表 - Supported search engines - SUPPORTED_ENGINES: ["google", "brave", "duckduckgo", "bing"], + SUPPORTED_ENGINES: ["duckduckgo", "bing"], // 默认启用的搜索引擎 - Default enabled engines DEFAULT_ENGINES: [ - "google", - "brave", "duckduckgo", // "bing" // Bing 目前结果不稳定,默认禁用 - Bing results are currently unstable, disabled by default ], - // Google Custom Search API credentials - GOOGLE_API_KEY: null, - GOOGLE_CX: null, - // API 访问令牌 - API access token for authentication // 如果设置了 TOKEN,则所有 /search 请求都需要在 header 或 query 中提供此 token // If TOKEN is set, all /search requests must provide this token in header or query diff --git a/index.d.ts b/index.d.ts index c9b3ebf..0553226 100644 --- a/index.d.ts +++ b/index.d.ts @@ -22,6 +22,8 @@ export type searchAll = (params: { number_of_results: number; enabled_engines: string[]; unresponsive_engines: string[]; + empty_engines: string[]; + engine_errors: Record; results: Array; }>; @@ -29,7 +31,5 @@ export interface Env { DEFAULT_TIMEOUT?: string; SUPPORTED_ENGINES?: string[]; DEFAULT_ENGINES?: string[]; - GOOGLE_API_KEY?: string; - GOOGLE_CX?: string; TOKEN?: string; } diff --git a/mcp/cf-search-mcp.js b/mcp/cf-search-mcp.js index eda0b92..2ac032a 100644 --- a/mcp/cf-search-mcp.js +++ b/mcp/cf-search-mcp.js @@ -86,11 +86,11 @@ const SEARCH_INPUT_SCHEMA = { type: "array", items: { type: "string", - enum: ["google", "brave", "duckduckgo", "bing"], + enum: ["duckduckgo", "bing"], }, description: "Optional: Array of search engines to use. If not specified, uses default engines. " + - "Available engines: google, brave, duckduckgo, bing", + "Available engines: duckduckgo, bing", }, }, required: ["query"], @@ -106,7 +106,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { name: "web_search", description: "Search the web for current information, news, or any topic. " + - "Uses multiple engines (Brave, DuckDuckGo, Google, Bing) simultaneously " + + "Uses DuckDuckGo and Bing simultaneously " + "and returns aggregated results with source URLs. " + "Use this when you need real-time information not in your training data. ", inputSchema: SEARCH_INPUT_SCHEMA, @@ -114,7 +114,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { { name: "search", description: - "Search across multiple search engines (Google, Brave, DuckDuckGo, Bing) and return aggregated results. " + + "Search across DuckDuckGo and Bing and return aggregated results. " + "This tool provides comprehensive search results from multiple sources, with source attribution for each result.", inputSchema: SEARCH_INPUT_SCHEMA, }, diff --git a/package.json b/package.json index 3750c18..fd01645 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,6 @@ "free-search", "privacy", "aggregated-search", - "google", - "brave", "duckduckgo", "bing" ], diff --git a/screenshot.png b/screenshot.png deleted file mode 100644 index e8aa08f8cf444c6d89b993c53480e557210223f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49782 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

;*7{ac*(cIDO!7?h|=VuWU`}N&9<)>Ro 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 diff --git a/utils/dev.js b/utils/dev.js index c541482..c3c3b72 100644 --- a/utils/dev.js +++ b/utils/dev.js @@ -1,7 +1,5 @@ // HTTPS_PROXY=http://localhost:7777 bun ./output/cloudflare-search/utils/dev.js -import searchBrave from "./searchBrave.js"; -import searchGoogle from "./searchGoogle.js"; import searchDuckDuckGo from "./searchDuckDuckGo.js"; import searchBing from "./searchBing.js"; diff --git a/utils/getHTML.js b/utils/getHTML.js index ac510fa..47cee7f 100644 --- a/utils/getHTML.js +++ b/utils/getHTML.js @@ -5,7 +5,6 @@ import { env } from "../envs.js"; // ============================================ export function getSearchHtml() { - const GOOGLE_ENABLED = env.GOOGLE_API_KEY && env.GOOGLE_CX; const TOKEN_ENABLED = !!env.TOKEN; const DEFAULT_ENGINES = env.DEFAULT_ENGINES || []; const handlerEngineDefaultChecked = (engine) => @@ -94,7 +93,7 @@ export function getSearchHtml() {

- 基于 Cloudflare Workers 的多引擎聚合搜索服务。并行搜索 Google、DuckDuckGo、Bing、Brave 四大引擎,快速获取更全面的搜索结果。 + 基于 Cloudflare Workers 的多引擎聚合搜索服务。并行搜索 DuckDuckGo、Bing 等搜索引擎,快速获取更全面的搜索结果。

如果这个项目对你有帮助,可以 @@ -112,30 +111,14 @@ export function getSearchHtml() {

⚙️ 服务配置状态

-
- Google 搜索引擎 - - ${ - GOOGLE_ENABLED - ? "✓ 已配置" - : "○ 未配置 (需要 API Key)" - } - -
访问鉴权 - 其他引擎 (Brave/DuckDuckGo/Bing) + 搜索引擎 (DuckDuckGo/Bing) ✓ 可用
- ${ - !GOOGLE_ENABLED - ? ` -
-

- 💡 提示: 配置 Google API 后可使用 Google 搜索引擎。在 Cloudflare Dashboard 的 Worker 设置中添加环境变量:
- • GOOGLE_API_KEY - 获取 API Key
- • GOOGLE_CX - 获取 CX ID -

-
- ` - : "" - } ${ !TOKEN_ENABLED ? ` -
-

+

+

💡 建议:为防止服务被滥用,建议在 Cloudflare Dashboard 的 Worker 设置中添加环境变量 TOKEN 启用访问鉴权。

@@ -213,29 +173,6 @@ export function getSearchHtml() { 选择搜索引擎 (可多选)
- -
@@ -343,14 +274,16 @@ export function getSearchHtml() {
{
   "query": "cloudflare",              // 搜索关键词
   "number_of_results": 15,            // 结果总数
-  "enabled_engines": ["google", ...], // 启用的搜索引擎列表
+  "enabled_engines": ["duckduckgo", ...], // 启用的搜索引擎列表
   "unresponsive_engines": [],         // 无响应的搜索引擎列表
+  "empty_engines": [],                // 响应成功但无结果的搜索引擎列表
+  "engine_errors": {},                // 各搜索引擎错误信息
   "results": [
     {
       "title": "...",                 // 结果标题
       "description": "...",           // 结果描述
       "url": "...",                   // 结果链接
-      "engine": "google"              // 来源引擎
+      "engine": "duckduckgo"          // 来源引擎
     }
   ]
 }
@@ -365,21 +298,6 @@ export function getSearchHtml() { 🚀 支持的搜索引擎
-
-
-
Google
- ${ - GOOGLE_ENABLED - ? '✓ 可用' - : '○ 需配置' - } -
-

- 全球最大的搜索引擎,需要配置 Google Custom Search API -

-
DuckDuckGo
@@ -387,13 +305,6 @@ export function getSearchHtml() {

注重隐私保护的搜索引擎,无需配置

-
-
-
Brave Search
- ✓ 可用 -
-

独立的搜索引擎,无需配置

-
Bing
@@ -425,11 +336,6 @@ export function getSearchHtml() { 在 Cloudflare Dashboard → Workers & Pages → 您的 Worker → 设置 → 变量 中添加:

    - ${ - !GOOGLE_ENABLED - ? '
  • GOOGLE_API_KEYGOOGLE_CX - 启用 Google 搜索 (获取 API Key / 获取 CX)
  • ' - : "" - } ${ !TOKEN_ENABLED ? '
  • TOKEN - 启用访问鉴权 (建议)
  • ' @@ -613,7 +519,7 @@ export function getSearchHtml() { const tokenBodyParam = TOKEN_ENABLED && currentToken ? \`&token=\${currentToken}\` : ''; document.getElementById('apiExample1').textContent = currentOrigin + '/search?q=cloudflare' + tokenParam; - document.getElementById('apiExample2').textContent = 'curl -X POST "' + currentOrigin + '/search" -d "q=cloudflare&engines=google,brave' + tokenBodyParam + '"'; + document.getElementById('apiExample2').textContent = 'curl -X POST "' + currentOrigin + '/search" -d "q=cloudflare&engines=duckduckgo,bing' + tokenBodyParam + '"'; document.getElementById('mcp-config-json').innerHTML = \`{ "mcpServers": { "cloudflare-search": { diff --git a/utils/searchBrave.js b/utils/searchBrave.js deleted file mode 100644 index 8e9c169..0000000 --- a/utils/searchBrave.js +++ /dev/null @@ -1,70 +0,0 @@ -import { normalizeResults } from "./index.js"; - -// type subSearch under index.d.ts -// TODO: support language, time_range, pageno -async function searchBrave({ query, language, time_range, pageno, signal }) { - const searchUrl = `https://search.brave.com/search?q=${encodeURIComponent( - query - )}&spellcheck=0&source=web&summary=0`; - - const response = await fetch(searchUrl, { - signal, - headers: { - accept: - "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", - "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7", - priority: "u=0, i", - "sec-ch-ua": - '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"', - "sec-ch-ua-mobile": "?0", - "sec-ch-ua-platform": '"macOS"', - "sec-fetch-dest": "document", - "sec-fetch-mode": "navigate", - "sec-fetch-site": "none", - "sec-fetch-user": "?1", - "upgrade-insecure-requests": "1", - cookie: - "ui_lang=zh; country=all; safesearch=off; useLocation=0; __Secure-sku#brave-search-captcha=eyJ0eXBlIjoic2luZ2xlLXVzZSIsInZlcnNpb24iOjEsInNrdSI6ImJyYXZlLXNlYXJjaC1jYXB0Y2hhIiwicHJlc2VudGF0aW9uIjoiZXlKcGMzTjFaWElpT2lKaWNtRjJaUzVqYjIwL2MydDFQV0p5WVhabExYTmxZWEpqYUMxallYQjBZMmhoSWl3aWMybG5ibUYwZFhKbElqb2lPRFY2YVU1S1JVMXFTbXhvTTI5TE5UUXhTMFJtTlZkdU4yWjJiall2VmtVMVVHTmFkMlIzV2poMGFreDBTR0V2Tm1SVVMyVjRZWFpSZVVKSFNIcHRVR0ZLUWtSbVFUTkxlV3gzUjNaSVkyZ3ZUVXhvTTFFOVBTSXNJblFpT2lKM1dVZzJTVTFVTlRsTGMzRnpaM1ptTWpVMVRDOHpOVlZrYzFVNU1sRnlSSGxGZVdoWVlXSXdOa2x3TVhscU1HMUlUa0ozUjNGVVpYTlliWGh1TlVGREx5OXdLMUpJUjJzemJrTTJVWHBQWW1aUFpVOHlVVDA5SW4wPSJ9", - }, - referrer: "https://search.brave.com/search?source=web", - }); - - if (!response.ok) { - console.error(`Brave search failed: ${response.status}`); - return []; - } - const html = await response.text(); - - if (!html) throw new Error("html is empty"); - - let data; - - html.split("\n").forEach((line) => { - if (data) return; - if (line.trimStart().startsWith(`data: [{type:"data",`)) { - const pureLine = line.trim(); - const jsonStr = pureLine.slice( - "data: ".length, - pureLine.endsWith(",") ? -1 : undefined - ); - data = eval("(" + jsonStr + ")"); - } - }); - - if (!data) throw new Error("can't find data in html"); - - let result; - - data.forEach((item) => { - if (result) return; - if (item?.data?.body?.response?.web?.results) - result = normalizeResults(item.data.body.response.web.results); - }); - - if (!result) throw new Error("can't find result in data"); - if (!(result instanceof Array)) throw new Error("result is not an array"); - - return result; -} - -export default searchBrave; diff --git a/utils/searchGoogle.js b/utils/searchGoogle.js deleted file mode 100644 index 931ef07..0000000 --- a/utils/searchGoogle.js +++ /dev/null @@ -1,34 +0,0 @@ -import { normalizeResults } from "./index.js"; -import { env } from "../envs.js"; - -// type subSearch under index.d.ts -// TODO: support language, time_range, pageno -async function searchGoogle({ query, language, time_range, pageno, signal }) { - const searchUrl = `https://www.googleapis.com/customsearch/v1?key=${env.GOOGLE_API_KEY}&cx=${env.GOOGLE_CX}&q=${encodeURIComponent( - query - )}`; - - const response = await fetch(searchUrl, { signal }); - - if (!response.ok) { - console.error(await response.text()); - return []; - } - - const data = await response.json(); - const results = []; - - if (data.items && Array.isArray(data.items)) { - for (const item of data.items) { - results.push({ - title: item.title, - url: item.link, - content: item.snippet || "", - }); - } - } - - return normalizeResults(results); -} - -export default searchGoogle; diff --git a/worker.js b/worker.js index fd85126..2632dd7 100644 --- a/worker.js +++ b/worker.js @@ -1,13 +1,9 @@ import { env, setEnv } from "./envs.js"; import { getSearchHtml } from "./utils/getHTML.js"; -import searchGoogle from "./utils/searchGoogle.js"; -import searchBrave from "./utils/searchBrave.js"; import searchDuckDuckGo from "./utils/searchDuckDuckGo.js"; import searchBing from "./utils/searchBing.js"; const SEARCH_ENGINES = { - google: searchGoogle, - brave: searchBrave, duckduckgo: searchDuckDuckGo, bing: searchBing, }; @@ -23,12 +19,7 @@ function parseEngines(enginesParam) { return enginesParam .split(",") .map((e) => e.trim().toLowerCase()) - .filter((e) => { - // Filter out google if not enabled - if (e === "google" && !(env.GOOGLE_API_KEY && env.GOOGLE_CX)) - return false; - return env.SUPPORTED_ENGINES.includes(e); - }); + .filter((e) => env.SUPPORTED_ENGINES.includes(e)); } /** @@ -41,28 +32,35 @@ async function searchSingle(engineName, query) { const searchFn = SEARCH_ENGINES[engineName]; if (!searchFn) { console.warn(`Unknown engine: ${engineName}`); - return []; + return { results: [], error: "Unknown engine" }; } // 创建 AbortController 用于取消请求 const controller = new AbortController(); const timeout = parseInt(env.DEFAULT_TIMEOUT ?? "3000", 10); + let timeoutId; try { // 设置超时自动取消 - const timeoutId = setTimeout(() => controller.abort(), timeout); + timeoutId = setTimeout(() => controller.abort(), timeout); - const result = await searchFn({ query, signal: controller.signal }); + const results = await searchFn({ query, signal: controller.signal }); clearTimeout(timeoutId); - return result; + return { results, error: null }; } catch (error) { + const message = + error.name === "AbortError" + ? `Timeout after ${timeout}ms` + : error.message || String(error); + if (error.name === "AbortError") { - console.error(`[${engineName}] Timeout after ${timeout}ms`); + console.error(`[${engineName}] ${message}`); } else { - console.error(`[${engineName}] Error:`, error.message); + console.error(`[${engineName}] Error:`, message); } - return []; + clearTimeout(timeoutId); + return { results: [], error: message }; } } @@ -86,21 +84,27 @@ async function searchAll({ query, engines }) { // Collect resultsArr and track unresponsive engines const results = []; const unresponsive = []; + const empty = []; + const errors = {}; resultsArr.forEach((result, index) => { const engineName = enabledEngines[index]; - if (result.status === "fulfilled" && result.value.length > 0) { + if (result.status === "fulfilled" && result.value.results.length > 0) { results.push( - ...result.value.map((item) => ({ + ...result.value.results.map((item) => ({ ...item, engine: engineName, })) ); + } else if (result.status === "fulfilled" && result.value.error) { + unresponsive.push(engineName); + errors[engineName] = result.value.error; + } else if (result.status === "fulfilled") { + empty.push(engineName); } else { unresponsive.push(engineName); - if (result.status === "rejected") { - console.error(`[${engineName}] Rejected:`, result.reason); - } + errors[engineName] = result.reason?.message || String(result.reason); + console.error(`[${engineName}] Rejected:`, result.reason); } }); @@ -109,6 +113,8 @@ async function searchAll({ query, engines }) { number_of_results: results.length, enabled_engines: enabledEngines, unresponsive_engines: unresponsive, + empty_engines: empty, + engine_errors: errors, results, }; } diff --git a/wrangler.toml b/wrangler.toml index 4e7cf52..168c4e9 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -1,6 +1,3 @@ -# Cloudflare Worker 配置文件 -# 更多配置选项: https://developers.cloudflare.com/workers/wrangler/configuration/ - # Worker 名称(修改为你想要的名称) name = "cloudflare-search" @@ -15,45 +12,16 @@ compatibility_date = "2024-12-04" # 设置为 true 会自动分配一个 xxx.workers.dev 域名 workers_dev = true -# 账号 ID (可选,部署时会自动填充) -# account_id = "your-account-id" - -# ============================================ -# 路由配置(可选) -# ============================================ -# 如果你想将 Worker 绑定到自定义域名,取消注释并配置: -# routes = [ -# { pattern = "search.yourdomain.com/*", zone_name = "yourdomain.com" } -# ] # ============================================ # 环境变量(可选) # ============================================ [vars] -# Google Custom Search API 配置 -# GOOGLE_API_KEY = "" -# GOOGLE_CX = "" - # API 访问令牌(可选) # 配置后,所有 /search 请求都需要提供此 token 进行鉴权 # 可以通过 Authorization header (Bearer token) 或 query 参数 (?token=xxx) 传递 -# TOKEN = "" +TOKEN = "shumengya520" -# ============================================ -# 环境配置(可选) -# ============================================ -# 生产环境配置 -# [env.production] -# name = "cloudflare-search-prod" -# workers_dev = false -# routes = [ -# { pattern = "search.yourdomain.com/*", zone_name = "yourdomain.com" } -# ] - -# 开发环境配置 -# [env.staging] -# name = "cloudflare-search-staging" -# workers_dev = true # ============================================ # 资源限制(可选)