diff --git a/frontend/src/components/ConsoleViewer.jsx b/frontend/src/components/ConsoleViewer.jsx deleted file mode 100644 index f88c5a9..0000000 --- a/frontend/src/components/ConsoleViewer.jsx +++ /dev/null @@ -1,86 +0,0 @@ -// VNC-консоль через noVNC. -// -// В noVNC 1.4.0 основной модуль — это ES-модуль (core/rfb.js), который -// экспортирует класс RFB через `export default`. Грузим его через -// динамический import(). Скрипт и его зависимости (vendor/, utils/, -// core/inflator.js и т.п.) лежат в /novnc/* внутри nginx-контейнера — -// собирается в Docker-образе из GitHub release (см. frontend/Dockerfile). - -import { useEffect, useRef, useState } from "react"; -import { api } from "../api.js"; - -// @vite-ignore — запрещаем Vite пытаться разрешить путь на этапе сборки, -// иначе он упадёт, не найдя модуль в локальной файловой системе. -const NOVNC_MODULE_URL = "/novnc/core/rfb.js"; - -let modulePromise = null; - -function loadNoVNC() { - if (modulePromise) return modulePromise; - modulePromise = import(/* @vite-ignore */ NOVNC_MODULE_URL) - .then((mod) => { - if (!mod.default) { - throw new Error("RFB не найден в модуле noVNC"); - } - return mod.default; - }); - return modulePromise; -} - -export default function ConsoleViewer({ instanceId }) { - const containerRef = useRef(null); - const rfbRef = useRef(null); - const [status, setStatus] = useState("Подключение…"); - const [error, setError] = useState(""); - - useEffect(() => { - let cancelled = false; - - async function connect() { - try { - const info = await api.getConsole(instanceId); - const RFB = await loadNoVNC(); - if (cancelled) return; - - const proto = window.location.protocol === "https:" ? "wss" : "ws"; - const wsUrl = - `${proto}://${window.location.host}/api/console/ws?` + - `node=${encodeURIComponent(info.node)}` + - `&vmid=${info.vmid}` + - `&guest_type=${info.guest_type}` + - `&port=${info.port}` + - `&ticket=${encodeURIComponent(info.ticket)}`; - - const rfb = new RFB(containerRef.current, wsUrl); - rfb.addEventListener("connect", () => !cancelled && setStatus("Подключено")); - rfb.addEventListener("disconnect", () => !cancelled && setStatus("Соединение закрыто")); - rfbRef.current = rfb; - } catch (err) { - if (!cancelled) setError(err.message || String(err)); - } - } - - connect(); - return () => { - cancelled = true; - if (rfbRef.current && typeof rfbRef.current.disconnect === "function") { - try { rfbRef.current.disconnect(); } catch { /* noop */ } - } - }; - }, [instanceId]); - - return ( -