redweb 0.6.2 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,69 +1,69 @@
1
- const express = require('express');
2
- const bodyParser = require('body-parser');
3
- const path = require('path');
4
- const cors = require('cors');
5
-
6
- /**
7
- * @typedef {'json' | 'urlencoded'} RedWebEncoding
8
- */
9
-
10
- /**
11
- * RedWeb options object.
12
- * @typedef {Object} RedWebOptions
13
- * @property {number} [port=80] - The port number to bind the server.
14
- * @property {string} [bind='0.0.0.0'] - The bind address for the server.
15
- * @property {string[]} [publicPaths=['./public']] - An array of paths to serve static files from.
16
- * @property {Array<{serviceName: string, method: string, function: Function}>} [services=[]] - An array of services with their endpoints and handlers.
17
- * @property {Function} [listenCallback] - Callback function to execute once the server starts listening.
18
- * @property {RedWebEncoding} [encoding='json'] - The encoding type for the request bodies ('json' or 'urlencoded').
19
- * @property {Object} [ssl] - SSL configuration for HTTPS server.
20
- * @property {string} [ssl.key] - Path to the SSL key file.
21
- * @property {string} [ssl.cert] - Path to the SSL certificate file.
22
- * @property {import('express').Application} [server] - Whether to automatically start listening.
23
- * @property {import('cors').CorsOptions} [corsOptions] - The CORS Options.
24
- */
25
-
26
- const ENCODINGS = { json: 'json', urlencoded: 'urlencoded' };
27
- const HTTP_OPTIONS = {
28
- port: 80,
29
- bind: '0.0.0.0',
30
- publicPaths: ['./public'],
31
- services: [],
32
- listenCallback: undefined,
33
- encoding: ENCODINGS.json,
34
- ssl: null,
35
- server: undefined,
36
- corsOptions: undefined,
37
- };
38
-
39
- /**
40
- * Base HTTP Server
41
- * @param {RedWebOptions} options - Configuration options for RedWeb.
42
- * @return {Object} Express application instance.
43
- */
44
- function BaseHttpServer(options = {}) {
45
- this.options = { ...HTTP_OPTIONS, ...options };
46
- this.app = express() || this.options.server;
47
- Object.assign(this, this.options);
48
-
49
- // Middleware to parse request bodies based on the specified encoding
50
- if (this.encoding === ENCODINGS.json) {
51
- this.app.use(bodyParser.json());
52
- } else if (this.encoding === ENCODINGS.urlencoded) {
53
- this.app.use(bodyParser.urlencoded({ extended: true }));
54
- }
55
- this.app.use(cors(this.options.corsOptions));
56
- this.publicPaths.forEach(public_path => this.app.use(express.static(path.join(process.cwd(), public_path))));
57
- const catchAll = this.services.find((service) => service.serviceName === '*');
58
- if (catchAll) this.services.splice(this.services.indexOf(catchAll), 1);
59
- this.services.forEach(service => this.app[service.method](service.serviceName, service.function));
60
- if (catchAll) this.app[catchAll.method](catchAll.serviceName, catchAll.function);
61
- return this;
62
- }
63
-
64
- module.exports = {
65
- BaseHttpServer,
66
- ENCODINGS,
67
- HTTP_OPTIONS,
68
- METHODS: {GET: 'get', POST: 'post', PUT: 'put', DELETE: 'delete'}
69
- };
1
+ const express = require('express');
2
+ const bodyParser = require('body-parser');
3
+ const path = require('path');
4
+ const cors = require('cors');
5
+
6
+ /**
7
+ * @typedef {'json' | 'urlencoded'} RedWebEncoding
8
+ */
9
+
10
+ /**
11
+ * RedWeb options object.
12
+ * @typedef {Object} RedWebOptions
13
+ * @property {number} [port=80] - The port number to bind the server.
14
+ * @property {string} [bind='0.0.0.0'] - The bind address for the server.
15
+ * @property {string[]} [publicPaths=['./public']] - An array of paths to serve static files from.
16
+ * @property {Array<{serviceName: string, method: string, function: Function}>} [services=[]] - An array of services with their endpoints and handlers.
17
+ * @property {Function} [listenCallback] - Callback function to execute once the server starts listening.
18
+ * @property {RedWebEncoding} [encoding='json'] - The encoding type for the request bodies ('json' or 'urlencoded').
19
+ * @property {Object} [ssl] - SSL configuration for HTTPS server.
20
+ * @property {string} [ssl.key] - Path to the SSL key file.
21
+ * @property {string} [ssl.cert] - Path to the SSL certificate file.
22
+ * @property {import('express').Application} [server] - Whether to automatically start listening.
23
+ * @property {import('cors').CorsOptions} [corsOptions] - The CORS Options.
24
+ */
25
+
26
+ const ENCODINGS = { json: 'json', urlencoded: 'urlencoded' };
27
+ const HTTP_OPTIONS = {
28
+ port: 80,
29
+ bind: '0.0.0.0',
30
+ publicPaths: ['./public'],
31
+ services: [],
32
+ listenCallback: undefined,
33
+ encoding: ENCODINGS.json,
34
+ ssl: null,
35
+ server: undefined,
36
+ corsOptions: undefined,
37
+ };
38
+
39
+ /**
40
+ * Base HTTP Server
41
+ * @param {RedWebOptions} options - Configuration options for RedWeb.
42
+ * @return {Object} Express application instance.
43
+ */
44
+ function BaseHttpServer(options = {}) {
45
+ this.options = { ...HTTP_OPTIONS, ...options };
46
+ this.app = express() || this.options.server;
47
+ Object.assign(this, this.options);
48
+
49
+ // Middleware to parse request bodies based on the specified encoding
50
+ if (this.encoding === ENCODINGS.json) {
51
+ this.app.use(bodyParser.json());
52
+ } else if (this.encoding === ENCODINGS.urlencoded) {
53
+ this.app.use(bodyParser.urlencoded({ extended: true }));
54
+ }
55
+ this.app.use(cors(this.options.corsOptions));
56
+ this.publicPaths.forEach(public_path => this.app.use(express.static(path.join(process.cwd(), public_path))));
57
+ const catchAll = this.services.find((service) => service.serviceName === '*');
58
+ if (catchAll) this.services.splice(this.services.indexOf(catchAll), 1);
59
+ this.services.forEach(service => this.app[service.method](service.serviceName, service.function));
60
+ if (catchAll) this.app[catchAll.method](catchAll.serviceName, catchAll.function);
61
+ return this;
62
+ }
63
+
64
+ module.exports = {
65
+ BaseHttpServer,
66
+ ENCODINGS,
67
+ HTTP_OPTIONS,
68
+ METHODS: {GET: 'get', POST: 'post', PUT: 'put', DELETE: 'delete'}
69
+ };
@@ -1,14 +1,14 @@
1
- const { BaseHttpServer } = require('./BaseHttpServer');
2
-
3
- /**
4
- * HTTP Server
5
- * @param {RedWebOptions} options - Configuration options for RedWeb.
6
- * @return {Object} Express application instance.
7
- */
8
- function HttpServer(options = {}) {
9
- BaseHttpServer.call(this, options);
10
- this.server = this.app.listen(this.port, this.listenCallback ? this.listenCallback : () => console.log(`RedWeb HttpServer listening on port ${this.port}`));
11
- return this;
12
- }
13
-
14
- module.exports = HttpServer;
1
+ const { BaseHttpServer } = require('./BaseHttpServer');
2
+
3
+ /**
4
+ * HTTP Server
5
+ * @param {RedWebOptions} options - Configuration options for RedWeb.
6
+ * @return {Object} Express application instance.
7
+ */
8
+ function HttpServer(options = {}) {
9
+ BaseHttpServer.call(this, options);
10
+ this.server = this.app.listen(this.port, this.listenCallback ? this.listenCallback : () => console.log(`RedWeb HttpServer listening on port ${this.port}`));
11
+ return this;
12
+ }
13
+
14
+ module.exports = HttpServer;
@@ -1,17 +1,17 @@
1
- const https = require('https');
2
- const { BaseHttpServer } = require('./BaseHttpServer');
3
- const loadSslConfig = require('../sslConfig');
4
-
5
- /**
6
- * HTTPS Server
7
- * @param {RedWebOptions} options - Configuration options for RedWeb.
8
- * @return {Object} Express application instance.
9
- */
10
- function HttpsServer(options = {}) {
11
- BaseHttpServer.call(this, options);
12
- const sslOptions = loadSslConfig(this.ssl);
13
- https.createServer(sslOptions, this.app).listen(this.port, this.listenCallback ? this.listenCallback : () => console.log(`RedWeb HttpsServer listening on port ${this.port}`));
14
- return this;
15
- }
16
-
17
- module.exports = HttpsServer;
1
+ const https = require('https');
2
+ const { BaseHttpServer } = require('./BaseHttpServer');
3
+ const loadSslConfig = require('../sslConfig');
4
+
5
+ /**
6
+ * HTTPS Server
7
+ * @param {RedWebOptions} options - Configuration options for RedWeb.
8
+ * @return {Object} Express application instance.
9
+ */
10
+ function HttpsServer(options = {}) {
11
+ BaseHttpServer.call(this, options);
12
+ const sslOptions = loadSslConfig(this.ssl);
13
+ https.createServer(sslOptions, this.app).listen(this.port, this.listenCallback ? this.listenCallback : () => console.log(`RedWeb HttpsServer listening on port ${this.port}`));
14
+ return this;
15
+ }
16
+
17
+ module.exports = HttpsServer;
package/src/http/index.js CHANGED
@@ -1,2 +1,2 @@
1
- const { METHODS } = require('./BaseHttpServer');
1
+ const { METHODS } = require('./BaseHttpServer');
2
2
  module.exports = { HttpServer: require('./HttpServer'), HttpsServer: require('./HttpsServer'), METHODS}
