mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-05-13 18:46:21 +02:00
5841acd06d830df14ebc8fa2f1a80eeecabfb2b0
83 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d1acabf41d |
docs: replace redundant TOC landing page with proper introduction (#1478)
The documentation.mdx page was just a table of contents duplicating the sidebar navigation, with irrelevant variant demo links at the top. Replace with a concise introduction that explains what World Monitor is, what users can do with it, quick navigation links, and the license summary. |
||
|
|
ddda3e58a1 |
docs: fix license from MIT to AGPL-3.0 and add license guide (#1476)
getting-started.mdx incorrectly stated "MIT" as the license. The actual license is AGPL-3.0-only (per LICENSE file and package.json). Adds a comprehensive license section to contributing.mdx explaining: - What AGPL-3.0 means in plain language - Rights and obligations table - Common scenarios (self-hosting, public deployment, API usage, PRs) - Why AGPL was chosen for this project |
||
|
|
a286580463 |
fix(docs): add .mintignore and enhance MDX lint for Mintlify compatibility (#1474)
* fix(docs): add .mintignore to exclude non-MDX-safe files
roadmap-pro.md contains curly braces ({hash}, {userId}) that Mintlify's
MDX parser interprets as JSX expressions, causing deploy failures.
Exclude it along with PRESS_KIT.md and Docs_To_Review/ (internal files
not in navigation).
* fix(docs): enhance MDX lint to catch curly braces and .md files
Mintlify parses all docs/ files as MDX, treating {expr} as JSX
expressions. The existing lint only checked .mdx files for bare angle
brackets. Now also checks:
- .md files (Mintlify processes these too)
- Bare curly braces {word} outside code fences/spans
- Respects docs/.mintignore for excluded files
|
||
|
|
09a582e089 |
fix(docs): correct numerical inaccuracies, add missing features and variants (#1472)
* fix(docs): correct variant count from 4 to 5 Updated overview.mdx and architecture.mdx to reflect all 5 platform variants (World Monitor, Tech Monitor, Happy Monitor, Finance Monitor, Commodity Monitor). Previously only 2 were listed in the table and the text said "four". * fix(docs): correct all numerical inaccuracies across documentation Updated counts verified against actual codebase: - AI datacenters: 111 → 313 - Undersea cables: 55 → 86 - Map layers: 45 → 49 - News sources: 80+ → 344 - Service domains: 22 → 24 - CII countries: 20 → 24 - Military bases: 210/220+ → 226 - Strategic ports: 61/83 → 62 - Airports: 30/107 → 111 - Chokepoints: 6/8 → 9 - Signal entities: 100+/600+ → 66 - Variant count: four → five (added Commodity Monitor) * docs(overview): add Happy, Finance, and Commodity Monitor sections Added detailed documentation sections for the three previously undocumented platform variants, including layers, panels, and news categories for each. * docs(features): document 13 previously undocumented features Added: trade routes, FIRMS fire detection, webcam surveillance, country brief, aviation intelligence panel, climate anomalies, displacement tracking, Gulf economies, WTO trade policy, central banks & BIS, market watchlist, NOTAM closure detection, and offline ML capabilities. * docs: standardize terminology, add cross-references, fix stale refs Phase 4: Renamed "News Importance Scoring" to "Headline Scoring", "Signal Correlation" to "Cross-Stream Correlation". Added cross-refs between CII/convergence, CORS/API-key, maritime/finance chokepoints. Deduplicated Population Exposure content. Clarified hotspot vs focal point distinction. Phase 5: Rewrote daily_stock_analysis as historical context. Added legacy note for api/*.js files. Added OREF 24h rolling history boost and GPS jamming classification thresholds to algorithms.mdx. Fixed orbital-surveillance tier table contradictions. Phase 6: Fixed orphaned markdown separator in orbital-surveillance. * fix(docs): catch remaining stale numbers in secondary doc files Fixed stale counts in data-sources.mdx, strategic-risk.mdx, documentation.mdx, ai-intelligence.mdx, PRESS_KIT.md, and roadmap-pro.md that were missed in the initial pass. * fix(docs): address review findings from fresh-eyes pass - desktop-app.mdx: "four variants" → "five", "22 services" → "24" - infrastructure-cascade.mdx: chokepoints 8 → 9, node total 279 → 350 - data-sources.mdx: chokepoints 8 → 9 - overview.mdx: remove duplicated intro sentence - getting-started.mdx: fix stale file tree comments (AI clusters, airports, entities) |
||
|
|
1f38dea225 |
docs: restructure documentation into focused, navigable pages (#1465)
* docs: restructure documentation into focused, navigable pages (#docs-reorg) Break the 4096-line documentation.mdx monolith into 13 focused pages organized by feature area. Reorganize Mintlify navigation from 5 generic groups into 7 feature-based groups. Move Orbital Surveillance from Infrastructure to Map Layers where it belongs. - Extract: signal-intelligence, features, overview, hotspots, CII, geographic-convergence, strategic-risk, infrastructure-cascade, military-tracking, maritime-intelligence, natural-disasters, contributing, getting-started - Append to: architecture.mdx (9 sections), ai-intelligence.mdx (3 sections) - Fix legacy .md links in map-engine.mdx, maps-and-geocoding.mdx - Slim documentation.mdx to an 80-line index/hub page - Eliminate duplicate content that caused repeated headings * fix(docs): remove duplicate H1 headings from all Mintlify pages Mintlify auto-renders the frontmatter `title` as an H1, so having `# Title` in the body creates a doubled heading on every page. Remove the redundant H1 (and repeated description lines) from all 31 .mdx files. |
||
|
|
dce6b77b27 |
fix(docs): remove invalid colors.anchors from Mintlify config (#1464)
Mintlify v2 schema rejects colors.anchors as unrecognized, blocking deployment validation. Added in #1463 but not a valid schema key. |
||
|
|
2a7d7fc3fe |
fix: standardize brand name to "World Monitor" with space (#1463)
Replace "WorldMonitor" with "World Monitor" in all user-facing display text across blog posts, docs, layouts, structured data, footer, offline page, and X-Title headers. Technical identifiers (User-Agent strings, X-WorldMonitor-Key headers, @WorldMonitorApp handle, function names) are preserved unchanged. Also adds anchors color to Mintlify docs config to fix blue link color in dark mode. |
||
|
|
878e30c371 |
style(docs): align Mintlify header/footer with site design (#1455)
* style(docs): align Mintlify header/footer with site design system - Navbar: Blog, Dashboard, Pro, GitHub (matching site header) - Primary CTA: "Get Early Access" green button linking to /pro#waitlist - Colors: switch from blue (#3b82f6) to green (#4ade80) accent - Footer: Dashboard, Pro, Blog + Community (GitHub, Discussions, X) - Add X/Twitter social link - Normalize all URLs to www.worldmonitor.app - Name simplified to "World Monitor" (matching site branding) * feat: add Docs link to dashboard, blog, and pro navigation Add link to /docs across all site surfaces: - Blog header nav and footer - Dashboard footer nav - Pro page footer (main + enterprise) |
||
|
|
8766b45a57 |
fix(docs): comprehensive MDX angle bracket escaping (#1453)
* fix(docs): comprehensive MDX angle bracket escaping Escape all bare < patterns that MDX interprets as JSX across documentation.mdx, algorithms.mdx, ai-intelligence.mdx, data-sources.mdx, finance-data.mdx, relay-parameters.mdx, and maps-and-geocoding.mdx. * feat(lint): add MDX bare angle bracket lint to pre-push Adds tests/mdx-lint.test.mjs that scans all docs/*.mdx files for bare <digit and <hyphen patterns outside code fences. These break Mintlify's MDX parser. Wired into .husky/pre-push so issues are caught before reaching Mintlify. |
||
|
|
60fa0c5907 |
fix(docs): escape angle brackets for MDX compatibility (#1452)
MDX interprets bare <digit patterns as JSX tags. Escape with < in architecture.mdx, data-sources.mdx, and documentation.mdx. |
||
|
|
caa3e9f82f |
fix(docs): rename doc files to lowercase for Mintlify (#1451)
* fix(docs): rename doc files to lowercase kebab-case for Mintlify Mintlify serves pages at lowercase URLs. Uppercase filenames caused 404s on the Documentation tab. Renames all 18 doc files, updates docs.json references, and fixes internal cross-links. * fix(docs): rename .md to .mdx for Mintlify compatibility Mintlify expects .mdx files. Plain .md files were not being found, causing 404s on all documentation pages. * feat(docs): add navbar links and footer with site variants - Navbar: Live App, Tech, Finance, Blog links + GitHub CTA - Footer: World Monitor variants + Resources columns - Logo links back to worldmonitor.app |
||
|
|
f292282ff7 |
fix(docs): add required theme field to Mintlify docs.json (#1450)
* fix(docs): add required theme field to Mintlify docs.json Mintlify v2 requires a theme discriminator. Without it, deployment fails with "Invalid discriminator value" error. * fix(docs): migrate docs.json to Mintlify v2 schema - navigation: object with tabs/groups instead of array - Remove colors.background (unrecognized in v2) - topbarLinks/topbarCtaButton → navbar - footerSocials → footer.socials - openapi: single string per group instead of array - Split docs into Documentation + API Reference tabs |
||
|
|
3d4c2fbdf4 |
chore(docs): trigger Mintlify deployment (#1449)
Trivial name change to force Mintlify GitHub App to detect docs changes after monorepo setting was updated in the dashboard. |
||
|
|
33a423b87f |
fix(docs): rename mint.json to docs.json for Mintlify v2 (#1446)
Mintlify deprecated mint.json in favor of docs.json. The dashboard expects docs.json to exist in the configured directory. |
||
|
|
01a7a791ab |
feat(docs): add Mintlify documentation site at /docs (#1444)
Set up Mintlify to serve docs at worldmonitor.app/docs via Vercel rewrites proxying to worldmonitor.mintlify.dev. - Add mint.json with navigation (5 doc groups + 22 OpenAPI API references) - Add Vercel rewrites for /docs, exclude from SPA catch-all and no-cache rules - Exclude /docs from CSP headers (Mintlify manages its own scripts/styles) - Add frontmatter to all 18 navigation docs for proper Mintlify rendering - Fix internal links from ./FILE.md to /FILE format for Mintlify routing - Convert ../path links to GitHub URLs (source code references) - Add .mintlifyignore for internal docs (Docs_To_Review, roadmap, etc.) - Copy app icon as logo.png and favicon.png |
||
|
|
99b1d3f5bf |
docs: add health endpoints reference (#1433)
Document /api/health and /api/seed-health with response schemas, key classifications, staleness thresholds, and monitoring integration examples. Linked from the main documentation index. |
||
|
|
78e7ae546e |
feat(natural): add tropical cyclone tracking from NHC and GDACS (#1357)
* feat(natural): add tropical cyclone tracking from NHC and GDACS Integrate NHC ArcGIS REST API (15 storm slots across AT/EP/CP basins) and GDACS TC field extraction to provide real-time tropical cyclone data with forecast tracks, uncertainty cones, and historical track paths. - Proto: add optional TC fields (storm_id, wind_kt, pressure_mb, etc.) plus ForecastPoint, PastTrackPoint, CoordRing messages - Server/seed: NHC two-pass query (forecast points then detail layers), GDACS wind/pressure parsing, Saffir-Simpson classification, dedup strategy (NHC > GDACS > EONET), pressureMb validation (850-1050), advisory date with Number.isFinite guard - Globe: dashed red forecast track, per-segment wind-colored past track, semi-transparent orange forecast cone polygon - Popup: TC details panel with color-coded category badge, wind/pressure - Frontend mapper: forward all TC fields, convert CoordRing to number[][][] * fix(natural): improve GDACS dedup, NHC classification, and TC popup i18n - GDACS dedup now checks name + geographic proximity instead of name-only - NHC classification uses stormtype field for subtropical/post-tropical - TC popup labels use t() for localization instead of hardcoded English * feat(map): add cyclone-specific deck.gl layers for 2D map - Storm center ScatterplotLayer with Saffir-Simpson wind coloring - Past track PathLayer with per-segment wind-speed color ramp - Forecast track PathLayer with dashed line via PathStyleExtension - Cone PolygonLayer for forecast uncertainty visualization - Tooltip and click routing for all new storm layer IDs * fix(map): remove click routing for synthetic storm track/cone layers Track and cone layers carry lightweight objects without full NaturalEvent fields. Clicking them would pass incomplete data to the popup renderer. Only storm-centers-layer (which holds the full NaturalEvent) routes to the natEvent popup. Tracks and cones remain tooltip-only. * fix(map): attach parent NaturalEvent to synthetic storm layers for clicks Synthetic track/cone objects now carry _event reference to the parent NaturalEvent. Click handler unwraps _event before passing to popup, so clicking any storm element opens the full TC popup. |
||
|
|
c5d196f29e |
feat(scoring): port frontend CII scoring formulas to server (#1351)
* feat(scoring): port frontend CII scoring formulas to server Port the frontend's proven scoring formulas (log2/sqrt scaling, fatality splits, outage/GPS severity tiers, OREF integration, advisory floors and boosts) to the server-side CII computation so scores are data-driven and self-correcting. - Add MX, BR, AE to TIER1_COUNTRIES (21 to 24) - Disable relay CII seed loop (RPC computes on-demand) - Add activeAlertCount to OREF Redis payload - Expand CountrySignals with fatality split, outage tiers, GPS severity, OREF fields, advisory level, and high severity strikes - Port calcUnrestScore (log2 dampening, protest fatality boost, outage severity tiers) - Port calcConflictScore (weighted ACLED events, sqrt fatalities, OREF boost, strike severity) - Port calcSecurityScore (GPS high/medium weighting, cap 35) - Add advisory floors (do-not-travel 60, reconsider 50) and boosts - Add OREF blend boost for IL - Fix fires fallback (empty array is truthy) and climate severity nullish coalescing - Add 14 fixture tests covering floors, boosts, scaling, and edge cases * docs: update CII scoring documentation to match server-side formulas Update ALGORITHMS.md and DOCUMENTATION.md to reflect the ported scoring formulas: 24 tier-1 countries, log2/sqrt scaling, outage/GPS severity tiers, OREF integration, advisory floors/boosts, and data source table. |
||
|
|
88846d75c8 |
docs: add press kit with project overview, data sources, and FAQ (#1341)
Non-technical document for press and external audiences covering what World Monitor does, where data comes from, key metrics, scoring systems, privacy posture, roadmap highlights, and 12 FAQs. |
||
|
|
6cec985b94 |
fix(railway): use npm install instead of npm ci for Railpack compat (#1290)
* fix(railway): use npm install instead of npm ci for Railpack compat Railpack runs the install step before source files are fully copied into the build container, so package-lock.json isn't available when npm ci executes. Switch to npm install --omit=dev which doesn't require a pre-existing lockfile. * fix(docs): add blank lines around lists in roadmap-pro.md Auto-fix MD032 markdownlint violations (blanks-around-lists). |
||
|
|
595e3dbb86 |
feat: premium finance stock analysis suite (#1268)
* Add premium finance stock analysis suite * docs: link premium finance from README Add Premium Stock Analysis entry to the Finance & Markets section with a link to docs/PREMIUM_FINANCE.md. * fix: address review feedback on premium finance suite - Chunk Redis pipelines into batches of 200 (Upstash limit) - Add try-catch around cachedFetchJson in backtest handler - Log warnings on Redis pipeline HTTP failures - Include name in analyze-stock cache key to avoid collisions - Change analyze-stock and backtest-stock gateway cache to 'slow' - Add dedup guard for concurrent ledger generation - Add SerpAPI date pre-filter (tbs=qdr:d/w) - Extract sanitizeSymbol to shared module - Extract buildEmptyAnalysisResponse helper - Fix RSI to use Wilder's smoothing (matches TradingView) - Add console.warn for daily brief summarization errors - Fall back to stale data in loadStockBacktest on error - Make daily-market-brief premium on all platforms - Use word boundaries for short token headline matching - Add stock-analysis 15-min refresh interval - Stagger stock-analysis and backtest requests (200ms) - Rename signalTone to stockSignalTone |
||
|
|
3cc3290ab7 |
docs(pro): implementation roadmap for Pro features (#1279)
* docs(pro): add implementation roadmap for Pro features 7-phase plan covering auth (Clerk), Convex backend expansion, payments (Stripe), feature gating, user dashboard, pro features, and enterprise. Includes 33 GitHub issues breakdown with labels and priorities. * docs(pro): comprehensive roadmap with 48 detailed GitHub issues Codex-reviewed (3 rounds, approved). 8 phases covering: - Auth (Clerk), schema expansion, Stripe payments, feature gating - User dashboard, pro features, API tier, enterprise - Entitlement projection table, webhook safety, cache invalidation - Security: API key hashing, envelope encryption, trusted-proxy rules - Data retention, audit logging, account deletion with anonymization * docs(pro): remove codex review status section |
||
|
|
9772548d83 |
feat: add orbital surveillance layer with real-time satellite tracking (#1278)
Track ~80-120 intelligence-relevant satellites on the 3D globe using CelesTrak TLE data and client-side SGP4 propagation (satellite.js). Satellites render at actual orbital altitude with country-coded colors, 15-min orbit trails, and ground footprint projections. Architecture: Railway seeds TLEs every 2h → Redis → Vercel CDN (1h cache) → browser does SGP4 math every 3s (zero server cost for real-time movement). - New relay seed loop (ais-relay.cjs) fetching military + resource groups - New edge handler (api/satellites.js) with 10min cache + negative cache - Frontend service with circuit breaker and propagation lifecycle - GlobeMap integration: markers, trails (pathsData), footprints, tooltips - Layer registry as globe-only "Orbital Surveillance" with i18n (21 locales) - Full documentation at docs/ORBITAL_SURVEILLANCE.md with roadmap - Fix pre-existing SearchModal TS error (non-null assertion) |
||
|
|
d6c9176213 |
Revert "fix(scripts): sync package-lock.json with h3-js dependency (#1254)" (#1256)
This reverts commit
|
||
|
|
4816e27d3c |
fix(scripts): sync package-lock.json with h3-js dependency (#1254)
* Add premium stock analysis for finance variant * fix(scripts): sync package-lock.json with h3-js dependency Railway npm ci requires lock file in sync with package.json. * fix(market): narrow undefined check for TS strict null safety |
||
|
|
5ad9057c67 |
feat(pro): restructure landing page with hybrid draft/current layout (#1207)
* feat(pro): restructure landing page with hybrid draft/current layout
Reorganize the Pro landing page into a 13-section structure that combines
the best of the external copy draft with existing high-value components:
New sections added:
- Two-path split (Pro vs Enterprise) right after social proof
- "Why upgrade" value props (Less noise, Faster, Control, Deeper)
- Audience personas (Journalists, Investors, Researchers, Security, Teams)
- Final dual CTA ("Get Pro" + "Talk to Sales")
Kept from current page:
- Live dashboard iframe embed
- Source marquee (43 scrolling sources)
- Slack morning brief mock
- API section with code example (separate tier)
- Enterprise specifics (air-gapped, MCP, white-label, satellite)
Copy updates:
- Hero: "for serious users and organizations" + mission line
- FAQ: warmer tone, 8 questions including "Is this only for conflict monitoring?"
- Schema markup updated to match new FAQ
* fix(pro): CRO quick wins — unified CTA, benefit-first hero, invisible Turnstile
- Unify all CTAs to "Reserve Your Early Access"
- Hero subtitle rewritten benefit-first ("Understand global events faster")
- Add "Launching March 2026" timeline badge
- Make Turnstile CAPTCHA invisible (size: 'invisible')
- Replace Enterprise mailto with inline contact form
- Move audience personas higher, add Gov & Energy traders, remove Journalists
- Update FAQ structured data in source template
- Remove unused Newspaper import
* fix(pro): update README image to jpg + add iframe fallback image
- README: reference worldmonitor-7-mar-2026.jpg instead of png
- Pro landing: add fallback image behind iframe for loading state
* feat(pro): dedicated enterprise page with contact form via hash routing
- Move enterprise contact form to dedicated #enterprise page
- Enterprise page has: hero, feature grid, use cases, and contact form
- Pro page enterprise section now links to #enterprise instead of inline form
- Hash routing: #enterprise → EnterprisePage, everything else → Pro landing
- Re-render Turnstile widgets on page transitions
* fix(pro): use Vite asset import for iframe fallback image (cached path)
Import dashboard fallback image via Vite so it gets content-hashed
into /pro/assets/ — hits CF 1-month immutable cache rule.
Placeholder jpg included; replace with real screenshot before deploy.
* fix(pro): add real dashboard screenshot for iframe fallback + README
Replace placeholder with actual 326K screenshot. Vite content-hashes
it to /pro/assets/worldmonitor-7-mar-2026-[hash].jpg (CF 1mo cache).
Also added to docs/images/ for README reference.
|
||
|
|
710cf621d8 |
docs: add consolidated CORS guide (#1193)
Create docs/CORS.md with allowed origins, step-by-step pattern for adding CORS to new edge functions, and guidance on adding new origins. Link from docs/ARCHITECTURE.md under API & Data Pipeline. |
||
|
|
526a73a4d1 |
docs: maps infrastructure and geocoding reference (#1165)
* docs: add maps infrastructure and geocoding reference New MAPS_AND_GEOCODING.md covers R2 CDN architecture (maps.worldmonitor.app), the country geometry service, boundary override mechanism (#1044), and common pitfalls. Cross-referenced from MAP_ENGINE.md. * docs: align with PR #1150 boundary override implementation - Data flow: sequential load (base first, then override with 3s timeout) instead of Promise.all parallel fetch - Override URL: R2 CDN (maps.worldmonitor.app), base from /data/ (Vercel) - Override lookup: Map-based O(1) matching, not find() O(n) - Common mistakes: timeout guidance replaces stale parallel fetch advice |
||
|
|
a61ec28ba3 |
chore: reduce default map layers, add user-requests doc (#1141)
* fix(desktop): settings UI redesign, IPC security hardening, release profile Settings window: - Add titlebar drag region (macOS traffic light clearance) - Move Export/Import from Overview to Debug & Logs section - Category cards grid changed to 3-column layout Security (IPC trust boundary): - Add require_trusted_window() to get_desktop_runtime_info, open_url, open_live_channels_window_command, open_youtube_login - Validate base_url in open_live_channels_window_command (localhost-only http) Performance: - Add [profile.release] with fat LTO, codegen-units=1, strip, panic=abort - Reuse reqwest::Client via app state with connection pooling - Debounce window resize handler (150ms) in EventHandlerManager * feat(pro): add Pro waitlist landing page with referral system - React 19 + Vite 6 + Tailwind v4 landing page at /pro - Cloudflare Turnstile + honeypot bot protection - Resend transactional confirmation emails with branded template - Viral referral system: unique codes, position tracking, social share - Convex schema: referralCode, referredBy, referralCount fields + counters table - O(1) position counter pattern instead of O(n) collection scan - SEO: structured data, sitemap, scrolling source marquee - Vercel routing: /pro rewrite + cache headers + SPA exclusion - XSS-safe DOM rendering (no innerHTML with user data) * chore: disable natural & economic map layers by default, add user-requests doc Reduce default-on map layers from 11 to 9 for better first-load performance. Natural disasters and economic overlays remain available via the layer toggle — just no longer enabled on first visit. Also adds docs/user-requests.md compiling all feature requests from GitHub issues (55+) and discussions (40+ threads, 391 comments). |
||
|
|
2a76c7cfa3 |
feat(map): add per-provider map theme selector (#1101)
Add a Map Theme dropdown in Settings below Map Tile Provider that lets users pick the basemap visual style. Each provider has different themes: - PMTiles: Black (default), Dark, Grayscale, Light, White - OpenFreeMap: Dark (default), Positron - CARTO: Dark Matter (default), Voyager, Positron Map theme is fully independent of app theme (Auto/Dark/Light) — app theme only affects UI chrome. Theme selection is per-provider and persisted independently in localStorage. Overlay paint colors adapt to the map theme, not the app theme. |
||
|
|
3fdf8ffa1c |
chore: remove dev artifacts and orphaned files from repo root (#1096)
Remove 12 files (~5MB) that don't belong in a public repo: - Dev artifacts: api-cache.json, verbose-mode.json, skills-lock.json - UI prototypes: 3 playground-settings-*.html - Orphaned images: page-load-baseline.png, web-app-overview.jpg, worldmonitor-header.png - tmp/ directory: design prototypes and demo videos Move new-world-monitor.png to docs/images/ and fix README links. Add runtime artifacts to .gitignore to prevent re-commit. |
||
|
|
fce836039b |
feat(map): migrate basemap from CARTO to self-hosted PMTiles on R2 (#1064)
* feat(map): migrate basemap from CARTO to self-hosted PMTiles on Cloudflare R2 Replace CARTO tile provider (frequent 403 errors) with self-hosted PMTiles served from Cloudflare R2. Uses @protomaps/basemaps for style generation with OpenFreeMap as automatic fallback when VITE_PMTILES_URL is unset. - Add pmtiles and @protomaps/basemaps dependencies - Create src/config/basemap.ts for PMTiles protocol registration and style building - Update DeckGLMap.ts to use PMTiles styles (non-happy variants) - Fix fallback detection using data event instead of style.load - Update SW cache rules: replace CARTO/MapTiler with PMTiles NetworkFirst - Add Protomaps preconnect hints in index.html - Bundle pmtiles + @protomaps/basemaps in maplibre chunk - Upload 3.4GB world tiles (zoom 0-10) to R2 bucket worldmonitor-maps * fix(map): use CDN custom domain maps.worldmonitor.app for PMTiles Replace r2.dev URL with custom domain backed by Cloudflare CDN edge. Update preconnect hint and .env.example with production URL. * fix(map): harden PMTiles fallback detection to prevent false triggers - Require 2+ network errors before triggering OpenFreeMap fallback - Use persistent data listener instead of once (clears timeout on first tile load) - Increase fallback timeout to 10s for PMTiles header + initial tile fetch - Add console.warn for map errors to aid debugging - Remove redundant style.load listener (fires immediately for inline styles) * feat(settings): add Map Tile Provider selector in settings Add dropdown in Settings → Map section to switch between: - Auto (PMTiles → OpenFreeMap fallback) - PMTiles (self-hosted) - OpenFreeMap - CARTO Choice persists in localStorage and reloads basemap instantly. * fix(map): make OSS-friendly — default to free OpenFreeMap, hide PMTiles when unconfigured - Default to OpenFreeMap when VITE_PMTILES_URL is unset (zero config for OSS users) - Hide PMTiles/Auto options from settings dropdown when no PMTiles URL configured - If user previously selected PMTiles but env var is removed, gracefully fall back - Remove production URL from .env.example to avoid exposing hosted tiles - Add docs link for self-hosting PMTiles in .env.example * docs: add map tile provider documentation to README and MAP_ENGINE.md Document the tile provider system (OpenFreeMap, CARTO, PMTiles) in MAP_ENGINE.md with self-hosting instructions, fallback behavior, and OSS-friendly defaults. Update README to reference tile providers in the feature list, tech stack, and environment variables table. * fix: resolve rebase conflicts and fix markdown lint errors - Restore OSS-friendly basemap defaults (MAP_PROVIDER_OPTIONS as IIFE, getMapProvider with hasTilesUrl check) - Fix markdown lint: add blank lines after ### headings in README - Reconcile UnifiedSettings import with MAP_PROVIDER_OPTIONS constant |
||
|
|
1d52398690 |
docs: restructure README — 77% smaller with linked detail docs (#1074)
* docs: update README with accurate counts and 9 new feature sections - Fix stale counts: 170+ feeds → 435+, 15 bootstrap keys → 38, 28+ data sources → 31, 20+ search types → 24, panel counts - Add Aviation Intelligence Panel documentation - Add Customizable Market Watchlist section - Add News Importance Scoring algorithm details - Add Railway Seed Data Pipeline table (21 cron jobs) - Add SmartPollLoop adaptive polling documentation - Expand Prediction Markets with 4-tier fetch strategy - Add Iran conflict monitoring layer details - Add Mobile search sheet and FAB section - Expand Regression Testing section (30 files, 554 tests) - Expand Bootstrap Hydration with full 38-key tier listing - Bump version 2.5.24 → 2.5.25 * docs: restructure README from 2375 to 539 lines with linked detail docs Extract detailed content from README into 7 focused documentation files: - docs/ALGORITHMS.md — scoring formulas, detection algorithms, classification - docs/DATA_SOURCES.md — all data sources, feed tiers, collection methods - docs/ARCHITECTURE.md — system design, caching, edge functions, patterns - docs/MAP_ENGINE.md — 3D globe and flat map rendering details - docs/AI_INTELLIGENCE.md — LLM chains, RAG, threat classification - docs/DESKTOP_APP.md — Tauri architecture, sidecar, secret management - docs/FINANCE_DATA.md — market radar, Gulf FDI, stablecoins, BIS, WTO README now has 1-2 line summaries with links to detail docs. Replace manual contributor list with auto-generated contrib.rocks grid. Add documentation index table to docs/DOCUMENTATION.md. Delete docs/DESKTOP_CONFIGURATION.md (merged into DESKTOP_APP.md). |
||
|
|
383f2e9596 |
fix: switch basemap fallback from Stadia Maps to OpenFreeMap (#1042)
Stadia Maps tiles return HTTP 401 without an API key — style.json loads but actual .pbf tile requests fail, leaving the map black with floating data points (issue #1031). Changes: - Switch fallback to OpenFreeMap (free, no auth, CORS *, dark style) - Replace overly broad 'style.json' error match with 'cartocdn.com' - Store style load timeout on instance, clear in destroy() - Update TODO-131 text to reference OpenFreeMap |
||
|
|
9514f52971 |
fix: harden basemap fallback with timeout and broader CORS error matching (#1039)
CARTO basemap CORS errors weren't triggering the Stadia fallback because the error message didn't always match 'Failed to fetch' or 'AJAXError'. Add broader error pattern matching (CORS, NetworkError, style.json) and a 5-second timeout that switches to fallback if style hasn't loaded. Also adds TODO-131 for self-hosted Protomaps + CloudFront tiles to eliminate third-party basemap dependency entirely. |
||
|
|
4de2f74210 |
feat: move EONET/GDACS to server-side with Redis caching (#983)
* feat: move EONET/GDACS to server-side with Redis caching and bootstrap hydration Browser-direct fetches to eonet.gsfc.nasa.gov and gdacs.org caused CORS errors and had no server-side caching. This moves both to the standard Vercel edge → cachedFetchJson → Redis → bootstrap hydration pattern. - Add proto definitions for NaturalService with ListNaturalEvents RPC - Create server handler merging EONET + GDACS with 30min Redis TTL - Add Vercel edge function at /api/natural/v1/list-natural-events - Register naturalEvents in bootstrap SLOW_KEYS for CDN hydration - Replace browser-direct fetches with RPC client + circuit breaker - Delete src/services/gdacs.ts (logic moved server-side) * fix: restore @ts-nocheck on generated files stripped by buf generate |
||
|
|
e53e55e8b3 |
docs: update outdated numbers in Community Promotion Guide (#922)
The promotion guide had stale figures from an earlier version that no longer match the README and current state of the project: - 150+ news feeds → 170+ - 35+ data layers → 40+ - 14 languages → 19 Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
c2f17dec45 |
fix(supply-chain): resolve P1 threat zeroing and P2 geo-first misclassification (#964)
* enhance supply chain panel * fix(supply-chain): resolve P1 threat zeroing and P2 geo-first misclassification P1: threat baseline is now always applied regardless of config staleness — stale config only adds a review-recommended note, never zeros the score. P2: resolveChokepointId now checks text evidence first and only falls back to proximity when text has no confident match. Adds regression test: text "Bab el-Mandeb" with location near Suez correctly resolves to bab_el_mandeb. --------- Co-authored-by: fayez bast <fayezbast15@gmail.com> |
||
|
|
e0a3ca5b62 |
Revert "feat: widget picker, layout tabs & panel close buttons (#882) (#890)" (#918)
This reverts commit
|
||
|
|
c99314ae09 |
feat: widget picker, layout tabs & panel close buttons (#882) (#890)
* docs: add widget picker & layout presets design for #882 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add widget picker implementation plan (#882) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add layout presets config (#882) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add i18n keys for layout presets (#882) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add LayoutTabs component (#882) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add WidgetPicker popover component (#882) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add close button to panel headers (#882) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: wire LayoutTabs and WidgetPicker into header (#882) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: remove Settings > Panels tab, replaced by widget picker (#882) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add layout presets validation tests (#882) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
ee27b91c8f |
refactor(proto): consolidate SummarizeArticleResponse status fields (#813)
* refactor(proto): consolidate SummarizeArticleResponse status fields Replace redundant boolean/string status fields (cached, skipped, error, error_type, reason) with a SummarizeStatus enum and a single status_detail string. This addresses L-8 lint issue by reducing field count and making the response status unambiguous. - Add SummarizeStatus enum (UNSPECIFIED, SUCCESS, CACHED, SKIPPED, ERROR) - Replace cached/skipped booleans with status enum field - Merge error/error_type/reason into statusDetail string - Reserve old field numbers (4, 7, 8, 9, 10) for wire compatibility - Update server handlers and client code to use new fields - Regenerate TypeScript types and OpenAPI docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(proto): restore error and errorType fields for programmatic error handling Keep the new SummarizeStatus enum and statusDetail for consolidated status tracking, but restore the separate error and errorType fields (proto field numbers 9, 10) to preserve structured error information for downstream consumers that need to programmatically handle errors. --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Elie Habib <elie.habib@gmail.com> |
||
|
|
363cf5e71c |
perf(api): convert POST RPCs to GET for CDN caching (#795)
* perf(api): convert classify-event to GET and add summarize-article cache endpoint for CDN caching classify-event (7.9M calls/wk) was POST — bypassing all CDN caching. Converting to GET with static cache tier (1hr) enables Cloudflare edge caching. Degraded responses (no API key, empty title, errors) are marked no-cache to prevent caching error states. summarize-article has repeated headlines too large for URL params. Added a new GetSummarizeArticleCache GET endpoint that looks up Redis by a deterministic cache key. Client computes key via shared buildSummaryCacheKey(), tries GET first (CDN-cacheable), falls back to existing POST on miss. Shared module ensures client/server key parity. * fix(types): wire missing DeductSituation and ListGulfQuotes RPCs, fix tsc errors - Added DeductSituation RPC to intelligence/v1/service.proto (messages existed, RPC declaration was missing) - Added ListGulfQuotes proto + RPC to market/v1/service.proto (handler existed, proto was missing) - Fixed scrapedAt type mismatch in conflict/index.ts (int64 → string) - Added @ts-nocheck to generated files with codegen type bugs - Regenerated all sebuf client/server code * fix(types): fix int64→string type mismatch in list-iran-events.ts |
||
|
|
b423995363 |
feat(conflict): wire UCDP (#760)
* feat(conflict): wire UCDP API access token across full stack UCDP API now requires an `x-ucdp-access-token` header. Renames the stub `UC_DP_KEY` to `UCDP_ACCESS_TOKEN` (matching ACLED convention) and wires it through Rust keychain, sidecar allowlist + verification, handler fetch headers, feature toggles, and desktop settings UI. - Rename UC_DP_KEY → UCDP_ACCESS_TOKEN in type system and labels - Add ucdpConflicts feature toggle with required secret - Add UCDP_ACCESS_TOKEN to Rust SUPPORTED_SECRET_KEYS (24→25) - Add sidecar ALLOWED_ENV_KEYS entry + validation with dynamic GED version probing - Handler sends x-ucdp-access-token header when token is present - UC_DP_KEY fallback in handler for one-release migration window - Update .env.example, desktop-readiness, and docs * feat(conflict): pre-fetch UCDP events via Railway cron + Redis cache Replace the 228-line edge handler that fetched UCDP GED API on every request with a thin Redis reader. The heavy fetch logic (version discovery, paginated backward fetch, 1-year trailing window filter) now runs as a setInterval loop in the Railway relay (ais-relay.cjs) every 6 hours, writing to Redis key conflict:ucdp-events:v1. Changes: - Add UCDP seed loop to ais-relay.cjs (6h interval, 6 pages, 2K cap) - Rewrite list-ucdp-events.ts as thin Redis reader (35 lines) - Add conflict:ucdp-events:v1 to bootstrap batch keys - Protect key from cache-purge via durable data prefix - Add manual-only seed-ucdp-events workflow + standalone script - Rename panel "UCDP Events" → "Armed Conflict Events" in locale - Add 24h TTL + 25h staleness check as safety nets |
||
|
|
4a5b38bddc |
fix(macro): replace hardcoded BTC mining thresholds with Mayer Multiple (#750)
* fix(macro): replace hardcoded BTC mining thresholds with Mayer Multiple The $60k/$40k thresholds were calibrated for 2023 BTC prices and always return 'PROFITABLE' at current price levels. Use the Mayer Multiple (price/SMA200) instead — this adapts to any price regime. Falls back to dollar thresholds only when SMA200 is unavailable. Addresses #197 (L-11). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(macro): rename Mining Cost → Price Momentum, use Mayer Multiple - Replace hardcoded $60k/$40k thresholds with Mayer Multiple (price/SMA200) - Reuse existing `mayerMultiple` variable instead of recomputing - Remove stale hashChange guard — momentum doesn't depend on hashrate - Drop dollar fallback; return UNKNOWN when SMA200 unavailable - Rename signal: MiningCost → PriceMomentum, statuses: STRONG/MODERATE/WEAK - Update proto, OpenAPI specs, generated types, frontend panel, all locales --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Elie Habib <elie.habib@gmail.com> |
||
|
|
a7efa7dda8 |
feat: implement deduct situation feature (#636) (#642)
* Add Security Advisories panel with government travel alerts (#460) * feat: add Security Advisories panel with government travel advisory feeds Adds a new panel aggregating travel/security advisories from official government foreign affairs agencies (US State Dept, AU DFAT Smartraveller, UK FCDO, NZ MFAT). Advisories are categorized by severity level (Do Not Travel, Reconsider, Caution, Normal) with filter tabs by source country. Includes summary counts, auto-refresh, and persistent caching via the existing data-freshness system. * chore: update package-lock.json * fix: event delegation, localization, and cleanup for SecurityAdvisories panel P1 fixes: - Use event delegation on this.content (bound once in constructor) instead of direct addEventListener after each innerHTML replacement — prevents memory leaks and stale listener issues on re-render - Use setContent() consistently instead of mixing with this.content.innerHTML - Add securityAdvisories translations to all 16 non-English locale files (panels name, component strings, common.all key) - Revert unrelated package-lock.json version bump P2 fixes: - Deduplicate loadSecurityAdvisories — loadIntelligenceData now calls the shared method instead of inlining duplicate fetch+set logic - Add Accept header to fetch calls for better content negotiation * feat(advisories): add US embassy alerts, CDC, ECDC, and WHO health feeds Adds 21 new advisory RSS feeds: - 13 US Embassy per-country security alerts (TH, AE, DE, UA, MX, IN, PK, CO, PL, BD, IT, DO, MM) - CDC Travel Notices - 5 ECDC feeds (epidemiological, threats, risk assessments, avian flu, publications) - 2 WHO feeds (global news, Africa emergencies) Panel gains a Health filter tab for CDC/ECDC/WHO sources. All new domains added to RSS proxy allowlist. i18n "health" key added across all 17 locales. * feat(cache): add negative-result caching to cachedFetchJson (#466) When upstream APIs return errors (HTTP 403, 429, timeout), fetchers return null. Previously null results were not cached, causing repeated request storms against broken APIs every refresh cycle. Now caches a sentinel value ('__WM_NEG__') with a short 2-minute TTL on null results. Subsequent requests within that window get null immediately without hitting upstream. Thrown errors (transient) skip sentinel caching and retry immediately. Also filters sentinels from getCachedJsonBatch pipeline reads and fixes theater posture coalescing test (expected 2 OpenSky fetches for 2 theater query regions, not 1). * feat: convert 52 API endpoints from POST to GET for edge caching (#468) * feat: convert 52 API endpoints from POST to GET for edge caching Convert all cacheable sebuf RPC endpoints to HTTP GET with query/path parameters, enabling CDN edge caching to reduce costs. Flatten nested request types (TimeRange, PaginationRequest, BoundingBox) into scalar query params. Add path params for resource lookups (GetFredSeries, GetHumanitarianSummary, GetCountryStockIndex, GetCountryIntelBrief, GetAircraftDetails). Rewrite router with hybrid static/dynamic matching for path param support. Kept as POST: SummarizeArticle, ClassifyEvent, RecordBaselineSnapshot, GetAircraftDetailsBatch, RegisterInterest. Generated with sebuf v0.9.0 (protoc-gen-ts-client, protoc-gen-ts-server). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add rate_limited field to market response protos The rateLimited field was hand-patched into generated files on main but never declared in the proto definitions. Regenerating wiped it out, breaking the build. Now properly defined in both ListEtfFlowsResponse and ListMarketQuotesResponse protos. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: remove accidentally committed .planning files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add Cloudflare edge caching infrastructure for api.worldmonitor.app (#471) Route web production RPC traffic through api.worldmonitor.app via fetch interceptor (installWebApiRedirect). Add default Cache-Control headers (s-maxage=300, stale-while-revalidate=60) on GET 200 responses, with no-store override for real-time endpoints (vessel snapshot). Update CORS to allow GET method. Skip Vercel bot middleware for API subdomain using hostname check (non-spoofable, replacing CF-Ray header approach). Update desktop cloud fallback to route through api.worldmonitor.app. * fix(beta): eagerly load T5-small model when beta mode is enabled BETA_MODE now couples the badge AND model loading — the summarization-beta model starts loading on startup instead of waiting for the first summarization call. * fix: move 5 path-param endpoints to query params for Vercel routing (#472) Vercel's `api/[domain]/v1/[rpc].ts` captures one dynamic segment. Path params like `/get-humanitarian-summary/SA` add an extra segment that has no matching route file, causing 404 on both OPTIONS preflight and direct requests. These endpoints were broken in production. Changes: - Remove `{param}` from 5 service.proto HTTP paths - Add `(sebuf.http.query)` annotations to request message fields - Update generated client/server code to use URLSearchParams - Update OpenAPI specs (YAML + JSON) to declare query params - Add early-return guards in 4 handlers for missing required params - Add happy.worldmonitor.app to runtime.ts redirect hosts Affected endpoints: - GET /api/conflict/v1/get-humanitarian-summary?country_code=SA - GET /api/economic/v1/get-fred-series?series_id=T10Y2Y&limit=120 - GET /api/market/v1/get-country-stock-index?country_code=US - GET /api/intelligence/v1/get-country-intel-brief?country_code=US - GET /api/military/v1/get-aircraft-details?icao24=a12345 * fix(security-advisories): route feeds through RSS proxy to avoid CORS blocks (#473) - Advisory feeds were fetched directly from the browser, hitting CORS on all 21 feeds (US State Dept, AU Smartraveller, US Embassies, ECDC, CDC, WHO). Route through /api/rss-proxy on web, keep proxyUrl for desktop. - Fix double slash in ECDC Avian Influenza URL (323//feed → 323/feed) - Add feeds.news24.com to RSS proxy allowlist (was returning 403) * feat(cache): tiered edge Cache-Control aligned to upstream TTLs (#474) * fix: move 5 path-param endpoints to query params for Vercel routing Vercel's `api/[domain]/v1/[rpc].ts` captures one dynamic segment. Path params like `/get-humanitarian-summary/SA` add an extra segment that has no matching route file, causing 404 on both OPTIONS preflight and direct requests. These endpoints were broken in production. Changes: - Remove `{param}` from 5 service.proto HTTP paths - Add `(sebuf.http.query)` annotations to request message fields - Update generated client/server code to use URLSearchParams - Update OpenAPI specs (YAML + JSON) to declare query params - Add early-return guards in 4 handlers for missing required params - Add happy.worldmonitor.app to runtime.ts redirect hosts Affected endpoints: - GET /api/conflict/v1/get-humanitarian-summary?country_code=SA - GET /api/economic/v1/get-fred-series?series_id=T10Y2Y&limit=120 - GET /api/market/v1/get-country-stock-index?country_code=US - GET /api/intelligence/v1/get-country-intel-brief?country_code=US - GET /api/military/v1/get-aircraft-details?icao24=a12345 * feat(cache): add tiered edge Cache-Control aligned to upstream TTLs Replace flat s-maxage=300 with 5 tiers (fast/medium/slow/static/no-store) mapped per-endpoint to respect upstream Redis TTLs. Adds stale-if-error resilience headers and X-No-Cache plumbing for future degraded responses. X-Cache-Tier debug header gated behind ?_debug query param. * fix(tech): use rss() for CISA feed, drop build from pre-push hook (#475) - CISA Advisories used dead rss.worldmonitor.app domain (404), switch to rss() helper - Remove Vite build from pre-push hook (tsc already catches errors) * fix(desktop): enable click-to-play YouTube embeds + CISA feed fixes (#476) * fix(tech): use rss() for CISA feed, drop build from pre-push hook - CISA Advisories used dead rss.worldmonitor.app domain (404), switch to rss() helper - Remove Vite build from pre-push hook (tsc already catches errors) * fix(desktop): enable click-to-play for YouTube embeds in WKWebView WKWebView blocks programmatic autoplay in cross-origin iframes regardless of allow attributes, Permissions-Policy, mute-first retries, or secure context. Documented all 10 approaches tested in docs/internal/. Changes: - Switch sidecar embed origin from 127.0.0.1 to localhost (secure context) - Add MutationObserver + retry chain as best-effort autoplay attempts - Use postMessage('*') to fix tauri://localhost cross-origin messaging - Make sidecar play overlay non-interactive (pointer-events:none) - Fix .webcam-iframe pointer-events:none blocking clicks in grid view - Add expand button to grid cells for switching to single view on desktop - Add http://localhost:* to CSP frame-src in index.html and tauri.conf.json * fix(gateway): convert stale POST requests to GET for backwards compat (#477) Stale cached client bundles still send POST to endpoints converted to GET in PR #468, causing 404s. The gateway now parses the POST JSON body into query params and retries the match as GET. * feat(proxy): add Cloudflare edge caching for proxy.worldmonitor.app (#478) Add CDN-Cache-Control headers to all proxy endpoints so Cloudflare can cache responses at the edge independently of browser Cache-Control: - RSS: 600s edge + stale-while-revalidate=300 (browser: 300s) - UCDP: 3600s edge (matches browser) - OpenSky: 15s edge (browser: 30s) for fresher flight data - WorldBank: 1800s/86400s edge (matches browser) - Polymarket: 120s edge (matches browser) - Telegram: 10s edge (matches browser) - AIS snapshot: 2s edge (matches browser) Also fixes: - Vary header merging: sendCompressed/sendPreGzipped now merge existing Vary: Origin instead of overwriting, preventing cross-origin cache poisoning at the edge - Stale fallback responses (OpenSky, WorldBank, Polymarket, RSS) now set Cache-Control: no-store + CDN-Cache-Control: no-store to prevent edge caching of degraded responses - All no-cache branches get CDN-Cache-Control: no-store - /opensky-reset gets no-store (state-changing endpoint) * fix(sentry): add noise filters for 4 unresolved issues (#479) - Tighten AbortError filter to match "AbortError: The operation was aborted" - Filter "The user aborted a request" (normal navigation cancellation) - Filter UltraViewer service worker injection errors (/uv/service/) - Filter Huawei WebView __isInQueue__ injection * feat: configurable VITE_WS_API_URL + harden POST→GET shim (#480) * fix(gateway): harden POST→GET shim with scalar guard and size limit - Only convert string/number/boolean values to query params (skip objects, nested arrays, __proto__ etc.) to prevent prototype pollution vectors - Skip body parsing for Content-Length > 1MB to avoid memory pressure * feat: make API base URL configurable via VITE_WS_API_URL Replace hardcoded api.worldmonitor.app with VITE_WS_API_URL env var. When empty, installWebApiRedirect() is skipped entirely — relative /api/* calls stay on the same domain (local installs). When set, browser fetch is redirected to that URL. Also adds VITE_WS_API_URL and VITE_WS_RELAY_URL hostnames to APP_HOSTS allowlist dynamically. * fix(analytics): use greedy regex in PostHog ingest rewrites (#481) Vercel's :path* wildcard doesn't match trailing slashes that PostHog SDK appends (e.g. /ingest/s/?compression=...), causing 404s. Switch to :path(.*) which matches all path segments including trailing slashes. Ref: PostHog/posthog#17596 * perf(proxy): increase AIS snapshot edge TTL from 2s to 10s (#482) With 20k requests/30min (60% of proxy traffic) and per-PoP caching, a 2s edge TTL expires before the next request from the same PoP arrives, resulting in near-zero cache hits. 10s allows same-PoP dedup while keeping browser TTL at 2s for fresh vessel positions. * fix(markets): commodities panel showing stocks instead of commodities (#483) The shared circuit breaker (cacheTtlMs: 0) cached the stocks response, then the stale-while-revalidate path returned that cached stocks data for the subsequent commodities fetch. Skip SWR when caching is disabled. * feat(gateway): complete edge cache tier coverage + degraded-response policy (#484) - Add 11 missing GET routes to RPC_CACHE_TIER map (8 slow, 3 medium) - Add response-headers side-channel (WeakMap) so handlers can signal X-No-Cache without codegen changes; wire into military-flights and positive-geo-events handlers on upstream failure - Add env-controlled per-endpoint tier override (CACHE_TIER_OVERRIDE_*) for incident response rollback - Add VITE_WS_API_URL hostname allowlist (*.worldmonitor.app + localhost) - Fix fetch.bind(globalThis) in positive-events-geo.ts (deferred lambda) - Add CI test asserting every generated GET route has an explicit cache tier entry (prevents silent default-tier drift) * chore: bump version to 2.5.20 + changelog Covers PRs #452–#484: Cloudflare edge caching, commodities SWR fix, security advisories panel, settings redesign, 52 POST→GET migrations. * fix(rss): remove stale indianewsnetwork.com from proxy allowlist (#486) Feed has no <pubDate> fields and latest content is from April 2022. Not referenced in any feed config — only in the proxy domain allowlist. * feat(i18n): add Korean (한국어) localization (#487) - Add ko.json with all 1606 translation keys matching en.json structure - Register 'ko' in SUPPORTED_LANGUAGES, LANGUAGES display array, and locale map - Korean appears as 🇰🇷 한국어 in the language dropdown * feat: add Polish tv livestreams (#488) * feat(rss): add Axios (api.axios.com/feed) as US news source (#494) Add api.axios.com to proxy allowlist and CSP connect-src, register Axios feed under US category as Tier 2 mainstream source. * perf: bootstrap endpoint + polling optimization (#495) * perf: bootstrap endpoint + polling optimization (phases 3-4) Replace 15+ individual RPC calls on startup with a single /api/bootstrap batch call that fetches pre-cached data from Redis. Consolidate 6 panel setInterval timers into the central RefreshScheduler for hidden-tab awareness (10x multiplier) and adaptive backoff (up to 4x for unchanged data). Convert IntelligenceGapBadge from 10s polling to event-driven updates with 60s safety fallback. * fix(bootstrap): inline Redis + cache keys in edge function Vercel Edge Functions cannot resolve cross-directory TypeScript imports from server/_shared/. Inline getCachedJsonBatch and BOOTSTRAP_CACHE_KEYS directly in api/bootstrap.js. Add sync test to ensure inlined keys stay in sync with the canonical server/_shared/cache-keys.ts registry. * test: add Edge Function module isolation guard for all api/*.js files Prevents any Edge Function from importing from ../server/ or ../src/ which breaks Vercel builds. Scans all 12 non-helper Edge Functions. * fix(bootstrap): read unprefixed cache keys on all environments Preview deploys set VERCEL_ENV=preview which caused getKeyPrefix() to prefix Redis keys with preview:<sha>:, but handlers only write to unprefixed keys on production. Bootstrap is a read-only consumer of production cache — always read unprefixed keys. * fix(bootstrap): wire sectors hydration + add coverage guard - Wire getHydratedData('sectors') in data-loader to skip Yahoo Finance fetch when bootstrap provides sector data - Add test ensuring every bootstrap key has a getHydratedData consumer — prevents adding keys without wiring them * fix(server): resolve 25 TypeScript errors + add server typecheck to CI - _shared.ts: remove unused `delay` variable - list-etf-flows.ts: add missing `rateLimited` field to 3 return literals - list-market-quotes.ts: add missing `rateLimited` field to 4 return literals - get-cable-health.ts: add non-null assertions for regex groups and array access - list-positive-geo-events.ts: add non-null assertion for array index - get-chokepoint-status.ts: add required fields to request objects - CI: run `typecheck:api` (tsconfig.api.json) alongside `typecheck` to catch server/ TS errors before merge * feat(military): server-side military bases 125K + rate limiting (#496) * feat(military): server-side military bases with 125K entries + rate limiting (#485) Migrate military bases from 224 static client-side entries to 125,380 server-side entries stored in Redis GEO sorted sets, served via bbox-filtered GEOSEARCH endpoint with server-side clustering. Data pipeline: - Pizzint/Polyglobe: 79,156 entries (Supabase extraction) - OpenStreetMap: 45,185 entries - MIRTA: 821 entries - Curated strategic: 218 entries - 277 proximity duplicates removed Server: - ListMilitaryBases RPC with GEOSEARCH + HMGET + tier/filter/clustering - Antimeridian handling (split bbox queries) - Blue-green Redis deployment with atomic version pointer switch - geoSearchByBox() + getHashFieldsBatch() helpers in redis.ts Security: - @upstash/ratelimit: 60 req/min sliding window per IP - IP spoofing fix: prioritize x-real-ip (Vercel-injected) over x-forwarded-for - Require API key for non-browser requests (blocks unauthenticated curl/scripts) - Input validation: allowlisted types/kinds, regex country, clamped bbox/zoom Frontend: - Viewport-driven loading with bbox quantization + debounce - Server-side grid clustering at low zoom levels - Enriched popup with kind, category badges (airforce/naval/nuclear/space) - Static 224 bases kept as search fallback + initial render * fix(military): fallback to production Redis keys in preview deployments Preview deployments prefix Redis keys with `preview:{sha}:` but military bases data is seeded to unprefixed (production) keys. When the prefixed `military:bases:active` key is missing, fall back to the unprefixed key and use raw (unprefixed) keys for geo/meta lookups. * fix: remove unused 'remaining' destructure in rate-limit (TS6133) * ci: add typecheck:api to pre-push hook to catch server-side TS errors * debug(military): add X-Bases-Debug response header for preview diagnostics * fix(bases): trigger initial server fetch on map load fetchServerBases() was only called on moveend — if the user never panned/zoomed, the API was never called and only the 224 static fallback bases showed. * perf(military): debounce base fetches + upgrade edge cache to static tier (#497) - Add 300ms debounce on moveend to prevent rapid pan flooding - Fixes stale-bbox bug where pendingFetch returns old viewport data - Upgrade edge cache tier from medium (5min) to static (1hr) — bases are static infrastructure, aligned with server-side cachedFetchJson TTL - Keep error logging in catch blocks for production diagnostics * fix(cyber): make GeoIP centroid fallback jitter deterministic (#498) Replace Math.random() jitter with DJB2 hash seeded by the threat indicator (IP/URL), so the same threat always maps to the same coordinates across requests while different threats from the same country still spread out. Closes #203 Co-authored-by: Chris Chen <fuleinist@users.noreply.github.com> * fix: use cross-env for Windows-compatible npm scripts (#499) Replace direct `VAR=value command` syntax with cross-env/cross-env-shell so dev, build, test, and desktop scripts work on Windows PowerShell/CMD. Co-authored-by: facusturla <facusturla@users.noreply.github.com> * feat(live-news): add CBC News to optional North America channels (#502) YouTube handle @CBCNews with fallback video ID 5vfaDsMhCF4. * fix(bootstrap): harden hydration cache + polling review fixes (#504) - Filter null/undefined values before storing in hydration cache to prevent future consumers using !== undefined from misinterpreting null as valid data - Debounce wm:intelligence-updated event handler via requestAnimationFrame to coalesce rapid alert generation into a single render pass - Include alert IDs in StrategicRiskPanel change fingerprint so content changes are detected even when alert count stays the same - Replace JSON.stringify change detection in ServiceStatusPanel with lightweight name:status fingerprint - Document max effective refresh interval (40x base) in scheduler * fix(geo): tokenization-based keyword matching to prevent false positives (#503) * fix(geo): tokenization-based keyword matching to prevent false positives Replace String.includes() with tokenization-based Set.has() matching across the geo-tagging pipeline. Prevents false positives like "assad" matching inside "ambassador" and "hts" matching inside "rights". - Add src/utils/keyword-match.ts as single source of truth - Decompose possessives/hyphens ("Assad's" → includes "assad") - Support multi-word phrase matching ("white house" as contiguous) - Remove false-positive-prone DC keywords ('house', 'us ') - Update 9 consumer files across geo-hub, map, CII, and asset systems - Add 44 tests covering false positives, true positives, edge cases Co-authored-by: karim <mirakijka@gmail.com> Fixes #324 * fix(geo): add inflection suffix matching + fix test imports Address code review feedback: P1a: Add suffix-aware matching for plurals and demonyms so existing keyword lists don't regress (houthi→houthis, ukraine→ukrainian, iran→iranian, israel→israeli, russia→russian, taiwan→taiwanese). Uses curated suffix list + e-dropping rule to avoid false positives. P1b: Expand conflictTopics arrays in DeckGLMap and Map with demonym forms so "Iranian senate..." correctly registers as conflict topic. P2: Replace inline test functions with real module import via tsx. Tests now exercise the production keyword-match.ts directly. * fix: wire geo-keyword tests into test:data command The .mts test file wasn't covered by `node --test tests/*.test.mjs`. Add `npx tsx --test tests/*.test.mts` so test:data runs both suites. * fix: cross-platform test:data + pin tsx in devDependencies - Use tsx as test runner for both .mjs and .mts (single invocation) - Removes ; separator which breaks on Windows cmd.exe - Add tsx to devDependencies so it works in offline/CI environments * fix(geo): multi-word demonym matching + short-keyword suffix guard - Add wordMatches() for suffix-aware phrase matching so "South Korean" matches keyword "south korea" and "North Korean" matches "north korea" - Add MIN_SUFFIX_KEYWORD_LEN=4 guard so short keywords like "ai", "us", "hts" only do exact-match (prevents "ais"→"ai", "uses"→"us" false positives) - Add 5 new tests covering both fixes (58 total, all passing) * fix(geo): support plural demonyms in keyword matching Add compound suffixes (ians, eans, ans, ns, is) to handle plural demonym forms like "Iranians"→"iran", "Ukrainians"→"ukraine", "Russians"→"russia", "Israelis"→"israel". Adds 5 new tests (63 total). --------- Co-authored-by: karim <mirakijka@gmail.com> * chore: strip 61 debug console.log calls from 20 service files (#501) * chore: strip 61 debug console.log calls from services Remove development/tracing console.log statements from 20 files. These add noise to production browser consoles and increase bundle size. Preserved: all console.error (error handling) and console.warn (warnings). Preserved: debug-gated logs in runtime.ts (controlled by verbose flag). Removed: debugInjectTestEvents() from geo-convergence.ts (test-only code). Removed: logSummary()/logReport() methods that were pure console.log wrappers. * fix: remove orphaned stubs and remaining debug logs from stripped services - Remove empty logReport() method and unused startTime variable (parallel-analysis.ts) - Remove orphaned console.group/console.groupEnd pair (parallel-analysis.ts) - Remove empty logSignalSummary() export (signal-aggregator.ts) - Remove logSignalSummary import/call and 3 remaining console.logs (InsightsPanel.ts) - Remove no-op logDirectFetchBlockedOnce() and dead infrastructure (prediction/index.ts) * fix: generalize Vercel preview origin regex + include filters in bases cache key (#506) - api/_api-key.js: preview URL pattern was user-specific (-elie-), rejecting other collaborators' Vercel preview deployments. Generalized to match any worldmonitor-*.vercel.app origin. - military-bases.ts: client cache key only checked bbox/zoom, ignoring type/kind/country filters. Switching filters without panning returned stale results. Unified into single cacheKey string. * fix(prediction): filter stale/expired markets from Polymarket panel (#507) Prediction panel was showing expired markets (e.g. "Will US strike Iran on Feb 9" at 0%). Root causes: no active/archived API filters, no end_date_min param, no client-side expiry guard, and sub-market selection picking highest volume before filtering expired ones. - Add active=true, archived=false, end_date_min API params to all 3 Gamma API call sites (events, markets, probe) - Pre-filter sub-markets by closed/expired BEFORE volume selection in both fetchPredictions() and fetchCountryMarkets() - Add defense-in-depth isExpired() client-side filter on final results - Propagate endDate through all market object paths including sebuf fallback - Show expiry date in PredictionPanel UI with new .prediction-meta layout - Add "closes" i18n key to all 18 locale files - Add endDate to server handler GammaMarket/GammaEvent interfaces and map to proto closesAt field * fix(relay): guard proxy handlers against ERR_HTTP_HEADERS_SENT crash (#509) Polymarket and World Bank proxy handlers had unguarded res.writeHead() calls in error/timeout callbacks that race with the response callback. When upstream partially responds then times out, both paths write headers → process crash. Replace 5 raw writeHead+end calls with safeEnd() which checks res.headersSent before writing. * feat(breaking-news): add active alert banner with audio for critical/high RSS items (#508) RSS items classified as critical/high threat now trigger a full-width breaking news banner with audio alert, auto-dismiss (60s/30s by severity), visibility-aware timer pause, dedup, and a toggle in the Intelligence Findings dropdown. * fix(sentry): filter Android OEM WebView bridge injection errors (#510) Add ignoreErrors pattern for LIDNotifyId, onWebViewAppeared, and onGetWiFiBSSID — native bridge functions injected by Lenovo/Huawei device SDKs into Chrome Mobile WebView. No stack frames in our code. * chore: add validated telegram channels list (global + ME + Iran + cyber) (#249) * feat(conflict): add Iran Attacks map layer + strip debug logs (#511) * chore: strip 61 debug console.log calls from services Remove development/tracing console.log statements from 20 files. These add noise to production browser consoles and increase bundle size. Preserved: all console.error (error handling) and console.warn (warnings). Preserved: debug-gated logs in runtime.ts (controlled by verbose flag). Removed: debugInjectTestEvents() from geo-convergence.ts (test-only code). Removed: logSummary()/logReport() methods that were pure console.log wrappers. * fix: remove orphaned stubs and remaining debug logs from stripped services - Remove empty logReport() method and unused startTime variable (parallel-analysis.ts) - Remove orphaned console.group/console.groupEnd pair (parallel-analysis.ts) - Remove empty logSignalSummary() export (signal-aggregator.ts) - Remove logSignalSummary import/call and 3 remaining console.logs (InsightsPanel.ts) - Remove no-op logDirectFetchBlockedOnce() and dead infrastructure (prediction/index.ts) * feat(conflict): add Iran Attacks map layer Adds a new Iran-focused conflict events layer that aggregates real-time events, geocodes via 40-city lookup table, caches 15min in Redis, and renders as a toggleable DeckGL ScatterplotLayer with severity coloring. - New proto + codegen for ListIranEvents RPC - Server handler with HTML parsing, city geocoding, category mapping - Frontend service with circuit breaker - DeckGL ScatterplotLayer with severity-based color/size - MapPopup with sanitized source links - iranAttacks toggle across all variants, harnesses, and URL state * fix: resolve bootstrap 401 and 429 rate limiting on page init (#512) Same-origin browser requests don't send Origin header (per CORS spec), causing validateApiKey to reject them. Extract origin from Referer as fallback. Increase rate limit from 60 to 200 req/min to accommodate the ~50 requests fired during page initialization. * fix(relay): prevent Polymarket OOM via request deduplication (#513) Concurrent Polymarket requests for the same cache key each fired independent https.get() calls. With 12 categories × multiple clients, 740 requests piled up in 10s, all buffering response bodies → 4.1GB heap → OOM crash on Railway. Fix: in-flight promise map deduplicates concurrent requests to the same cache key. 429/error responses are negative-cached for 30s to prevent retry storms. * fix(threat-classifier): add military/conflict keyword gaps and news-to-conflict bridge (#514) Breaking news headlines like "Israel's strike on Iran" were classified as info level because the keyword classifier lacked standalone conflict phrases. Additionally, the conflict instability score depended solely on ACLED data (1-7 day lag) with no bridge from real-time breaking news. - Add 3 critical + 18 high contextual military/conflict keywords - Preserve threat classification on semantically merged clusters - Add news-derived conflict floor when ACLED/HAPI report zero signal - Upsert news events by cluster ID to prevent duplicates - Extract newsEventIndex to module-level Map for serialization safety * fix(breaking-news): let critical alerts bypass global cooldown and replace HIGH alerts (#516) Global cooldown (60s) was blocking critical alerts when a less important HIGH alert fired from an earlier RSS batch. Added priority-aware cooldown so critical alerts always break through. Banner now auto-dismisses HIGH alerts when a CRITICAL arrives. Added Iran/strikes keywords to classifier. * fix(rate-limit): increase sliding window to 300 req/min (#515) App init fires many concurrent classify-event, summarize-article, and record-baseline-snapshot calls, exhausting the 200/min limit and causing 429s. Bump to 300 as a temporary measure while client-side batching is implemented. * fix(breaking-news): fix fake pubDate fallback and filter noisy think-tank alerts (#517) Two bugs causing stale CrisisWatch article to fire as breaking alert: 1. Non-standard pubDate format ("Friday, February 27, 2026 - 12:38") failed to parse → fallback was `new Date()` (NOW) → day-old articles appeared as "just now" and passed recency gate on every fetch 2. Tier 3+ sources (think tanks) firing alerts on keyword-only matches like "War" in policy analysis titles — too noisy for breaking alerts Fix: parsePubDate() handles non-standard formats and falls back to epoch (not now). Tier 3+ sources require LLM classification to fire. * fix: make iran-events handler read-only from Redis (#518) Remove server-side LiveUAMap scraper (blocked by Cloudflare 403 on Vercel IPs). Handler now reads pre-populated Redis cache pushed from local browser scraping. Change cache tier from slow to fast to prevent CDN from serving stale empty responses for 30+ minutes. * fix(relay): Polymarket circuit breaker + concurrency limiter (OOM fix) (#519) * fix(rate-limit): increase sliding window to 300 req/min App init fires many concurrent classify-event, summarize-article, and record-baseline-snapshot calls, exhausting the 200/min limit and causing 429s. Bump to 300 as a temporary measure while client-side batching is implemented. * fix(relay): add Polymarket circuit breaker + concurrency limiter to prevent OOM Railway relay OOM crash: 280 Polymarket 429 errors in 8s, heap hit 3.7GB. Multiple unique cache keys bypassed per-key dedup, flooding upstream. - Circuit breaker: trips after 5 consecutive failures, 60s cooldown - Concurrent upstream limiter: max 3 simultaneous requests - Negative cache TTL: 30s → 60s to reduce retry frequency - Upstream slot freed on response.on('end'), not headers, preventing body buffer accumulation past the concurrency cap * fix(relay): guard against double-finalization on Polymarket timeout request.destroy() in timeout handler also fires request.on('error'), causing double decrement of polymarketActiveUpstream (counter goes negative, disabling concurrency cap) and double circuit breaker trip. Add finalized guard so decrement + failure accounting happens exactly once per request regardless of which error path fires first. * fix(threat-classifier): stagger AI classification requests to avoid Groq 429 (#520) flushBatch() fired up to 20 classifyEvent RPCs simultaneously via Promise.all, instantly hitting Groq's ~30 req/min rate limit. - Sequential execution with 2s min-gap between requests (~28 req/min) - waitForGap() enforces hard floor + jitter across batch boundaries - batchInFlight guard prevents concurrent flush loops - 429/5xx: requeue failed job (with retry cap) + remaining untouched jobs - Queue cap at 100 items with warn on overflow * fix(relay): regenerate package-lock.json with telegram dependency The lockfile was missing resolved entries for the telegram package, causing Railway to skip installation despite it being in package.json. * chore: trigger deploy to flush CDN cache for iran-events endpoint * Revert "fix(relay): regenerate package-lock.json with telegram dependency" This reverts commit |
||
|
|
3ec97c7ac6 |
feat(news): server-side feed aggregation to reduce edge invocations by ~95% (#622)
Phase 1: Force CDN caching on rss-proxy (s-maxage=300 for 2xx, short
TTL for errors) — fixes bug where upstream no-cache headers were passed
through verbatim, defeating Vercel CDN.
Phase 2: Add ListFeedDigest RPC that aggregates all feeds server-side
into a single Redis-cached response. Client makes 1 request instead of
~90 per cycle. Includes circuit breaker with persistent cache fallback,
per-feed AI reclassification, and headline ingestion parity.
Phase 3: Increase polling interval from 5min to 7min to offset CDN
cache alignment.
New files:
- proto/worldmonitor/news/v1/list_feed_digest.proto
- server/worldmonitor/news/v1/{_feeds,_classifier,list-feed-digest}.ts
- src/services/ai-classify-queue.ts (extracted from rss.ts)
|
||
|
|
1f07a258aa |
feat: add day/night solar terminator overlay to map (#529)
* Trigger redeploy with preview env vars * Trigger deployment * chore: trigger redeploy for PR #41 * chore: trigger Vercel redeploy (edge function transient failure) * chore: retrigger Vercel deploy * feat: add Nigeria feeds and Greek locale feeds (#271) - Add 5 Nigeria news sources to Africa section (Premium Times, Vanguard, Channels TV, Daily Trust, ThisDay) - Add 5 Greek feeds with lang: 'el' for locale-aware filtering (Kathimerini, Naftemporiki, in.gr, iefimerida, Proto Thema) - Add source tiers for all new outlets - Allowlist 8 new domains in RSS proxy * fix: enforce military bbox filtering and add behavioral cache tests (#284) * fix: add request coalescing to Redis cache layer Concurrent cache misses for the same key now share a single upstream fetch instead of each triggering redundant API calls. This eliminates duplicate work within Edge Function invocations under burst traffic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: reduce AIS polling frequency from 10s to 30s Vessel positions do not change meaningfully in 10 seconds at sea. Reduces Railway relay requests by 66% with negligible UX impact. Stale threshold bumped to 45s to match the new interval. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: quantize military flights bbox cache keys to 1-degree grid Precise bounding box coordinates caused near-zero cache hit rate since every map pan/zoom produced a unique key. Snapping to a 1-degree grid lets nearby viewports share cache entries, dramatically reducing redundant OpenSky API calls. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: parallelize ETF chart fetches instead of sequential await loop The loop awaited each ETF chart fetch individually, blocking on every Yahoo gate delay. Using Promise.allSettled lets all 10 fetches queue concurrently through the Yahoo gate, cutting wall time from ~12s to ~6s. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add Redis pipeline batch GET to reduce round-trips Add getCachedJsonBatch() using the Upstash pipeline API to fetch multiple keys in a single HTTP call. Refactor aircraft details batch handler from 20 sequential GETs to 1 pipelined request. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add structural tests for Redis caching optimizations 18 tests covering: cachedFetchJson request coalescing (in-flight dedup, cache-before-fetch ordering, cleanup), getCachedJsonBatch pipeline API, aircraft batch handler pipeline usage, bbox grid quantization (1-degree step, expanded fetch bbox), and ETF parallel fetch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: enforce military bbox contract and add behavioral cache tests --------- Co-authored-by: Elias El Khoury <efk@anghami.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: add User-Agent and Cloudflare 403 detection to all secret validation probes (#296) Sidecar validation probes were missing User-Agent headers, causing Cloudflare-fronted APIs (e.g. Wingbits) to return 403 which was incorrectly treated as an auth rejection. Added CHROME_UA to all 13 probes and isCloudflare403() helper to soft-pass CDN blocks. * fix: open external links in system browser on Tauri desktop (#297) Tauri WKWebView/WebView2 traps target="_blank" navigation, so news links and other external URLs silently fail to open. Added a global capture-phase click interceptor that routes cross-origin links through the existing open_url Tauri command, falling back to window.open. * fix: add Greek flag mapping to language selector (#307) * fix: add missing country brief i18n keys and export PDF option (#308) - Add levels, trends, fallback keys to top-level countryBrief in en/el/th/vi locales (fixes raw key display in intelligence brief and header badge) - Add Export PDF option to country brief dropdown using scoped print dialog - Add exportPdf i18n key to all 17 locale files * feat: add day/night solar terminator overlay to map Add a real-time day/night overlay layer using deck.gl PolygonLayer that renders the solar terminator (boundary between day and night zones). The overlay uses astronomical formulas (Meeus) to compute the subsolar point and trace the terminator line at 1° resolution. - New toggleable "Day/Night" layer in all 3 variants (full/tech/finance) - Theme-aware styling (lighter fill on light theme, darker on dark) - Auto-refresh every 5 minutes with conditional timer (only runs when layer is enabled, pauses when render is paused) - Cached polygon computation to avoid recomputing on every render - i18n translations for all 17 locales - Updated documentation with new layer entry Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address review feedback — equinox terminator + locale indentation - Replace safeTanDecl epsilon clamp with proper equinox handling: when |tanDecl| < 1e-6, draw terminator as vertical great circle through the poles (subsolar meridian ±90°) instead of clamping - Fix JSON indentation in all 17 locale files: dayNight and tradeRoutes keys were left-aligned instead of matching 8-space indentation of surrounding keys --------- Co-authored-by: Elie Habib <elie.habib@gmail.com> Co-authored-by: Elias El Khoury <efk@anghami.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
3d2c638a72 |
feat(military): server-side military bases 125K + rate limiting (#496)
* feat(military): server-side military bases with 125K entries + rate limiting (#485) Migrate military bases from 224 static client-side entries to 125,380 server-side entries stored in Redis GEO sorted sets, served via bbox-filtered GEOSEARCH endpoint with server-side clustering. Data pipeline: - Pizzint/Polyglobe: 79,156 entries (Supabase extraction) - OpenStreetMap: 45,185 entries - MIRTA: 821 entries - Curated strategic: 218 entries - 277 proximity duplicates removed Server: - ListMilitaryBases RPC with GEOSEARCH + HMGET + tier/filter/clustering - Antimeridian handling (split bbox queries) - Blue-green Redis deployment with atomic version pointer switch - geoSearchByBox() + getHashFieldsBatch() helpers in redis.ts Security: - @upstash/ratelimit: 60 req/min sliding window per IP - IP spoofing fix: prioritize x-real-ip (Vercel-injected) over x-forwarded-for - Require API key for non-browser requests (blocks unauthenticated curl/scripts) - Input validation: allowlisted types/kinds, regex country, clamped bbox/zoom Frontend: - Viewport-driven loading with bbox quantization + debounce - Server-side grid clustering at low zoom levels - Enriched popup with kind, category badges (airforce/naval/nuclear/space) - Static 224 bases kept as search fallback + initial render * fix(military): fallback to production Redis keys in preview deployments Preview deployments prefix Redis keys with `preview:{sha}:` but military bases data is seeded to unprefixed (production) keys. When the prefixed `military:bases:active` key is missing, fall back to the unprefixed key and use raw (unprefixed) keys for geo/meta lookups. * fix: remove unused 'remaining' destructure in rate-limit (TS6133) * ci: add typecheck:api to pre-push hook to catch server-side TS errors * debug(military): add X-Bases-Debug response header for preview diagnostics * fix(bases): trigger initial server fetch on map load fetchServerBases() was only called on moveend — if the user never panned/zoomed, the API was never called and only the 224 static fallback bases showed. |
||
|
|
84e39ba4b1 |
fix(desktop): enable click-to-play YouTube embeds + CISA feed fixes (#476)
* fix(tech): use rss() for CISA feed, drop build from pre-push hook
- CISA Advisories used dead rss.worldmonitor.app domain (404), switch to rss() helper
- Remove Vite build from pre-push hook (tsc already catches errors)
* fix(desktop): enable click-to-play for YouTube embeds in WKWebView
WKWebView blocks programmatic autoplay in cross-origin iframes regardless
of allow attributes, Permissions-Policy, mute-first retries, or secure
context. Documented all 10 approaches tested in docs/internal/.
Changes:
- Switch sidecar embed origin from 127.0.0.1 to localhost (secure context)
- Add MutationObserver + retry chain as best-effort autoplay attempts
- Use postMessage('*') to fix tauri://localhost cross-origin messaging
- Make sidecar play overlay non-interactive (pointer-events:none)
- Fix .webcam-iframe pointer-events:none blocking clicks in grid view
- Add expand button to grid cells for switching to single view on desktop
- Add http://localhost:* to CSP frame-src in index.html and tauri.conf.json
|
||
|
|
248bdad6ae |
fix: move 5 path-param endpoints to query params for Vercel routing (#472)
Vercel's `api/[domain]/v1/[rpc].ts` captures one dynamic segment.
Path params like `/get-humanitarian-summary/SA` add an extra segment
that has no matching route file, causing 404 on both OPTIONS preflight
and direct requests. These endpoints were broken in production.
Changes:
- Remove `{param}` from 5 service.proto HTTP paths
- Add `(sebuf.http.query)` annotations to request message fields
- Update generated client/server code to use URLSearchParams
- Update OpenAPI specs (YAML + JSON) to declare query params
- Add early-return guards in 4 handlers for missing required params
- Add happy.worldmonitor.app to runtime.ts redirect hosts
Affected endpoints:
- GET /api/conflict/v1/get-humanitarian-summary?country_code=SA
- GET /api/economic/v1/get-fred-series?series_id=T10Y2Y&limit=120
- GET /api/market/v1/get-country-stock-index?country_code=US
- GET /api/intelligence/v1/get-country-intel-brief?country_code=US
- GET /api/military/v1/get-aircraft-details?icao24=a12345
|