fix: use Open-Meteo instead of wttr.in (wttr.in response gets truncated in container)
All checks were successful
Deploy to Coolify / deploy (push) Successful in 4s

This commit is contained in:
Cosmo
2026-04-22 10:37:43 +00:00
parent 56d03ae781
commit a2fb233363

View File

@@ -1,63 +1,84 @@
export const dynamic = 'force-dynamic';
import { NextResponse } from "next/server";
function getWeatherCode(desc: string): string {
const d = desc.toLowerCase();
if (d.includes("overcast")) return "122";
if (d.includes("partly cloudy") || d.includes("partly") ) return "116";
if (d.includes("cloudy") || d.includes("cloud")) return "119";
if (d.includes("drizzle")) return "185";
if (d.includes("rain") || d.includes("shower")) return "305";
if (d.includes("snow") || d.includes("blizzard")) return "230";
if (d.includes("thunder") || d.includes("storm")) return "200";
if (d.includes("fog") || d.includes("mist")) return "248";
if (d.includes("sunny") || d.includes("clear")) return "113";
// 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 res = await fetch(
"https://wttr.in/Saint+Petersburg?format=j1",
{
signal: controller.signal,
cache: "no-store",
headers: {
"User-Agent": "SmartHomeDashboard/1.0",
"Accept": "application/json",
},
}
);
clearTimeout(timeout);
if (!res.ok) throw new Error(`wttr responded ${res.status}`);
const data = await res.json();
const current = data.current_condition[0];
const days = (data.weather || []).slice(0, 3).map((day: any) => {
const hourly = day.hourly?.[4] || day.hourly?.[0] || {};
const desc = hourly.weatherDesc?.[0]?.value || "";
const code = hourly.weatherCode || getWeatherCode(desc);
return {
date: day.date,
maxTemp: day.maxtempC,
minTemp: day.mintempC,
desc,
weatherCode: String(code),
};
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: current.temp_C,
feelsLike: current.FeelsLikeC,
humidity: current.humidity,
desc: current.weatherDesc[0]?.value || "",
weatherCode: String(current.weatherCode),
windSpeed: current.windspeedKmph,
forecast: days,
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)),
forecast,
});
} catch (e) {
console.error("Weather fetch error:", e);