Cloudflare Workers 代理部署全流程一、背景与动机LLM-Switch是一个 AI 模型供应商管理 App核心诉求是无论在什么平台/工具中使用 API Key都能统一监控用量。问题在于Trae IDE、Claude Code、Cursor 等工具内置的 API 调用无法被本地应用直接监控本地代理服务器proxy_server.dart需要保持电脑开机运行不够可靠需要一个永不宕机的代理层来拦截和记录所有 API 请求解决方案将代理逻辑部署到 Cloudflare 的全球边缘节点Workers利用其免费额度实现零成本运维。二、架构设计Base URL转发请求转发请求SSE / JSON 响应ctx.waitUntil()异步写入用量GET /proxy/usage查询用量返回用量数据更新仪表盘外部工具Trae / Claude Code / CursorCloudflare Workerllm-switch-proxy.xxx.workers.dev真实 APIDeepSeek / GPT-4 / Claude-3D1 数据库SQLite (proxy_request_logs)Flutter App定时轮询 /proxy/usageLLM-Switch App仪表盘 用量记录核心接口端点方法功能/proxy/healthGET健康检查 D1 连接状态/proxy/usage?sincelimitGET拉取用量记录增量/proxy/usageDELETE清空所有记录其他所有路径ANY转发到目标 API三、项目文件结构llm_switch/cf-proxy/ ├── wrangler.toml # Wrangler 配置Worker 名/D1 绑定/环境变量 ├── package.json # Node.js 依赖wrangler ├── schema.sql # D1 数据库建表语句 └── src/ └── index.js # Worker 核心逻辑~380 行wrangler.tomlname llm-switch-proxy main src/index.js compatibility_date 2024-12-01 [[d1_databases]] binding DB database_name llm-switch-usage database_id e030a225-3868-4084-8cb4-2120da9e501a [vars] TARGET_BASE_URL https://api.deepseek.comschema.sqlCREATETABLEIFNOTEXISTSproxy_request_logs(idTEXTPRIMARYKEY,provider_nameTEXTNOTNULLDEFAULTCloudflare代理,model_nameTEXTNOTNULLDEFAULTunknown,app_typeTEXTNOTNULLDEFAULTllm,input_tokensINTEGERNOTNULLDEFAULT0,output_tokensINTEGERNOTNULLDEFAULT0,costREALNOTNULLDEFAULT0,latency_msINTEGERNOTNULLDEFAULT0,status_codeINTEGERNOTNULL,error_messageTEXT,created_atINTEGERNOTNULL);index.js 核心逻辑~380 行主要模块CORS 处理—Access-Control-Allow-Origin: *所有接口支持跨域费用估算— 根据模型名称deepseek/gpt-4/claude-3 等自动计算$input/1M * 输入 $output/1M * 输出请求转发— 剥离 Cloudflare 特定头cf-connecting-ip等转发到TARGET_BASE_URL流式响应处理— 对 SSE 流式请求tee 流读取[DONE]前的最后一条 data 帧解析usage非流式响应处理— 解析response.json().usage获取 prompt_tokens/completion_tokensD1 写入—ctx.waitUntil()异步写入不阻塞响应用量查询— 支持?sinceISO时间limitN增量查询四、部署步骤4.1 前置准备Cloudflare 账号Node.js 22.x npm 10.xAPI Token权限Workers Scripts 编辑、D1 编辑、Account Settings 读取Cloudflare 登录地址https://dash.cloudflare.com/login三种登录方式一般选取谷歌进行登录。这里可以进行语言的切换。4.2 API Token 权限配置在 Cloudflare 控制台 → 个人资料 → API 令牌 → 创建令牌 → 自定义点击右上角的图像可以看到配置文件的这个选项。然后进行以下的操作添加三个权限权限类型级别资源Workers Scripts编辑包括 - 所有区域D1编辑包括 - 所有区域Account Settings读取包括 - 所有区域Token 格式cfut_xxx...只显示一次立即保存4.3 安装 Wranglercdllm_switch/cf-proxynpminstall4.4 注册 workers.dev 子域名每个 Cloudflare 账户需要注册一次。可通过 Cloudflare Dashboard 或 API# 查询当前子域名Invoke-RestMethod-Urihttps://api.cloudflare.com/client/v4/accounts/{account_id}/workers/subdomain-Method GET-Headers {AuthorizationBearer$TOKEN}# 返回: { subdomain: 3395769576hy }4.5 创建 D1 数据库npx wrangler d1 create llm-switch-usage# 输出: database_id e030a225xxxxxxx501a将返回的database_id填入wrangler.toml。4.6 建表必须先远程执行npx wrangler d1 execute llm-switch-usage--remote--fileschema.sql注意必须加--remote否则只在本地执行。4.7 部署 Worker由于 Wrangler CLI 在某些 Windows 环境下存在交互式认证问题卡在 “Getting User settings…”改用Cloudflare REST API直接上传。创建deploy.js部署后已删除constfsrequire(fs);consthttpsrequire(https);constACCOUNT_ID30ad86xxxxxxxx;constAPI_TOKENcfuxxxxx;constSCRIPT_NAMEllm-switch-proxy;constD1_IDexxxxxxxxxxxxx;constmetadataJSON.stringify({main_module:index.js,bindings:[{type:d1,name:DB,database_id:D1_ID}// 关键type 必须是 d1],vars:{TARGET_BASE_URL:https://api.deepseek.com},compatibility_date:2024-12-01,});// 构建 multipart/form-data 上传// PUT /client/v4/accounts/{id}/workers/scripts/{name}4.8 验证部署# 健康检查curlhttps://llm-switch-proxy.3395769576hy.workers.dev/proxy/health# {status:ok,target:https://api.deepseek.com,platform:cloudflare,records:1}# 用量查询curlhttps://llm-switch-proxy.3395769576hy.workers.dev/proxy/usage# [{id:...,providerName:Cloudflare代理,modelName:deepseek-chat,...}]五、Flutter App 端适配5.1 问题Web 平台跨域超时Flutter Web 的package:http底层使用XMLHttpRequest对 Cloudflare Worker 的 HTTPS 跨域请求出现TimeoutException即使 Worker 已设置 CORS。5.2 解决方案条件导出 dart:htmllib/services/ ├── proxy_service.dart # 条件导出入口 ├── proxy_service_io.dart # 移动端http 包 └── proxy_service_web.dart # Web 端dart:html HttpRequestproxy_service.dart核心一行exportproxy_service_io.dartif(dart.library.html)proxy_service_web.dart;Web 版使用dart:html的HttpRequest.request()绕过http包的跨域问题。六、关键踩坑记录坑现象原因解决D1 绑定类型错误binding DB has an unknown type d1_databaseAPI metadata 中 type 字段值不对改为type: d1字段用database_id而非idD1 绑定不生效/proxy/usage返回env.DB is undefinedd1_databases顶层字段 API 不支持将 D1 绑定放入bindings数组type 为d1Wrangler 卡认证停在 “Getting User settings…”Windows xdg.config 日志目录权限 交互式 OAuth改用 REST API 直接上传 WorkerSchema 仅本地执行部署后 Worker 查询失败wrangler d1 execute默认--local加--remote标志端口占用SocketException: 端口只允许使用一次多个flutter run残留进程Get-NetTCPConnection查进程 →Stop-Process_languageCode私有访问Flutter 编译错误私有字段跨类访问改用公开 getterlanguageCode子域名未注册You need to register a workers.dev subdomain账户首次使用 WorkersAPI 调用或 Dashboard 注册一次七、使用方式在 LLM-Switch App → 设置 → 代理监控 → 输入https://llm-switch-proxy.3xxxxxxxxxhy.workers.dev→ 连接将外部工具Claude Code / Cursor / 自定义应用的 Base URL 改为上述地址App 每 30 秒自动拉取最新用量仪表盘实时更新八、扩展性多模型支持修改TARGET_BASE_URL或根据请求路由动态判断目标 API密钥禁用Worker 端暂未实现/proxy/keys/disable可后续添加用量保护可添加USAGE_TOKEN环境变量保护查询接口成本优化Worker 免费额度 10 万次/天D1 免费 5GB 存储日常使用足够