kuzzle 2.60.0-beta.3 → 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/controllers/documentController.d.ts +1 -1
- 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/cluster/command.d.ts +7 -0
- package/dist/lib/cluster/command.js +67 -28
- package/dist/lib/cluster/node.d.ts +13 -2
- package/dist/lib/cluster/node.js +20 -4
- package/dist/lib/cluster/subscriber.d.ts +2 -0
- package/dist/lib/cluster/subscriber.js +6 -0
- package/dist/lib/config/index.js +60 -3
- package/dist/lib/core/backend/backend.d.ts +4 -3
- package/dist/lib/core/backend/backend.js +15 -15
- package/dist/lib/core/backend/backendCluster.d.ts +1 -1
- 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/shared/store.js +4 -0
- package/dist/lib/core/storage/clientAdapter.d.ts +6 -0
- package/dist/lib/core/storage/clientAdapter.js +40 -5
- package/dist/lib/kuzzle/Logger.d.ts +4 -0
- package/dist/lib/kuzzle/Logger.js +9 -2
- package/dist/lib/kuzzle/internalIndexHandler.js +28 -11
- 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/model/security/profile.d.ts +1 -1
- package/dist/lib/model/security/role.d.ts +1 -1
- package/dist/lib/model/security/user.d.ts +1 -1
- package/dist/lib/service/storage/7/elasticsearch.d.ts +18 -0
- package/dist/lib/service/storage/7/elasticsearch.js +50 -10
- package/dist/lib/service/storage/7/esWrapper.js +31 -2
- package/dist/lib/service/storage/8/elasticsearch.d.ts +18 -0
- package/dist/lib/service/storage/8/elasticsearch.js +50 -10
- package/dist/lib/service/storage/8/esWrapper.js +31 -2
- 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/types/events/EventGenericDocument.d.ts +2 -1
- package/dist/lib/types/events/EventProtocol.d.ts +1 -1
- package/dist/lib/util/mutex.d.ts +5 -4
- package/dist/lib/util/mutex.js +8 -2
- package/dist/package.json +1 -1
- package/package.json +1 -1
|
@@ -61,8 +61,8 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
61
61
|
const kuzzle_1 = __importDefault(require("../../kuzzle"));
|
|
62
62
|
const embeddedSdk_1 = require("../shared/sdk/embeddedSdk");
|
|
63
63
|
const kerror = __importStar(require("../../kerror"));
|
|
64
|
-
const
|
|
65
|
-
const
|
|
64
|
+
const name_generator_1 = require("../../util/name-generator");
|
|
65
|
+
const index_1 = require("./index");
|
|
66
66
|
const Logger_1 = require("../../kuzzle/Logger");
|
|
67
67
|
const plugin_1 = __importDefault(require("../plugin/plugin"));
|
|
68
68
|
const assertionError = kerror.wrap("plugin", "assert");
|
|
@@ -130,7 +130,7 @@ class Backend {
|
|
|
130
130
|
throw assertionError.get("invalid_application_name", name);
|
|
131
131
|
}
|
|
132
132
|
this._name = name;
|
|
133
|
-
this.nodeId =
|
|
133
|
+
this.nodeId = name_generator_1.NameGenerator.generateRandomName({ prefix: "knode" });
|
|
134
134
|
Reflect.defineProperty(this, "_kuzzle", {
|
|
135
135
|
writable: true,
|
|
136
136
|
});
|
|
@@ -162,18 +162,18 @@ class Backend {
|
|
|
162
162
|
// Silent if no version can be found
|
|
163
163
|
}
|
|
164
164
|
global.app = this;
|
|
165
|
-
this.pipe = new
|
|
166
|
-
this.hook = new
|
|
167
|
-
this.config = new
|
|
168
|
-
this.vault = new
|
|
169
|
-
this.controller = new
|
|
170
|
-
this.plugin = new
|
|
171
|
-
this.storage = new
|
|
172
|
-
this.import = new
|
|
173
|
-
this.cluster = new
|
|
174
|
-
this.openApi = new
|
|
175
|
-
this.errors = new
|
|
176
|
-
this.subscription = new
|
|
165
|
+
this.pipe = new index_1.BackendPipe(this);
|
|
166
|
+
this.hook = new index_1.BackendHook(this);
|
|
167
|
+
this.config = new index_1.BackendConfig(this);
|
|
168
|
+
this.vault = new index_1.BackendVault(this);
|
|
169
|
+
this.controller = new index_1.BackendController(this);
|
|
170
|
+
this.plugin = new index_1.BackendPlugin(this);
|
|
171
|
+
this.storage = new index_1.BackendStorage(this);
|
|
172
|
+
this.import = new index_1.BackendImport(this);
|
|
173
|
+
this.cluster = new index_1.BackendCluster();
|
|
174
|
+
this.openApi = new index_1.BackendOpenApi(this);
|
|
175
|
+
this.errors = new index_1.BackendErrors(this);
|
|
176
|
+
this.subscription = new index_1.BackendSubscription(this);
|
|
177
177
|
this.log = new Logger_1.Logger(this.config.content, "kuzzle:app");
|
|
178
178
|
this.kerror = kerror;
|
|
179
179
|
try {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EventDefinition, ClusterEventHandler } from "
|
|
1
|
+
import type { EventDefinition, ClusterEventHandler } from "../../types/EventHandler";
|
|
2
2
|
export declare class BackendCluster {
|
|
3
3
|
/**
|
|
4
4
|
* Broadcasts an event to listeners across all registered Kuzzle nodes
|
|
@@ -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 = {}) {
|
|
@@ -156,6 +156,10 @@ class Store {
|
|
|
156
156
|
}
|
|
157
157
|
const exist = await global.kuzzle.ask(`core:storage:${this.scope}:collection:exist`, this.index, collection);
|
|
158
158
|
if (exist) {
|
|
159
|
+
// Another node created it, maybe so recently that its primary
|
|
160
|
+
// shards are still initializing: a document request on it would
|
|
161
|
+
// get a 503 until they are active
|
|
162
|
+
await global.kuzzle.ask(`core:storage:${this.scope}:collection:shards:wait`, this.index, collection);
|
|
159
163
|
// @deprecated
|
|
160
164
|
const dynamicSettings = isConfigDeprecated
|
|
161
165
|
? null
|
|
@@ -41,6 +41,12 @@ declare class ClientAdapter {
|
|
|
41
41
|
* Also checks for duplicated index names.
|
|
42
42
|
*/
|
|
43
43
|
populateCache(): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Adds the indexes and collections found in storage to the index cache,
|
|
46
|
+
* then reloads the collection write locks. Unlike `populateCache`, it never
|
|
47
|
+
* writes to storage.
|
|
48
|
+
*/
|
|
49
|
+
reloadCache(): Promise<void>;
|
|
44
50
|
/**
|
|
45
51
|
* Replaces the cached collection write locks with the cluster-wide ones,
|
|
46
52
|
* stored in Redis.
|
|
@@ -56,6 +56,7 @@ const Elasticsearch_1 = require("../../service/storage/Elasticsearch");
|
|
|
56
56
|
const indexCache_1 = require("./indexCache");
|
|
57
57
|
const safeObject_1 = require("../../util/safeObject");
|
|
58
58
|
const kerror = __importStar(require("../../kerror"));
|
|
59
|
+
const kuzzleError_1 = require("../../kerror/errors/kuzzleError");
|
|
59
60
|
const distributedLock_1 = require("../../util/distributedLock");
|
|
60
61
|
require("../../types/Global");
|
|
61
62
|
const servicesError = kerror.wrap("services", "storage");
|
|
@@ -93,10 +94,16 @@ class ClientAdapter {
|
|
|
93
94
|
this.registerMappingEvents();
|
|
94
95
|
this.registerCacheEvents();
|
|
95
96
|
this.registerLockEvents();
|
|
96
|
-
// The locks were first loaded
|
|
97
|
-
// cluster:
|
|
98
|
-
//
|
|
99
|
-
|
|
97
|
+
// The cache and the locks were first loaded before this node joined the
|
|
98
|
+
// cluster: an index, a collection or a lock a peer added in between was
|
|
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.
|
|
106
|
+
global.kuzzle.onPipe("kuzzle:state:live", () => this.reloadCache());
|
|
100
107
|
// Global store events registration
|
|
101
108
|
/**
|
|
102
109
|
* Manually refresh the index cache (e.g. after alias creation)
|
|
@@ -120,7 +127,20 @@ class ClientAdapter {
|
|
|
120
127
|
throw servicesError.get("index_already_exists", this.scope, index);
|
|
121
128
|
}
|
|
122
129
|
if (!indexCacheOnly) {
|
|
123
|
-
|
|
130
|
+
try {
|
|
131
|
+
await this.client.createIndex(index);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
// The index exists in storage but not in this node's cache: a peer
|
|
135
|
+
// created it and its cache broadcast never reached this node (e.g. the
|
|
136
|
+
// peer stopped before this node joined the cluster). Cache it so that
|
|
137
|
+
// the next request to that index finds it, then report the conflict.
|
|
138
|
+
if (error instanceof kuzzleError_1.KuzzleError &&
|
|
139
|
+
error.id === "services.storage.index_already_exists") {
|
|
140
|
+
this.cache.addIndex(index);
|
|
141
|
+
}
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
124
144
|
}
|
|
125
145
|
this.cache.addIndex(index);
|
|
126
146
|
if (propagate) {
|
|
@@ -191,6 +211,14 @@ class ClientAdapter {
|
|
|
191
211
|
if (global.kuzzle.config.services.storageEngine.generateMissingAliases) {
|
|
192
212
|
await this.client.generateMissingAliases();
|
|
193
213
|
}
|
|
214
|
+
await this.reloadCache();
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Adds the indexes and collections found in storage to the index cache,
|
|
218
|
+
* then reloads the collection write locks. Unlike `populateCache`, it never
|
|
219
|
+
* writes to storage.
|
|
220
|
+
*/
|
|
221
|
+
async reloadCache() {
|
|
194
222
|
// `client` is untyped (see the field's comment), so the schema shape is
|
|
195
223
|
// stated here: index name -> its collection names.
|
|
196
224
|
const schema = await this.client.getSchema();
|
|
@@ -311,6 +339,13 @@ class ClientAdapter {
|
|
|
311
339
|
this.cache.assertCollectionExists(index, collection);
|
|
312
340
|
return this.client.getSettings(index, collection);
|
|
313
341
|
});
|
|
342
|
+
/**
|
|
343
|
+
* Wait, for a bounded time, until the primary shards of a collection
|
|
344
|
+
* another node has just created are active
|
|
345
|
+
* @param {string} index
|
|
346
|
+
* @param {string} collection
|
|
347
|
+
*/
|
|
348
|
+
global.kuzzle.onAsk(`core:storage:${this.scope}:collection:shards:wait`, (index, collection) => this.client.waitForCollectionShards(index, collection));
|
|
314
349
|
/**
|
|
315
350
|
* Check a collection existence
|
|
316
351
|
* @param {string} index
|
|
@@ -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");
|
|
@@ -132,8 +133,11 @@ class InternalIndexHandler extends store_1.Store {
|
|
|
132
133
|
return;
|
|
133
134
|
}
|
|
134
135
|
await bluebird_1.default.resolve(this._bootstrapSequence()).timeout(this.timeout);
|
|
135
|
-
|
|
136
|
-
|
|
136
|
+
// A node that stopped while holding the lock may still have its
|
|
137
|
+
// own bootstrap in flight, and write this marker after the check
|
|
138
|
+
// above: replacing it keeps this node from failing on it
|
|
139
|
+
await this.createOrReplace("config", this._BOOTSTRAP_DONE_ID, {
|
|
140
|
+
timestamp: Date.now(),
|
|
137
141
|
});
|
|
138
142
|
}
|
|
139
143
|
catch (error) {
|
|
@@ -154,9 +158,11 @@ class InternalIndexHandler extends store_1.Store {
|
|
|
154
158
|
await this.createInitialValidations();
|
|
155
159
|
debug("Bootstrapping JWT secret");
|
|
156
160
|
await this._initSecret();
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
161
|
+
// Replaced rather than created: a bootstrap interrupted after this write
|
|
162
|
+
// but before the `.done` marker leaves it behind, and the next attempt
|
|
163
|
+
// must not fail on it (every step above is idempotent too)
|
|
164
|
+
await this.createOrReplace("config", this._DATAMODEL_VERSION_ID, {
|
|
165
|
+
version: dataModelVersion,
|
|
160
166
|
});
|
|
161
167
|
}
|
|
162
168
|
/**
|
|
@@ -186,16 +192,27 @@ class InternalIndexHandler extends store_1.Store {
|
|
|
186
192
|
await bluebird_1.default.all(promises);
|
|
187
193
|
}
|
|
188
194
|
async _initSecret() {
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
const { authToken, jwt } = global.kuzzle.config.security; // NOSONAR
|
|
193
|
-
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");
|
|
194
198
|
let storedSeed = await this.exists("config", this._JWT_SECRET_ID);
|
|
195
199
|
if (!configSeed) {
|
|
196
200
|
if (!storedSeed) {
|
|
197
201
|
storedSeed = node_crypto_1.default.randomBytes(512).toString("hex");
|
|
198
|
-
|
|
202
|
+
try {
|
|
203
|
+
await this.create("config", { seed: storedSeed }, { id: this._JWT_SECRET_ID });
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
// An interrupted bootstrap stored one in the meantime: it is the
|
|
207
|
+
// seed other nodes may already sign with, so keep it, and read it
|
|
208
|
+
// back below
|
|
209
|
+
const alreadyStored = error instanceof Error &&
|
|
210
|
+
"id" in error &&
|
|
211
|
+
error.id === "services.storage.document_already_exists";
|
|
212
|
+
if (!alreadyStored) {
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
199
216
|
}
|
|
200
217
|
this.logger.warn("[!] Kuzzle is using a generated seed for authentication. This is suitable for development but should NEVER be used in production. See https://docs.kuzzle.io/core/2/guides/getting-started/deploy-your-application/");
|
|
201
218
|
}
|
|
@@ -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
|
*
|