# account-ops **Repository Path**: api-token/account-ops ## Basic Information - **Project Name**: account-ops - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: dev - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-15 - **Last Updated**: 2026-09-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ppt-account-ops(Gamma 账号运维工具) 内部运维服务,与对外客户端服务 [ppt-account](../ppt-account)(客户端邀请注册)分离。两个项目**共用独立库 `ppt-account`**(专属 MySQL 用户,与主项目 `ppt` 库完全分离),通过 `pa_master_accounts` 表协作: - **ppt-account(8091,对外)**:用户下单 → 母账号 `InviteMembers` 发官方邀请邮件 → 到期踢出 - **ppt-account-ops(8092,内部,本项目)**:母账号管理、浏览器自动化注册、邮箱池、子账号积分扫踢 拆分原因:浏览器自动化(Puppeteer)只应跑在运维者本地机器;对外服务部署公网,两者故障域、暴露面、依赖完全不同。母账号席位分配用 `SELECT ... FOR UPDATE` 原子预订(两边同时抢最后一个空位不会超卖)。 ## 启动 ```bash cd ppt-account-ops npm install npm run dev # 或 npm start ``` 依赖:海外网络(`GAMMA_HTTPS_PROXY` 服务端 API + `BROWSER_PROXY_SERVER` 浏览器访问 gamma.app,本机 Clash 7897)。`EMAIL_MODE=tempmail`(默认,已验证产线)无需心蓝;`pool`/`outlook` 模式才需要本地心蓝邮箱助手(`XINLAN_API_URL`)。 ## 管理 API(强制 PA_API_KEY) 调用方式:`Authorization: Bearer ` 或 `X-API-KEY: `。 | 方法 | 路径 | 说明 | |---|---|---| | POST | `/admin/masters` | 录入/更新母账号(email、cookie、workspaceId 必填) | | GET | `/admin/masters` | 母账号列表(含剩余空位) | | POST | `/admin/masters/:id/check` | 校验 cookie + 对账 workspace | | GET | `/admin/masters/:id/join-url` | 查 workspace 通用邀请码 | | POST | `/admin/email-pool` | 导入邮箱池(body: `{ "emails": ["a@x.com", ...] }`) | | GET | `/admin/email-pool` | 邮箱池统计 | | POST | `/admin/auto-register` | 触发一次自动注册(耗时长,同步等待) | | GET | `/admin/sub-accounts` | 子账号列表 | | POST | `/admin/sub-accounts/:id/kick` | 手动踢出 | | POST | `/admin/sub-accounts/:id/relogin` | 子账号 cookie 重登刷新 | | POST | `/admin/sweep` | 手动全量扫描(母账号巡检 + 积分) | | GET | `/api/health` | 健康检查(无鉴权) | 示例: ```bash # 录入母账号 curl -X POST http://localhost:8092/admin/masters \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{"email":"admin@xx.com","cookie":"<浏览器登录 gamma.app 后复制的 Cookie>","workspaceId":""}' # 导入邮箱池(需已托管到心蓝邮箱助手) curl -X POST http://localhost:8092/admin/email-pool \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer ' \ -d '{"emails":["a@x.com","b@x.com"]}' # 触发自动注册 curl -X POST http://localhost:8092/admin/auto-register \ -H 'Authorization: Bearer ' ``` ## 定时器 启动后每 `SWEEP_INTERVAL_MINUTES`(默认 30)分钟: 1. **母账号巡检**:cookie 有效性 + workspace 成员对账(失效标记 `cookie_expired`) 2. **子账号积分扫踢**:逐个查积分(cookie 失效自动重登),`credits <= CREDITS_KICK_THRESHOLD` 踢出 workspace 并释放席位 订单到期踢人在 ppt-account 项目运行,职责分离。 ## 自动注册产线(tempmail 模式,2026-09-17 端到端验证通过) 流程(全程约 2 分钟/个): 选母账号(原子预订席位)→ 创建 26ai.art 临时邮箱(token 落库 `pa_email_pool`)→ 母账号 `InviteMembers` 发官方邀请邮件 → 轮询收件箱提取邀请链接(track.pstmrk.it 跳转)→ 浏览器打开邀请页填邮箱 SendCode → 轮询收件箱提取 setup 魔法链接 → 同会话打开填 first name/last name/password 三项提交 Signup → 再开邀请链接点 "Join workspace" 入组 → 服务端验证(cookie 鉴权 + workspace 成员 + 积分 4000/席)→ 回写 `pa_sub_accounts`;任一步失败自动释放席位。 ### 命令行直接跑(推荐) 前置:本机 Clash(7897)运行中、DB 可连(`PA_DB_*`)、`EMAIL_MODE=tempmail`。 单个产出: ```bash cd ppt-account-ops node -e 'require("./src/services/autoRegisterService").runAutoRegister({ onProgress: m => console.log("[step]", m) }).then(r => { console.log("=== 成功 ===", r.email, "积分:", r.credits); process.exit(0); }).catch(e => { console.error("=== 失败 ===", e.message); process.exit(1); })' ``` 连续产出多个(失败自动释放席位,不影响下一轮): ```bash node -e 'const svc = require("./src/services/autoRegisterService"); (async () => { for (let i = 0; i < 3; i++) { try { const r = await svc.runAutoRegister(); console.log(`#${i+1} 成功:`, r.email, r.credits); } catch (e) { console.error(`#${i+1} 失败:`, e.message); } } })()' ``` 也可走 HTTP:服务启动后 `POST /admin/auto-register`(同步等待,耗时长)。 ### 排查 - 浏览器每步截图在 `debug/`(`DEBUG_SCREENSHOTS=true`) - 关键链路约束(详见 project memory):仅 26ai.art 域能收到 SendCode 确认邮件(创建收件箱自动重试该域);魔法链接 code 与邀请码均一次性消费;鉴权 cookie(gamma_session/gamma_edge_auth)仅设 api.gamma.app 子域,导出优先从请求头抓取 - 页面选择器随 Gamma 改版可能失效,调整 `src/gamma/browser.js`(`createPageKit` 含 Turnstile/OneTrust/语言切换/空白页全套防护)。Gamma GraphQL 操作提取自官方前端 bundle(`duibi/gamma-20260831`),见 `src/gamma/client.js` ## 出口 IP 测试(新节点验收 SOP,2026-09-19) 背景:SendCode 弃发根因已定案为**出口 IP 段被 Gamma 标记**(2026-09-18 85.137.246.x 整段被烧,与浏览器指纹/时段/邮箱/workspace 全部无关;v3 实验证实 Linux 服务器 + 干净出口 + 指纹伪装可过)。新买 VPS 必须先验收再入池。 ### 第一步:新 VPS 部署 gost TLS 代理(整段粘贴) ```bash ssh root@<新VPS IP> 'bash -s' <<'EOF' cd /tmp && curl -sL https://github.com/go-gost/gost/releases/download/v2.12.0/gost_2.12.0_linux_amd64.tar.gz -o gost.tar.gz && tar xzf gost.tar.gz cat > /etc/systemd/system/gost.service <<'UNIT' [Unit] Description=gost tls proxy After=network.target [Service] ExecStart=/tmp/gost -L "http+tls://root:cjFotQ1YTOag@:8788" Restart=always RestartSec=5 [Install] WantedBy=multi-user.target UNIT systemctl daemon-reload && systemctl enable --now gost && systemctl is-active gost EOF ``` 要点:必须 `http+tls://`(TLS 加密,明文 HTTP 代理的 CONNECT 出境即触发 GFW IP 级动态拉黑,149.88.67.198 已报废);账号密码与现有节点统一;VPS 选型**必须避开 85.137.246.x 同段/同 ASN**(整段连坐)。 ### 第二步:节点入池(调管理接口,不改代码) ```bash curl -X POST http://175.27.252.14:8092/admin/proxy-nodes \ -H "Authorization: Bearer " -H "Content-Type: application/json" \ -d '{"name":"ops-<新IP尾段>","host":"<新VPS IP>","port":8788,"username":"root","password":"cjFotQ1YTOag","protocol":"https"}' ``` 也可先不入池直接测(`--node=` 从 DB 按 host 匹配,节点需在表里,状态 disabled 也能测)。 ### 第三步:跑 IP 质量测试(约 3-6 分钟) ```bash node scripts/test-sdk-sendcode.cjs --no-signup --plain --node=<新VPS IP> --master=3 ``` - `--node=`:强制指定出口节点 - `--plain`:普通 Chrome(排除 kernel 浏览器变量) - `--no-signup`:只测到魔法链接送达为止,不做注册、不消耗邀请 - `--master=3`:固定用 master#3 测试(风险隔离到单母账号,其他母账号保持干净) ### 第四步:判读 | 输出 | 结论 | 动作 | |---|---|---| | `★★★ 魔法链接已送达`(提交后 ~7-60s) | IP 干净 | 入池投产 | | 190s 轮询 0 送达(SendCode 返回 emailSent:true 假成功) | IP 已被 Gamma 标记 | 弃用或退款换供应商 | | CF 挑战循环 >3 分钟不出 | IP 被 Cloudflare 盯上(与 Gamma 标记独立) | 不建议使用 | ### 协作约定(多人同时测必读) 1. **并发错开**:两人同时跑会抢同一个 pending 邀请邮箱——约定一人测一台 IP,或轮流串行(脚本每次新建邮箱+新邀请,串行无冲突) 2. **本地 `.env` 前置**:`PA_DB_HOST` 需可达(服务器 MySQL 不通外网时走 SSH 隧道 `ssh -L 3306:127.0.0.1:3306 root@175.27.252.14`);tempmail.lol API 封 CN 出口(403),`GAMMA_HTTPS_PROXY` 配任意海外出口即可 3. **测后清理**:DELETE FROM pa_sub_accounts WHERE status='failed' + DELETE FROM pa_email_pool WHERE status='failed' + Gamma 侧撤销 pending 邀请(`client.js` 的 `revokeInvitation`,invitation id 从 `getWorkspace().invitations` 取)+ 席位对齐 active 数 4. **观察期**:新 IP 一次通过是常态(重点不是能否通过,是投产后能撑多久被烧)——建议每台投产首日观察送达率,跌落即隔离该节点(`POST /admin/proxy-nodes/:id/status` 置 disabled),被烧段冷却 3-7 天后可用 `--node=` 复测是否解封