From 2e265c08f156fb37c37834f9ec187d6d49be4529 Mon Sep 17 00:00:00 2001 From: Talon Date: Mon, 21 Apr 2025 23:22:32 +0200 Subject: [PATCH] More attempts to fix docker --- .dockerignore | 27 ++ Caddyfile | 3 + DOCKER-README.md | 58 +-- Dockerfile | 12 +- docker-compose.yml | 16 - docker-production.js | 20 + docker/app.Dockerfile | 47 +++ docker/ws.Dockerfile | 29 ++ integrated-server.js | 209 ++++++++++ package-lock.json | 719 ++++++++++++++++++++++++++++++++-- package.json | 5 +- production.js | 22 +- run-production.js | 44 +++ server.js | 22 ++ src/hooks.server.js | 205 +++++++++- src/proxy-websocket-server.js | 233 +++++++++++ unified-server.js | 12 +- 17 files changed, 1567 insertions(+), 116 deletions(-) create mode 100644 .dockerignore create mode 100644 Caddyfile create mode 100644 docker-production.js create mode 100644 docker/app.Dockerfile create mode 100644 docker/ws.Dockerfile create mode 100644 integrated-server.js create mode 100644 run-production.js create mode 100644 server.js create mode 100644 src/proxy-websocket-server.js diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a5a0194 --- /dev/null +++ b/.dockerignore @@ -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 diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 0000000..fa792cd --- /dev/null +++ b/Caddyfile @@ -0,0 +1,3 @@ +mud.iamtalon.me { + reverse_proxy svelte-mud:3000 +} diff --git a/DOCKER-README.md b/DOCKER-README.md index 4763cba..c58a0a3 100644 --- a/DOCKER-README.md +++ b/DOCKER-README.md @@ -2,6 +2,10 @@ 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 - [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 ``` -2. Build and start the containers: +2. Build and start the container: ```bash docker-compose up -d ``` 3. Access the application: - 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 @@ -43,57 +47,31 @@ docker-compose up --build ```bash # Stop containers docker-compose down - -# Stop containers and remove volumes -docker-compose down -v ``` ### Viewing Logs ```bash -# View all logs -docker-compose logs - -# Follow logs +# View logs 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: -- Port 3000: Unified server (Web + WebSocket) +For use with Caddy as a reverse proxy, use this simple configuration: -You can modify these ports in the `docker-compose.yml` file if needed. - -## Customization - -### 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 +``` +mud.example.com { + reverse_proxy svelte-mud:3000 +} ``` -### Building for Production - -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 +Both the web interface and WebSocket connections will be routed correctly through this single reverse proxy rule. ## 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. - -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`. \ No newline at end of file +```bash +docker-compose logs -f +``` diff --git a/Dockerfile b/Dockerfile index f43ca7d..c090a6d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,18 +34,14 @@ RUN npm install --omit=dev # Copy built application from the build stage COPY --from=build /app/build ./build -COPY --from=build /app/src/websocket-server.js ./src/ -COPY --from=build /app/unified-server.js ./ +COPY --from=build /app/src ./src +COPY --from=build /app/run-production.js ./ # Switch to non-root user USER nodejs -# Expose ports for both web and websocket servers -EXPOSE 3000 -EXPOSE 3001 - # Set environment variables ENV NODE_ENV=production -# Start the unified server (web + websocket on the same port) -CMD ["node", "unified-server.js"] \ No newline at end of file +# Start both servers using the production script +CMD ["node", "run-production.js"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 10fcade..93e4bc5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,26 +7,10 @@ services: dockerfile: Dockerfile container_name: svelte-mud 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: - revproxy environment: - 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 networks: diff --git a/docker-production.js b/docker-production.js new file mode 100644 index 0000000..e586998 --- /dev/null +++ b/docker-production.js @@ -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); +} diff --git a/docker/app.Dockerfile b/docker/app.Dockerfile new file mode 100644 index 0000000..c529a7a --- /dev/null +++ b/docker/app.Dockerfile @@ -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"] diff --git a/docker/ws.Dockerfile b/docker/ws.Dockerfile new file mode 100644 index 0000000..9e8a026 --- /dev/null +++ b/docker/ws.Dockerfile @@ -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"] diff --git a/integrated-server.js b/integrated-server.js new file mode 100644 index 0000000..e07269d --- /dev/null +++ b/integrated-server.js @@ -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}`); +}); diff --git a/package-lock.json b/package-lock.json index 4c928d8..b250c99 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,9 @@ "@types/ws": "^8.18.1", "ansi-to-html": "^0.7.2", "events": "^3.3.0", + "express": "^4.18.2", "howler": "^2.2.4", + "http-proxy-middleware": "^2.0.6", "net": "^1.0.2", "split.js": "^1.6.5", "ws": "^8.18.1" @@ -3197,6 +3199,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/http-proxy": { + "version": "1.17.16", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz", + "integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "22.14.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", @@ -3236,6 +3247,19 @@ "@types/node": "*" } }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.14.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", @@ -3362,6 +3386,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, "node_modules/arraybuffer.prototype.slice": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", @@ -3608,6 +3638,60 @@ "dev": true, "license": "MIT" }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3623,7 +3707,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -3865,6 +3948,15 @@ "dev": true, "license": "MIT" }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -3888,7 +3980,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3902,7 +3993,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -4124,6 +4214,27 @@ "dev": true, "license": "MIT" }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -4141,6 +4252,12 @@ "node": ">= 0.6" } }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, "node_modules/core-js-compat": { "version": "3.41.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", @@ -4413,6 +4530,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/des.js": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", @@ -4424,6 +4550,16 @@ "minimalistic-assert": "^1.0.0" } }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/detect-indent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", @@ -4501,7 +4637,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -4519,6 +4654,12 @@ "dev": true, "license": "MIT" }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -4572,6 +4713,15 @@ "dev": true, "license": "MIT" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/entities": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", @@ -4651,7 +4801,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4661,7 +4810,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4671,7 +4819,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -4770,6 +4917,12 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, "node_modules/esm-env": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", @@ -4797,6 +4950,21 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -4817,6 +4985,91 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/express/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4912,7 +5165,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -4921,6 +5173,39 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -4938,6 +5223,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -4971,6 +5276,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -4985,6 +5299,15 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -5027,7 +5350,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5078,7 +5400,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -5110,7 +5431,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -5204,7 +5524,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5276,7 +5595,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5330,7 +5648,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -5357,6 +5674,60 @@ "integrity": "sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w==", "license": "MIT" }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", + "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, "node_modules/https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -5364,6 +5735,18 @@ "dev": true, "license": "MIT" }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/idb": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", @@ -5419,7 +5802,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/internal-slot": { @@ -5437,6 +5819,15 @@ "node": ">= 0.4" } }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-arguments": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", @@ -5613,7 +6004,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5668,7 +6058,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -5718,7 +6107,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -5751,6 +6139,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-reference": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", @@ -6173,7 +6573,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -6198,6 +6597,24 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -6208,11 +6625,19 @@ "node": ">= 8" } }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -6243,6 +6668,39 @@ "dev": true, "license": "MIT" }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -6337,7 +6795,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/mz": { @@ -6371,6 +6828,15 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/net": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/net/-/net-1.0.2.tgz", @@ -6467,7 +6933,6 @@ "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -6524,6 +6989,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -6623,6 +7100,15 @@ "node": ">= 0.10" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -6684,6 +7170,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, "node_modules/pbkdf2": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", @@ -6724,7 +7216,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -6956,6 +7447,19 @@ "dev": true, "license": "MIT" }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -7052,6 +7556,30 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -7232,6 +7760,12 @@ "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -7397,7 +7931,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -7456,6 +7989,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/sander": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz", @@ -7482,6 +8021,54 @@ "node": ">=10" } }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", @@ -7492,6 +8079,21 @@ "randombytes": "^2.1.0" } }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/set-cookie-parser": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", @@ -7555,6 +8157,12 @@ "dev": true, "license": "MIT" }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, "node_modules/sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", @@ -7636,7 +8244,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -7656,7 +8263,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -7673,7 +8279,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -7692,7 +8297,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -7827,6 +8431,15 @@ "integrity": "sha512-mPTnGCiS/RiuTNsVhCm9De9cCAUsrNFFviRbADdKiiV+Kk8HKp/0fWu7Kr8pi3/yBmsqLFHuXGT9UUZ+CNLwFw==", "license": "MIT" }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/stream-browserify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", @@ -8455,7 +9068,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -8464,6 +9076,15 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", @@ -8528,6 +9149,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -8712,6 +9346,15 @@ "node": ">= 10.0.0" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", @@ -8789,6 +9432,24 @@ "dev": true, "license": "MIT" }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vite": { "version": "5.4.18", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.18.tgz", diff --git a/package.json b/package.json index 8056ec4..6dce90d 100644 --- a/package.json +++ b/package.json @@ -5,15 +5,12 @@ "type": "module", "scripts": { "dev": "vite dev", - "ws": "node src/websocket-server.js", "dev:full": "node start-server.js", "build": "vite build", "preview": "vite preview", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "start": "node build/index.js", - "start:full": "node production.js", - "start:unified": "node unified-server.js", + "start": "node run-production.js", "generate-icons": "node generate-icons.js" }, "dependencies": { diff --git a/production.js b/production.js index df39425..dcdaa41 100644 --- a/production.js +++ b/production.js @@ -1,20 +1,22 @@ 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 WebSocket server -console.log('Starting WebSocket server'); -const wsServer = spawn('node', ['src/websocket-server.js'], { +// 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 + cwd: __dirname, + env: { ...process.env, PORT: svelteKitPort } }); -// Start the SvelteKit production server -console.log('Starting SvelteKit production server'); -const sveltekit = spawn('node', ['build/index.js'], { +// 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 @@ -23,14 +25,14 @@ const sveltekit = spawn('node', ['build/index.js'], { // Handle process exit process.on('exit', () => { console.log('Shutting down servers...'); - wsServer.kill(); + proxyServer.kill(); sveltekit.kill(); }); // Handle ctrl+c process.on('SIGINT', () => { console.log('Received SIGINT, shutting down servers...'); - wsServer.kill('SIGINT'); + proxyServer.kill('SIGINT'); sveltekit.kill('SIGINT'); process.exit(0); }); @@ -38,7 +40,7 @@ process.on('SIGINT', () => { // Handle termination process.on('SIGTERM', () => { console.log('Received SIGTERM, shutting down servers...'); - wsServer.kill('SIGTERM'); + proxyServer.kill('SIGTERM'); sveltekit.kill('SIGTERM'); process.exit(0); }); \ No newline at end of file diff --git a/run-production.js b/run-production.js new file mode 100644 index 0000000..df39425 --- /dev/null +++ b/run-production.js @@ -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); +}); \ No newline at end of file diff --git a/server.js b/server.js new file mode 100644 index 0000000..c5ad2e4 --- /dev/null +++ b/server.js @@ -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}`); +}); diff --git a/src/hooks.server.js b/src/hooks.server.js index 0c0904d..8d36ac5 100644 --- a/src/hooks.server.js +++ b/src/hooks.server.js @@ -1,6 +1,205 @@ -// This is a placeholder hooks.server.js file -// We're using a standalone WebSocket server instead of integrating with SvelteKit hooks +import { WebSocketServer } from 'ws'; +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 }) { return await resolve(event); -} \ No newline at end of file +} diff --git a/src/proxy-websocket-server.js b/src/proxy-websocket-server.js new file mode 100644 index 0000000..3cfa7a2 --- /dev/null +++ b/src/proxy-websocket-server.js @@ -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 }; diff --git a/unified-server.js b/unified-server.js index eda1db1..bd738ba 100644 --- a/unified-server.js +++ b/unified-server.js @@ -1,16 +1,16 @@ -import { handler } from './build/handler.js'; +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 http from 'http'; +import { handler } from './build/handler.js'; -// Create an express app +// Create Express app const app = express(); // Create HTTP server from Express -const server = http.createServer(app); +const server = createServer(app); // Create WebSocket server const wss = new WebSocketServer({ noServer: true }); @@ -18,8 +18,8 @@ const wss = new WebSocketServer({ noServer: true }); // Active connections and their proxies const connections = new Map(); -// Set up SvelteKit handler for all routes -// This must be the last middleware before error handlers +// Apply SvelteKit handler as middleware +// This needs to be before any other routes app.use(handler); // Handle WebSocket connections