mirror of
https://github.com/sailor-sh/CK-X.git
synced 2026-05-20 15:32:57 +00:00
238 lines
7.7 KiB
JavaScript
238 lines
7.7 KiB
JavaScript
const express = require('express');
|
|
const cors = require('cors');
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
const { createProxyMiddleware } = require('http-proxy-middleware');
|
|
const http = require('http');
|
|
const socketio = require('socket.io');
|
|
const { Client } = require('ssh2');
|
|
|
|
// Server configuration
|
|
const PORT = process.env.PORT || 3000;
|
|
|
|
// VNC service configuration from environment variables
|
|
const VNC_SERVICE_HOST = process.env.VNC_SERVICE_HOST || 'remote-desktop-service';
|
|
const VNC_SERVICE_PORT = process.env.VNC_SERVICE_PORT || 6901;
|
|
const VNC_PASSWORD = process.env.VNC_PASSWORD || 'bakku-the-wizard'; // Default password
|
|
|
|
// SSH service configuration
|
|
const SSH_HOST = process.env.SSH_HOST || 'remote-terminal'; // Use remote-terminal service
|
|
const SSH_PORT = process.env.SSH_PORT || 22;
|
|
const SSH_USER = process.env.SSH_USER || 'candidate';
|
|
const SSH_PASSWORD = process.env.SSH_PASSWORD || 'password';
|
|
|
|
const app = express();
|
|
const server = http.createServer(app);
|
|
const io = socketio(server);
|
|
|
|
// SSH terminal namespace
|
|
const sshIO = io.of('/ssh');
|
|
|
|
// Handle SSH connections
|
|
sshIO.on('connection', (socket) => {
|
|
console.log('New SSH terminal connection established');
|
|
|
|
let ssh = new Client();
|
|
|
|
// Connect to the SSH server
|
|
ssh.on('ready', () => {
|
|
console.log('SSH connection established');
|
|
|
|
// Create shell session
|
|
ssh.shell((err, stream) => {
|
|
if (err) {
|
|
console.error('SSH shell error:', err);
|
|
socket.emit('data', `Error: ${err.message}\r\n`);
|
|
socket.disconnect();
|
|
return;
|
|
}
|
|
|
|
// Handle incoming data from SSH server
|
|
stream.on('data', (data) => {
|
|
socket.emit('data', data.toString('utf-8'));
|
|
});
|
|
|
|
// Handle errors on stream
|
|
stream.on('close', () => {
|
|
console.log('SSH stream closed');
|
|
ssh.end();
|
|
socket.disconnect();
|
|
});
|
|
|
|
stream.on('error', (err) => {
|
|
console.error('SSH stream error:', err);
|
|
socket.emit('data', `Error: ${err.message}\r\n`);
|
|
});
|
|
|
|
// Handle incoming data from browser
|
|
socket.on('data', (data) => {
|
|
stream.write(data);
|
|
});
|
|
|
|
// Handle resize events
|
|
socket.on('resize', (dimensions) => {
|
|
if (dimensions && dimensions.cols && dimensions.rows) {
|
|
stream.setWindow(dimensions.rows, dimensions.cols, 0, 0);
|
|
}
|
|
});
|
|
|
|
// Handle socket disconnection
|
|
socket.on('disconnect', () => {
|
|
console.log('Client disconnected from SSH terminal');
|
|
stream.close();
|
|
ssh.end();
|
|
});
|
|
});
|
|
});
|
|
|
|
// Handle SSH connection errors
|
|
ssh.on('error', (err) => {
|
|
console.error('SSH connection error:', err);
|
|
socket.emit('data', `SSH connection error: ${err.message}\r\n`);
|
|
socket.disconnect();
|
|
});
|
|
|
|
// Connect to SSH server
|
|
ssh.connect({
|
|
host: SSH_HOST,
|
|
port: SSH_PORT,
|
|
username: SSH_USER,
|
|
password: SSH_PASSWORD,
|
|
readyTimeout: 30000,
|
|
keepaliveInterval: 10000
|
|
});
|
|
});
|
|
|
|
// Create the public directory if it doesn't exist
|
|
const publicDir = path.join(__dirname, 'public');
|
|
if (!fs.existsSync(publicDir)) {
|
|
fs.mkdirSync(publicDir, { recursive: true });
|
|
console.log('Created public directory');
|
|
}
|
|
|
|
// Copy index.html to public directory if it doesn't exist
|
|
const indexHtmlSrc = path.join(__dirname, 'index.html');
|
|
const indexHtmlDest = path.join(publicDir, 'index.html');
|
|
if (fs.existsSync(indexHtmlSrc) && !fs.existsSync(indexHtmlDest)) {
|
|
fs.copyFileSync(indexHtmlSrc, indexHtmlDest);
|
|
console.log('Copied index.html to public directory');
|
|
}
|
|
|
|
// Enable CORS
|
|
app.use(cors());
|
|
|
|
// Serve static files from the public directory
|
|
app.use(express.static(path.join(__dirname, 'public')));
|
|
|
|
// Configure VNC proxy middleware
|
|
const vncProxyConfig = {
|
|
target: `http://${VNC_SERVICE_HOST}:${VNC_SERVICE_PORT}`,
|
|
changeOrigin: true,
|
|
ws: true,
|
|
secure: false,
|
|
pathRewrite: {
|
|
'^/vnc-proxy': ''
|
|
},
|
|
onProxyReq: (proxyReq, req, res) => {
|
|
// Log HTTP requests being proxied
|
|
console.log(`Proxying HTTP request to VNC server: ${req.url}`);
|
|
},
|
|
onProxyReqWs: (proxyReq, req, socket, options, head) => {
|
|
// Log WebSocket connections
|
|
console.log(`WebSocket connection established to VNC server: ${req.url}`);
|
|
},
|
|
onProxyRes: (proxyRes, req, res) => {
|
|
// Log the responses from VNC server
|
|
console.log(`Received response from VNC server for: ${req.url}`);
|
|
},
|
|
onError: (err, req, res) => {
|
|
console.error(`Proxy error: ${err.message}`);
|
|
if (res && res.writeHead) {
|
|
res.writeHead(500, {
|
|
'Content-Type': 'text/plain'
|
|
});
|
|
res.end(`Proxy error: ${err.message}`);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Middleware to enhance VNC URLs with authentication if needed
|
|
app.use('/vnc-proxy', (req, res, next) => {
|
|
// Check if the URL already has a password parameter
|
|
if (!req.query.password) {
|
|
// If no password provided, add default password
|
|
console.log('Adding default VNC password to request');
|
|
const separator = req.url.includes('?') ? '&' : '?';
|
|
req.url = `${req.url}${separator}password=${VNC_PASSWORD}`;
|
|
}
|
|
next();
|
|
}, createProxyMiddleware(vncProxyConfig));
|
|
|
|
// Direct WebSocket proxy to handle the websockify endpoint
|
|
app.use('/websockify', createProxyMiddleware({
|
|
...vncProxyConfig,
|
|
pathRewrite: {
|
|
'^/websockify': '/websockify'
|
|
},
|
|
ws: true,
|
|
onProxyReqWs: (proxyReq, req, socket, options, head) => {
|
|
// Log WebSocket connections to websockify
|
|
console.log(`WebSocket connection to websockify established: ${req.url}`);
|
|
|
|
// Add additional headers if needed
|
|
proxyReq.setHeader('Origin', `http://${VNC_SERVICE_HOST}:${VNC_SERVICE_PORT}`);
|
|
},
|
|
onError: (err, req, res) => {
|
|
console.error(`Websockify proxy error: ${err.message}`);
|
|
if (res && res.writeHead) {
|
|
res.writeHead(500, {
|
|
'Content-Type': 'text/plain'
|
|
});
|
|
res.end(`Websockify proxy error: ${err.message}`);
|
|
}
|
|
}
|
|
}));
|
|
|
|
// API endpoint to get VNC server info
|
|
app.get('/api/vnc-info', (req, res) => {
|
|
res.json({
|
|
host: VNC_SERVICE_HOST,
|
|
port: VNC_SERVICE_PORT,
|
|
wsUrl: `/websockify`,
|
|
defaultPassword: VNC_PASSWORD,
|
|
status: 'connected'
|
|
});
|
|
});
|
|
|
|
// Health check endpoint
|
|
app.get('/health', (req, res) => {
|
|
res.status(200).json({ status: 'ok', message: 'Service is healthy' });
|
|
});
|
|
|
|
// Catch-all route to serve index.html for any other requests
|
|
app.get('*', (req, res) => {
|
|
// Special handling for exam page
|
|
if (req.path === '/exam') {
|
|
res.sendFile(path.join(__dirname, 'public', 'exam.html'));
|
|
}
|
|
// Special handling for results page
|
|
else if (req.path === '/results') {
|
|
res.sendFile(path.join(__dirname, 'public', 'results.html'));
|
|
}
|
|
else {
|
|
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
|
}
|
|
});
|
|
|
|
// Handle errors
|
|
app.use((err, req, res, next) => {
|
|
console.error('Server error:', err);
|
|
res.status(500).sendFile(path.join(__dirname, 'public', '50x.html'));
|
|
});
|
|
|
|
// Start the server
|
|
server.listen(PORT, () => {
|
|
console.log(`Server running on port ${PORT}`);
|
|
console.log(`VNC proxy configured to ${VNC_SERVICE_HOST}:${VNC_SERVICE_PORT}`);
|
|
console.log(`SSH service configured to ${SSH_HOST}:${SSH_PORT}`);
|
|
});
|