feat(speedtest): full speedtest-tracker-style automation (2.9.0)
Switch worker to the Ookla CLI (jitter, packet loss, server, ISP, shareable result URL, bytes). Migration 028 enriches speedtest_results + adds a generic app_settings store. New /speedtest page: KPIs, throughput + latency charts, window stats, configurable schedule (reschedulable cron) & low-speed alert threshold, history table. SV card gains ping/jitter + a link through to the page. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// public/views/cards/speedtest.js
|
||||
// public/views/cards/speedtest.js — at-a-glance summary; full history at #/speedtest
|
||||
import { el, mount } from '../../dom.js';
|
||||
import { api } from '../../api.js';
|
||||
|
||||
@@ -6,17 +6,21 @@ let body;
|
||||
async function load() {
|
||||
if (!body) return;
|
||||
try {
|
||||
const hist = await api.get('/api/speedtest/history');
|
||||
const hist = (await api.get('/api/speedtest/history?limit=30')).filter(h => h.ok !== false);
|
||||
const latest = hist[0];
|
||||
const max = Math.max(1, ...hist.map(h => Number(h.down_mbps)));
|
||||
const bars = el('div', { style: { display: 'flex', gap: '2px', alignItems: 'flex-end', height: '40px', marginTop: '8px' } },
|
||||
const bars = el('div', { style: { display: 'flex', gap: '2px', alignItems: 'flex-end', height: '38px', marginTop: '8px' } },
|
||||
hist.slice(0, 30).reverse().map(h =>
|
||||
el('div', { style: { flex: '1', background: 'var(--accent-dim)',
|
||||
height: (Number(h.down_mbps) / max * 100) + '%' } })));
|
||||
mount(body,
|
||||
el('div', { class: 'sv-row', style: { fontSize: '20px' } },
|
||||
el('span', { style: { fontFamily: 'var(--font-mono)' } }, latest ? `${Number(latest.down_mbps).toFixed(0)}↓ ${Number(latest.up_mbps).toFixed(0)}↑` : '—'),
|
||||
el('span', { style: { fontFamily: 'var(--font-mono)' } },
|
||||
latest ? `${Number(latest.down_mbps).toFixed(0)}↓ ${Number(latest.up_mbps).toFixed(0)}↑` : '—'),
|
||||
el('button', { class: 'sv-run', onclick: runNow }, 'Run')),
|
||||
latest ? el('div', { class: 'sv-row', style: { fontSize: '11px' } },
|
||||
el('span', { class: 'k' }, `ping ${latest.ping_ms == null ? '—' : Number(latest.ping_ms).toFixed(0)} ms · jitter ${latest.jitter_ms == null ? '—' : Number(latest.jitter_ms).toFixed(1)}`),
|
||||
el('a', { href: '#/speedtest', class: 'sv-link' }, 'history ↗')) : null,
|
||||
bars);
|
||||
} catch { mount(body, el('span', { class: 'muted' }, 'No speedtest data')); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user