redux-cluster 1.10.0 → 2.0.1

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.
Files changed (57) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +345 -471
  3. package/dist/cjs/core/backup.d.ts +10 -0
  4. package/dist/cjs/core/backup.d.ts.map +1 -0
  5. package/dist/cjs/core/backup.js +166 -0
  6. package/dist/cjs/core/redux-cluster.d.ts +47 -0
  7. package/dist/cjs/core/redux-cluster.d.ts.map +1 -0
  8. package/dist/cjs/core/redux-cluster.js +370 -0
  9. package/dist/cjs/index.d.ts +22 -0
  10. package/dist/cjs/index.d.ts.map +1 -0
  11. package/dist/cjs/index.js +43 -0
  12. package/dist/cjs/network/client.d.ts +23 -0
  13. package/dist/cjs/network/client.d.ts.map +1 -0
  14. package/dist/cjs/network/client.js +251 -0
  15. package/dist/cjs/network/server.d.ts +39 -0
  16. package/dist/cjs/network/server.d.ts.map +1 -0
  17. package/dist/cjs/network/server.js +440 -0
  18. package/dist/cjs/package.json +1 -0
  19. package/dist/cjs/types/index.d.ts +125 -0
  20. package/dist/cjs/types/index.d.ts.map +1 -0
  21. package/dist/cjs/types/index.js +20 -0
  22. package/dist/cjs/utils/crypto.d.ts +22 -0
  23. package/dist/cjs/utils/crypto.d.ts.map +1 -0
  24. package/dist/cjs/utils/crypto.js +404 -0
  25. package/dist/esm/core/backup.d.ts +10 -0
  26. package/dist/esm/core/backup.d.ts.map +1 -0
  27. package/dist/esm/core/backup.js +129 -0
  28. package/dist/esm/core/redux-cluster.d.ts +47 -0
  29. package/dist/esm/core/redux-cluster.d.ts.map +1 -0
  30. package/dist/esm/core/redux-cluster.js +363 -0
  31. package/dist/esm/index.d.ts +22 -0
  32. package/dist/esm/index.d.ts.map +1 -0
  33. package/dist/esm/index.js +25 -0
  34. package/dist/esm/network/client.d.ts +23 -0
  35. package/dist/esm/network/client.d.ts.map +1 -0
  36. package/dist/esm/network/client.js +214 -0
  37. package/dist/esm/network/server.d.ts +39 -0
  38. package/dist/esm/network/server.d.ts.map +1 -0
  39. package/dist/esm/network/server.js +403 -0
  40. package/dist/esm/package.json +1 -0
  41. package/dist/esm/types/index.d.ts +125 -0
  42. package/dist/esm/types/index.d.ts.map +1 -0
  43. package/dist/esm/types/index.js +17 -0
  44. package/dist/esm/utils/crypto.d.ts +22 -0
  45. package/dist/esm/utils/crypto.d.ts.map +1 -0
  46. package/dist/esm/utils/crypto.js +351 -0
  47. package/package.json +115 -34
  48. package/index.js +0 -678
  49. package/test.auto.js +0 -94
  50. package/test.auto.proc1.js +0 -97
  51. package/test.auto.proc2.js +0 -85
  52. package/test.visual.client.highload.js +0 -102
  53. package/test.visual.client.js +0 -103
  54. package/test.visual.error.js +0 -45
  55. package/test.visual.js +0 -97
  56. package/test.visual.server.highload.js +0 -102
  57. package/test.visual.server.js +0 -103
