// 终端审计实时监控 Dialog — 与 WebSSH 终端弹框一致的布局与适配 // Logan.Li import { useEffect, useRef, useState } from 'react'; import { Terminal as XTerm } from 'xterm'; import { FitAddon } from 'xterm-addon-fit'; import 'xterm/css/xterm.css'; import { Dialog, DialogContent, DialogDescription, DialogTitle, } from '@/components/ui/dialog'; import { Badge } from '@/components/ui/badge'; import { Loader2, Maximize2, Minimize2, Monitor, X } from 'lucide-react'; import { cn } from '@/lib/utils'; import { attachTerminalResize, mountXterm } from '@/lib/xterm-utils'; import { useUserStore } from '@/store'; import { TerminalLoadingOverlay } from '@/components/common/TerminalLoadingOverlay'; interface SessionInfo { session_id: number; username: string; server_name: string; server_ip: string; start_time: string; status: string; } interface LiveMonitorProps { open: boolean; onOpenChange: (open: boolean) => void; sessionId: number | null; sessionInfo?: SessionInfo | null; } export function LiveMonitorDialog({ open, onOpenChange, sessionId, sessionInfo }: LiveMonitorProps) { const containerRef = useRef(null); const termRef = useRef(null); const fitRef = useRef(null); const wsRef = useRef(null); const [connected, setConnected] = useState(false); const [connectError, setConnectError] = useState(''); const [meta, setMeta] = useState(sessionInfo || null); const [fullscreen, setFullscreen] = useState(false); useEffect(() => { if (!open) { setConnected(false); setConnectError(''); setFullscreen(false); } }, [open]); useEffect(() => { if (!open || !sessionId) return; setConnected(false); setConnectError(''); const term = new XTerm({ cursorBlink: false, fontSize: 13, fontFamily: 'Menlo, Monaco, "Courier New", monospace', disableStdin: true, allowProposedApi: true, scrollback: 5000, theme: { background: '#0a0e14', foreground: '#d6d6d6', cursor: '#d6d6d6', selectionBackground: 'rgba(255,255,255,0.2)', }, }); const fitAddon = new FitAddon(); term.loadAddon(fitAddon); termRef.current = term; fitRef.current = fitAddon; const token = useUserStore.getState().token || localStorage.getItem('token') || (() => { try { const stored = JSON.parse(localStorage.getItem('user-storage') || '{}'); return stored?.state?.token || ''; } catch { return ''; } })(); const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const wsUrl = `${protocol}//${window.location.host}/ws/audit/${sessionId}?token=${encodeURIComponent(token)}`; const ws = new WebSocket(wsUrl); wsRef.current = ws; let didOpen = false; ws.onopen = () => { didOpen = true; setConnected(true); setConnectError(''); mountXterm(term, fitAddon, () => containerRef.current); term.writeln('\x1b[36m[审计监控] 已连接到会话...\x1b[0m'); }; ws.onmessage = (event) => { try { const msg = JSON.parse(event.data); switch (msg.type) { case 'session_info': setMeta(msg.payload); term.writeln( `\x1b[36m用户: ${msg.payload.username} 服务器: ${msg.payload.server_name} (${msg.payload.server_ip})\x1b[0m`, ); break; case 'history': if (msg.data) term.write(msg.data); break; case 'output': if (msg.data) term.write(msg.data); break; } } catch { /* ignore */ } }; ws.onerror = () => setConnectError('WebSocket 连接失败'); ws.onclose = (e) => { if (!didOpen) { setConnectError(`连接失败 (code=${e.code}): ${e.reason || '认证失败或网络问题'}`); } setConnected(false); term.writeln('\r\n\x1b[33m[审计监控] 连接已断开\x1b[0m'); }; const timeout = setTimeout(() => { if (!didOpen) { setConnectError('连接超时 — 请确认已登录且有活跃会话'); ws.close(); } }, 5000); return () => { clearTimeout(timeout); ws.close(); term.dispose(); termRef.current = null; fitRef.current = null; wsRef.current = null; setConnected(false); }; }, [open, sessionId]); // 容器尺寸变化时持续 fit // Logan.Li useEffect(() => { if (!open) return; let cleanup: (() => void) | undefined; let retry = 0; const attach = () => { const container = containerRef.current; const fit = fitRef.current; const term = termRef.current; if (!container || !fit || !term) { if (retry++ < 30) requestAnimationFrame(attach); return; } cleanup = attachTerminalResize(container, fit, term); }; attach(); return () => cleanup?.(); }, [open, sessionId, fullscreen]); const formatTime = (t: string) => (t ? new Date(t).toLocaleString() : '--'); return ( 终端实时监控 审计在线 WebSSH 会话 {/* 工具栏 — 与 TerminalDialog 一致 // Logan.Li */}
终端实时监控 {meta && ( {meta.username} · {meta.server_name} ({meta.server_ip}) · {formatTime(meta.start_time)} )} {connected && ( ● 在线 )} {!connected && !connectError && ( 连接中 )} {connectError && ( 连接失败 )}
{connectError && (

{connectError}

)}
); }