More attempts to fix docker
This commit is contained in:
27
.dockerignore
Normal file
27
.dockerignore
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Node modules
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
.svelte-kit
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Development files
|
||||||
|
.git
|
||||||
|
.github
|
||||||
|
.vscode
|
||||||
|
*.md
|
||||||
|
!README.md
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
|
||||||
|
# Specific configuration files
|
||||||
|
.env*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# Previous server implementations
|
||||||
|
unified-server.js
|
||||||
|
production.js
|
||||||
|
src/proxy-websocket-server.js
|
||||||
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
This guide explains how to use Docker to build and run the Svelte MUD client.
|
This guide explains how to use Docker to build and run the Svelte MUD client.
|
||||||
|
|
||||||
|
## Solution Overview
|
||||||
|
|
||||||
|
This setup runs both the SvelteKit application and the WebSocket server in a single container, avoiding CORS issues. It follows the same approach used in development, where both servers run as separate processes but within the same context.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- [Docker](https://docs.docker.com/get-docker/)
|
- [Docker](https://docs.docker.com/get-docker/)
|
||||||
@@ -14,14 +18,14 @@ This guide explains how to use Docker to build and run the Svelte MUD client.
|
|||||||
cd path/to/svelte-mud
|
cd path/to/svelte-mud
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Build and start the containers:
|
2. Build and start the container:
|
||||||
```bash
|
```bash
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Access the application:
|
3. Access the application:
|
||||||
- Web interface: http://localhost:3000
|
- Web interface: http://localhost:3000
|
||||||
- WebSocket server: ws://localhost:3000/mud-ws (both services now run on the same port)
|
- WebSocket server: ws://localhost:3001/mud-ws
|
||||||
|
|
||||||
## Docker Commands
|
## Docker Commands
|
||||||
|
|
||||||
@@ -43,57 +47,31 @@ docker-compose up --build
|
|||||||
```bash
|
```bash
|
||||||
# Stop containers
|
# Stop containers
|
||||||
docker-compose down
|
docker-compose down
|
||||||
|
|
||||||
# Stop containers and remove volumes
|
|
||||||
docker-compose down -v
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Viewing Logs
|
### Viewing Logs
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# View all logs
|
# View logs
|
||||||
docker-compose logs
|
|
||||||
|
|
||||||
# Follow logs
|
|
||||||
docker-compose logs -f
|
docker-compose logs -f
|
||||||
|
|
||||||
# View logs for specific service
|
|
||||||
docker-compose logs -f svelte-mud
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Caddy Configuration
|
||||||
|
|
||||||
The Docker setup uses a single port for both the web interface and WebSocket server:
|
For use with Caddy as a reverse proxy, use this simple configuration:
|
||||||
- Port 3000: Unified server (Web + WebSocket)
|
|
||||||
|
|
||||||
You can modify these ports in the `docker-compose.yml` file if needed.
|
```
|
||||||
|
mud.example.com {
|
||||||
## Customization
|
reverse_proxy svelte-mud:3000
|
||||||
|
}
|
||||||
### Environment Variables
|
|
||||||
|
|
||||||
You can add environment variables in the `docker-compose.yml` file:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
svelte-mud:
|
|
||||||
environment:
|
|
||||||
- NODE_ENV=production
|
|
||||||
- PORT=3000
|
|
||||||
# Add your custom environment variables here
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Building for Production
|
Both the web interface and WebSocket connections will be routed correctly through this single reverse proxy rule.
|
||||||
|
|
||||||
The default configuration is optimized for production use. It:
|
|
||||||
- Uses a multi-stage build process to minimize image size
|
|
||||||
- Runs as a non-root user for better security
|
|
||||||
- Includes only production dependencies
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
1. **Port conflicts**: If port 3000 is already in use, modify the port mapping in `docker-compose.yml`.
|
If you encounter any issues, check the container logs:
|
||||||
|
|
||||||
2. **Build failures**: Ensure that all dependencies are properly defined in your package.json.
|
```bash
|
||||||
|
docker-compose logs -f
|
||||||
3. **Connection issues**: If you can't connect to the WebSocket server, verify that your client is using the correct URL format: `ws://localhost:3000/mud-ws?host=YOUR_MUD_HOST&port=YOUR_MUD_PORT`.
|
```
|
||||||
|
|||||||
12
Dockerfile
12
Dockerfile
@@ -34,18 +34,14 @@ RUN npm install --omit=dev
|
|||||||
|
|
||||||
# Copy built application from the build stage
|
# Copy built application from the build stage
|
||||||
COPY --from=build /app/build ./build
|
COPY --from=build /app/build ./build
|
||||||
COPY --from=build /app/src/websocket-server.js ./src/
|
COPY --from=build /app/src ./src
|
||||||
COPY --from=build /app/unified-server.js ./
|
COPY --from=build /app/run-production.js ./
|
||||||
|
|
||||||
# Switch to non-root user
|
# Switch to non-root user
|
||||||
USER nodejs
|
USER nodejs
|
||||||
|
|
||||||
# Expose ports for both web and websocket servers
|
|
||||||
EXPOSE 3000
|
|
||||||
EXPOSE 3001
|
|
||||||
|
|
||||||
# Set environment variables
|
# Set environment variables
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
# Start the unified server (web + websocket on the same port)
|
# Start both servers using the production script
|
||||||
CMD ["node", "unified-server.js"]
|
CMD ["node", "run-production.js"]
|
||||||
@@ -7,26 +7,10 @@ services:
|
|||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
container_name: svelte-mud
|
container_name: svelte-mud
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
# We don't need to publish ports to the host if we're using a reverse proxy
|
|
||||||
# ports:
|
|
||||||
# - "3005:3000" # Unified server (web + WebSocket)
|
|
||||||
networks:
|
networks:
|
||||||
- revproxy
|
- revproxy
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
# Optional: You can set a custom port
|
|
||||||
# - PORT=3000
|
|
||||||
# Uncomment for adding custom healthcheck
|
|
||||||
# healthcheck:
|
|
||||||
# test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/"]
|
|
||||||
# interval: 30s
|
|
||||||
# timeout: 10s
|
|
||||||
# retries: 3
|
|
||||||
# start_period: 10s
|
|
||||||
volumes:
|
|
||||||
# Optional: Add persistent volumes if needed
|
|
||||||
# - ./logs:/app/logs
|
|
||||||
- /app/node_modules # Don't mount local node_modules
|
|
||||||
|
|
||||||
# Define networks to connect to external services
|
# Define networks to connect to external services
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
20
docker-production.js
Normal file
20
docker-production.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// 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);
|
||||||
|
}
|
||||||
47
docker/app.Dockerfile
Normal file
47
docker/app.Dockerfile
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# 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"]
|
||||||
29
docker/ws.Dockerfile
Normal file
29
docker/ws.Dockerfile
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# 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"]
|
||||||
209
integrated-server.js
Normal file
209
integrated-server.js
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
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}`);
|
||||||
|
});
|
||||||
719
package-lock.json
generated
719
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,15 +5,12 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
"ws": "node src/websocket-server.js",
|
|
||||||
"dev:full": "node start-server.js",
|
"dev:full": "node start-server.js",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
"start": "node build/index.js",
|
"start": "node run-production.js",
|
||||||
"start:full": "node production.js",
|
|
||||||
"start:unified": "node unified-server.js",
|
|
||||||
"generate-icons": "node generate-icons.js"
|
"generate-icons": "node generate-icons.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
import { spawn } from 'child_process';
|
import { spawn } from 'child_process';
|
||||||
import { dirname } from 'path';
|
import { dirname } from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
|
import { svelteKitPort } from './src/proxy-websocket-server.js';
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
// Start the WebSocket server
|
// Start the SvelteKit production server on internal port
|
||||||
console.log('Starting WebSocket server');
|
console.log(`Starting SvelteKit production server on internal port ${svelteKitPort}`);
|
||||||
const wsServer = spawn('node', ['src/websocket-server.js'], {
|
const sveltekit = spawn('node', ['build/index.js'], {
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
shell: true,
|
shell: true,
|
||||||
cwd: __dirname
|
cwd: __dirname,
|
||||||
|
env: { ...process.env, PORT: svelteKitPort }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Start the SvelteKit production server
|
// Start the Proxy WebSocket server on the main port (3000)
|
||||||
console.log('Starting SvelteKit production server');
|
console.log('Starting Proxy WebSocket server on public port 3000');
|
||||||
const sveltekit = spawn('node', ['build/index.js'], {
|
const proxyServer = spawn('node', ['src/proxy-websocket-server.js'], {
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
shell: true,
|
shell: true,
|
||||||
cwd: __dirname
|
cwd: __dirname
|
||||||
@@ -23,14 +25,14 @@ const sveltekit = spawn('node', ['build/index.js'], {
|
|||||||
// Handle process exit
|
// Handle process exit
|
||||||
process.on('exit', () => {
|
process.on('exit', () => {
|
||||||
console.log('Shutting down servers...');
|
console.log('Shutting down servers...');
|
||||||
wsServer.kill();
|
proxyServer.kill();
|
||||||
sveltekit.kill();
|
sveltekit.kill();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle ctrl+c
|
// Handle ctrl+c
|
||||||
process.on('SIGINT', () => {
|
process.on('SIGINT', () => {
|
||||||
console.log('Received SIGINT, shutting down servers...');
|
console.log('Received SIGINT, shutting down servers...');
|
||||||
wsServer.kill('SIGINT');
|
proxyServer.kill('SIGINT');
|
||||||
sveltekit.kill('SIGINT');
|
sveltekit.kill('SIGINT');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
@@ -38,7 +40,7 @@ process.on('SIGINT', () => {
|
|||||||
// Handle termination
|
// Handle termination
|
||||||
process.on('SIGTERM', () => {
|
process.on('SIGTERM', () => {
|
||||||
console.log('Received SIGTERM, shutting down servers...');
|
console.log('Received SIGTERM, shutting down servers...');
|
||||||
wsServer.kill('SIGTERM');
|
proxyServer.kill('SIGTERM');
|
||||||
sveltekit.kill('SIGTERM');
|
sveltekit.kill('SIGTERM');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
44
run-production.js
Normal file
44
run-production.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { spawn } from 'child_process';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
// Start the WebSocket server
|
||||||
|
console.log('Starting WebSocket server');
|
||||||
|
const wsServer = spawn('node', ['src/websocket-server.js'], {
|
||||||
|
stdio: 'inherit',
|
||||||
|
shell: true,
|
||||||
|
cwd: __dirname
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start the SvelteKit production server
|
||||||
|
console.log('Starting SvelteKit production server');
|
||||||
|
const sveltekit = spawn('node', ['build/index.js'], {
|
||||||
|
stdio: 'inherit',
|
||||||
|
shell: true,
|
||||||
|
cwd: __dirname
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle process exit
|
||||||
|
process.on('exit', () => {
|
||||||
|
console.log('Shutting down servers...');
|
||||||
|
wsServer.kill();
|
||||||
|
sveltekit.kill();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle ctrl+c
|
||||||
|
process.on('SIGINT', () => {
|
||||||
|
console.log('Received SIGINT, shutting down servers...');
|
||||||
|
wsServer.kill('SIGINT');
|
||||||
|
sveltekit.kill('SIGINT');
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle termination
|
||||||
|
process.on('SIGTERM', () => {
|
||||||
|
console.log('Received SIGTERM, shutting down servers...');
|
||||||
|
wsServer.kill('SIGTERM');
|
||||||
|
sveltekit.kill('SIGTERM');
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
22
server.js
Normal file
22
server.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
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,6 +1,205 @@
|
|||||||
// This is a placeholder hooks.server.js file
|
import { WebSocketServer } from 'ws';
|
||||||
// We're using a standalone WebSocket server instead of integrating with SvelteKit hooks
|
import * as net from 'net';
|
||||||
|
import * as tls from 'tls';
|
||||||
|
import { parse } from 'url';
|
||||||
|
|
||||||
|
// Create WebSocket server instance
|
||||||
|
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 WebSocket upgrades
|
||||||
|
export const handleWebSocket = (server) => {
|
||||||
|
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), let SvelteKit handle them
|
||||||
|
// Don't destroy the socket here
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Standard SvelteKit hook
|
||||||
export async function handle({ event, resolve }) {
|
export async function handle({ event, resolve }) {
|
||||||
return await resolve(event);
|
return await resolve(event);
|
||||||
}
|
}
|
||||||
233
src/proxy-websocket-server.js
Normal file
233
src/proxy-websocket-server.js
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
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,16 +1,16 @@
|
|||||||
import { handler } from './build/handler.js';
|
import { createServer } from 'http';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { WebSocketServer } from 'ws';
|
import { WebSocketServer } from 'ws';
|
||||||
import * as net from 'net';
|
import * as net from 'net';
|
||||||
import * as tls from 'tls';
|
import * as tls from 'tls';
|
||||||
import { parse } from 'url';
|
import { parse } from 'url';
|
||||||
import http from 'http';
|
import { handler } from './build/handler.js';
|
||||||
|
|
||||||
// Create an express app
|
// Create Express app
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
// Create HTTP server from Express
|
// Create HTTP server from Express
|
||||||
const server = http.createServer(app);
|
const server = createServer(app);
|
||||||
|
|
||||||
// Create WebSocket server
|
// Create WebSocket server
|
||||||
const wss = new WebSocketServer({ noServer: true });
|
const wss = new WebSocketServer({ noServer: true });
|
||||||
@@ -18,8 +18,8 @@ const wss = new WebSocketServer({ noServer: true });
|
|||||||
// Active connections and their proxies
|
// Active connections and their proxies
|
||||||
const connections = new Map();
|
const connections = new Map();
|
||||||
|
|
||||||
// Set up SvelteKit handler for all routes
|
// Apply SvelteKit handler as middleware
|
||||||
// This must be the last middleware before error handlers
|
// This needs to be before any other routes
|
||||||
app.use(handler);
|
app.use(handler);
|
||||||
|
|
||||||
// Handle WebSocket connections
|
// Handle WebSocket connections
|
||||||
|
|||||||
Reference in New Issue
Block a user