package/src/sslConfig.js CHANGED
@@ -1,20 +1,20 @@
1
- const fs = require('fs');
2
-
3
- /**
4
- * Load SSL configuration
5
- * @param {Object} sslOptions - The SSL options object containing the paths to key and cert files.
6
- * @param {string} sslOptions.key - Path to the SSL key file.
7
- * @param {string} sslOptions.cert - Path to the SSL certificate file.
8
- * @return {Object} - The loaded SSL options containing key and cert.
9
- */
10
- function loadSslConfig(sslOptions) {
11
- if (!sslOptions || !sslOptions.key || !sslOptions.cert) {
12
- throw new Error('SSL key and certificate paths must be provided');
13
- }
14
- return {
15
- key: fs.readFileSync(sslOptions.key),
16
- cert: fs.readFileSync(sslOptions.cert)
17
- };
18
- }
19
-
20
- module.exports = loadSslConfig;
1
+ const fs = require('fs');
2
+
3
+ /**
4
+ * Load SSL configuration
5
+ * @param {Object} sslOptions - The SSL options object containing the paths to key and cert files.
6
+ * @param {string} sslOptions.key - Path to the SSL key file.
7
+ * @param {string} sslOptions.cert - Path to the SSL certificate file.
8
+ * @return {Object} - The loaded SSL options containing key and cert.
9
+ */
10
+ function loadSslConfig(sslOptions) {
11
+ if (!sslOptions || !sslOptions.key || !sslOptions.cert) {
12
+ throw new Error('SSL key and certificate paths must be provided');
13
+ }
14
+ return {
15
+ key: fs.readFileSync(sslOptions.key),
16
+ cert: fs.readFileSync(sslOptions.cert)
17
+ };
18
+ }
19
+
20
+ module.exports = loadSslConfig;
@@ -1,93 +1,39 @@
1
- /**
2
- * Represents the base class for a WebSocket message handler.
3
- */
4
- class BaseHandler {
5
- /**
6
- * Creates a new handler instance.
7
- * @param {{ name: string, handlers: Record<string, (socket: WebSocket, data: object) => void> }} config
8
- * - `name`: The unique name of the handler.
9
- * - `handlers`: A dictionary of message types and their corresponding handler functions.
10
- */
11
- constructor(config) {
12
- /**
13
- * The name of the handler (used to identify this handler in the server).
14
- * @type {string}
15
- */
16
- this.name = config.name;
17
-
18
- /**
19
- * The dictionary of message handlers for this handler.
20
- * @type {Record<string, (socket: WebSocket, data: object) => void>}
21
- */
22
- this.messageHandlers = config.handlers || {};
23
-
24
- /**
25
- * The list of active WebSocket connections managed by this handler.
26
- * @type {WebSocket[]}
27
- */
28
- this.connections = [];
29
- }
30
-
31
- handleSocketMessages(socket) {
32
- socket.on('message', (message) => this.handleMessage(socket, message));
33
- }
34
-
35
- handleSocketClose(socket) {
36
- socket.on('close', () => {
37
- this.connections = this.connections.filter((conn) => conn !== socket);
38
- console.log(`Connection closed for handler "${this.name}".`);
39
- });
40
- }
41
-
42
- handleNewConnection(socket) {
43
- this.connections.push(socket);
44
- this.handleSocketMessages(socket);
45
- this.handleSocketClose(socket);
46
- socket.isAssigned = true;
47
- console.log(`New connection added to handler "${this.name}".`);
48
- }
49
-
50
- /**
51
- * Adds a new WebSocket connection and sets up message handling for this handler.
52
- * @param {WebSocket} socket - The WebSocket connection to add.
53
- */
54
- newConnection(socket, data) {
55
- console.log(`Received newConnection data ${data}`);
56
- this.handleNewConnection(socket);
57
- }
58
-
59
- /**
60
- * Handles an incoming message and routes it to the appropriate handler function.
61
- * @param {WebSocket} socket - The WebSocket connection that sent the message.
62
- * @param {string} message - The incoming message in JSON string format.
63
- */
64
- handleMessage(socket, message) {
65
- try {
66
- const parsedMessage = JSON.parse(message);
67
- const { type, ...data } = parsedMessage;
68
-
69
- if (this.messageHandlers[type]) {
70
- this.messageHandlers[type](socket, data);
71
- } else {
72
- console.warn(`Unhandled message type: "${type}" in handler "${this.name}".`);
73
- socket.close();
74
- }
75
- } catch (error) {
76
- console.error(`Error processing message in handler "${this.name}":`, error);
77
- }
78
- }
79
-
80
- /**
81
- * Broadcasts a message to all connections managed by this handler.
82
- * @param {object} message - The message to broadcast.
83
- */
84
- broadcast(message) {
85
- this.connections.forEach((socket) => {
86
- if (socket.readyState === WebSocket.OPEN) {
87
- socket.send(JSON.stringify(message));
88
- }
89
- });
90
- }
91
- }
92
-
93
- module.exports = { BaseHandler };
1
+ /**
2
+ * Represents the base class for a WebSocket message handler.
3
+ */
4
+ class BaseHandler {
5
+ /**
6
+ * Creates a new handler instance.
7
+ * @param {string} name - The name of the Handler, used in the client 'type' arg of request e.g {"type": "<handler-name>", ...}
8
+ */
9
+ constructor(name) {
10
+ /**
11
+ * he name of the Handler, used in the client 'type' arg of request e.g {"type": "<handler-name>", ...}.
12
+ * @type {string}
13
+ */
14
+ this.name = name;
15
+ }
16
+ /**
17
+ * Handles an incoming message and routes it to the appropriate handler function.
18
+ * @param {WebSocket & {sendJson: (message: Object) => void, broadcast: (message: Object) => void}} socket - The WebSocket connection that sent the message.
19
+ * @param {string} message - The incoming message in JSON string format.
20
+ */
21
+ handleMessage(socket, message) {
22
+ this.onMessage(socket, message);
23
+ }
24
+
25
+ /**
26
+ * Method to be overriden to process messages.
27
+ * @param {WebSocket} socket - The WebSocket connection that sent the message.
28
+ * @param {string} message - The incoming message in JSON string format.
29
+ */
30
+ onMessage(socket, message) {
31
+ throw "Not yet implemented!";
32
+ }
33
+
34
+ onInitialContact(socket) {
35
+
36
+ }
37
+ }
38
+
39
+ module.exports = { BaseHandler };
@@ -1,203 +1,57 @@
1
- const WebSocket = require('ws');
2
-
3
- /**
4
- * @typedef {Object} SocketServerOptions
5
- * @property {import('http').Server} [server] - The HTTP server instance to bind the WebSocket server to.
6
- * @property {number} [port=3000] - The port number for the WebSocket server.
7
- * @property {(socket: WebSocket) => void} [connectionOpenCallback] - Callback executed when a client connects.
8
- * @property {(socket: WebSocket) => void} [connectionCloseCallback] - Callback executed when a client disconnects.
9
- * @property {(socket: WebSocket, message: object) => void} [messageCallback] - Callback executed when a message is received.
10
- * @property {Record<string, (socket: WebSocket, data: object) => void>} [messageHandlers] - A dictionary of message types and their corresponding handler functions.
11
- * @property {{ key: string, cert: string } | null} [ssl] - SSL configuration for a secure WebSocket server.
12
- * @property {Array<new () => BaseHandler>} [handlerConfig] - An array of handler classes to use for routing.
13
- */
14
-
15
- const SOCKET_OPTIONS = {
16
- port: 3000,
17
- connectionOpenCallback: undefined,
18
- connectionCloseCallback: undefined,
19
- messageCallback: undefined,
20
- messageHandlers: {
21
- ping: (socket, data) => socket.send(JSON.stringify({ type: 'pong' }))
22
- },
23
- ssl: null
24
- };
25
-
26
- /**
27
- * Broadcasts a message to all connected clients.
28
- * @param {BaseSocketServer} socketServer - The WebSocket server instance.
29
- * @param {object} message - The message to broadcast.
30
- */
31
- function broadcast(socketServer, message) {
32
- socketServer.clients.forEach(client => client.send(JSON.stringify(message)));
33
- }
34
-
35
- /**
36
- * Represents the base WebSocket server.
37
- */
38
- class BaseSocketServer {
39
- /**
40
- * @param {import('http').Server} server - The HTTP server instance to bind the WebSocket server to.
41
- * @param {SocketServerOptions} options - The configuration options for the WebSocket server.
42
- */
43
- constructor(server, options = {}) {
44
- this.wss = new WebSocket.Server({ server });
45
- this.clients = new Map(); // Map of clients by their IP addresses.
46
- Object.assign(this, { ...SOCKET_OPTIONS, ...options });
47
-
48
- this.handlers = this.initHandlers(options.handlerConfig || []);
49
- this.wss.on('connection', this.handleConnection.bind(this));
50
- }
51
-
52
- /**
53
- * Initializes the handler classes.
54
- * @param {Array<new () => BaseHandler>} handlerConfig - Array of handler classes.
55
- * @returns {BaseHandler[]} - Array of handler instances.
56
- */
57
- initHandlers(handlerConfig) {
58
- return handlerConfig.map(HandlerClass => new HandlerClass());
59
- }
60
-
61
- /**
62
- * Adds a new handler to the WebSocket server.
63
- * @param {new () => BaseHandler} HandlerClass - The handler class to add.
64
- */
65
- addHandler(HandlerClass) {
66
- const newHandler = new HandlerClass();
67
- if (this.handlers.find(handler => handler.name === newHandler.name)) {
68
- console.warn(`Handler with name '${newHandler.name}' already exists.`);
69
- return;
70
- }
71
- this.handlers.push(newHandler);
72
- console.log(`Handler '${newHandler.name}' added successfully.`);
73
- }
74
-
75
- /**
76
- * Handles a new WebSocket connection.
77
- * @param {WebSocket} socket - The WebSocket connection instance.
78
- * @param {import('http').IncomingMessage} req - The HTTP request object associated with the connection.
79
- */
80
- handleConnection(socket, req) {
81
- const ip = req.socket.remoteAddress;
82
- console.log(`New client connected: ${ip}`);
83
- if (this.clients.get(ip) !== undefined) {
84
- const oldClient = this.clients.get(ip);
85
- console.warn(`Client ${ip} already connected, disconnecting existing connection.`);
86
- oldClient.send(
87
- JSON.stringify({ msg: 'You are being disconnected because a new client is connected with your IP address.' })
88
- );
89
- oldClient.close();
90
- }
91
- this.clients.set(ip, socket);
92
- socket.isAssigned = false; // Tracks whether the socket has been assigned a handler.
93
-
94
- if (this.connectionOpenCallback) this.connectionOpenCallback(socket);
95
-
96
- socket.on('message', message => this.initialMessageHandler(socket, message, ip));
97
- socket.on('close', () => this.handleClose(socket, ip));
98
- socket.on('error', error => this.handleError(socket, error, ip));
99
- }
100
-
101
- /**
102
- * Processes the initial message to determine if a handler assignment is required.
103
- * @param {WebSocket} socket - The WebSocket connection instance.
104
- * @param {string} message - The received message.
105
- * @param {string} ip - The client's IP address.
106
- */
107
- initialMessageHandler(socket, message, ip) {
108
- try {
109
- const parsedMessage = JSON.parse(message);
110
-
111
- // If the first message is '__handlerConnect', attempt to assign a handler
112
- if (parsedMessage.type === '__handlerConnect') {
113
- this.assignToHandler(socket, parsedMessage);
114
- return;
115
- }
116
-
117
- // If no handler is assigned, use the default messageHandlers or fallback
118
- if (!socket.isAssigned) {
119
- this.handleMessage(socket, parsedMessage, ip);
120
- } else {
121
- throw new Error('Unexpected message after handler assignment');
122
- }
123
- } catch (error) {
124
- console.error(`Error handling initial message from ${ip}:`, error);
125
- socket.close(); // Optionally close the socket for invalid behavior.
126
- }
127
- }
128
-
129
- /**
130
- * Assigns the socket to a specific handler.
131
- * @param {WebSocket} socket - The WebSocket connection instance.
132
- * @param {{ handlerName: string, key?: string }} data - Data containing the handler name and optional authentication key.
133
- */
134
- assignToHandler(socket, connectMessage) {
135
- if (!connectMessage.data) {
136
- console.warn(`No data provided for handlerConnect message.`);
137
- socket.close();
138
- return;
139
- }
140
- const { handlerName, data } = connectMessage.data;
141
- const handler = this.handlers.find(h => h.name === handlerName);
142
- if (!handler) {
143
- console.warn(`Handler not found: ${handlerName}`);
144
- socket.send(JSON.stringify({ error: `Handler '${handlerName}' not found` }));
145
- socket.close();
146
- return;
147
- }
148
-
149
- // Optional: Perform authentication with `key`
150
- socket.removeAllListeners('message');
151
- handler.newConnection(socket, connectMessage.data);
152
- }
153
-
154
- /**
155
- * Handles a message using the default messageHandlers.
156
- * @param {WebSocket} socket - The WebSocket connection instance.
157
- * @param {{ type: string, [key: string]: any }} parsedMessage - The parsed message object.
158
- * @param {string} ip - The client's IP address.
159
- */
160
- handleMessage(socket, parsedMessage, ip) {
161
- const { type, ...data } = parsedMessage;
162
-
163
- if (this.messageCallback) this.messageCallback(socket, parsedMessage);
164
-
165
- if (this.messageHandlers[type]) {
166
- this.messageHandlers[type](socket, data);
167
- } else {
168
- console.warn(`Unhandled message type from ${ip}: ${type}`);
169
- socket.close();
170
- }
171
- }
172
-
173
- /**
174
- * Handles socket disconnection.
175
- * @param {WebSocket} socket - The WebSocket connection instance.
176
- * @param {string} ip - The client's IP address.
177
- */
178
- handleClose(socket, ip) {
179
- console.log(`Client disconnected: ${ip}`);
180
- this.clients.delete(ip);
181
- if (this.connectionCloseCallback) this.connectionCloseCallback(socket);
182
- }
183
-
184
- /**
185
- * Handles socket errors.
186
- * @param {WebSocket} socket - The WebSocket connection instance.
187
- * @param {Error} error - The error object.
188
- * @param {string} ip - The client's IP address.
189
- */
190
- handleError(socket, error, ip) {
191
- console.error(`Socket error from ${ip}:`, error);
192
- }
193
-
194
- /**
195
- * Broadcasts a message to all connected clients.
196
- * @param {object} message - The message to broadcast.
197
- */
198
- broadcast(message) {
199
- broadcast(this, message);
200
- }
201
- }
202
-
203
- module.exports = { BaseSocketServer, SOCKET_OPTIONS };
1
+ /**
2
+ * @typedef {Object} SocketServerOptions
3
+ * @property {import('http').Server} [server] - The HTTP server instance to bind the WebSocket server to.
4
+ * @property {number} [port=3000] - The port number for the WebSocket server.
5
+ * @property {Array<new () => import('./SocketRoute').SocketRoute>} [routes] - An array of handler classes to use for routing.
6
+ */
7
+
8
+ const DefaultRoute = require('./DefaultRoute');
9
+
10
+ const SOCKET_OPTIONS = {
11
+ port: 3000,
12
+ ssl: null,
13
+ routes: []
14
+ };
15
+
16
+ /**
17
+ * Broadcasts a message to all connected clients.
18
+ * @param {BaseSocketServer} socketServer - The WebSocket server instance.
19
+ * @param {object} message - The message to broadcast.
20
+ */
21
+ function broadcast(socketServer, message) {
22
+ socketServer.clients.forEach(client => client.send(JSON.stringify(message)));
23
+ }
24
+
25
+ /**
26
+ * Represents the base WebSocket server.
27
+ */
28
+ class BaseSocketServer {
29
+ /**
30
+ * @param {import('http').Server} server - The HTTP server instance to bind the WebSocket server to.
31
+ * @param {SocketServerOptions} options - The configuration options for the WebSocket server.
32
+ */
33
+ constructor(server, options = {}) {
34
+ this.clients = new Map(); // Map of clients by their IP addresses.
35
+ Object.assign(this, { ...SOCKET_OPTIONS, ...options });
36
+ this.server = server;
37
+ if (!options.routes?.length) options.routes = [ DefaultRoute ];
38
+ this.routes = options.routes.map((route) => new route(server));
39
+ this.server.on('upgrade', this.handleUpgrade.bind(this));
40
+ }
41
+
42
+ handleUpgrade(req, sock, head) {
43
+ const route = this.routes.find(route => route.path == req.url);
44
+ if (!route) sock.destroy();
45
+ else route.server.handleUpgrade(req, sock, head, (s, r) => route.server.emit('connection', s, r));
46
+ }
47
+
48
+ /**
49
+ *
50
+ * @param {new () => import('./SocketRoute')} route
51
+ */
52
+ addRoute(route) {
53
+ this.routes.push(new route(this.server));
54
+ }
55
+ }
56
+
57
+ module.exports = { BaseSocketServer, SOCKET_OPTIONS };
@@ -0,0 +1,14 @@
1
+ const { BaseHandler } = require("./BaseHandler");
2
+ const { sendJson } = require("./util");
3
+
4
+ class DefaultHandler extends BaseHandler {
5
+ constructor() {
6
+ super("DefaultHandler");
7
+ }
8
+
9
+ onMessage(socket, message) {
10
+ socket.send(sendJson(`I got your message of ${JSON.stringify(message)}`));
11
+ }
12
+ }
13
+
14
+ module.exports = DefaultHandler;