fix(weather): give the Open-Meteo calls a deadline

All six outbound calls ran without an AbortSignal, so a hung connection held
the caller until the socket gave up on its own. Every other outbound client in
the codebase carries a timeout; this one is the reference domain and did not.
This commit is contained in:
Maurice
2026-08-18 20:02:28 +02:00
committed by Maurice
parent 85794a5a59
commit 3e4b36ac71
+11 -6
View File
@@ -1,4 +1,9 @@
// Open-Meteo sits on the request path as a third party: without a deadline a hung
// connection holds the caller until the socket gives up on its own. Every other
// outbound client in the codebase carries one.
const WEATHER_TIMEOUT_MS = 8000;
// ── Interfaces ──────────────────────────────────────────────────────────
export interface WeatherResult {
@@ -191,7 +196,7 @@ async function _getWeatherImpl(
// Forecast range (-1 .. +16 days)
if (diffDays >= -1 && diffDays <= 16) {
const url = `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lng}&daily=temperature_2m_max,temperature_2m_min,weathercode&timezone=auto&forecast_days=16`;
const response = await fetch(url);
const response = await fetch(url, { signal: AbortSignal.timeout(WEATHER_TIMEOUT_MS) });
const data = await response.json() as OpenMeteoForecast;
if (!response.ok || data.error) {
@@ -223,7 +228,7 @@ async function _getWeatherImpl(
if (diffDays < -1) {
const dateStr = targetDate.toISOString().slice(0, 10);
const url = `https://archive-api.open-meteo.com/v1/archive?latitude=${lat}&longitude=${lng}&start_date=${dateStr}&end_date=${dateStr}&daily=temperature_2m_max,temperature_2m_min,weathercode,precipitation_sum&timezone=auto`;
const response = await fetch(url);
const response = await fetch(url, { signal: AbortSignal.timeout(WEATHER_TIMEOUT_MS) });
const data = await response.json() as OpenMeteoForecast;
if (!response.ok || data.error) {
@@ -264,7 +269,7 @@ async function _getWeatherImpl(
const endStr = endDate.toISOString().slice(0, 10);
const url = `https://archive-api.open-meteo.com/v1/archive?latitude=${lat}&longitude=${lng}&start_date=${startStr}&end_date=${endStr}&daily=temperature_2m_max,temperature_2m_min,precipitation_sum&timezone=auto`;
const response = await fetch(url);
const response = await fetch(url, { signal: AbortSignal.timeout(WEATHER_TIMEOUT_MS) });
const data = await response.json() as OpenMeteoForecast;
if (!response.ok || data.error) {
@@ -317,7 +322,7 @@ async function _getWeatherImpl(
if (cached) return cached;
const url = `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lng}&current=temperature_2m,weathercode&timezone=auto`;
const response = await fetch(url);
const response = await fetch(url, { signal: AbortSignal.timeout(WEATHER_TIMEOUT_MS) });
const data = await response.json() as OpenMeteoForecast;
if (!response.ok || data.error) {
@@ -388,7 +393,7 @@ async function _getDetailedWeatherImpl(
+ `&hourly=temperature_2m,precipitation,weathercode,windspeed_10m,relativehumidity_2m`
+ `&daily=temperature_2m_max,temperature_2m_min,weathercode,precipitation_sum,windspeed_10m_max,sunrise,sunset`
+ `&timezone=auto`;
const response = await fetch(url);
const response = await fetch(url, { signal: AbortSignal.timeout(WEATHER_TIMEOUT_MS) });
const data = await response.json() as OpenMeteoForecast;
if (!response.ok || data.error) {
@@ -451,7 +456,7 @@ async function _getDetailedWeatherImpl(
+ `&daily=temperature_2m_max,temperature_2m_min,weathercode,sunrise,sunset,precipitation_probability_max,precipitation_sum,windspeed_10m_max`
+ `&timezone=auto&start_date=${dateStr}&end_date=${dateStr}`;
const response = await fetch(url);
const response = await fetch(url, { signal: AbortSignal.timeout(WEATHER_TIMEOUT_MS) });
const data = await response.json() as OpenMeteoForecast;
if (!response.ok || data.error) {