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")