diff --git a/VERSION b/VERSION index 1171c84..2c69d9f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.42 +1.1.43 diff --git a/management-ui/src/pages/RoutingRules/index.tsx b/management-ui/src/pages/RoutingRules/index.tsx index a657310..8cbb67b 100644 --- a/management-ui/src/pages/RoutingRules/index.tsx +++ b/management-ui/src/pages/RoutingRules/index.tsx @@ -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 { return (r.data.data as { backends?: Backend[] }).backends ?? [] } +interface HAProxyStat { backend: string; status: string } +async function listHAProxyStats(): Promise { + 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(null) const [creating, setCreating] = useState(false) @@ -96,7 +116,19 @@ export default function RoutingRulesPage() { const columns: ColumnsType = [ { 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 ( + + {backendLabel(id)} + {health === 'UP' && UP} + {health === 'DOWN' && DOWN} + + ) + }, + }, { title: t('routing.priority'), dataIndex: 'priority', key: 'priority' }, { title: t('routing.active'), dataIndex: 'active', key: 'active', render: (v: boolean) => }, {