98 lines
3.6 KiB
Cheetah
98 lines
3.6 KiB
Cheetah
{{define "glass"}}
|
|
<div class="hero container">
|
|
<p class="label accent mb-4">Network</p>
|
|
<h1 class="mb-4">Looking Glass</h1>
|
|
<p class="lead">{{len .Pops}} points of presence. Find the fastest node for you.</p>
|
|
</div>
|
|
|
|
<div class="section container" style="padding-top:24px">
|
|
<div class="glass-grid">
|
|
{{range .Pops}}
|
|
<div class="glass-pop {{if eq .Status "live"}}glass-live{{else}}glass-planned{{end}}">
|
|
<div class="glass-header">
|
|
<div><div class="pop-city">{{.City}}</div><div class="pop-country">{{.CountryFull}}</div></div>
|
|
<span class="glass-status {{if eq .Status "live"}}glass-status-live{{else}}glass-status-planned{{end}}">{{.Status}}</span>
|
|
</div>
|
|
{{if eq .Status "live"}}<div class="glass-latency-block">
|
|
<div class="glass-latency-left"><span class="glass-latency-title">Response time</span><span class="glass-latency-hint">lower is better</span></div>
|
|
<div class="glass-latency-hero glass-latency" data-dns="{{.DNS}}" data-status="{{.Status}}">—</div>
|
|
</div>
|
|
<div class="glass-details">
|
|
{{if .IP}}<div class="glass-row">
|
|
<span class="glass-key">IPv4</span>
|
|
<span class="glass-val mono">{{.IP}}</span>
|
|
</div>{{end}}
|
|
{{if .DNS}}<div class="glass-row">
|
|
<span class="glass-key">DNS</span>
|
|
<span class="glass-val mono">{{.DNS}}</span>
|
|
</div>{{end}}
|
|
</div>{{end}}
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{define "glass-script"}}
|
|
<script>
|
|
(function() {
|
|
const grid = document.querySelector('.glass-grid');
|
|
|
|
function classify(ms) {
|
|
if (ms < 60) return 'glass-fast';
|
|
if (ms < 120) return 'glass-ok';
|
|
return 'glass-slow';
|
|
}
|
|
|
|
function applyResult(el, ms) {
|
|
el.classList.remove('glass-fast', 'glass-ok', 'glass-slow');
|
|
if (ms < 4900) {
|
|
el.textContent = ms + ' ms';
|
|
el.dataset.ms = ms;
|
|
el.classList.add(classify(ms));
|
|
} else {
|
|
el.textContent = 'down';
|
|
el.dataset.ms = 99999;
|
|
el.classList.add('glass-slow');
|
|
}
|
|
}
|
|
|
|
function sortGrid() {
|
|
const arr = Array.from(grid.querySelectorAll('.glass-pop'));
|
|
arr.sort((a, b) => {
|
|
const aMs = parseInt(a.querySelector('.glass-latency')?.dataset.ms || 99998);
|
|
const bMs = parseInt(b.querySelector('.glass-latency')?.dataset.ms || 99998);
|
|
return aMs - bMs;
|
|
});
|
|
arr.forEach(el => grid.appendChild(el));
|
|
}
|
|
|
|
function ping(el) {
|
|
const dns = el.dataset.dns;
|
|
if (!dns) { el.textContent = '—'; return Promise.resolve(); }
|
|
el.textContent = '...';
|
|
// Warm-up: first fetch establishes TLS, second measures actual latency
|
|
return fetch('https://' + dns + ':1984/ping').then(() => {}).catch(() => {}).then(() => {
|
|
const t0 = performance.now();
|
|
return fetch('https://' + dns + ':1984/ping').then(() => {}).catch(() => {}).then(() => {
|
|
applyResult(el, Math.round(performance.now() - t0));
|
|
sortGrid();
|
|
});
|
|
});
|
|
}
|
|
|
|
async function pingAll() {
|
|
const els = Array.from(document.querySelectorAll('.glass-latency[data-status="live"][data-dns]'));
|
|
if (window.innerWidth < 768) {
|
|
for (const el of els) { await ping(el); sortGrid(); }
|
|
} else {
|
|
els.forEach(el => ping(el));
|
|
}
|
|
}
|
|
|
|
pingAll();
|
|
setInterval(pingAll, 60000);
|
|
})();
|
|
</script>
|
|
{{end}}
|