auth/runtime config, URLs centralizados, parsing/hardening dos hooks/charts
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { appendAccessToken, getVncWebSocketUrl } from "../../../lib/api/gatewayConfig";
|
||||
|
||||
export type VncConnectionState =
|
||||
| "IDLE"
|
||||
@@ -11,6 +12,7 @@ export type VncConnectionState =
|
||||
|
||||
export type UseVncConsoleOptions = {
|
||||
websocketUrl?: string;
|
||||
accessToken?: string | null;
|
||||
defaultHost?: string;
|
||||
defaultPort?: number;
|
||||
};
|
||||
@@ -21,31 +23,35 @@ export type ConnectVncInput = {
|
||||
password: string;
|
||||
};
|
||||
|
||||
const DEFAULT_WEBSOCKET_URL = "ws://localhost:18450/ws/vnc";
|
||||
const DEFAULT_HOST = "198.19.0.176";
|
||||
const DEFAULT_PORT = 5900;
|
||||
|
||||
export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
const websocketUrl = options.websocketUrl ?? DEFAULT_WEBSOCKET_URL;
|
||||
const websocketUrl = options.websocketUrl ?? getVncWebSocketUrl();
|
||||
const accessToken = options.accessToken ?? null;
|
||||
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
const wsRef = useRef<WebSocket | null>(null);
|
||||
const ctxRef = useRef<CanvasRenderingContext2D | null>(null);
|
||||
const rgbaRef = useRef<Uint8ClampedArray | null>(null);
|
||||
|
||||
const framebufferRef = useRef({
|
||||
width: 0,
|
||||
height: 0,
|
||||
});
|
||||
const framebufferRef = useRef({ width: 0, height: 0 });
|
||||
|
||||
const [state, setState] = useState<VncConnectionState>("IDLE");
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [host, setHost] = useState(options.defaultHost ?? DEFAULT_HOST);
|
||||
const [host, setHost] = useState(options.defaultHost ?? "");
|
||||
const [port, setPort] = useState(options.defaultPort ?? DEFAULT_PORT);
|
||||
const [password, setPassword] = useState("");
|
||||
const [frameSize, setFrameSize] = useState({ width: 0, height: 0 });
|
||||
const [lastFrameAt, setLastFrameAt] = useState<string | null>(null);
|
||||
|
||||
const buildWebSocketUrl = useCallback(() => {
|
||||
if (!accessToken) {
|
||||
return websocketUrl;
|
||||
}
|
||||
|
||||
return appendAccessToken(websocketUrl, accessToken);
|
||||
}, [websocketUrl, accessToken]);
|
||||
|
||||
const clearFrame = useCallback(() => {
|
||||
const canvas = canvasRef.current;
|
||||
const ctx = ctxRef.current;
|
||||
@@ -58,11 +64,7 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
|
||||
ctxRef.current = null;
|
||||
rgbaRef.current = null;
|
||||
|
||||
framebufferRef.current = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
framebufferRef.current = { width: 0, height: 0 };
|
||||
|
||||
setFrameSize({ width: 0, height: 0 });
|
||||
setLastFrameAt(null);
|
||||
@@ -104,14 +106,16 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
const width = view.getInt32(0);
|
||||
const height = view.getInt32(4);
|
||||
|
||||
console.log("[VNC] drawFrame", {
|
||||
byteLength: buffer.byteLength,
|
||||
width,
|
||||
height,
|
||||
});
|
||||
|
||||
if (!width || !height || width <= 0 || height <= 0) return;
|
||||
|
||||
const pixels = new Uint8ClampedArray(buffer, 8);
|
||||
|
||||
framebufferRef.current = {
|
||||
width,
|
||||
height,
|
||||
};
|
||||
framebufferRef.current = { width, height };
|
||||
|
||||
if (!ctxRef.current) {
|
||||
ctxRef.current = canvas.getContext("2d", {
|
||||
@@ -156,6 +160,12 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
const nextPort = input?.port ?? port;
|
||||
const nextPassword = input?.password ?? password;
|
||||
|
||||
if (!accessToken) {
|
||||
setError("Token de autenticação em falta.");
|
||||
setState("ERROR");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nextHost.trim()) {
|
||||
setError("Host VNC em falta.");
|
||||
setState("ERROR");
|
||||
@@ -169,7 +179,7 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
setState("CONNECTING_WS");
|
||||
|
||||
window.setTimeout(() => {
|
||||
const socket = new WebSocket(websocketUrl);
|
||||
const socket = new WebSocket(buildWebSocketUrl());
|
||||
|
||||
wsRef.current = socket;
|
||||
socket.binaryType = "arraybuffer";
|
||||
@@ -236,8 +246,19 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
|
||||
return;
|
||||
}
|
||||
if (event.data instanceof ArrayBuffer) {
|
||||
console.log("[VNC] binary ArrayBuffer", event.data.byteLength);
|
||||
drawFrame(event.data);
|
||||
return;
|
||||
}
|
||||
|
||||
drawFrame(event.data);
|
||||
if (event.data instanceof Blob) {
|
||||
console.log("[VNC] binary Blob", event.data.size);
|
||||
event.data.arrayBuffer().then(drawFrame);
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn("[VNC] unknown binary payload", event.data);
|
||||
};
|
||||
|
||||
socket.onerror = () => {
|
||||
@@ -261,13 +282,14 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
}, 100);
|
||||
},
|
||||
[
|
||||
accessToken,
|
||||
buildWebSocketUrl,
|
||||
clearFrame,
|
||||
closeSocket,
|
||||
drawFrame,
|
||||
host,
|
||||
password,
|
||||
port,
|
||||
websocketUrl,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -288,26 +310,36 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
|
||||
const relativeX = (clientX - rect.left) / rect.width;
|
||||
const relativeY = (clientY - rect.top) / rect.height;
|
||||
const containerAspect = rect.width / rect.height;
|
||||
const frameAspect = framebuffer.width / framebuffer.height;
|
||||
|
||||
const x = Math.max(
|
||||
0,
|
||||
Math.min(framebuffer.width - 1, Math.round(relativeX * framebuffer.width)),
|
||||
);
|
||||
let renderedWidth: number;
|
||||
let renderedHeight: number;
|
||||
let offsetX: number;
|
||||
let offsetY: number;
|
||||
|
||||
const y = Math.max(
|
||||
0,
|
||||
Math.min(framebuffer.height - 1, Math.round(relativeY * framebuffer.height)),
|
||||
);
|
||||
if (containerAspect > frameAspect) {
|
||||
renderedHeight = rect.height;
|
||||
renderedWidth = rect.height * frameAspect;
|
||||
offsetX = rect.left + (rect.width - renderedWidth) / 2;
|
||||
offsetY = rect.top;
|
||||
} else {
|
||||
renderedWidth = rect.width;
|
||||
renderedHeight = rect.width / frameAspect;
|
||||
offsetX = rect.left;
|
||||
offsetY = rect.top + (rect.height - renderedHeight) / 2;
|
||||
}
|
||||
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "click",
|
||||
x,
|
||||
y,
|
||||
}),
|
||||
);
|
||||
const relativeX = (clientX - offsetX) / renderedWidth;
|
||||
const relativeY = (clientY - offsetY) / renderedHeight;
|
||||
|
||||
const clampedX = Math.max(0, Math.min(1, relativeX));
|
||||
const clampedY = Math.max(0, Math.min(1, relativeY));
|
||||
|
||||
const x = Math.round(clampedX * (framebuffer.width - 1));
|
||||
const y = Math.round(clampedY * (framebuffer.height - 1));
|
||||
|
||||
ws.send(JSON.stringify({ type: "click", x, y }));
|
||||
}, []);
|
||||
|
||||
const handleCanvasPointerDown = useCallback(
|
||||
@@ -343,4 +375,4 @@ export function useVncConsole(options: UseVncConsoleOptions = {}) {
|
||||
connected: state === "CONNECTED" || state === "FIRST_FRAME",
|
||||
connecting: state === "CONNECTING_WS" || state === "CONNECTING_VNC",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user