缘起:一个"查余额"的痛点
事情的起点很朴素——我手上有几个 AI 平台的订阅,每个月有固定的配额。每次想知道还剩多少,都得打开浏览器、登录控制台、点好几个页面才能看到。烦。
更烦的是,有些平台的控制台把用量信息藏在很深的菜单里,手机上根本没法查。作为一个把"自动化一切"当人生信条的人,这事儿忍不了。
于是就有了这个项目——Quota Pulse。名字很直白:像脉搏一样实时跳动,告诉你配额还剩多少。功能也很明确:自动抓取各 AI 平台的 Token Plan 剩余量,集中展示在一个页面上,支持本地和远程访问。
技术选型:为什么是"本地 + 公网"双模
需求很明确,但实现路径有好几条。我最终选了 本地服务 + 公网代理 的混合架构,理由如下:
核心思路: 本地跑采集逻辑(安全、不受限),公网只暴露只读的展示层(不暴露任何凭证和原始接口)。
架构组成:
- 本地服务:Node.js + Express,定时任务 + 数据缓存
- 数据采集层:HTTP 直连 + 浏览器自动化兜底
- 凭证隔离:环境变量注入,零硬编码
- 公网暴露:内网穿透,轻量页面
爬虫实战:两种采集策略
API 用量数据不是公开的——它需要身份认证。这就意味着爬虫必须模拟登录态去请求平台的后端接口。
我面对的是两类平台,策略也不同:
| 平台类型 | 采集方式 | 难度 | 风险 |
|---|---|---|---|
| 提供用量查询 API | HTTP 请求直连,携带认证头 | 低 | 频率控制、凭证泄露 |
| 无公开用量 API | 浏览器自动化登录 → 解析页面 → 提取数据 | 高 | 验证码、页面结构变更、会话过期 |
策略一:HTTP 直连
对于有开放用量接口的平台,流程非常简单——发一个带认证头的 GET 请求,解析 JSON 响应,提取剩余量和重置时间。
// 核心逻辑(脱敏后)
const resp = await fetch(USAGE_ENDPOINT, {
headers: { 'Authorization': `Bearer ${process.env.API_KEY}` }
});
const data = await resp.json();
return {
remaining: data.remaining_quota,
resetAt: data.reset_time,
percentage: data.usage_percentage
};
这里的关键原则:任何凭证(Key、Token、ID)都不出现在代码中,全部通过 .env 文件注入,且 .env 被 .gitignore 排除。代码仓库里零敏感信息。
策略二:浏览器自动化
有些平台没有公开的用量 API,数据嵌在管理后台的页面里。这时候就得祭出无头浏览器了。
流程是:自动打开登录页 → 填入凭证 → 处理可能的验证码(由人工介入)→ 导航到用量页面 → 解析 DOM 提取数据。
⚠️ 安全边界: 验证码环节不尝试自动破解——这是明确的红线。遇到验证码时,程序挂起等待人工处理。自动化不是为了绕过安全机制,而是减少重复劳动。
数据流设计
整个系统的数据流动如下:
定时任务触发 → 采集模块(HTTP / 浏览器)→ 数据清洗 & 脱敏 → 本地缓存(内存 + 文件)→ 展示 API(只读)→ 前端页面
几个设计要点:
- 采集与展示分离: 采集层有完整凭证权限,展示层只有只读的数据读取权限。两个模块运行在同一个进程里,但代码边界清晰——展示 API 永远不转发请求到外部平台。
- 缓存降级: 采集失败时不报错,返回上一次成功缓存的数据并标注"数据可能过期"。用户体验不受单次故障影响。
- 频率控制: 每个平台的采集间隔独立配置(5~30 分钟),避免对上游造成压力或被限流。
安全设计:三道防线
一个要处理 API 凭证、又要暴露到公网的项目,安全不是可选项。我设计了三道防线:
| 防线 | 措施 | 防护目标 |
|---|---|---|
| 第一道:代码层 | 零硬编码凭证;.env 不入库;日志脱敏(自动替换敏感字段) | 防止源码泄露导致凭证外泄 |
| 第二道:传输层 | HTTPS 强制;内网穿透隧道加密;展示 API 不代理任何外部请求 | 防止中间人攻击、SSRF |
| 第三道:访问层 | 展示页面无写入能力;API 仅返回聚合后的只读数据;无原始接口路径暴露 | 防止通过展示层反推凭证或攻击上游 |
额外一提:日志系统做了全局脱敏处理——任何匹配到凭证格式(如特定前缀的字符串)的输出,自动替换为 [REDACTED]。这样即使日志不小心被分享出去,也不会泄露敏感信息。
部署方案:内网穿透的正确姿势
本地服务跑在开发机上(Windows),公网访问通过内网穿透实现。选型上对比了几个方案:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| frp / ngrok | 简单快速,一行命令 | 依赖第三方服务;稳定性不可控 | 临时演示 |
| Cloudflare Tunnel | 免费、自带 HTTPS、不限流量 | 需要自有域名;国内延迟偏高 | 个人长期使用 |
| 自有公网服务器 + 反向代理 | 完全可控、延迟最低 | 需要服务器成本;运维复杂 | 生产环境 |
我最终选了 Cloudflare Tunnel 作为公网通道——零成本、自带证书、配置简单。在本地启动 tunnel 客户端后,一个随机子域名就映射到了本地的 3000 端口。
# 启动隧道(脱敏后)
cloudflared tunnel --url http://localhost:3000
# 输出:
# +--------------------------------------------------------------------------------------------+
# | Your quick tunnel has been created! |
# | https://xxxx-xxxx-xxxx.trycloudflare.com → http://localhost:3000 |
# +--------------------------------------------------------------------------------------------+
公网访问的只是展示页面——一个静态 HTML 配上几个 AJAX 请求,从本地只读 API 拉取缓存的用量数据。公网侧永远接触不到任何外部平台的接口或凭证。
总结与反思
这个项目从构思到上线大概花了一个周末,但从中沉淀的经验远不止一个工具本身:
- 爬虫不是"能爬就行"—— 区分"有 API 的平台"和"没有 API 的平台",用不同的策略,能省大量维护成本。HTTP 直连远比浏览器自动化稳定。
- 安全的本质是"最小暴露"—— 不是把所有东西锁起来,而是让每一层只接触它需要接触的信息。展示层不需要凭证,那就永远不传给它。
- "本地 + 公网"的混合架构在个人工具场景下非常实用—— 享受本地的灵活性和安全性,同时拥有公网的便利性。两全其美。
- 验证码是红线—— 自动化可以帮你省时间,但不能帮你突破安全机制。遇到验证码,停下来,人工处理。这是原则问题。
最后,Quota Pulse 这个名字——配额脉搏——恰好概括了这个工具的本质:不复杂、不花哨,但它跳动的时候,你就知道一切都在运转。
本文所有凭证、Key、Token、接口路径均已脱敏处理 · 技术细节仅作架构参考
