From c38dc80f58355888bfc1d7004519c8a8a030efa1 Mon Sep 17 00:00:00 2001
From: "2569718930@qq.com" <2569718930@qq.com>
Date: Mon, 18 May 2026 23:25:16 +0800
Subject: [PATCH] =?UTF-8?q?=E5=90=8E=E5=8F=B0=E6=96=B0=E5=A2=9E=20API=20?=
=?UTF-8?q?=E7=8A=B6=E6=80=81=E6=A3=80=E6=B5=8B=E9=A1=B5=EF=BC=9A=E5=AE=9E?=
=?UTF-8?q?=E6=97=B6=E6=A3=80=E6=B5=8B=20Supabase/Open-Meteo/METAR/KNMI/MA?=
=?UTF-8?q?DIS/Telegram=20=E8=BF=9E=E9=80=9A=E6=80=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
frontend/app/api/ops/health-check/route.ts | 16 ++++
frontend/app/ops/health/page.tsx | 10 ++
frontend/app/page.tsx | 2 +-
.../SubscriptionHelpClient.tsx | 4 +-
frontend/components/account/AccountCenter.tsx | 8 +-
.../dashboard/ScanTerminalDashboard.tsx | 54 +++++++++++
.../ops/health/HealthPageClient.tsx | 92 +++++++++++++++++++
.../components/ops/layout/AdminSidebar.tsx | 1 +
.../subscription/UnlockProOverlay.tsx | 4 +-
web/routers/ops.py | 6 ++
web/services/ops_api.py | 71 ++++++++++++++
11 files changed, 259 insertions(+), 9 deletions(-)
create mode 100644 frontend/app/api/ops/health-check/route.ts
create mode 100644 frontend/app/ops/health/page.tsx
create mode 100644 frontend/components/ops/health/HealthPageClient.tsx
diff --git a/frontend/app/api/ops/health-check/route.ts b/frontend/app/api/ops/health-check/route.ts
new file mode 100644
index 00000000..25642c28
--- /dev/null
+++ b/frontend/app/api/ops/health-check/route.ts
@@ -0,0 +1,16 @@
+import { NextRequest, NextResponse } from "next/server";
+import { applyAuthResponseCookies, buildBackendRequestHeaders } from "@/lib/backend-auth";
+import { buildProxyExceptionResponse } from "@/lib/api-proxy";
+
+const API_BASE = process.env.POLYWEATHER_API_BASE_URL;
+
+export async function GET(req: NextRequest) {
+ if (!API_BASE) return NextResponse.json({ error: "API_BASE not configured" }, { status: 500 });
+ try {
+ const auth = await buildBackendRequestHeaders(req);
+ const res = await fetch(`${API_BASE}/api/ops/health-check`, { headers: auth.headers, cache: "no-store" });
+ const raw = await res.text();
+ const response = new NextResponse(raw, { status: res.status, headers: { "Content-Type": "application/json", "Cache-Control": "no-store" } });
+ return applyAuthResponseCookies(response, auth.response);
+ } catch (e) { return buildProxyExceptionResponse(e, { publicMessage: "Health check failed" }); }
+}
diff --git a/frontend/app/ops/health/page.tsx b/frontend/app/ops/health/page.tsx
new file mode 100644
index 00000000..c908926b
--- /dev/null
+++ b/frontend/app/ops/health/page.tsx
@@ -0,0 +1,10 @@
+import type { Metadata } from "next";
+import { requireOpsAdmin } from "@/lib/ops-admin";
+import { HealthPageClient } from "@/components/ops/health/HealthPageClient";
+
+export const metadata: Metadata = { title: "API 状态 — PolyWeather Ops" };
+
+export default async function HealthPage() {
+ await requireOpsAdmin("/ops/health");
+ return ;
+}
diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx
index af0bebb0..02455197 100644
--- a/frontend/app/page.tsx
+++ b/frontend/app/page.tsx
@@ -4,7 +4,7 @@ import { DashboardEntry } from "@/components/dashboard/DashboardEntry";
export const metadata: Metadata = {
title: "PolyWeather - Global Weather Intelligence Map",
description:
- "PolyWeather dashboard with METAR, MGM, DEB fusion forecast, multi-model comparison, and history reconciliation.",
+ "PolyWeather dashboard with METAR, MGM, DEB fusion forecast, multi-model comparison, and AI weather decision cards.",
};
export default function HomePage() {
diff --git a/frontend/app/subscription-help/SubscriptionHelpClient.tsx b/frontend/app/subscription-help/SubscriptionHelpClient.tsx
index 2dce2f6b..d587dd0e 100644
--- a/frontend/app/subscription-help/SubscriptionHelpClient.tsx
+++ b/frontend/app/subscription-help/SubscriptionHelpClient.tsx
@@ -21,8 +21,8 @@ const FAQ_ITEMS = [
{
q_zh: "Pro 包含哪些功能?",
q_en: "What features does Pro include?",
- a_zh: "开通后可解锁:今日日内机场报文规则分析(含高温时段)、历史对账 + 未来日期分析、全平台智能气象推送。",
- a_en: "Unlocks: intraday METAR rule analysis (including peak window), historical reconciliation + future-date analysis, and cross-platform smart weather push.",
+ a_zh: "开通后可解锁:今日日内机场报文规则分析(含高温时段)、未来日期分析、城市决策卡、全平台智能气象推送。",
+ a_en: "Unlocks: intraday METAR rule analysis (including peak window), future-date analysis, city decision cards, and cross-platform smart weather push.",
},
{
q_zh: "当前订阅价格是多少?",
diff --git a/frontend/components/account/AccountCenter.tsx b/frontend/components/account/AccountCenter.tsx
index 318ea400..442d885d 100644
--- a/frontend/components/account/AccountCenter.tsx
+++ b/frontend/components/account/AccountCenter.tsx
@@ -707,8 +707,8 @@ export function AccountCenter() {
weatherEngine: isEn ? "Weather Engine" : "气象引擎",
intradayAnalysis: isEn ? "Intraday Analysis" : "今日内分析",
historyFuture: isEn
- ? "Historical + Future-date Analysis"
- : "历史对账 + 未来日期分析",
+ ? "Future-date + Decision Card Analysis"
+ : "未来日期分析 + 城市决策卡",
smartPush: isEn
? "Cross-platform Smart Weather Push"
: "全平台智能气象查询",
@@ -814,8 +814,8 @@ export function AccountCenter() {
noProSubscription: isEn ? "No Pro subscription" : "暂无 Pro 订阅",
trialEndsSoonTitle: isEn ? "Trial ending soon" : "试用即将结束",
trialEndsSoonBody: isEn
- ? "Your 3-day trial is almost over. Upgrade to Pro to keep full intraday analysis and history."
- : "你的 3 天试用即将结束。升级 Pro 后可继续使用完整日内分析和历史对账。",
+ ? "Your 3-day trial is almost over. Upgrade to Pro to keep full intraday analysis and decision cards."
+ : "你的 3 天试用即将结束。升级 Pro 后可继续使用完整日内分析和城市决策卡。",
trialExpiredTitle: isEn ? "Trial ended" : "试用已结束",
trialExpiredBody: isEn
? "Your trial access has ended. Renew with Pro to restore full access."
diff --git a/frontend/components/dashboard/ScanTerminalDashboard.tsx b/frontend/components/dashboard/ScanTerminalDashboard.tsx
index 2c7b6d8d..f57cbbcc 100644
--- a/frontend/components/dashboard/ScanTerminalDashboard.tsx
+++ b/frontend/components/dashboard/ScanTerminalDashboard.tsx
@@ -259,6 +259,21 @@ function ScanTerminalScreen() {
: selectedRow;
const scanStatus = terminalData?.status || "ready";
const staleReason = terminalData?.stale_reason || null;
+ const proPreviewItems = isEn
+ ? [
+ "Intraday METAR rule analysis",
+ "Multi-model high-temp forecast",
+ "Real-time observation deviation",
+ "Future-date decision cards",
+ "Telegram group price $5",
+ ]
+ : [
+ "日内机场报文规则分析",
+ "多模型高温预测",
+ "实时观测偏差",
+ "未来日期城市决策卡",
+ "Telegram 群内价 5U",
+ ];
useEffect(() => {
if (!activeDetailRow) return;
@@ -405,6 +420,45 @@ function ScanTerminalScreen() {
userLocalTime={userLocalTime}
/>
+ {!isPro ? (
+
+
+ {isEn ? "What Pro unlocks" : "开通 Pro 后可看到"}
+
+ {isEn
+ ? "Full weather decision context, not just the public map."
+ : "不只是公开地图,而是完整天气交易辅助信息。"}
+
+
+ {isEn
+ ? "Guests and free users can browse the map. Pro adds live evidence, model deltas and city-level decision cards for current and future dates."
+ : "游客和免费用户可浏览地图;Pro 会补齐实时证据、模型偏差和当前/未来日期的城市决策卡。"}
+