diff --git a/frontend/app/api/m/route.ts b/frontend/app/api/m/route.ts index 6e7eb4eb..f7e42727 100644 --- a/frontend/app/api/m/route.ts +++ b/frontend/app/api/m/route.ts @@ -5,19 +5,9 @@ const API_BASE = process.env.POLYWEATHER_API_BASE_URL; export async function GET(req: NextRequest) { if (!API_BASE) { - return new NextResponse("API not configured", { status: 500 }); + return NextResponse.json({ error: "API not configured" }, { status: 500 }); } - const isCards = req.nextUrl.searchParams.get("cards") === "1"; - const asJson = req.nextUrl.searchParams.get("json") === "1"; - const path = asJson ? "/m/json" : isCards ? "/m/cards" : "/m"; const auth = await buildBackendRequestHeaders(req, { includeSupabaseIdentity: false }); - const res = await fetch(`${API_BASE}${path}`, { headers: auth.headers }); - if (asJson) { - return NextResponse.json(await res.json()); - } - const html = await res.text(); - return new NextResponse(html, { - status: res.status, - headers: { "Content-Type": "text/html; charset=utf-8" }, - }); + const res = await fetch(`${API_BASE}/m/json`, { headers: auth.headers }); + return NextResponse.json(await res.json()); } diff --git a/frontend/components/dashboard/monitoring/MonitorPanel.tsx b/frontend/components/dashboard/monitoring/MonitorPanel.tsx index b0182ba3..f6830645 100644 --- a/frontend/components/dashboard/monitoring/MonitorPanel.tsx +++ b/frontend/components/dashboard/monitoring/MonitorPanel.tsx @@ -41,7 +41,7 @@ export default function MonitorPanel() { const fetchData = useCallback(async () => { try { - const res = await fetch("/api/m?json=1"); + const res = await fetch("/api/m"); const data = await res.json(); setCities(data); setTime(new Date().toLocaleTimeString()); diff --git a/web/monitor_routes.py b/web/monitor_routes.py index 27dc6946..26a92311 100644 --- a/web/monitor_routes.py +++ b/web/monitor_routes.py @@ -1,20 +1,17 @@ -"""市场监控网页版 — f-string 拼 HTML,零模板引擎依赖。""" +"""市场监控 — JSON API for frontend React component.""" from __future__ import annotations from datetime import datetime, timezone from typing import Any, Dict, List, Optional -from fastapi import APIRouter, Request -from fastapi.responses import HTMLResponse +from fastapi import APIRouter from loguru import logger from web.analysis_service import _analyze router = APIRouter() -# ── city config ── - _CITIES: List[Dict[str, Any]] = [ {"key": "seoul", "en_name": "Seoul", "icao": "RKSI", "airport": "Incheon", "tz": 9, "tz_abbr": "KST", "rw": True}, {"key": "busan", "en_name": "Busan", "icao": "RKPK", "airport": "Gimhae", "tz": 9, "tz_abbr": "KST", "rw": True}, @@ -29,7 +26,6 @@ _CITIES: List[Dict[str, Any]] = [ {"key": "taipei", "en_name": "Taipei", "icao": "466920", "airport": "Songshan", "tz": 8, "tz_abbr": "TST", "rw": False}, ] -# ── helpers ── def _sf(v: Any) -> Optional[float]: if v is None: @@ -39,6 +35,7 @@ def _sf(v: Any) -> Optional[float]: except (ValueError, TypeError): return None + def _trend_info(icao: str) -> tuple: try: from src.utils.telegram_push import _check_rising_trend @@ -56,6 +53,7 @@ def _trend_info(icao: str) -> tuple: pass return ("→", "flat") + def _obs_age(obs_time_str: Optional[str]) -> Optional[int]: if not obs_time_str: return None @@ -77,7 +75,8 @@ def _obs_age(obs_time_str: Optional[str]) -> Optional[int]: pass return None -def _build_cards() -> tuple: + +def _build_cards() -> list: cards = [] for cfg in _CITIES: try: @@ -105,164 +104,20 @@ def _build_cards() -> tuple: cards.append({ "en_name": cfg["en_name"], "airport": cfg["airport"], - "time": obs or local_time, - "ct": ct, "msf": _sf(msf), "mtt": mtt, - "tsym": tsym, "tcss": tcss, "age": age, - "new_high": new_high, "rw_html": rw_html, - "warm": ct is not None and ct >= 30, + "obs_time": obs or local_time, + "current_temp": ct, "max_so_far": _sf(msf), "max_temp_time": mtt, + "trend_sym": tsym, "trend_css": tcss, "obs_age_min": age, + "new_high": new_high, + "temp_warm": ct is not None and ct >= 30, + "runway_pairs": rw_html, }) except Exception: logger.exception("monitor: failed city {}", cfg["key"]) - cards.sort(key=lambda c: (c["ct"] is not None, c["ct"] or -999), reverse=True) - return cards, datetime.now(timezone.utc).isoformat() + cards.sort(key=lambda c: (c["current_temp"] is not None, c["current_temp"] or -999), reverse=True) + return cards -def _render(cards, gts): - """Build card grid HTML.""" - cards_html = [] - for c in cards: - nc = " new-high-card" if c["new_high"] else "" - wc = " warm" if c["warm"] else "" - nv = " new-high-val" if c["new_high"] else "" - nh = '◆新高' if c["new_high"] else "" - ct_str = f'{c["ct"]:.1f}' if c["ct"] is not None else "--" - ct_na = ' na' if c["ct"] is None else "" - msf_str = f'{c["msf"]:.1f}°C' if c["msf"] is not None else "" - msf_na = ' na' if c["msf"] is None else "" - mtt_str = f'{c["mtt"]}' if c["mtt"] else "" - age_str = f'{c["age"]} min ago' if c["age"] is not None else "" - age_na = ' na' if c["age"] is None else "" - - cards_html.append(f'''