14 KiB
14 KiB
配置与密钥管理(中文)
最后更新:2026-05-28
1. 目标
PolyWeather 的环境变量很多,但不是所有变量都属于同一层级。
当前推荐做法是把配置拆成三类:
-
可复现基础配置
放在:.env.example -
敏感密钥模板
放在:.env.secrets.example -
平台侧真实密钥
放在:- VPS / Docker
.env - GitHub Secrets(构建期
NEXT_PUBLIC_*通过 build-arg 注入前端镜像) - GitHub Secrets(如需要)
- VPS / Docker
2. 为什么要拆
如果把所有变量都平铺在一个 .env 里,会有三个问题:
- 新环境很难知道“最小启动到底需要哪些变量”
- 敏感密钥和普通开关混在一起,容易误泄露
- 调优参数太多时,团队很难区分“必须填”和“保持默认即可”
所以正确做法不是“减少变量数量”,而是:
- 保留变量能力
- 按职责分层
- 给出最小启动路径
3. 文件职责
3.1 根 .env.example
文件:
用途:
- 后端 / Bot / Docker 的可复现配置模板
- 只放变量名、默认值、开关与非敏感示例
3.2 根 .env.secrets.example
文件:
用途:
- 只列敏感项
- 帮助运维明确哪些值必须从密钥系统注入
3.3 前端 .env.example
文件:
用途:
- 前端本地开发与容器运行时环境变量模板
4. 配置分级
4.1 L1:最小启动必需项
这是“服务能跑起来”的最小集合。
后端 / Bot:
TELEGRAM_BOT_TOKENTELEGRAM_CHAT_IDPOLYWEATHER_RUNTIME_DATA_DIRPOLYWEATHER_DB_PATHPOLYWEATHER_STATE_STORAGE_MODEPOLYWEATHER_EVENT_STORE
前端:
POLYWEATHER_API_BASE_URLPOLYWEATHER_OPS_ADMIN_EMAILS(如果启用/ops页面级管理员守卫)
如果启用登录:
NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEYSUPABASE_URLSUPABASE_ANON_KEYSUPABASE_SERVICE_ROLE_KEY
4.2 L2:功能开关
这些变量一般不敏感,但会决定功能是否启用。
例如:
POLYWEATHER_AUTH_ENABLEDPOLYWEATHER_AUTH_REQUIREDPOLYWEATHER_AUTH_REQUIRE_SUBSCRIPTIONPOLYWEATHER_OPS_ADMIN_EMAILSPOLYWEATHER_STATE_STORAGE_MODEPOLYWEATHER_EVENT_STOREPOLYWEATHER_REDIS_REQUIREDPOLYWEATHER_PAYMENT_ENABLEDPOLYWEATHER_PAYMENT_RPC_URLS_BY_CHAIN_JSONPOLYGON_WALLET_WATCH_ENABLEDPOLYWEATHER_TURNSTILE_BYPASSPOLYWEATHER_TURNSTILE_ENFORCE_ACTIONPOLYWEATHER_TURNSTILE_REQUIRE_PAYMENT_SUBMITTELEGRAM_ALERT_PUSH_ENABLEDTELEGRAM_MARKET_FOCUS_DIGEST_ENABLED
4.3 L3:运行调优项
这些一般不需要在第一天就改。
例如:
- 各类
*_TTL_SEC - 各类
*_TIMEOUT_SEC - 各类
*_COOLDOWN_SEC - 各类
*_INTERVAL_SEC TELEGRAM_ALERT_MIN_TRIGGER_COUNTTELEGRAM_ALERT_MIN_SEVERITYTELEGRAM_ALERT_MISPRICING_ONLYTELEGRAM_ALERT_MISPRICING_INTERVAL_SECTELEGRAM_MARKET_FOCUS_DIGEST_INTERVAL_SECTELEGRAM_MARKET_FOCUS_DIGEST_TOP_NPOLYWEATHER_PAYMENT_RPC_URLSPOLYWEATHER_PAYMENT_RPC_URLS_BY_CHAIN_JSONTAF_CACHE_TTL_SECPOLYWEATHER_REDIS_URLPOLYWEATHER_REDIS_STREAM_KEYPOLYWEATHER_REDIS_STREAM_MAXLEN
策略:
- 先用默认值
- 出现性能或运维问题时再调
4.4 L4:敏感项
这些变量不应写进公开文档截图,也不应提交到仓库。
例如:
TELEGRAM_BOT_TOKENSUPABASE_SERVICE_ROLE_KEYPOLYWEATHER_BACKEND_ENTITLEMENT_TOKENPOLYWEATHER_DASHBOARD_ACCESS_TOKENNEXT_PUBLIC_WALLETCONNECT_PROJECT_IDPOLYWEATHER_TURNSTILE_SECRET_KEYPOLYWEATHER_R2_ACCESS_KEY_IDPOLYWEATHER_R2_SECRET_ACCESS_KEY
5. 推荐部署矩阵
5.1 VPS / Docker(后端 + Bot)
建议放这些:
- 根
.env的后端项 - 所有 secrets
- Bot / 支付 / watcher 配置
5.2 前端容器(Docker Compose)
前端与后端一起以 Docker Compose 部署,环境变量分两类来源:
运行时变量(.env 或 compose environment 块):
POLYWEATHER_API_BASE_URL(容器内使用http://polyweather_web:8000)POLYWEATHER_AUTH_ENABLEDPOLYWEATHER_AUTH_REQUIREDPOLYWEATHER_OPS_ADMIN_EMAILSPOLYWEATHER_DASHBOARD_ACCESS_TOKENPOLYWEATHER_BACKEND_ENTITLEMENT_TOKEN
构建期变量(GitHub Secrets → CI build-and-push → frontend/Dockerfile 的 ARG,改了必须重新构建镜像):
NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEYNEXT_PUBLIC_SITE_URLNEXT_PUBLIC_WALLETCONNECT_PROJECT_IDNEXT_PUBLIC_WALLETCONNECT_POLYGON_RPC_URLNEXT_PUBLIC_PAYMENT_ALLOWED_HOSTSNEXT_PUBLIC_TURNSTILE_SITE_KEYNEXT_PUBLIC_POLYWEATHER_APP_ANALYTICSNEXT_PUBLIC_POLYWEATHER_WEB_VITALSNEXT_PUBLIC_POLYWEATHER_EAGER_CITY_SUMMARIES
说明:
/ops现在是前后端双层限制:- 前端页面入口读取
POLYWEATHER_OPS_ADMIN_EMAILS - 后端写接口同样读取
POLYWEATHER_OPS_ADMIN_EMAILS
- 前端页面入口读取
- 因此,前端容器和后端容器两侧都应配置相同的管理员邮箱白名单。
不要把后端专用密钥全搬进前端容器。
5.3 GitHub Actions
当前 CI 已配置自动部署,需要的 secrets 见 .github/workflows/ci.yml:
VPS_SSH_KEY/VPS_HOST/VPS_USER/GHCR_PAT(SSH 部署到 VPS)CLOUDFLARE_API_TOKEN/CLOUDFLARE_ZONE_ID(同步 Cloudflare Cache Rules)NEXT_PUBLIC_TURNSTILE_SITE_KEY(构建期注入前端镜像)- 前端构建期
NEXT_PUBLIC_*(注入前端镜像)
5.4 Cloudflare 免费能力
Turnstile:
NEXT_PUBLIC_TURNSTILE_SITE_KEY是浏览器可见的 site key,属于前端构建期变量;改动后需要重新构建前端镜像。POLYWEATHER_TURNSTILE_SECRET_KEY只放 VPS / Docker.env,用于 Next API Route 服务端校验。POLYWEATHER_TURNSTILE_BYPASS=true可在排障时临时关闭校验。- 支付 tx 提交默认不强制二次 Turnstile,因为 Cloudflare token 是一次性校验;订单创建已做校验。只有确认 UX 能支持二次挑战时,才设置
POLYWEATHER_TURNSTILE_REQUIRE_PAYMENT_SUBMIT=true。
R2:
POLYWEATHER_R2_ACCOUNT_IDPOLYWEATHER_R2_BUCKETPOLYWEATHER_R2_ACCESS_KEY_IDPOLYWEATHER_R2_SECRET_ACCESS_KEYPOLYWEATHER_R2_REGION=autoPOLYWEATHER_R2_ARCHIVE_SOURCE=redis
归档脚本只读 Redis Stream 或 SQLite,不删除热路径数据:
python scripts/archive_realtime_events_to_r2.py --date 2026-06-16 --dry-run
python scripts/archive_realtime_events_to_r2.py --date 2026-06-16
6. 最小部署示例
6.1 前端最小变量
POLYWEATHER_API_BASE_URL=https://your-backend.example.com
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key
POLYWEATHER_AUTH_ENABLED=true
POLYWEATHER_AUTH_REQUIRED=true
NEXT_PUBLIC_POLYWEATHER_APP_ANALYTICS=false
NEXT_PUBLIC_POLYWEATHER_WEB_VITALS=false
NEXT_PUBLIC_POLYWEATHER_EAGER_CITY_SUMMARIES=false
6.2 后端最小变量
TELEGRAM_BOT_TOKEN=...
TELEGRAM_CHAT_ID=...
POLYWEATHER_RUNTIME_DATA_DIR=/var/lib/polyweather
POLYWEATHER_DB_PATH=/var/lib/polyweather/polyweather.db
POLYWEATHER_STATE_STORAGE_MODE=sqlite
POLYWEATHER_EVENT_STORE=redis
POLYWEATHER_REDIS_URL=redis://polyweather_redis:6379/0
POLYWEATHER_REDIS_STREAM_MAXLEN=50000
POLYWEATHER_REDIS_REQUIRED=true
UID=1000
GID=1000
POLYWEATHER_AUTH_ENABLED=true
POLYWEATHER_AUTH_REQUIRED=false
POLYWEATHER_OPS_ADMIN_EMAILS=yhrsc30@gmail.com
TAF_CACHE_TTL_SEC=900
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=...
SUPABASE_SERVICE_ROLE_KEY=...
POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN=...
TELEGRAM_ALERT_PUSH_ENABLED=true
TELEGRAM_ALERT_PUSH_INTERVAL_SEC=300
TELEGRAM_ALERT_PUSH_COOLDOWN_SEC=1800
TELEGRAM_ALERT_MIN_TRIGGER_COUNT=2
TELEGRAM_ALERT_MIN_SEVERITY=medium
TELEGRAM_ALERT_MISPRICING_ONLY=true
TELEGRAM_ALERT_MISPRICING_INTERVAL_SEC=7200
TELEGRAM_MARKET_FOCUS_DIGEST_ENABLED=true
TELEGRAM_MARKET_FOCUS_DIGEST_INTERVAL_SEC=1800
TELEGRAM_MARKET_FOCUS_DIGEST_TOP_N=5
POLYWEATHER_BACKEND_URL=http://polyweather_web:8000
说明:
UID/GID主要给 Linux Docker 主机用,避免容器把运行文件写成 root 所有。- Windows / macOS 一般可以直接保留默认值。
POLYWEATHER_RUNTIME_DATA_DIR建议放在仓库外,例如/var/lib/polyweather。docker-compose.yml会把这个目录同时挂载到容器内的/var/lib/polyweather和/app/data,兼容现有缓存与 SQLite 路径。POLYWEATHER_STATE_STORAGE_MODE当前线上推荐直接使用sqlite。POLYWEATHER_EVENT_STORE=redis表示实时观测 patch 使用 Redis Stream 做短窗口 replay 和多 worker fanout;本地或单进程可改为sqlite。POLYWEATHER_REDIS_REQUIRED=true表示 Redis 不可用时后端启动失败,避免生产环境广播不可 replay 的实时事件;开发环境可设为false允许回退 SQLite。POLYWEATHER_PAYMENT_RPC_URLS支持默认链的逗号分隔多个 RPC;如果暂时只用单 RPC,也可以继续只配POLYWEATHER_PAYMENT_RPC_URL。POLYWEATHER_PAYMENT_RPC_URLS_BY_CHAIN_JSON用于多链支付,例如同时支持 Polygon 和 Ethereum 主网 USDC。- 机器人市场监控包含
关键提醒与关注清单:关键提醒逐城判断并受冷却控制,关注清单每轮先扫描完整城市列表,再按全局 Top N 推送;同一轮已经触发关键提醒的城市不会重复出现在关注清单里。 TELEGRAM_MARKET_FOCUS_DIGEST_INTERVAL_SEC表示主动推送间隔,默认1800秒(30 分钟)。
说明:
- 这层只负责把结构化信号改写成短摘要,不替代真实模型、机场锚点和结算逻辑。
6.3 支付多链配置示例
当前生产推荐:
- 默认链:Polygon
chain_id=137,继续承载 checkout 合约支付。 - 补充链:Ethereum Mainnet
chain_id=1,正式支持 USDC 直转确认。 - 前端创建支付 intent 时会提交用户选择的
chain_id;后端确认时按 intent 的链和 token 查询对应 RPC。
POLYWEATHER_PAYMENT_ENABLED=true
POLYWEATHER_PAYMENT_CHAIN_ID=137
POLYWEATHER_PAYMENT_RPC_URL=https://polygon-rpc.com
POLYWEATHER_PAYMENT_RPC_URLS=https://polygon-rpc.com,https://polygon-bor-rpc.publicnode.com
POLYWEATHER_PAYMENT_RPC_URLS_BY_CHAIN_JSON={"137":["https://polygon-rpc.com","https://polygon-bor-rpc.publicnode.com"],"1":["https://ethereum-rpc.example"]}
POLYWEATHER_PAYMENT_RECEIVER_CONTRACT=0x<polygon_checkout_contract>
POLYWEATHER_PAYMENT_DIRECT_RECEIVER_ADDRESS=0x<treasury_or_receiver_wallet>
POLYWEATHER_PAYMENT_ACCEPTED_TOKENS_JSON=[{"code":"usdc_polygon","symbol":"USDC","name":"USDC on Polygon","chain_id":137,"chain_code":"polygon","chain_name":"Polygon","address":"0x3c499c542cef5e3811e1192ce70d8cc03d5c3359","decimals":6,"receiver_contract":"0x<polygon_checkout_contract>","direct_receiver_address":"0x<treasury_or_receiver_wallet>","is_default":true},{"code":"usdc_ethereum","symbol":"USDC","name":"USDC on Ethereum","chain_id":1,"chain_code":"ethereum","chain_name":"Ethereum Mainnet","address":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","decimals":6,"direct_receiver_address":"0x<treasury_or_receiver_wallet>","supports_contract_checkout":false,"supports_direct_transfer":true,"explorer_tx_url":"https://etherscan.io/tx/{tx_hash}"}]
注意:
POLYWEATHER_PAYMENT_CHAIN_ID只是默认链,不代表只支持这一条链。POLYWEATHER_PAYMENT_ACCEPTED_TOKENS_JSON里每个 token 必须有明确chain_id。- Ethereum 行如果没有部署 checkout 合约,必须设置
supports_contract_checkout=false,前端会显示手动转账并阻止钱包合约支付。 - 私有 RPC URL 带 API key 时应放入真实
.env或密钥管理,不要提交。
6.5 机器人市场监控建议配置
这套配置围绕市场本身做两类推送:
关键提醒:实时错价/触发条件满足时发送关注清单:按亚洲时区定时推送当日重点市场摘要
推荐值:
TELEGRAM_ALERT_PUSH_ENABLED=true
TELEGRAM_ALERT_PUSH_INTERVAL_SEC=300
TELEGRAM_ALERT_PUSH_COOLDOWN_SEC=1800
TELEGRAM_ALERT_MIN_TRIGGER_COUNT=2
TELEGRAM_ALERT_MIN_SEVERITY=medium
TELEGRAM_ALERT_MISPRICING_ONLY=true
TELEGRAM_ALERT_MISPRICING_INTERVAL_SEC=7200
TELEGRAM_MARKET_FOCUS_DIGEST_ENABLED=true
TELEGRAM_MARKET_FOCUS_DIGEST_INTERVAL_SEC=1800
TELEGRAM_MARKET_FOCUS_DIGEST_TOP_N=5
说明:
TELEGRAM_ALERT_MISPRICING_ONLY=true表示关键提醒优先围绕错价/市场触发,不把机器人做成泛通知器。TELEGRAM_MARKET_FOCUS_DIGEST_INTERVAL_SEC=1800表示频道每 30 分钟主动推送一轮全局机会清单;每轮会先扫描完整TELEGRAM_ALERT_CITIES,再选 Top N。TELEGRAM_MARKET_FOCUS_DIGEST_TOP_N=5建议先保持较小,避免机器人一次推太多城市。
7. 当前建议的运维规则
7.1 仓库中允许存在
.env.example.env.secrets.examplefrontend/.env.example
7.2 仓库中不应提交
.env.env.local- 任何带真实 token / key 的配置文件
7.3 截图与共享规则
以下值一旦出现在截图或聊天里,建议视为泄露并轮换:
SUPABASE_SERVICE_ROLE_KEYPOLYWEATHER_BACKEND_ENTITLEMENT_TOKENTELEGRAM_BOT_TOKEN- 第三方私有 API Key
8. 如何收口配置复杂度
如果你觉得变量仍然太多,正确的做法不是一刀删掉,而是:
- 把“功能开关”和“调优参数”分开看
- 保持
.env.example中:- 最小启动项
- 常用功能开关
- 默认调优值
- 让不常改的高阶参数继续留默认
也就是说:
- 使用者只需要先关心 10-20 个关键变量
- 其余变量保持默认即可
9. 当前已经完成的配置治理
- 根
.env.example收口 .env.secrets.example新增- 前端
.env.example收口 - 运行时配置校验脚本新增
/ops管理员白名单与前后端职责边界已明确- 支付运行态与多 RPC 配置支持
- 运行态 SQLite 迁移配置支持
10. 配置校验命令
在不启动服务的情况下,你可以直接检查配置:
python scripts/validate_runtime_env.py --component web
python scripts/validate_runtime_env.py --component bot