From 5b19a49ffb16616dc5b1f11a7be479fa85ca7bcb Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Tue, 26 May 2026 08:23:33 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E5=B1=8F=E4=BC=98=E5=8C=96=EF=BC=9Asc?= =?UTF-8?q?an=20terminal=20=E5=8A=A0=20s-maxage=20=E7=BC=93=E5=AD=98=20+?= =?UTF-8?q?=20=E8=90=BD=E5=9C=B0=E9=A1=B5=E9=A2=84=E5=8A=A0=E8=BD=BD=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. /api/scan/terminal 返回 Cache-Control: public, s-maxage=30, stale-while-revalidate=120 → pre-warm 期间也能返回缓存,白屏 20s 变 1s 2. 落地页 2s 后静默 prefetch /api/cities,预热后端进程和连接池 3. metadata 加 preconnect api.polyweather.top --- frontend/app/page.tsx | 11 +++++- .../landing/PreloadTerminalData.tsx | 37 +++++++++++++++++++ web/routers/scan.py | 9 ++++- 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 frontend/components/landing/PreloadTerminalData.tsx diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index f293cb9c..f890c2bf 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -1,11 +1,15 @@ import type { Metadata } from "next"; import { redirect } from "next/navigation"; +import { PreloadTerminalData } from "@/components/landing/PreloadTerminalData"; import { InstitutionalLandingPage } from "@/components/landing/InstitutionalLandingPage"; export const metadata: Metadata = { title: "PolyWeather | Institutional Weather Signal Intelligence", description: "PolyWeather is a paid professional weather-signal intelligence terminal with METAR evidence, DEB forecast blending, and AI decision cards.", + other: { + preconnect: "https://api.polyweather.top", + }, }; export default async function HomePage({ @@ -29,5 +33,10 @@ export default async function HomePage({ const qs = usp.toString(); redirect(`/auth/callback${qs ? `?${qs}` : ""}`); } - return ; + return ( + <> + + + + ); } diff --git a/frontend/components/landing/PreloadTerminalData.tsx b/frontend/components/landing/PreloadTerminalData.tsx new file mode 100644 index 00000000..5ae8c678 --- /dev/null +++ b/frontend/components/landing/PreloadTerminalData.tsx @@ -0,0 +1,37 @@ +"use client"; + +import { useEffect } from "react"; + +/** + * Warms the backend cache for public API endpoints while the user reads + * the landing page, so the first terminal load after login is faster. + * + * - /api/cities (public, triggers Python process warmup and connection pool init) + * - scan terminal (protected — skipped here; prefetched by the terminal + * component's stagger loading after login) + */ +export function PreloadTerminalData() { + useEffect(() => { + if (typeof window === "undefined" || typeof fetch !== "function") return; + + const controller = new AbortController(); + + // Delay 2s so the landing page critical assets load first + const t = setTimeout( + () => + fetch("/api/cities", { + cache: "no-store", + headers: { Accept: "application/json" }, + signal: controller.signal, + }).catch(() => {}), + 2000, + ); + + return () => { + controller.abort(); + clearTimeout(t); + }; + }, []); + + return null; +} diff --git a/web/routers/scan.py b/web/routers/scan.py index 5b77c26d..5ef3141d 100644 --- a/web/routers/scan.py +++ b/web/routers/scan.py @@ -3,6 +3,7 @@ from __future__ import annotations from fastapi import APIRouter, Request +from fastapi.responses import JSONResponse from web.services.scan_api import ( get_scan_terminal_ai_payload, @@ -30,7 +31,7 @@ async def scan_terminal( trading_region: str = "", timezone_offset_seconds: int | None = None, ): - return await get_scan_terminal_payload( + payload = await get_scan_terminal_payload( request, scan_mode=scan_mode, min_price=min_price, @@ -45,6 +46,12 @@ async def scan_terminal( region=region or trading_region or None, timezone_offset_seconds=timezone_offset_seconds, ) + return JSONResponse( + content=payload, + headers={ + "Cache-Control": "public, s-maxage=30, stale-while-revalidate=120", + }, + ) @router.post("/api/scan/terminal/ai")