Show durable connection requests and timestamped node availability

This commit is contained in:
archipelago
2026-10-05 23:10:34 -04:00
parent 10d31ae13c
commit 0c25449566
20 changed files with 473 additions and 142 deletions
+1 -1
View File
@@ -84,7 +84,7 @@
</div>
<div class="flex flex-wrap gap-x-3 gap-y-1 items-center justify-between text-xs text-white/40 mt-1">
<span>{{ node.uptime_secs === null ? 'Uptime unavailable' : 'Up ' + formatUptime(node.uptime_secs) }}</span>
<span>{{ fleetStatus(node.reported_at) === 'unknown' ? 'Status unknown' : fleetStatus(node.reported_at) === 'offline' ? 'Offline · last seen ' + timeAgo(node.reported_at) : 'Seen ' + timeAgo(node.reported_at) }}</span>
<span>{{ fleetStatus(node.reported_at) === 'unknown' ? 'Status unknown' : fleetStatus(node.reported_at) === 'stale' ? 'Not reporting · last seen ' + timeAgo(node.reported_at) : 'Seen ' + timeAgo(node.reported_at) }}</span>
</div>
</div>
</div>
@@ -5,7 +5,7 @@
<p class="text-2xl font-bold text-white">{{ nodeCount }}</p>
<p class="text-xs text-white/40">
<span class="fleet-dot-online"></span> {{ onlineCount }} online
<span class="ml-1 fleet-dot-offline"></span> {{ offlineCount }} offline
<span class="ml-1 fleet-dot-offline"></span> {{ offlineCount }} not reporting
<span v-if="unknownCount" class="ml-1">{{ unknownCount }} unknown</span>
</p>
</div>
@@ -18,7 +18,7 @@ describe('fleet unavailable readings', () => {
expect(cards).toHaveLength(2)
expect(cards[0]!.text()).toContain('0%')
expect(cards[0]!.text()).toContain('—')
expect(cards[0]!.text()).toContain('Offline · last seen 2d ago')
expect(cards[0]!.text()).toContain('Not reporting · last seen 2d ago')
expect(cards[1]!.text()).toContain('Status unknown')
expect(cards[1]!.text()).not.toContain('0%')
expect(cards[1]!.text()).toContain('Uptime unavailable')
@@ -46,7 +46,7 @@ describe('fleet data helpers', () => {
vi.useRealTimers()
})
it('sorts status with online nodes first, then latest report', () => {
it('sorts status with online nodes first without shuffling within a status group', () => {
vi.useFakeTimers()
vi.setSystemTime(new Date('2026-06-10T12:00:00Z'))
const nodes = [
@@ -56,8 +56,8 @@ describe('fleet data helpers', () => {
]
expect(sortFleetNodes(nodes, 'status').map(n => n.node_id)).toEqual([
'online-new',
'online-old',
'online-new',
'offline',
])
@@ -133,9 +133,9 @@ describe('fleet missing data and clock boundaries', () => {
expect(isOnline(value)).toBe(false)
expect(timeAgo(value)).toBe('Unknown')
}
expect(fleetStatus('2026-06-10T11:30:00Z')).toBe('offline')
expect(fleetStatus('2026-06-10T11:30:00Z')).toBe('stale')
expect(fleetStatus('2026-06-10T12:00:30Z')).toBe('online')
expect(sortFleetNodes([node('unknown', ''), node('future', '2027-01-01T00:00:00Z'), node('old', '2026-06-10T10:00:00Z'), node('new', '2026-06-10T11:59:00Z')], 'status').map(n => n.node_id)).toEqual(['new', 'old', 'unknown', 'future'])
expect(sortFleetNodes([node('unknown', ''), node('future', '2027-01-01T00:00:00Z'), node('old', '2026-06-10T10:00:00Z'), node('new', '2026-06-10T11:59:00Z')], 'status').map(n => n.node_id)).toEqual(['new', 'unknown', 'future', 'old'])
})
it('keeps valid zero separate from unavailable and invalid measurements', () => {
expect(normalizeFleetNode({cpu_pct: 0}).cpu_pct).toBe(0)
+5 -7
View File
@@ -70,10 +70,10 @@ export function timeAgo(dateStr: string): string {
return `${diffDays}d ago`
}
export function fleetStatus(reportedAt: string): 'online' | 'offline' | 'unknown' {
export function fleetStatus(reportedAt: string): 'online' | 'stale' | 'unknown' {
const age = Date.now() - new Date(reportedAt).getTime()
if (!Number.isFinite(age) || age < -60_000) return 'unknown'
return age < 30 * 60 * 1000 ? 'online' : 'offline'
return age < 30 * 60 * 1000 ? 'online' : 'stale'
}
export function isOnline(reportedAt: string): boolean {
@@ -159,10 +159,8 @@ export function sortFleetNodes(nodes: FleetNode[], sortBy: SortOption): FleetNod
switch (sortBy) {
case 'status':
sorted.sort((a, b) => {
const aOnline = isOnline(a.reported_at)
const bOnline = isOnline(b.reported_at)
if (aOnline !== bOnline) return aOnline ? -1 : 1
return reportTime(b) - reportTime(a)
const rank = { online: 0, unknown: 1, stale: 2 }
return rank[fleetStatus(a.reported_at)] - rank[fleetStatus(b.reported_at)]
})
break
case 'last-seen':
@@ -277,7 +275,7 @@ export function useFleetData() {
// --- Computed ---
const onlineCount = computed(() => nodes.value.filter(n => isOnline(n.reported_at)).length)
const offlineCount = computed(() => nodes.value.filter(n => fleetStatus(n.reported_at) === 'offline').length)
const offlineCount = computed(() => nodes.value.filter(n => fleetStatus(n.reported_at) === 'stale').length)
const unknownCount = computed(() => nodes.value.filter(n => fleetStatus(n.reported_at) === 'unknown').length)
const healthyCount = computed(() => nodes.value.filter(n => isOnline(n.reported_at) && n.cpu_pct !== null && n.mem_pct !== null && n.disk_pct !== null && n.recent_alerts.length === 0).length)