/** # 脚本说明 * 压测准备:批量在 admin(127.0.0.1:5310)创建 N 个 Profile。 * - 归属当前 MCP 用户(users.id=2,phptits),Token 由脚本直取 DB 不落盘不回显 * - 浏览器内核 CloakB(cloak)、代理 ipmart.io-US、标签 SERP、节点 OVH-FR * - 输出 data/pool.json:[{ id, fingerprint, name }] * * 用法:bun create-profiles.ts [N=10000] */ import { $ } from "bun"; /** # 成员变量 admin 基址 **/ const ADMIN = "http://127.0.0.1:5310"; /** # 成员变量 目标代理(pressure 测试固定 ipmart.io-US,{sid} 每指纹独立出口) **/ const PROXY_ID = "19e3ab5c-c61e-4936-8a7f-d78f6635f57c"; /** # 成员变量 归属节点 **/ const NODE = "OVH-FR"; /** # 成员变量 标签 **/ const TAGS = ["SERP"]; const N = Number(process.argv[2] ?? 10000); /** # 函数 * 通过 docker exec 查询 admin DB,取单值文本。 * 参数 sql:完整 SQL 语句 * 返回值:第一行第一列,去尾部换行 */ 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 失败: ${p.stderr.toString().slice(0, 300)}`); return out; } /** # 成员变量 admin API Token(phptits,id=2;仅内存) **/ const TOKEN = sqlOne("SELECT api_token FROM users WHERE id=2"); const results: { id: string; fingerprint: string; name: string }[] = []; let okCount = 0; let errCount = 0; const t0 = Date.now(); /** # 函数 * 创建单个 Profile,成功返回 {id, fingerprint, name},失败返回 null。 */ async function createOne(i: number): Promise<{ id: string; fingerprint: string; name: string } | null> { const name = `sz-serp-${String(i + 1).padStart(5, "0")}`; try { const res = await fetch(`${ADMIN}/api/profiles`, { method: "POST", headers: { "content-type": "application/json", authorization: `Bearer ${TOKEN}` }, // 浏览器外观随机浅色/深色(用户 2026-10-20 要求 SERP 号不再全浅色) body: JSON.stringify({ name, browserKind: "cloak", browserTheme: Math.random() < 0.5 ? "light" : "dark", proxyId: PROXY_ID, tags: TAGS, node: NODE }), signal: AbortSignal.timeout(20000), }); if (res.status === 409) return null; // 指纹冲突(极小概率),跳过 if (!res.ok) throw new Error(`HTTP ${res.status} ${(await res.text()).slice(0, 120)}`); const j = (await res.json()) as any; const d = (j.data ?? j) as { id?: string; fingerprint?: string }; okCount++; return { id: d.id ?? "", fingerprint: d.fingerprint ?? d.id ?? "", name }; } catch (e) { errCount++; if (errCount < 20) console.error(`create fail ${i}: ${String(e).slice(0, 140)}`); return null; } } /** 主循环:16 并发创建 N 个 */ const workers = 16; let cursor = 0; async function worker() { while (cursor < N) { const i = cursor++; const r = await createOne(i); if (r) results.push(r); } } await Promise.all(Array.from({ length: workers }, () => worker())); await Bun.write(new URL(import.meta.url).pathname.replace(/[^/]+$/, "") + `data/pool.json`, JSON.stringify(results)); console.log(`done: created=${okCount} failed=${errCount} elapsed=${((Date.now() - t0) / 1000).toFixed(1)}s -> data/pool.json`);