diff --git a/app/components/dashboard/realtime/Globe.vue b/app/components/dashboard/realtime/Globe.vue
index 9134c7f..b84922b 100644
--- a/app/components/dashboard/realtime/Globe.vue
+++ b/app/components/dashboard/realtime/Globe.vue
@@ -18,6 +18,8 @@ const filters = inject('filters')
const countries = ref({})
const locations = ref([])
+const colos = ref({})
+const currentLocation = ref({})
const el = useTemplateRef('globeEl')
const { width } = useElementSize(el)
@@ -39,6 +41,16 @@ async function getGlobeJSON() {
countries.value = data
}
+async function getColosJSON() {
+ const data = await $fetch('/colos.json')
+ colos.value = data
+}
+
+async function getCurrentLocation() {
+ const data = await useAPI('/api/location')
+ currentLocation.value = data
+}
+
async function getLiveLocations() {
const { data } = await useAPI('/api/logs/locations', {
query: {
@@ -54,15 +66,32 @@ async function getLiveLocations() {
}))
}
-function initGlobe() {
- const normalized = 5 / props.minutes
- const weightColor = scaleSequentialSqrt(interpolateYlOrRd).domain([0, highest.value * normalized * 15])
+function trafficEvent({ props }) {
+ const arc = {
+ startLat: props.item.latitude,
+ startLng: props.item.longitude,
+ endLat: colos.value[props.item.COLO]?.lat,
+ endLng: colos.value[props.item.COLO]?.lon,
+ color: 'red',
+ arcAltitude: 0.5,
+ }
+ console.info(`from ${props.item.city}(${props.item.latitude}, ${props.item.longitude}) to ${props.item.COLO}(${colos.value[props.item.COLO]?.lat}, ${colos.value[props.item.COLO]?.lon})`)
+ const random = Math.random()
+ globe.arcsData([arc])
+ .arcColor('color')
+ .arcDashLength(() => random + 0.2)
+ .arcDashGap(() => random - 0.2)
+ .arcDashAnimateTime(2000)
+}
+const normalized = 5 / props.minutes
+const weightColor = scaleSequentialSqrt(interpolateYlOrRd).domain([0, highest.value * normalized * 15])
+function initGlobe() {
globe = new Globe(globeEl.value)
.width(size.value.width)
.height(size.value.height)
// .globeOffset([width.value > 768 ? -100 : 0, width.value > 768 ? 0 : 100])
- .atmosphereColor('rgba(170, 170, 200, 1)')
+ .atmosphereColor('rgba(170, 170, 200, 0.8)')
.globeMaterial(new MeshPhongMaterial({
color: 'rgb(228, 228, 231)',
transparent: false,
@@ -77,13 +106,11 @@ function initGlobe() {
.hexPolygonAltitude(() => hexAltitude.value)
.hexBinMerge(true)
.hexBinPointWeight('count')
- .hexPolygonColor(() => `rgba(120, 140, 110, ${Math.random() / 2 + 0.5})`)
- .hexTopColor(d => weightColor(d.sumWeight))
- .hexSideColor(d => weightColor(d.sumWeight))
+ .hexPolygonColor(() => `rgba(54, 211, 153, ${Math.random() / 1.5 + 0.5})`)
.onGlobeReady(() => {
- globe.pointOfView({ lat: 20, lng: -36, altitude: width.value > 768 ? 2 : 3 })
+ globe.pointOfView({ lat: currentLocation.value.latitude, lng: currentLocation.value.longitude, altitude: width.value > 768 ? 2 : 3 })
globe.controls().autoRotate = true
- globe.controls().autoRotateSpeed = 0.2
+ globe.controls().autoRotateSpeed = 0.3
})
globe.controls().addEventListener('end', debounce(() => {
@@ -96,6 +123,8 @@ function initGlobe() {
if (nextAlt !== hexAltitude.value)
hexAltitude.value = nextAlt
}, 200))
+
+ globalTrafficEvent.on(trafficEvent)
}
function stopRotation() {
@@ -118,13 +147,28 @@ watch(width, () => {
watch(locations, () => {
if (globe) {
globe.hexBinPointsData(locations.value)
+ globe.hexTopColor(d => weightColor(d.sumWeight))
+ globe.hexSideColor(d => weightColor(d.sumWeight))
}
})
onMounted(async () => {
- await Promise.all([getGlobeJSON(), getLiveLocations()])
+ await Promise.all([
+ getLiveLocations(),
+ getCurrentLocation(),
+ getGlobeJSON(),
+ getColosJSON(),
+ ])
initGlobe()
})
+
+onBeforeUnmount(() => {
+ globalTrafficEvent.off(trafficEvent)
+ if (globe) {
+ globe._destructor?.()
+ globe = null
+ }
+})
diff --git a/app/components/dashboard/realtime/Index.vue b/app/components/dashboard/realtime/Index.vue
index 5e8dee5..ecf1287 100644
--- a/app/components/dashboard/realtime/Index.vue
+++ b/app/components/dashboard/realtime/Index.vue
@@ -61,7 +61,7 @@ onBeforeMount(() => {
-
+
diff --git a/app/components/dashboard/realtime/Logs.vue b/app/components/dashboard/realtime/Logs.vue
index 1c686f5..67c5c93 100644
--- a/app/components/dashboard/realtime/Logs.vue
+++ b/app/components/dashboard/realtime/Logs.vue
@@ -26,10 +26,14 @@ watch([time, filters], getEvents, {
onMounted(async () => {
getEvents()
})
+
+function onUpdateItems(items) {
+ globalTrafficEvent.emit(items)
+}
-
+
{
:description="[item.os, item.browser].filter(Boolean).join(' ')"
:icon="getFlag(item.country)"
:time="item.timestamp"
+ :item="item"
class="w-full"
/>
diff --git a/app/components/spark-ui/AnimatedList.vue b/app/components/spark-ui/AnimatedList.vue
index fc9bf86..c59176b 100644
--- a/app/components/spark-ui/AnimatedList.vue
+++ b/app/components/spark-ui/AnimatedList.vue
@@ -9,6 +9,8 @@ const props = withDefaults(defineProps<{
delay: 1000,
})
+const emit = defineEmits(['update:items'])
+
const slots = useSlots()
const index = ref(0)
const slotsArray = ref([])
@@ -19,6 +21,10 @@ const itemsToShow = computed(() => {
return slotsArray.value.slice(start, index.value)
})
+watch([itemsToShow], () => {
+ emit('update:items', itemsToShow.value.at(-1))
+})
+
async function loadComponents() {
slotsArray.value = slots.default ? slots.default()?.[0]?.children : []
diff --git a/app/utils/events.ts b/app/utils/events.ts
new file mode 100644
index 0000000..77b4123
--- /dev/null
+++ b/app/utils/events.ts
@@ -0,0 +1,3 @@
+import { useEventBus } from '@vueuse/core'
+
+export const globalTrafficEvent = useEventBus(Symbol('traffic'))
diff --git a/package.json b/package.json
index 3f4f0f5..bcef1cf 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
"dev": "nuxt dev",
"build": "nuxt build",
"build:map": "node scripts/build-map.js",
+ "build:colo": "node scripts/build-colo.js",
"preview": "wrangler pages dev dist",
"deploy": "wrangler pages deploy dist",
"postinstall": "npm run build:map && nuxt prepare",
diff --git a/public/colos.json b/public/colos.json
new file mode 100644
index 0000000..bcc7b6c
--- /dev/null
+++ b/public/colos.json
@@ -0,0 +1 @@
+{"TIA":{"lat":41.4146995544,"lon":19.7206001282},"ALG":{"lat":36.6910018921,"lon":3.2154099941},"AAE":{"lat":36.85596,"lon":7.79207},"ORN":{"lat":35.6911,"lon":-0.6416},"LAD":{"lat":-8.8583698273,"lon":13.2312002182},"EZE":{"lat":-34.8222,"lon":-58.5358},"COR":{"lat":-31.31,"lon":-64.208333},"NQN":{"lat":-38.9490013123,"lon":-68.1557006836},"EVN":{"lat":40.1473007202,"lon":44.3959007263},"ADL":{"lat":-34.9431729,"lon":138.5335637},"BNE":{"lat":-27.3841991425,"lon":153.117004394},"CBR":{"lat":-35.3069000244,"lon":149.1950073242},"HBA":{"lat":-42.883209,"lon":147.331665},"MEL":{"lat":-37.6733016968,"lon":144.843002319},"PER":{"lat":-31.9402999878,"lon":115.967002869},"SYD":{"lat":-33.9460983276,"lon":151.177001953},"VIE":{"lat":48.1102981567,"lon":16.5697002411},"LLK":{"lat":38.7463989258,"lon":48.8180007935},"GYD":{"lat":40.4674987793,"lon":50.0466995239},"BAH":{"lat":26.2707996368,"lon":50.6335983276},"CGP":{"lat":22.2495995,"lon":91.8133011},"DAC":{"lat":23.843347,"lon":90.397783},"BGI":{"lat":13.103562,"lon":-59.603226},"MSQ":{"lat":53.9006,"lon":27.599},"BRU":{"lat":50.9014015198,"lon":4.4844398499},"PBH":{"lat":27.4712,"lon":89.6339},"LPB":{"lat":-16.4897,"lon":-68.1193},"GBE":{"lat":-24.6282,"lon":25.9231},"QWJ":{"lat":-22.738,"lon":-47.334},"ARU":{"lat":-21.1413002014,"lon":-50.4247016907},"BEL":{"lat":-1.4563,"lon":-48.5013},"CNF":{"lat":-19.624444,"lon":-43.971944},"BNU":{"lat":-26.89245,"lon":-49.07696},"BSB":{"lat":-15.79824,"lon":-47.90859},"CFC":{"lat":-26.7762,"lon":-51.0125},"VCP":{"lat":-22.90662,"lon":-47.08576},"CAW":{"lat":-21.698299408,"lon":-41.301700592},"XAP":{"lat":-27.1341991425,"lon":-52.6566009521},"CGB":{"lat":-15.59611,"lon":-56.09667},"CWB":{"lat":-25.5284996033,"lon":-49.1758003235},"FLN":{"lat":-27.6702785492,"lon":-48.5525016785},"FOR":{"lat":-3.7762799263,"lon":-38.5326004028},"GYN":{"lat":-16.69727,"lon":-49.26851},"ITJ":{"lat":-27.6116676331,"lon":-48.6727790833},"JOI":{"lat":-26.304408,"lon":-48.846383},"JDO":{"lat":-7.2242,"lon":-39.313},"MAO":{"lat":-3.11286,"lon":-60.01949},"PMW":{"lat":-10.2915000916,"lon":-48.3569984436},"POA":{"lat":-29.9944000244,"lon":-51.1713981628},"REC":{"lat":-8.1264896393,"lon":-34.9235992432},"RAO":{"lat":-21.1363887787,"lon":-47.7766685486},"GIG":{"lat":-22.8099994659,"lon":-43.2505569458},"SSA":{"lat":-12.9086112976,"lon":-38.3224983215},"SJP":{"lat":-20.807157,"lon":-49.378994},"SJK":{"lat":-23.1791,"lon":-45.8872},"GRU":{"lat":-23.4355564117,"lon":-46.4730567932},"SOD":{"lat":-23.54389,"lon":-46.63445},"NVT":{"lat":-26.8251,"lon":-49.2695},"UDI":{"lat":-18.8836116791,"lon":-48.225276947},"VIX":{"lat":-20.64871,"lon":-41.90857},"BWN":{"lat":4.903052,"lon":114.939819},"SOF":{"lat":42.6966934204,"lon":23.4114360809},"OUA":{"lat":12.3531999588,"lon":-1.5124200583},"PNH":{"lat":11.5466003418,"lon":104.84400177},"YYC":{"lat":51.113899231,"lon":-114.019996643},"YVR":{"lat":49.193901062,"lon":-123.183998108},"YWG":{"lat":49.9099998474,"lon":-97.2398986816},"YHZ":{"lat":44.64601,"lon":-63.66844},"YOW":{"lat":45.3224983215,"lon":-75.6691970825},"YYZ":{"lat":43.6772003174,"lon":-79.6305999756},"YUL":{"lat":45.4706001282,"lon":-73.7407989502},"YXE":{"lat":52.1707992554,"lon":-106.699996948},"ARI":{"lat":-18.348611,"lon":-70.338889},"SCL":{"lat":-33.3930015564,"lon":-70.7857971191},"BAQ":{"lat":10.8896,"lon":-74.7808},"BOG":{"lat":4.70159,"lon":-74.1469},"CLO":{"lat":3.54322,"lon":-76.3816},"MDE":{"lat":6.16454,"lon":-75.4231},"FIH":{"lat":-4.3857498169,"lon":15.4446001053},"SJO":{"lat":9.9938602448,"lon":-84.2088012695},"ABJ":{"lat":5.292598,"lon":-3.999133},"ASK":{"lat":6.842178,"lon":-5.259932},"ZAG":{"lat":45.7429008484,"lon":16.0687999725},"LCA":{"lat":34.8750991821,"lon":33.6249008179},"PRG":{"lat":50.1007995605,"lon":14.2600002289},"CPH":{"lat":55.6179008484,"lon":12.6560001373},"JIB":{"lat":11.5473003387,"lon":43.1595001221},"STI":{"lat":19.4060993195,"lon":-70.6046981812},"SDQ":{"lat":18.4297008514,"lon":-69.6688995361},"GYE":{"lat":-2.1894,"lon":-79.8891},"UIO":{"lat":-0.1291666667,"lon":-78.3575},"CAI":{"lat":30.1219005585,"lon":31.4055995941},"TLL":{"lat":59.4132995605,"lon":24.8327999115},"ADD":{"lat":9.00005,"lon":38.78446},"SUV":{"lat":-18.11319,"lon":178.43859},"HEL":{"lat":60.317199707,"lon":24.963300705},"BOD":{"lat":44.82946,"lon":-0.58355},"LYS":{"lat":45.7263,"lon":5.0908},"MRS":{"lat":43.439271922,"lon":5.2214241028},"CDG":{"lat":49.0127983093,"lon":2.5499999523},"PPT":{"lat":-17.5536994934,"lon":-149.606994629},"TBS":{"lat":41.6692008972,"lon":44.95470047},"TXL":{"lat":52.5597000122,"lon":13.2876996994},"DUS":{"lat":51.2895011902,"lon":6.7667798996},"FRA":{"lat":50.0264015198,"lon":8.543129921},"HAM":{"lat":53.6304016113,"lon":9.9882297516},"MUC":{"lat":48.3538017273,"lon":11.7861003876},"STR":{"lat":48.783333,"lon":9.183333},"ACC":{"lat":5.614818,"lon":-0.205874},"ATH":{"lat":37.9364013672,"lon":23.9444999695},"SKG":{"lat":40.5196990967,"lon":22.9708995819},"GND":{"lat":12.007116,"lon":-61.7882288},"GUM":{"lat":13.4834003448,"lon":144.796005249},"GUA":{"lat":14.5832996368,"lon":-90.5274963379},"GEO":{"lat":6.825648,"lon":-58.163756},"SAP":{"lat":15.4525995255,"lon":-87.9235992432},"TGU":{"lat":14.0608,"lon":-87.2172},"HKG":{"lat":22.3089008331,"lon":113.915000916},"BUD":{"lat":47.4369010925,"lon":19.2555999756},"KEF":{"lat":63.9850006104,"lon":-22.6056003571},"AMD":{"lat":23.0225,"lon":72.5714},"BLR":{"lat":13.7835719,"lon":76.6165937},"BBI":{"lat":20.2961,"lon":85.8245},"IXC":{"lat":30.673500061,"lon":76.7884979248},"MAA":{"lat":12.9900054932,"lon":80.1692962646},"HYD":{"lat":17.2313175201,"lon":78.4298553467},"CNN":{"lat":11.915858,"lon":75.55094},"KNU":{"lat":26.4499,"lon":80.3319},"COK":{"lat":9.9312,"lon":76.2673},"CCU":{"lat":22.6476933,"lon":88.4349249},"BOM":{"lat":19.0886993408,"lon":72.8678970337},"NAG":{"lat":21.1610714,"lon":79.0024702},"DEL":{"lat":28.5664997101,"lon":77.1031036377},"PAT":{"lat":25.591299057,"lon":85.0879974365},"DPS":{"lat":-8.748169899,"lon":115.1669998169},"CGK":{"lat":-6.1275229,"lon":106.6515118},"JOG":{"lat":-7.7881798744,"lon":110.4319992065},"BGW":{"lat":33.2625007629,"lon":44.2346000671},"BSR":{"lat":30.5491008759,"lon":47.6621017456},"EBL":{"lat":36.1901,"lon":43.993},"NJF":{"lat":31.989722,"lon":44.404167},"XNH":{"lat":30.9358005524,"lon":46.0900993347},"ISU":{"lat":35.5668,"lon":45.4161},"ORK":{"lat":51.8413009644,"lon":-8.491109848},"DUB":{"lat":53.4212989807,"lon":-6.270070076},"HFA":{"lat":32.78492,"lon":34.96069},"TLV":{"lat":32.0113983154,"lon":34.8866996765},"MXP":{"lat":45.6305999756,"lon":8.7281103134},"PMO":{"lat":38.16114,"lon":13.31546},"FCO":{"lat":41.8045005798,"lon":12.2508001328},"KIN":{"lat":17.9951,"lon":-76.7846},"FUK":{"lat":33.5902,"lon":130.4017},"OKA":{"lat":26.1958,"lon":127.646},"KIX":{"lat":34.4272994995,"lon":135.244003296},"NRT":{"lat":35.7647018433,"lon":140.386001587},"AMM":{"lat":31.7226009369,"lon":35.9931983948},"AKX":{"lat":50.286922,"lon":57.224121},"ALA":{"lat":43.3521003723,"lon":77.0404968262},"NQZ":{"lat":51.167801,"lon":71.418893},"MBA":{"lat":-4.0348300934,"lon":39.5942001343},"NBO":{"lat":-1.319239974,"lon":36.9277992249},"ICN":{"lat":37.4691009521,"lon":126.450996399},"KWI":{"lat":29.226600647,"lon":47.9688987732},"VTE":{"lat":17.9757,"lon":102.5683},"RIX":{"lat":56.9235992432,"lon":23.9710998535},"BEY":{"lat":33.8208999634,"lon":35.4883995056},"VNO":{"lat":54.6341018677,"lon":25.2858009338},"LUX":{"lat":49.6265983582,"lon":6.211520195},"MFM":{"lat":22.1495990753,"lon":113.592002869},"TNR":{"lat":-18.91368,"lon":47.53613},"JHB":{"lat":1.635848,"lon":103.665943},"KUL":{"lat":2.745579958,"lon":101.709999084},"KCH":{"lat":1.709727,"lon":110.353455},"MLE":{"lat":4.1748,"lon":73.50888},"MRU":{"lat":-20.4302005768,"lon":57.6836013794},"GDL":{"lat":20.5217990875,"lon":-103.3109970093},"MEX":{"lat":19.4363002777,"lon":-99.0720977783},"QRO":{"lat":20.6173000336,"lon":-100.185997009},"KIV":{"lat":46.9277000427,"lon":28.9309997559},"ULN":{"lat":47.8431015015,"lon":106.766998291},"MPM":{"lat":-25.9207992554,"lon":32.5726013184},"WDH":{"lat":-22.565587,"lon":17.085334},"KTM":{"lat":27.6965999603,"lon":85.3591003418},"AMS":{"lat":52.3086013794,"lon":4.7638897896},"NOU":{"lat":-22.0146007538,"lon":166.212997436},"AKL":{"lat":-37.0080986023,"lon":174.792007446},"CHC":{"lat":-43.4893989563,"lon":172.5319976807},"LOS":{"lat":6.5773701668,"lon":3.321160078},"SKP":{"lat":41.9616012573,"lon":21.6214008331},"OSL":{"lat":60.193901062,"lon":11.100399971},"MCT":{"lat":23.5932998657,"lon":58.2844009399},"ISB":{"lat":33.6166992188,"lon":73.0991973877},"KHI":{"lat":24.9064998627,"lon":67.1607971191},"LHE":{"lat":31.5216007233,"lon":74.4036026001},"ZDM":{"lat":32.2719,"lon":35.0194},"PTY":{"lat":9.0713596344,"lon":-79.3834991455},"ASU":{"lat":-25.2399997711,"lon":-57.5200004578},"LIM":{"lat":-12.021900177,"lon":-77.1143035889},"CGY":{"lat":8.4156198502,"lon":124.611000061},"CEB":{"lat":10.3074998856,"lon":123.978996277},"MNL":{"lat":14.508600235,"lon":121.019996643},"CRK":{"lat":15.1859,"lon":120.5599},"WAW":{"lat":52.1656990051,"lon":20.9671001434},"LIS":{"lat":38.7812995911,"lon":-9.1359195709},"SJU":{"lat":18.411391,"lon":-66.102793},"DOH":{"lat":25.2605946,"lon":51.6137665},"RUN":{"lat":-20.8871002197,"lon":55.5102996826},"OTP":{"lat":44.5722007751,"lon":26.1021995544},"KJA":{"lat":56.0153,"lon":92.8932},"DME":{"lat":55.4087982178,"lon":37.9062995911},"LED":{"lat":59.8003005981,"lon":30.2625007629},"SVX":{"lat":56.8431,"lon":60.6454},"KGL":{"lat":-1.9686299563,"lon":30.1394996643},"DMM":{"lat":26.471200943,"lon":49.7979011536},"JED":{"lat":21.679599762,"lon":39.15650177},"RUH":{"lat":24.9575996399,"lon":46.6987991333},"DKR":{"lat":14.7412099,"lon":-17.4889771},"BEG":{"lat":44.8184013367,"lon":20.3090991974},"SIN":{"lat":1.3501900434,"lon":103.994003296},"BTS":{"lat":48.1486,"lon":17.1077},"CPT":{"lat":-33.9648017883,"lon":18.6016998291},"DUR":{"lat":-29.6144444444,"lon":31.1197222222},"JNB":{"lat":-26.133333,"lon":28.25},"BCN":{"lat":41.2971000671,"lon":2.0784599781},"MAD":{"lat":40.4936,"lon":-3.56676},"CMB":{"lat":7.1807599068,"lon":79.8841018677},"PBM":{"lat":5.452831,"lon":-55.187783},"GOT":{"lat":57.6627998352,"lon":12.279800415},"ARN":{"lat":59.6519012451,"lon":17.9186000824},"GVA":{"lat":46.2380981445,"lon":6.1089501381},"ZRH":{"lat":47.4646987915,"lon":8.5491695404},"KHH":{"lat":22.5771007538,"lon":120.3499984741},"TPE":{"lat":25.0776996613,"lon":121.233001709},"DAR":{"lat":-6.8781099319,"lon":39.2025985718},"BKK":{"lat":13.6810998917,"lon":100.747001648},"CNX":{"lat":18.7667999268,"lon":98.962600708},"URT":{"lat":9.1325998306,"lon":99.135597229},"POS":{"lat":10.5953998566,"lon":-61.3372001648},"TUN":{"lat":36.8510017395,"lon":10.2271995544},"IST":{"lat":40.9768981934,"lon":28.8145999908},"ADB":{"lat":38.32377,"lon":27.14317},"EBB":{"lat":0.3152,"lon":32.5816},"KBP":{"lat":50.3450012207,"lon":30.8946990967},"DXB":{"lat":25.2527999878,"lon":55.3643989563},"EDI":{"lat":55.9500007629,"lon":-3.3724999428},"LHR":{"lat":51.4706001282,"lon":-0.4619410038},"MAN":{"lat":53.3536987305,"lon":-2.2749500275},"ANC":{"lat":61.158555,"lon":-149.890208},"PHX":{"lat":33.434299469,"lon":-112.012001038},"LAX":{"lat":33.94250107,"lon":-118.4079971},"SMF":{"lat":38.695400238,"lon":-121.591003418},"SAN":{"lat":32.7336006165,"lon":-117.190002441},"SFO":{"lat":37.6189994812,"lon":-122.375},"SJC":{"lat":37.3625984192,"lon":-121.929000855},"DEN":{"lat":39.8616981506,"lon":-104.672996521},"JAX":{"lat":30.4941005707,"lon":-81.6878967285},"MIA":{"lat":25.7931995392,"lon":-80.2906036377},"TLH":{"lat":30.3964996338,"lon":-84.3503036499},"TPA":{"lat":27.9755001068,"lon":-82.533203125},"ATL":{"lat":33.6366996765,"lon":-84.4281005859},"HNL":{"lat":21.3187007904,"lon":-157.9219970703},"ORD":{"lat":41.97859955,"lon":-87.90480042},"IND":{"lat":39.717300415,"lon":-86.2944030762},"BGR":{"lat":44.8081,"lon":-68.795},"BOS":{"lat":42.36429977,"lon":-71.00520325},"DTW":{"lat":42.2123985291,"lon":-83.3534011841},"MSP":{"lat":44.8819999695,"lon":-93.2218017578},"MCI":{"lat":39.2975997925,"lon":-94.7138977051},"STL":{"lat":38.7486991882,"lon":-90.3700027466},"OMA":{"lat":41.3031997681,"lon":-95.8940963745},"LAS":{"lat":36.08010101,"lon":-115.1520004},"EWR":{"lat":40.6925010681,"lon":-74.1687011719},"ABQ":{"lat":35.0844,"lon":-106.6504},"BUF":{"lat":42.94049835,"lon":-78.73220062},"CLT":{"lat":35.2140007019,"lon":-80.9430999756},"RDU":{"lat":35.93543,"lon":-78.88075},"CLE":{"lat":41.50069,"lon":-81.68412},"CMH":{"lat":39.9980010986,"lon":-82.8918991089},"OKC":{"lat":35.46655,"lon":-97.65373},"PDX":{"lat":45.58869934,"lon":-122.5979996},"PHL":{"lat":39.8718986511,"lon":-75.2410964966},"PIT":{"lat":40.49150085,"lon":-80.23290253},"FSD":{"lat":43.540819819502,"lon":-96.65511577730963},"MEM":{"lat":35.0424003601,"lon":-89.9766998291},"BNA":{"lat":36.1245002747,"lon":-86.6781997681},"AUS":{"lat":30.1975,"lon":-97.6664},"DFW":{"lat":32.8968009949,"lon":-97.0380020142},"IAH":{"lat":29.9843997955,"lon":-95.3414001465},"MFE":{"lat":26.17580032,"lon":-98.23860168},"SAT":{"lat":29.429461,"lon":-98.487061},"SLC":{"lat":40.7883987427,"lon":-111.977996826},"IAD":{"lat":38.94449997,"lon":-77.45580292},"ORF":{"lat":36.8945999146,"lon":-76.2012023926},"RIC":{"lat":37.5051994324,"lon":-77.3197021484},"SEA":{"lat":47.4490013123,"lon":-122.308998108},"DAD":{"lat":16.02636,"lon":108.20869},"HAN":{"lat":21.221200943,"lon":105.806999206},"SGN":{"lat":10.8187999725,"lon":106.652000427},"LUN":{"lat":-15.371446,"lon":28.317837},"HRE":{"lat":-17.9318008423,"lon":31.0928001404}}
\ No newline at end of file
diff --git a/scripts/build-colo.js b/scripts/build-colo.js
new file mode 100644
index 0000000..763f0de
--- /dev/null
+++ b/scripts/build-colo.js
@@ -0,0 +1,19 @@
+import { writeFileSync } from 'node:fs'
+import { join } from 'node:path'
+
+async function main() {
+ const locations = await fetch('https://raw.githubusercontent.com/Netrvin/cloudflare-colo-list/refs/heads/main/locations.json')
+ if (!locations.ok) {
+ throw new Error('Failed to fetch locations')
+ }
+ const colos = await locations.json()
+ writeFileSync(join(import.meta.dirname, '../public/colos.json'), JSON.stringify(colos.reduce((acc, c) => {
+ acc[c.iata] = {
+ lat: c.lat,
+ lon: c.lon,
+ }
+ return acc
+ }, {}), 'utf8'))
+}
+
+main().catch(console.error)
diff --git a/server/api/location.ts b/server/api/location.ts
new file mode 100644
index 0000000..ca7c3e2
--- /dev/null
+++ b/server/api/location.ts
@@ -0,0 +1,7 @@
+export default eventHandler((event) => {
+ const { request: { cf } } = event.context.cloudflare
+ return {
+ latitude: cf.latitude,
+ longitude: cf.longitude,
+ }
+})
diff --git a/server/api/logs/events.ts b/server/api/logs/events.ts
index 663ac54..ed9c5ea 100644
--- a/server/api/logs/events.ts
+++ b/server/api/logs/events.ts
@@ -22,8 +22,13 @@ function events2logs(events: WAEEvents[]) {
_.push(event[`blob${i + 1}`])
return _
}, [])
+ const doubles = Array.from({ length: Object.keys(doublesMap).length }).fill(0).reduce((_, _c, i) => {
+ _.push(+event[`double${i + 1}`])
+ return _
+ }, [])
return {
...blobs2logs(blobs),
+ ...doubles2logs(doubles),
id: crypto.randomUUID(),
timestamp: date2unix(new Date(`${event.timestamp}Z`)),
}