redux-cluster 1.10.0 → 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.
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 +367 -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 +439 -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 +134 -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 +376 -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 +221 -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 +408 -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
package/index.js DELETED
@@ -1,678 +0,0 @@
1
- /**
2
- * Redux-Cluster
3
- * (c) 2018 by Siarhei Dudko.
4
- *
5
- * Cluster (default IPC cluster channel) module for redux synchronizes all redux store in cluster processes (v.1.0.x).
6
- * Cluster (default IPC cluster channel) and Socket (custom IPC or TCP channel) for redux synchronizes all redux store (v.1.1.x).
7
- * Use new Parser and Zlib Stream (v.1.6.x).
8
- * LICENSE MIT
9
- */
10
-
11
- "use strict"
12
-
13
- var Redux = require('redux'),
14
- Cluster = require('cluster'),
15
- Lodash = require('lodash'),
16
- Crypto = require('crypto'),
17
- Net = require('net'),
18
- Path = require('path'),
19
- Os = require('os'),
20
- Objectstream = require('@sergdudko/objectstream'),
21
- Eventstream = require('event-stream'),
22
- Fs = require('fs'),
23
- Stream = require('stream'),
24
- Zlib = require('zlib');
25
-
26
- var ReduxClusterModule = {}; //модуль
27
- Object.assign(ReduxClusterModule, Redux); //копирую свойства Redux
28
- var reducers = {}; //список редьюсеров (хэш собирается по имени редьюсера для совместимости различных ОС, т.к. hasher(<function>.toString()) для разных ос дает разные суммы)
29
-
30
- //эмулирую performance.now()
31
- const hrtimeproc = process.hrtime.bigint();
32
- var performance = {now:function(){
33
- try{
34
- let hrtimeprocnew = process.hrtime.bigint();
35
- let my = [parseInt(((hrtimeprocnew - hrtimeproc) / 1000000n).toString()), parseInt(((hrtimeprocnew - hrtimeproc) % 1000000n).toString())];
36
- return parseFloat(my[0]+"."+my[1]);
37
- } catch(err){
38
- return undefined;
39
- }
40
- }};
41
-
42
- function hasher(data, algorithm = 'sha1'){ //хэширование редьюсера
43
- if(typeof(data) === 'string'){
44
- const hash = Crypto.createHash(algorithm);
45
- hash.update(data);
46
- return(hash.digest('hex'));
47
- } else
48
- return;
49
- }
50
-
51
- function encrypter(data, pass){ //енкриптор
52
- const cipher = Crypto.createCipheriv('aes-256-ctr', hasher(pass, "sha256").toString('hex').slice(0, 32), hasher("gRy56$#hEUjs*4@h", "md5").toString('hex').slice(0, 16));
53
- let encrypted = cipher.update(data, 'utf8', 'hex');
54
- encrypted += cipher.final('hex');
55
- return encrypted;
56
- }
57
-
58
- function decrypter(data, pass){ //декриптор
59
- const cipher = Crypto.createDecipheriv('aes-256-ctr', hasher(pass, "sha256").toString('hex').slice(0, 32), hasher("gRy56$#hEUjs*4@h", "md5").toString('hex').slice(0, 16));
60
- let decrypted = cipher.update(data, 'hex', 'utf8');
61
- decrypted += cipher.final('utf8');
62
- return decrypted;
63
- }
64
-
65
-
66
- /*
67
- Типы сообщений:
68
-
69
- МастерВВоркер:
70
- СтатусСоединения{ //отправка статуса соединения с сервером в воркеры
71
- _msg:"REDUX_CLUSTER_CONNSTATUS", //тип сообщения (обязательно)
72
- _hash:_store.RCHash, //хэш названия редьюсера (обязательно для идентификации нужного стора)
73
- _connected:true //статус соединения с сервером
74
- }
75
- Диспатчер{ //отправка экшена клиенту
76
- _msg:"REDUX_CLUSTER_MSGTOWORKER",
77
- _hash:self.store.RCHash,
78
- _action:{ //диспатчер (обязательно)
79
- type:"REDUX_CLUSTER_SYNC",
80
- payload:self.store.getState()
81
- }
82
- }
83
-
84
- СерверВСокет:
85
- Авторизация{ //ответ на запрос авторизации в сокете
86
- _msg:"REDUX_CLUSTER_SOCKET_AUTHSTATE",
87
- _hash:self.store.RCHash,
88
- _value:false, //статус авторизации (обязательно)
89
- _banned: true //IP заблокирован (не обязательно)
90
- }
91
- Диспатчер{ //отправка экшена клиенту
92
- _msg:"REDUX_CLUSTER_MSGTOWORKER",
93
- _hash:self.store.RCHash,
94
- _action:{ //диспатчер (обязательно)
95
- type:"REDUX_CLUSTER_SYNC",
96
- payload:self.store.getState()
97
- }
98
- }
99
-
100
- КлиентВСокет:
101
- Авторизация{ //запрос авторизации в сокете
102
- _msg:'REDUX_CLUSTER_SOCKET_AUTH',
103
- _hash:self.store.RCHash,
104
- _login:self.login, //логин для авторизации в сокете
105
- _password:self.password //пароль для авторизации в сокете
106
- }
107
- Диспатчер{ //отправка экшена серверу
108
- _msg:'REDUX_CLUSTER_MSGTOMASTER',
109
- _hash:self.store.RCHash,
110
- _action:_data //экшн для передачи в редьюсер сервера
111
- }
112
- Старт{ //отправка запроса на получения снимка хранилища
113
- _msg:'REDUX_CLUSTER_START',
114
- _hash:self.store.RCHash
115
- }
116
-
117
- */
118
-
119
- function ReduxCluster(_reducer){
120
- let self = this;
121
- self.stderr = console.error; //callback для ошибок
122
- self.role = []; //роль
123
- self.mode = "action"; //тип синхронизации по умолчанию
124
- self.connected = false; //статус соединения
125
- self.resync = 1000; //количество действий для пересинхронизации
126
- self.RCHash = hasher(_reducer.name); //создаю метку текущего редьюсера для каждого экземпляра
127
- self.version = require('./package.json').version; //версия пакета
128
- self.homepage = require('./package.json').homepage; //домашняя страница пакета
129
- self.altReducer = _reducer; //оригинальный редьюсер
130
- self.allsock = {}; //сервера
131
- if(typeof(reducers[_reducer.name]) === 'undefined'){
132
- reducers[_reducer.name] = self.RCHash;
133
- } else {
134
- throw new Error("Please don't use a reducer with the same name!");
135
- }
136
- self.sendtoall = function(_message){ //отправка снимков во все воркеры
137
- if(Cluster.isMaster){
138
- if(typeof(_message) ==='object'){
139
- for (const id in Cluster.workers) {
140
- Cluster.workers[id].send(_message);
141
- }
142
- } else{
143
- for (const id in Cluster.workers) {
144
- Cluster.workers[id].send({_msg:"REDUX_CLUSTER_MSGTOWORKER", _hash:self.RCHash, _action:{type:"REDUX_CLUSTER_SYNC", payload:self.getState()}});
145
- }
146
- }
147
- }
148
- }
149
- self.sendtoallsock = function(_message){ //отправка сообщений во все сокеты
150
- for(const id in self.allsock){
151
- if((typeof(self.allsock[id]) === 'object') && (typeof(self.allsock[id].sendtoall) === 'function'))
152
- setTimeout(self.allsock[id].sendtoall, 1, _message);
153
- }
154
- };
155
- try{
156
- let _d = self.altReducer(undefined, {}); //получаю значение state при старте
157
- if(typeof(_d) === 'object'){
158
- self.defaulstate = _d;
159
- } else {
160
- throw new Error('The returned value is not an object.');
161
- }
162
- } catch(e){
163
- self.defaulstate = {};
164
- };
165
- self.newReducer = function(state=self.defaulstate, action){ //собственный редьюсер
166
- if(self.mode === "action"){ //в режиме action отправляем action в воркеры и сокеты
167
- if((typeof(self.counter) === 'undefined') || (self.counter === self.resync))
168
- self.counter = 1;
169
- else
170
- self.counter++;
171
- if(self.counter === self.resync){ //каждый N-action синхронизируем хранилище (на случай рассинхронизации)
172
- if(self.role.indexOf("master") !== -1)
173
- setTimeout(self.sendtoall, 100);
174
- if(self.role.indexOf("server") !== -1)
175
- setTimeout(self.sendtoallsock, 100);
176
- }
177
- if(self.role.indexOf("master") !== -1)
178
- setTimeout(self.sendtoall, 1, {_msg:"REDUX_CLUSTER_MSGTOWORKER", _hash:self.RCHash, _action:action});
179
- if(self.role.indexOf("server") !== -1)
180
- setTimeout(self.sendtoallsock, 1, {_msg:"REDUX_CLUSTER_MSGTOWORKER", _hash:self.RCHash, _action:action});
181
- }
182
- if (action.type === 'REDUX_CLUSTER_SYNC'){
183
- let state_new = Lodash.clone(action.payload);
184
- return state_new;
185
- } else {
186
- return self.altReducer(state, action);
187
- }
188
- }
189
- Object.assign(self, Redux.createStore(self.newReducer)); //создаю хранилище с собственным редьюсером
190
- delete self.replaceReducer; //удаляю замену редьюсера
191
- self.backup = function(object){
192
- let _object = Lodash.clone(object);
193
- return new Promise(function(resolve, reject){
194
- loadBackup().then(function(val){
195
- new createBackup();
196
- resolve(true);
197
- }).catch(function(err){
198
- if(err && err.message.toLowerCase().indexOf("no such file or directory") !== -1){
199
- new createBackup();
200
- resolve(true);
201
- } else {
202
- reject(err);
203
- }
204
- });
205
- });
206
- function loadBackup(){
207
- return new Promise(function(res, rej){
208
- Fs.readFile(_object["path"], function(_err,_data){
209
- if(_err){
210
- rej(new Error('ReduxCluster.backup load error: '+_err.message));
211
- } else {
212
- try{
213
- let _string = _data.toString();
214
- if(typeof(_object["key"]) !== 'undefined')
215
- _string = decrypter(_string, _object["key"]);
216
- let _obj = JSON.parse(_string);
217
- if(typeof(self.dispatchNEW) === 'function')
218
- self.dispatchNEW({type:"REDUX_CLUSTER_SYNC", payload:_obj});
219
- else
220
- self.dispatch({type:"REDUX_CLUSTER_SYNC", payload:_obj});
221
- setTimeout(function(){
222
- res(true);
223
- }, 500);
224
- } catch (_e) {
225
- rej(new Error('ReduxCluster.backup decoding error: '+_e.message))
226
- }
227
- }
228
- });
229
- });
230
- }
231
- function createBackup(){
232
- let _createBackup = this;
233
- _createBackup.c = 0;
234
- _createBackup.allowed = true;
235
- _createBackup.disable = self.subscribe(function(){ //подписываю на обновление
236
- if(typeof(_object['timeout']) === 'number'){ //приоритетная настройка
237
- if(_createBackup.allowed){ //запись разрешена
238
- _createBackup.allowed = false; //запрещаю повторный запуск
239
- setTimeout(function(){
240
- _createBackup.write(true);
241
- },_object['timeout']*1000);
242
- }
243
- } else {
244
- let _update = false;
245
- if(typeof(_object['count']) === 'number'){ //проверяю счетчик
246
- _createBackup.c++;
247
- if(_createBackup.c === _object['count']){
248
- _createBackup.c = 0;
249
- }
250
- if(_createBackup.c === 0){
251
- _update = true;
252
- }
253
- }
254
- if(_update){
255
- _createBackup.write();
256
- }
257
- }
258
- });
259
- _createBackup.write = function(_restart, _callback){ //запись в fs
260
- if(_createBackup.allowed || _restart){
261
- try{
262
- let _string = JSON.stringify(self.getState());
263
- if(typeof(_object['key']) !== 'undefined'){
264
- try{
265
- _string = encrypter(_string, _object['key']);
266
- backupwrite();
267
- } catch(_err){
268
- self.stderr('ReduxCluster.backup encrypt error: '+_err);
269
- }
270
- } else {
271
- backupwrite();
272
- }
273
- function backupwrite(){
274
- let _resultBackup = Fs.writeFileSync(_object['path'], _string, (_err) => {
275
- if (_err) {
276
- self.stderr('ReduxCluster.backup write error: '+_err);
277
- _createBackup.allowed = false;
278
- setTimeout(_createBackup.write, 1000, true, _callback);
279
- }
280
- });
281
- if(typeof(_resultBackup) === 'undefined'){
282
- _createBackup.allowed = true;
283
- if(typeof(_callback) === 'function'){
284
- _callback(true);
285
- }
286
- }
287
- }
288
- } catch(_e){
289
- self.stderr('ReduxCluster.backup write error: '+_e);
290
- _createBackup.allowed = false;
291
- setTimeout(_createBackup.write, 1000, true, _callback);
292
- }
293
- }
294
- }
295
- }
296
- }
297
- if(Cluster.isMaster){ //мастер
298
- if(self.role.indexOf("master") === -1) { self.role.push("master"); }
299
- self.unsubscribe = self.subscribe(function(){ //подписываю отправку снимков при изменении только в режиме snapshot
300
- if(self.mode === "snapshot")
301
- self.sendtoall();
302
- });
303
- Cluster.on('message', (worker, message, handle) => { //получение сообщения мастером
304
- if (arguments.length === 2) { //поддержка старой версии (без идентификатора воркера)
305
- handle = message;
306
- message = worker;
307
- worker = undefined;
308
- }
309
- if(message._hash === self.RCHash){
310
- switch(message._msg){
311
- case 'REDUX_CLUSTER_MSGTOMASTER': //получаю диспатчер от воркера
312
- if(message._action.type === 'REDUX_CLUSTER_SYNC')
313
- throw new Error("Please don't use REDUX_CLUSTER_SYNC action type!");
314
- self.dispatch(message._action);
315
- break;
316
- case 'REDUX_CLUSTER_START': //получаю метку, что воркер запущен
317
- if(worker){
318
- Cluster.workers[worker.id].send({_msg:"REDUX_CLUSTER_MSGTOWORKER", _hash:self.RCHash, _action:{type:"REDUX_CLUSTER_SYNC", payload:self.getState()}}); //в зависимости от наличия метки воркера, отправляю снимок ему, либо всем
319
- } else {
320
- self.sendtoall();
321
- }
322
- break;
323
- }
324
- }
325
- });
326
- self.connected = true;
327
- } else { //воркер
328
- if(self.role.indexOf("worker") === -1) { self.role.push("worker"); }
329
- self.dispatchNEW = self.dispatch; //переопределяю диспатчер
330
- self.dispatch = function(_data){
331
- process.send({_msg:'REDUX_CLUSTER_MSGTOMASTER', _hash:self.RCHash, _action:_data}); //вместо оригинального диспатчера подкладываю IPC
332
- };
333
- process.on("message", function(data){ //получение сообщения воркером
334
- if((data._hash === self.RCHash) && (self.role.indexOf("worker") !== -1)){ //если роль worker не активна больше не слушаем default IPC
335
- switch(data._msg){
336
- case 'REDUX_CLUSTER_MSGTOWORKER': //получение снимка от мастера
337
- self.dispatchNEW(data._action); //запускаю диспатчер Redux
338
- break;
339
- case 'REDUX_CLUSTER_CONNSTATUS':
340
- self.connected = data._connected;
341
- break;
342
- }
343
- }
344
- });
345
- self.connected = true;
346
- process.send({_msg:'REDUX_CLUSTER_START', _hash:self.RCHash}); //запрашиваю у мастера снимок хранилища
347
- }
348
- }
349
-
350
- function createStore(_reducer){ //функция создания хранилища
351
- let _ReduxCluster = new ReduxCluster(_reducer); //создаю экземпляр хранилища
352
- _ReduxCluster.createServer = function(_settings){ //подключаю объект создания сервера
353
- if((!Cluster.isMaster) && (typeof(_settings.path) === 'string') && (Os.platform() === 'win32')){ //IPC в дочернем процессе кластера не работает в windows
354
- throw new Error("Named channel is not supported in the child process, please use TCP-server");
355
- }
356
- if(_ReduxCluster.role.indexOf("server") === -1) { _ReduxCluster.role.push("server"); }
357
- _ReduxCluster.connected = false;
358
- return new createServer(_ReduxCluster, _settings);
359
- }
360
- _ReduxCluster.createClient = function(_settings){ //подключаю объект создания клиента
361
- if(_ReduxCluster.role.indexOf("client") === -1) { _ReduxCluster.role.push("client"); } else {
362
- throw new Error('One storage cannot be connected to two servers at the same time.');
363
- }
364
- if(_ReduxCluster.role.indexOf("worker") !== -1) { _ReduxCluster.role.splice(_ReduxCluster.role.indexOf("worker"), 1); } //удаляю роль воркера, т.к. IPC Master->Worker уничтожена (не обрабатывается)
365
- _ReduxCluster.connected = false;
366
- return new createClient(_ReduxCluster, _settings);
367
- }
368
- return _ReduxCluster;
369
- }
370
-
371
- function createServer(_store, _settings){ //объект создания сервера
372
- let self = this;
373
- self.uid = generateUID();
374
- self.sockets = {};
375
- self.database = {};
376
- self.ip2ban = {};
377
- self.ip2banTimeout = 10800000;
378
- self.ip2banGCStart = setInterval(function(){
379
- for(const key in self.ip2ban){
380
- if((self.ip2ban[key].time+self.ip2banTimeout) < Date.now()){
381
- delete self.ip2ban[key];
382
- }
383
- }
384
- }, 60000);
385
- self.ip2banGCStop = function(){ clearInterval(self.ip2banGCStart); }
386
- self.listen = {port:10001}; //дефолтные настройки
387
- if(typeof(_settings) === 'object'){ //переопределяю конфиг
388
- if(typeof(_settings.path) === 'string'){
389
- switch(Os.platform ()){
390
- case 'win32':
391
- self.listen = {path:Path.join('\\\\?\\pipe', _settings.path)};
392
- break;
393
- default:
394
- self.listen = {path:Path.join(_settings.path)};
395
- break;
396
- }
397
- } else{
398
- if(typeof(_settings.host) === 'string')
399
- self.listen.host = _settings.host;
400
- if(typeof(_settings.port) === 'number')
401
- self.listen.port = _settings.port;
402
- }
403
- if(typeof(_settings.logins) === 'object')
404
- for(const login in _settings.logins){ self.database[hasher("REDUX_CLUSTER"+login)] = hasher("REDUX_CLUSTER"+_settings.logins[login]); }
405
- }
406
- self.sendtoall = function(_message){
407
- if(typeof(_message) === 'object'){
408
- for(const uid in self.sockets){
409
- self.sockets[uid].write(_message);
410
- }
411
- } else {
412
- for(const uid in self.sockets){
413
- self.sockets[uid].write({_msg:"REDUX_CLUSTER_MSGTOWORKER", _hash:_store.RCHash, _action:{type:"REDUX_CLUSTER_SYNC", payload:_store.getState()}});
414
- }
415
- }
416
- }
417
- if(_store.mode === "action") //переопределяю функцию отправки в сокеты (вызывается редьюсером первичного мастера)
418
- _store.allsock[self.uid] = self;
419
- self.unsubscribe = _store.subscribe(function(){ //подписываю сокет на изменения Redux только в режиме snapshot
420
- if(_store.mode === "snapshot")
421
- self.sendtoall();
422
- });
423
- self.server = Net.createServer((socket) => {
424
- let _i2bTest = replacer(socket.remoteAddress, true);
425
- let _uid = generateUID();
426
- socket.uid = _uid;
427
- socket.writeNEW = socket.write; //переопределяю write (объектный режим + сжатие)
428
- socket.write = function(_data){
429
- try{
430
- return socket.writeNEW(Zlib.gzipSync(Buffer.from(JSON.stringify(_data))));
431
- } catch(err){
432
- _store.stderr('ReduxCluster.createServer write error: '+err.message);
433
- return;
434
- }
435
- }
436
- socket.on('error', function(err){ //обработка ошибок сокета
437
- _store.stderr('ReduxCluster.createServer client error: '+err.message);
438
- if(typeof(socket.end) === 'function'){
439
- socket.end();
440
- }
441
- if((typeof(socket.uid) !== 'undefined') && (typeof(self.sockets[socket.uid]) !== 'undefined')){
442
- delete self.sockets[socket.uid];
443
- }
444
- });
445
- if((typeof(_i2bTest) === 'undefined') || (typeof(self.ip2ban[_i2bTest]) === 'undefined') || ((typeof(self.ip2ban[_i2bTest]) === 'object') && ((self.ip2ban[_i2bTest].count < 5) || ((self.ip2ban[_i2bTest].time+self.ip2banTimeout) < Date.now())))){
446
- self.parser = new Objectstream.Parser();
447
- self.mbstring = new Stream.Transform({ //обработка мультибайтовых символов без присвоенной кодировки может срабатывать некорректно
448
- transform(_buffer, encoding, callback) {
449
- this.push(_buffer)
450
- return callback();
451
- }
452
- });
453
- self.mbstring.setEncoding('utf8');
454
- self.gunzipper = Zlib.createGunzip(); //поток декомпрессии
455
- self.event = Eventstream.map(function (data, next1) {
456
- if(data._hash === _store.RCHash){ //проверяю что сообщение привязано к текущему хранилищу
457
- switch(data._msg){
458
- case 'REDUX_CLUSTER_MSGTOMASTER': //получаю диспатчер от клиента
459
- if((typeof(socket.uid) !== 'undefined') && (typeof(self.sockets[socket.uid]) !== 'undefined')){
460
- if(data._action.type === 'REDUX_CLUSTER_SYNC')
461
- throw new Error("Please don't use REDUX_CLUSTER_SYNC action type!");
462
- _store.dispatch(data._action);
463
- }
464
- break;
465
- case 'REDUX_CLUSTER_START': //получаю метку, что клиент запущен
466
- if((typeof(socket.uid) !== 'undefined') && (typeof(self.sockets[socket.uid]) !== 'undefined')){
467
- self.sockets[socket.uid].write({_msg:"REDUX_CLUSTER_MSGTOWORKER", _hash:_store.RCHash, _action:{type:"REDUX_CLUSTER_SYNC", payload:_store.getState()}});
468
- }
469
- break;
470
- case 'REDUX_CLUSTER_SOCKET_AUTH':
471
- if( (typeof(data._login) !== 'undefined') &&
472
- (typeof(data._password) !== 'undefined') &&
473
- (typeof(self.database[data._login]) !== 'undefined') &&
474
- (self.database[data._login] === data._password)){
475
- self.sockets[socket.uid] = socket;
476
- if((typeof(_i2bTest) === 'string') && (typeof(self.ip2ban[_i2bTest]) === 'object')) { delete self.ip2ban[_i2bTest]; } //если логин присутствует в таблице забаненных удаляю
477
- socket.write({_msg:"REDUX_CLUSTER_SOCKET_AUTHSTATE", _hash:_store.RCHash, _value:true});
478
- } else {
479
- if(typeof(_i2bTest) === 'string') {
480
- let _tempCount = 0;
481
- if(typeof(self.ip2ban[_i2bTest]) === 'object'){
482
- _tempCount = self.ip2ban[_i2bTest].count;
483
- if(_tempCount >= 5) { _tempCount = 0; } //по таймауту сбрасываю счетчик попыток
484
- }
485
- self.ip2ban[_i2bTest] = {time: Date.now(), count:_tempCount+1};
486
- }
487
- socket.write({_msg:"REDUX_CLUSTER_SOCKET_AUTHSTATE", _hash:_store.RCHash, _value:false});
488
- if(typeof(socket.end) === 'function'){
489
- socket.end();
490
- }
491
- if((typeof(socket.uid) !== 'undefined') && (typeof(self.sockets[socket.uid]) !== 'undefined')){
492
- delete self.sockets[socket.uid];
493
- }
494
- }
495
- break;
496
- }
497
- }
498
- next1();
499
- });
500
- self.gunzipper.on('error',function(err){
501
- _store.stderr('ReduxCluster.createServer gunzipper error: '+err);
502
- });
503
- self.mbstring.on('error',function(err){
504
- _store.stderr('ReduxCluster.createServer mbstring error: '+err);
505
- });
506
- self.parser.on('error',function(err){
507
- _store.stderr('ReduxCluster.createServer parser error: '+err);
508
- });
509
- self.event.on('error',function(err){
510
- _store.stderr('ReduxCluster.createServer event error: '+err);
511
- });
512
- socket.pipe(self.gunzipper).pipe(self.mbstring).pipe(self.parser).pipe(self.event);
513
- } else {
514
- socket.write({_msg:"REDUX_CLUSTER_SOCKET_AUTHSTATE", _hash:_store.RCHash, _value:false, _banned: true});
515
- if(typeof(socket.end) === 'function'){
516
- socket.end();
517
- }
518
- if((typeof(socket.uid) !== 'undefined') && (typeof(self.sockets[socket.uid]) !== 'undefined')){
519
- delete self.sockets[socket.uid];
520
- }
521
- }
522
- }).on('listening', function(){ //сервер слушает
523
- _store.connected = true;
524
- _store.sendtoall({_msg:"REDUX_CLUSTER_CONNSTATUS", _hash:_store.RCHash, _connected:true});
525
- }).on('close', function(){ //все коннекты уничтожены
526
- _store.connected = false;
527
- _store.sendtoall({_msg:"REDUX_CLUSTER_CONNSTATUS", _hash:_store.RCHash, _connected:false});
528
- self.unsubscribe();
529
- self.ip2banGCStop();
530
- delete _store.allsock[self.uid];
531
- setTimeout(function(){ new createServer(_store, _settings) }, 10000);
532
- }).on('error', function(err){ //обработка ошибок сервера
533
- _store.stderr('ReduxCluster.createServer socket error: '+err.message);
534
- if(typeof(self.server.close) === 'function')
535
- self.server.close();
536
- });
537
- if(typeof(self.listen.path) === 'string'){
538
- Fs.unlink(self.listen.path, function(err){
539
- if(err && err.message.toLowerCase().indexOf("no such file or directory") === -1)
540
- _store.stderr('ReduxCluster.createServer socket error: '+err);
541
- self.server.listen(self.listen);
542
- });
543
- } else {
544
- self.server.listen(self.listen);
545
- }
546
- }
547
-
548
- function createClient(_store, _settings){ //объект создания клиента
549
- let self = this;
550
- self.listen = {port:10001}; //дефолтные настройки
551
- if(typeof(_settings) === 'object'){ //переопределяю конфиг
552
- if(typeof(_settings.path) === 'string'){
553
- switch(Os.platform ()){
554
- case 'win32':
555
- self.listen = {path:Path.join('\\\\?\\pipe', _settings.path)};
556
- break;
557
- default:
558
- self.listen = {path:Path.join(_settings.path)};
559
- break;
560
- }
561
- } else{
562
- if(typeof(_settings.host) === 'string')
563
- self.listen.host = _settings.host;
564
- if(typeof(_settings.port) === 'number')
565
- self.listen.port = _settings.port;
566
- }
567
- if(typeof(_settings.login) === 'string')
568
- self.login = hasher("REDUX_CLUSTER"+_settings.login);
569
- if(typeof(_settings.password) === 'string')
570
- self.password = hasher("REDUX_CLUSTER"+_settings.password);
571
- }
572
- self.client = new Net.createConnection(self.listen);
573
- self.parser = new Objectstream.Parser();
574
- self.mbstring = new Stream.Transform({ //обработка мультибайтовых символов без присвоенной кодировки может срабатывать некорректно
575
- transform(_buffer, encoding, callback) {
576
- this.push(_buffer)
577
- return callback();
578
- }
579
- });
580
- self.mbstring.setEncoding('utf8');
581
- self.gunzipper = Zlib.createGunzip(); //поток декомпрессии
582
- self.event = Eventstream.map(function (data, next1) {
583
- if(!self.client.destroyed){
584
- if(data._hash === _store.RCHash){
585
- switch(data._msg){
586
- case 'REDUX_CLUSTER_MSGTOWORKER':
587
- _store.dispatchNEW(data._action);
588
- break;
589
- case 'REDUX_CLUSTER_SOCKET_AUTHSTATE':
590
- if(data._value === true){
591
- self.client.write({_msg:'REDUX_CLUSTER_START', _hash:_store.RCHash}); //синхронизирую хранилище
592
- }else{
593
- if(data._banned)
594
- self.client.destroy(new Error('your ip is locked for 3 hours'));
595
- else
596
- self.client.destroy(new Error('authorization failed'));
597
- }
598
- break;
599
- }
600
- }
601
- }
602
- next1();
603
- });
604
- self.gunzipper.on('error',function(err){
605
- _store.stderr('ReduxCluster.createClient gunzipper error: '+err);
606
- });
607
- self.mbstring.on('error',function(err){
608
- _store.stderr('ReduxCluster.createClient mbstring error: '+err);
609
- });
610
- self.parser.on('error',function(err){
611
- _store.stderr('ReduxCluster.createClient parser error: '+err);
612
- });
613
- self.event.on('error',function(err){
614
- _store.stderr('ReduxCluster.createClient event error: '+err);
615
- });
616
- self.client.on('connect', function(){
617
- _store.connected = true;
618
- _store.sendtoall({_msg:"REDUX_CLUSTER_CONNSTATUS", _hash:_store.RCHash, _connected:true});
619
- self.client.writeNEW = self.client.write; //переопределяю write (объектный режим + сжатие)
620
- self.client.write = function(_data){
621
- try {
622
- return self.client.writeNEW(Zlib.gzipSync(Buffer.from(JSON.stringify(_data))));
623
- } catch(err){
624
- _store.stderr('ReduxCluster.createClient write error: '+err.message);
625
- return;
626
- }
627
- }
628
- if(typeof(_store.dispatchNEW) !== 'function'){ //переопределяю dispatch для мастера
629
- _store.dispatchNEW = _store.dispatch;
630
- }
631
- _store.dispatch = function(_data){
632
- self.client.write({_msg:'REDUX_CLUSTER_MSGTOMASTER', _hash:_store.RCHash, _action:_data});
633
- }
634
- self.client.write({_msg:'REDUX_CLUSTER_SOCKET_AUTH', _hash:_store.RCHash, _login:self.login, _password:self.password}); //авторизация в сокете
635
- }).on('close', function(){
636
- _store.connected = false;
637
- _store.sendtoall({_msg:"REDUX_CLUSTER_CONNSTATUS", _hash:_store.RCHash, _connected:false});
638
- setTimeout(function(){ new createClient(_store, _settings) }, 250);
639
- }).on('error', function(err){ //обработка ошибок клиента
640
- _store.stderr('ReduxCluster.createClient client error: '+err.message);
641
- }).pipe(self.gunzipper).pipe(self.mbstring).pipe(self.parser).pipe(self.event);
642
- }
643
-
644
- //генерация uid
645
- function generateUID() {
646
- let d = new Date().getTime();
647
- if (typeof performance !== 'undefined' && typeof performance.now === 'function'){
648
- d += performance.now();
649
- }
650
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
651
- let r = (d + Math.random() * 16) % 16 | 0;
652
- d = Math.floor(d / 16);
653
- return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
654
- });
655
- }
656
-
657
- //функция замены "." на "_" и обратно
658
- function replacer(data_val, value_val){
659
- if(typeof(data_val) === 'string'){
660
- if(value_val){
661
- return data_val.replace(/\./gi,"_");
662
- } else {
663
- return data_val.replace(/\_/gi,".");
664
- }
665
- }
666
- }
667
-
668
- ReduxClusterModule.createStore = createStore; //переопределяю функцию создания хранилища
669
- ReduxClusterModule.functions = {
670
- generateUID: generateUID,
671
- replacer: replacer,
672
- hasher: hasher,
673
- encrypter: encrypter,
674
- decrypter: decrypter
675
- };
676
-
677
- module.exports = ReduxClusterModule;
678
-