1.77.6: SERP sorry-重定向按验证码语义(不换号+15min冷却);浏览池保温释放跳过+60min持有上限;速率-门壳实验与监控工具
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/** # 脚本说明
|
||||
* 压测收尾:删除全部测试 Profile(SERP 标签)。
|
||||
* 分页拉取(限 500)→ 并发 DELETE /api/profiles/:id(admin 级联:节点数据删除/扩展绑定清理)。
|
||||
* 404 视为已删;最后复查 admin DB 与节点 profile 目录残留数。
|
||||
* 用法:bun cleanup-profiles.ts
|
||||
*/
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const ADMIN = "http://127.0.0.1:5310";
|
||||
const DATA = `${new URL(".", import.meta.url).pathname}data`;
|
||||
|
||||
/** # 函数:docker exec 查 admin DB 单值 **/
|
||||
function sqlOne(sql: string): string {
|
||||
const p = Bun.spawnSync(["docker", "exec", "serpmax-pg", "psql", "-U", "postgres", "-d", "serpmax_admin", "-tAc", sql], { stdout: "pipe", stderr: "pipe" });
|
||||
const out = p.stdout.toString().trim();
|
||||
if (p.exitCode !== 0 || !out) throw new Error(`SQL 失败`);
|
||||
return out;
|
||||
}
|
||||
const TOKEN = sqlOne("SELECT api_token FROM users WHERE id=2");
|
||||
|
||||
// 分页拉取 SERP 标签 Profile(标签过滤走服务端,兜底本地再筛)
|
||||
const fps: { id: string; fp: string }[] = [];
|
||||
let page = 1;
|
||||
while (page <= 40) {
|
||||
const res = await fetch(`${ADMIN}/api/profiles?limit=500&page=${page}`, {
|
||||
headers: { authorization: `Bearer ${TOKEN}` },
|
||||
signal: AbortSignal.timeout(30000),
|
||||
});
|
||||
if (!res.ok) throw new Error(`列表 ${res.status}`);
|
||||
const j: any = await res.json();
|
||||
const data = j.data ?? j;
|
||||
const items: any[] = data.items ?? [];
|
||||
for (const it of items) {
|
||||
const tags: string[] = it.tags ?? [];
|
||||
if (tags.includes("SERP") && (it.name ?? "").startsWith("sz-serp-")) {
|
||||
fps.push({ id: it.id, fp: it.fingerprint ?? "" });
|
||||
}
|
||||
}
|
||||
const total = data.total ?? 0;
|
||||
if (items.length < 500 || fps.length >= total) break;
|
||||
page++;
|
||||
}
|
||||
console.log(`[cleanup] 目标 ${fps.length} 个`);
|
||||
|
||||
let deleted = 0, skipped = 0;
|
||||
let cursor = 0;
|
||||
const workers = 16;
|
||||
async function worker(): Promise<void> {
|
||||
while (cursor < fps.length) {
|
||||
const i = cursor++;
|
||||
const t = fps[i];
|
||||
if (!t.id) { skipped++; continue; }
|
||||
try {
|
||||
const res = await fetch(`${ADMIN}/api/profiles/${t.id}`, { method: "DELETE", headers: { authorization: `Bearer ${TOKEN}` }, signal: AbortSignal.timeout(60000) });
|
||||
if (res.ok || res.status === 404) deleted++;
|
||||
else { skipped++; if (skipped < 10) console.error(`[cleanup] 删 ${t.fp} 状态 ${res.status}`); }
|
||||
} catch (e) { skipped++; }
|
||||
if (deleted % 500 === 0 && deleted > 0) console.log(`[cleanup] deleted=${deleted} skipped=${skipped}`);
|
||||
}
|
||||
}
|
||||
await Promise.all(Array.from({ length: workers }, () => worker()));
|
||||
console.log(`[cleanup] 完成 deleted=${deleted} skipped=${skipped}`);
|
||||
console.log(`[cleanup] admin 残留 SERP=${sqlOne("SELECT count(*) FROM profiles WHERE tags @> ARRAY['SERP']::text[]")}`);
|
||||
console.log(`[cleanup] 节点目录残留=${Bun.spawnSync(["bash", "-c", "ls /opt/serpmax/node/data/profiles/ | wc -l"], { stdout: "pipe" }).stdout.toString().trim()}`);
|
||||
Reference in New Issue
Block a user