feat(dashboard): resolve eg_backend_<id> → friendly name in HAProxy card

The HAProxy stats card now shows the configured backend name (e.g.
"My Backend/web1") instead of the internal HAProxy name
("eg_backend_1/web1"). Also surfaces the socket error message when
HAProxy is unreachable (⚠ HAProxy socket: <error>) instead of the
generic "no backends" empty state — operators immediately see whether
the box is unconfigured or the socket is down. v1.1.114.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-05-27 07:59:53 +02:00
parent 67c5180168
commit 7c86656c36
5 changed files with 28 additions and 10 deletions

View File

@@ -6,7 +6,7 @@ import {
} from '@ant-design/icons'
import { Link } from 'react-router-dom'
import { useQuery } from '@tanstack/react-query'
import { useEffect, useRef, useState } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import apiClient, { isEnvelope } from '../../api/client'
@@ -201,9 +201,9 @@ export default function DashboardPage() {
try {
const r = await apiClient.get('/haproxy/stats')
if (!isEnvelope(r.data)) return { backends: [] as HAProxyBackend[], frontends: [] as HAProxyFrontend[] }
const d = r.data.data as { backends?: HAProxyBackend[]; frontends?: HAProxyFrontend[] }
return { backends: d.backends ?? [], frontends: d.frontends ?? [] }
} catch { return { backends: [] as HAProxyBackend[], frontends: [] as HAProxyFrontend[] } }
const d = r.data.data as { backends?: HAProxyBackend[]; frontends?: HAProxyFrontend[]; error?: string }
return { backends: d.backends ?? [], frontends: d.frontends ?? [], error: d.error }
} catch { return { backends: [] as HAProxyBackend[], frontends: [] as HAProxyFrontend[], error: undefined } }
},
refetchInterval: 10_000,
})
@@ -267,6 +267,18 @@ export default function DashboardPage() {
const now = Date.now()
const maintenanceDomains = (domains.data ?? []).filter(d => d.maintenance_mode)
// eg_backend_<id> → friendly name lookup for HAProxy stats display.
const backendIdToName = useMemo(() => {
const m = new Map<number, string>()
for (const b of backends.data ?? []) m.set(b.id, b.name)
return m
}, [backends.data])
const resolveHAName = (haName: string): string => {
const match = /^eg_backend_(\d+)$/.exec(haName)
const id = match ? Number(match[1]) : null
return id != null ? (backendIdToName.get(id) ?? haName) : haName
}
// Backends die komplett DOWN sind (mind. 1 Server mit echtem Check,
// aber kein einziger UP) — liefert friendly Backend-Namen.
const downBackends = (() => {
@@ -486,13 +498,19 @@ export default function DashboardPage() {
</div>
)}
{(haproxyBackends.data ?? []).length === 0 ? (
<Text type="secondary">{t('dashboard.haproxyCard.empty')}</Text>
haproxyStats.data?.error ? (
<Text type="secondary" style={{ fontSize: 12 }}>
HAProxy socket: <code style={{ fontSize: 11 }}>{haproxyStats.data.error}</code>
</Text>
) : (
<Text type="secondary">{t('dashboard.haproxyCard.empty')}</Text>
)
) : (
<Space direction="vertical" style={{ width: '100%' }} size={2}>
{(haproxyBackends.data ?? []).map((b, i) => (
<div key={i} style={{ fontSize: 12, color: '#334155', borderBottom: '1px solid #F1F5F9', padding: '4px 0' }}>
<Space size={6} wrap>
<code style={{ fontSize: 11 }}>{b.backend}/{b.server}</code>
<code style={{ fontSize: 11 }}>{resolveHAName(b.backend)}/{b.server}</code>
<Tag
color={b.status === 'UP' ? 'green' : b.status === 'no check' ? 'default' : 'red'}
style={{ margin: 0, fontSize: 10 }}