chore: sync local changes to Gitea
This commit is contained in:
@@ -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";
|
||||
|
||||
|
||||
118
utils/getHTML.js
118
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() {
|
||||
</h1>
|
||||
<div class="mt-6 text-base text-zinc-600 dark:text-zinc-400">
|
||||
<p class="">
|
||||
基于 Cloudflare Workers 的多引擎聚合搜索服务。并行搜索 Google、DuckDuckGo、Bing、Brave 四大引擎,快速获取更全面的搜索结果。
|
||||
基于 Cloudflare Workers 的多引擎聚合搜索服务。并行搜索 DuckDuckGo、Bing 等搜索引擎,快速获取更全面的搜索结果。
|
||||
</p>
|
||||
<p class="mt-2">
|
||||
如果这个项目对你有帮助,可以
|
||||
@@ -112,30 +111,14 @@ export function getSearchHtml() {
|
||||
|
||||
<!-- 服务状态 -->
|
||||
<div class="mt-8 rounded-2xl border ${
|
||||
GOOGLE_ENABLED && !TOKEN_ENABLED
|
||||
? "border-green-200 bg-green-50 dark:border-green-800/40 dark:bg-green-900/10"
|
||||
: !GOOGLE_ENABLED || TOKEN_ENABLED
|
||||
TOKEN_ENABLED
|
||||
? "border-amber-200 bg-amber-50 dark:border-amber-800/40 dark:bg-amber-900/10"
|
||||
: "border-zinc-100 dark:border-zinc-700/40"
|
||||
: "border-green-200 bg-green-50 dark:border-green-800/40 dark:bg-green-900/10"
|
||||
} p-6">
|
||||
<h2 class="text-sm font-semibold text-zinc-900 dark:text-zinc-100 mb-3">
|
||||
⚙️ 服务配置状态
|
||||
</h2>
|
||||
<div class="space-y-2 text-sm">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-zinc-700 dark:text-zinc-300">Google 搜索引擎</span>
|
||||
<span class="${
|
||||
GOOGLE_ENABLED
|
||||
? "text-green-600 dark:text-green-400"
|
||||
: "text-amber-600 dark:text-amber-400"
|
||||
}">
|
||||
${
|
||||
GOOGLE_ENABLED
|
||||
? "✓ 已配置"
|
||||
: "○ 未配置 (需要 API Key)"
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-zinc-700 dark:text-zinc-300">访问鉴权</span>
|
||||
<span class="${
|
||||
@@ -147,40 +130,17 @@ export function getSearchHtml() {
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-zinc-700 dark:text-zinc-300">其他引擎 (Brave/DuckDuckGo/Bing)</span>
|
||||
<span class="text-zinc-700 dark:text-zinc-300">搜索引擎 (DuckDuckGo/Bing)</span>
|
||||
<span class="text-green-600 dark:text-green-400">✓ 可用</span>
|
||||
</div>
|
||||
</div>
|
||||
${
|
||||
!GOOGLE_ENABLED
|
||||
? `
|
||||
<div class="mt-4 pt-4 border-t border-amber-200 dark:border-amber-800/40">
|
||||
<p class="text-xs text-amber-700 dark:text-amber-400">
|
||||
💡 提示: 配置 Google API 后可使用 Google 搜索引擎。在 Cloudflare Dashboard 的 Worker 设置中添加环境变量:<br/>
|
||||
• <code class="px-1 py-0.5 bg-amber-100 dark:bg-amber-900/30 rounded">GOOGLE_API_KEY</code> - <a href="https://console.cloud.google.com/apis/credentials" target="_blank" class="underline hover:text-amber-900 dark:hover:text-amber-100">获取 API Key</a><br/>
|
||||
• <code class="px-1 py-0.5 bg-amber-100 dark:bg-amber-900/30 rounded">GOOGLE_CX</code> - <a href="https://programmablesearchengine.google.com/" target="_blank" class="underline hover:text-amber-900 dark:hover:text-amber-100">获取 CX ID</a>
|
||||
</p>
|
||||
</div>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
${
|
||||
!TOKEN_ENABLED
|
||||
? `
|
||||
<div class="mt-4 pt-4 border-t ${
|
||||
!GOOGLE_ENABLED
|
||||
? "border-amber-200 dark:border-amber-800/40"
|
||||
: "border-green-200 dark:border-green-800/40"
|
||||
}">
|
||||
<p class="text-xs ${
|
||||
!GOOGLE_ENABLED
|
||||
? "text-amber-700 dark:text-amber-400"
|
||||
: "text-green-700 dark:text-green-400"
|
||||
}">
|
||||
<div class="mt-4 pt-4 border-t border-green-200 dark:border-green-800/40">
|
||||
<p class="text-xs text-green-700 dark:text-green-400">
|
||||
💡 建议:为防止服务被滥用,建议在 Cloudflare Dashboard 的 Worker 设置中添加环境变量 <code class="px-1 py-0.5 ${
|
||||
!GOOGLE_ENABLED
|
||||
? "bg-amber-100 dark:bg-amber-900/30"
|
||||
: "bg-green-100 dark:bg-green-900/30"
|
||||
"bg-green-100 dark:bg-green-900/30"
|
||||
} rounded">TOKEN</code> 启用访问鉴权。
|
||||
</p>
|
||||
</div>
|
||||
@@ -213,29 +173,6 @@ export function getSearchHtml() {
|
||||
选择搜索引擎 (可多选)
|
||||
</label>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<label class="flex items-center space-x-2 ${
|
||||
GOOGLE_ENABLED
|
||||
? "cursor-pointer"
|
||||
: "cursor-not-allowed opacity-50"
|
||||
}" ${
|
||||
!GOOGLE_ENABLED ? 'title="Google 引擎需要配置 API Key"' : ""
|
||||
}>
|
||||
<input type="checkbox" name="engine" value="google" ${
|
||||
GOOGLE_ENABLED
|
||||
? handlerEngineDefaultChecked("google")
|
||||
: "disabled"
|
||||
} class="rounded text-blue-500 focus:ring-blue-500 ${
|
||||
!GOOGLE_ENABLED ? "cursor-not-allowed" : ""
|
||||
}">
|
||||
<span class="text-sm text-zinc-700 dark:text-zinc-300">
|
||||
Google
|
||||
${
|
||||
!GOOGLE_ENABLED
|
||||
? '<span class="text-xs text-zinc-400 dark:text-zinc-500 ml-1">(未配置)</span>'
|
||||
: ""
|
||||
}
|
||||
</span>
|
||||
</label>
|
||||
<label class="flex items-center space-x-2 cursor-pointer">
|
||||
<input type="checkbox" name="engine" value="duckduckgo" ${handlerEngineDefaultChecked(
|
||||
"duckduckgo"
|
||||
@@ -251,12 +188,6 @@ export function getSearchHtml() {
|
||||
<span class="text-xs text-zinc-400 dark:text-zinc-500 ml-1">(不稳定)</span>
|
||||
</span>
|
||||
</label>
|
||||
<label class="flex items-center space-x-2 cursor-pointer">
|
||||
<input type="checkbox" name="engine" value="brave" ${handlerEngineDefaultChecked(
|
||||
"brave"
|
||||
)} class="rounded text-blue-500 focus:ring-blue-500">
|
||||
<span class="text-sm text-zinc-700 dark:text-zinc-300">Brave</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -343,14 +274,16 @@ export function getSearchHtml() {
|
||||
<pre class="text-xs overflow-x-auto"><code>{
|
||||
"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" // 来源引擎
|
||||
}
|
||||
]
|
||||
}</code></pre>
|
||||
@@ -365,21 +298,6 @@ export function getSearchHtml() {
|
||||
🚀 支持的搜索引擎
|
||||
</h2>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div class="rounded-lg bg-zinc-50 p-4 dark:bg-zinc-800/50 ${
|
||||
!GOOGLE_ENABLED ? "opacity-50" : ""
|
||||
}">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="font-medium text-zinc-900 dark:text-zinc-100">Google</div>
|
||||
${
|
||||
GOOGLE_ENABLED
|
||||
? '<span class="text-xs text-green-600 dark:text-green-400">✓ 可用</span>'
|
||||
: '<span class="text-xs text-amber-600 dark:text-amber-400">○ 需配置</span>'
|
||||
}
|
||||
</div>
|
||||
<p class="text-xs text-zinc-600 dark:text-zinc-400">
|
||||
全球最大的搜索引擎,需要配置 Google Custom Search API
|
||||
</p>
|
||||
</div>
|
||||
<div class="rounded-lg bg-zinc-50 p-4 dark:bg-zinc-800/50">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="font-medium text-zinc-900 dark:text-zinc-100">DuckDuckGo</div>
|
||||
@@ -387,13 +305,6 @@ export function getSearchHtml() {
|
||||
</div>
|
||||
<p class="text-xs text-zinc-600 dark:text-zinc-400">注重隐私保护的搜索引擎,无需配置</p>
|
||||
</div>
|
||||
<div class="rounded-lg bg-zinc-50 p-4 dark:bg-zinc-800/50">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="font-medium text-zinc-900 dark:text-zinc-100">Brave Search</div>
|
||||
<span class="text-xs text-green-600 dark:text-green-400">✓ 可用</span>
|
||||
</div>
|
||||
<p class="text-xs text-zinc-600 dark:text-zinc-400">独立的搜索引擎,无需配置</p>
|
||||
</div>
|
||||
<div class="rounded-lg bg-zinc-50 p-4 dark:bg-zinc-800/50 opacity-60">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="font-medium text-zinc-900 dark:text-zinc-100">Bing</div>
|
||||
@@ -425,11 +336,6 @@ export function getSearchHtml() {
|
||||
在 Cloudflare Dashboard → Workers & Pages → 您的 Worker → 设置 → 变量 中添加:
|
||||
</p>
|
||||
<ul class="text-xs text-blue-700 dark:text-blue-300 mt-1 ml-4 list-disc">
|
||||
${
|
||||
!GOOGLE_ENABLED
|
||||
? '<li><code class="px-1 py-0.5 bg-blue-100 dark:bg-blue-900/30 rounded">GOOGLE_API_KEY</code> 和 <code class="px-1 py-0.5 bg-blue-100 dark:bg-blue-900/30 rounded">GOOGLE_CX</code> - 启用 Google 搜索 (<a href="https://console.cloud.google.com/apis/credentials" target="_blank" class="underline hover:text-blue-900 dark:hover:text-blue-100">获取 API Key</a> / <a href="https://programmablesearchengine.google.com/" target="_blank" class="underline hover:text-blue-900 dark:hover:text-blue-100">获取 CX</a>)</li>'
|
||||
: ""
|
||||
}
|
||||
${
|
||||
!TOKEN_ENABLED
|
||||
? '<li><code class="px-1 py-0.5 bg-blue-100 dark:bg-blue-900/30 rounded">TOKEN</code> - 启用访问鉴权 (建议)</li>'
|
||||
@@ -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": {
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user