100 lines
3.6 KiB
TypeScript
100 lines
3.6 KiB
TypeScript
export const dynamic = 'force-dynamic';
|
||
import { NextResponse } from "next/server";
|
||
|
||
// Convert WMO weather codes to wttr.in-compatible codes for emoji mapping
|
||
function wmoToWttrCode(wmo: number): string {
|
||
if (wmo === 0) return "113"; // Clear sunny
|
||
if (wmo === 1) return "116"; // Mainly clear
|
||
if (wmo === 2) return "116"; // Partly cloudy
|
||
if (wmo === 3) return "122"; // Overcast
|
||
if (wmo >= 45 && wmo <= 48) return "143"; // Fog
|
||
if (wmo >= 51 && wmo <= 55) return "185"; // Drizzle
|
||
if (wmo >= 56 && wmo <= 57) return "281"; // Freezing drizzle
|
||
if (wmo >= 61 && wmo <= 65) return "305"; // Rain
|
||
if (wmo >= 66 && wmo <= 67) return "281"; // Freezing rain
|
||
if (wmo >= 71 && wmo <= 77) return "227"; // Snow
|
||
if (wmo >= 80 && wmo <= 82) return "305"; // Rain showers
|
||
if (wmo >= 85 && wmo <= 86) return "260"; // Snow showers
|
||
if (wmo === 95) return "200"; // Thunderstorm
|
||
if (wmo >= 96 && wmo <= 99) return "389"; // Thunderstorm with hail
|
||
return "116";
|
||
}
|
||
|
||
function wmoToDesc(wmo: number): string {
|
||
if (wmo === 0) return "Ясно";
|
||
if (wmo === 1) return "Преим. ясно";
|
||
if (wmo === 2) return "Переменная облачность";
|
||
if (wmo === 3) return "Пасмурно";
|
||
if (wmo === 45 || wmo === 48) return "Туман";
|
||
if (wmo >= 51 && wmo <= 55) return "Морось";
|
||
if (wmo >= 61 && wmo <= 65) return "Дождь";
|
||
if (wmo >= 71 && wmo <= 77) return "Снег";
|
||
if (wmo >= 80 && wmo <= 82) return "Ливень";
|
||
if (wmo >= 85 && wmo <= 86) return "Снегопад";
|
||
if (wmo === 95) return "Гроза";
|
||
if (wmo >= 96 && wmo <= 99) return "Гроза с градом";
|
||
return "Облачно";
|
||
}
|
||
|
||
export async function GET() {
|
||
try {
|
||
const controller = new AbortController();
|
||
const timeout = setTimeout(() => controller.abort(), 8000);
|
||
|
||
const url = "https://api.open-meteo.com/v1/forecast?" + new URLSearchParams({
|
||
latitude: "59.9343",
|
||
longitude: "30.3351",
|
||
current: "temperature_2m,relative_humidity_2m,apparent_temperature,weather_code,wind_speed_10m",
|
||
daily: "weather_code,temperature_2m_max,temperature_2m_min",
|
||
timezone: "Europe/Moscow",
|
||
forecast_days: "3",
|
||
});
|
||
|
||
const res = await fetch(url, {
|
||
signal: controller.signal,
|
||
cache: "no-store",
|
||
headers: { "Accept": "application/json" },
|
||
});
|
||
clearTimeout(timeout);
|
||
|
||
if (!res.ok) throw new Error(`Open-Meteo responded ${res.status}`);
|
||
const data = await res.json();
|
||
|
||
const current = data.current;
|
||
const daily = data.daily;
|
||
|
||
const forecast = (daily.time || []).slice(0, 3).map((date: string, i: number) => ({
|
||
date,
|
||
maxTemp: String(Math.round(daily.temperature_2m_max[i])),
|
||
minTemp: String(Math.round(daily.temperature_2m_min[i])),
|
||
desc: wmoToDesc(daily.weather_code[i]),
|
||
weatherCode: wmoToWttrCode(daily.weather_code[i]),
|
||
}));
|
||
|
||
return NextResponse.json({
|
||
temp: String(Math.round(current.temperature_2m)),
|
||
feelsLike: String(Math.round(current.apparent_temperature)),
|
||
humidity: String(current.relative_humidity_2m),
|
||
desc: wmoToDesc(current.weather_code),
|
||
weatherCode: wmoToWttrCode(current.weather_code),
|
||
windSpeed: String(Math.round(current.wind_speed_10m / 3.6)),
|
||
forecast,
|
||
});
|
||
} catch (e) {
|
||
console.error("Weather fetch error:", e);
|
||
return NextResponse.json(
|
||
{
|
||
temp: "—",
|
||
feelsLike: "—",
|
||
humidity: "—",
|
||
desc: "Нет данных",
|
||
weatherCode: "116",
|
||
windSpeed: "—",
|
||
forecast: [],
|
||
error: String(e),
|
||
},
|
||
{ status: 200 }
|
||
);
|
||
}
|
||
}
|