From 2e11e3fd563ac4e41be2821202b30b57184aafd9 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Sun, 14 Jun 2026 23:21:03 +0800 Subject: [PATCH] Refresh VPS deployment docs --- .gitignore | 2 +- docs/API_ZH.md | 2 +- docs/CONFIGURATION_ZH.md | 35 +++-- docs/FRONTEND_DEPLOYMENT_ZH.md | 248 ++++++++++++++------------------- docs/OPS_ADMIN_ZH.md | 2 +- docs/SUPABASE_SETUP_ZH.md | 4 +- docs/deep-research-report.md | 6 +- frontend/README.md | 14 +- grep.exe.stackdump | 15 -- 9 files changed, 137 insertions(+), 191 deletions(-) delete mode 100644 grep.exe.stackdump diff --git a/.gitignore b/.gitignore index d4884f7f..25cde8b6 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/docs/API_ZH.md b/docs/API_ZH.md index 10d5374d..552ec492 100644 --- a/docs/API_ZH.md +++ b/docs/API_ZH.md @@ -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. 请求链路 diff --git a/docs/CONFIGURATION_ZH.md b/docs/CONFIGURATION_ZH.md index 3e324622..44d628df 100644 --- a/docs/CONFIGURATION_ZH.md +++ b/docs/CONFIGURATION_ZH.md @@ -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. 最小部署示例 diff --git a/docs/FRONTEND_DEPLOYMENT_ZH.md b/docs/FRONTEND_DEPLOYMENT_ZH.md index 1e26c1f4..304ede74 100644 --- a/docs/FRONTEND_DEPLOYMENT_ZH.md +++ b/docs/FRONTEND_DEPLOYMENT_ZH.md @@ -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:// -NEXT_PUBLIC_SUPABASE_URL=https://.supabase.co -NEXT_PUBLIC_SUPABASE_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=` 打开带令牌的看板入口。 -### 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/ 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://.supabase.co -NEXT_PUBLIC_SUPABASE_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://.supabase.co -NEXT_PUBLIC_SUPABASE_ANON_KEY= -POLYWEATHER_AUTH_ENABLED=true -POLYWEATHER_AUTH_REQUIRED=true -POLYWEATHER_BACKEND_ENTITLEMENT_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= - `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`。 diff --git a/docs/OPS_ADMIN_ZH.md b/docs/OPS_ADMIN_ZH.md index 07782bea..2fca9510 100644 --- a/docs/OPS_ADMIN_ZH.md +++ b/docs/OPS_ADMIN_ZH.md @@ -6,7 +6,7 @@ 前端入口: -- `https://polyweather-pro.vercel.app/ops` +- `https://polyweather.top/ops` ## 2. 权限 diff --git a/docs/SUPABASE_SETUP_ZH.md b/docs/SUPABASE_SETUP_ZH.md index 2f8467af..1f70bef6 100644 --- a/docs/SUPABASE_SETUP_ZH.md +++ b/docs/SUPABASE_SETUP_ZH.md @@ -15,7 +15,7 @@ - `https://.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= diff --git a/docs/deep-research-report.md b/docs/deep-research-report.md index 79b73e1f..36dab11a 100644 --- a/docs/deep-research-report.md +++ b/docs/deep-research-report.md @@ -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 @@ DEB(Dynamic Error Balancing)基于过去 N 天模型误差(MAE)倒数加 从 README、Docker/Compose、入口脚本与核心模块引用关系,可以抽象出如下模块地图(按“运行时组件”与“Python 域模块”两层描述): | 层级 | 目录/文件 | 角色定位 | 关键说明 | | ------------- | ------------------------------------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 运行时组件 | `frontend/` | Next.js 前端(Vercel) | 前端重构报告提到 App Router、Route Handlers(BFF)、缓存策略、支付与账户中心等;Scan Terminal 已新增城市决策卡、结构化实况层、页面内存/localStorage 双层缓存、structured detail 小并发队列与完整市场桶映射。 | +| 运行时组件 | `frontend/` | Next.js 前端(Docker / VPS) | 前端重构报告提到 App Router、Route Handlers(BFF)、缓存策略、支付与账户中心等;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 @@ DEB(Dynamic Error Balancing)基于过去 N 天模型误差(MAE)倒数加 ```mermaid flowchart TB subgraph Clients - WEB[Next.js Frontend
Vercel] + WEB[Next.js Frontend
Docker / VPS] CITYCARD[Scan Terminal City Cards
structured observations + probability mapping] TG[Telegram Bot
TeleBot + Handlers] end diff --git a/frontend/README.md b/frontend/README.md index 850ba7d2..239c5d6c 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -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,不直接感知 Redis;Redis 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 与商用边界说明 diff --git a/grep.exe.stackdump b/grep.exe.stackdump deleted file mode 100644 index 58995648..00000000 --- a/grep.exe.stackdump +++ /dev/null @@ -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