// 中间件监控 — Redis(单机/集群)/ MySQL 可视化可观测 // Logan.Li import { useState, useEffect, useCallback, useRef } from 'react'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Database, Server, Activity, Loader2, Plus, Zap, RefreshCw } from 'lucide-react'; import { middlewareMonitorApi } from '@/api'; import { toast } from 'sonner'; import { useConfirm } from '@/components/ui/confirm-dialog'; import { MwOverviewCharts } from './middleware-monitor/MwOverviewCharts'; import { MwTargetChartsPanel } from './middleware-monitor/MwTargetChartsPanel'; import { InstanceCard } from './middleware-monitor/InstanceCard'; import { MiddlewareDialog } from './middleware-monitor/MiddlewareDialog'; import type { MwTarget } from './middleware-monitor/helpers'; import { REDIS_CHARTS, MYSQL_CHARTS, evalMwHealth, worstHealthLevel } from './middleware-monitor/helpers'; import { useTranslation } from 'react-i18next'; type TabKey = 'redis' | 'mysql'; const REFRESH_INTERVAL_MS = 30_000; interface MwListResponse { list: MwTarget[]; total: number } export function MiddlewareMonitor() { const { t } = useTranslation(); const confirm = useConfirm(); const [targets, setTargets] = useState([]); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(''); const [tab, setTab] = useState('redis'); const [selectedRedis, setSelectedRedis] = useState(null); const [selectedMysql, setSelectedMysql] = useState(null); const [dialogOpen, setDialogOpen] = useState(false); const [editTarget, setEditTarget] = useState(null); const [testing, setTesting] = useState(null); const [seeding, setSeeding] = useState(false); const [lastRefreshAt, setLastRefreshAt] = useState(null); const hasLoaded = useRef(false); const syncSelection = useCallback((list: MwTarget[]) => { const redis = list.filter((t) => t.description === 'redis'); const mysql = list.filter((t) => t.description === 'mysql'); setSelectedRedis((prev) => { if (prev && redis.some((t) => t.id === prev.id)) return redis.find((t) => t.id === prev.id) || null; return redis.find((t) => t.status === 'online') || redis[0] || null; }); setSelectedMysql((prev) => { if (prev && mysql.some((t) => t.id === prev.id)) return mysql.find((t) => t.id === prev.id) || null; return mysql.find((t) => t.status === 'online') || mysql[0] || null; }); if (redis.length === 0 && mysql.length > 0) setTab('mysql'); }, []); const loadTargets = useCallback(async (silent = false) => { try { if (!silent) setLoading(true); else setRefreshing(true); setError(''); const data = await middlewareMonitorApi.list() as MwListResponse; const list = data?.list || []; setTargets(list); syncSelection(list); setLastRefreshAt(new Date()); hasLoaded.current = true; } catch (err) { if (!silent) { setError((err as { message?: string }).message || t('common.loadFailed')); setTargets([]); } } finally { setLoading(false); setRefreshing(false); } }, [syncSelection]); useEffect(() => { loadTargets(); }, [loadTargets]); // 30s 静默刷新,与采集周期对齐 // Logan.Li useEffect(() => { if (!hasLoaded.current || targets.length === 0) return; const timer = setInterval(() => loadTargets(true), REFRESH_INTERVAL_MS); return () => clearInterval(timer); }, [targets.length, loadTargets]); const redisTargets = targets.filter((t) => t.description === 'redis'); const mysqlTargets = targets.filter((t) => t.description === 'mysql'); const onlineCount = targets.filter((t) => t.status === 'online').length; const warnCount = targets.filter((t) => worstHealthLevel(evalMwHealth(t)) !== 'ok').length; const handleDelete = async (target: MwTarget) => { const ok = await confirm({ title: t('monitor.middlewaremonitor.sf9be97'), description: t('monitor.middlewaremonitor.s45a9e0', { target.name: target.name }), confirmText: t('common.delete') }); if (!ok) return; try { await middlewareMonitorApi.delete(target.id); toast.success(t('alert.deleted')); loadTargets(); } catch (err) { toast.error(t('common.deleteFailed'), { description: (err as { message?: string }).message }); } }; const handleTest = async (target: MwTarget) => { setTesting(target.id); try { const res = await middlewareMonitorApi.test(target.id) as { success: boolean; message: string }; if (res.success) { toast.success(t('aiops.llmTestSuccess'), { description: res.message }); loadTargets(true); } else toast.error(t('aiops.llmTestFailed'), { description: res.message }); } catch (err) { toast.error(t('monitor.middlewaremonitor.s9710d9'), { description: (err as { message?: string }).message }); } finally { setTesting(null); } }; const handleSeedDefaults = async () => { setSeeding(true); try { const res = await middlewareMonitorApi.seedDefaults() as { created: number; message: string; failed?: string[] }; await loadTargets(); if (res.failed?.length) toast.error(t('monitor.middlewaremonitor.s669e89'), { description: res.message }); else if (res.created > 0) toast.success(t('monitor.middlewaremonitor.s11a31e'), { description: res.message }); else toast.success(t('monitor.middlewaremonitor.s7dda0f')); } catch (err) { toast.error(t('deploy.toast.addFailed'), { description: (err as { message?: string }).message }); } finally { setSeeding(false); } }; const handleCollectNow = async () => { try { const res = await middlewareMonitorApi.collectNow() as { message: string }; toast.success(t('monitor.middlewaremonitor.sc2c744'), { description: res.message }); loadTargets(true); } catch (err) { toast.error(t('monitor.middlewaremonitor.s5665b6'), { description: (err as { message?: string }).message }); } }; const openCreate = (type?: TabKey) => { setEditTarget(null); if (type) setTab(type); setDialogOpen(true); }; const currentTargets = tab === 'redis' ? redisTargets : mysqlTargets; const selected = tab === 'redis' ? selectedRedis : selectedMysql; const setSelected = tab === 'redis' ? setSelectedRedis : setSelectedMysql; return (

