import http from 'node:http'; import https from 'node:https'; import net from 'node:net'; import fs from 'node:fs'; import {readFile} from 'node:fs/promises'; import path from 'node:path'; import {fileURLToPath} from 'node:url'; import {WebSocketServer} from 'ws'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const NOVNC = path.join(__dirname, 'node_modules/@novnc/novnc'); const PORT = process.env['PORT'] || 3000; const VNC_HOST = '127.0.0.1'; const VNC_PORT = 5900; const MIME_TYPES = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript', '.css': 'text/css', '.json': 'application/json', '.svg': 'image/svg+xml', '.png': 'image/png', '.ico': 'image/x-icon', '.wasm': 'application/wasm' }; const server = https.createServer({ key: fs.readFileSync('./certs/key.pem'), cert: fs.readFileSync('./certs/cert.pem') }, async (req, res) => { let file; if (req.url === '/') file = path.join(__dirname, '../public/index.html'); else if (req.url.startsWith('/novnc/')) file = path.join(NOVNC, req.url.slice(7)); else { res.writeHead(404); res.end('Not found'); return; } try { const data = await readFile(file); const type = MIME_TYPES[path.extname(file)] || 'application/octet-stream'; res.writeHead(200, {'Content-Type': type}); res.end(data); } catch (error) { console.error('HTTP error:', error.message); res.writeHead(404); res.end('Not found'); } }); const wss = new WebSocketServer({ server, path: '/websockify', handleProtocols: protocols => { console.log('WebSocket protocols:', [...protocols]); if (protocols.has('binary')) return 'binary'; return false; } }); wss.on('connection', ws => { console.log('VNC client connected'); const socket = net.createConnection(VNC_PORT, VNC_HOST); socket.on('connect', () => { console.log(`Connected to VNC ${VNC_HOST}:${VNC_PORT}`); }); socket.on('data', data => { console.log(`VNC -> browser: ${data.length} bytes`); if (data.length <= 64) { console.log(` hex: ${data.toString('hex')}`); console.log(` text: ${JSON.stringify(data.toString())}`); } if (ws.readyState === ws.OPEN) { ws.send(data, {binary: true}); } }); ws.on('message', (data, isBinary) => { const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data); console.log(`Browser -> VNC: ${buffer.length} bytes (${isBinary ? 'binary' : 'text'})`); if (buffer.length <= 64) { console.log(` hex: ${buffer.toString('hex')}`); console.log(` text: ${JSON.stringify(buffer.toString())}`); } if (socket.writable) { socket.write(buffer); } }); ws.on('error', error => { console.error('WebSocket error:', error.message); socket.destroy(); }); ws.on('close', (code, reason) => { console.log(`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(); }); }); server.on('error', error => { console.error('Server error:', error); }); server.listen(PORT, '0.0.0.0', () => { console.log(`https://0.0.0.0:${PORT}`); })