Files
Proxmox-VPS-Panel/frontend/src/components/ConsoleViewer.jsx
T
2026-07-24 22:56:02 +03:00

87 lines
3.3 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 (
<div>
<div className="status-row" style={{ marginBottom: 8 }}>
<span className={`dot ${error ? "error" : "running"}`} /> {error ? "Ошибка консоли" : status}
</div>
{error && (
<div className="error-box">
{error}. Консоль VNC зависит от версии Proxmox и настроек аутентификации см. README, раздел
«Консоль VNC».
</div>
)}
<div className="console-frame" ref={containerRef} style={{ width: "100%", height: 480 }} />
</div>
);
}