Update production deployment references

This commit is contained in:
2569718930@qq.com
2026-06-14 23:13:37 +08:00
parent e909f1f4c9
commit ef21a5808e
11 changed files with 22 additions and 26 deletions
+7 -5
View File
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project Overview ## Project Overview
PolyWeather Pro — a paid institutional weather-intelligence terminal. 50 monitored cities with real-time METAR/AMOS/MADIS observations, DEB multi-model temperature blending, Mu probability calibration, and intraday bias correction. Pure meteorological decision workspace; no market/price layer. Next.js 15 + React 19 (Vercel) frontend, FastAPI backend (VPS), Telegram bot. PolyWeather Pro — a paid institutional weather-intelligence terminal. 50 monitored cities with real-time METAR/AMOS/MADIS observations, DEB multi-model temperature blending, Mu probability calibration, and intraday bias correction. Pure meteorological decision workspace; no market/price layer. Next.js 15 + React 19 frontend (Docker / VPS, behind Cloudflare + Nginx), FastAPI backend (VPS), Telegram bot.
**Business model**: Paid-only, $10/month, no free tier, no trial. Landing page is public; `/terminal` requires login + active subscription. **Business model**: Paid-only, $10/month, no free tier, no trial. Landing page is public; `/terminal` requires login + active subscription.
@@ -47,10 +47,12 @@ docker compose down && docker compose up -d --build
## Architecture ## Architecture
``` ```
Users → Next.js (Vercel) → FastAPI :8000 (VPS) Users → Cloudflare → Nginx → Docker Compose (VPS)
/terminal (paid gate) Weather Collector ├── Next.js frontend → FastAPI :8000
/ (landing page) Analysis (DEB + Mu) │ /terminal (paid gate) Weather Collector
Payment Layer (USDC on Polygon) │ / (landing page) Analysis (DEB + Mu)
│ Payment Layer (USDC on Polygon)
└── Redis (SSE event store)
Telegram Bot → bot_listener.py Telegram Bot → bot_listener.py
``` ```
+1 -1
View File
@@ -84,7 +84,7 @@ See: [AGPL-3.0 & Commercial Boundary](docs/OPEN_CORE_POLICY.md)
```mermaid ```mermaid
flowchart LR flowchart LR
U["Users (Web / Telegram)"] --> FE["Next.js Frontend (Vercel)"] U["Users (Web / Telegram)"] --> FE["Next.js Frontend (Docker / VPS)"]
U --> BOT["Telegram Bot (VPS)"] U --> BOT["Telegram Bot (VPS)"]
FE --> API["FastAPI /web/app.py"] FE --> API["FastAPI /web/app.py"]
BOT --> API BOT --> API
+2 -2
View File
@@ -83,7 +83,7 @@
```mermaid ```mermaid
flowchart LR flowchart LR
U["用户(Web / Telegram"] --> FE["Next.js 前端(Vercel"] U["用户(Web / Telegram"] --> FE["Next.js 前端(Docker / VPS"]
U --> BOT["Telegram BotVPS"] U --> BOT["Telegram BotVPS"]
FE --> API["FastAPI /web/app.py"] FE --> API["FastAPI /web/app.py"]
BOT --> API BOT --> API
@@ -218,7 +218,7 @@ POLYWEATHER_OPS_ADMIN_EMAILS=yhrsc30@gmail.com
- AGPL-3.0 边界:[docs/OPEN_CORE_POLICY.md](docs/OPEN_CORE_POLICY.md) - AGPL-3.0 边界:[docs/OPEN_CORE_POLICY.md](docs/OPEN_CORE_POLICY.md)
- Supabase 接入:[docs/SUPABASE_SETUP_ZH.md](docs/SUPABASE_SETUP_ZH.md) - Supabase 接入:[docs/SUPABASE_SETUP_ZH.md](docs/SUPABASE_SETUP_ZH.md)
- 配置与密钥管理:[docs/CONFIGURATION_ZH.md](docs/CONFIGURATION_ZH.md) - 配置与密钥管理:[docs/CONFIGURATION_ZH.md](docs/CONFIGURATION_ZH.md)
- 前端部署(Vercel):[docs/FRONTEND_DEPLOYMENT_ZH.md](docs/FRONTEND_DEPLOYMENT_ZH.md) - 前端部署(Docker / VPS):[docs/FRONTEND_DEPLOYMENT_ZH.md](docs/FRONTEND_DEPLOYMENT_ZH.md)
- 技术债:[docs/TECH_DEBT_ZH.md](docs/TECH_DEBT_ZH.md) - 技术债:[docs/TECH_DEBT_ZH.md](docs/TECH_DEBT_ZH.md)
- 机场实时数据源:[docs/AIRPORT_REALTIME_SOURCES.md](docs/AIRPORT_REALTIME_SOURCES.md) - 机场实时数据源:[docs/AIRPORT_REALTIME_SOURCES.md](docs/AIRPORT_REALTIME_SOURCES.md)
- 机场市场监控(中文):[docs/AIRPORT_MARKET_MONITOR_ZH.md](docs/AIRPORT_MARKET_MONITOR_ZH.md) - 机场市场监控(中文):[docs/AIRPORT_MARKET_MONITOR_ZH.md](docs/AIRPORT_MARKET_MONITOR_ZH.md)
+1 -1
View File
@@ -43,7 +43,7 @@
首次建议打开扩展"选项页"并确认: 首次建议打开扩展"选项页"并确认:
- `网站基础地址`:你的前端域名(例如 `https://polyweather-pro.vercel.app` - `网站基础地址`:你的前端域名(例如 `https://polyweather.top`
- `API 基础地址`:你的后端 API 域名(若同域也可填前端域名) - `API 基础地址`:你的后端 API 域名(若同域也可填前端域名)
- `Bearer Token`:后端开启鉴权时填写 - `Bearer Token`:后端开启鉴权时填写
+2 -2
View File
@@ -12,12 +12,12 @@
<label class="field"> <label class="field">
<span id="siteBaseLabel">Site Base URL</span> <span id="siteBaseLabel">Site Base URL</span>
<input id="siteBaseInput" type="text" placeholder="https://polyweather-pro.vercel.app" /> <input id="siteBaseInput" type="text" placeholder="https://polyweather.top" />
</label> </label>
<label class="field"> <label class="field">
<span id="apiBaseLabel">API Base URL</span> <span id="apiBaseLabel">API Base URL</span>
<input id="apiBaseInput" type="text" placeholder="https://polyweather-pro.vercel.app" /> <input id="apiBaseInput" type="text" placeholder="https://polyweather.top" />
</label> </label>
<label class="field"> <label class="field">
+2 -2
View File
@@ -1,6 +1,6 @@
const DEFAULT_CONFIG = { const DEFAULT_CONFIG = {
apiBase: "https://polyweather-pro.vercel.app", apiBase: "https://polyweather.top",
siteBase: "https://polyweather-pro.vercel.app", siteBase: "https://polyweather.top",
authToken: "", authToken: "",
selectedCity: "" selectedCity: ""
}; };
+2 -2
View File
@@ -1,8 +1,8 @@
const DEFAULT_CONFIG = { const DEFAULT_CONFIG = {
apiBase: "https://polyweather-pro.vercel.app", apiBase: "https://polyweather.top",
authToken: "", authToken: "",
selectedCity: "", selectedCity: "",
siteBase: "https://polyweather-pro.vercel.app" siteBase: "https://polyweather.top"
}; };
const CACHE_VERSION = "v3"; const CACHE_VERSION = "v3";
const locale = String(navigator.language || "en").toLowerCase().startsWith("zh") const locale = String(navigator.language || "en").toLowerCase().startsWith("zh")
+1 -4
View File
@@ -55,10 +55,7 @@ export async function POST(req: NextRequest) {
...(body ?? {}), ...(body ?? {}),
payload: { payload: {
...payload, ...payload,
cf_country: cf_country: req.headers.get("cf-ipcountry") || "",
req.headers.get("cf-ipcountry") ||
req.headers.get("x-vercel-ip-country") ||
"",
user_agent: req.headers.get("user-agent") || "", user_agent: req.headers.get("user-agent") || "",
referer_header: req.headers.get("referer") || "", referer_header: req.headers.get("referer") || "",
}, },
+1 -4
View File
@@ -165,10 +165,7 @@ async function trackAuthDiagnosticEvent(
response_mode: responseMode, response_mode: responseMode,
user_id: normalizedUserId || undefined, user_id: normalizedUserId || undefined,
email_domain: email?.includes("@") ? email.split("@").pop() : undefined, email_domain: email?.includes("@") ? email.split("@").pop() : undefined,
cf_country: cf_country: req.headers.get("cf-ipcountry") || "",
req.headers.get("cf-ipcountry") ||
req.headers.get("x-vercel-ip-country") ||
"",
user_agent: req.headers.get("user-agent") || "", user_agent: req.headers.get("user-agent") || "",
referer_header: req.headers.get("referer") || "", referer_header: req.headers.get("referer") || "",
captured_at: new Date().toISOString(), captured_at: new Date().toISOString(),
@@ -41,7 +41,7 @@ async function findSupabaseUserIdByEmail(email: string) {
.replace(/\/$/, ""); .replace(/\/$/, "");
const serviceRoleKey = String(process.env.SUPABASE_SERVICE_ROLE_KEY || "").trim(); const serviceRoleKey = String(process.env.SUPABASE_SERVICE_ROLE_KEY || "").trim();
if (!supabaseUrl || !serviceRoleKey) { if (!supabaseUrl || !serviceRoleKey) {
throw new Error("Supabase service role is not configured on Vercel"); throw new Error("Supabase service role is not configured on the server");
} }
const profileRes = await fetch( const profileRes = await fetch(
`${supabaseUrl}/rest/v1/profiles?select=id&email=eq.${encodeURIComponent(email)}&limit=1`, `${supabaseUrl}/rest/v1/profiles?select=id&email=eq.${encodeURIComponent(email)}&limit=1`,
+2 -2
View File
@@ -34,8 +34,8 @@ self.addEventListener("fetch", (event) => {
// Do not cache-first Next.js build chunks. A user can keep an old app shell // Do not cache-first Next.js build chunks. A user can keep an old app shell
// open across deployments; if checkout UI is loaded later, stale/missing // open across deployments; if checkout UI is loaded later, stale/missing
// chunks surface as a generic page fault. Let the browser/Vercel handle // chunks surface as a generic page fault. Let the browser handle immutable
// immutable _next/static asset caching instead. // _next/static asset caching instead.
if (CACHEABLE_PUBLIC_ASSETS.includes(url.pathname)) { if (CACHEABLE_PUBLIC_ASSETS.includes(url.pathname)) {
event.respondWith( event.respondWith(
caches.open(CACHE_NAME).then((cache) => caches.open(CACHE_NAME).then((cache) =>