redux-cluster 1.9.2 → 2.0.0
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/LICENSE +21 -21
- package/README.md +345 -471
- package/dist/cjs/core/backup.d.ts +10 -0
- package/dist/cjs/core/backup.d.ts.map +1 -0
- package/dist/cjs/core/backup.js +166 -0
- package/dist/cjs/core/redux-cluster.d.ts +47 -0
- package/dist/cjs/core/redux-cluster.d.ts.map +1 -0
- package/dist/cjs/core/redux-cluster.js +367 -0
- package/dist/cjs/index.d.ts +22 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +43 -0
- package/dist/cjs/network/client.d.ts +23 -0
- package/dist/cjs/network/client.d.ts.map +1 -0
- package/dist/cjs/network/client.js +251 -0
- package/dist/cjs/network/server.d.ts +39 -0
- package/dist/cjs/network/server.d.ts.map +1 -0
- package/dist/cjs/network/server.js +439 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/types/index.d.ts +125 -0
- package/dist/cjs/types/index.d.ts.map +1 -0
- package/dist/cjs/types/index.js +20 -0
- package/dist/cjs/utils/crypto.d.ts +22 -0
- package/dist/cjs/utils/crypto.d.ts.map +1 -0
- package/dist/cjs/utils/crypto.js +404 -0
- package/dist/esm/core/backup.d.ts +10 -0
- package/dist/esm/core/backup.d.ts.map +1 -0
- package/dist/esm/core/backup.js +134 -0
- package/dist/esm/core/redux-cluster.d.ts +47 -0
- package/dist/esm/core/redux-cluster.d.ts.map +1 -0
- package/dist/esm/core/redux-cluster.js +376 -0
- package/dist/esm/index.d.ts +22 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +25 -0
- package/dist/esm/network/client.d.ts +23 -0
- package/dist/esm/network/client.d.ts.map +1 -0
- package/dist/esm/network/client.js +221 -0
- package/dist/esm/network/server.d.ts +39 -0
- package/dist/esm/network/server.d.ts.map +1 -0
- package/dist/esm/network/server.js +408 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/types/index.d.ts +125 -0
- package/dist/esm/types/index.d.ts.map +1 -0
- package/dist/esm/types/index.js +17 -0
- package/dist/esm/utils/crypto.d.ts +22 -0
- package/dist/esm/utils/crypto.d.ts.map +1 -0
- package/dist/esm/utils/crypto.js +351 -0
- package/package.json +115 -42
- package/index.js +0 -678
- package/test.auto.js +0 -94
- package/test.auto.proc1.js +0 -97
- package/test.auto.proc2.js +0 -85
- package/test.visual.client.highload.js +0 -102
- package/test.visual.client.js +0 -103
- package/test.visual.error.js +0 -45
- package/test.visual.js +0 -97
- package/test.visual.server.highload.js +0 -102
- 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;IAmB3B,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,gBAAgB;IAyBxB,OAAO,CAAC,WAAW;IAyBnB,OAAO,CAAC,mBAAmB;IA2D3B,OAAO,CAAC,mBAAmB;IA0C3B,OAAO,CAAC,oBAAoB;IAyD5B,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,qBAAqB;IActB,SAAS,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI;IAezC,YAAY,IAAI,IAAI;IAI3B,OAAO,CAAC,OAAO;IAQR,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAiD9B"}
|