From f6c08b24c5bc3cb221ea6e0c476a86e487d9345a Mon Sep 17 00:00:00 2001 From: BigCode Date: Thu, 29 Oct 2026 16:01:12 +0800 Subject: [PATCH] =?UTF-8?q?1.77.7:=20serpPoolPreview=20SQL=20=E4=B8=8A?= =?UTF-8?q?=E9=99=90=20100=E2=86=922000(=E4=BF=AE=E5=A4=8D=E6=B5=8F?= =?UTF-8?q?=E8=A7=88=E6=B1=A0=E6=B8=85=E7=90=86=E8=AF=AF=E5=88=A4:?= =?UTF-8?q?=E6=B1=A0>100=20=E6=97=B6=E6=9C=80=E8=BF=91=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E6=B8=A9=E5=8F=B7=E8=A2=AB=E8=AF=AF=20unwarm)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- stress/serp-recovery-probe.ts | 50 +++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 stress/serp-recovery-probe.ts diff --git a/stress/serp-recovery-probe.ts b/stress/serp-recovery-probe.ts new file mode 100644 index 0000000..13cf4fc --- /dev/null +++ b/stress/serp-recovery-probe.ts @@ -0,0 +1,50 @@ +/** # 脚本说明 + * sorry 恢复探测器(在 admin 服务器本机运行,极低负载):每 15 分钟经 serp-api + * (runtime=fetch)发 5 个搜索,记录 ok/gate/captcha 比例,写 data/recovery-probe.jsonl, + * 全部通过(5/5 ok)时输出 RECOVERED 并退出。用于测量 Google 前缀标记的恢复时间。 + * nohup env SERP_TOKEN=xxx bun serp-recovery-probe.ts > data/recovery.log 2>&1 & + */ +import fs from "node:fs"; + +/** # 成员变量 基址与令牌(本机) **/ +const BASE = "http://127.0.0.1:53100"; +const TOKEN = process.env.SERP_TOKEN ?? ""; +if (!TOKEN) { console.error("缺少 SERP_TOKEN"); process.exit(1); } +/** # 成员变量 输出文件与采样参数 **/ +const DATA = new URL("./data/", import.meta.url).pathname; +fs.mkdirSync(DATA, { recursive: true }); +const JSONL = `${DATA}recovery-probe.jsonl`; +const INTERVAL_MIN = 15; +const N = 5; +const QUERIES = ["recovery probe coffee maker", "recovery probe running shoes", "recovery probe laptop stand", "recovery probe rice cooker", "recovery probe desk lamp"]; + +/** # 函数 + * 发一个 fetch 出口搜索,分类:ok(200 非门壳)/ gate(x-serp-gate:1)/ captcha(502 CAPTCHA)/ err(其它)。 + */ +async function probe(q: string): Promise { + try { + const res = await fetch(`${BASE}/api/serp/html/search?q=${encodeURIComponent(q)}&num=10&runtime=fetch`, { + headers: { authorization: `Bearer ${TOKEN}` }, signal: AbortSignal.timeout(60000), + }); + if (res.ok) return res.headers.get("x-serp-gate") === "1" ? "gate" : "ok"; + const j = await res.json().catch(() => null) as any; + return j?.code === "CAPTCHA_DETECTED" ? "captcha" : "err"; + } catch { return "err"; } +} + +/** # 主流程:每 15 分钟 5 发(间隔 2s),全 ok 则退出;其它情况最多跑 24h */ +const started = Date.now(); +for (;;) { + const results: string[] = []; + for (let i = 0; i < N; i++) { + results.push(await probe(QUERIES[i % QUERIES.length]!)); + if (i < N - 1) await Bun.sleep(2000); + } + const ok = results.filter(r => r === "ok").length; + const row = { at: new Date().toISOString(), results, ok, gate: results.filter(r => r === "gate").length, captcha: results.filter(r => r === "captcha").length, err: results.filter(r => r === "err").length }; + fs.appendFileSync(JSONL, JSON.stringify(row) + "\n"); + console.log(JSON.stringify(row)); + if (ok === N) { console.log("RECOVERED"); break; } + if (Date.now() - started > 24 * 3600_000) { console.log("TIMEOUT 24h"); break; } + await Bun.sleep(INTERVAL_MIN * 60_000); +}