import React, { useState, useEffect, useRef } from 'react'; const SCREEN_OPTIONS = [ { value: 0, label: 'Alle Bildschirme' }, { value: 1, label: 'Bildschirm 1' }, { value: 2, label: 'Bildschirm 2' }, { value: 3, label: 'Bildschirm 3' }, { value: 4, label: 'Bildschirm 4' }, ]; export default function RemoteDesktopPanel({ agentId, agentHostname, autoConnect = false, fullscreen = false }) { const [status, setStatus] = useState('idle'); // idle | connecting | waiting | connected | error const [error, setError] = useState(''); const [fps, setFps] = useState(0); const [resolution, setResolution] = useState(''); const [screenIdx, setScreenIdx] = useState(0); const canvasRef = useRef(null); const wsRef = useRef(null); const fpsRef = useRef({ count: 0, last: Date.now() }); const connectedRef = useRef(false); const getWsUrl = () => { const proto = window.location.protocol === 'https:' ? 'wss' : 'ws'; return `${proto}://${window.location.host}/ws?type=rdp&agentId=${agentId}`; }; const connect = (screen = screenIdx) => { setStatus('connecting'); setError(''); const ws = new WebSocket(getWsUrl()); wsRef.current = ws; connectedRef.current = false; ws.onopen = () => { ws.send(JSON.stringify({ type: 'auth', token: localStorage.getItem('token') })); ws.send(JSON.stringify({ type: 'rdp_start', screen })); }; ws.onmessage = (e) => { try { const msg = JSON.parse(e.data); if (msg.type === 'rdp_consent_pending') { setStatus('waiting'); } else if (msg.type === 'rdp_frame') { if (!connectedRef.current) { connectedRef.current = true; setStatus('connected'); } const img = new window.Image(); img.onload = () => { const canvas = canvasRef.current; if (!canvas) return; if (canvas.width !== img.naturalWidth) canvas.width = img.naturalWidth; if (canvas.height !== img.naturalHeight) canvas.height = img.naturalHeight; canvas.getContext('2d').drawImage(img, 0, 0); if (msg.w && msg.h) setResolution(`${msg.w}×${msg.h}`); const now = Date.now(); fpsRef.current.count++; if (now - fpsRef.current.last >= 1000) { setFps(fpsRef.current.count); fpsRef.current = { count: 0, last: now }; } }; img.src = 'data:image/jpeg;base64,' + msg.data; } else if (msg.type === 'rdp_denied') { setStatus('error'); setError(msg.reason === 'consent_spawn_failed' ? 'Consent-Dialog konnte nicht geöffnet werden (schtasks-Fehler)' : 'Zugriff wurde vom Benutzer abgelehnt'); ws.close(); } else if (msg.type === 'rdp_disconnected') { setStatus('idle'); setError('Agent hat die Verbindung getrennt'); } } catch { } }; ws.onerror = () => { setStatus('error'); setError('WebSocket-Fehler'); }; ws.onclose = () => setStatus(s => (s === 'connected' || s === 'waiting' || s === 'connecting') ? 'idle' : s); }; const disconnect = () => { if (wsRef.current?.readyState === WebSocket.OPEN) { wsRef.current.send(JSON.stringify({ type: 'rdp_stop' })); wsRef.current.close(); } wsRef.current = null; const canvas = canvasRef.current; if (canvas) canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height); setStatus('idle'); setFps(0); setResolution(''); }; useEffect(() => { if (autoConnect) connect(screenIdx); return () => { wsRef.current?.close(); }; }, []); const statusColor = { connected: '#34d399', connecting: '#f59e0b', waiting: '#f59e0b', error: '#ef4444', idle: '#6b7280' }[status] ?? '#6b7280'; const statusLabel = { connected: 'Verbunden', connecting: 'Verbinde…', waiting: 'Warte auf Zustimmung…', error: 'Fehler', idle: 'Getrennt' }[status] ?? ''; const containerStyle = fullscreen ? { display: 'flex', flexDirection: 'column', height: '100vh', background: '#0d1117' } : { marginTop: 24, borderRadius: 16, border: '1px solid var(--border-color)', overflow: 'hidden', background: 'var(--bg-secondary)' }; return (
{/* Header */}
🖥️ Remote Desktop {agentHostname && {agentHostname}} {statusLabel} {status === 'connected' && resolution && ( {resolution} · {fps} fps )} {/* Screen-Selektor */}
{!fullscreen && ( )} {status === 'idle' || status === 'error' ? ( ) : ( )}
{/* Canvas-Bereich */} {(status === 'idle' || status === 'error') ? (
🖥️
Remote Desktop
Bildschirmübertragung via WebSocket · Agent v2.2.0+
{error &&
{error}
}
) : status === 'waiting' ? (
Warte auf Zustimmung…
Der Benutzer muss den Bildschirmzugriff erst erlauben.
) : (
{status === 'connecting' && (
Verbinde…
)}
)}
); }