43 lines
1.6 KiB
TypeScript
43 lines
1.6 KiB
TypeScript
import { NextResponse, type NextRequest } from "next/server";
|
|
import { fetchJson } from "@/lib/weather/http";
|
|
import { errorResponse, forbidCrossSite } from "@/lib/weather/respond";
|
|
import type { RadarFrame } from "@/lib/weather/types";
|
|
|
|
/**
|
|
* GET /api/radar → RainViewer frames, oldest first, ready for Leaflet.
|
|
*
|
|
* RainViewer publishes an index of frames. Each frame's `path` is a hash, not a
|
|
* timestamp, so tile URLs must always be built from this index — never guessed.
|
|
* Docs: https://www.rainviewer.com/api/weather-maps-api.html
|
|
*/
|
|
|
|
interface RainViewerIndex {
|
|
host: string;
|
|
radar: { past: { time: number; path: string }[] };
|
|
}
|
|
|
|
// Last 7 frames ≈ one hour. Keeps tile requests well under RainViewer's
|
|
// limit of 100 requests per minute per IP.
|
|
const FRAME_COUNT = 7;
|
|
const COLOR_SCHEME = 2; // "Universal Blue", the only scheme still offered
|
|
const OPTIONS = "1_1"; // smoothed, snow shown in its own colors
|
|
|
|
export async function GET(request: NextRequest) {
|
|
const blocked = forbidCrossSite(request);
|
|
if (blocked) return blocked;
|
|
|
|
try {
|
|
const index = await fetchJson<RainViewerIndex>("https://api.rainviewer.com/public/weather-maps.json", "RainViewer", {
|
|
cacheSeconds: 300, // new frames arrive every 10 minutes
|
|
});
|
|
|
|
const frames: RadarFrame[] = index.radar.past.slice(-FRAME_COUNT).map((frame) => ({
|
|
time: new Date(frame.time * 1000).toISOString(),
|
|
url: `${index.host}${frame.path}/256/{z}/{x}/{y}/${COLOR_SCHEME}/${OPTIONS}.png`,
|
|
}));
|
|
|
|
return NextResponse.json(frames);
|
|
} catch (error) {
|
|
return errorResponse(error);
|
|
}
|
|
}
|