Remove unnecessary files
This commit is contained in:
@@ -1,20 +0,0 @@
|
||||
// Simple production entry point for Docker
|
||||
// Runs the SvelteKit server only, WebSocket server is run separately
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname } from 'path';
|
||||
import { execFileSync } from 'child_process';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Run the SvelteKit production server
|
||||
try {
|
||||
console.log('Starting SvelteKit production server');
|
||||
// Execute the built SvelteKit server directly
|
||||
execFileSync('node', ['build/index.js'], {
|
||||
stdio: 'inherit',
|
||||
cwd: __dirname
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error running SvelteKit server:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
# Multi-stage build Dockerfile for Svelte MUD web application
|
||||
|
||||
# Build stage
|
||||
FROM node:20-alpine AS build
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm install
|
||||
|
||||
# Copy source files
|
||||
COPY . .
|
||||
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
|
||||
# Production stage
|
||||
FROM node:20-alpine AS production
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Create a non-root user and group with the node user ID
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S -u 1001 -G nodejs nodejs
|
||||
|
||||
# Install only production dependencies
|
||||
COPY package*.json ./
|
||||
RUN npm install --omit=dev
|
||||
|
||||
# Copy built application from the build stage
|
||||
COPY --from=build /app/build ./build
|
||||
COPY --from=build /app/docker-production.js ./
|
||||
|
||||
# Switch to non-root user
|
||||
USER nodejs
|
||||
|
||||
# Set environment variables
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
|
||||
# Start the SvelteKit production server
|
||||
CMD ["node", "docker-production.js"]
|
||||
@@ -1,29 +0,0 @@
|
||||
# Dockerfile for WebSocket server only
|
||||
|
||||
FROM node:20-alpine
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install only production dependencies
|
||||
RUN npm install --omit=dev
|
||||
|
||||
# Copy only the websocket server code
|
||||
COPY src/websocket-server.js ./src/
|
||||
|
||||
# Create a non-root user and group with the node user ID
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S -u 1001 -G nodejs nodejs
|
||||
|
||||
# Switch to non-root user
|
||||
USER nodejs
|
||||
|
||||
# Set environment variables
|
||||
ENV NODE_ENV=production
|
||||
ENV WS_PORT=3001
|
||||
|
||||
# Start the WebSocket server
|
||||
CMD ["node", "src/websocket-server.js"]
|
||||
@@ -1,209 +0,0 @@
|
||||
import { createServer } from 'http';
|
||||
import { WebSocketServer } from 'ws';
|
||||
import * as net from 'net';
|
||||
import * as tls from 'tls';
|
||||
import { parse } from 'url';
|
||||
import { handler } from './build/handler.js';
|
||||
|
||||
// Create an HTTP server
|
||||
const server = createServer(handler);
|
||||
|
||||
// Create WebSocket server
|
||||
const wss = new WebSocketServer({ noServer: true });
|
||||
|
||||
// Active connections and their proxies
|
||||
const connections = new Map();
|
||||
|
||||
// Handle WebSocket connections
|
||||
wss.on('connection', (ws, req, mudHost, mudPort, useSSL) => {
|
||||
console.log(`WebSocket connection established for ${mudHost}:${mudPort} (SSL: ${useSSL})`);
|
||||
|
||||
// Create a unique ID for this connection
|
||||
const connectionId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
// Special handling for test connections
|
||||
if (mudHost === 'example.com' && mudPort === '23') {
|
||||
console.log('Test connection detected - using echo server mode');
|
||||
|
||||
// Send welcome message
|
||||
ws.send('Hello from WebSocket test server! This is an echo server.');
|
||||
|
||||
// Echo back messages
|
||||
ws.on('message', (message) => {
|
||||
console.log('Test server received:', message.toString());
|
||||
ws.send(`Echo: ${message.toString()}`);
|
||||
});
|
||||
|
||||
// Handle close
|
||||
ws.on('close', () => {
|
||||
console.log('Test connection closed');
|
||||
connections.delete(connectionId);
|
||||
});
|
||||
|
||||
// Store the connection (without a socket)
|
||||
connections.set(connectionId, { ws, testMode: true });
|
||||
return;
|
||||
}
|
||||
|
||||
let socket;
|
||||
try {
|
||||
// Create a TCP socket connection to the MUD server
|
||||
// Use tls for SSL connections, net for regular connections
|
||||
socket = useSSL
|
||||
? tls.connect({ host: mudHost, port: parseInt(mudPort), rejectUnauthorized: false })
|
||||
: net.createConnection({ host: mudHost, port: parseInt(mudPort) });
|
||||
|
||||
// Add error handler
|
||||
socket.on('error', (error) => {
|
||||
console.error(`Socket error for ${mudHost}:${mudPort}:`, error.message);
|
||||
// Send error to client
|
||||
if (ws.readyState === 1) {
|
||||
ws.send(Buffer.from(`ERROR: Connection to MUD server failed: ${error.message}\r\n`));
|
||||
setTimeout(() => {
|
||||
if (ws.readyState === 1) ws.close();
|
||||
}, 1000);
|
||||
}
|
||||
// Remove from connections map
|
||||
connections.delete(connectionId);
|
||||
});
|
||||
|
||||
// Store the connection
|
||||
connections.set(connectionId, { ws, socket });
|
||||
} catch (error) {
|
||||
console.error(`Error creating socket connection: ${error.message}`);
|
||||
if (ws.readyState === 1) {
|
||||
ws.send(Buffer.from(`ERROR: Failed to connect to MUD server: ${error.message}\r\n`));
|
||||
ws.close();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle data from the MUD server - only in regular mode, not test mode
|
||||
if (socket) {
|
||||
socket.on('data', (data) => {
|
||||
// Check for GMCP data (IAC SB GMCP) - very basic check for debugging
|
||||
// IAC = 255, SB = 250, GMCP = 201
|
||||
let isGmcp = false;
|
||||
for (let i = 0; i < data.length - 2; i++) {
|
||||
if (data[i] === 255 && data[i+1] === 250 && data[i+2] === 201) {
|
||||
isGmcp = true;
|
||||
console.log('WebSocket server: Detected GMCP data in server response');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Forward data to the WebSocket client if it's still open
|
||||
if (ws.readyState === 1) { // WebSocket.OPEN
|
||||
ws.send(data);
|
||||
console.log(`WebSocket server: Sent ${data.length} bytes to client${isGmcp ? ' (contains GMCP data)' : ''}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle socket close
|
||||
if (socket) {
|
||||
socket.on('close', () => {
|
||||
console.log(`MUD connection closed for ${mudHost}:${mudPort}`);
|
||||
// Close WebSocket if it's still open
|
||||
if (ws.readyState === 1) {
|
||||
ws.close();
|
||||
}
|
||||
// Remove from connections map
|
||||
connections.delete(connectionId);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle WebSocket messages (data from client to server)
|
||||
ws.on('message', (message) => {
|
||||
try {
|
||||
// Skip if this is a test connection (already handled in the test mode section)
|
||||
const conn = connections.get(connectionId);
|
||||
if (conn.testMode) return;
|
||||
|
||||
// Check for GMCP data (IAC SB GMCP) in client messages
|
||||
let isGmcp = false;
|
||||
if (message instanceof Buffer || message instanceof Uint8Array) {
|
||||
for (let i = 0; i < message.length - 2; i++) {
|
||||
if (message[i] === 255 && message[i+1] === 250 && message[i+2] === 201) {
|
||||
isGmcp = true;
|
||||
console.log('WebSocket server: Detected GMCP data in client message');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Forward data to the MUD server
|
||||
// The message might be Buffer, ArrayBuffer, or string
|
||||
if (conn.socket && conn.socket.writable) {
|
||||
conn.socket.write(message);
|
||||
console.log(`WebSocket server: Sent ${message.length} bytes to MUD server${isGmcp ? ' (contains GMCP data)' : ''}`);
|
||||
} else {
|
||||
console.error('Socket not writable, cannot send data to MUD server');
|
||||
if (ws.readyState === 1) { // WebSocket.OPEN
|
||||
ws.send(Buffer.from(`ERROR: Cannot send data to MUD server: Socket not connected\r\n`));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error forwarding message to MUD server:', error);
|
||||
if (ws.readyState === 1) { // WebSocket.OPEN
|
||||
ws.send(Buffer.from(`ERROR: Failed to send data to MUD server: ${error.message}\r\n`));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Handle WebSocket close
|
||||
ws.on('close', () => {
|
||||
console.log(`WebSocket closed for ${mudHost}:${mudPort}`);
|
||||
// Close socket if it's still open
|
||||
const conn = connections.get(connectionId);
|
||||
if (conn && conn.socket) {
|
||||
conn.socket.end();
|
||||
}
|
||||
// Remove from connections map
|
||||
connections.delete(connectionId);
|
||||
});
|
||||
|
||||
// Handle WebSocket errors
|
||||
ws.on('error', (error) => {
|
||||
console.error(`WebSocket error for ${mudHost}:${mudPort}:`, error.message);
|
||||
// Close socket on error
|
||||
const conn = connections.get(connectionId);
|
||||
if (conn && conn.socket) {
|
||||
conn.socket.end();
|
||||
}
|
||||
// Remove from connections map
|
||||
connections.delete(connectionId);
|
||||
});
|
||||
});
|
||||
|
||||
// Handle HTTP server upgrade (WebSocket handshake)
|
||||
server.on('upgrade', (request, socket, head) => {
|
||||
// Parse URL to get query parameters
|
||||
const { pathname, query } = parse(request.url, true);
|
||||
|
||||
// Only handle WebSocket connections to /mud-ws
|
||||
if (pathname === '/mud-ws') {
|
||||
// Extract MUD server details from query parameters
|
||||
const { host, port, useSSL } = query;
|
||||
|
||||
if (!host || !port) {
|
||||
socket.write('HTTP/1.1 400 Bad Request\r\n\r\n');
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle WebSocket upgrade
|
||||
wss.handleUpgrade(request, socket, head, (ws) => {
|
||||
wss.emit('connection', ws, request, host, port, useSSL === 'true');
|
||||
});
|
||||
} else {
|
||||
// For other upgrades (not to /mud-ws), close the connection
|
||||
socket.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
// Start the integrated server
|
||||
const PORT = process.env.PORT || 3000;
|
||||
server.listen(PORT, () => {
|
||||
console.log(`Integrated server (HTTP + WebSocket) is running on port ${PORT}`);
|
||||
});
|
||||
@@ -1,46 +0,0 @@
|
||||
import { spawn } from 'child_process';
|
||||
import { dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { svelteKitPort } from './src/proxy-websocket-server.js';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Start the SvelteKit production server on internal port
|
||||
console.log(`Starting SvelteKit production server on internal port ${svelteKitPort}`);
|
||||
const sveltekit = spawn('node', ['build/index.js'], {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
cwd: __dirname,
|
||||
env: { ...process.env, PORT: svelteKitPort }
|
||||
});
|
||||
|
||||
// Start the Proxy WebSocket server on the main port (3000)
|
||||
console.log('Starting Proxy WebSocket server on public port 3000');
|
||||
const proxyServer = spawn('node', ['src/proxy-websocket-server.js'], {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
cwd: __dirname
|
||||
});
|
||||
|
||||
// Handle process exit
|
||||
process.on('exit', () => {
|
||||
console.log('Shutting down servers...');
|
||||
proxyServer.kill();
|
||||
sveltekit.kill();
|
||||
});
|
||||
|
||||
// Handle ctrl+c
|
||||
process.on('SIGINT', () => {
|
||||
console.log('Received SIGINT, shutting down servers...');
|
||||
proxyServer.kill('SIGINT');
|
||||
sveltekit.kill('SIGINT');
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
// Handle termination
|
||||
process.on('SIGTERM', () => {
|
||||
console.log('Received SIGTERM, shutting down servers...');
|
||||
proxyServer.kill('SIGTERM');
|
||||
sveltekit.kill('SIGTERM');
|
||||
process.exit(0);
|
||||
});
|
||||
22
server.js
22
server.js
@@ -1,22 +0,0 @@
|
||||
import { createServer } from 'http';
|
||||
import { handler } from './build/handler.js';
|
||||
import express from 'express';
|
||||
import { handleWebSocket } from './src/hooks.server.js';
|
||||
|
||||
// Create Express app
|
||||
const app = express();
|
||||
|
||||
// Apply SvelteKit handler
|
||||
app.use(handler);
|
||||
|
||||
// Create HTTP server
|
||||
const server = createServer(app);
|
||||
|
||||
// Apply WebSocket handling
|
||||
handleWebSocket(server);
|
||||
|
||||
// Start server
|
||||
const PORT = process.env.PORT || 3000;
|
||||
server.listen(PORT, () => {
|
||||
console.log(`Server running on port ${PORT}`);
|
||||
});
|
||||
@@ -1,233 +0,0 @@
|
||||
import { WebSocketServer } from 'ws';
|
||||
import * as net from 'net';
|
||||
import * as tls from 'tls';
|
||||
import { createProxyMiddleware } from 'http-proxy-middleware';
|
||||
import express from 'express';
|
||||
import { parse } from 'url';
|
||||
import http from 'http';
|
||||
|
||||
// Define SvelteKit server port (internal only)
|
||||
const SVELTEKIT_PORT = 3030;
|
||||
|
||||
// Create Express app for the proxy
|
||||
const app = express();
|
||||
|
||||
// Create HTTP server
|
||||
const server = http.createServer(app);
|
||||
|
||||
// Create WebSocket server
|
||||
const wss = new WebSocketServer({ noServer: true });
|
||||
|
||||
// Active connections and their proxies
|
||||
const connections = new Map();
|
||||
|
||||
// Proxy all regular HTTP requests to the SvelteKit server
|
||||
app.use('/', createProxyMiddleware({
|
||||
target: `http://localhost:${SVELTEKIT_PORT}`,
|
||||
changeOrigin: true,
|
||||
ws: false, // Don't proxy WebSockets here, we'll handle them separately
|
||||
}));
|
||||
|
||||
// Handle WebSocket connections
|
||||
wss.on('connection', (ws, req, mudHost, mudPort, useSSL) => {
|
||||
console.log(`WebSocket connection established for ${mudHost}:${mudPort} (SSL: ${useSSL})`);
|
||||
|
||||
// Create a unique ID for this connection
|
||||
const connectionId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
// Special handling for test connections
|
||||
if (mudHost === 'example.com' && mudPort === '23') {
|
||||
console.log('Test connection detected - using echo server mode');
|
||||
|
||||
// Send welcome message
|
||||
ws.send('Hello from WebSocket test server! This is an echo server.');
|
||||
|
||||
// Echo back messages
|
||||
ws.on('message', (message) => {
|
||||
console.log('Test server received:', message.toString());
|
||||
ws.send(`Echo: ${message.toString()}`);
|
||||
});
|
||||
|
||||
// Handle close
|
||||
ws.on('close', () => {
|
||||
console.log('Test connection closed');
|
||||
connections.delete(connectionId);
|
||||
});
|
||||
|
||||
// Store the connection (without a socket)
|
||||
connections.set(connectionId, { ws, testMode: true });
|
||||
return;
|
||||
}
|
||||
|
||||
let socket;
|
||||
try {
|
||||
// Create a TCP socket connection to the MUD server
|
||||
// Use tls for SSL connections, net for regular connections
|
||||
socket = useSSL
|
||||
? tls.connect({ host: mudHost, port: parseInt(mudPort), rejectUnauthorized: false })
|
||||
: net.createConnection({ host: mudHost, port: parseInt(mudPort) });
|
||||
|
||||
// Add error handler
|
||||
socket.on('error', (error) => {
|
||||
console.error(`Socket error for ${mudHost}:${mudPort}:`, error.message);
|
||||
// Send error to client
|
||||
if (ws.readyState === 1) {
|
||||
ws.send(Buffer.from(`ERROR: Connection to MUD server failed: ${error.message}\r\n`));
|
||||
setTimeout(() => {
|
||||
if (ws.readyState === 1) ws.close();
|
||||
}, 1000);
|
||||
}
|
||||
// Remove from connections map
|
||||
connections.delete(connectionId);
|
||||
});
|
||||
|
||||
// Store the connection
|
||||
connections.set(connectionId, { ws, socket });
|
||||
} catch (error) {
|
||||
console.error(`Error creating socket connection: ${error.message}`);
|
||||
if (ws.readyState === 1) {
|
||||
ws.send(Buffer.from(`ERROR: Failed to connect to MUD server: ${error.message}\r\n`));
|
||||
ws.close();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle data from the MUD server - only in regular mode, not test mode
|
||||
if (socket) {
|
||||
socket.on('data', (data) => {
|
||||
// Check for GMCP data (IAC SB GMCP) - very basic check for debugging
|
||||
// IAC = 255, SB = 250, GMCP = 201
|
||||
let isGmcp = false;
|
||||
for (let i = 0; i < data.length - 2; i++) {
|
||||
if (data[i] === 255 && data[i+1] === 250 && data[i+2] === 201) {
|
||||
isGmcp = true;
|
||||
console.log('WebSocket server: Detected GMCP data in server response');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Forward data to the WebSocket client if it's still open
|
||||
if (ws.readyState === 1) { // WebSocket.OPEN
|
||||
ws.send(data);
|
||||
console.log(`WebSocket server: Sent ${data.length} bytes to client${isGmcp ? ' (contains GMCP data)' : ''}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Socket error handler already defined above
|
||||
|
||||
// Handle socket close
|
||||
if (socket) {
|
||||
socket.on('close', () => {
|
||||
console.log(`MUD connection closed for ${mudHost}:${mudPort}`);
|
||||
// Close WebSocket if it's still open
|
||||
if (ws.readyState === 1) {
|
||||
ws.close();
|
||||
}
|
||||
// Remove from connections map
|
||||
connections.delete(connectionId);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle WebSocket messages (data from client to server)
|
||||
ws.on('message', (message) => {
|
||||
try {
|
||||
// Skip if this is a test connection (already handled in the test mode section)
|
||||
const conn = connections.get(connectionId);
|
||||
if (conn.testMode) return;
|
||||
|
||||
// Check for GMCP data (IAC SB GMCP) in client messages
|
||||
let isGmcp = false;
|
||||
if (message instanceof Buffer || message instanceof Uint8Array) {
|
||||
for (let i = 0; i < message.length - 2; i++) {
|
||||
if (message[i] === 255 && message[i+1] === 250 && message[i+2] === 201) {
|
||||
isGmcp = true;
|
||||
console.log('WebSocket server: Detected GMCP data in client message');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Forward data to the MUD server
|
||||
// The message might be Buffer, ArrayBuffer, or string
|
||||
if (conn.socket && conn.socket.writable) {
|
||||
conn.socket.write(message);
|
||||
console.log(`WebSocket server: Sent ${message.length} bytes to MUD server${isGmcp ? ' (contains GMCP data)' : ''}`);
|
||||
} else {
|
||||
console.error('Socket not writable, cannot send data to MUD server');
|
||||
if (ws.readyState === 1) { // WebSocket.OPEN
|
||||
ws.send(Buffer.from(`ERROR: Cannot send data to MUD server: Socket not connected\r\n`));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error forwarding message to MUD server:', error);
|
||||
if (ws.readyState === 1) { // WebSocket.OPEN
|
||||
ws.send(Buffer.from(`ERROR: Failed to send data to MUD server: ${error.message}\r\n`));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Handle WebSocket close
|
||||
ws.on('close', () => {
|
||||
console.log(`WebSocket closed for ${mudHost}:${mudPort}`);
|
||||
// Close socket if it's still open
|
||||
const conn = connections.get(connectionId);
|
||||
if (conn && conn.socket) {
|
||||
conn.socket.end();
|
||||
}
|
||||
// Remove from connections map
|
||||
connections.delete(connectionId);
|
||||
});
|
||||
|
||||
// Handle WebSocket errors
|
||||
ws.on('error', (error) => {
|
||||
console.error(`WebSocket error for ${mudHost}:${mudPort}:`, error.message);
|
||||
// Close socket on error
|
||||
const conn = connections.get(connectionId);
|
||||
if (conn && conn.socket) {
|
||||
conn.socket.end();
|
||||
}
|
||||
// Remove from connections map
|
||||
connections.delete(connectionId);
|
||||
});
|
||||
});
|
||||
|
||||
// Handle HTTP server upgrade (WebSocket handshake)
|
||||
server.on('upgrade', (request, socket, head) => {
|
||||
// Parse URL to get query parameters
|
||||
const { pathname, query } = parse(request.url, true);
|
||||
|
||||
// Only handle WebSocket connections to /mud-ws
|
||||
if (pathname === '/mud-ws') {
|
||||
// Extract MUD server details from query parameters
|
||||
const { host, port, useSSL } = query;
|
||||
|
||||
if (!host || !port) {
|
||||
socket.write('HTTP/1.1 400 Bad Request\r\n\r\n');
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle WebSocket upgrade
|
||||
wss.handleUpgrade(request, socket, head, (ws) => {
|
||||
wss.emit('connection', ws, request, host, port, useSSL === 'true');
|
||||
});
|
||||
} else {
|
||||
// For other upgrades (not to /mud-ws), close the connection
|
||||
socket.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
// Start the server if this file is run directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
const PORT = process.env.PORT || 3000;
|
||||
server.listen(PORT, () => {
|
||||
console.log(`Proxy WebSocket server is running on port ${PORT}, forwarding to SvelteKit on port ${SVELTEKIT_PORT}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Export SVELTEKIT_PORT for use in other files
|
||||
export const svelteKitPort = SVELTEKIT_PORT;
|
||||
|
||||
// Export the server for use in the main file
|
||||
export default { server, SVELTEKIT_PORT };
|
||||
@@ -1,219 +0,0 @@
|
||||
import { createServer } from 'http';
|
||||
import express from 'express';
|
||||
import { WebSocketServer } from 'ws';
|
||||
import * as net from 'net';
|
||||
import * as tls from 'tls';
|
||||
import { parse } from 'url';
|
||||
import { handler } from './build/handler.js';
|
||||
|
||||
// Create Express app
|
||||
const app = express();
|
||||
|
||||
// Create HTTP server from Express
|
||||
const server = createServer(app);
|
||||
|
||||
// Create WebSocket server
|
||||
const wss = new WebSocketServer({ noServer: true });
|
||||
|
||||
// Active connections and their proxies
|
||||
const connections = new Map();
|
||||
|
||||
// Apply SvelteKit handler as middleware
|
||||
// This needs to be before any other routes
|
||||
app.use(handler);
|
||||
|
||||
// Handle WebSocket connections
|
||||
wss.on('connection', (ws, req, mudHost, mudPort, useSSL) => {
|
||||
console.log(`WebSocket connection established for ${mudHost}:${mudPort} (SSL: ${useSSL})`);
|
||||
|
||||
// Create a unique ID for this connection
|
||||
const connectionId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
// Special handling for test connections
|
||||
if (mudHost === 'example.com' && mudPort === '23') {
|
||||
console.log('Test connection detected - using echo server mode');
|
||||
|
||||
// Send welcome message
|
||||
ws.send('Hello from WebSocket test server! This is an echo server.');
|
||||
|
||||
// Echo back messages
|
||||
ws.on('message', (message) => {
|
||||
console.log('Test server received:', message.toString());
|
||||
ws.send(`Echo: ${message.toString()}`);
|
||||
});
|
||||
|
||||
// Handle close
|
||||
ws.on('close', () => {
|
||||
console.log('Test connection closed');
|
||||
connections.delete(connectionId);
|
||||
});
|
||||
|
||||
// Store the connection (without a socket)
|
||||
connections.set(connectionId, { ws, testMode: true });
|
||||
return;
|
||||
}
|
||||
|
||||
let socket;
|
||||
try {
|
||||
// Create a TCP socket connection to the MUD server
|
||||
// Use tls for SSL connections, net for regular connections
|
||||
socket = useSSL
|
||||
? tls.connect({ host: mudHost, port: parseInt(mudPort), rejectUnauthorized: false })
|
||||
: net.createConnection({ host: mudHost, port: parseInt(mudPort) });
|
||||
|
||||
// Add error handler
|
||||
socket.on('error', (error) => {
|
||||
console.error(`Socket error for ${mudHost}:${mudPort}:`, error.message);
|
||||
// Send error to client
|
||||
if (ws.readyState === 1) {
|
||||
ws.send(Buffer.from(`ERROR: Connection to MUD server failed: ${error.message}\r\n`));
|
||||
setTimeout(() => {
|
||||
if (ws.readyState === 1) ws.close();
|
||||
}, 1000);
|
||||
}
|
||||
// Remove from connections map
|
||||
connections.delete(connectionId);
|
||||
});
|
||||
|
||||
// Store the connection
|
||||
connections.set(connectionId, { ws, socket });
|
||||
} catch (error) {
|
||||
console.error(`Error creating socket connection: ${error.message}`);
|
||||
if (ws.readyState === 1) {
|
||||
ws.send(Buffer.from(`ERROR: Failed to connect to MUD server: ${error.message}\r\n`));
|
||||
ws.close();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle data from the MUD server - only in regular mode, not test mode
|
||||
if (socket) {
|
||||
socket.on('data', (data) => {
|
||||
// Check for GMCP data (IAC SB GMCP) - very basic check for debugging
|
||||
// IAC = 255, SB = 250, GMCP = 201
|
||||
let isGmcp = false;
|
||||
for (let i = 0; i < data.length - 2; i++) {
|
||||
if (data[i] === 255 && data[i+1] === 250 && data[i+2] === 201) {
|
||||
isGmcp = true;
|
||||
console.log('WebSocket server: Detected GMCP data in server response');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Forward data to the WebSocket client if it's still open
|
||||
if (ws.readyState === 1) { // WebSocket.OPEN
|
||||
ws.send(data);
|
||||
console.log(`WebSocket server: Sent ${data.length} bytes to client${isGmcp ? ' (contains GMCP data)' : ''}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle socket close
|
||||
if (socket) {
|
||||
socket.on('close', () => {
|
||||
console.log(`MUD connection closed for ${mudHost}:${mudPort}`);
|
||||
// Close WebSocket if it's still open
|
||||
if (ws.readyState === 1) {
|
||||
ws.close();
|
||||
}
|
||||
// Remove from connections map
|
||||
connections.delete(connectionId);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle WebSocket messages (data from client to server)
|
||||
ws.on('message', (message) => {
|
||||
try {
|
||||
// Skip if this is a test connection (already handled in the test mode section)
|
||||
const conn = connections.get(connectionId);
|
||||
if (conn.testMode) return;
|
||||
|
||||
// Check for GMCP data (IAC SB GMCP) in client messages
|
||||
let isGmcp = false;
|
||||
if (message instanceof Buffer || message instanceof Uint8Array) {
|
||||
for (let i = 0; i < message.length - 2; i++) {
|
||||
if (message[i] === 255 && message[i+1] === 250 && message[i+2] === 201) {
|
||||
isGmcp = true;
|
||||
console.log('WebSocket server: Detected GMCP data in client message');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Forward data to the MUD server
|
||||
// The message might be Buffer, ArrayBuffer, or string
|
||||
if (conn.socket && conn.socket.writable) {
|
||||
conn.socket.write(message);
|
||||
console.log(`WebSocket server: Sent ${message.length} bytes to MUD server${isGmcp ? ' (contains GMCP data)' : ''}`);
|
||||
} else {
|
||||
console.error('Socket not writable, cannot send data to MUD server');
|
||||
if (ws.readyState === 1) { // WebSocket.OPEN
|
||||
ws.send(Buffer.from(`ERROR: Cannot send data to MUD server: Socket not connected\r\n`));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error forwarding message to MUD server:', error);
|
||||
if (ws.readyState === 1) { // WebSocket.OPEN
|
||||
ws.send(Buffer.from(`ERROR: Failed to send data to MUD server: ${error.message}\r\n`));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Handle WebSocket close
|
||||
ws.on('close', () => {
|
||||
console.log(`WebSocket closed for ${mudHost}:${mudPort}`);
|
||||
// Close socket if it's still open
|
||||
const conn = connections.get(connectionId);
|
||||
if (conn && conn.socket) {
|
||||
conn.socket.end();
|
||||
}
|
||||
// Remove from connections map
|
||||
connections.delete(connectionId);
|
||||
});
|
||||
|
||||
// Handle WebSocket errors
|
||||
ws.on('error', (error) => {
|
||||
console.error(`WebSocket error for ${mudHost}:${mudPort}:`, error.message);
|
||||
// Close socket on error
|
||||
const conn = connections.get(connectionId);
|
||||
if (conn && conn.socket) {
|
||||
conn.socket.end();
|
||||
}
|
||||
// Remove from connections map
|
||||
connections.delete(connectionId);
|
||||
});
|
||||
});
|
||||
|
||||
// Handle HTTP server upgrade (WebSocket handshake)
|
||||
server.on('upgrade', (request, socket, head) => {
|
||||
// Parse URL to get query parameters
|
||||
const { pathname, query } = parse(request.url, true);
|
||||
|
||||
// Only handle WebSocket connections to /mud-ws
|
||||
if (pathname === '/mud-ws') {
|
||||
// Extract MUD server details from query parameters
|
||||
const { host, port, useSSL } = query;
|
||||
|
||||
if (!host || !port) {
|
||||
socket.write('HTTP/1.1 400 Bad Request\r\n\r\n');
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle WebSocket upgrade
|
||||
wss.handleUpgrade(request, socket, head, (ws) => {
|
||||
wss.emit('connection', ws, request, host, port, useSSL === 'true');
|
||||
});
|
||||
} else {
|
||||
// For other upgrades (not to /mud-ws), close the connection
|
||||
socket.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
// Start the unified server
|
||||
const PORT = process.env.PORT || 3000;
|
||||
server.listen(PORT, () => {
|
||||
console.log(`Unified server (HTTP + WebSocket) is running on port ${PORT}`);
|
||||
});
|
||||
|
||||
export default server;
|
||||
Reference in New Issue
Block a user