Обновление файла
This commit is contained in:
@@ -1,7 +1,32 @@
|
|||||||
import React, { useEffect, useRef, useState } from "react";
|
"""VNC-консоль через noVNC.
|
||||||
|
|
||||||
|
noVNC — UMD/IIFE-библиотека, она не экспортирует ES-модули, поэтому
|
||||||
|
подключаем её через <script> (лежит в /novnc/rfb.js, копируется из
|
||||||
|
node_modules/@novnc/novnc/lib на этапе сборки Docker-образа).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { api } from "../api.js";
|
import { api } from "../api.js";
|
||||||
|
|
||||||
const NOVNC_CDN = "https://cdn.jsdelivr.net/npm/@novnc/novnc@1.4.0/lib/rfb.js";
|
const NOVNC_SCRIPT = "/novnc/rfb.js";
|
||||||
|
|
||||||
|
let scriptPromise = null;
|
||||||
|
|
||||||
|
function loadNoVNC() {
|
||||||
|
if (typeof window !== "undefined" && window.RFB) {
|
||||||
|
return Promise.resolve(window.RFB);
|
||||||
|
}
|
||||||
|
if (scriptPromise) return scriptPromise;
|
||||||
|
scriptPromise = new Promise((resolve, reject) => {
|
||||||
|
const script = document.createElement("script");
|
||||||
|
script.src = NOVNC_SCRIPT;
|
||||||
|
script.async = true;
|
||||||
|
script.onload = () => (window.RFB ? resolve(window.RFB) : reject(new Error("RFB не найден после загрузки скрипта")));
|
||||||
|
script.onerror = () => reject(new Error(`Не удалось загрузить ${NOVNC_SCRIPT}`));
|
||||||
|
document.head.appendChild(script);
|
||||||
|
});
|
||||||
|
return scriptPromise;
|
||||||
|
}
|
||||||
|
|
||||||
export default function ConsoleViewer({ instanceId }) {
|
export default function ConsoleViewer({ instanceId }) {
|
||||||
const containerRef = useRef(null);
|
const containerRef = useRef(null);
|
||||||
@@ -15,14 +40,17 @@ export default function ConsoleViewer({ instanceId }) {
|
|||||||
async function connect() {
|
async function connect() {
|
||||||
try {
|
try {
|
||||||
const info = await api.getConsole(instanceId);
|
const info = await api.getConsole(instanceId);
|
||||||
const { default: RFB } = await import(/* @vite-ignore */ NOVNC_CDN);
|
const RFB = await loadNoVNC();
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
|
|
||||||
const proto = window.location.protocol === "https:" ? "wss" : "ws";
|
const proto = window.location.protocol === "https:" ? "wss" : "ws";
|
||||||
const wsUrl =
|
const wsUrl =
|
||||||
`${proto}://${window.location.host}/api/console/ws?` +
|
`${proto}://${window.location.host}/api/console/ws?` +
|
||||||
`node=${encodeURIComponent(info.node)}&vmid=${info.vmid}&guest_type=${info.guest_type}` +
|
`node=${encodeURIComponent(info.node)}` +
|
||||||
`&port=${info.port}&ticket=${encodeURIComponent(info.ticket)}`;
|
`&vmid=${info.vmid}` +
|
||||||
|
`&guest_type=${info.guest_type}` +
|
||||||
|
`&port=${info.port}` +
|
||||||
|
`&ticket=${encodeURIComponent(info.ticket)}`;
|
||||||
|
|
||||||
const rfb = new RFB(containerRef.current, wsUrl);
|
const rfb = new RFB(containerRef.current, wsUrl);
|
||||||
rfb.addEventListener("connect", () => !cancelled && setStatus("Подключено"));
|
rfb.addEventListener("connect", () => !cancelled && setStatus("Подключено"));
|
||||||
@@ -36,7 +64,9 @@ export default function ConsoleViewer({ instanceId }) {
|
|||||||
connect();
|
connect();
|
||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
rfbRef.current?.disconnect?.();
|
if (rfbRef.current && typeof rfbRef.current.disconnect === "function") {
|
||||||
|
try { rfbRef.current.disconnect(); } catch { /* noop */ }
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [instanceId]);
|
}, [instanceId]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user