@@ -0,0 +1,23 @@
1
+ import { ReduxClusterStore, ClientSettings } from "../types";
2
+ export declare class ClusterClient {
3
+ private store;
4
+ private settings;
5
+ login?: string;
6
+ password?: string;
7
+ private client;
8
+ private listenOptions;
9
+ private shouldReconnect;
10
+ private reconnectTimeout?;
11
+ constructor(store: ReduxClusterStore, settings?: ClientSettings);
12
+ private setupCredentials;
13
+ private setupConnection;
14
+ private getListenOptions;
15
+ private setupEventHandlers;
16
+ private handleConnection;
17
+ private handleDisconnection;
18
+ private setupPipeline;
19
+ private handleServerMessage;
20
+ private connectToServer;
21
+ disconnect(): void;
22
+ }
23
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/network/client.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAe,MAAM,UAAU,CAAC;AAG1E,qBAAa,aAAa;IAUtB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ;IAVX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAEzB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,aAAa,CAAM;IAC3B,OAAO,CAAC,eAAe,CAAiB;IACxC,OAAO,CAAC,gBAAgB,CAAC,CAAiB;gBAGhC,KAAK,EAAE,iBAAiB,EACxB,QAAQ,GAAE,cAAmB;IAOvC,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,gBAAgB;IAuBxB,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,gBAAgB;IA6CxB,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,aAAa;IA2DrB,OAAO,CAAC,mBAAmB;IA4C3B,OAAO,CAAC,eAAe;IAIhB,UAAU,IAAI,IAAI;CAmB1B"}
@@ -0,0 +1,251 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ClusterClient = void 0;
37
+ const net = __importStar(require("net"));
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
40
+ const zlib = __importStar(require("zlib"));
41
+ const stream = __importStar(require("stream"));
42
+ const types_1 = require("../types");
43
+ const crypto_1 = require("../utils/crypto");
44
+ class ClusterClient {
45
+ constructor(store, settings = {}) {
46
+ this.store = store;
47
+ this.settings = settings;
48
+ this.shouldReconnect = true;
49
+ this.setupCredentials();
50
+ this.setupConnection();
51
+ this.connectToServer();
52
+ }
53
+ setupCredentials() {
54
+ if (typeof this.settings.login === "string") {
55
+ this.login = (0, crypto_1.hasher)(`REDUX_CLUSTER${this.settings.login}`);
56
+ }
57
+ if (typeof this.settings.password === "string") {
58
+ this.password = (0, crypto_1.hasher)(`REDUX_CLUSTER${this.settings.password}`);
59
+ }
60
+ }
61
+ setupConnection() {
62
+ this.listenOptions = this.getListenOptions();
63
+ this.client = new net.Socket();
64
+ this.setupEventHandlers();
65
+ this.setupPipeline();
66
+ }
67
+ getListenOptions() {
68
+ const defaultOptions = { port: 10001 };
69
+ if (typeof this.settings.path === "string") {
70
+ switch (os.platform()) {
71
+ case "win32":
72
+ return { path: path.join("\\\\?\\pipe", this.settings.path) };
73
+ default:
74
+ return { path: path.join(this.settings.path) };
75
+ }
76
+ }
77
+ const options = { ...defaultOptions };
78
+ if (typeof this.settings.host === "string") {
79
+ options.host = this.settings.host;
80
+ }
81
+ if (typeof this.settings.port === "number") {
82
+ options.port = this.settings.port;
83
+ }
84
+ return options;
85
+ }
86
+ setupEventHandlers() {
87
+ this.client.on("connect", () => {
88
+ this.handleConnection();
89
+ });
90
+ this.client.on("close", () => {
91
+ this.handleDisconnection();
92
+ });
93
+ this.client.on("error", (err) => {
94
+ this.store.stderr(`ReduxCluster.createClient client error: ${err.message}`);
95
+ });
96
+ }
97
+ handleConnection() {
98
+ this.store.connected = true;
99
+ this.store.sendtoall({
100
+ _msg: types_1.MessageType.CONN_STATUS,
101
+ _hash: this.store.RCHash,
102
+ _connected: true,
103
+ });
104
+ // Override write method for object mode + compression
105
+ this.client.writeNEW = this.client.write;
106
+ this.client.write = (data) => {
107
+ try {
108
+ const compressed = zlib.gzipSync(Buffer.from(JSON.stringify(data)));
109
+ return this.client.writeNEW(compressed);
110
+ }
111
+ catch (err) {
112
+ this.store.stderr(`ReduxCluster.createClient write error: ${err.message}`);
113
+ return false;
114
+ }
115
+ };
116
+ // Save original dispatch for local state updates
117
+ if (typeof this.store.dispatchNEW !== "function") {
118
+ this.store.dispatchNEW = this.store.dispatch;
119
+ }
120
+ // Override dispatch to send actions to server
121
+ this.store.dispatch = (action) => {
122
+ this.client.write({
123
+ _msg: types_1.MessageType.MSG_TO_MASTER,
124
+ _hash: this.store.RCHash,
125
+ _action: action,
126
+ });
127
+ };
128
+ // Authenticate with server
129
+ this.client.write({
130
+ _msg: types_1.MessageType.SOCKET_AUTH,
131
+ _hash: this.store.RCHash,
132
+ _login: this.login,
133
+ _password: this.password,
134
+ });
135
+ }
136
+ handleDisconnection() {
137
+ this.store.connected = false;
138
+ this.store.sendtoall({
139
+ _msg: types_1.MessageType.CONN_STATUS,
140
+ _hash: this.store.RCHash,
141
+ _connected: false,
142
+ });
143
+ // Reconnect after 250ms only if reconnection is enabled
144
+ if (this.shouldReconnect) {
145
+ this.reconnectTimeout = setTimeout(() => {
146
+ new ClusterClient(this.store, this.settings);
147
+ }, 250);
148
+ }
149
+ }
150
+ setupPipeline() {
151
+ // Create processing pipeline
152
+ const mbstring = new stream.Transform({
153
+ transform(buffer, encoding, callback) {
154
+ this.push(buffer);
155
+ callback();
156
+ },
157
+ });
158
+ mbstring.setEncoding("utf8");
159
+ const gunzipper = zlib.createGunzip();
160
+ // Simple JSON parser
161
+ const parser = new stream.Transform({
162
+ objectMode: true,
163
+ transform(chunk, encoding, callback) {
164
+ try {
165
+ const data = JSON.parse(chunk.toString());
166
+ this.push(data);
167
+ callback();
168
+ }
169
+ catch {
170
+ callback(); // Invalid JSON, ignore but continue
171
+ }
172
+ },
173
+ });
174
+ const eventHandler = new stream.Writable({
175
+ objectMode: true,
176
+ write: (data, encoding, callback) => {
177
+ this.handleServerMessage(data);
178
+ callback();
179
+ },
180
+ });
181
+ // Setup error handlers
182
+ [gunzipper, mbstring, parser, eventHandler].forEach((streamObj) => {
183
+ streamObj.on("error", (err) => {
184
+ this.store.stderr(`ReduxCluster.createClient stream error: ${err.message}`);
185
+ });
186
+ });
187
+ // Connect pipeline
188
+ this.client.pipe(gunzipper).pipe(mbstring).pipe(parser).pipe(eventHandler);
189
+ }
190
+ handleServerMessage(data) {
191
+ if (this.client.destroyed || data._hash !== this.store.RCHash) {
192
+ return;
193
+ }
194
+ switch (data._msg) {
195
+ case types_1.MessageType.MSG_TO_WORKER:
196
+ // Always use dispatchNEW (original dispatch) for local state updates
197
+ // because the regular dispatch is overridden to send to server
198
+ if (this.store.dispatchNEW) {
199
+ // Mark SYNC actions as internal
200
+ let actionToDispatch = data._action;
201
+ if (actionToDispatch.type === "REDUX_CLUSTER_SYNC") {
202
+ actionToDispatch = { ...actionToDispatch, _internal: true };
203
+ }
204
+ this.store.dispatchNEW(actionToDispatch);
205
+ }
206
+ else {
207
+ this.store.stderr("dispatchNEW method not available");
208
+ }
209
+ break;
210
+ case types_1.MessageType.SOCKET_AUTH_STATE:
211
+ if (data._value === true) {
212
+ // Authentication successful, request initial state
213
+ this.client.write({
214
+ _msg: types_1.MessageType.START,
215
+ _hash: this.store.RCHash,
216
+ });
217
+ }
218
+ else {
219
+ // Authentication failed
220
+ const error = data._banned
221
+ ? new Error("your ip is locked for 3 hours")
222
+ : new Error("authorization failed");
223
+ this.client.destroy(error);
224
+ }
225
+ break;
226
+ default:
227
+ // Ignore unknown message types
228
+ break;
229
+ }
230
+ }
231
+ connectToServer() {
232
+ this.client.connect(this.listenOptions);
233
+ }
234
+ disconnect() {
235
+ this.shouldReconnect = false;
236
+ // Clear reconnection timeout if it exists
237
+ if (this.reconnectTimeout) {
238
+ clearTimeout(this.reconnectTimeout);
239
+ this.reconnectTimeout = undefined;
240
+ }
241
+ // Remove all event listeners
242
+ if (this.client) {
243
+ this.client.removeAllListeners();
244
+ // Destroy the socket
245
+ if (!this.client.destroyed) {
246
+ this.client.destroy();
247
+ }
248
+ }
249
+ }
250
+ }
251
+ exports.ClusterClient = ClusterClient;
@@ -0,0 +1,39 @@
1
+ import { ServerSettings, ReduxClusterStore, ClusterMessage, ClusterSocket } from "../types";
2
+ export declare class ClusterServer {
3
+ private store;
4
+ private settings;
5
+ readonly uid: string;
6
+ readonly sockets: Record<string, ClusterSocket>;
7
+ readonly database: Record<string, string>;
8
+ readonly ip2ban: Record<string, {
9
+ time: number;
10
+ count: number;
11
+ }>;
12
+ private readonly ip2banTimeout;
13
+ private readonly ip2banGC;
14
+ private server;
15
+ private unsubscribe?;
16
+ private shouldAutoRestart;
17
+ constructor(store: ReduxClusterStore, settings?: ServerSettings);
18
+ private setupDatabase;
19
+ private setupBanSystem;
20
+ private cleanupBannedIPs;
21
+ private setupServer;
22
+ private getListenOptions;
23
+ private startListening;
24
+ private handleNewConnection;
25
+ private isIPBanned;
26
+ private rejectConnection;
27
+ private setupSocket;
28
+ private setupSocketPipeline;
29
+ private handleSocketMessage;
30
+ private handleAuthentication;
31
+ private recordFailedLogin;
32
+ private closeSocket;
33
+ private setupStoreIntegration;
34
+ sendtoall(message?: ClusterMessage): void;
35
+ ip2banGCStop(): void;
36
+ private cleanup;
37
+ close(): Promise<void>;
38
+ }
39
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/network/server.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,cAAc,EAEd,aAAa,EACd,MAAM,UAAU,CAAC;AAGlB,qBAAa,aAAa;IAatB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ;IAblB,SAAgB,GAAG,EAAE,MAAM,CAAC;IAC5B,SAAgB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAM;IAC5D,SAAgB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IACtD,SAAgB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;IAE7E,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAY;IAC1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,WAAW,CAAC,CAAa;IACjC,OAAO,CAAC,iBAAiB,CAAiB;gBAGhC,KAAK,EAAE,iBAAiB,EACxB,QAAQ,GAAE,cAAmB;IAavC,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,WAAW;IA8CnB,OAAO,CAAC,gBAAgB;IAuBxB,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,mBAAmB;IAiB3B,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,gBAAgB;IAyBxB,OAAO,CAAC,WAAW;IAyBnB,OAAO,CAAC,mBAAmB;IA2D3B,OAAO,CAAC,mBAAmB;IA8C3B,OAAO,CAAC,oBAAoB;IAyD5B,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,qBAAqB;IActB,SAAS,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI;IAgBzC,YAAY,IAAI,IAAI;IAI3B,OAAO,CAAC,OAAO;IAQR,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAiD9B"}