kuzzle 2.60.0-beta.4 → 2.60.0-beta.5
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/dist/lib/api/controllers/adminController.d.ts +7 -1
- package/dist/lib/api/controllers/adminController.js +9 -2
- package/dist/lib/api/funnel.js +12 -1
- package/dist/lib/api/httpRoutes.js +52 -20
- package/dist/lib/api/request/kuzzleRequest.d.ts +7 -2
- package/dist/lib/api/request/kuzzleRequest.js +20 -10
- package/dist/lib/api/request/requestContext.d.ts +1 -1
- package/dist/lib/api/request/requestResponse.js +2 -1
- package/dist/lib/config/index.js +60 -3
- package/dist/lib/core/backend/backend.d.ts +3 -2
- package/dist/lib/core/backend/internalLogger.d.ts +6 -0
- package/dist/lib/core/backend/internalLogger.js +6 -0
- package/dist/lib/core/network/router.d.ts +18 -0
- package/dist/lib/core/network/router.js +44 -10
- package/dist/lib/core/security/authTokenSettings.d.ts +21 -0
- package/dist/lib/core/security/authTokenSettings.js +61 -0
- package/dist/lib/core/security/tokenRepository.js +8 -14
- package/dist/lib/core/shared/KoncordeWrapper.d.ts +9 -0
- package/dist/lib/core/shared/KoncordeWrapper.js +8 -8
- package/dist/lib/core/storage/clientAdapter.js +7 -2
- package/dist/lib/kuzzle/Logger.d.ts +4 -0
- package/dist/lib/kuzzle/Logger.js +9 -2
- package/dist/lib/kuzzle/internalIndexHandler.js +4 -5
- package/dist/lib/kuzzle/kuzzle.d.ts +17 -0
- package/dist/lib/kuzzle/kuzzle.js +62 -0
- package/dist/lib/kuzzle/vault.d.ts +13 -0
- package/dist/lib/kuzzle/vault.js +25 -7
- package/dist/lib/service/storage/7/elasticsearch.d.ts +6 -0
- package/dist/lib/service/storage/7/elasticsearch.js +23 -14
- package/dist/lib/service/storage/8/elasticsearch.d.ts +6 -0
- package/dist/lib/service/storage/8/elasticsearch.js +23 -14
- package/dist/lib/types/config/KuzzleConfiguration.d.ts +7 -0
- package/dist/lib/types/config/SecurityConfiguration.d.ts +4 -3
- package/dist/lib/types/controllers/ControllerDefinition.d.ts +4 -4
- package/dist/lib/util/mutex.d.ts +3 -3
- package/dist/lib/util/mutex.js +1 -1
- package/dist/package.json +1 -1
- package/package.json +1 -1
|
@@ -44,7 +44,13 @@ declare class AdminController extends NativeController {
|
|
|
44
44
|
shutdown(): Promise<{
|
|
45
45
|
acknowledge: boolean;
|
|
46
46
|
}>;
|
|
47
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Always waits for the import, so that a failure is reported to the caller:
|
|
49
|
+
* `refresh=false` only skips waiting for the documents to be indexed.
|
|
50
|
+
*/
|
|
51
|
+
loadFixtures(request: KuzzleRequest): Promise<{
|
|
52
|
+
acknowledge: boolean;
|
|
53
|
+
}>;
|
|
48
54
|
loadMappings(request: KuzzleRequest): Promise<{
|
|
49
55
|
acknowledge: boolean;
|
|
50
56
|
}>;
|
|
@@ -161,15 +161,22 @@ class AdminController extends baseController_1.NativeController {
|
|
|
161
161
|
if (this.shuttingDown) {
|
|
162
162
|
throw kerror.get("api", "process", "action_locked", "Kuzzle is already shutting down.");
|
|
163
163
|
}
|
|
164
|
+
this.shuttingDown = true;
|
|
164
165
|
global.kuzzle.shutdown();
|
|
165
166
|
return { acknowledge: true };
|
|
166
167
|
}
|
|
167
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Always waits for the import, so that a failure is reported to the caller:
|
|
170
|
+
* `refresh=false` only skips waiting for the documents to be indexed.
|
|
171
|
+
*/
|
|
172
|
+
async loadFixtures(request) {
|
|
168
173
|
const fixtures = request.getBody();
|
|
169
174
|
const refresh = request.getRefresh("wait_for");
|
|
170
|
-
|
|
175
|
+
await global.kuzzle.ask("core:storage:public:document:import", fixtures, {
|
|
171
176
|
refresh,
|
|
172
177
|
});
|
|
178
|
+
// The documented answer. 2.17.6 to 2.59 answered `null` instead.
|
|
179
|
+
return { acknowledge: true };
|
|
173
180
|
}
|
|
174
181
|
loadMappings(request) {
|
|
175
182
|
const mappings = request.getBody();
|
package/dist/lib/api/funnel.js
CHANGED
|
@@ -664,13 +664,24 @@ class Funnel {
|
|
|
664
664
|
catch (e) {
|
|
665
665
|
error = e;
|
|
666
666
|
debug("Error processing request %s: %a", request.id, error);
|
|
667
|
+
// processRequest does not dump: do it as the event-less path does.
|
|
668
|
+
// Rethrown, never falling through to the event-less execution below,
|
|
669
|
+
// which ran a failed (or pipe-rejected) request a second time.
|
|
670
|
+
this.handleErrorDump((0, thrown_1.causeOf)(e));
|
|
671
|
+
throw e;
|
|
667
672
|
}
|
|
668
673
|
finally {
|
|
669
|
-
|
|
674
|
+
// Not awaited, as it always was: the caller's result does not depend
|
|
675
|
+
// on it. Its rejection is logged instead of left unhandled.
|
|
676
|
+
global.kuzzle
|
|
677
|
+
.pipe("request:afterExecution", {
|
|
670
678
|
error: error,
|
|
671
679
|
request: request,
|
|
672
680
|
result: res,
|
|
673
681
|
success: error === undefined,
|
|
682
|
+
})
|
|
683
|
+
?.catch((pipeError) => {
|
|
684
|
+
this.logger.error(pipeError, `request:afterExecution pipe failed for plugin request ${request.id}`);
|
|
674
685
|
});
|
|
675
686
|
}
|
|
676
687
|
}
|
|
@@ -184,12 +184,6 @@ const routes = [
|
|
|
184
184
|
controller: "admin",
|
|
185
185
|
action: "resetDatabase",
|
|
186
186
|
},
|
|
187
|
-
{
|
|
188
|
-
verb: "post",
|
|
189
|
-
path: "/admin/_resetKuzzleData",
|
|
190
|
-
controller: "admin",
|
|
191
|
-
action: "resetKuzzleData",
|
|
192
|
-
},
|
|
193
187
|
{ verb: "post", path: "/admin/_dump", controller: "admin", action: "dump" },
|
|
194
188
|
{
|
|
195
189
|
verb: "post",
|
|
@@ -251,12 +245,6 @@ const routes = [
|
|
|
251
245
|
controller: "index",
|
|
252
246
|
action: "exists",
|
|
253
247
|
},
|
|
254
|
-
{
|
|
255
|
-
verb: "get",
|
|
256
|
-
path: "/:index/_autoRefresh",
|
|
257
|
-
controller: "index",
|
|
258
|
-
action: "getAutoRefresh",
|
|
259
|
-
},
|
|
260
248
|
{ verb: "get", path: "/_list", controller: "index", action: "list" },
|
|
261
249
|
{ verb: "get", path: "/_storageStats", controller: "index", action: "stats" },
|
|
262
250
|
{
|
|
@@ -1133,13 +1121,6 @@ const routes = [
|
|
|
1133
1121
|
controller: "ms",
|
|
1134
1122
|
action: "append",
|
|
1135
1123
|
},
|
|
1136
|
-
{
|
|
1137
|
-
verb: "post",
|
|
1138
|
-
path: "/ms/_bgrewriteaof",
|
|
1139
|
-
controller: "ms",
|
|
1140
|
-
action: "bgrewriteaof",
|
|
1141
|
-
},
|
|
1142
|
-
{ verb: "post", path: "/ms/_bgsave", controller: "ms", action: "bgsave" },
|
|
1143
1124
|
{ verb: "post", path: "/ms/_bitop/:_id", controller: "ms", action: "bitop" },
|
|
1144
1125
|
{ verb: "post", path: "/ms/_decr/:_id", controller: "ms", action: "decr" },
|
|
1145
1126
|
{
|
|
@@ -1514,7 +1495,7 @@ const routes = [
|
|
|
1514
1495
|
controller: "security",
|
|
1515
1496
|
action: "deleteApiKey",
|
|
1516
1497
|
openapi: {
|
|
1517
|
-
description: "Deletes a user API key, identified by its clear-text `key` or its `fingerprint` instead of its `_id`.
|
|
1498
|
+
description: "Deletes a user API key, identified by its clear-text `key` or its `fingerprint` instead of its `_id`. One of them must be provided; if several are given, `_id` is used first, then `key`, then `fingerprint`, and the others are ignored. A `key` in the query string is a secret written in the URL: prefer the `fingerprint`. If you are looking to manage your own API keys, use the `auth` controller instead.",
|
|
1518
1499
|
parameters: [
|
|
1519
1500
|
{
|
|
1520
1501
|
in: "path",
|
|
@@ -1572,6 +1553,57 @@ const routes = [
|
|
|
1572
1553
|
controller: "auth",
|
|
1573
1554
|
action: "deleteApiKey",
|
|
1574
1555
|
},
|
|
1556
|
+
{
|
|
1557
|
+
verb: "delete",
|
|
1558
|
+
path: "/api-keys",
|
|
1559
|
+
controller: "auth",
|
|
1560
|
+
action: "deleteApiKey",
|
|
1561
|
+
openapi: {
|
|
1562
|
+
description: "Deletes an API key of the current user, identified by its clear-text `key` or its `fingerprint` instead of its `_id`. One of them must be provided; if several are given, `_id` is used first, then `key`, then `fingerprint`, and the others are ignored. A `key` in the query string is a secret written in the URL: prefer the `fingerprint`.",
|
|
1563
|
+
parameters: [
|
|
1564
|
+
{
|
|
1565
|
+
in: "query",
|
|
1566
|
+
name: "key",
|
|
1567
|
+
schema: { type: "string" },
|
|
1568
|
+
required: false,
|
|
1569
|
+
description: "Clear-text API key to delete",
|
|
1570
|
+
},
|
|
1571
|
+
{
|
|
1572
|
+
in: "query",
|
|
1573
|
+
name: "fingerprint",
|
|
1574
|
+
schema: { type: "string" },
|
|
1575
|
+
required: false,
|
|
1576
|
+
description: "SHA256 fingerprint of the API key to delete",
|
|
1577
|
+
},
|
|
1578
|
+
{
|
|
1579
|
+
in: "query",
|
|
1580
|
+
name: "refresh",
|
|
1581
|
+
schema: {
|
|
1582
|
+
type: "string",
|
|
1583
|
+
enum: ["wait_for", "false"],
|
|
1584
|
+
default: "wait_for",
|
|
1585
|
+
},
|
|
1586
|
+
required: false,
|
|
1587
|
+
description: "If set to `wait_for`, waits for the deletion to be indexed before responding",
|
|
1588
|
+
},
|
|
1589
|
+
],
|
|
1590
|
+
responses: {
|
|
1591
|
+
200: {
|
|
1592
|
+
description: "The deleted API key ID",
|
|
1593
|
+
content: {
|
|
1594
|
+
"application/json": {
|
|
1595
|
+
schema: {
|
|
1596
|
+
type: "object",
|
|
1597
|
+
properties: {
|
|
1598
|
+
_id: { type: "string" },
|
|
1599
|
+
},
|
|
1600
|
+
},
|
|
1601
|
+
},
|
|
1602
|
+
},
|
|
1603
|
+
},
|
|
1604
|
+
},
|
|
1605
|
+
},
|
|
1606
|
+
},
|
|
1575
1607
|
// PUT (idempotent)
|
|
1576
1608
|
{ verb: "put", path: "/_me", controller: "auth", action: "updateSelf" },
|
|
1577
1609
|
{
|
|
@@ -385,12 +385,17 @@ export declare class KuzzleRequest {
|
|
|
385
385
|
*/
|
|
386
386
|
getDate(name: string, format?: string): string;
|
|
387
387
|
/**
|
|
388
|
-
* Gets a parameter from a request arguments and
|
|
388
|
+
* Gets a parameter from a request arguments and checks that it is an integer
|
|
389
|
+
* timestamp (e.g. milliseconds since Epoch).
|
|
390
|
+
*
|
|
391
|
+
* An integer string, such as an HTTP query string value, is accepted and
|
|
392
|
+
* converted. A date string (e.g. `"2026-10-27"`) is rejected.
|
|
389
393
|
*
|
|
390
394
|
* @param name parameter name.
|
|
391
395
|
* @throws {api.assert.missing_argument} If parameter not found and no default
|
|
392
396
|
* value provided
|
|
393
|
-
* @throws {api.assert.invalid_type} If parameter value is not
|
|
397
|
+
* @throws {api.assert.invalid_type} If parameter value is not an integer
|
|
398
|
+
* timestamp.
|
|
394
399
|
*/
|
|
395
400
|
getTimestamp(name: string): number;
|
|
396
401
|
/**
|
|
@@ -676,31 +676,41 @@ class KuzzleRequest {
|
|
|
676
676
|
if (args[name] === undefined) {
|
|
677
677
|
throw assertionError.get("missing_argument", name);
|
|
678
678
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
679
|
+
// A custom format replaces the ISO 8601 check: applying both rejected
|
|
680
|
+
// every value of a format that is not ISO 8601.
|
|
681
|
+
const isValid = format
|
|
682
|
+
? (0, moment_1.default)(args[name], format, true).isValid()
|
|
683
|
+
: (0, moment_1.default)(args[name], moment_1.default.ISO_8601).isValid();
|
|
684
|
+
if (!isValid) {
|
|
683
685
|
throw assertionError.get("invalid_type", name, "date");
|
|
684
686
|
}
|
|
685
687
|
return this.getString(name);
|
|
686
688
|
}
|
|
687
689
|
/**
|
|
688
|
-
* Gets a parameter from a request arguments and
|
|
690
|
+
* Gets a parameter from a request arguments and checks that it is an integer
|
|
691
|
+
* timestamp (e.g. milliseconds since Epoch).
|
|
692
|
+
*
|
|
693
|
+
* An integer string, such as an HTTP query string value, is accepted and
|
|
694
|
+
* converted. A date string (e.g. `"2026-10-27"`) is rejected.
|
|
689
695
|
*
|
|
690
696
|
* @param name parameter name.
|
|
691
697
|
* @throws {api.assert.missing_argument} If parameter not found and no default
|
|
692
698
|
* value provided
|
|
693
|
-
* @throws {api.assert.invalid_type} If parameter value is not
|
|
699
|
+
* @throws {api.assert.invalid_type} If parameter value is not an integer
|
|
700
|
+
* timestamp.
|
|
694
701
|
*/
|
|
695
702
|
getTimestamp(name) {
|
|
696
|
-
const
|
|
697
|
-
if (
|
|
703
|
+
const value = this.input.args[name];
|
|
704
|
+
if (value === undefined) {
|
|
698
705
|
throw assertionError.get("missing_argument", name);
|
|
699
706
|
}
|
|
700
|
-
|
|
707
|
+
const timestamp = typeof value === "string" && /^-?\d+$/.test(value)
|
|
708
|
+
? Number(value)
|
|
709
|
+
: value;
|
|
710
|
+
if (!Number.isSafeInteger(timestamp)) {
|
|
701
711
|
throw assertionError.get("invalid_type", name, "date");
|
|
702
712
|
}
|
|
703
|
-
return
|
|
713
|
+
return timestamp;
|
|
704
714
|
}
|
|
705
715
|
/**
|
|
706
716
|
* Returns the index specified in the request
|
|
@@ -127,7 +127,8 @@ class Headers {
|
|
|
127
127
|
case "host":
|
|
128
128
|
case "if-modified-since":
|
|
129
129
|
case "if-unmodified-since":
|
|
130
|
-
case "last-modified
|
|
130
|
+
case "last-modified":
|
|
131
|
+
case "location":
|
|
131
132
|
case "max-forwards":
|
|
132
133
|
case "proxy-authorization":
|
|
133
134
|
case "referer":
|
package/dist/lib/config/index.js
CHANGED
|
@@ -28,21 +28,27 @@ const assert_1 = __importDefault(require("assert"));
|
|
|
28
28
|
const node_util_1 = require("node:util");
|
|
29
29
|
const rc_1 = __importDefault(require("rc"));
|
|
30
30
|
const default_config_1 = __importDefault(require("./default.config"));
|
|
31
|
+
const authTokenSettings_1 = require("../core/security/authTokenSettings");
|
|
31
32
|
const package_json_1 = __importDefault(require("../../package.json"));
|
|
32
33
|
const kerror_1 = require("../kerror");
|
|
33
34
|
const safeObject_1 = require("../util/safeObject");
|
|
34
35
|
const bytes_1 = __importDefault(require("../util/bytes"));
|
|
35
36
|
const wrapped = (0, kerror_1.wrap)("core", "configuration");
|
|
36
37
|
function loadConfig() {
|
|
37
|
-
let
|
|
38
|
+
let raw;
|
|
38
39
|
try {
|
|
39
|
-
|
|
40
|
+
raw = (0, rc_1.default)("kuzzle", withoutTokenDefaults(default_config_1.default));
|
|
40
41
|
}
|
|
41
42
|
catch (e) {
|
|
42
43
|
throw wrapped.get("cannot_parse", e instanceof Error ? e.message : (0, node_util_1.inspect)(e));
|
|
43
44
|
}
|
|
44
45
|
// In place: what `unstringify` answers is the object it was given.
|
|
45
|
-
unstringify(
|
|
46
|
+
unstringify(raw);
|
|
47
|
+
const { authToken, deprecatedSettings, jwt } = resolveTokenSettings(raw.security);
|
|
48
|
+
const loaded = {
|
|
49
|
+
...raw,
|
|
50
|
+
security: { ...raw.security, authToken, jwt },
|
|
51
|
+
};
|
|
46
52
|
checkLimitsConfig(loaded);
|
|
47
53
|
checkHttpOptions(loaded);
|
|
48
54
|
checkWebSocketOptions(loaded);
|
|
@@ -56,6 +62,7 @@ function loadConfig() {
|
|
|
56
62
|
hash: {
|
|
57
63
|
seed: Buffer.from("^m&mOISKBvb1xpl1mRsrylaQXpjb&IJX"),
|
|
58
64
|
},
|
|
65
|
+
deprecatedSettings,
|
|
59
66
|
notifiableProtocols: [],
|
|
60
67
|
},
|
|
61
68
|
version: package_json_1.default.version,
|
|
@@ -66,6 +73,56 @@ function loadConfig() {
|
|
|
66
73
|
preprocessRedisOptions(config.services.memoryStorage);
|
|
67
74
|
return config;
|
|
68
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* The packaged defaults, minus the values of `security.authToken` and of its
|
|
78
|
+
* deprecated spelling `security.jwt`: both sections ship the same non-null
|
|
79
|
+
* defaults, so once `rc` has merged them nothing tells a user's
|
|
80
|
+
* `jwt.expiresIn` from the packaged one. Left empty here, they hold only what
|
|
81
|
+
* the user set, and {@link resolveTokenSettings} applies the defaults after.
|
|
82
|
+
*
|
|
83
|
+
* A shallow copy: the other sections stay the packaged objects `rc` merges
|
|
84
|
+
* into, as before.
|
|
85
|
+
*/
|
|
86
|
+
function withoutTokenDefaults(defaults) {
|
|
87
|
+
const authToken = {};
|
|
88
|
+
const jwt = {};
|
|
89
|
+
return { ...defaults, security: { ...defaults.security, authToken, jwt } };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Resolves the authentication token settings: for each one, the value set
|
|
93
|
+
* under `security.authToken` wins, then the one set under the deprecated
|
|
94
|
+
* `security.jwt`, then the packaged default. A `null` counts as unset, as the
|
|
95
|
+
* readers' `authToken.x ?? jwt.x` always did. Exception: `gracePeriod` set in
|
|
96
|
+
* both sections keeps the `jwt` one, as it always did (see
|
|
97
|
+
* {@link LEGACY_FIRST}).
|
|
98
|
+
*
|
|
99
|
+
* Both sections get the resolved values, so a reader of either — a plugin
|
|
100
|
+
* may still read `jwt` — sees the settings in effect. `deprecatedSettings`
|
|
101
|
+
* lists the `security.jwt.*` keys the user set, for the deprecation warning.
|
|
102
|
+
*/
|
|
103
|
+
function resolveTokenSettings(security) {
|
|
104
|
+
const packaged = default_config_1.default.security.authToken;
|
|
105
|
+
const fromPackage = packaged;
|
|
106
|
+
const fromAuthToken = (0, safeObject_1.isPlainObject)(security.authToken)
|
|
107
|
+
? security.authToken
|
|
108
|
+
: {};
|
|
109
|
+
const fromJwt = (0, safeObject_1.isPlainObject)(security.jwt) ? security.jwt : {};
|
|
110
|
+
const resolved = {};
|
|
111
|
+
for (const key of new Set([
|
|
112
|
+
...Object.keys(packaged),
|
|
113
|
+
...Object.keys(fromJwt),
|
|
114
|
+
...Object.keys(fromAuthToken),
|
|
115
|
+
])) {
|
|
116
|
+
resolved[key] = authTokenSettings_1.LEGACY_FIRST.has(key)
|
|
117
|
+
? (fromJwt[key] ?? fromAuthToken[key] ?? fromPackage[key])
|
|
118
|
+
: (fromAuthToken[key] ?? fromJwt[key] ?? fromPackage[key]);
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
authToken: { ...packaged, ...resolved },
|
|
122
|
+
deprecatedSettings: Object.keys(fromJwt).map((key) => `security.jwt.${key}`),
|
|
123
|
+
jwt: { ...resolved },
|
|
124
|
+
};
|
|
125
|
+
}
|
|
69
126
|
/**
|
|
70
127
|
* RC params can be overriden using environment variables,
|
|
71
128
|
* in which case all values are passed as strings.
|
|
@@ -99,13 +99,14 @@ export declare class Backend {
|
|
|
99
99
|
*/
|
|
100
100
|
plugin: BackendPlugin;
|
|
101
101
|
/**
|
|
102
|
-
*
|
|
102
|
+
* Application logger
|
|
103
103
|
*
|
|
104
|
+
* @method trace
|
|
104
105
|
* @method debug
|
|
105
106
|
* @method info
|
|
106
107
|
* @method warn
|
|
107
108
|
* @method error
|
|
108
|
-
* @method
|
|
109
|
+
* @method fatal
|
|
109
110
|
*/
|
|
110
111
|
log: Logger;
|
|
111
112
|
/**
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { ApplicationManager } from "./index";
|
|
2
|
+
/**
|
|
3
|
+
* Kuzzle no longer creates instances of this class: `Backend.log` is a
|
|
4
|
+
* `Logger` since 2.45.0.
|
|
5
|
+
*
|
|
6
|
+
* @deprecated Use `Logger` (`app.log`) instead.
|
|
7
|
+
*/
|
|
2
8
|
export declare class InternalLogger extends ApplicationManager {
|
|
3
9
|
/**
|
|
4
10
|
* Logs a trace message
|
|
@@ -26,6 +26,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
26
26
|
exports.InternalLogger = void 0;
|
|
27
27
|
const util_1 = __importDefault(require("util"));
|
|
28
28
|
const index_1 = require("./index");
|
|
29
|
+
/**
|
|
30
|
+
* Kuzzle no longer creates instances of this class: `Backend.log` is a
|
|
31
|
+
* `Logger` since 2.45.0.
|
|
32
|
+
*
|
|
33
|
+
* @deprecated Use `Logger` (`app.log`) instead.
|
|
34
|
+
*/
|
|
29
35
|
class InternalLogger extends index_1.ApplicationManager {
|
|
30
36
|
/**
|
|
31
37
|
* Logs a trace message
|
|
@@ -32,9 +32,27 @@ declare class Router {
|
|
|
32
32
|
* Transmit HTTP requests to the funnel controller and forward its response
|
|
33
33
|
* back to the client
|
|
34
34
|
*
|
|
35
|
+
* Triggers the `http:<verb>` pipe with the verb lowercased, as documented.
|
|
36
|
+
* A route declared with another case (e.g. `verb: 'GET'`) also triggers the
|
|
37
|
+
* event under the declared case (`http:GET`), which is the one it used to
|
|
38
|
+
* trigger: see docs/v3-breaking-changes.md.
|
|
39
|
+
*
|
|
40
|
+
* @param verb - the route's HTTP verb, as declared
|
|
35
41
|
* @param request - includes URL and POST query data
|
|
36
42
|
* @param cb - callback to invoke with the result
|
|
37
43
|
*/
|
|
38
44
|
_executeFromHttp(verb: string, request: Request, cb: RouteCallback): void;
|
|
45
|
+
/**
|
|
46
|
+
* Triggers an `http:<verb>` pipe, and answers its error through `cb` or
|
|
47
|
+
* hands the piped request over to `next`.
|
|
48
|
+
*
|
|
49
|
+
* Callbacks rather than promises: this runs before the funnel.
|
|
50
|
+
*/
|
|
51
|
+
private _pipeHttpEvent;
|
|
52
|
+
/**
|
|
53
|
+
* Executes the piped request in the funnel and answers its result through
|
|
54
|
+
* `cb`, falling back on the original request when there is none.
|
|
55
|
+
*/
|
|
56
|
+
private _executeInFunnel;
|
|
39
57
|
}
|
|
40
58
|
export = Router;
|
|
@@ -190,25 +190,59 @@ class Router {
|
|
|
190
190
|
* Transmit HTTP requests to the funnel controller and forward its response
|
|
191
191
|
* back to the client
|
|
192
192
|
*
|
|
193
|
+
* Triggers the `http:<verb>` pipe with the verb lowercased, as documented.
|
|
194
|
+
* A route declared with another case (e.g. `verb: 'GET'`) also triggers the
|
|
195
|
+
* event under the declared case (`http:GET`), which is the one it used to
|
|
196
|
+
* trigger: see docs/v3-breaking-changes.md.
|
|
197
|
+
*
|
|
198
|
+
* @param verb - the route's HTTP verb, as declared
|
|
193
199
|
* @param request - includes URL and POST query data
|
|
194
200
|
* @param cb - callback to invoke with the result
|
|
195
201
|
*/
|
|
196
202
|
_executeFromHttp(verb, request, cb) {
|
|
197
|
-
|
|
203
|
+
const event = `http:${verb.toLowerCase()}`;
|
|
204
|
+
const declaredEvent = `http:${verb}`;
|
|
205
|
+
this._pipeHttpEvent(event, request, cb, (pipedRequest) => {
|
|
206
|
+
if (declaredEvent === event) {
|
|
207
|
+
this._executeInFunnel(pipedRequest, request, cb);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// @deprecated - declared-case event, to drop in v3
|
|
211
|
+
this._pipeHttpEvent(declaredEvent, pipedRequest, cb, (legacyRequest) => {
|
|
212
|
+
this._executeInFunnel(legacyRequest, request, cb);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Triggers an `http:<verb>` pipe, and answers its error through `cb` or
|
|
218
|
+
* hands the piped request over to `next`.
|
|
219
|
+
*
|
|
220
|
+
* Callbacks rather than promises: this runs before the funnel.
|
|
221
|
+
*/
|
|
222
|
+
_pipeHttpEvent(event, request, cb, next) {
|
|
223
|
+
// callback form: the outcome, error included, goes through the callback
|
|
224
|
+
void global.kuzzle.pipe(event, request, (error, mutatedRequest) => {
|
|
198
225
|
if (error) {
|
|
199
226
|
request.setError(error);
|
|
200
227
|
cb(request);
|
|
201
228
|
return;
|
|
202
229
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
230
|
+
next(mutatedRequest);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Executes the piped request in the funnel and answers its result through
|
|
235
|
+
* `cb`, falling back on the original request when there is none.
|
|
236
|
+
*/
|
|
237
|
+
_executeInFunnel(pipedRequest, request, cb) {
|
|
238
|
+
global.kuzzle.funnel.execute(pipedRequest, (err, result) => {
|
|
239
|
+
const _res = result || request;
|
|
240
|
+
if (err && !_res.error) {
|
|
241
|
+
_res.setError(err);
|
|
242
|
+
}
|
|
243
|
+
// no sanitisation here: `_res` is a KuzzleRequest, and the stack
|
|
244
|
+
// is stripped by the protocols, on the serialized response
|
|
245
|
+
cb(_res);
|
|
212
246
|
});
|
|
213
247
|
}
|
|
214
248
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SecurityConfiguration } from "../../types/config/SecurityConfiguration";
|
|
2
|
+
type AuthTokenSettings = SecurityConfiguration["authToken"];
|
|
3
|
+
/**
|
|
4
|
+
* The settings for which a value set under the deprecated `security.jwt`
|
|
5
|
+
* wins over one set under `security.authToken`: `gracePeriod` used to be
|
|
6
|
+
* read from `jwt` only, so a configuration setting both keeps working as it
|
|
7
|
+
* did. To drop in v3 (docs/v3-breaking-changes.md).
|
|
8
|
+
*/
|
|
9
|
+
export declare const LEGACY_FIRST: ReadonlySet<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Reads one authentication token setting: the value set under
|
|
12
|
+
* `security.authToken` wins, then the one set under its deprecated spelling
|
|
13
|
+
* `security.jwt`, then the packaged default (except for {@link LEGACY_FIRST}).
|
|
14
|
+
*
|
|
15
|
+
* `loadConfig()` already resolves both sections from what the configuration
|
|
16
|
+
* files and the environment set. This covers a change made afterwards, e.g.
|
|
17
|
+
* `app.config.set("security.jwt.expiresIn", "2h")` before `app.start()`: a
|
|
18
|
+
* value that differs from the packaged default is one somebody set.
|
|
19
|
+
*/
|
|
20
|
+
export declare function authTokenSetting<K extends keyof AuthTokenSettings>(key: K): AuthTokenSettings[K];
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Kuzzle, a backend software, self-hostable and ready to use
|
|
4
|
+
* to power modern apps
|
|
5
|
+
*
|
|
6
|
+
* Copyright 2015-2022 Kuzzle
|
|
7
|
+
* mailto: support AT kuzzle.io
|
|
8
|
+
* website: http://kuzzle.io
|
|
9
|
+
*
|
|
10
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
11
|
+
* you may not use this file except in compliance with the License.
|
|
12
|
+
* You may obtain a copy of the License at
|
|
13
|
+
*
|
|
14
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
15
|
+
*
|
|
16
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
17
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
18
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
19
|
+
* See the License for the specific language governing permissions and
|
|
20
|
+
* limitations under the License.
|
|
21
|
+
*/
|
|
22
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.LEGACY_FIRST = void 0;
|
|
27
|
+
exports.authTokenSetting = authTokenSetting;
|
|
28
|
+
const default_config_1 = __importDefault(require("../../config/default.config"));
|
|
29
|
+
/**
|
|
30
|
+
* The settings for which a value set under the deprecated `security.jwt`
|
|
31
|
+
* wins over one set under `security.authToken`: `gracePeriod` used to be
|
|
32
|
+
* read from `jwt` only, so a configuration setting both keeps working as it
|
|
33
|
+
* did. To drop in v3 (docs/v3-breaking-changes.md).
|
|
34
|
+
*/
|
|
35
|
+
exports.LEGACY_FIRST = new Set(["gracePeriod"]);
|
|
36
|
+
function isSet(value) {
|
|
37
|
+
return value !== undefined && value !== null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Reads one authentication token setting: the value set under
|
|
41
|
+
* `security.authToken` wins, then the one set under its deprecated spelling
|
|
42
|
+
* `security.jwt`, then the packaged default (except for {@link LEGACY_FIRST}).
|
|
43
|
+
*
|
|
44
|
+
* `loadConfig()` already resolves both sections from what the configuration
|
|
45
|
+
* files and the environment set. This covers a change made afterwards, e.g.
|
|
46
|
+
* `app.config.set("security.jwt.expiresIn", "2h")` before `app.start()`: a
|
|
47
|
+
* value that differs from the packaged default is one somebody set.
|
|
48
|
+
*/
|
|
49
|
+
function authTokenSetting(key) {
|
|
50
|
+
const { authToken, jwt } = global.kuzzle.config.security; // NOSONAR: the deprecated section is read on purpose, as the documented fallback
|
|
51
|
+
const packaged = default_config_1.default.security.authToken[key];
|
|
52
|
+
const current = authToken?.[key];
|
|
53
|
+
const legacy = jwt?.[key]; // NOSONAR
|
|
54
|
+
const candidates = exports.LEGACY_FIRST.has(key)
|
|
55
|
+
? [legacy, current]
|
|
56
|
+
: [current, legacy];
|
|
57
|
+
return (candidates.find((value) => isSet(value) && value !== packaged) ??
|
|
58
|
+
candidates.find(isSet) ??
|
|
59
|
+
packaged);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=authTokenSettings.js.map
|
|
@@ -66,15 +66,8 @@ const token_1 = require("../../model/security/token");
|
|
|
66
66
|
const apiKey_1 = __importDefault(require("../../model/storage/apiKey"));
|
|
67
67
|
const ObjectRepository_1 = require("../shared/ObjectRepository");
|
|
68
68
|
const crypto_1 = require("../../util/crypto");
|
|
69
|
+
const authTokenSettings_1 = require("./authTokenSettings");
|
|
69
70
|
const securityError = kerror.wrap("security", "token");
|
|
70
|
-
/**
|
|
71
|
-
* The deprecated `security.jwt` section, which the `authToken` settings fall
|
|
72
|
-
* back to. The packaged defaults always ship it; the type says optional only
|
|
73
|
-
* because v2.56.0's did (see SecurityConfiguration.jwt).
|
|
74
|
-
*/
|
|
75
|
-
function legacyJwtConfig() {
|
|
76
|
-
return global.kuzzle.config.security.jwt; // NOSONAR: the deprecated section is read on purpose, as the documented fallback
|
|
77
|
-
}
|
|
78
71
|
class TokenRepository extends ObjectRepository_1.ObjectRepository {
|
|
79
72
|
constructor(opts = {}) {
|
|
80
73
|
super();
|
|
@@ -83,7 +76,11 @@ class TokenRepository extends ObjectRepository_1.ObjectRepository {
|
|
|
83
76
|
if (opts.ttl !== undefined) {
|
|
84
77
|
this.ttl = opts.ttl;
|
|
85
78
|
}
|
|
86
|
-
this.tokenGracePeriod = Math.floor(
|
|
79
|
+
this.tokenGracePeriod = Math.floor((0, authTokenSettings_1.authTokenSetting)("gracePeriod"));
|
|
80
|
+
const deprecated = global.kuzzle.config.internal?.deprecatedSettings;
|
|
81
|
+
if (deprecated?.length) {
|
|
82
|
+
global.kuzzle.log.warn(`[DEPRECATED] The configuration section "security.jwt" is deprecated, use "security.authToken" instead (set: ${deprecated.join(", ")}). A value set under "security.authToken" takes precedence.`);
|
|
83
|
+
}
|
|
87
84
|
this.anonymousToken = new token_1.Token({ userId: "-1" });
|
|
88
85
|
}
|
|
89
86
|
async init() {
|
|
@@ -194,17 +191,14 @@ class TokenRepository extends ObjectRepository_1.ObjectRepository {
|
|
|
194
191
|
*
|
|
195
192
|
* @returns {Promise.<Object>} { _id, jwt, userId, ttl, expiresAt }
|
|
196
193
|
*/
|
|
197
|
-
async generateToken(user, { algorithm =
|
|
198
|
-
legacyJwtConfig().algorithm, expiresIn = global.kuzzle.config.security.authToken.expiresIn ??
|
|
199
|
-
legacyJwtConfig().expiresIn, bypassMaxTTL = false, type = "authToken", singleUse = false, } = {}) {
|
|
194
|
+
async generateToken(user, { algorithm = (0, authTokenSettings_1.authTokenSetting)("algorithm"), expiresIn = (0, authTokenSettings_1.authTokenSetting)("expiresIn"), bypassMaxTTL = false, type = "authToken", singleUse = false, } = {}) {
|
|
200
195
|
if (!user || user._id === null) {
|
|
201
196
|
throw securityError.get("unknown_user");
|
|
202
197
|
}
|
|
203
198
|
const parsedExpiresIn = parseTimespan(expiresIn);
|
|
204
199
|
const maxTTL = type === "apiKey"
|
|
205
200
|
? global.kuzzle.config.security.apiKey.maxTTL
|
|
206
|
-
: (
|
|
207
|
-
legacyJwtConfig().maxTTL);
|
|
201
|
+
: (0, authTokenSettings_1.authTokenSetting)("maxTTL");
|
|
208
202
|
if (!bypassMaxTTL &&
|
|
209
203
|
maxTTL > -1 &&
|
|
210
204
|
(parsedExpiresIn > maxTTL || parsedExpiresIn === -1)) {
|
|
@@ -29,6 +29,15 @@ export declare class NormalizedFilterV3 {
|
|
|
29
29
|
normalized: JSONObject[][];
|
|
30
30
|
constructor(index: string, collection: string, normalized: JSONObject);
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Exposes Koncorde v4 through an interface emulating Koncorde v3, to prevent
|
|
34
|
+
* breaking changes in Kuzzle.
|
|
35
|
+
*
|
|
36
|
+
* This class should either be removed entirely in the next major version, or
|
|
37
|
+
* perhaps proposed in a compatibility mode to help clients migrate smoothly.
|
|
38
|
+
*
|
|
39
|
+
* @deprecated Use the `koncorde` package (v4) directly.
|
|
40
|
+
*/
|
|
32
41
|
export declare class Koncorde {
|
|
33
42
|
private filterIdIndexMap;
|
|
34
43
|
private koncorde;
|
|
@@ -35,14 +35,14 @@ class NormalizedFilterV3 {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
exports.NormalizedFilterV3 = NormalizedFilterV3;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Exposes Koncorde v4 through an interface emulating Koncorde v3, to prevent
|
|
40
|
+
* breaking changes in Kuzzle.
|
|
41
|
+
*
|
|
42
|
+
* This class should either be removed entirely in the next major version, or
|
|
43
|
+
* perhaps proposed in a compatibility mode to help clients migrate smoothly.
|
|
44
|
+
*
|
|
45
|
+
* @deprecated Use the `koncorde` package (v4) directly.
|
|
46
46
|
*/
|
|
47
47
|
class Koncorde {
|
|
48
48
|
constructor(options = {}) {
|
|
@@ -96,8 +96,13 @@ class ClientAdapter {
|
|
|
96
96
|
this.registerLockEvents();
|
|
97
97
|
// The cache and the locks were first loaded before this node joined the
|
|
98
98
|
// cluster: an index, a collection or a lock a peer added in between was
|
|
99
|
-
// broadcast before this node could hear it.
|
|
100
|
-
// cluster is joined
|
|
99
|
+
// broadcast before this node could hear it. `Kuzzle.start()` asks for this
|
|
100
|
+
// reload right after the cluster is joined, before the plugins' `init()`
|
|
101
|
+
// runs (#3044).
|
|
102
|
+
global.kuzzle.onAsk(`core:storage:${this.scope}:cache:reload`, () => this.reloadCache());
|
|
103
|
+
// Read again once the node goes live: a peer that joined after this node
|
|
104
|
+
// may have created indexes or collections before its own join (e.g. its
|
|
105
|
+
// internal index), and that broadcast reached no one.
|
|
101
106
|
global.kuzzle.onPipe("kuzzle:state:live", () => this.reloadCache());
|
|
102
107
|
// Global store events registration
|
|
103
108
|
/**
|
|
@@ -28,5 +28,9 @@ export declare class Logger extends KuzzleLogger {
|
|
|
28
28
|
verbose(message: string): void;
|
|
29
29
|
getLevel(): string;
|
|
30
30
|
setLevel(level: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Creates a child logger with the given namespace, appended to the parent's
|
|
33
|
+
* one. Its level follows the parent's until it is set on the child.
|
|
34
|
+
*/
|
|
31
35
|
child(namespace: string): Logger;
|
|
32
36
|
}
|
|
@@ -128,9 +128,16 @@ class Logger extends kuzzle_logger_1.KuzzleLogger {
|
|
|
128
128
|
setLevel(level) {
|
|
129
129
|
this.level = level;
|
|
130
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Creates a child logger with the given namespace, appended to the parent's
|
|
133
|
+
* one. Its level follows the parent's until it is set on the child.
|
|
134
|
+
*/
|
|
131
135
|
child(namespace) {
|
|
132
|
-
//
|
|
133
|
-
|
|
136
|
+
// kuzzle-logger builds the child as a plain KuzzleLogger (no getLevel,
|
|
137
|
+
// setLevel, silly, verbose): give it this class's prototype so it is the
|
|
138
|
+
// Logger it is declared to be. Its state is all KuzzleLogger's, and the
|
|
139
|
+
// deprecation flags start unset, as on a new Logger.
|
|
140
|
+
return Object.setPrototypeOf(super.child(namespace), Logger.prototype);
|
|
134
141
|
}
|
|
135
142
|
}
|
|
136
143
|
exports.Logger = Logger;
|
|
@@ -57,6 +57,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
57
57
|
};
|
|
58
58
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
59
59
|
const bluebird_1 = __importDefault(require("bluebird"));
|
|
60
|
+
const authTokenSettings_1 = require("../core/security/authTokenSettings");
|
|
60
61
|
const store_1 = require("../core/shared/store");
|
|
61
62
|
const storeScopeEnum_1 = require("../core/storage/storeScopeEnum");
|
|
62
63
|
const specification_1 = require("../core/validation/specification");
|
|
@@ -191,11 +192,9 @@ class InternalIndexHandler extends store_1.Store {
|
|
|
191
192
|
await bluebird_1.default.all(promises);
|
|
192
193
|
}
|
|
193
194
|
async _initSecret() {
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
const { authToken, jwt } = global.kuzzle.config.security; // NOSONAR
|
|
198
|
-
const configSeed = authToken?.secret ?? jwt?.secret; // NOSONAR
|
|
195
|
+
// `jwt` is the deprecated spelling of `authToken`, read second so an
|
|
196
|
+
// existing configuration keeps working (see authTokenSetting).
|
|
197
|
+
const configSeed = (0, authTokenSettings_1.authTokenSetting)("secret");
|
|
199
198
|
let storedSeed = await this.exists("config", this._JWT_SECRET_ID);
|
|
200
199
|
if (!configSeed) {
|
|
201
200
|
if (!storedSeed) {
|
|
@@ -107,6 +107,23 @@ declare class Kuzzle extends KuzzleEventEmitter {
|
|
|
107
107
|
* it hands the application wrapper the implementation reads.
|
|
108
108
|
*/
|
|
109
109
|
start(application: unknown, options?: StartOptions): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* A node asked to stop while it starts must not go on taking cluster-wide
|
|
112
|
+
* locks or running the application's installations: if it is killed before
|
|
113
|
+
* its shutdown releases them, the other nodes wait for their TTL (#3045).
|
|
114
|
+
*/
|
|
115
|
+
private stopIfShuttingDown;
|
|
116
|
+
/**
|
|
117
|
+
* The shutdown began during the startup, which stopped (or failed because
|
|
118
|
+
* of it): no "Cannot start" error and no second shutdown, the one running
|
|
119
|
+
* keeps its exit code and ends the process.
|
|
120
|
+
*
|
|
121
|
+
* The returned promise never settles, as `withLock` once the process is
|
|
122
|
+
* exiting: rejecting would send the caller down its failed-start path
|
|
123
|
+
* (often a `process.exit(1)`, which would cut the shutdown short), and
|
|
124
|
+
* resolving would tell it that Kuzzle is running.
|
|
125
|
+
*/
|
|
126
|
+
private leaveStartupToShutdown;
|
|
110
127
|
/**
|
|
111
128
|
* Generates the node ID.
|
|
112
129
|
*
|
|
@@ -99,6 +99,17 @@ let _kuzzle = null;
|
|
|
99
99
|
* not keep a failed node alive, which a bare `process.exit(1)` never did
|
|
100
100
|
*/
|
|
101
101
|
const FAILURE_SHUTDOWN_TIMEOUT = 30000;
|
|
102
|
+
/**
|
|
103
|
+
* Stops the startup sequence once a shutdown has begun (#3045): `start()`
|
|
104
|
+
* catches it and leaves the shutdown to end the process. Internal: never
|
|
105
|
+
* handed to the caller of `start()`.
|
|
106
|
+
*/
|
|
107
|
+
class StartupInterruptedError extends Error {
|
|
108
|
+
constructor() {
|
|
109
|
+
super("Kuzzle is shutting down");
|
|
110
|
+
this.name = "StartupInterruptedError";
|
|
111
|
+
}
|
|
112
|
+
}
|
|
102
113
|
Reflect.defineProperty(global, "kuzzle", {
|
|
103
114
|
configurable: true,
|
|
104
115
|
enumerable: false,
|
|
@@ -175,9 +186,11 @@ class Kuzzle extends KuzzleEventEmitter_1.default {
|
|
|
175
186
|
await new cacheEngine_1.default().init();
|
|
176
187
|
await new storageEngine_1.default().init();
|
|
177
188
|
await new realtime_1.default().init();
|
|
189
|
+
this.stopIfShuttingDown();
|
|
178
190
|
await this.internalIndex.init();
|
|
179
191
|
await new security_1.default().init();
|
|
180
192
|
// This will init the cluster module if enabled
|
|
193
|
+
this.stopIfShuttingDown();
|
|
181
194
|
await this.initKuzzleNode();
|
|
182
195
|
this.vault = vault_1.default.load(options?.vaultKey, options?.secretsFile, this.config.vault?.newAlgorithm);
|
|
183
196
|
this.validation.init();
|
|
@@ -189,21 +202,25 @@ class Kuzzle extends KuzzleEventEmitter_1.default {
|
|
|
189
202
|
// before opening connections to external users
|
|
190
203
|
await this.entryPoint.init();
|
|
191
204
|
await this.debugger.init();
|
|
205
|
+
this.stopIfShuttingDown();
|
|
192
206
|
this.pluginsManager.application = application;
|
|
193
207
|
const pluginImports = await this.pluginsManager.init(options?.plugins);
|
|
194
208
|
const imports = lodash_1.default.merge({}, pluginImports, options?.import);
|
|
195
209
|
// Authentification plugins must be loaded before users import to avoid
|
|
196
210
|
// credentials related error which would prevent Kuzzle from starting
|
|
211
|
+
this.stopIfShuttingDown();
|
|
197
212
|
await this.loadInitialState(imports, options?.support);
|
|
198
213
|
await this.ask("core:security:verify");
|
|
199
214
|
this.router.init();
|
|
200
215
|
this.log.info("[✔] Core components loaded");
|
|
216
|
+
this.stopIfShuttingDown();
|
|
201
217
|
await this.install(options?.installations);
|
|
202
218
|
// `application` is the very wrapper handed to the plugins manager six
|
|
203
219
|
// lines up, so reading the name back off the manager was a round trip
|
|
204
220
|
// through a getter that can answer `undefined`.
|
|
205
221
|
this.log.info(`[✔] Start "${application.name}" application`);
|
|
206
222
|
this.openApiManager = new openapi_1.OpenApiManager(application.openApi, this.config.http.routes, this.pluginsManager.routes);
|
|
223
|
+
this.stopIfShuttingDown();
|
|
207
224
|
// @deprecated
|
|
208
225
|
await this.pipe("kuzzle:start");
|
|
209
226
|
await this.pipe("kuzzle:state:live");
|
|
@@ -215,6 +232,9 @@ class Kuzzle extends KuzzleEventEmitter_1.default {
|
|
|
215
232
|
this._state = kuzzleStateEnum_1.default.RUNNING;
|
|
216
233
|
}
|
|
217
234
|
catch (error) {
|
|
235
|
+
if (this._state === kuzzleStateEnum_1.default.SHUTTING_DOWN) {
|
|
236
|
+
return this.leaveStartupToShutdown(error);
|
|
237
|
+
}
|
|
218
238
|
this.log.error(`[X] Cannot start Kuzzle ${this.version}: ${error instanceof Error ? error.message : (0, node_util_1.inspect)(error)}`);
|
|
219
239
|
// A failed start cannot be retried (a second Kuzzle instance cannot be
|
|
220
240
|
// built), and what it did start keeps the process alive: in production,
|
|
@@ -229,6 +249,36 @@ class Kuzzle extends KuzzleEventEmitter_1.default {
|
|
|
229
249
|
throw error;
|
|
230
250
|
}
|
|
231
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* A node asked to stop while it starts must not go on taking cluster-wide
|
|
254
|
+
* locks or running the application's installations: if it is killed before
|
|
255
|
+
* its shutdown releases them, the other nodes wait for their TTL (#3045).
|
|
256
|
+
*/
|
|
257
|
+
stopIfShuttingDown() {
|
|
258
|
+
if (this._state === kuzzleStateEnum_1.default.SHUTTING_DOWN) {
|
|
259
|
+
throw new StartupInterruptedError();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* The shutdown began during the startup, which stopped (or failed because
|
|
264
|
+
* of it): no "Cannot start" error and no second shutdown, the one running
|
|
265
|
+
* keeps its exit code and ends the process.
|
|
266
|
+
*
|
|
267
|
+
* The returned promise never settles, as `withLock` once the process is
|
|
268
|
+
* exiting: rejecting would send the caller down its failed-start path
|
|
269
|
+
* (often a `process.exit(1)`, which would cut the shutdown short), and
|
|
270
|
+
* resolving would tell it that Kuzzle is running.
|
|
271
|
+
*/
|
|
272
|
+
leaveStartupToShutdown(error) {
|
|
273
|
+
let message = "[ℹ] Startup stopped: Kuzzle is shutting down";
|
|
274
|
+
// A step that failed because of the shutdown, rather than stopped by it
|
|
275
|
+
if (!(error instanceof StartupInterruptedError)) {
|
|
276
|
+
const reason = error instanceof Error ? error.message : (0, node_util_1.inspect)(error);
|
|
277
|
+
message += ` (${reason})`;
|
|
278
|
+
}
|
|
279
|
+
this.log.info(message);
|
|
280
|
+
return new Promise(() => undefined);
|
|
281
|
+
}
|
|
232
282
|
/**
|
|
233
283
|
* Generates the node ID.
|
|
234
284
|
*
|
|
@@ -238,6 +288,13 @@ class Kuzzle extends KuzzleEventEmitter_1.default {
|
|
|
238
288
|
if (this.config.cluster.enabled) {
|
|
239
289
|
await new cluster_1.default().init();
|
|
240
290
|
this.log.info("[✔] Cluster initialized");
|
|
291
|
+
// The storage caches were loaded before the join: what a peer created
|
|
292
|
+
// in between was broadcast before this node could hear it. Read them
|
|
293
|
+
// again before the plugins' `init()` relies on them (#3044).
|
|
294
|
+
await Promise.all([
|
|
295
|
+
this.ask("core:storage:public:cache:reload"),
|
|
296
|
+
this.ask("core:storage:private:cache:reload"),
|
|
297
|
+
]);
|
|
241
298
|
}
|
|
242
299
|
else {
|
|
243
300
|
this.log.info("[X] Cluster disabled: single node mode.");
|
|
@@ -317,6 +374,9 @@ class Kuzzle extends KuzzleEventEmitter_1.default {
|
|
|
317
374
|
// one must not start before they are recorded
|
|
318
375
|
await (0, distributedLock_1.withLock)("backend:installations", async () => {
|
|
319
376
|
for (const installation of installations) {
|
|
377
|
+
// The lock may come long after it was asked for, once the node
|
|
378
|
+
// holding it is done: by then, this one may be shutting down
|
|
379
|
+
this.stopIfShuttingDown();
|
|
320
380
|
const isAlreadyInstalled = await this.ask("core:storage:private:document:exist", "kuzzle", "installations", installation.id);
|
|
321
381
|
if (!isAlreadyInstalled) {
|
|
322
382
|
try {
|
|
@@ -508,6 +568,8 @@ class Kuzzle extends KuzzleEventEmitter_1.default {
|
|
|
508
568
|
// cannot serialise, and the payload is built right above.
|
|
509
569
|
const importPayloadHash = (0, crypto_1.sha256)((0, json_stable_stringify_1.default)(importPayload) ?? "");
|
|
510
570
|
const importType = async () => {
|
|
571
|
+
// Same as for the installations: the lock may come after a SIGTERM
|
|
572
|
+
this.stopIfShuttingDown();
|
|
511
573
|
// Read the bookkeeping *after* the lock, never before: a read taken
|
|
512
574
|
// before acquiring it is not protected by it, and the holder that
|
|
513
575
|
// acted on a stale "not initialized" used to create a document the
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { Vault } from "kuzzle-vault";
|
|
2
|
+
/**
|
|
3
|
+
* The default secrets file, `config/secrets.enc.json` at the project root.
|
|
4
|
+
*
|
|
5
|
+
* Installed as an npm package (KaaF), Kuzzle lives under `node_modules/kuzzle`
|
|
6
|
+
* and the project root is above it: from `node_modules/kuzzle/dist/lib/kuzzle`
|
|
7
|
+
* since the build outputs to `dist/` (2.50.0), from
|
|
8
|
+
* `node_modules/kuzzle/lib/kuzzle` before. Anywhere else (a Kuzzle checkout),
|
|
9
|
+
* the file is the checkout's own `config/secrets.enc.json`.
|
|
10
|
+
*
|
|
11
|
+
* @param dirname - the directory of this module
|
|
12
|
+
*/
|
|
13
|
+
declare function defaultSecretsFile(dirname: string): string;
|
|
2
14
|
declare function load(vaultKey?: string, secretsFile?: string, useNewAlgorithm?: boolean): Vault;
|
|
3
15
|
declare const _default: {
|
|
16
|
+
defaultSecretsFile: typeof defaultSecretsFile;
|
|
4
17
|
load: typeof load;
|
|
5
18
|
};
|
|
6
19
|
export = _default;
|
package/dist/lib/kuzzle/vault.js
CHANGED
|
@@ -35,15 +35,33 @@ const isEmpty_1 = __importDefault(require("lodash/isEmpty"));
|
|
|
35
35
|
// strict while being false for every read before that first assignment
|
|
36
36
|
// (TD-57, #2760).
|
|
37
37
|
let ENV_VAULT_KEY;
|
|
38
|
+
/**
|
|
39
|
+
* The default secrets file, `config/secrets.enc.json` at the project root.
|
|
40
|
+
*
|
|
41
|
+
* Installed as an npm package (KaaF), Kuzzle lives under `node_modules/kuzzle`
|
|
42
|
+
* and the project root is above it: from `node_modules/kuzzle/dist/lib/kuzzle`
|
|
43
|
+
* since the build outputs to `dist/` (2.50.0), from
|
|
44
|
+
* `node_modules/kuzzle/lib/kuzzle` before. Anywhere else (a Kuzzle checkout),
|
|
45
|
+
* the file is the checkout's own `config/secrets.enc.json`.
|
|
46
|
+
*
|
|
47
|
+
* @param dirname - the directory of this module
|
|
48
|
+
*/
|
|
49
|
+
function defaultSecretsFile(dirname) {
|
|
50
|
+
const dir = dirname.split(node_path_1.default.sep).join("/");
|
|
51
|
+
for (const installed of [
|
|
52
|
+
"/node_modules/kuzzle/dist/lib/kuzzle",
|
|
53
|
+
"/node_modules/kuzzle/lib/kuzzle",
|
|
54
|
+
]) {
|
|
55
|
+
if (dir.endsWith(installed)) {
|
|
56
|
+
return node_path_1.default.resolve(dirname.slice(0, dirname.length - installed.length + 1), "config/secrets.enc.json");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return node_path_1.default.resolve(dirname, "../../config/secrets.enc.json");
|
|
60
|
+
}
|
|
38
61
|
function load(vaultKey, secretsFile, useNewAlgorithm = false) {
|
|
39
|
-
// Using KaaF kuzzle is an npm package and is located under node_modules folder
|
|
40
|
-
// We need to get back to root folder of the project to get the secret file
|
|
41
|
-
const defaultEncryptedSecretsFile = __dirname.endsWith("/node_modules/kuzzle/lib/kuzzle")
|
|
42
|
-
? node_path_1.default.resolve(`${__dirname}/../../../../config/secrets.enc.json`)
|
|
43
|
-
: node_path_1.default.resolve(`${__dirname}/../../config/secrets.enc.json`);
|
|
44
62
|
const encryptedSecretsFile = secretsFile ||
|
|
45
63
|
process.env.KUZZLE_SECRETS_FILE ||
|
|
46
|
-
|
|
64
|
+
defaultSecretsFile(__dirname);
|
|
47
65
|
let key = vaultKey;
|
|
48
66
|
if ((0, isEmpty_1.default)(vaultKey) &&
|
|
49
67
|
(!(0, isEmpty_1.default)(process.env.KUZZLE_VAULT_KEY) || !(0, isEmpty_1.default)(ENV_VAULT_KEY))) {
|
|
@@ -71,5 +89,5 @@ function load(vaultKey, secretsFile, useNewAlgorithm = false) {
|
|
|
71
89
|
}
|
|
72
90
|
return vault;
|
|
73
91
|
}
|
|
74
|
-
module.exports = { load };
|
|
92
|
+
module.exports = { defaultSecretsFile, load };
|
|
75
93
|
//# sourceMappingURL=vault.js.map
|
|
@@ -452,6 +452,12 @@ export declare class ES7 {
|
|
|
452
452
|
mappings?: TypeMapping;
|
|
453
453
|
settings?: JSONObject;
|
|
454
454
|
}): Promise<null>;
|
|
455
|
+
/**
|
|
456
|
+
* Applies the mappings and settings of a `createCollection` call to a
|
|
457
|
+
* collection that already exists because another request created it,
|
|
458
|
+
* maybe so recently that its primary shards are still initializing.
|
|
459
|
+
*/
|
|
460
|
+
private _updateConcurrentlyCreatedCollection;
|
|
455
461
|
/**
|
|
456
462
|
* Waits until the primary shards of a collection are active, for at most
|
|
457
463
|
* `timeout`, and rejects if they are not by then.
|
|
@@ -1149,23 +1149,23 @@ class ES7 {
|
|
|
1149
1149
|
this._checkDynamicProperty(mappings);
|
|
1150
1150
|
const exists = await this.hasCollection(index, collection);
|
|
1151
1151
|
if (exists) {
|
|
1152
|
-
|
|
1153
|
-
// are still initializing
|
|
1154
|
-
await this.waitForCollectionShards(index, collection);
|
|
1155
|
-
return this.updateCollection(index, collection, { mappings, settings });
|
|
1152
|
+
return this._updateConcurrentlyCreatedCollection(index, collection, mappings, settings);
|
|
1156
1153
|
}
|
|
1157
1154
|
this._checkMappings(mappings);
|
|
1155
|
+
// Copies, not in-place changes: the race branch below must hand the
|
|
1156
|
+
// caller's own mappings and settings to `updateCollection`
|
|
1158
1157
|
requestBody.mappings = {
|
|
1159
1158
|
_meta: mappings._meta || this._config.commonMapping._meta,
|
|
1160
1159
|
dynamic: mappings.dynamic || this._config.commonMapping.dynamic,
|
|
1161
|
-
properties: lodash_1.default.merge(mappings.properties, this._config.commonMapping.properties),
|
|
1160
|
+
properties: lodash_1.default.merge({}, mappings.properties, this._config.commonMapping.properties),
|
|
1161
|
+
};
|
|
1162
|
+
requestBody.settings = {
|
|
1163
|
+
...settings,
|
|
1164
|
+
number_of_replicas: settings.number_of_replicas ||
|
|
1165
|
+
this._config.defaultSettings.number_of_replicas,
|
|
1166
|
+
number_of_shards: settings.number_of_shards ||
|
|
1167
|
+
this._config.defaultSettings.number_of_shards,
|
|
1162
1168
|
};
|
|
1163
|
-
settings.number_of_replicas =
|
|
1164
|
-
settings.number_of_replicas ||
|
|
1165
|
-
this._config.defaultSettings.number_of_replicas;
|
|
1166
|
-
settings.number_of_shards =
|
|
1167
|
-
settings.number_of_shards ||
|
|
1168
|
-
this._config.defaultSettings.number_of_shards;
|
|
1169
1169
|
try {
|
|
1170
1170
|
await this._client.indices.create(esRequest);
|
|
1171
1171
|
}
|
|
@@ -1174,9 +1174,9 @@ class ES7 {
|
|
|
1174
1174
|
"resource_already_exists_exception") {
|
|
1175
1175
|
// race condition: the indice has been created between the "exists"
|
|
1176
1176
|
// check above and this "create" attempt, by a request that may not
|
|
1177
|
-
// have waited for its shards yet
|
|
1178
|
-
|
|
1179
|
-
return
|
|
1177
|
+
// have waited for its shards yet. This request's mappings and
|
|
1178
|
+
// settings must still be applied, as if it had seen it exist
|
|
1179
|
+
return this._updateConcurrentlyCreatedCollection(index, collection, mappings, settings);
|
|
1180
1180
|
}
|
|
1181
1181
|
throw this._esWrapper.formatESError(error);
|
|
1182
1182
|
}
|
|
@@ -1195,6 +1195,15 @@ class ES7 {
|
|
|
1195
1195
|
}
|
|
1196
1196
|
return null;
|
|
1197
1197
|
}
|
|
1198
|
+
/**
|
|
1199
|
+
* Applies the mappings and settings of a `createCollection` call to a
|
|
1200
|
+
* collection that already exists because another request created it,
|
|
1201
|
+
* maybe so recently that its primary shards are still initializing.
|
|
1202
|
+
*/
|
|
1203
|
+
async _updateConcurrentlyCreatedCollection(index, collection, mappings, settings) {
|
|
1204
|
+
await this.waitForCollectionShards(index, collection);
|
|
1205
|
+
return this.updateCollection(index, collection, { mappings, settings });
|
|
1206
|
+
}
|
|
1198
1207
|
/**
|
|
1199
1208
|
* Waits until the primary shards of a collection are active, for at most
|
|
1200
1209
|
* `timeout`, and rejects if they are not by then.
|
|
@@ -443,6 +443,12 @@ export declare class ES8 {
|
|
|
443
443
|
mappings?: estypes.MappingTypeMapping;
|
|
444
444
|
settings?: estypes.IndicesIndexSettings;
|
|
445
445
|
}): Promise<null>;
|
|
446
|
+
/**
|
|
447
|
+
* Applies the mappings and settings of a `createCollection` call to a
|
|
448
|
+
* collection that already exists because another request created it,
|
|
449
|
+
* maybe so recently that its primary shards are still initializing.
|
|
450
|
+
*/
|
|
451
|
+
private _updateConcurrentlyCreatedCollection;
|
|
446
452
|
/**
|
|
447
453
|
* Waits until the primary shards of a collection are active, for at most
|
|
448
454
|
* `timeout`, and rejects if they are not by then.
|
|
@@ -1171,23 +1171,23 @@ class ES8 {
|
|
|
1171
1171
|
this._checkDynamicProperty(mappings);
|
|
1172
1172
|
const exists = await this.hasCollection(index, collection);
|
|
1173
1173
|
if (exists) {
|
|
1174
|
-
|
|
1175
|
-
// are still initializing
|
|
1176
|
-
await this.waitForCollectionShards(index, collection);
|
|
1177
|
-
return this.updateCollection(index, collection, { mappings, settings });
|
|
1174
|
+
return this._updateConcurrentlyCreatedCollection(index, collection, mappings, settings);
|
|
1178
1175
|
}
|
|
1179
1176
|
this._checkMappings(mappings);
|
|
1177
|
+
// Copies, not in-place changes: the race branch below must hand the
|
|
1178
|
+
// caller's own mappings and settings to `updateCollection`
|
|
1180
1179
|
esRequest.mappings = {
|
|
1181
1180
|
_meta: mappings._meta || this._config.commonMapping._meta,
|
|
1182
1181
|
dynamic: mappings.dynamic || this._config.commonMapping.dynamic,
|
|
1183
|
-
properties: lodash_1.default.merge(mappings.properties, this._config.commonMapping.properties),
|
|
1182
|
+
properties: lodash_1.default.merge({}, mappings.properties, this._config.commonMapping.properties),
|
|
1183
|
+
};
|
|
1184
|
+
esRequest.settings = {
|
|
1185
|
+
...settings,
|
|
1186
|
+
number_of_replicas: settings.number_of_replicas ||
|
|
1187
|
+
this._config.defaultSettings.number_of_replicas,
|
|
1188
|
+
number_of_shards: settings.number_of_shards ||
|
|
1189
|
+
this._config.defaultSettings.number_of_shards,
|
|
1184
1190
|
};
|
|
1185
|
-
settings.number_of_replicas =
|
|
1186
|
-
settings.number_of_replicas ||
|
|
1187
|
-
this._config.defaultSettings.number_of_replicas;
|
|
1188
|
-
settings.number_of_shards =
|
|
1189
|
-
settings.number_of_shards ||
|
|
1190
|
-
this._config.defaultSettings.number_of_shards;
|
|
1191
1191
|
try {
|
|
1192
1192
|
await this._client.indices.create(esRequest);
|
|
1193
1193
|
}
|
|
@@ -1196,9 +1196,9 @@ class ES8 {
|
|
|
1196
1196
|
"resource_already_exists_exception") {
|
|
1197
1197
|
// race condition: the indice has been created between the "exists"
|
|
1198
1198
|
// check above and this "create" attempt, by a request that may not
|
|
1199
|
-
// have waited for its shards yet
|
|
1200
|
-
|
|
1201
|
-
return
|
|
1199
|
+
// have waited for its shards yet. This request's mappings and
|
|
1200
|
+
// settings must still be applied, as if it had seen it exist
|
|
1201
|
+
return this._updateConcurrentlyCreatedCollection(index, collection, mappings, settings);
|
|
1202
1202
|
}
|
|
1203
1203
|
throw this._esWrapper.formatESError(error);
|
|
1204
1204
|
}
|
|
@@ -1217,6 +1217,15 @@ class ES8 {
|
|
|
1217
1217
|
}
|
|
1218
1218
|
return null;
|
|
1219
1219
|
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Applies the mappings and settings of a `createCollection` call to a
|
|
1222
|
+
* collection that already exists because another request created it,
|
|
1223
|
+
* maybe so recently that its primary shards are still initializing.
|
|
1224
|
+
*/
|
|
1225
|
+
async _updateConcurrentlyCreatedCollection(index, collection, mappings, settings) {
|
|
1226
|
+
await this.waitForCollectionShards(index, collection);
|
|
1227
|
+
return this.updateCollection(index, collection, { mappings, settings });
|
|
1228
|
+
}
|
|
1220
1229
|
/**
|
|
1221
1230
|
* Waits until the primary shards of a collection are active, for at most
|
|
1222
1231
|
* `timeout`, and rejects if they are not by then.
|
|
@@ -163,6 +163,13 @@ export interface IKuzzleConfiguration {
|
|
|
163
163
|
* Optional here for the reason given on {@link IKuzzleConfiguration.version}.
|
|
164
164
|
*/
|
|
165
165
|
allowAllOrigins?: boolean;
|
|
166
|
+
/**
|
|
167
|
+
* Derived at startup: the deprecated configuration keys the user set
|
|
168
|
+
* (today, those of "security.jwt"), logged as a warning.
|
|
169
|
+
*
|
|
170
|
+
* Optional here for the reason given on {@link IKuzzleConfiguration.version}.
|
|
171
|
+
*/
|
|
172
|
+
deprecatedSettings?: string[];
|
|
166
173
|
};
|
|
167
174
|
/**
|
|
168
175
|
* Collection specifications to apply at startup, by index then collection.
|
|
@@ -21,9 +21,10 @@ export type SecurityConfiguration = {
|
|
|
21
21
|
/**
|
|
22
22
|
* @deprecated Use `security.authToken` instead.
|
|
23
23
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* v2.56.0 declared it:
|
|
24
|
+
* Each setting the configuration sets here applies unless `authToken` sets
|
|
25
|
+
* it too (`loadConfig()` resolves both sections to the values in effect,
|
|
26
|
+
* see `authTokenSetting`). Optional all the same, as v2.56.0 declared it:
|
|
27
|
+
* a required member breaks code that builds this type.
|
|
27
28
|
*/
|
|
28
29
|
jwt?: JSONObject;
|
|
29
30
|
/**
|
|
@@ -54,8 +54,8 @@ export type ControllerDefinition = {
|
|
|
54
54
|
handler: (request: KuzzleRequest) => Promise<any>;
|
|
55
55
|
/**
|
|
56
56
|
* Declare HTTP routes (optional).
|
|
57
|
-
*
|
|
58
|
-
*
|
|
57
|
+
* When omitted or empty, a default route is generated:
|
|
58
|
+
* `GET /_/<controller>/<action>`, both names in kebab-case.
|
|
59
59
|
*/
|
|
60
60
|
http?: HttpRoute[];
|
|
61
61
|
};
|
|
@@ -71,8 +71,8 @@ export type HttpRoute = {
|
|
|
71
71
|
verb: "get" | "head" | "post" | "put" | "delete" | "patch" | "options";
|
|
72
72
|
/**
|
|
73
73
|
* Route path.
|
|
74
|
-
* A route starting with `/`
|
|
75
|
-
*
|
|
74
|
+
* A route starting with `/` is used as is, any other route is prefixed
|
|
75
|
+
* with `/_/`.
|
|
76
76
|
*/
|
|
77
77
|
path: string;
|
|
78
78
|
/**
|
package/dist/lib/util/mutex.d.ts
CHANGED
|
@@ -86,9 +86,9 @@ export declare class Mutex {
|
|
|
86
86
|
*
|
|
87
87
|
* @return {Promise.<boolean>} True if the ressource has been unlocked before the `timeout`
|
|
88
88
|
*/
|
|
89
|
-
wait({ attemptDelay, timeout, }
|
|
90
|
-
attemptDelay?: number
|
|
91
|
-
timeout?: number
|
|
89
|
+
wait({ attemptDelay, timeout, }?: {
|
|
90
|
+
attemptDelay?: number;
|
|
91
|
+
timeout?: number;
|
|
92
92
|
}): Promise<boolean>;
|
|
93
93
|
/**
|
|
94
94
|
* Frees every lock this process holds, and stops it from taking new ones.
|
package/dist/lib/util/mutex.js
CHANGED
|
@@ -196,7 +196,7 @@ class Mutex {
|
|
|
196
196
|
*
|
|
197
197
|
* @return {Promise.<boolean>} True if the ressource has been unlocked before the `timeout`
|
|
198
198
|
*/
|
|
199
|
-
async wait({ attemptDelay = this.attemptDelay, timeout = this.timeout, }) {
|
|
199
|
+
async wait({ attemptDelay = this.attemptDelay, timeout = this.timeout, } = {}) {
|
|
200
200
|
let duration = 0;
|
|
201
201
|
let isLocked;
|
|
202
202
|
do {
|
package/dist/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.60.0-beta.
|
|
4
|
+
"version": "2.60.0-beta.5",
|
|
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
|
"scripts": {
|
|
7
7
|
"build": "rm -Rf ./dist && tsc && node -r ts-node/register/transpile-only ./bin/copy-binaries.ts",
|
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.60.0-beta.
|
|
4
|
+
"version": "2.60.0-beta.5",
|
|
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
|
"scripts": {
|
|
7
7
|
"build": "rm -Rf ./dist && tsc && node -r ts-node/register/transpile-only ./bin/copy-binaries.ts",
|