// 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 (
{error ? "Ошибка консоли" : status}
{error && (
{error}. Консоль VNC зависит от версии Proxmox и настроек аутентификации — см. README, раздел «Консоль VNC».
)}
); }