missile-dashboard

March 6, 2026 ยท View on GitHub

Data compiled by the Institute for National Security Studies (INSS), an Israeli think tank affiliated with Tel Aviv University. These figures were widely cited in reports published on Feb 28 - March 5, 2026, by outlets such as The New Arab, Middle East Eye, and CBS News.

Live feed integration

The dashboard now polls /api/live-feed on a 15-minute interval in main.js.

  1. Copy .env.example to .env.
  2. Set one or more server-side feed URLs:
    • TARGETS_JSON_API_URL
    • TARGETS_CSV_API_URL
    • TARGETS_HTML_SOURCE_URL (must contain a <table> with rows)
  3. For full frontend + API locally, run vercel dev (or run npm run dev for frontend-only).

Sources are attempted in this order: JSON API -> CSV API -> HTML scraper. If all fail, the UI falls back to the bundled snapshot.

API routes

  • GET /api/live-feed
    • Public route used by the dashboard
    • Query params:
      • ai=1 includes cached Gemini summary
      • force=1 bypasses in-memory refresh TTL
  • GET /api/cron-refresh
    • Protected with Authorization: Bearer <CRON_SECRET>
    • Forces feed refresh, runs Gemini daily fact-check, and returns:
      • normalized rows (factCheck.rows)
      • citations with dates (factCheck.citations)
      • a ready-to-paste fallbackTargetDataPreview module snippet

Vercel cron (free-friendly)

vercel.json is set to run once daily:

  • 0 9 * * * -> /api/cron-refresh

To keep costs lower while still polling every 15 minutes on the frontend:

  • keep VITE_FEED_REFRESH_MINUTES=15
  • keep AI_REFRESH_MINUTES=1440 (Gemini once/day cache)
  • optionally set DATA_REFRESH_MINUTES=1440 if you want source scraping once/day too

Accepted JSON shapes

  • Array root:
    • [{"country":"UAE","drones":941,"ballistic":189,"cruise":8}]
  • Wrapped array:
    • {"data":[...]}
    • {"targets":[...]}
    • {"results":[...]}

Field aliases supported by the normalizer include:

  • Country: country, target, name
  • Drones: drones, drone, uav
  • Ballistic: ballistic, ballistic_missiles, missiles
  • Cruise: cruise, cruise_missiles
  • Total: total, count, attacks (or computed from components)

Gemini config

Set:

  • GEMINI_API_KEY
  • GEMINI_MODEL (default: gemini-3.1-pro-preview)
  • ENABLE_AI_SUMMARY=true
  • GEMINI_TOPIC for cron topic summary text
  • DAILY_FACTCHECK_SOURCE_URLS (comma-separated trusted source URLs used by daily fact-check)
  1. Cron runs once/day (/api/cron-refresh).
  2. Endpoint fetches trusted source pages from DAILY_FACTCHECK_SOURCE_URLS.
  3. Gemini returns strict structured JSON (rows + citations + warnings).
  4. Server validates/normalizes values against your baseline schema.
  5. Use fallbackTargetDataPreview from response to update fallback-target-data.js when acceptable.

This keeps fallback-target-data.js as the offline fallback for main.js, while preserving dated citations and structured fact rows from the daily run.