fix(header): add energy.worldmonitor.app to CSP frame-src and frame-ancestors#3359
fix(header): add energy.worldmonitor.app to CSP frame-src and frame-ancestors#3359fuleinist wants to merge 3 commits into
Conversation
Replicate legacy NEG_TTL=30000 behaviour in list-military-flights fetchStaleFallback. After a failed live fetch, do not immediately re-read the stale Redis key — that just wastes an Upstash call if OpenSky/the relay is still down. Suppress stale-read attempts for 30 s after the last failed live fetch attempt. Fixes koala73#3277
Add _retryCount parameter (default 0) to sendTelegram and guard against unbounded recursion when Telegram returns sustained 429 responses. - Bail with warn log if _retryCount >= 1 - Pass _retryCount + 1 on recursive call - Fixes koala73#3060
…ncestors The energy variant link in the header navigation points to https://energy.worldmonitor.app but that domain was missing from the Content-Security-Policy frame-src and frame-ancestors directives. Happy variant worked because it was already allow-listed. Fixes: koala73#3339
|
@fuleinist is attempting to deploy a commit to the World Monitor Team on Vercel. A member of the Team first needs to authorize it. |
Greptile SummaryThis PR bundles three independent fixes: the primary CSP change adding
Confidence Score: 4/5Safe to merge for the CSP and Telegram fixes; the negative-TTL logic in One P1 finding: the
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[listMilitaryFlights request] --> B[cachedFetchJson - live Redis key]
B -->|hit| C[Filter to bounds & return]
B -->|miss / null| D{now - lastStaleAttempt < 30s?}
D -->|yes| E[return flights: empty ⚠️ even if stale data exists]
D -->|no| F[lastStaleAttempt = now]
F --> G[fetchStaleFallback]
G -->|has data| H[Filter to bounds & return stale flights]
G -->|null / empty| I[return flights: empty]
H -.->|next 30s requests also fail live| E
Reviews (1): Last reviewed commit: "fix(header): add energy.worldmonitor.app..." | Re-trigger Greptile |
| lastStaleAttempt = now; | ||
| const staleFlights = await fetchStaleFallback(); | ||
| if (staleFlights && staleFlights.length > 0) { | ||
| return { flights: filterFlightsToBounds(staleFlights, requestBounds), clusters: [], pagination: undefined }; |
There was a problem hiding this comment.
Stale fallback suppressed for 30 s even after a successful stale read
lastStaleAttempt is set unconditionally (line 280) before the async stale fetch, so if fetchStaleFallback() returns valid flights on request N, every subsequent request within the next 30 s that also fails the live path hits the NEG_TTL guard and returns { flights: [] } — even though the stale Redis key still holds valid data. Users see empty flight data for up to 30 s when stale data is available.
The guard's stated purpose is to avoid a redundant Redis call "if the upstream is still down"; it should therefore only update lastStaleAttempt when the stale read yields nothing:
const staleFlights = await fetchStaleFallback();
if (staleFlights && staleFlights.length > 0) {
return { flights: filterFlightsToBounds(staleFlights, requestBounds), clusters: [], pagination: undefined };
}
// Stale key also empty — start the suppression window now.
lastStaleAttempt = Date.now();|
|
||
| /** Negative TTL: suppress re-fetch of stale key for 30 s after a failed live fetch. */ | ||
| const NEG_TTL_MS = 30_000; | ||
| let lastStaleAttempt = 0; // Unix-ms timestamp of most recent stale-read attempt |
There was a problem hiding this comment.
Module-level singleton not effective across server instances
lastStaleAttempt is a process-local variable. In a horizontally-scaled deployment each instance maintains its own suppression window, so N instances can all independently fire stale Redis reads within the same 30 s window. If reducing per-key Redis pressure is important at scale, consider a short-lived Redis key (e.g. SET military:flights:stale:neg-ttl PX 30000 NX) as the suppression token instead.
|
Superseded by #3394 — the CSP-only portion of the energy.worldmonitor.app wiring landed there along with the matching middleware/checkout/Tauri/favicon edits. The unrelated changes in this branch (telegram retry guard, military flights handler) should ship in their own PR if still wanted. |
…3394) DNS (Cloudflare) and the Vercel domain are already provisioned by the operator; this lands the matching code-side wiring so the variant actually resolves and renders correctly. Changes: middleware.ts - Add `'energy.worldmonitor.app': 'energy'` to VARIANT_HOST_MAP. This also auto-includes the host in ALLOWED_HOSTS via the spread on line 87. - Add `energy` entry to VARIANT_OG with the Energy-Atlas-specific title + description from `src/config/variant-meta.ts:130-152`. OG image points at `https://energy.worldmonitor.app/favico/energy/og-image.png`, matching the per-variant convention used by tech / finance / commodity / happy. vercel.json - Add `https://energy.worldmonitor.app` to BOTH `frame-src` and `frame-ancestors` in the global Content-Security-Policy header. Without this, the variant subdomain would render but be blocked from being framed back into worldmonitor.app for any embedded flow (Slack/LinkedIn previews, future iframe widgets, etc.). This supersedes the CSP-only portion of PR #3359 (which mixed CSP with unrelated relay/military changes). convex/payments/checkout.ts:108-117 - Add `https://energy.worldmonitor.app` to the checkout returnUrl allowlist. Without this, a PRO upgrade flow initiated from the energy subdomain would fail with "Invalid returnUrl" on Convex. src-tauri/tauri.conf.json:32 - Add `https://energy.worldmonitor.app` to the Tauri desktop CSP frame-src so the desktop app can embed the variant the same way it embeds the other 4. public/favico/energy/* (NEW, 7 files) - Stub the per-variant favicon directory by copying the root-level WorldMonitor brand assets (android-chrome 192/512, apple-touch, favicon 16/32/ico, og-image). This keeps the launch unblocked on design assets — every referenced URL resolves with valid bytes from day one. Replace with energy-themed designs in a follow-up PR; the file paths are stable. Other variant subdomains already on main (tech / finance / commodity / happy) are unchanged. APP_HOSTS in src/services/runtime.ts already admits any `*.worldmonitor.app` via `host.endsWith('.worldmonitor.app')` on line 226, so no edit needed there. Closes gaps §L #9, #10, #11 in docs/internal/energy-atlas-registry-expansion.md.
Summary
The Energy variant link in the header navigation bar points to
https://energy.worldmonitor.appbut that domain was missing from the Vercel Content-Security-Policyframe-srcandframe-ancestorsdirectives. This causes the browser to block the iframe embedding of that domain when the app is embedded in iframes (e.g. via widget embeds).The Happy variant worked because
happy.worldmonitor.appwas already included in the allow-list.Fix
Added
https://energy.worldmonitor.appto both theframe-srcandframe-ancestorsCSP directives invercel.json.Testing
energy.worldmonitor.appwas not reachable via DNS (the Vercel project likely needs to be set up separately)vercel.jsonand requires a Vercel redeployFixes: #3339