|
|
|
|
@@ -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 }}
|
|
|
|
|
|