import { useState, useEffect, useRef, useCallback } from 'react'; import { useServerMonitor } from './hooks/useServerMonitor'; import { usePublicServers } from './hooks/usePublicServers'; import { ServerCard } from './components/ServerCard/ServerCard'; import { ServerDetail } from './components/ServerDetail/ServerDetail'; import { AdminPanel } from './components/AdminPanel/AdminPanel'; import { RandomBackdrop } from './components/RandomBackdrop/RandomBackdrop'; import { verifyAdminToken, ADMIN_TOKEN_STORAGE, } from './api/central'; import './App.css'; function App() { const { servers, loading, error, reload } = usePublicServers(30000); const [selectedServerId, setSelectedServerId] = useState(null); const [showHeader, setShowHeader] = useState(true); const [showTokenGate, setShowTokenGate] = useState(false); const [tokenInput, setTokenInput] = useState(''); const [tokenError, setTokenError] = useState(null); const [adminOpen, setAdminOpen] = useState(false); const logoClickCount = useRef(0); const logoClickReset = useRef | null>(null); const statuses = useServerMonitor(); const handleLogoClick = useCallback(() => { if (logoClickReset.current) { clearTimeout(logoClickReset.current); } logoClickCount.current += 1; if (logoClickCount.current >= 5) { logoClickCount.current = 0; const saved = sessionStorage.getItem(ADMIN_TOKEN_STORAGE); if (saved) { setAdminOpen(true); } else { setShowTokenGate(true); setTokenInput(''); setTokenError(null); } return; } logoClickReset.current = setTimeout(() => { logoClickCount.current = 0; }, 2000); }, []); const submitToken = async () => { const ok = await verifyAdminToken(tokenInput.trim()); if (!ok) { setTokenError('口令错误'); return; } sessionStorage.setItem(ADMIN_TOKEN_STORAGE, tokenInput.trim()); setShowTokenGate(false); setTokenError(null); setAdminOpen(true); }; const closeAdmin = () => { setAdminOpen(false); }; const invalidateAdminSession = () => { sessionStorage.removeItem(ADMIN_TOKEN_STORAGE); setAdminOpen(false); alert('管理凭证已失效,请重新验证'); }; useEffect(() => { return () => { if (logoClickReset.current) clearTimeout(logoClickReset.current); }; }, []); const selectedStatus = selectedServerId ? statuses[selectedServerId] : null; const selectedServer = servers.find((s) => s.id === selectedServerId); const adminToken = sessionStorage.getItem(ADMIN_TOKEN_STORAGE) || ''; return (
{showTokenGate && (

管理验证

请输入管理口令(内网默认与中心服务 ADMIN_TOKEN 一致)。

{ setTokenInput(e.target.value); setTokenError(null); }} autoFocus onKeyDown={(e) => { if (e.key === 'Enter') void submitToken(); }} /> {tokenError &&

{tokenError}

}
)} {adminOpen && adminToken && ( void reload()} /> )} {!showHeader && ( )} {showHeader && (

萌芽监控面板

)}
{loading ? (

正在从中心服务加载节点列表…

) : error ? (

无法加载监控列表

{error}

请确认 mengyamonitor-backend-server 已启动,且 VITE_CENTRAL_API_URL 或开发代理配置正确。

) : servers.length === 0 ? (

暂无已启用的监控节点

请联系管理员在后台添加采集端地址。

) : ( servers.map((server) => { const status = statuses[server.id]; const storageUsage = status?.metrics?.storage?.reduce((max, s) => Math.max(max, s.usedPercent), 0) || 0; return ( setSelectedServerId(server.id)} /> ); }) )}
{selectedStatus?.metrics && selectedServer && ( setSelectedServerId(null)} /> )}
); } export default App;