feat(ui): HAProxy-Health-Chip in Routing-Rules-Tabelle

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-05-19 16:22:15 +02:00
parent 35b7308ce2
commit 20f3a4fe26
2 changed files with 35 additions and 3 deletions

View File

@@ -1,5 +1,5 @@
import { useState } from 'react'
import { Button, Form, Input, InputNumber, Modal, Select, Switch, message } from 'antd'
import { Button, Form, Input, InputNumber, Modal, Select, Space, Switch, Tag, message } from 'antd'
import type { ColumnsType } from 'antd/es/table'
import { BranchesOutlined, PlusOutlined } from '@ant-design/icons'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
@@ -50,6 +50,15 @@ async function listBackends(): Promise<Backend[]> {
return (r.data.data as { backends?: Backend[] }).backends ?? []
}
interface HAProxyStat { backend: string; status: string }
async function listHAProxyStats(): Promise<HAProxyStat[]> {
try {
const r = await apiClient.get('/haproxy/stats')
if (!isEnvelope(r.data)) return []
return (r.data.data as { backends?: HAProxyStat[] }).backends ?? []
} catch { return [] }
}
export default function RoutingRulesPage() {
const { t } = useTranslation()
const qc = useQueryClient()
@@ -57,12 +66,23 @@ export default function RoutingRulesPage() {
const { data: rules, isLoading } = useQuery({ queryKey: ['routing-rules'], queryFn: listRules })
const { data: domains } = useQuery({ queryKey: ['domains'], queryFn: listDomains })
const { data: backends } = useQuery({ queryKey: ['backends'], queryFn: listBackends })
const { data: haproxyStats } = useQuery({
queryKey: ['haproxy', 'stats'],
queryFn: listHAProxyStats,
refetchInterval: 15_000,
})
const domainName = (id: number) => domains?.find((d) => d.id === id)?.name ?? `#${id}`
const backendLabel = (id: number) => {
const b = backends?.find((x) => x.id === id)
return b ? `${b.name} (${b.address}:${b.port})` : `#${id}`
}
const backendHealth = (id: number): 'UP' | 'DOWN' | null => {
if (!haproxyStats?.length) return null
const servers = haproxyStats.filter(s => s.backend === `eg_backend_${id}`)
if (!servers.length) return null
return servers.some(s => s.status === 'UP') ? 'UP' : 'DOWN'
}
const [editing, setEditing] = useState<RoutingRule | null>(null)
const [creating, setCreating] = useState(false)
@@ -96,7 +116,19 @@ export default function RoutingRulesPage() {
const columns: ColumnsType<RoutingRule> = [
{ title: t('routing.domain'), dataIndex: 'domain_id', key: 'domain', render: (id: number) => domainName(id) },
{ title: t('routing.pathPrefix'), dataIndex: 'path_prefix', key: 'path' },
{ title: t('routing.backend'), dataIndex: 'backend_id', key: 'backend', render: (id: number) => backendLabel(id) },
{
title: t('routing.backend'), dataIndex: 'backend_id', key: 'backend',
render: (id: number) => {
const health = backendHealth(id)
return (
<Space size={4}>
<span>{backendLabel(id)}</span>
{health === 'UP' && <Tag color="green" style={{ margin: 0 }}>UP</Tag>}
{health === 'DOWN' && <Tag color="red" style={{ margin: 0 }}>DOWN</Tag>}
</Space>
)
},
},
{ title: t('routing.priority'), dataIndex: 'priority', key: 'priority' },
{ title: t('routing.active'), dataIndex: 'active', key: 'active', render: (v: boolean) => <StatusDot active={v} /> },
{