@stonyx/sockets 0.1.1-alpha.1 → 0.1.1-alpha.11

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.
package/src/server.js DELETED
@@ -1,175 +0,0 @@
1
- import { WebSocketServer } from 'ws';
2
- import config from 'stonyx/config';
3
- import log from 'stonyx/log';
4
- import { forEachFileImport } from '@stonyx/utils/file';
5
- import { encrypt, decrypt, deriveKey, generateSessionKey } from './encryption.js';
6
-
7
- let clientId = 0;
8
-
9
- export default class SocketServer {
10
- clientMap = new Map();
11
- handlers = {};
12
-
13
- constructor() {
14
- if (SocketServer.instance) return SocketServer.instance;
15
- SocketServer.instance = this;
16
- }
17
-
18
- async init() {
19
- await this.discoverHandlers();
20
- this.validateAuthHandler();
21
-
22
- const { port, encryption, authKey } = config.sockets;
23
- this.encryptionEnabled = encryption === 'true' || encryption === true;
24
-
25
- if (this.encryptionEnabled) {
26
- this.globalKey = deriveKey(authKey);
27
- }
28
-
29
- const wss = new WebSocketServer({ port });
30
- this.wss = wss;
31
-
32
- log.socket(`WebSocket server is listening on port ${port}`);
33
-
34
- wss.on('connection', (client, request) => {
35
- const { remoteAddress } = request.socket;
36
- log.socket(`[${remoteAddress}] Client connected`);
37
- client.id = ++clientId;
38
- client.ip = remoteAddress;
39
- client.__authenticated = false;
40
- this.prepareSend(client);
41
-
42
- client.on('message', payload => this.onMessage(payload, client));
43
- client.on('close', () => this.handleDisconnect(client));
44
- });
45
- }
46
-
47
- async discoverHandlers() {
48
- const { handlerDir } = config.sockets;
49
-
50
- await forEachFileImport(handlerDir, (HandlerClass, { name }) => {
51
- const instance = new HandlerClass();
52
-
53
- if (typeof instance.server === 'function') {
54
- instance._serverRef = this;
55
- this.handlers[name] = instance;
56
- }
57
- }, { ignoreAccessFailure: true });
58
- }
59
-
60
- validateAuthHandler() {
61
- if (!this.handlers.auth) {
62
- throw new Error('SocketServer requires an "auth" handler with a server() method');
63
- }
64
- }
65
-
66
- onMessage(payload, client) {
67
- try {
68
- let parsed;
69
-
70
- if (this.encryptionEnabled) {
71
- const key = client.__authenticated ? client.__sessionKey : this.globalKey;
72
- const raw = Buffer.isBuffer(payload) ? payload : Buffer.from(payload);
73
- parsed = JSON.parse(decrypt(raw, key));
74
- } else {
75
- parsed = JSON.parse(payload);
76
- }
77
-
78
- const { request, data } = parsed;
79
-
80
- // Built-in heartbeat — no handler needed
81
- if (request === 'heartBeat') {
82
- if (client.__authenticated) client.send({ request: 'heartBeat', response: true });
83
- return;
84
- }
85
-
86
- const handler = this.handlers[request];
87
-
88
- if (!handler) {
89
- log.socket(`Invalid request received: ${request}`);
90
- return;
91
- }
92
-
93
- if (request !== 'auth' && !handler.constructor.skipAuth && !client.__authenticated) {
94
- log.socket(`Rejected unauthenticated request: ${request}`);
95
- client.close();
96
- return;
97
- }
98
-
99
- const response = handler.server(data, client);
100
- if (response === undefined || response === null) return;
101
-
102
- if (request === 'auth' && response) {
103
- client.__authenticated = true;
104
-
105
- if (this.encryptionEnabled) {
106
- const sessionKey = generateSessionKey();
107
- client.__sessionKey = sessionKey;
108
- client.send({ request, response, sessionKey: sessionKey.toString('base64') }, this.globalKey);
109
- } else {
110
- client.send({ request, response });
111
- }
112
- return;
113
- }
114
-
115
- client.send({ request, response });
116
- } catch (error) {
117
- log.socket(`Invalid payload from client`);
118
- if (config.debug) console.error(error);
119
- client.close();
120
- }
121
- }
122
-
123
- prepareSend(client) {
124
- const { send: socketSend } = client;
125
- const server = this;
126
-
127
- client.send = (payload, keyOverride) => {
128
- if (server.encryptionEnabled) {
129
- const key = keyOverride || client.__sessionKey;
130
- const data = encrypt(JSON.stringify(payload), key);
131
- socketSend.bind(client)(data);
132
- } else {
133
- socketSend.bind(client)(JSON.stringify(payload));
134
- }
135
- };
136
- }
137
-
138
- handleDisconnect(client) {
139
- const { ip } = client;
140
- log.socket(`[${ip}] Client disconnected`);
141
- this.clientMap.delete(client.id);
142
- }
143
-
144
- sendTo(clientId, request, data) {
145
- const client = this.clientMap.get(clientId);
146
- if (!client) return;
147
- client.send({ request, data });
148
- }
149
-
150
- broadcast(request, data) {
151
- for (const [, client] of this.clientMap) {
152
- if (client.__authenticated) {
153
- client.send({ request, data });
154
- }
155
- }
156
- }
157
-
158
- close() {
159
- if (this.wss) {
160
- for (const client of this.wss.clients) {
161
- client.terminate();
162
- }
163
- this.wss.close();
164
- this.wss = null;
165
- }
166
- }
167
-
168
- reset() {
169
- this.close();
170
- this.clientMap.clear();
171
- this.handlers = {};
172
- clientId = 0;
173
- SocketServer.instance = null;
174
- }
175
- }