feat(monitor/frontend): country-centroid plotting for ransomware + internet_outage on globe

This commit is contained in:
CarterPerez-dev 2026-05-03 18:11:15 -04:00
parent 0a41102e75
commit e690b30e92
2 changed files with 256 additions and 2 deletions

View File

@ -0,0 +1,194 @@
// ©AngelaMos | 2026
// countryCentroids.ts
export interface Centroid {
lat: number
lng: number
}
const ISO_ALIASES: Record<string, string> = {
UK: 'GB',
}
export const COUNTRY_CENTROIDS: Record<string, Centroid> = {
AE: { lat: 24.2762, lng: 53.9882 },
AF: { lat: 33.9024, lng: 67.8432 },
AL: { lat: 41.1566, lng: 20.1623 },
AM: { lat: 39.9947, lng: 45.0442 },
AO: { lat: -11.8974, lng: 17.86 },
AQ: { lat: -76.6353, lng: 0.0 },
AR: { lat: -37.0911, lng: -63.5219 },
AT: { lat: 47.7354, lng: 13.2298 },
AU: { lat: -24.852, lng: 133.4542 },
AZ: { lat: 40.0655, lng: 47.6827 },
BA: { lat: 43.9419, lng: 17.6749 },
BD: { lat: 23.5587, lng: 90.3786 },
BE: { lat: 50.5023, lng: 4.3351 },
BF: { lat: 12.3635, lng: -1.6467 },
BG: { lat: 42.7347, lng: 25.4693 },
BI: { lat: -3.4242, lng: 29.8886 },
BJ: { lat: 9.1889, lng: 2.2847 },
BN: { lat: 4.7277, lng: 114.8274 },
BO: { lat: -16.3175, lng: -63.5444 },
BR: { lat: -14.2619, lng: -54.3586 },
BS: { lat: 24.4601, lng: -77.9716 },
BT: { lat: 27.5079, lng: 90.459 },
BW: { lat: -22.2452, lng: 24.6638 },
BY: { lat: 53.7443, lng: 27.9466 },
BZ: { lat: 17.1935, lng: -88.668 },
CA: { lat: 56.7978, lng: -98.3406 },
CD: { lat: -4.0006, lng: 21.6782 },
CF: { lat: 6.705, lng: 20.9168 },
CG: { lat: -0.6549, lng: 14.7734 },
CH: { lat: 46.8039, lng: 8.2327 },
CI: { lat: 7.4312, lng: -5.5825 },
CL: { lat: -35.7182, lng: -71.3148 },
CM: { lat: 7.2935, lng: 12.2508 },
CN: { lat: 36.8706, lng: 104.3508 },
CO: { lat: 4.0696, lng: -72.9336 },
CR: { lat: 9.7211, lng: -84.244 },
CU: { lat: 21.522, lng: -79.5765 },
CY: { lat: 34.8725, lng: 33.1308 },
CZ: { lat: 49.8363, lng: 15.5466 },
DE: { lat: 51.1428, lng: 10.5028 },
DJ: { lat: 11.8133, lng: 42.4898 },
DK: { lat: 56.2804, lng: 9.5011 },
DO: { lat: 18.7417, lng: -70.1315 },
DZ: { lat: 28.0879, lng: 1.6576 },
EC: { lat: -1.7891, lng: -78.1007 },
EE: { lat: 58.5428, lng: 25.7357 },
EG: { lat: 26.7928, lng: 30.7831 },
EH: { lat: 24.3281, lng: -12.8643 },
ER: { lat: 15.2269, lng: 39.7022 },
ES: { lat: 39.8476, lng: -3.1767 },
ET: { lat: 9.1907, lng: 40.3718 },
FI: { lat: 65.0053, lng: 26.0808 },
FJ: { lat: -17.814, lng: 178.0016 },
FK: { lat: -51.7, lng: -59.475 },
FR: { lat: 46.7459, lng: 1.7535 },
GA: { lat: -0.826, lng: 11.6117 },
GB: { lat: 54.2975, lng: -2.2342 },
GE: { lat: 42.3088, lng: 43.2965 },
GH: { lat: 7.9044, lng: -1.0921 },
GL: { lat: 71.8409, lng: -42.7528 },
GM: { lat: 13.5034, lng: -15.3432 },
GN: { lat: 9.9476, lng: -11.4812 },
GQ: { lat: 1.647, lng: 10.2953 },
GR: { lat: 39.1185, lng: 23.3771 },
GT: { lat: 15.7773, lng: -90.2271 },
GW: { lat: 11.8343, lng: -15.189 },
GY: { lat: 4.8176, lng: -58.9748 },
HN: { lat: 14.495, lng: -86.2503 },
HR: { lat: 44.4919, lng: 16.5237 },
HT: { lat: 18.9733, lng: -73.0415 },
HU: { lat: 47.1917, lng: 19.4564 },
ID: { lat: -2.0151, lng: 122.0041 },
IE: { lat: 53.4005, lng: -8.005 },
IL: { lat: 31.3894, lng: 35.0509 },
IN: { lat: 21.7298, lng: 82.7896 },
IQ: { lat: 33.2421, lng: 43.6802 },
IR: { lat: 32.3956, lng: 53.7129 },
IS: { lat: 65.0116, lng: -18.968 },
IT: { lat: 42.5121, lng: 12.6151 },
JM: { lat: 18.1127, lng: -77.2687 },
JO: { lat: 31.2881, lng: 37.059 },
JP: { lat: 36.2041, lng: 135.6614 },
KE: { lat: 0.4146, lng: 37.8743 },
KG: { lat: 41.2889, lng: 74.8624 },
KH: { lat: 12.5286, lng: 104.9813 },
KP: { lat: 40.3272, lng: 127.5228 },
KR: { lat: 36.5011, lng: 127.7929 },
KW: { lat: 29.2926, lng: 47.4924 },
KZ: { lat: 48.0238, lng: 66.9132 },
LA: { lat: 18.1729, lng: 103.8403 },
LB: { lat: 33.867, lng: 35.8689 },
LK: { lat: 7.8962, lng: 80.7416 },
LR: { lat: 6.4484, lng: -9.4892 },
LS: { lat: -29.6463, lng: 28.1622 },
LT: { lat: 55.1391, lng: 23.822 },
LU: { lat: 49.7854, lng: 5.9584 },
LV: { lat: 56.7926, lng: 24.6163 },
LY: { lat: 26.3587, lng: 17.2421 },
MA: { lat: 28.5904, lng: -9.0725 },
MD: { lat: 46.9777, lng: 28.322 },
ME: { lat: 42.7007, lng: 19.3949 },
MG: { lat: -18.821, lng: 46.8654 },
MK: { lat: 41.5815, lng: 21.7078 },
ML: { lat: 17.5355, lng: -3.9503 },
MM: { lat: 19.1345, lng: 96.7416 },
MN: { lat: 46.8224, lng: 103.762 },
MR: { lat: 21.0063, lng: -10.9934 },
MW: { lat: -13.0159, lng: 34.23 },
MX: { lat: 23.6298, lng: -101.9699 },
MY: { lat: 3.8506, lng: 114.4226 },
MZ: { lat: -18.5296, lng: 35.4775 },
NA: { lat: -22.9934, lng: 18.4093 },
NC: { lat: -21.2528, lng: 165.5748 },
NE: { lat: 17.5659, lng: 8.0994 },
NG: { lat: 9.0533, lng: 8.6344 },
NI: { lat: 12.8716, lng: -85.4079 },
NL: { lat: 52.1571, lng: 5.2035 },
NP: { lat: 28.4103, lng: 84.1316 },
NZ: { lat: -38.0695, lng: 175.5765 },
OM: { lat: 20.7879, lng: 55.904 },
PA: { lat: 8.4161, lng: -80.1042 },
PE: { lat: -9.2026, lng: -75.038 },
PG: { lat: -6.6263, lng: 145.9009 },
PH: { lat: 15.521, lng: 122.0325 },
PK: { lat: 30.4125, lng: 69.3558 },
PL: { lat: 51.9395, lng: 19.0523 },
PR: { lat: 18.2336, lng: -66.4167 },
PS: { lat: 31.943, lng: 35.2365 },
PT: { lat: 39.5594, lng: -7.9578 },
PY: { lat: -23.4456, lng: -58.489 },
QA: { lat: 25.3355, lng: 51.1753 },
RO: { lat: 45.9547, lng: 24.9234 },
RS: { lat: 44.2085, lng: 20.9079 },
RU: { lat: 59.4247, lng: 103.6441 },
RW: { lat: -2.0263, lng: 29.9205 },
SA: { lat: 24.2544, lng: 45.1495 },
SB: { lat: -7.9292, lng: 159.0643 },
SD: { lat: 15.1146, lng: 30.1734 },
SE: { lat: 62.234, lng: 17.4654 },
SI: { lat: 46.1524, lng: 15.1315 },
SK: { lat: 48.665, lng: 19.7191 },
SL: { lat: 8.4165, lng: -11.7383 },
SN: { lat: 14.4652, lng: -14.5465 },
SO: { lat: 5.1707, lng: 46.0575 },
SR: { lat: 3.9215, lng: -56.0014 },
SS: { lat: 7.8786, lng: 29.5925 },
SV: { lat: 13.7866, lng: -88.9095 },
SY: { lat: 34.7714, lng: 39.0252 },
SZ: { lat: -26.473, lng: 31.3741 },
TD: { lat: 15.4158, lng: 18.7136 },
TF: { lat: -49.2, lng: 69.64 },
TG: { lat: 8.4738, lng: 0.9077 },
TH: { lat: 13.0546, lng: 101.4825 },
TJ: { lat: 38.8492, lng: 71.2111 },
TL: { lat: -8.8333, lng: 126.1523 },
TM: { lat: 39.0111, lng: 59.5243 },
TN: { lat: 33.8288, lng: 9.5066 },
TR: { lat: 38.9309, lng: 35.4824 },
TT: { lat: 10.445, lng: -61.4225 },
TW: { lat: 23.633, lng: 121.0287 },
TZ: { lat: -6.3355, lng: 34.8283 },
UA: { lat: 48.8142, lng: 31.0832 },
UG: { lat: 1.4033, lng: 32.3077 },
US: { lat: 37.2345, lng: -95.8259 },
UY: { lat: -32.5312, lng: -55.8183 },
UZ: { lat: 41.3659, lng: 64.4922 },
VE: { lat: 6.4434, lng: -66.5316 },
VN: { lat: 15.9759, lng: 105.7529 },
VU: { lat: -15.1833, lng: 166.9496 },
XK: { lat: 42.5596, lng: 20.9229 },
YE: { lat: 15.793, lng: 47.8567 },
ZA: { lat: -28.4552, lng: 24.5875 },
ZM: { lat: -13.0997, lng: 27.6868 },
ZW: { lat: -18.8897, lng: 29.057 },
}
export function getCentroid(iso2: string): Centroid | null {
if (!iso2) return null
const code = ISO_ALIASES[iso2] ?? iso2
return COUNTRY_CENTROIDS[code] ?? null
}