{t('monitor.middleware')}

Redis 单机/集群 · MySQL · 30s 自动刷新 {lastRefreshAt && ( 上次更新 {lastRefreshAt.toLocaleTimeString()} {refreshing && t('monitor.middlewaremonitor.s4d2bc9')} )}

{targets.length === 0 && ( )}
{loading && (
加载中...
)} {error && !loading && ( {error} )} {!loading && !error && targets.length === 0 && (

{t('monitor.middlewaremonitor.sbbe62a')}

)} {!loading && !error && targets.length > 0 && ( <>
{[ { label: t('monitor.middlewaremonitor.s35a558'), value: targets.length, icon: Activity }, { label: t('common.online'), value: onlineCount, icon: Database, color: 'dg-text-green' }, { label: t('dashboard.stat.alerts'), value: warnCount, icon: Activity, color: warnCount > 0 ? 'dg-text-yellow' : 'dg-text-green' }, { label: 'Redis', value: redisTargets.length, icon: Database, color: 'dg-text-red' }, { label: 'MySQL', value: mysqlTargets.length, icon: Server, color: 'dg-text-blue' }, ].map((item) => (

{item.label}

{item.value}

))}
{([ { key: 'redis' as TabKey, label: 'Redis', count: redisTargets.length, icon: Database }, { key: 'mysql' as TabKey, label: 'MySQL', count: mysqlTargets.length, icon: Server }, ]).map((t) => ( ))}
{currentTargets.length === 0 ? ( 暂无 {tab === 'redis' ? 'Redis' : 'MySQL'} 实例, ) : (
{currentTargets.map((t) => ( setSelected(t)} onEdit={() => { setEditTarget(t); setDialogOpen(true); }} onDelete={() => handleDelete(t)} onTest={() => handleTest(t)} testing={testing === t.id} /> ))}
)} )} loadTargets(true)} />
); }