From 3eea2eb47b4bfbb11c62734d0916d5bf53b6dd33 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 15 Aug 2026 21:08:44 +0000 Subject: [PATCH] fix(meteo): fall back to nearest cached data when the history API fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dashboard widget lost the previous-year temperature and the difference: when the day's previous-year row is not yet cached and the Visual Crossing history call fails (quota exhausted, outage, or a plan without history access), the route silently returned previousYear: null and the widget hid the comparison. The weather cache accumulates one previous-year row per day, so the route now falls back to the nearest cached previous-year entry within 7 days of the weekday-aligned target date, keeping the comparison on screen through API failures. The exact API error remains logged server-side (🌤️ [ERROR] / [FETCH-HISTORY]) for diagnosis. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01FHdE9nEh8zHhQNGpCgrtYe --- server/routes.ts | 10 ++++++++++ server/storage.ts | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/server/routes.ts b/server/routes.ts index c3a9f0a..5cd8c5b 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -5723,6 +5723,16 @@ RÉSUMÉ DU SCAN } } + // Repli : si l'API historique a échoué (quota épuisé, panne, plan sans + // accès à l'historique), reprendre la ligne en cache la plus proche de + // la date cible plutôt que de faire disparaître la comparaison N-1 + if (!previousYearData) { + previousYearData = await storage.getNearestWeatherData(previousYearDate, false, 7); + if (previousYearData) { + console.log(`🌤️ [FALLBACK] Using nearest cached previous-year data (${previousYearData.date}) for target ${previousYearDate}`); + } + } + // Toujours retourner une réponse même si seule l'année actuelle est disponible const response = { currentYear: currentYearData ? { diff --git a/server/storage.ts b/server/storage.ts index 467b312..78fb197 100644 --- a/server/storage.ts +++ b/server/storage.ts @@ -274,6 +274,7 @@ export interface IStorage { createWeatherData(data: InsertWeatherData): Promise; updateWeatherData(id: number, data: Partial): Promise; deleteOldWeatherData(daysToKeep: number): Promise; + getNearestWeatherData(date: string, isCurrentYear: boolean, maxDistanceDays: number): Promise; clearWeatherCache(): Promise; // Historique des mails fournisseurs (rapprochement) @@ -2923,6 +2924,22 @@ export class DatabaseStorage implements IStorage { return weatherInfo; } + // Repli : ligne en cache la plus proche de la date demandée (à N jours près). + // Utilisé quand l'API météo historique échoue (quota, panne) pour continuer + // d'afficher la comparaison avec l'année dernière. + async getNearestWeatherData(date: string, isCurrentYear: boolean, maxDistanceDays: number): Promise { + const [data] = await db + .select() + .from(weatherData) + .where(and( + eq(weatherData.isCurrentYear, isCurrentYear), + sql`ABS(${weatherData.date} - ${date}::date) <= ${maxDistanceDays}` + )) + .orderBy(sql`ABS(${weatherData.date} - ${date}::date)`) + .limit(1); + return data; + } + async updateWeatherData(id: number, data: Partial): Promise { const [weatherInfo] = await db .update(weatherData) @@ -5240,6 +5257,10 @@ export class MemStorage implements IStorage { return undefined; // Will trigger API calls } + async getNearestWeatherData(date: string, isCurrentYear: boolean, maxDistanceDays: number): Promise { + return undefined; + } + async createWeatherData(data: InsertWeatherData): Promise { return { id: 1,