Refresh VPS deployment docs

This commit is contained in:
2569718930@qq.com
2026-06-14 23:21:03 +08:00
parent ef21a5808e
commit 2e11e3fd56
9 changed files with 137 additions and 191 deletions
+1 -1
View File
@@ -71,6 +71,6 @@ frontend/.next-start.log
tmp_apikey.js
tmp_obs.js
tmp_rctp.html
playwright-home-check.png
.codex-backend-*.log
frontend-next-*.log
*.stackdump
+1 -1
View File
@@ -7,7 +7,7 @@
## 1. 基础信息
- 后端直连:`http://127.0.0.1:8000`
- 前端 BFF`https://polyweather-pro.vercel.app/api/*`
- 前端 BFF`https://polyweather.top/api/*`
- 返回格式:`application/json`
## 2. 请求链路
+20 -15
View File
@@ -17,7 +17,7 @@ PolyWeather 的环境变量很多,但不是所有变量都属于同一层级
3. 平台侧真实密钥
放在:
- VPS / Docker `.env`
- Vercel Environment Variables
- GitHub Secrets(构建期 `NEXT_PUBLIC_*` 通过 build-arg 注入前端镜像)
- GitHub Secrets(如需要)
## 2. 为什么要拆
@@ -66,7 +66,7 @@ PolyWeather 的环境变量很多,但不是所有变量都属于同一层级
用途:
- 前端本地开发与 Vercel 环境变量模板
- 前端本地开发与容器运行时环境变量模板
## 4. 配置分级
@@ -165,20 +165,27 @@ PolyWeather 的环境变量很多,但不是所有变量都属于同一层级
- 所有 secrets
- Bot / 支付 / watcher 配置
### 5.2 Vercel前端)
### 5.2 前端容器(Docker Compose
建议只放前端真正需要的变量
前端与后端一起以 Docker Compose 部署,环境变量分两类来源
- `POLYWEATHER_API_BASE_URL`
- `NEXT_PUBLIC_SUPABASE_URL`
- `NEXT_PUBLIC_SUPABASE_ANON_KEY`
**运行时变量**`.env` 或 compose `environment` 块):
- `POLYWEATHER_API_BASE_URL`(容器内使用 `http://polyweather_web:8000`
- `POLYWEATHER_AUTH_ENABLED`
- `POLYWEATHER_AUTH_REQUIRED`
- `POLYWEATHER_OPS_ADMIN_EMAILS`
- `POLYWEATHER_DASHBOARD_ACCESS_TOKEN`
- `POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN`
**构建期变量**GitHub Secrets → CI `build-and-push``frontend/Dockerfile``ARG`,改了必须重新构建镜像):
- `NEXT_PUBLIC_SUPABASE_URL`
- `NEXT_PUBLIC_SUPABASE_ANON_KEY`
- `NEXT_PUBLIC_SITE_URL`
- `NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID`
- `NEXT_PUBLIC_WALLETCONNECT_POLYGON_RPC_URL`
- `NEXT_PUBLIC_PAYMENT_ALLOWED_HOSTS`
- `NEXT_PUBLIC_POLYWEATHER_APP_ANALYTICS`
- `NEXT_PUBLIC_POLYWEATHER_WEB_VITALS`
- `NEXT_PUBLIC_POLYWEATHER_EAGER_CITY_SUMMARIES`
@@ -188,19 +195,17 @@ PolyWeather 的环境变量很多,但不是所有变量都属于同一层级
- `/ops` 现在是前后端双层限制:
- 前端页面入口读取 `POLYWEATHER_OPS_ADMIN_EMAILS`
- 后端写接口同样读取 `POLYWEATHER_OPS_ADMIN_EMAILS`
- 因此,Vercel 和 VPS / Docker 两侧都应配置相同的管理员邮箱白名单。
- 因此,前端容器和后端容器两侧都应配置相同的管理员邮箱白名单。
不要把后端专用密钥全搬进 Vercel
不要把后端专用密钥全搬进前端容器
### 5.3 GitHub Actions
当前 CI 不需要大规模 secrets。
当前 CI 已配置自动部署,需要的 secrets 见 `.github/workflows/ci.yml`
如果未来要做自动部署,再考虑:
- `VERCEL_TOKEN`
- `VERCEL_ORG_ID`
- `VERCEL_PROJECT_ID`
- `VPS_SSH_KEY` / `VPS_HOST` / `VPS_USER` / `GHCR_PAT`SSH 部署到 VPS
- `CLOUDFLARE_API_TOKEN` / `CLOUDFLARE_ZONE_ID`(同步 Cloudflare Cache Rules
- 前端构建期 `NEXT_PUBLIC_*`(注入前端镜像)
## 6. 最小部署示例
+102 -146
View File
@@ -1,65 +1,97 @@
# 前端部署配置(Vercel
# 前端部署配置(Docker / VPS
最后更新:`2026-05-29`
最后更新:`2026-06-14`
本文只覆盖 `frontend` 目录对应的 Next.js 前端部署。
本文只覆盖 `frontend` 目录对应的 Next.js 前端部署。前端当前不再使用 Vercel,统一与后端一起以 Docker Compose 形式部署在同一台 VPS 上,前面挂 Cloudflare + Nginx。
## 一、部署目标
推荐方案:
当前方案:
1. GitHub Actions 负责 `CI`
2. Vercel 负责前端 `CD`
3. FastAPI 后端单独部署在 VPS / Docker 主机
1. GitHub Actions 负责 `CI``python-quality` + `frontend-quality`
2. `build-and-push` job 把前端构建为 Docker 镜像 `ghcr.io/yangyuan-zhen/polyweather-frontend`
3. `deploy` job 通过 SSH 把 `deploy.sh` 推到 VPS,由 VPS 拉取新镜像并滚动更新
前端本身不直接访问天气源,而是通过 Next Route Handlers 转发到后端:
1. 浏览器 -> Vercel 上的 Next.js 前端
2. Next `/api/*` -> `POLYWEATHER_API_BASE_URL`
3. FastAPI 后端 -> 分析 / 支付 / 鉴权服务
1. 浏览器 → Cloudflare → Nginx → 前端容器(Next.js standalone
2. Next `/api/*` `POLYWEATHER_API_BASE_URL`(容器内默认 `http://polyweather_web:8000`
3. FastAPI 后端 分析 / 支付 / 鉴权服务
实时图表同样走 Next Route Handler / rewrite
实时图表同样走 Next Route Handler
1. 浏览器 `EventSource` -> `/api/events?cities=...&since_revision=...`
1. 浏览器 `EventSource` `/api/events?cities=...&since_revision=...`
2. Next 转发到 FastAPI `/api/events`
3. FastAPI 从 Redis Stream / SQLite event log replay 后进入 live SSE
## 二、Vercel 项目设置
## 二、镜像与构建
在 Vercel 导入 GitHub 仓库后,使用下面的设置
前端镜像定义在 `frontend/Dockerfile`,三阶段构建
- Framework Preset: `Next.js`
- Root Directory: `frontend`
- Build Command: `npm run build`
- Install Command: `npm ci`
- `deps``npm ci` 安装依赖
- `builder`:通过 `ARG` 注入 `NEXT_PUBLIC_*` 变量后执行 `npm run build`,产出 standalone 产物
- `runner`:只拷贝 `.next/standalone``.next/static``public`,以 `node server.js` 启动
如果仓库已经连接过 Vercel,通常只需要确认 `Root Directory` 仍然是 `frontend`
`NEXT_PUBLIC_*` 变量是在 **构建期** 注入的(见 `frontend/Dockerfile``ARG` 块),CI 在 `.github/workflows/ci.yml``build-and-push` job 里从 GitHub Secrets 读取并作为 `--build-arg` 传入。修改这类变量必须重新构建镜像,仅改运行时环境无效
## 三、最小必填环境变量
## 三、Compose 服务
只部署天气看板和基础登录时,先填下面 4 项
前端在 `docker-compose.yml` 中对应 `polyweather_frontend` 服务
```env
POLYWEATHER_API_BASE_URL=https://<your-fastapi-host>
NEXT_PUBLIC_SUPABASE_URL=https://<your-project>.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=<your-anon-key>
POLYWEATHER_AUTH_ENABLED=true
- 镜像:`ghcr.io/yangyuan-zhen/polyweather-frontend:${IMAGE_TAG:-latest}`
- 容器内监听 `:3000`,映射到宿主 `127.0.0.1:3001`
- 健康检查:`wget -qO- http://$(hostname):3000`
- 运行时环境变量(非 `NEXT_PUBLIC_*` 的那部分)通过 compose `environment` 注入,例如:
- `POLYWEATHER_API_BASE_URL=http://polyweather_web:8000`(容器内走后端服务名)
- `POLYWEATHER_AUTH_ENABLED` / `POLYWEATHER_AUTH_REQUIRED`
- `POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN`
- `POLYWEATHER_OPS_ADMIN_EMAILS`
`POLYWEATHER_API_BASE_URL` **禁止** 指向前端站点自身(`polyweather.top`),否则会形成回环。`deploy.sh` 里的 `validate_frontend_api_base_url` 会在部署前拦截。容器内应使用 `http://polyweather_web:8000`
## 四、部署流程(`deploy.sh`
生产部署由 GitHub Actions 在 `main` push 时触发,关键步骤:
1. SSH 登录 VPS,用 GHCR PAT 登录镜像仓库
2. `git fetch origin main && git reset --hard origin/main` 同步仓库(含 `docker-compose.yml`
3. 同步 `data/city_thread_ids.json` 到运行态目录
4. `docker compose pull` 拉取新镜像(带重试)
5. 按顺序滚动更新:`redis``web` + `bot``collector``warmer``frontend`
6. 每步后做本地健康检查;前端额外等待 `/terminal``/api/scan/terminal` 就绪
7. 公网 smoke check`https://api.polyweather.top/healthz``https://polyweather.top/api/cities``https://www.polyweather.top/`
8. 任意一步失败自动回滚到上一个镜像 tag(记录在 `/var/lib/polyweather/.current_tag`
部署失败时优先看 `deploy.sh` 输出里哪一步打了 `❌`,并检查 `docker compose logs polyweather_frontend`
## 五、最小必填配置
只部署天气看板和基础登录时,至少需要:
构建期(CI Secrets,对应 `frontend/Dockerfile``ARG`):
```
NEXT_PUBLIC_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY
NEXT_PUBLIC_SITE_URL=https://polyweather.top
```
建议显式补
运行期(`.env` 或 compose `environment`
```env
POLYWEATHER_API_BASE_URL=http://polyweather_web:8000
POLYWEATHER_AUTH_ENABLED=true
POLYWEATHER_AUTH_REQUIRED=true
POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN=<与后端共享>
```
说明:
- `POLYWEATHER_API_BASE_URL`:前端所有 `/api/*` Route Handler 转发时依赖它,没填会直接返回 500。
- `NEXT_PUBLIC_SUPABASE_URL` / `NEXT_PUBLIC_SUPABASE_ANON_KEY`Supabase 客户端依赖它们。
- `POLYWEATHER_AUTH_ENABLED`:关闭时,前端不会启用登录能力
- `POLYWEATHER_AUTH_REQUIRED`:控制 middleware 是否强制登录。
- `POLYWEATHER_API_BASE_URL`:前端所有 `/api/*` Route Handler 转发时依赖它,没填或填错会直接返回 500。
- `NEXT_PUBLIC_SUPABASE_URL` / `NEXT_PUBLIC_SUPABASE_ANON_KEY`Supabase 客户端依赖它们,构建期注入
- `POLYWEATHER_AUTH_ENABLED` / `POLYWEATHER_AUTH_REQUIRED`:控制 middleware 是否强制登录
## 、按功能启用的可选环境变量
## 、按功能启用的可选环境变量
### 1. 分享式看板
@@ -69,60 +101,45 @@ POLYWEATHER_DASHBOARD_ACCESS_TOKEN=
设置后,可通过 `/?access_token=<token>` 打开带令牌的看板入口。
### 2. 前后端 entitlement 校验
### 2. 钱包支付(构建期)
```env
POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN=
```
仅当后端开启 entitlement / 订阅校验时需要。
### 3. 钱包支付
```env
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=
NEXT_PUBLIC_WALLETCONNECT_POLYGON_RPC_URL=https://polygon-bor-rpc.publicnode.com
NEXT_PUBLIC_PAYMENT_ALLOWED_HOSTS=polyweather.top,www.polyweather.top
```
如果不启用钱包支付,可以留空。
### 4. `/ops` 管理员页面守卫
### 3. `/ops` 管理员页面守卫
```env
POLYWEATHER_OPS_ADMIN_EMAILS=yhrsc30@gmail.com
```
说明:
`/ops` 页面入口会读取管理员邮箱白名单,前端和后端容器都应配置相同的值。
- `/ops` 现在不是只有后端接口限制,前端页面入口也会读取管理员邮箱白名单。
- 因此前端部署到 Vercel 时,也应配置 `POLYWEATHER_OPS_ADMIN_EMAILS`
### 4. Telegram 入口(构建期)
### 5. Telegram 入口
```env
```
NEXT_PUBLIC_TELEGRAM_GROUP_URL=https://t.me/<your_group>
NEXT_PUBLIC_TELEGRAM_BOT_URL=https://t.me/polyyuanbot
NEXT_PUBLIC_TELEGRAM_LOGIN_BOT_USERNAME=polyyuanbot
```
只影响按钮跳转,不影响核心页面加载。
### 6. 前端观测与预热开关(推荐默认关闭)
### 5. 前端观测与预热开关(推荐默认关闭)
```env
```
NEXT_PUBLIC_POLYWEATHER_APP_ANALYTICS=false
NEXT_PUBLIC_POLYWEATHER_WEB_VITALS=false
NEXT_PUBLIC_POLYWEATHER_EAGER_CITY_SUMMARIES=false
```
说明:
## 七、支付配置与旧镜像治理
- `NEXT_PUBLIC_POLYWEATHER_APP_ANALYTICS=false`:关闭前端自建埋点。
- `NEXT_PUBLIC_POLYWEATHER_WEB_VITALS=false`:关闭前端 Web Vitals 上报。
- `NEXT_PUBLIC_POLYWEATHER_EAGER_CITY_SUMMARIES=false`:关闭首页全量城市 summary 预热,避免白白消耗 Vercel function / edge 成本。
## 五、支付配置与旧部署治理
支付区现在有一层额外防护:
支付区有一层额外防护:
1. 用户点击支付前,前端会重新请求 `/api/payments/config`
2. 若发现 `receiver_contract` 与页面旧状态不一致,会自动切换到最新地址
@@ -130,61 +147,19 @@ NEXT_PUBLIC_POLYWEATHER_EAGER_CITY_SUMMARIES=false
4. 多链支付时,前端会展示后端返回的网络列表,并把用户选择的 `chain_id` 传给后端创建 intent
5. Ethereum 主网 USDC 当前走手动直转确认,前端不会把它当成 Polygon checkout 合约支付
这层防护的目的,是降低以下事故概率:
这层防护降低以下事故概率:
- 用户使用长期未刷新的旧标签页
- 命中旧 deployment URL
- 页面本地状态残留旧收款地址
- 用户在钱包默认网络(例如 Ethereum)付款,但系统按 Polygon intent 查账
如果变更过支付收款地址,建议同步执行:
如果变更过支付收款地址,由于前端镜像是构建期注入地址,需要触发一次 `main` push(或重新运行 deploy workflow)来发布新镜像;浏览器侧靠 `/api/payments/config` 的运行时校验兜底,无需等待所有用户刷新。
1. 在 Vercel 对当前 production 做一次 redeploy
2. 删除明显过期、可能还带旧支付配置的旧 deployment
3.`Settings -> Security -> Deployment Retention Policy` 中收紧旧部署保留周期
## 八、不要放进前端容器的变量
## 六、推荐的三套配置口径
这些属于后端私密配置,不应该放到前端服务:
### 1. 公开游客模式
```env
POLYWEATHER_API_BASE_URL=https://api.example.com
POLYWEATHER_AUTH_ENABLED=false
POLYWEATHER_AUTH_REQUIRED=false
```
适合公开演示站。
### 2. 正常登录模式
```env
POLYWEATHER_API_BASE_URL=https://api.example.com
NEXT_PUBLIC_SUPABASE_URL=https://<project>.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon-key>
POLYWEATHER_AUTH_ENABLED=true
POLYWEATHER_AUTH_REQUIRED=true
```
适合正式前端站点。
### 3. 登录 + entitlement 联动
```env
POLYWEATHER_API_BASE_URL=https://api.example.com
NEXT_PUBLIC_SUPABASE_URL=https://<project>.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon-key>
POLYWEATHER_AUTH_ENABLED=true
POLYWEATHER_AUTH_REQUIRED=true
POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN=<shared-token>
```
适合前后端都启用了会员/订阅保护的生产环境。
## 七、不要放进 Vercel 的变量
这些属于后端私密配置,不应该放到前端项目:
- `SUPABASE_SERVICE_ROLE_KEY`
- `SUPABASE_SERVICE_ROLE_KEY`(除非前端 Route Handler 明确需要,且仅以非 `NEXT_PUBLIC_` 形式注入容器)
- `TELEGRAM_BOT_TOKEN`
- `POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN` 以外的后端 secret
- 支付签名私钥 / 交易私钥 / 任何 bot 凭据
@@ -194,74 +169,55 @@ POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN=<shared-token>
- `NEXT_PUBLIC_*` 会暴露给浏览器
- 只有明确允许前端公开使用的值,才应加 `NEXT_PUBLIC_`
## 、上线前检查
## 、上线前检查
Vercel 部署前至少确认:
部署前至少确认:
1. `POLYWEATHER_API_BASE_URL` 指向可访问的后端生产地址
2. `frontend/.env.example` 和 Vercel Project Settings 中的实际值一致
1. `POLYWEATHER_API_BASE_URL` 指向容器内后端服务名 `http://polyweather_web:8000`**不是** `polyweather.top`
2. CI Secrets 中的 `NEXT_PUBLIC_*` 值与预期一致(构建期注入,改了要重新构建镜像)
3. GitHub Actions 中 `frontend-quality` 已通过
4. 如果启用鉴权,Supabase redirect URL 已包含前端域名
5. `GET /api/payments/config` 返回的是当前最新地址,而不是旧收款合约
6. 如果启用了 `/ops`,确认 `POLYWEATHER_OPS_ADMIN_EMAILS` 已在 Vercel 与后端同时配置
7. 确认 `/api/events` 没有被 CDN 缓存或压缩成普通 JSON;它必须保持 `text/event-stream`
6. 如果启用了 `/ops`,确认 `POLYWEATHER_OPS_ADMIN_EMAILS` 已在前端与后端容器同时配置
7. 确认 `/api/events` 没有被 Cloudflare / Nginx 缓存或压缩成普通 JSON;它必须保持 `text/event-stream`
## 、常见问题
## 、常见问题
### 1. 页面打开后 API 全部 500
先检查
先检查容器内 `POLYWEATHER_API_BASE_URL` 是否指向 `http://polyweather_web:8000`,以及 `polyweather_web` 容器是否健康。
```env
POLYWEATHER_API_BASE_URL
```
这是最常见原因。
### 2. Vercel 构建通过,但登录失败
### 2. 构建通过,但登录失败
先检查:
- `NEXT_PUBLIC_SUPABASE_URL`
- `NEXT_PUBLIC_SUPABASE_ANON_KEY`
- Supabase 项目里的站点 URL / redirect URL
- 构建期注入的 `NEXT_PUBLIC_SUPABASE_URL` / `NEXT_PUBLIC_SUPABASE_ANON_KEY`
- Supabase 项目里的站点 URL / redirect URL 是否包含前端域名
### 3. 钱包入口显示未配置
检查
检查构建期 `NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID` 是否注入(前端镜像需要重新构建)。
```env
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID
```
### 4. 改了 `NEXT_PUBLIC_*` 但线上没生效
是钱包连接的必需项
类变量是构建期注入的。仅改 CI Secrets 不会更新已部署镜像,需要重新触发 `build-and-push` + `deploy`(即一次 `main` push,或手动重跑 deploy workflow
## 十、Vercel 成本与节流建议
## 十、成本与节流建议
### 1. 建议先关闭的项目级能力
### 1. Cloudflare 缓存规则
- `Web Analytics`
- `Speed Insights`
前端通过 `next.config.mjs``headers()` 为静态资源(`_next/static`、图片、字体)设置 `Cache-Control: public, max-age=31536000, immutable`,并为公共页面设置 `s-maxage=600, stale-while-revalidate=3600`。CI 的 `cloudflare-cache-rules` job 会同步 Cloudflare Cache Rules(见 `scripts/configure_cloudflare_free.py`)。
它们对排查前端体验有价值,但在 Hobby / 低预算阶段会额外消耗数据点和边缘资源。
### 2. Cloudflare WAF 规则
### 2. 建议加的 Firewall 自定义规则
如果你的 Next.js 项目根本不提供 WordPress / PHP 路径,建议在 Vercel Firewall 里先 `Log``Deny` 这条规则:
如果发现大量 WordPress / PHP 扫描流量命中 Next.js(实际并不提供这些路径),建议在 Cloudflare WAF 中先 `Log``Deny` 这条规则:
```regex
(^/(wp-admin|wp-includes|wp-content|wp-login|wordpress|xmlrpc\.php))|\.php($|\?)
```
目的:
目的:在边缘层提前拦截扫描流量,避免无效请求继续触发 Nginx、Next.js middleware 与 route handler。
- 在边缘层提前拦截 WordPress / PHP 扫描流量
- 避免无效请求继续触发 middleware 与 route handler
### 3. SSE 路径不要进缓存
### 3. 建议的上线前检查
除了功能本身,额外确认:
1. `Web Analytics``Speed Insights` 是否真的关闭
2. `NEXT_PUBLIC_POLYWEATHER_APP_ANALYTICS` / `NEXT_PUBLIC_POLYWEATHER_WEB_VITALS` / `NEXT_PUBLIC_POLYWEATHER_EAGER_CITY_SUMMARIES` 是否保持关闭
3. Firewall 自定义规则是否已从 `Log` 切到 `Deny`
`/api/events` 必须保持 `text/event-stream`Cloudflare 和 Nginx 都不应缓存或压缩它。检查 Nginx 配置(`deploy/nginx/polyweather.conf`)中对 `/api/events``proxy_buffering off`
+1 -1
View File
@@ -6,7 +6,7 @@
前端入口:
- `https://polyweather-pro.vercel.app/ops`
- `https://polyweather.top/ops`
## 2. 权限
+2 -2
View File
@@ -15,7 +15,7 @@
- `https://<project-ref>.supabase.co/auth/v1/callback`
3. `Auth -> URL Configuration` 添加:
- 站点 URL(生产域名)
- 回调 URL(例如 `https://polyweather-pro.vercel.app/auth/callback`
- 回调 URL(例如 `https://polyweather.top/auth/callback`
## 3. 数据库脚本
@@ -45,7 +45,7 @@
## 4. 环境变量
### 4.1 前端(Vercel / frontend/.env.local
### 4.1 前端(Docker 容器 / frontend/.env.local
```env
NEXT_PUBLIC_SUPABASE_URL=
+3 -3
View File
@@ -7,7 +7,7 @@ PolyWeather(仓库:`yangyuan-zhen/PolyWeather`)定位为**面向温度类
> **2026-05-29 更新**:支付 intent 已支持多链 `chain_id`Polygon 继续承载 checkout 合约,Ethereum 主网 USDC 作为直转确认通道;终端图表继续使用 HTTP snapshot + SSE Patch + Redis Stream/SQLite replay 架构。
## 项目概览
PolyWeather 的目标与范围在 README/README_ZH 中定义得较清楚:为温度结算市场提供气象情报(多源采集→融合→概率→对照市场报价),并提供“官方看板(Vercel 前端)+ VPS 后端 + Telegram Bot”。
PolyWeather 的目标与范围在 README/README_ZH 中定义得较清楚:为温度结算市场提供气象情报(多源采集→融合→概率→对照市场报价),并提供“官方看板(Docker/VPS 前端)+ VPS 后端 + Telegram Bot”。
项目主功能可归纳为五层:
**天气层(数据源/采集)**:聚合 51 个城市的实测与预报;支持 AviationWeather METAR/TAF、韩国 AMOS 跑道级观测(首尔/釜山)、中国 AMSC AWOS 跑道端点气温、香港 CoWIN 6087 / HKO、土耳其 MGM、Open-Meteo 多模型与集合预报、美国 MADIS HFMETAR 等。机场类市场仍以 METAR / 机场主站或明确官方结算源为锚点,Wunderground 不描述为物理观测站。
**分析层(DEB/趋势/概率/结算口径)**
@@ -28,7 +28,7 @@ DEBDynamic Error Balancing)基于过去 N 天模型误差(MAE)倒数加
从 README、Docker/Compose、入口脚本与核心模块引用关系,可以抽象出如下模块地图(按“运行时组件”与“Python 域模块”两层描述):
| 层级 | 目录/文件 | 角色定位 | 关键说明 |
| ------------- | ------------------------------------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 运行时组件 | `frontend/` | Next.js 前端(Vercel) | 前端重构报告提到 App Router、Route HandlersBFF)、缓存策略、支付与账户中心等;Scan Terminal 已新增城市决策卡、结构化实况层、页面内存/localStorage 双层缓存、structured detail 小并发队列与完整市场桶映射。 |
| 运行时组件 | `frontend/` | Next.js 前端(Docker / VPS) | 前端重构报告提到 App Router、Route HandlersBFF)、缓存策略、支付与账户中心等;Scan Terminal 已新增城市决策卡、结构化实况层、页面内存/localStorage 双层缓存、structured detail 小并发队列与完整市场桶映射。 |
| 运行时组件 | `web/app.py` + `web/core.py` + `web/routes.py` + `web/analysis_service.py` + `web/scan_terminal_service.py` | FastAPI 后端 API | 已从单文件入口拆为启动入口、核心上下文、路由层、分析服务层;Scan Terminal 侧提供 `/api/city/{name}/detail`,城市结构化分析 默认 30s 超时并支持 stream parse failure 的非流式重试。 |
| 运行时组件 | `bot_listener.py` + `src/bot/*` | Telegram Bot | 入口 `bot_listener.py``start_bot()`,并由 `StartupCoordinator` 启动多个后台 loop。 |
| Python 域模块 | `src/data_collection/*` | 天气采集 + 城市注册 | 采集层已拆为 `weather_sources.py` 编排层 + `open_meteo_cache.py``settlement_sources.py``metar_sources.py``mgm_sources.py``amos_station_sources.py``jma_amedas_sources.py``nws_open_meteo_sources.py``country_networks.py` 等。v1.7.0 已移除 NMC、pogodaiklimat、Meteoblue 数据源。 |
@@ -44,7 +44,7 @@ DEBDynamic Error Balancing)基于过去 N 天模型误差(MAE)倒数加
```mermaid
flowchart TB
subgraph Clients
WEB[Next.js Frontend<br/>Vercel]
WEB[Next.js Frontend<br/>Docker / VPS]
CITYCARD[Scan Terminal City Cards<br/>structured observations + probability mapping]
TG[Telegram Bot<br/>TeleBot + Handlers]
end
+7 -7
View File
@@ -46,7 +46,7 @@ npm ci
npm run dev
```
## Vercel 最小部署配置
## 最小部署配置
只跑看板和基础鉴权时,先填这 4 项:
@@ -97,7 +97,7 @@ NEXT_PUBLIC_POLYWEATHER_WEB_VITALS=false
NEXT_PUBLIC_POLYWEATHER_EAGER_CITY_SUMMARIES=false
```
更完整的 Vercel 配置说明见:
更完整的部署配置说明见:
- [docs/FRONTEND_DEPLOYMENT_ZH.md](/E:/web/PolyWeather/docs/FRONTEND_DEPLOYMENT_ZH.md)
## 路由处理器
@@ -155,7 +155,7 @@ Ops
注意:
- `/ops` 现在是前后端双层管理员限制
- Vercel 前端和后端都应配置相同的 `POLYWEATHER_OPS_ADMIN_EMAILS`
- 前端容器和后端都应配置相同的 `POLYWEATHER_OPS_ADMIN_EMAILS`
- 前端登录邮箱本身不会自动获得管理员权限
## 支付安全补充
@@ -172,7 +172,7 @@ Ops
这意味着:
- 旧标签页风险已明显降低
- 支付地址变更后,仍建议在 Vercel 上 redeploy 当前 production,并清理明显过期 deployment
- 支付地址变更后,由于地址在前端镜像构建期注入,需要触发一次 `main` push(或重跑 deploy workflow)发布新镜像;浏览器侧靠 `/api/payments/config` 的运行时校验兜底
## 缓存行为
@@ -184,11 +184,11 @@ Ops
- 终端图表订阅 `/api/events?cities=...&since_revision=...&replay_limit=按可见城市数动态限制`,接收 `city_observation_patch.v1`;无 patch 超过 2 分钟时,可见图表才触发 60 秒兜底刷新
- 前端只消费 HTTP snapshot + SSE patch,不直接感知 RedisRedis Stream / SQLite event log 都由后端统一封装
## Vercel 节流建议
## 成本与节流建议
- 生产环境建议关闭 `Web Analytics` `Speed Insights`
- 前端与后端以 Docker Compose 部署在同一 VPS,静态资源通过 Cloudflare 缓存(见 `next.config.mjs` `headers()` 和 CI 的 `cloudflare-cache-rules` job
- 建议把自建 `app analytics / web vitals / eager city summaries` 默认保持关闭
- 如果你部署在 Vercel,可在 Firewall 中加一条 `WordPress / php scanner` 拦截规则,避免无效扫描白白触发 middleware
- 如果扫描流量明显,可在 Cloudflare WAF 中加一条 `WordPress / php scanner` 拦截规则,避免无效扫描白白触发 Nginx / middleware
## AGPL 与商用边界说明
-15
View File
@@ -1,15 +0,0 @@
Stack trace:
Frame Function Args
000FFFFBCA0 00180062F57 (000FFFFBEA8, 00000000002, 00000000002, 000FFFFDE50)
00000000000 00180065045 (00000000064, 00000000000, 00000000188, 00000000000)
000FFFFC3B0 001801369F8 (00000000000, 00000000000, 00000000000, 00000000000)
000000000C1 0018013202B (00000000000, 00000000000, 00000000000, 000007E39A0)
000FFFFC7D0 00180132435 (00000000000, 00800000000, 000000000B5, 00100437000)
000FFFFC7D0 001802195E8 (00000000000, 00800000030, 00000000000, 00000000000)
000FFFFC7D0 00100429F25 (001800DB6FE, 0007FFE0358, 7FFEDD6B00F3, 000002432EE)
000FFFFC840 00100404B11 (0000000003F, 00000000040, 00000000040, 00000000000)
000FFFFCA30 001004294B0 (000FFFFCBD0, 00180235040, 00000000000, 04EC36FD370)
000FFFFCD30 00180049B91 (00000000000, 00000000000, 00000000000, 00000000000)
000FFFFFFF0 00180047716 (00000000000, 00000000000, 00000000000, 00000000000)
000FFFFFFF0 001800477C4 (00000000000, 00000000000, 00000000000, 00000000000)
End of stack trace