View File

@ -6,11 +6,16 @@ import { useEffect, useRef } from 'react'
import { SNAPSHOT_KEY, type Snapshot, useSnapshot } from '@/api/snapshot'
import { browserDriver, createDashboardWS, type WSEvent } from '@/api/ws'
import { unlockOnFirstGesture } from '@/lib/audio'
import { getCentroid } from '@/lib/countryCentroids'
import { type CveEvent, useCveStore } from '@/stores/cve'
import { useGlobeEvents } from '@/stores/globeEvents'
import { type KevEntry, useKevStore } from '@/stores/kev'
import { usePrices } from '@/stores/prices'
import { type RansomwareVictim, useRansomwareStore } from '@/stores/ransomware'
import {
type RansomwareVictim,
useRansomwareStore,
victimKey,
} from '@/stores/ransomware'
import { useTicker } from '@/stores/ticker'
const ALL_TOPICS: readonly string[] = [
@ -67,6 +72,16 @@ interface GdeltSpikePayload {
zscore: number
}
interface OutagePayload {
id: string
startDate?: string
endDate?: string | null
locations?: string[]
asns?: number[]
reason?: string
outageType?: string
}
export function useDashboardLifecycle(): void {
const { data: snapshot, isSuccess } = useSnapshot()
const queryClient = useQueryClient()
@ -140,8 +155,10 @@ function routeEvent(ev: WSEvent, queryClient: QueryClient): void {
case 'gdelt_spike':
handleGdelt(data as GdeltSpikePayload)
break
case 'space_weather':
case 'internet_outage':
handleOutage(data as OutagePayload, queryClient)
break
case 'space_weather':
case 'bgp_hijack':
case 'scan_firehose':
mergeIntoSnapshot(queryClient, ev.topic, data)
@ -164,6 +181,43 @@ function handleKev(p: KevEntry): void {
function handleRansomware(p: RansomwareVictim): void {
if (!p.post_title) return
useRansomwareStore.getState().push(p)
pushRansomwarePoint(p)
}
function pushRansomwarePoint(p: RansomwareVictim): void {
if (!p.country) return
const c = getCentroid(p.country)
if (!c) return
useGlobeEvents.getState().pushPoint({
id: `rw-${victimKey(p)}`,
type: 'ransomware',
lat: c.lat,
lng: c.lng,
emittedAt: Date.now(),
meta: { group_name: p.group_name, post_title: p.post_title },
})
}
function handleOutage(p: OutagePayload, queryClient: QueryClient): void {
pushOutagePoints(p)
mergeIntoSnapshot(queryClient, 'internet_outage', p)
}
function pushOutagePoints(p: OutagePayload): void {
if (!p.id || !Array.isArray(p.locations)) return
const now = Date.now()
for (const loc of p.locations) {
const c = getCentroid(loc)
if (!c) continue
useGlobeEvents.getState().pushPoint({
id: `outage-${p.id}-${loc}`,
type: 'outage',
lat: c.lat,
lng: c.lng,
emittedAt: now,
meta: { reason: p.reason, type: p.outageType },
})
}
}
function handleCoinbase(p: CoinbaseTickPayload): void {
@ -278,4 +332,10 @@ function seedGlobeFromSnapshot(snap: Snapshot): void {
emittedAt: Date.now(),
})
}
const rw = snap.ransomware_victim as RansomwareVictim | undefined
if (rw) pushRansomwarePoint(rw)
const outage = snap.internet_outage as OutagePayload | undefined
if (outage) pushOutagePoints(outage)
}