Files
worldmonitor/server/_shared/cache-keys.ts
Elie Habib e08457aadf feat(fuel-prices): add retail gasoline and diesel prices panel (#2150)
* feat(fuel-prices): add retail fuel prices panel and seeder

- Add ListFuelPrices proto RPC with FuelPrice/FuelCountryPrice messages
- Create seed-fuel-prices.mjs seeder with 5 sources: Malaysia (data.gov.my),
  Spain (minetur.gob.es), Mexico (datos.gob.mx), US EIA, EU oil bulletin CSV
- Add list-fuel-prices.ts RPC handler reading from Redis seed cache
- Wire handler into EconomicService handler.ts
- Register fuelPrices in cache-keys.ts, bootstrap.js, health.js, gateway.ts
- Add FuelPricesPanel frontend component with gasoline/diesel/source columns
- Wire panel into panel-layout.ts, App.ts (prime + refresh scheduler)
- Add panel config, command entry with fuel/gas/diesel/petrol keywords
- Add fuelPrices refresh interval (6h) in base.ts
- Add i18n keys in en.json (panels.fuelPrices + components.fuelPrices)

Task: fuel-prices

* fix(fuel-prices): add BR/NZ/UK sources, fix EU non-euro currency, review fixes

- Add fetchBrazil() — ANP CSVs (GASOLINA + DIESEL), Promise.allSettled for
  independent partial results, BRL→USD via FX
- Add fetchNewZealand() — MBIE weekly-table.csv, Board price national avg, NZD→USD
- Add fetchUK_ModeA() — CMA retailer JSON feeds (Asda/BP/JET/MFG/Sainsbury's/
  Morrisons), E10+B7 pence→GBP, max-date observedAt across retailers
- Fix EU non-euro members (BG/CZ/DK/HU/PL/RO/SE) using local currency FX on
  EUR-denominated prices — all EU entries now use currency:'EUR'
- Fix fetchBrazil Promise.all → Promise.allSettled (partial CSV failure no
  longer discards both fuels)
- Fix UK observedAt: keep latest date across retailers (not last-processed)
- Fix WoW anomaly: omit wowPct instead of setting to 0
- Lift parseEUPrice out of inner loop to module scope
- Pre-compute parseBRDate per row to avoid double-conversion
- Update infoTooltip: describe methodology without exposing source URLs
- Add BRL, NZD, GBP to FX symbols list

* fix(fuel-prices): fix 4 live data bugs found in external review

EU CSV: replace hardcoded 2024 URLs (both returning 404) with dynamic
discovery — scrape EC energy page for current CSV link, fall back to
generated YYYY-MM patterns for last 4 months.

NZ: live MBIE header has no Region column (Week,Date,Fuel,Variable,Value,
Unit,Status) — remove regionIdx guard that was forcing return []. Values
are in NZD c/L not NZD/L — divide by 100 before storing.

UK: last_updated is DD/MM/YYYY HH:mm:ss not ISO — parse to YYYY-MM-DD
before lexicographic max-date comparison; previous code stored the
seed-run date instead of the latest retailer timestamp.

Panel: source column fell back to — for diesel-only countries because it
only read gas?.source. Use (gas ?? dsl)?.source so diesel-only rows
display their source correctly.
2026-03-23 20:26:57 +04:00

94 lines
4.3 KiB
TypeScript

/**
* Static cache keys for the bootstrap endpoint.
* Only keys with NO request-varying suffixes are included.
*/
export const BOOTSTRAP_CACHE_KEYS: Record<string, string> = {
earthquakes: 'seismology:earthquakes:v1',
outages: 'infra:outages:v1',
serviceStatuses: 'infra:service-statuses:v1',
ddosAttacks: 'cf:radar:ddos:v1',
trafficAnomalies: 'cf:radar:traffic-anomalies:v1',
sectors: 'market:sectors:v1',
etfFlows: 'market:etf-flows:v1',
macroSignals: 'economic:macro-signals:v1',
bisPolicy: 'economic:bis:policy:v1',
bisExchange: 'economic:bis:eer:v1',
bisCredit: 'economic:bis:credit:v1',
shippingRates: 'supply_chain:shipping:v2',
chokepoints: 'supply_chain:chokepoints:v4',
chokepointTransits: 'supply_chain:chokepoint_transits:v1',
minerals: 'supply_chain:minerals:v2',
giving: 'giving:summary:v1',
climateAnomalies: 'climate:anomalies:v1',
radiationWatch: 'radiation:observations:v1',
thermalEscalation: 'thermal:escalation:v1',
wildfires: 'wildfire:fires:v1',
marketQuotes: 'market:stocks-bootstrap:v1',
commodityQuotes: 'market:commodities-bootstrap:v1',
cyberThreats: 'cyber:threats-bootstrap:v2',
techReadiness: 'economic:worldbank-techreadiness:v1',
progressData: 'economic:worldbank-progress:v1',
renewableEnergy: 'economic:worldbank-renewable:v1',
positiveGeoEvents: 'positive_events:geo-bootstrap:v1',
theaterPosture: 'theater_posture:sebuf:stale:v1',
riskScores: 'risk:scores:sebuf:stale:v1',
naturalEvents: 'natural:events:v1',
flightDelays: 'aviation:delays-bootstrap:v1',
insights: 'news:insights:v1',
predictions: 'prediction:markets-bootstrap:v1',
cryptoQuotes: 'market:crypto:v1',
gulfQuotes: 'market:gulf-quotes:v1',
stablecoinMarkets: 'market:stablecoins:v1',
unrestEvents: 'unrest:events:v1',
iranEvents: 'conflict:iran-events:v1',
ucdpEvents: 'conflict:ucdp-events:v1',
temporalAnomalies: 'temporal:anomalies:v1',
weatherAlerts: 'weather:alerts:v1',
spending: 'economic:spending:v1',
techEvents: 'research:tech-events-bootstrap:v1',
gdeltIntel: 'intelligence:gdelt-intel:v1',
correlationCards: 'correlation:cards-bootstrap:v1',
securityAdvisories: 'intelligence:advisories-bootstrap:v1',
forecasts: 'forecast:predictions:v2',
customsRevenue: 'trade:customs-revenue:v1',
sanctionsPressure: 'sanctions:pressure:v1',
groceryBasket: 'economic:grocery-basket:v1',
bigmac: 'economic:bigmac:v1',
fuelPrices: 'economic:fuel-prices:v1',
cryptoSectors: 'market:crypto-sectors:v1',
defiTokens: 'market:defi-tokens:v1',
aiTokens: 'market:ai-tokens:v1',
otherTokens: 'market:other-tokens:v1',
nationalDebt: 'economic:national-debt:v1',
};
export const BOOTSTRAP_TIERS: Record<string, 'slow' | 'fast'> = {
bisPolicy: 'slow', bisExchange: 'slow', bisCredit: 'slow',
minerals: 'slow', giving: 'slow', sectors: 'slow',
progressData: 'slow', renewableEnergy: 'slow',
etfFlows: 'slow', shippingRates: 'fast', wildfires: 'slow',
climateAnomalies: 'slow', sanctionsPressure: 'slow', radiationWatch: 'slow', thermalEscalation: 'slow', cyberThreats: 'slow', techReadiness: 'slow',
theaterPosture: 'fast', naturalEvents: 'slow',
cryptoQuotes: 'slow', gulfQuotes: 'slow', stablecoinMarkets: 'slow',
unrestEvents: 'slow', ucdpEvents: 'slow', techEvents: 'slow',
earthquakes: 'fast', outages: 'fast', serviceStatuses: 'fast', ddosAttacks: 'fast', trafficAnomalies: 'fast',
macroSignals: 'fast', chokepoints: 'fast', chokepointTransits: 'fast', riskScores: 'fast',
marketQuotes: 'fast', commodityQuotes: 'fast', positiveGeoEvents: 'fast',
flightDelays: 'fast', insights: 'fast', predictions: 'fast',
iranEvents: 'fast', temporalAnomalies: 'fast', weatherAlerts: 'fast',
spending: 'fast', gdeltIntel: 'fast', correlationCards: 'fast',
securityAdvisories: 'slow',
forecasts: 'fast',
customsRevenue: 'slow',
consumerPricesOverview: 'slow', consumerPricesCategories: 'slow',
consumerPricesMovers: 'slow', consumerPricesSpread: 'slow',
groceryBasket: 'slow',
bigmac: 'slow',
fuelPrices: 'slow',
cryptoSectors: 'slow',
defiTokens: 'slow',
aiTokens: 'slow',
otherTokens: 'slow',
nationalDebt: 'slow',
};