feat(ui): traffic tooltip (sess/bytes) auf Backend-Health-Chips
Backends- und Routing-Rules-Seite zeigen jetzt beim Hover auf den UP/DOWN-Chip die aggregierten HAProxy-Stats des jeweiligen Backends: aktive Sessions + kumulierte Bytes rein/raus seit letztem HAProxy-Start. Daten kommen aus dem bereits vorhandenen /haproxy/stats-Endpunkt. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react'
|
||||
import {
|
||||
Alert, Button, Card, Form, Input, InputNumber, Modal, Popconfirm,
|
||||
Select, Space, Switch, Table, Tag, Typography, message,
|
||||
Select, Space, Switch, Table, Tag, Tooltip, Typography, message,
|
||||
} from 'antd'
|
||||
import type { ColumnsType } from 'antd/es/table'
|
||||
import { DatabaseOutlined, PlusOutlined } from '@ant-design/icons'
|
||||
@@ -86,7 +86,17 @@ async function listDomains(): Promise<DomainFull[]> {
|
||||
return (r.data.data as { domains?: DomainFull[] }).domains ?? []
|
||||
}
|
||||
|
||||
interface HAProxyStat { backend: string; server: string; status: string }
|
||||
interface HAProxyStat {
|
||||
backend: string; server: string; status: string
|
||||
sessions: number; bytes_in: number; bytes_out: number
|
||||
}
|
||||
|
||||
function fmtBytes(n: number): string {
|
||||
if (n >= 1_073_741_824) return (n / 1_073_741_824).toFixed(1) + ' GB'
|
||||
if (n >= 1_048_576) return (n / 1_048_576).toFixed(1) + ' MB'
|
||||
if (n >= 1_024) return (n / 1_024).toFixed(0) + ' KB'
|
||||
return n + ' B'
|
||||
}
|
||||
async function listHAProxyStats(): Promise<HAProxyStat[]> {
|
||||
try {
|
||||
const r = await apiClient.get('/haproxy/stats')
|
||||
@@ -118,6 +128,16 @@ export default function BackendsPage() {
|
||||
return 'DOWN'
|
||||
}
|
||||
|
||||
const backendTraffic = (id: number) => {
|
||||
const servers = (haproxyStats ?? []).filter(s => s.backend === `eg_backend_${id}`)
|
||||
if (!servers.length) return null
|
||||
return {
|
||||
sessions: servers.reduce((a, s) => a + (s.sessions ?? 0), 0),
|
||||
bytesIn: servers.reduce((a, s) => a + (s.bytes_in ?? 0), 0),
|
||||
bytesOut: servers.reduce((a, s) => a + (s.bytes_out ?? 0), 0),
|
||||
}
|
||||
}
|
||||
|
||||
// server-counts pro Backend laden wir lazy bei Expansion; in der
|
||||
// Tabelle reicht ein Hinweis ob 0 / N Server.
|
||||
const { data: serverCounts } = useQuery({
|
||||
@@ -243,7 +263,15 @@ export default function BackendsPage() {
|
||||
const s = backendLiveStatus(row.id)
|
||||
if (!s) return <Text type="secondary" style={{ fontSize: 12 }}>—</Text>
|
||||
const color = s === 'UP' ? 'green' : s === 'DEGRADED' ? 'orange' : 'red'
|
||||
return <Tag color={color} style={{ margin: 0 }}>{s}</Tag>
|
||||
const tr = backendTraffic(row.id)
|
||||
const tip = tr
|
||||
? `${tr.sessions} sess · ↓${fmtBytes(tr.bytesIn)} ↑${fmtBytes(tr.bytesOut)}`
|
||||
: undefined
|
||||
return (
|
||||
<Tooltip title={tip}>
|
||||
<Tag color={color} style={{ margin: 0 }}>{s}</Tag>
|
||||
</Tooltip>
|
||||
)
|
||||
},
|
||||
},
|
||||
{ title: t('backends.active'), dataIndex: 'active', key: 'active', render: (v: boolean) => <StatusDot active={v} /> },
|
||||
|
||||
Reference in New Issue
Block a user