kuzzle 2.20.3 → 2.21.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/lib/api/controllers/debugController.d.ts +2 -27
- package/lib/api/controllers/debugController.js +8 -182
- package/lib/api/controllers/documentController.js +122 -10
- package/lib/api/request/kuzzleRequest.d.ts +8 -0
- package/lib/api/request/kuzzleRequest.js +12 -0
- package/lib/cluster/idCardHandler.js +19 -6
- package/lib/cluster/node.js +18 -1
- package/lib/cluster/protobuf/sync.proto +5 -0
- package/lib/cluster/publisher.js +10 -0
- package/lib/cluster/subscriber.js +8 -1
- package/lib/cluster/workers/IDCardRenewer.js +23 -26
- package/lib/core/debug/kuzzleDebugger.d.ts +45 -0
- package/lib/core/debug/kuzzleDebugger.js +205 -0
- package/lib/kuzzle/dumpGenerator.js +1 -1
- package/lib/kuzzle/kuzzle.js +3 -0
- package/lib/service/storage/elasticsearch.js +44 -30
- package/lib/types/events/EventGenericDocument.d.ts +21 -1
- package/package.json +1 -1
- package/lib/types/DebugModule.d.ts +0 -24
- package/lib/types/DebugModule.js +0 -39
|
@@ -4,23 +4,10 @@ import { NativeController } from "./baseController";
|
|
|
4
4
|
* @class DebugController
|
|
5
5
|
*/
|
|
6
6
|
export declare class DebugController extends NativeController {
|
|
7
|
-
|
|
8
|
-
private debuggerStatus;
|
|
9
|
-
/**
|
|
10
|
-
* Map<eventName, Set<connectionId>>
|
|
11
|
-
*/
|
|
12
|
-
private events;
|
|
13
|
-
/**
|
|
14
|
-
* Map of functions from the DebugModules
|
|
15
|
-
*/
|
|
16
|
-
private kuzzlePostMethods;
|
|
7
|
+
constructor();
|
|
17
8
|
/**
|
|
18
|
-
*
|
|
19
|
-
* Used to add new methods and events to the protocol
|
|
9
|
+
* Return the node version of the current Kuzzle instance
|
|
20
10
|
*/
|
|
21
|
-
private modules;
|
|
22
|
-
constructor();
|
|
23
|
-
init(): Promise<void>;
|
|
24
11
|
nodeVersion(): Promise<string>;
|
|
25
12
|
/**
|
|
26
13
|
* Connect the debugger
|
|
@@ -44,16 +31,4 @@ export declare class DebugController extends NativeController {
|
|
|
44
31
|
* Remove the websocket connection from the events' listeners
|
|
45
32
|
*/
|
|
46
33
|
removeListener(request: KuzzleRequest): Promise<void>;
|
|
47
|
-
/**
|
|
48
|
-
* Execute a method using the Chrome Debug Protocol
|
|
49
|
-
* @param method Chrome Debug Protocol method to execute
|
|
50
|
-
* @param params
|
|
51
|
-
* @returns
|
|
52
|
-
*/
|
|
53
|
-
private inspectorPost;
|
|
54
|
-
/**
|
|
55
|
-
* Sends a direct notification to a websocket connection without having to listen to a specific room
|
|
56
|
-
*/
|
|
57
|
-
private notifyConnection;
|
|
58
|
-
private notifyGlobalListeners;
|
|
59
34
|
}
|
|
@@ -42,16 +42,10 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
42
42
|
__setModuleDefault(result, mod);
|
|
43
43
|
return result;
|
|
44
44
|
};
|
|
45
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
46
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
47
|
-
};
|
|
48
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
46
|
exports.DebugController = void 0;
|
|
50
47
|
const baseController_1 = require("./baseController");
|
|
51
|
-
const inspector_1 = __importDefault(require("inspector"));
|
|
52
48
|
const kerror = __importStar(require("../../kerror"));
|
|
53
|
-
const get_1 = __importDefault(require("lodash/get"));
|
|
54
|
-
const DEBUGGER_EVENT = "kuzzle-debugger-event";
|
|
55
49
|
/**
|
|
56
50
|
* @class DebugController
|
|
57
51
|
*/
|
|
@@ -65,53 +59,10 @@ class DebugController extends baseController_1.NativeController {
|
|
|
65
59
|
"addListener",
|
|
66
60
|
"removeListener",
|
|
67
61
|
]);
|
|
68
|
-
this.debuggerStatus = false;
|
|
69
|
-
/**
|
|
70
|
-
* Map<eventName, Set<connectionId>>
|
|
71
|
-
*/
|
|
72
|
-
this.events = new Map();
|
|
73
|
-
/**
|
|
74
|
-
* Map of functions from the DebugModules
|
|
75
|
-
*/
|
|
76
|
-
this.kuzzlePostMethods = new Map();
|
|
77
|
-
/**
|
|
78
|
-
* List of DebugModule for DebugController
|
|
79
|
-
* Used to add new methods and events to the protocol
|
|
80
|
-
*/
|
|
81
|
-
this.modules = [];
|
|
82
|
-
}
|
|
83
|
-
async init() {
|
|
84
|
-
super.init();
|
|
85
|
-
this.inspector = new inspector_1.default.Session();
|
|
86
|
-
// Remove connection id from the list of listeners for each event
|
|
87
|
-
global.kuzzle.on("connection:remove", (connectionId) => {
|
|
88
|
-
if (!this.debuggerStatus) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
for (const listener of this.events.values()) {
|
|
92
|
-
listener.delete(connectionId);
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
this.inspector.on("inspectorNotification", async (payload) => {
|
|
96
|
-
if (!this.debuggerStatus) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
await this.notifyGlobalListeners(payload.method, payload);
|
|
100
|
-
const listeners = this.events.get(payload.method);
|
|
101
|
-
if (!listeners) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
const promises = [];
|
|
105
|
-
for (const connectionId of listeners) {
|
|
106
|
-
promises.push(this.notifyConnection(connectionId, DEBUGGER_EVENT, {
|
|
107
|
-
event: payload.method,
|
|
108
|
-
result: payload,
|
|
109
|
-
}));
|
|
110
|
-
}
|
|
111
|
-
// No need to catch, notify is already try-catched
|
|
112
|
-
await Promise.all(promises);
|
|
113
|
-
});
|
|
114
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Return the node version of the current Kuzzle instance
|
|
65
|
+
*/
|
|
115
66
|
async nodeVersion() {
|
|
116
67
|
return process.version;
|
|
117
68
|
}
|
|
@@ -119,82 +70,22 @@ class DebugController extends baseController_1.NativeController {
|
|
|
119
70
|
* Connect the debugger
|
|
120
71
|
*/
|
|
121
72
|
async enable() {
|
|
122
|
-
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
this.inspector.connect();
|
|
126
|
-
this.debuggerStatus = true;
|
|
127
|
-
for (const module of this.modules) {
|
|
128
|
-
await module.init(this.inspector);
|
|
129
|
-
for (const methodName of module.methods) {
|
|
130
|
-
if (!module[methodName]) {
|
|
131
|
-
throw new Error(`Missing implementation of method "${methodName}" inside DebugModule "${module.name}"`);
|
|
132
|
-
}
|
|
133
|
-
this.kuzzlePostMethods.set(`Kuzzle.${module.name}.${methodName}`, module[methodName].bind(module));
|
|
134
|
-
}
|
|
135
|
-
for (const eventName of module.events) {
|
|
136
|
-
module.on(eventName, async (payload) => {
|
|
137
|
-
if (!this.debuggerStatus) {
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
const event = `Kuzzle.${module.name}.${eventName}`;
|
|
141
|
-
await this.notifyGlobalListeners(event, payload);
|
|
142
|
-
const listeners = this.events.get(event);
|
|
143
|
-
if (!listeners) {
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
const promises = [];
|
|
147
|
-
for (const connectionId of listeners) {
|
|
148
|
-
promises.push(this.notifyConnection(connectionId, DEBUGGER_EVENT, {
|
|
149
|
-
event,
|
|
150
|
-
result: payload,
|
|
151
|
-
}));
|
|
152
|
-
}
|
|
153
|
-
// No need to catch, notify is already try-catched
|
|
154
|
-
await Promise.all(promises);
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
}
|
|
73
|
+
await global.kuzzle.ask("core:debugger:enable");
|
|
158
74
|
}
|
|
159
75
|
/**
|
|
160
76
|
* Disconnect the debugger and clears all the events listeners
|
|
161
77
|
*/
|
|
162
78
|
async disable() {
|
|
163
|
-
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
for (const module of this.modules) {
|
|
167
|
-
for (const eventName of module.events) {
|
|
168
|
-
module.removeAllListeners(eventName);
|
|
169
|
-
}
|
|
170
|
-
await module.cleanup();
|
|
171
|
-
}
|
|
172
|
-
this.inspector.disconnect();
|
|
173
|
-
this.debuggerStatus = false;
|
|
174
|
-
this.events.clear();
|
|
175
|
-
this.kuzzlePostMethods.clear();
|
|
79
|
+
await global.kuzzle.ask("core:debugger:disable");
|
|
176
80
|
}
|
|
177
81
|
/**
|
|
178
82
|
* Trigger action from debugger directly following the Chrome Debug Protocol
|
|
179
83
|
* See: https://chromedevtools.github.io/devtools-protocol/v8/
|
|
180
84
|
*/
|
|
181
85
|
async post(request) {
|
|
182
|
-
if (!this.debuggerStatus) {
|
|
183
|
-
throw kerror.get("core", "debugger", "not_enabled");
|
|
184
|
-
}
|
|
185
86
|
const method = request.getBodyString("method");
|
|
186
87
|
const params = request.getBodyObject("params", {});
|
|
187
|
-
|
|
188
|
-
const debugModuleMethod = this.kuzzlePostMethods.get(method);
|
|
189
|
-
if (debugModuleMethod) {
|
|
190
|
-
return debugModuleMethod(params);
|
|
191
|
-
}
|
|
192
|
-
throw kerror.get("core", "debugger", "method_not_found", method);
|
|
193
|
-
}
|
|
194
|
-
if (!(0, get_1.default)(global.kuzzle.config, "security.debug.native_debug_protocol")) {
|
|
195
|
-
throw kerror.get("core", "debugger", "native_debug_protocol_usage_denied");
|
|
196
|
-
}
|
|
197
|
-
return this.inspectorPost(method, params);
|
|
88
|
+
return global.kuzzle.ask("core:debugger:post", method, params);
|
|
198
89
|
}
|
|
199
90
|
/**
|
|
200
91
|
* Make the websocket connection listen and receive events from Chrome Debug Protocol
|
|
@@ -204,16 +95,8 @@ class DebugController extends baseController_1.NativeController {
|
|
|
204
95
|
if (request.context.connection.protocol !== "websocket") {
|
|
205
96
|
throw kerror.get("api", "assert", "unsupported_protocol", request.context.connection.protocol, "debug:addListener");
|
|
206
97
|
}
|
|
207
|
-
if (!this.debuggerStatus) {
|
|
208
|
-
throw kerror.get("core", "debugger", "not_enabled");
|
|
209
|
-
}
|
|
210
98
|
const event = request.getBodyString("event");
|
|
211
|
-
|
|
212
|
-
if (!listeners) {
|
|
213
|
-
listeners = new Set();
|
|
214
|
-
this.events.set(event, listeners);
|
|
215
|
-
}
|
|
216
|
-
listeners.add(request.context.connection.id);
|
|
99
|
+
await global.kuzzle.ask("core:debugger:addListener", event, request.context.connection.id);
|
|
217
100
|
}
|
|
218
101
|
/**
|
|
219
102
|
* Remove the websocket connection from the events' listeners
|
|
@@ -222,65 +105,8 @@ class DebugController extends baseController_1.NativeController {
|
|
|
222
105
|
if (request.context.connection.protocol !== "websocket") {
|
|
223
106
|
throw kerror.get("api", "assert", "unsupported_protocol", request.context.connection.protocol, "debug:removeListener");
|
|
224
107
|
}
|
|
225
|
-
if (!this.debuggerStatus) {
|
|
226
|
-
throw kerror.get("core", "debugger", "not_enabled");
|
|
227
|
-
}
|
|
228
108
|
const event = request.getBodyString("event");
|
|
229
|
-
|
|
230
|
-
if (listeners) {
|
|
231
|
-
listeners.delete(request.context.connection.id);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Execute a method using the Chrome Debug Protocol
|
|
236
|
-
* @param method Chrome Debug Protocol method to execute
|
|
237
|
-
* @param params
|
|
238
|
-
* @returns
|
|
239
|
-
*/
|
|
240
|
-
async inspectorPost(method, params) {
|
|
241
|
-
if (!this.debuggerStatus) {
|
|
242
|
-
throw kerror.get("core", "debugger", "not_enabled");
|
|
243
|
-
}
|
|
244
|
-
let resolve;
|
|
245
|
-
const promise = new Promise((res) => {
|
|
246
|
-
resolve = res;
|
|
247
|
-
});
|
|
248
|
-
this.inspector.post(method, params, (err, res) => {
|
|
249
|
-
if (err) {
|
|
250
|
-
resolve({
|
|
251
|
-
error: JSON.stringify(Object.getOwnPropertyDescriptors(err)),
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
resolve(res);
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
return promise;
|
|
259
|
-
}
|
|
260
|
-
/**
|
|
261
|
-
* Sends a direct notification to a websocket connection without having to listen to a specific room
|
|
262
|
-
*/
|
|
263
|
-
async notifyConnection(connectionId, event, payload) {
|
|
264
|
-
global.kuzzle.entryPoint._notify({
|
|
265
|
-
channels: [event],
|
|
266
|
-
connectionId,
|
|
267
|
-
payload,
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
async notifyGlobalListeners(event, payload) {
|
|
271
|
-
const listeners = this.events.get("*");
|
|
272
|
-
if (!listeners) {
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
const promises = [];
|
|
276
|
-
for (const connectionId of listeners) {
|
|
277
|
-
promises.push(this.notifyConnection(connectionId, DEBUGGER_EVENT, {
|
|
278
|
-
event,
|
|
279
|
-
result: payload,
|
|
280
|
-
}));
|
|
281
|
-
}
|
|
282
|
-
// No need to catch, notify is already try-catched
|
|
283
|
-
await Promise.all(promises);
|
|
109
|
+
await global.kuzzle.ask("core:debugger:removeListener", event, request.context.connection.id);
|
|
284
110
|
}
|
|
285
111
|
}
|
|
286
112
|
exports.DebugController = DebugController;
|
|
@@ -390,12 +390,34 @@ class DocumentController extends NativeController {
|
|
|
390
390
|
|
|
391
391
|
const validated = await global.kuzzle.validation.validate(request, false);
|
|
392
392
|
|
|
393
|
+
// Add metadata
|
|
394
|
+
const pipeMetadataResult = await this.pipe(
|
|
395
|
+
"generic:document:injectMetadata",
|
|
396
|
+
{
|
|
397
|
+
metadata: {
|
|
398
|
+
author: userId,
|
|
399
|
+
createdAt: Date.now(),
|
|
400
|
+
updatedAt: null,
|
|
401
|
+
updater: null,
|
|
402
|
+
},
|
|
403
|
+
request,
|
|
404
|
+
}
|
|
405
|
+
);
|
|
406
|
+
|
|
393
407
|
const created = await this.ask(
|
|
394
408
|
"core:storage:public:document:create",
|
|
395
409
|
index,
|
|
396
410
|
collection,
|
|
397
|
-
|
|
398
|
-
|
|
411
|
+
{
|
|
412
|
+
...validated.getBody(),
|
|
413
|
+
_kuzzle_info: pipeMetadataResult.metadata,
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
id,
|
|
417
|
+
injectKuzzleMeta: false,
|
|
418
|
+
refresh,
|
|
419
|
+
userId,
|
|
420
|
+
}
|
|
399
421
|
);
|
|
400
422
|
|
|
401
423
|
if (!silent) {
|
|
@@ -440,13 +462,34 @@ class DocumentController extends NativeController {
|
|
|
440
462
|
false
|
|
441
463
|
);
|
|
442
464
|
|
|
465
|
+
// Add metadata
|
|
466
|
+
const pipeMetadataResult = await this.pipe(
|
|
467
|
+
"generic:document:injectMetadata",
|
|
468
|
+
{
|
|
469
|
+
metadata: {
|
|
470
|
+
author: userId,
|
|
471
|
+
createdAt: Date.now(),
|
|
472
|
+
updatedAt: Date.now(),
|
|
473
|
+
updater: userId,
|
|
474
|
+
},
|
|
475
|
+
request,
|
|
476
|
+
}
|
|
477
|
+
);
|
|
478
|
+
|
|
443
479
|
const response = await this.ask(
|
|
444
480
|
"core:storage:public:document:createOrReplace",
|
|
445
481
|
index,
|
|
446
482
|
collection,
|
|
447
483
|
id,
|
|
448
|
-
|
|
449
|
-
|
|
484
|
+
{
|
|
485
|
+
...content,
|
|
486
|
+
_kuzzle_info: pipeMetadataResult.metadata,
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
injectKuzzleMeta: false,
|
|
490
|
+
refresh,
|
|
491
|
+
userId,
|
|
492
|
+
}
|
|
450
493
|
);
|
|
451
494
|
|
|
452
495
|
if (!silent) {
|
|
@@ -493,13 +536,33 @@ class DocumentController extends NativeController {
|
|
|
493
536
|
false
|
|
494
537
|
);
|
|
495
538
|
|
|
539
|
+
// Add metadata
|
|
540
|
+
const pipeMetadataResult = await this.pipe(
|
|
541
|
+
"generic:document:injectMetadata",
|
|
542
|
+
{
|
|
543
|
+
metadata: {
|
|
544
|
+
updatedAt: Date.now(),
|
|
545
|
+
updater: userId,
|
|
546
|
+
},
|
|
547
|
+
request,
|
|
548
|
+
}
|
|
549
|
+
);
|
|
550
|
+
|
|
496
551
|
const updatedDocument = await this.ask(
|
|
497
552
|
"core:storage:public:document:update",
|
|
498
553
|
index,
|
|
499
554
|
collection,
|
|
500
555
|
id,
|
|
501
|
-
|
|
502
|
-
|
|
556
|
+
{
|
|
557
|
+
...content,
|
|
558
|
+
_kuzzle_info: pipeMetadataResult.metadata,
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
injectKuzzleMeta: false,
|
|
562
|
+
refresh,
|
|
563
|
+
retryOnConflict,
|
|
564
|
+
userId,
|
|
565
|
+
}
|
|
503
566
|
);
|
|
504
567
|
|
|
505
568
|
const _updatedFields = extractFields(content, {
|
|
@@ -548,13 +611,41 @@ class DocumentController extends NativeController {
|
|
|
548
611
|
const source = request.getBoolean("source");
|
|
549
612
|
const { index, collection } = request.getIndexAndCollection();
|
|
550
613
|
|
|
614
|
+
// Add metadata
|
|
615
|
+
const pipeMetadataResult = await this.pipe(
|
|
616
|
+
"generic:document:injectMetadata",
|
|
617
|
+
{
|
|
618
|
+
defaultMetadata: {
|
|
619
|
+
author: userId,
|
|
620
|
+
createdAt: Date.now(),
|
|
621
|
+
},
|
|
622
|
+
metadata: {
|
|
623
|
+
updatedAt: Date.now(),
|
|
624
|
+
updater: userId,
|
|
625
|
+
},
|
|
626
|
+
request,
|
|
627
|
+
}
|
|
628
|
+
);
|
|
629
|
+
|
|
551
630
|
const updatedDocument = await this.ask(
|
|
552
631
|
"core:storage:public:document:upsert",
|
|
553
632
|
index,
|
|
554
633
|
collection,
|
|
555
634
|
id,
|
|
556
|
-
|
|
557
|
-
|
|
635
|
+
{
|
|
636
|
+
...content,
|
|
637
|
+
_kuzzle_info: pipeMetadataResult.metadata,
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
defaultValues: {
|
|
641
|
+
...defaultValues,
|
|
642
|
+
_kuzzle_info: pipeMetadataResult.defaultMetadata,
|
|
643
|
+
},
|
|
644
|
+
injectKuzzleMeta: false,
|
|
645
|
+
refresh,
|
|
646
|
+
retryOnConflict,
|
|
647
|
+
userId,
|
|
648
|
+
}
|
|
558
649
|
);
|
|
559
650
|
|
|
560
651
|
if (!silent && updatedDocument.created) {
|
|
@@ -633,13 +724,34 @@ class DocumentController extends NativeController {
|
|
|
633
724
|
false
|
|
634
725
|
);
|
|
635
726
|
|
|
727
|
+
// Add metadata
|
|
728
|
+
const pipeMetadataResult = await this.pipe(
|
|
729
|
+
"generic:document:injectMetadata",
|
|
730
|
+
{
|
|
731
|
+
metadata: {
|
|
732
|
+
author: userId,
|
|
733
|
+
createdAt: Date.now(),
|
|
734
|
+
updatedAt: Date.now(),
|
|
735
|
+
updater: userId,
|
|
736
|
+
},
|
|
737
|
+
request,
|
|
738
|
+
}
|
|
739
|
+
);
|
|
740
|
+
|
|
636
741
|
const response = await this.ask(
|
|
637
742
|
"core:storage:public:document:replace",
|
|
638
743
|
index,
|
|
639
744
|
collection,
|
|
640
745
|
id,
|
|
641
|
-
|
|
642
|
-
|
|
746
|
+
{
|
|
747
|
+
...content,
|
|
748
|
+
_kuzzle_info: pipeMetadataResult.metadata,
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
injectKuzzleMeta: false,
|
|
752
|
+
refresh,
|
|
753
|
+
userId,
|
|
754
|
+
}
|
|
643
755
|
);
|
|
644
756
|
|
|
645
757
|
if (!silent) {
|
|
@@ -135,6 +135,14 @@ export declare class KuzzleRequest {
|
|
|
135
135
|
status: number;
|
|
136
136
|
timestamp: number;
|
|
137
137
|
};
|
|
138
|
+
/**
|
|
139
|
+
* Return the requested controller
|
|
140
|
+
*/
|
|
141
|
+
getController(): string;
|
|
142
|
+
/**
|
|
143
|
+
* Returns the requested controller's action
|
|
144
|
+
*/
|
|
145
|
+
getAction(): string;
|
|
138
146
|
/**
|
|
139
147
|
* Returns the `lang` param of the request.
|
|
140
148
|
*
|
|
@@ -324,6 +324,18 @@ class KuzzleRequest {
|
|
|
324
324
|
timestamp: this.timestamp,
|
|
325
325
|
};
|
|
326
326
|
}
|
|
327
|
+
/**
|
|
328
|
+
* Return the requested controller
|
|
329
|
+
*/
|
|
330
|
+
getController() {
|
|
331
|
+
return this[_input].controller;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Returns the requested controller's action
|
|
335
|
+
*/
|
|
336
|
+
getAction() {
|
|
337
|
+
return this[_input].action;
|
|
338
|
+
}
|
|
327
339
|
/**
|
|
328
340
|
* Returns the `lang` param of the request.
|
|
329
341
|
*
|
|
@@ -25,7 +25,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.ClusterIdCardHandler = exports.IdCard = void 0;
|
|
27
27
|
const name_generator_1 = require("../util/name-generator");
|
|
28
|
-
const
|
|
28
|
+
const child_process_1 = require("child_process");
|
|
29
29
|
const bluebird_1 = __importDefault(require("bluebird"));
|
|
30
30
|
require("../types");
|
|
31
31
|
const REDIS_PREFIX = "{cluster/node}/";
|
|
@@ -110,14 +110,16 @@ class ClusterIdCardHandler {
|
|
|
110
110
|
} while (!reserved);
|
|
111
111
|
await this.addIdCardToIndex();
|
|
112
112
|
this.refreshWorker = this.constructWorker(`${__dirname}/workers/IDCardRenewer.js`);
|
|
113
|
-
this.refreshWorker.unref();
|
|
114
113
|
this.refreshWorker.on("message", async (message) => {
|
|
115
114
|
if (message.error) {
|
|
116
115
|
await this.node.evictSelf(message.error);
|
|
117
116
|
}
|
|
118
117
|
});
|
|
118
|
+
this.refreshWorker.on("close", () => {
|
|
119
|
+
this.disposed = true;
|
|
120
|
+
});
|
|
119
121
|
// Transfer informations to the worker
|
|
120
|
-
this.refreshWorker.
|
|
122
|
+
this.refreshWorker.send({
|
|
121
123
|
action: "start",
|
|
122
124
|
kuzzle: {
|
|
123
125
|
config: global.kuzzle.config,
|
|
@@ -138,7 +140,7 @@ class ClusterIdCardHandler {
|
|
|
138
140
|
* Helper method to mock worker instantiation in unit tests
|
|
139
141
|
*/
|
|
140
142
|
constructWorker(path) {
|
|
141
|
-
return
|
|
143
|
+
return (0, child_process_1.fork)(path);
|
|
142
144
|
}
|
|
143
145
|
/**
|
|
144
146
|
* Start refreshing the ID Card before the worker starts to ensure the ID Card
|
|
@@ -163,9 +165,20 @@ class ClusterIdCardHandler {
|
|
|
163
165
|
});
|
|
164
166
|
}
|
|
165
167
|
async dispose() {
|
|
168
|
+
if (this.disposed) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
166
171
|
this.disposed = true;
|
|
167
|
-
if (this.refreshWorker
|
|
168
|
-
this.refreshWorker.
|
|
172
|
+
if (this.refreshWorker &&
|
|
173
|
+
this.refreshWorker.connected &&
|
|
174
|
+
!this.refreshWorker.killed &&
|
|
175
|
+
this.refreshWorker.channel) {
|
|
176
|
+
try {
|
|
177
|
+
this.refreshWorker.send({ action: "dispose" });
|
|
178
|
+
}
|
|
179
|
+
catch (e) {
|
|
180
|
+
// It could happens that the worker has been killed before the dispose causing send to fail
|
|
181
|
+
}
|
|
169
182
|
}
|
|
170
183
|
}
|
|
171
184
|
/**
|
package/lib/cluster/node.js
CHANGED
|
@@ -249,6 +249,15 @@ class ClusterNode {
|
|
|
249
249
|
this.command.dispose();
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Notify other nodes to not evict this node
|
|
254
|
+
*
|
|
255
|
+
* @param {bool} evictionPrevented
|
|
256
|
+
*/
|
|
257
|
+
preventEviction(evictionPrevented) {
|
|
258
|
+
this.publisher.sendNodePreventEviction(evictionPrevented);
|
|
259
|
+
}
|
|
260
|
+
|
|
252
261
|
/**
|
|
253
262
|
* Adds a new remote node, and subscribes to it.
|
|
254
263
|
* @param {string} id - remote node ID
|
|
@@ -678,6 +687,10 @@ class ClusterNode {
|
|
|
678
687
|
* Registers ask events
|
|
679
688
|
*/
|
|
680
689
|
registerAskEvents() {
|
|
690
|
+
global.kuzzle.onAsk("cluster:node:preventEviction", (state) => {
|
|
691
|
+
this.preventEviction(state);
|
|
692
|
+
});
|
|
693
|
+
|
|
681
694
|
/**
|
|
682
695
|
* Removes a room from the full state, and only for this node.
|
|
683
696
|
* Removes the room from Koncorde if, and only if, no other node uses it.
|
|
@@ -816,7 +829,11 @@ class ClusterNode {
|
|
|
816
829
|
this.onAuthStrategyRemoved(name, pluginName)
|
|
817
830
|
);
|
|
818
831
|
|
|
819
|
-
global.kuzzle.on("admin:afterDump", (
|
|
832
|
+
global.kuzzle.on("admin:afterDump", (request) => {
|
|
833
|
+
const suffix = request.getString("suffix", "manual-api-action");
|
|
834
|
+
|
|
835
|
+
return this.onDumpRequest(suffix);
|
|
836
|
+
});
|
|
820
837
|
|
|
821
838
|
global.kuzzle.on("admin:afterResetSecurity", () => this.onSecurityReset());
|
|
822
839
|
|
package/lib/cluster/publisher.js
CHANGED
|
@@ -270,6 +270,16 @@ class ClusterPublisher {
|
|
|
270
270
|
return this.send("NodeShutdown", { nodeId });
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Publishes an event about the node being in debug mode
|
|
275
|
+
* @param {bool} evictionPrevented
|
|
276
|
+
*
|
|
277
|
+
* @returns {Long} ID of the message sent
|
|
278
|
+
*/
|
|
279
|
+
sendNodePreventEviction(evictionPrevented) {
|
|
280
|
+
return this.send("NodePreventEviction", { evictionPrevented });
|
|
281
|
+
}
|
|
282
|
+
|
|
273
283
|
/**
|
|
274
284
|
* Publishes an event about a node being evicted
|
|
275
285
|
*
|
|
@@ -55,6 +55,8 @@ class ClusterSubscriber {
|
|
|
55
55
|
this.remoteNodeIP = remoteNodeIP;
|
|
56
56
|
this.remoteNodeAddress = `tcp://${remoteNodeIP}:${this.localNode.config.ports.sync}`;
|
|
57
57
|
this.remoteNodeId = remoteNodeId;
|
|
58
|
+
// Used in debug mode when the node might be slower
|
|
59
|
+
this.remoteNodeEvictionPrevented = false;
|
|
58
60
|
this.socket = null;
|
|
59
61
|
this.protoroot = null;
|
|
60
62
|
|
|
@@ -87,6 +89,7 @@ class ClusterSubscriber {
|
|
|
87
89
|
NewAuthStrategy: this.handleNewAuthStrategy,
|
|
88
90
|
NewRealtimeRoom: this.handleNewRealtimeRoom,
|
|
89
91
|
NodeEvicted: this.handleNodeEviction,
|
|
92
|
+
NodePreventEviction: this.handleNodePreventEviction,
|
|
90
93
|
NodeShutdown: this.handleNodeShutdown,
|
|
91
94
|
RefreshIndexCache: this.handleRefreshIndexCache,
|
|
92
95
|
RefreshValidators: this.handleRefreshValidators,
|
|
@@ -229,6 +232,10 @@ class ClusterSubscriber {
|
|
|
229
232
|
}
|
|
230
233
|
}
|
|
231
234
|
|
|
235
|
+
async handleNodePreventEviction(message) {
|
|
236
|
+
this.remoteNodeEvictionPrevented = message.evictionPrevented;
|
|
237
|
+
}
|
|
238
|
+
|
|
232
239
|
/**
|
|
233
240
|
* Handles a heartbeat from the remote node
|
|
234
241
|
*
|
|
@@ -673,7 +680,7 @@ class ClusterSubscriber {
|
|
|
673
680
|
* to recover, otherwise we evict it from the cluster.
|
|
674
681
|
*/
|
|
675
682
|
async checkHeartbeat() {
|
|
676
|
-
if (this.state === stateEnum.EVICTED) {
|
|
683
|
+
if (this.state === stateEnum.EVICTED || this.remoteNodeEvictionPrevented) {
|
|
677
684
|
return;
|
|
678
685
|
}
|
|
679
686
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const { isMainThread, parentPort } = require("worker_threads");
|
|
4
3
|
const Redis = require("../../service/cache/redis");
|
|
5
4
|
|
|
6
5
|
class IDCardRenewer {
|
|
7
6
|
constructor() {
|
|
8
|
-
this.parentPort = parentPort;
|
|
9
7
|
this.redis = null;
|
|
10
8
|
this.refreshTimer = null;
|
|
11
9
|
this.nodeIdKey = null;
|
|
@@ -50,7 +48,7 @@ class IDCardRenewer {
|
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
// Notify that the worker is running and updating the ID Card
|
|
53
|
-
|
|
51
|
+
process.send({ initialized: true });
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
async initRedis(config, name) {
|
|
@@ -73,13 +71,13 @@ class IDCardRenewer {
|
|
|
73
71
|
// => this node is too slow, we need to remove it from the cluster
|
|
74
72
|
if (refreshed === 0) {
|
|
75
73
|
await this.dispose();
|
|
76
|
-
|
|
74
|
+
process.send({
|
|
77
75
|
error: "Node too slow: ID card expired",
|
|
78
76
|
});
|
|
79
77
|
}
|
|
80
78
|
} catch (error) {
|
|
81
79
|
await this.dispose();
|
|
82
|
-
|
|
80
|
+
process.send({
|
|
83
81
|
error: `Failed to refresh ID Card: ${error.message}`,
|
|
84
82
|
});
|
|
85
83
|
}
|
|
@@ -112,26 +110,25 @@ class IDCardRenewer {
|
|
|
112
110
|
}
|
|
113
111
|
}
|
|
114
112
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
113
|
+
const idCardRenewer = new IDCardRenewer();
|
|
114
|
+
|
|
115
|
+
process.on("message", async (message) => {
|
|
116
|
+
if (message.action === "start") {
|
|
117
|
+
// Simulate basic global Kuzzle Context
|
|
118
|
+
global.kuzzle = { ...message.kuzzle };
|
|
119
|
+
global.kuzzle.log = {
|
|
120
|
+
debug: console.debug, // eslint-disable-line no-console
|
|
121
|
+
error: console.error, // eslint-disable-line no-console
|
|
122
|
+
info: console.info, // eslint-disable-line no-console
|
|
123
|
+
warn: console.warn, // eslint-disable-line no-console
|
|
124
|
+
};
|
|
125
|
+
// Should never throw
|
|
126
|
+
await idCardRenewer.init(message);
|
|
127
|
+
} else if (message.action === "dispose") {
|
|
128
|
+
// Should never throw
|
|
129
|
+
await idCardRenewer.dispose();
|
|
130
|
+
process.exit(0);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
136
133
|
|
|
137
134
|
module.exports = { IDCardRenewer };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { JSONObject } from "kuzzle-sdk";
|
|
2
|
+
export declare class KuzzleDebugger {
|
|
3
|
+
private inspector;
|
|
4
|
+
private debuggerStatus;
|
|
5
|
+
/**
|
|
6
|
+
* Map<eventName, Set<connectionId>>
|
|
7
|
+
*/
|
|
8
|
+
private events;
|
|
9
|
+
init(): Promise<void>;
|
|
10
|
+
registerAsks(): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Connect the debugger
|
|
13
|
+
*/
|
|
14
|
+
enable(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Disconnect the debugger and clears all the events listeners
|
|
17
|
+
*/
|
|
18
|
+
disable(): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Trigger action from debugger directly following the Chrome Debug Protocol
|
|
21
|
+
* See: https://chromedevtools.github.io/devtools-protocol/v8/
|
|
22
|
+
*/
|
|
23
|
+
post(method: string, params?: JSONObject): Promise<JSONObject>;
|
|
24
|
+
/**
|
|
25
|
+
* Make the websocket connection listen and receive events from Chrome Debug Protocol
|
|
26
|
+
* See events from: https://chromedevtools.github.io/devtools-protocol/v8/
|
|
27
|
+
*/
|
|
28
|
+
addListener(event: string, connectionId: string): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Remove the websocket connection from the events" listeners
|
|
31
|
+
*/
|
|
32
|
+
removeListener(event: string, connectionId: string): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Execute a method using the Chrome Debug Protocol
|
|
35
|
+
* @param method Chrome Debug Protocol method to execute
|
|
36
|
+
* @param params
|
|
37
|
+
* @returns
|
|
38
|
+
*/
|
|
39
|
+
private inspectorPost;
|
|
40
|
+
/**
|
|
41
|
+
* Sends a direct notification to a websocket connection without having to listen to a specific room
|
|
42
|
+
*/
|
|
43
|
+
private notifyConnection;
|
|
44
|
+
private notifyGlobalListeners;
|
|
45
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
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 (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.KuzzleDebugger = void 0;
|
|
30
|
+
const inspector_1 = __importDefault(require("inspector"));
|
|
31
|
+
const kerror = __importStar(require("../../kerror"));
|
|
32
|
+
const get_1 = __importDefault(require("lodash/get"));
|
|
33
|
+
const DEBUGGER_EVENT = "kuzzle-debugger-event";
|
|
34
|
+
class KuzzleDebugger {
|
|
35
|
+
constructor() {
|
|
36
|
+
this.debuggerStatus = false;
|
|
37
|
+
/**
|
|
38
|
+
* Map<eventName, Set<connectionId>>
|
|
39
|
+
*/
|
|
40
|
+
this.events = new Map();
|
|
41
|
+
}
|
|
42
|
+
async init() {
|
|
43
|
+
this.inspector = new inspector_1.default.Session();
|
|
44
|
+
// Remove connection id from the list of listeners for each event
|
|
45
|
+
global.kuzzle.on("connection:remove", (connectionId) => {
|
|
46
|
+
if (!this.debuggerStatus) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
for (const listener of this.events.values()) {
|
|
50
|
+
listener.delete(connectionId);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
this.inspector.on("inspectorNotification", async (payload) => {
|
|
54
|
+
if (!this.debuggerStatus) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
await this.notifyGlobalListeners(payload.method, payload);
|
|
58
|
+
const listeners = this.events.get(payload.method);
|
|
59
|
+
if (!listeners) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const promises = [];
|
|
63
|
+
for (const connectionId of listeners) {
|
|
64
|
+
promises.push(this.notifyConnection(connectionId, DEBUGGER_EVENT, {
|
|
65
|
+
event: payload.method,
|
|
66
|
+
result: payload,
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
// No need to catch, notify is already try-catched
|
|
70
|
+
await Promise.all(promises);
|
|
71
|
+
});
|
|
72
|
+
await this.registerAsks();
|
|
73
|
+
}
|
|
74
|
+
async registerAsks() {
|
|
75
|
+
global.kuzzle.onAsk("core:debugger:enable", () => this.enable());
|
|
76
|
+
global.kuzzle.onAsk("core:debugger:disable", () => this.disable());
|
|
77
|
+
global.kuzzle.onAsk("core:debugger:post", (method, params) => this.post(method, params));
|
|
78
|
+
global.kuzzle.onAsk("core:debugger:isEnabled", () => this.debuggerStatus);
|
|
79
|
+
global.kuzzle.onAsk("core:debugger:removeListener", (event, connectionId) => this.removeListener(event, connectionId));
|
|
80
|
+
global.kuzzle.onAsk("core:debugger:addListener", (event, connectionId) => this.addListener(event, connectionId));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Connect the debugger
|
|
84
|
+
*/
|
|
85
|
+
async enable() {
|
|
86
|
+
if (this.debuggerStatus) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
this.inspector.connect();
|
|
90
|
+
this.debuggerStatus = true;
|
|
91
|
+
await global.kuzzle.ask("cluster:node:preventEviction", true);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Disconnect the debugger and clears all the events listeners
|
|
95
|
+
*/
|
|
96
|
+
async disable() {
|
|
97
|
+
if (!this.debuggerStatus) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
this.inspector.disconnect();
|
|
101
|
+
this.debuggerStatus = false;
|
|
102
|
+
await global.kuzzle.ask("cluster:node:preventEviction", false);
|
|
103
|
+
this.events.clear();
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Trigger action from debugger directly following the Chrome Debug Protocol
|
|
107
|
+
* See: https://chromedevtools.github.io/devtools-protocol/v8/
|
|
108
|
+
*/
|
|
109
|
+
async post(method, params = {}) {
|
|
110
|
+
if (!this.debuggerStatus) {
|
|
111
|
+
throw kerror.get("core", "debugger", "not_enabled");
|
|
112
|
+
}
|
|
113
|
+
if (!(0, get_1.default)(global.kuzzle.config, "security.debug.native_debug_protocol")) {
|
|
114
|
+
throw kerror.get("core", "debugger", "native_debug_protocol_usage_denied");
|
|
115
|
+
}
|
|
116
|
+
// Always disable report progress because this params causes a segfault.
|
|
117
|
+
// The reason this happens is because the inspector is running inside the same thread
|
|
118
|
+
// as the Kuzzle Process and reportProgress forces the inspector to send events
|
|
119
|
+
// to the main thread, while it is being inspected by the HeapProfiler, which causes javascript code
|
|
120
|
+
// to be executed as the HeapProfiler is running, which causes a segfault.
|
|
121
|
+
// See: https://github.com/nodejs/node/issues/44634
|
|
122
|
+
params.reportProgress = false;
|
|
123
|
+
return this.inspectorPost(method, params);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Make the websocket connection listen and receive events from Chrome Debug Protocol
|
|
127
|
+
* See events from: https://chromedevtools.github.io/devtools-protocol/v8/
|
|
128
|
+
*/
|
|
129
|
+
async addListener(event, connectionId) {
|
|
130
|
+
if (!this.debuggerStatus) {
|
|
131
|
+
throw kerror.get("core", "debugger", "not_enabled");
|
|
132
|
+
}
|
|
133
|
+
let listeners = this.events.get(event);
|
|
134
|
+
if (!listeners) {
|
|
135
|
+
listeners = new Set();
|
|
136
|
+
this.events.set(event, listeners);
|
|
137
|
+
}
|
|
138
|
+
listeners.add(connectionId);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Remove the websocket connection from the events" listeners
|
|
142
|
+
*/
|
|
143
|
+
async removeListener(event, connectionId) {
|
|
144
|
+
if (!this.debuggerStatus) {
|
|
145
|
+
throw kerror.get("core", "debugger", "not_enabled");
|
|
146
|
+
}
|
|
147
|
+
const listeners = this.events.get(event);
|
|
148
|
+
if (listeners) {
|
|
149
|
+
listeners.delete(connectionId);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Execute a method using the Chrome Debug Protocol
|
|
154
|
+
* @param method Chrome Debug Protocol method to execute
|
|
155
|
+
* @param params
|
|
156
|
+
* @returns
|
|
157
|
+
*/
|
|
158
|
+
async inspectorPost(method, params) {
|
|
159
|
+
if (!this.debuggerStatus) {
|
|
160
|
+
throw kerror.get("core", "debugger", "not_enabled");
|
|
161
|
+
}
|
|
162
|
+
let resolve;
|
|
163
|
+
const promise = new Promise((res) => {
|
|
164
|
+
resolve = res;
|
|
165
|
+
});
|
|
166
|
+
this.inspector.post(method, params, (err, res) => {
|
|
167
|
+
if (err) {
|
|
168
|
+
resolve({
|
|
169
|
+
error: JSON.stringify(Object.getOwnPropertyDescriptors(err)),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
resolve(res);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
return promise;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Sends a direct notification to a websocket connection without having to listen to a specific room
|
|
180
|
+
*/
|
|
181
|
+
async notifyConnection(connectionId, event, payload) {
|
|
182
|
+
global.kuzzle.entryPoint._notify({
|
|
183
|
+
channels: [event],
|
|
184
|
+
connectionId,
|
|
185
|
+
payload,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
async notifyGlobalListeners(event, payload) {
|
|
189
|
+
const listeners = this.events.get("*");
|
|
190
|
+
if (!listeners) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const promises = [];
|
|
194
|
+
for (const connectionId of listeners) {
|
|
195
|
+
promises.push(this.notifyConnection(connectionId, DEBUGGER_EVENT, {
|
|
196
|
+
event,
|
|
197
|
+
result: payload,
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
// No need to catch, notify is already try-catched
|
|
201
|
+
await Promise.all(promises);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
exports.KuzzleDebugger = KuzzleDebugger;
|
|
205
|
+
//# sourceMappingURL=kuzzleDebugger.js.map
|
|
@@ -228,7 +228,7 @@ class DumpGenerator {
|
|
|
228
228
|
while (dumps.length >= config.history.reports) {
|
|
229
229
|
const dir = dumps.shift().path;
|
|
230
230
|
|
|
231
|
-
fs.
|
|
231
|
+
fs.rmdirSync(dir, { recursive: true });
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
for (let i = 0; i < dumps.length - config.history.coredump; i++) {
|
package/lib/kuzzle/kuzzle.js
CHANGED
|
@@ -80,6 +80,7 @@ const package_json_1 = require("../../package.json");
|
|
|
80
80
|
const name_generator_1 = require("../util/name-generator");
|
|
81
81
|
const openapi_1 = require("../api/openapi");
|
|
82
82
|
const crypto_1 = require("../util/crypto");
|
|
83
|
+
const kuzzleDebugger_1 = require("../core/debug/kuzzleDebugger");
|
|
83
84
|
exports.BACKEND_IMPORT_KEY = "backend:init:import";
|
|
84
85
|
let _kuzzle = null;
|
|
85
86
|
Reflect.defineProperty(global, "kuzzle", {
|
|
@@ -119,6 +120,7 @@ class Kuzzle extends kuzzleEventEmitter_1.default {
|
|
|
119
120
|
this.dumpGenerator = new dumpGenerator_1.default();
|
|
120
121
|
this.vault = null;
|
|
121
122
|
this.asyncStore = new asyncStore_1.default();
|
|
123
|
+
this.debugger = new kuzzleDebugger_1.KuzzleDebugger();
|
|
122
124
|
this.version = package_json_1.version;
|
|
123
125
|
this.importTypes = {
|
|
124
126
|
fixtures: this.importFixtures.bind(this),
|
|
@@ -146,6 +148,7 @@ class Kuzzle extends kuzzleEventEmitter_1.default {
|
|
|
146
148
|
regExpEngine: this.config.realtime.pcreSupport ? "js" : "re2",
|
|
147
149
|
seed: this.config.internal.hash.seed,
|
|
148
150
|
});
|
|
151
|
+
await this.debugger.init();
|
|
149
152
|
await new cacheEngine_1.default().init();
|
|
150
153
|
await new storageEngine_1.default().init();
|
|
151
154
|
await new realtime_1.default().init();
|
|
@@ -681,7 +681,7 @@ class ElasticSearch extends Service {
|
|
|
681
681
|
index,
|
|
682
682
|
collection,
|
|
683
683
|
content,
|
|
684
|
-
{ id, refresh, userId = null } = {}
|
|
684
|
+
{ id, refresh, userId = null, injectKuzzleMeta = true } = {}
|
|
685
685
|
) {
|
|
686
686
|
assertIsObject(content);
|
|
687
687
|
|
|
@@ -697,12 +697,14 @@ class ElasticSearch extends Service {
|
|
|
697
697
|
assertWellFormedRefresh(esRequest);
|
|
698
698
|
|
|
699
699
|
// Add metadata
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
700
|
+
if (injectKuzzleMeta) {
|
|
701
|
+
esRequest.body._kuzzle_info = {
|
|
702
|
+
author: getKuid(userId),
|
|
703
|
+
createdAt: Date.now(),
|
|
704
|
+
updatedAt: null,
|
|
705
|
+
updater: null,
|
|
706
|
+
};
|
|
707
|
+
}
|
|
706
708
|
|
|
707
709
|
debug("Create document: %o", esRequest);
|
|
708
710
|
|
|
@@ -789,7 +791,7 @@ class ElasticSearch extends Service {
|
|
|
789
791
|
collection,
|
|
790
792
|
id,
|
|
791
793
|
content,
|
|
792
|
-
{ refresh, userId = null, retryOnConflict } = {}
|
|
794
|
+
{ refresh, userId = null, retryOnConflict, injectKuzzleMeta = true } = {}
|
|
793
795
|
) {
|
|
794
796
|
const esRequest = {
|
|
795
797
|
_source: true,
|
|
@@ -804,11 +806,13 @@ class ElasticSearch extends Service {
|
|
|
804
806
|
assertNoRouting(esRequest);
|
|
805
807
|
assertWellFormedRefresh(esRequest);
|
|
806
808
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
809
|
+
if (injectKuzzleMeta) {
|
|
810
|
+
// Add metadata
|
|
811
|
+
esRequest.body.doc._kuzzle_info = {
|
|
812
|
+
updatedAt: Date.now(),
|
|
813
|
+
updater: getKuid(userId),
|
|
814
|
+
};
|
|
815
|
+
}
|
|
812
816
|
|
|
813
817
|
debug("Update document: %o", esRequest);
|
|
814
818
|
|
|
@@ -841,7 +845,13 @@ class ElasticSearch extends Service {
|
|
|
841
845
|
collection,
|
|
842
846
|
id,
|
|
843
847
|
content,
|
|
844
|
-
{
|
|
848
|
+
{
|
|
849
|
+
defaultValues = {},
|
|
850
|
+
refresh,
|
|
851
|
+
userId = null,
|
|
852
|
+
retryOnConflict,
|
|
853
|
+
injectKuzzleMeta = true,
|
|
854
|
+
} = {}
|
|
845
855
|
) {
|
|
846
856
|
const esRequest = {
|
|
847
857
|
_source: true,
|
|
@@ -863,14 +873,16 @@ class ElasticSearch extends Service {
|
|
|
863
873
|
const user = getKuid(userId);
|
|
864
874
|
const now = Date.now();
|
|
865
875
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
876
|
+
if (injectKuzzleMeta) {
|
|
877
|
+
esRequest.body.doc._kuzzle_info = {
|
|
878
|
+
updatedAt: now,
|
|
879
|
+
updater: user,
|
|
880
|
+
};
|
|
881
|
+
esRequest.body.upsert._kuzzle_info = {
|
|
882
|
+
author: user,
|
|
883
|
+
createdAt: now,
|
|
884
|
+
};
|
|
885
|
+
}
|
|
874
886
|
|
|
875
887
|
debug("Upsert document: %o", esRequest);
|
|
876
888
|
|
|
@@ -904,7 +916,7 @@ class ElasticSearch extends Service {
|
|
|
904
916
|
collection,
|
|
905
917
|
id,
|
|
906
918
|
content,
|
|
907
|
-
{ refresh, userId = null } = {}
|
|
919
|
+
{ refresh, userId = null, injectKuzzleMeta = true } = {}
|
|
908
920
|
) {
|
|
909
921
|
const alias = this._getAlias(index, collection);
|
|
910
922
|
const esRequest = {
|
|
@@ -917,13 +929,15 @@ class ElasticSearch extends Service {
|
|
|
917
929
|
assertNoRouting(esRequest);
|
|
918
930
|
assertWellFormedRefresh(esRequest);
|
|
919
931
|
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
932
|
+
if (injectKuzzleMeta) {
|
|
933
|
+
// Add metadata
|
|
934
|
+
esRequest.body._kuzzle_info = {
|
|
935
|
+
author: getKuid(userId),
|
|
936
|
+
createdAt: Date.now(),
|
|
937
|
+
updatedAt: Date.now(),
|
|
938
|
+
updater: getKuid(userId),
|
|
939
|
+
};
|
|
940
|
+
}
|
|
927
941
|
|
|
928
942
|
try {
|
|
929
943
|
const { body: exists } = await this._client.exists({ id, index: alias });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KuzzleRequest, KDocument, JSONObject } from "../../../";
|
|
1
|
+
import { KuzzleRequest, KDocument, JSONObject, PipeEventHandler } from "../../../";
|
|
2
2
|
/**
|
|
3
3
|
* Events with documents only having the `_id`
|
|
4
4
|
*/
|
|
@@ -23,4 +23,24 @@ export type EventGenericDocumentAfterWrite<KDocumentContent = JSONObject> = Even
|
|
|
23
23
|
export type EventGenericDocumentBeforeUpdate<KDocumentContent = JSONObject> = EventGenericDocument<"beforeUpdate", KDocumentContent>;
|
|
24
24
|
export type EventGenericDocumentAfterUpdate<KDocumentContent = JSONObject> = EventGenericDocument<"afterUpdate", KDocumentContent>;
|
|
25
25
|
export type EventGenericDocumentAfterGet<KDocumentContent = JSONObject> = EventGenericDocument<"afterGet", KDocumentContent>;
|
|
26
|
+
export type EventGenericDocumentInjectMetadata = {
|
|
27
|
+
name: `generic:document:injectMetadata`;
|
|
28
|
+
args: [
|
|
29
|
+
{
|
|
30
|
+
/**
|
|
31
|
+
* Kuzzle Request that triggered the event
|
|
32
|
+
*/
|
|
33
|
+
request: KuzzleRequest;
|
|
34
|
+
/**
|
|
35
|
+
* Metadata of the document
|
|
36
|
+
*/
|
|
37
|
+
metadata: JSONObject;
|
|
38
|
+
/**
|
|
39
|
+
* Default metadata of the document.
|
|
40
|
+
* Only used when calling document:upsert.
|
|
41
|
+
*/
|
|
42
|
+
defaultMetadata?: JSONObject;
|
|
43
|
+
}
|
|
44
|
+
];
|
|
45
|
+
} & PipeEventHandler;
|
|
26
46
|
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kuzzle",
|
|
3
3
|
"author": "The Kuzzle Team <support@kuzzle.io>",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.21.0",
|
|
5
5
|
"description": "Kuzzle is an open-source solution that handles all the data management through a secured API, with a large choice of protocols.",
|
|
6
6
|
"bin": "bin/start-kuzzle-server",
|
|
7
7
|
"scripts": {
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
/// <reference types="node" />
|
|
3
|
-
import EventEmitter from "events";
|
|
4
|
-
import Inspector from "inspector";
|
|
5
|
-
export type DebugModuleOptions = {
|
|
6
|
-
methods?: string[];
|
|
7
|
-
events?: string[];
|
|
8
|
-
};
|
|
9
|
-
export declare abstract class DebugModule extends EventEmitter {
|
|
10
|
-
name: string;
|
|
11
|
-
methods: string[];
|
|
12
|
-
events: string[];
|
|
13
|
-
/**
|
|
14
|
-
* Called when the module is loaded, after the debugger has been enabled
|
|
15
|
-
*/
|
|
16
|
-
abstract init(inspector: Inspector.Session): Promise<void>;
|
|
17
|
-
/**
|
|
18
|
-
* Called when the module should be cleaned up.
|
|
19
|
-
* - After the Debug Controller has been disabled
|
|
20
|
-
* - Before the debugger is disconnected
|
|
21
|
-
*/
|
|
22
|
-
abstract cleanup(): Promise<void>;
|
|
23
|
-
constructor(name: string, options?: DebugModuleOptions);
|
|
24
|
-
}
|
package/lib/types/DebugModule.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DebugModule = void 0;
|
|
7
|
-
const events_1 = __importDefault(require("events"));
|
|
8
|
-
class DebugModule extends events_1.default {
|
|
9
|
-
constructor(name, options = {}) {
|
|
10
|
-
super();
|
|
11
|
-
this.name = name;
|
|
12
|
-
this.methods = options.methods || [];
|
|
13
|
-
this.events = options.events || [];
|
|
14
|
-
if (!this.name || this.name.length === 0) {
|
|
15
|
-
throw new Error("DebugModule should have a name");
|
|
16
|
-
}
|
|
17
|
-
if (this.name.charAt(0) !== this.name.charAt(0).toUpperCase()) {
|
|
18
|
-
throw new Error(`Debug Module name "${name}" should start with an uppercase letter`);
|
|
19
|
-
}
|
|
20
|
-
for (const event of this.events) {
|
|
21
|
-
if (event.length === 0) {
|
|
22
|
-
throw new Error(`Event name should not be empty for "${name}"`);
|
|
23
|
-
}
|
|
24
|
-
if (event.charAt(0) !== event.charAt(0).toLowerCase()) {
|
|
25
|
-
throw new Error(`Event name "${event}" should start with a lowercase letter for module "${name}"`);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
for (const method of this.methods) {
|
|
29
|
-
if (method.length === 0) {
|
|
30
|
-
throw new Error(`Method name should not be empty for Debug Module "${name}"`);
|
|
31
|
-
}
|
|
32
|
-
if (method.charAt(0) !== method.charAt(0).toLowerCase()) {
|
|
33
|
-
throw new Error(`Method name "${method}" should start with a lowercase letter for module "${name}"`);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
exports.DebugModule = DebugModule;
|
|
39
|
-
//# sourceMappingURL=DebugModule.js.map
|