fix(meteo): fall back to nearest cached data when the history API fails

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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FHdE9nEh8zHhQNGpCgrtYe
This commit is contained in:
Claude committed 2026-08-15 21:08:44 +00:00
1 parent c375229c05
commit 3eea2eb47b
2 files changed
+31

No files matched your search

+10
View File
@@ -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 ? {
+21
View File
@@ -274,6 +274,7 @@ export interface IStorage {
createWeatherData(data: InsertWeatherData): Promise<WeatherData>;
updateWeatherData(id: number, data: Partial<InsertWeatherData>): Promise<WeatherData>;
deleteOldWeatherData(daysToKeep: number): Promise<void>;
getNearestWeatherData(date: string, isCurrentYear: boolean, maxDistanceDays: number): Promise<WeatherData | undefined>;
clearWeatherCache(): Promise<void>;
// 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<WeatherData | undefined> {
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<InsertWeatherData>): Promise<WeatherData> {
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<WeatherData | undefined> {
return undefined;
}
async createWeatherData(data: InsertWeatherData): Promise<WeatherData> {
return {
id: 1,