VNC setup
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import express from 'express';
|
||||
import net from 'node:net';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { registerSocket } from './websocket.mjs';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const NOVNC = path.join(__dirname, '../node_modules/@novnc/novnc');
|
||||
const VNC_HOST = process.env.VNC_HOST || '127.0.0.1';
|
||||
const VNC_PORT = process.env.VNC_PORT || 5900;
|
||||
|
||||
export const vncRouter = express.Router();
|
||||
vncRouter.use('/novnc', express.static(NOVNC));
|
||||
vncRouter.get('/', (req, res) => res.sendFile(path.join(__dirname, '../public/vnc.html')));
|
||||
|
||||
// Namespaced so other websocket features can register alongside this one
|
||||
const wss = registerSocket('/ws/vnc', {
|
||||
handleProtocols: protocols => (protocols.has('binary') ? 'binary' : false)
|
||||
});
|
||||
|
||||
wss.on('connection', ws => {
|
||||
console.log('VNC client connected');
|
||||
const socket = net.createConnection(VNC_PORT, VNC_HOST);
|
||||
|
||||
socket.on('data', data => {
|
||||
if (ws.readyState === ws.OPEN) ws.send(data, { binary: true });
|
||||
});
|
||||
|
||||
ws.on('message', data => {
|
||||
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
||||
if (socket.writable) socket.write(buffer);
|
||||
});
|
||||
|
||||
ws.on('error', error => {
|
||||
console.error('VNC websocket error:', error.message);
|
||||
socket.destroy();
|
||||
});
|
||||
|
||||
ws.on('close', (code, reason) => {
|
||||
console.log(`VNC browser disconnected: ${code}${reason?.length ? ` ${reason}` : ''}`);
|
||||
socket.destroy();
|
||||
});
|
||||
|
||||
socket.on('error', error => {
|
||||
console.error('VNC socket error:', error.message);
|
||||
if (ws.readyState === ws.OPEN) ws.close();
|
||||
});
|
||||
|
||||
socket.on('close', () => {
|
||||
console.log('VNC socket closed');
|
||||
if (ws.readyState === ws.OPEN) ws.close();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
/** Registry of path -> WebSocketServer, letting multiple namespaced sockets share one HTTP server. */
|
||||
const sockets = new Map();
|
||||
|
||||
/**
|
||||
* Register a WebSocketServer under an exact path (e.g. '/ws/vnc').
|
||||
* @param {string} path
|
||||
* @param {object} [options] - extra ws.Server options (handleProtocols, etc.)
|
||||
* @returns {WebSocketServer}
|
||||
*/
|
||||
export function registerSocket(path, options = {}) {
|
||||
if (sockets.has(path)) throw new Error(`Socket already registered on ${path}`);
|
||||
const wss = new WebSocketServer({ noServer: true, ...options });
|
||||
sockets.set(path, wss);
|
||||
return wss;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach one 'upgrade' listener to the HTTP server that routes to the
|
||||
* registered WebSocketServer matching the request path.
|
||||
* @param {import('http').Server} server
|
||||
*/
|
||||
export function attachUpgrade(server) {
|
||||
server.on('upgrade', (req, socket, head) => {
|
||||
const { pathname } = new URL(req.url, `http://${req.headers.host}`);
|
||||
const wss = sockets.get(pathname);
|
||||
if (!wss) return socket.destroy();
|
||||
wss.handleUpgrade(req, socket, head, ws => wss.emit('connection', ws, req));
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user