@universis/janitor 1.8.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -1,113 +1,88 @@
1
+ import 'core-js/stable/string/replace-all';
1
2
  import { ApplicationService, TraceUtils, ConfigurationStrategy, Args, HttpForbiddenError, DataError, HttpError } from '@themost/common';
2
3
  import { rateLimit } from 'express-rate-limit';
3
4
  import express from 'express';
4
5
  import path from 'path';
6
+ import { BehaviorSubject } from 'rxjs';
5
7
  import slowDown from 'express-slow-down';
6
- import RedisStore from 'rate-limit-redis';
8
+ import { RedisStore } from 'rate-limit-redis';
7
9
  import { Redis } from 'ioredis';
8
10
  import '@themost/promise-sequence';
9
11
  import url, { URL } from 'url';
10
12
  import { Request } from 'superagent';
11
13
  import jwt from 'jsonwebtoken';
12
14
 
13
- if (!String.prototype.replaceAll) {
14
- String.prototype.replaceAll = function (str, newStr) {
15
- // If a regex pattern
16
- if (Object.prototype.toString.call(str).toLowerCase() === '[object regexp]') {
17
- return this.replace(str, newStr);
18
- }
19
- // If a string
20
- return this.replace(new RegExp(str, 'g'), newStr);
21
- };
22
- }
23
-
24
15
  class RateLimitService extends ApplicationService {
25
16
  /**
26
17
  * @param {import('@themost/express').ExpressDataApplication} app
27
18
  */
28
19
  constructor(app) {
29
20
  super(app);
30
- app.serviceRouter.subscribe((serviceRouter) => {
31
- if (serviceRouter == null) {
21
+
22
+ // get proxy address forwarding option
23
+ const proxyAddressForwarding = app.getConfiguration().getSourceAt('settings/universis/api/proxyAddressForwarding');
24
+ this.proxyAddressForwarding = typeof proxyAddressForwarding !== 'boolean' ? false : proxyAddressForwarding;
25
+
26
+ /**
27
+ * @type {BehaviorSubject<{ target: RateLimitService }>}
28
+ */
29
+ this.loaded = new BehaviorSubject(null);
30
+
31
+ const serviceContainer = this.getServiceContainer();
32
+ if (serviceContainer == null) {
33
+ TraceUtils.warn(`${this.getServiceName()} is being started but the parent router seems to be unavailable.`);
34
+ return;
35
+ }
36
+ serviceContainer.subscribe((router) => {
37
+ if (router == null) {
32
38
  return;
33
39
  }
34
40
  try {
35
- const addRouter = express.Router();
36
- let serviceConfiguration = app.getConfiguration().getSourceAt('settings/universis/janitor/rateLimit') || {
37
- profiles: [],
38
- paths: []
39
- };
40
- if (serviceConfiguration.extends) {
41
- // get additional configuration
42
- const configurationPath = app.getConfiguration().getConfigurationPath();
43
- const extendsPath = path.resolve(configurationPath, serviceConfiguration.extends);
44
- TraceUtils.log(`@universis/janitor#RateLimitService will try to extend service configuration from ${extendsPath}`);
45
- serviceConfiguration = require(extendsPath);
46
- }
47
- const pathsArray = serviceConfiguration.paths || [];
48
- const profilesArray = serviceConfiguration.profiles || [];
41
+ // set router for further processing
42
+ Object.defineProperty(this, 'router', {
43
+ value: express.Router(),
44
+ writable: false,
45
+ enumerable: false,
46
+ configurable: true
47
+ });
48
+ const serviceConfiguration = this.getServiceConfiguration();
49
49
  // create maps
50
- const paths = new Map(pathsArray);
51
- const profiles = new Map(profilesArray);
50
+ const paths = serviceConfiguration.paths;
52
51
  if (paths.size === 0) {
53
- TraceUtils.warn('@universis/janitor#RateLimitService is being started but the collection of paths is empty.');
54
- }
55
- // get proxy address forwarding option
56
- let proxyAddressForwarding = app.getConfiguration().getSourceAt('settings/universis/api/proxyAddressForwarding');
57
- if (typeof proxyAddressForwarding !== 'boolean') {
58
- proxyAddressForwarding = false;
52
+ TraceUtils.warn(`${this.getServiceName()} is being started but the collection of paths is empty.`);
59
53
  }
60
54
  paths.forEach((value, path) => {
61
- let profile;
62
- // get profile
63
- if (value.profile) {
64
- profile = profiles.get(value.profile);
65
- } else {
66
- // or options defined inline
67
- profile = value;
68
- }
69
- if (profile != null) {
70
- const rateLimitOptions = Object.assign({
71
- windowMs: 5 * 60 * 1000, // 5 minutes
72
- limit: 50, // 50 requests
73
- legacyHeaders: true // send headers
74
- }, profile, {
75
- keyGenerator: (req) => {
76
- let remoteAddress;
77
- if (proxyAddressForwarding) {
78
- // get proxy headers or remote address
79
- remoteAddress = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
80
- } else {
81
- // get remote address
82
- remoteAddress = req.connection ? req.connection.remoteAddress : req.socket.remoteAddress;
83
- }
84
- return `${path}:${remoteAddress}`;
85
- }
55
+ this.set(path, value);
56
+ });
57
+ if (router.stack) {
58
+ router.stack.unshift.apply(router.stack, this.router.stack);
59
+ } else {
60
+ // use router
61
+ router.use(this.router);
62
+ // get router stack (use a workaround for express 4.x)
63
+ const stack = router._router && router._router.stack;
64
+ if (Array.isArray(stack)) {
65
+ // stage #1 find logger middleware (for supporting request logging)
66
+ let index = stack.findIndex((item) => {
67
+ return item.name === 'logger';
86
68
  });
87
- if (typeof rateLimitOptions.store === 'string') {
88
- // load store
89
- const store = rateLimitOptions.store.split('#');
90
- let StoreClass;
91
- if (store.length === 2) {
92
- const storeModule = require(store[0]);
93
- if (Object.prototype.hasOwnProperty.call(storeModule, store[1])) {
94
- StoreClass = storeModule[store[1]];
95
- rateLimitOptions.store = new StoreClass(this, rateLimitOptions);
96
- } else {
97
- throw new Error(`${store} cannot be found or is inaccessible`);
98
- }
99
- } else {
100
- StoreClass = require(store[0]);
101
- // create store
102
- rateLimitOptions.store = new StoreClass(this, rateLimitOptions);
103
- }
69
+ if (index === -1) {
70
+ // stage #2 find expressInit middleware
71
+ index = stack.findIndex((item) => {
72
+ return item.name === 'expressInit';
73
+ });
104
74
  }
105
- addRouter.use(path, rateLimit(rateLimitOptions));
75
+ // if found, move the last middleware to be after expressInit
76
+ if (index > -1) {
77
+ // move the last middleware to be after expressInit
78
+ stack.splice(index + 1, 0, stack.pop());
79
+ }
80
+ } else {
81
+ TraceUtils.warn(`${this.getServiceName()} is being started but the container stack is not available.`);
106
82
  }
107
- });
108
- if (addRouter.stack.length) {
109
- serviceRouter.stack.unshift.apply(serviceRouter.stack, addRouter.stack);
110
83
  }
84
+ // notify that the service is loaded
85
+ this.loaded.next({ target: this });
111
86
  } catch (err) {
112
87
  TraceUtils.error('An error occurred while validating rate limit configuration.');
113
88
  TraceUtils.error(err);
@@ -116,106 +91,218 @@ class RateLimitService extends ApplicationService {
116
91
  });
117
92
  }
118
93
 
94
+ /**
95
+ * Returns the service router that is used to register rate limit middleware.
96
+ * @returns {import('rxjs').BehaviorSubject<import('express').Router | import('express').Application>} The service router.
97
+ */
98
+ getServiceContainer() {
99
+ return this.getApplication() && this.getApplication().serviceRouter;
100
+ }
101
+
102
+ /**
103
+ * Returns the service name.
104
+ * @returns {string} The service name.
105
+ */
106
+ getServiceName() {
107
+ return '@universis/janitor#RateLimitService';
108
+ }
109
+ /**
110
+ * Returns the service configuration.
111
+ * @returns {{extends?: string, profiles?: Array, paths?: Array}} The service configuration.
112
+ */
113
+ getServiceConfiguration() {
114
+ if (this.serviceConfiguration) {
115
+ return this.serviceConfiguration;
116
+ }
117
+ let serviceConfiguration = {
118
+ profiles: [],
119
+ paths: []
120
+ };
121
+ // get service configuration
122
+ const serviceConfigurationSource = this.getApplication().getConfiguration().getSourceAt('settings/universis/janitor/rateLimit');
123
+ if (serviceConfigurationSource) {
124
+ if (typeof serviceConfigurationSource.extends === 'string') {
125
+ // get additional configuration
126
+ const configurationPath = this.getApplication().getConfiguration().getConfigurationPath();
127
+ const extendsPath = path.resolve(configurationPath, serviceConfigurationSource.extends);
128
+ TraceUtils.log(`${this.getServiceName()} will try to extend service configuration using ${extendsPath}`);
129
+ serviceConfiguration = Object.assign({}, {
130
+ profiles: [],
131
+ paths: []
132
+ }, require(extendsPath));
133
+ } else {
134
+ TraceUtils.log(`${this.getServiceName()} will use service configuration from settings/universis/janitor/rateLimit`);
135
+ serviceConfiguration = Object.assign({}, {
136
+ profiles: [],
137
+ paths: []
138
+ }, serviceConfigurationSource);
139
+ }
140
+ }
141
+ const pathsArray = serviceConfiguration.paths || [];
142
+ const profilesArray = serviceConfiguration.profiles || [];
143
+ // create maps
144
+ serviceConfiguration.paths = new Map(pathsArray);
145
+ serviceConfiguration.profiles = new Map(profilesArray);
146
+ // set service configuration
147
+ Object.defineProperty(this, 'serviceConfiguration', {
148
+ value: serviceConfiguration,
149
+ writable: false,
150
+ enumerable: false,
151
+ configurable: true
152
+ });
153
+ return this.serviceConfiguration;
154
+ }
155
+
156
+ /**
157
+ * Sets the rate limit configuration for a specific path.
158
+ * @param {string} path
159
+ * @param {{ profile: string } | import('express-rate-limit').Options} options
160
+ * @returns {RateLimitService} The service instance for chaining.
161
+ */
162
+ set(path, options) {
163
+ let opts;
164
+ // get profile
165
+ if (options.profile) {
166
+ opts = this.serviceConfiguration.profiles.get(options.profile);
167
+ } else {
168
+ // or options defined inline
169
+ opts = options;
170
+ }
171
+ /**
172
+ * @type { import('express-rate-limit').Options }
173
+ */
174
+ const rateLimitOptions = Object.assign({
175
+ windowMs: 5 * 60 * 1000, // 5 minutes
176
+ limit: 50, // 50 requests
177
+ legacyHeaders: true // send headers
178
+ }, opts, {
179
+ keyGenerator: (req) => {
180
+ let remoteAddress;
181
+ if (this.proxyAddressForwarding) {
182
+ // get proxy headers or remote address
183
+ remoteAddress = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
184
+ } else {
185
+ // get remote address
186
+ remoteAddress = req.connection ? req.connection.remoteAddress : req.socket.remoteAddress;
187
+ }
188
+ return `${path}:${remoteAddress}`;
189
+ }
190
+ });
191
+ if (typeof rateLimitOptions.store === 'undefined') {
192
+ const StoreClass = this.getStoreType();
193
+ if (typeof StoreClass === 'function') {
194
+ rateLimitOptions.store = new StoreClass(this, rateLimitOptions);
195
+ }
196
+ }
197
+ this.router.use(path, rateLimit(rateLimitOptions));
198
+ return this;
199
+ }
200
+
201
+ /**
202
+ * @returns {function} The type of store used for rate limiting.
203
+ */
204
+ getStoreType() {
205
+ const serviceConfiguration = this.getServiceConfiguration();
206
+ if (typeof serviceConfiguration.storeType !== 'string') {
207
+ return;
208
+ }
209
+ let StoreClass;
210
+ const store = serviceConfiguration.storeType.split('#');
211
+ if (store.length === 2) {
212
+ const storeModule = require(store[0]);
213
+ if (Object.prototype.hasOwnProperty.call(storeModule, store[1])) {
214
+ StoreClass = storeModule[store[1]];
215
+ return StoreClass;
216
+ } else {
217
+ throw new Error(`${store} cannot be found or is inaccessible`);
218
+ }
219
+ } else {
220
+ StoreClass = require(store[0]);
221
+ return StoreClass;
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Unsets the rate limit configuration for a specific path.
227
+ * @param {string} path
228
+ * @returns {RateLimitService} The service instance for chaining.
229
+ */
230
+ unset(path) {
231
+ const index = this.router.stack.findIndex((layer) => {
232
+ return layer.route && layer.route.path === path;
233
+ });
234
+ if (index !== -1) {
235
+ this.router.stack.splice(index, 1);
236
+ }
237
+ return this;
238
+ }
239
+
119
240
  }
120
241
 
121
242
  class SpeedLimitService extends ApplicationService {
122
243
  constructor(app) {
123
244
  super(app);
124
245
 
125
- app.serviceRouter.subscribe((serviceRouter) => {
126
- if (serviceRouter == null) {
246
+ // get proxy address forwarding option
247
+ const proxyAddressForwarding = app.getConfiguration().getSourceAt('settings/universis/api/proxyAddressForwarding');
248
+ this.proxyAddressForwarding = typeof proxyAddressForwarding !== 'boolean' ? false : proxyAddressForwarding;
249
+
250
+ /**
251
+ * @type {BehaviorSubject<{ target: SpeedLimitService }>}
252
+ */
253
+ this.loaded = new BehaviorSubject(null);
254
+
255
+ const serviceContainer = this.getServiceContainer();
256
+ if (serviceContainer == null) {
257
+ TraceUtils.warn(`${this.getServiceName()} is being started but the parent router seems to be unavailable.`);
258
+ return;
259
+ }
260
+
261
+ serviceContainer.subscribe((router) => {
262
+ if (router == null) {
127
263
  return;
128
264
  }
129
265
  try {
130
- const addRouter = express.Router();
131
- let serviceConfiguration = app.getConfiguration().getSourceAt('settings/universis/janitor/speedLimit') || {
132
- profiles: [],
133
- paths: []
134
- };
135
- if (serviceConfiguration.extends) {
136
- // get additional configuration
137
- const configurationPath = app.getConfiguration().getConfigurationPath();
138
- const extendsPath = path.resolve(configurationPath, serviceConfiguration.extends);
139
- TraceUtils.log(`@universis/janitor#SpeedLimitService will try to extend service configuration from ${extendsPath}`);
140
- serviceConfiguration = require(extendsPath);
141
- }
142
- const pathsArray = serviceConfiguration.paths || [];
143
- const profilesArray = serviceConfiguration.profiles || [];
266
+ // set router for further processing
267
+ Object.defineProperty(this, 'router', {
268
+ value: express.Router(),
269
+ writable: false,
270
+ enumerable: false,
271
+ configurable: true
272
+ });
273
+ const serviceConfiguration = this.getServiceConfiguration();
144
274
  // create maps
145
- const paths = new Map(pathsArray);
146
- const profiles = new Map(profilesArray);
275
+ const paths = serviceConfiguration.paths;
147
276
  if (paths.size === 0) {
148
- TraceUtils.warn('@universis/janitor#SpeedLimitService is being started but the collection of paths is empty.');
149
- }
150
- // get proxy address forwarding option
151
- let proxyAddressForwarding = app.getConfiguration().getSourceAt('settings/universis/api/proxyAddressForwarding');
152
- if (typeof proxyAddressForwarding !== 'boolean') {
153
- proxyAddressForwarding = false;
277
+ TraceUtils.warn(`${this.getServiceName()} is being started but the collection of paths is empty.`);
154
278
  }
155
279
  paths.forEach((value, path) => {
156
- let profile;
157
- // get profile
158
- if (value.profile) {
159
- profile = profiles.get(value.profile);
160
- } else {
161
- // or options defined inline
162
- profile = value;
163
- }
164
- if (profile != null) {
165
- const slowDownOptions = Object.assign({
166
- windowMs: 5 * 60 * 1000, // 5 minutes
167
- delayAfter: 20, // 20 requests
168
- delayMs: 500, // 500 ms
169
- maxDelayMs: 10000 // 10 seconds
170
- }, profile, {
171
- keyGenerator: (req) => {
172
- let remoteAddress;
173
- if (proxyAddressForwarding) {
174
- // get proxy headers or remote address
175
- remoteAddress = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
176
- } else {
177
- // get remote address
178
- remoteAddress = req.connection ? req.connection.remoteAddress : req.socket.remoteAddress;
179
- }
180
- return `${path}:${remoteAddress}`;
181
- }
280
+ this.set(path, value);
281
+ });
282
+ if (router.stack) {
283
+ router.stack.unshift.apply(router.stack, this.router.stack);
284
+ } else {
285
+ // use router
286
+ router.use(this.router);
287
+ // get router stack (use a workaround for express 4.x)
288
+ const stack = router._router && router._router.stack;
289
+ if (Array.isArray(stack)) {
290
+ // stage #1 find logger middleware (for supporting request logging)
291
+ let index = stack.findIndex((item) => {
292
+ return item.name === 'logger';
182
293
  });
183
- if (Array.isArray(slowDownOptions.randomDelayMs)) {
184
- slowDownOptions.delayMs = () => {
185
- const delayMs = Math.floor(Math.random() * (slowDownOptions.randomDelayMs[1] - slowDownOptions.randomDelayMs[0] + 1) + slowDownOptions.randomDelayMs[0]);
186
- return delayMs;
187
- };
188
- }
189
- if (Array.isArray(slowDownOptions.randomMaxDelayMs)) {
190
- slowDownOptions.maxDelayMs = () => {
191
- const maxDelayMs = Math.floor(Math.random() * (slowDownOptions.randomMaxDelayMs[1] - slowDownOptions.randomMaxDelayMs[0] + 1) + slowDownOptions.randomMaxDelayMs[0]);
192
- return maxDelayMs;
193
- };
194
- }
195
- if (typeof slowDownOptions.store === 'string') {
196
- // load store
197
- const store = slowDownOptions.store.split('#');
198
- let StoreClass;
199
- if (store.length === 2) {
200
- const storeModule = require(store[0]);
201
- if (Object.prototype.hasOwnProperty.call(storeModule, store[1])) {
202
- StoreClass = storeModule[store[1]];
203
- slowDownOptions.store = new StoreClass(this, slowDownOptions);
204
- } else {
205
- throw new Error(`${store} cannot be found or is inaccessible`);
206
- }
207
- } else {
208
- StoreClass = require(store[0]);
209
- // create store
210
- slowDownOptions.store = new StoreClass(this, slowDownOptions);
211
- }
294
+ if (index === -1) {
295
+ // stage #2 find expressInit middleware
296
+ index = stack.findIndex((item) => {
297
+ return item.name === 'expressInit';
298
+ });
212
299
  }
213
- addRouter.use(path, slowDown(slowDownOptions));
300
+ } else {
301
+ TraceUtils.warn(`${this.getServiceName()} is being started but the container stack is not available.`);
214
302
  }
215
- });
216
- if (addRouter.stack.length) {
217
- serviceRouter.stack.unshift.apply(serviceRouter.stack, addRouter.stack);
218
303
  }
304
+ // notify that the service is loaded
305
+ this.loaded.next({ target: this });
219
306
  } catch (err) {
220
307
  TraceUtils.error('An error occurred while validating speed limit configuration.');
221
308
  TraceUtils.error(err);
@@ -224,6 +311,163 @@ class SpeedLimitService extends ApplicationService {
224
311
  });
225
312
  }
226
313
 
314
+ /**
315
+ * @returns {function} The type of store used for rate limiting.
316
+ */
317
+ getStoreType() {
318
+ const serviceConfiguration = this.getServiceConfiguration();
319
+ if (typeof serviceConfiguration.storeType !== 'string') {
320
+ return;
321
+ }
322
+ let StoreClass;
323
+ const store = serviceConfiguration.storeType.split('#');
324
+ if (store.length === 2) {
325
+ const storeModule = require(store[0]);
326
+ if (Object.prototype.hasOwnProperty.call(storeModule, store[1])) {
327
+ StoreClass = storeModule[store[1]];
328
+ return StoreClass;
329
+ } else {
330
+ throw new Error(`${store} cannot be found or is inaccessible`);
331
+ }
332
+ } else {
333
+ StoreClass = require(store[0]);
334
+ return StoreClass;
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Returns the service name.
340
+ * @returns {string} The service name.
341
+ */
342
+ getServiceName() {
343
+ return '@universis/janitor#SpeedLimitService';
344
+ }
345
+
346
+ /**
347
+ * Returns the service router that is used to register speed limit middleware.
348
+ * @returns {import('express').Router | import('express').Application} The service router.
349
+ */
350
+ getServiceContainer() {
351
+ return this.getApplication() && this.getApplication().serviceRouter;
352
+ }
353
+
354
+ /**
355
+ * Sets the speed limit configuration for a specific path.
356
+ * @param {string} path
357
+ * @param {{ profile: string } | import('express-slow-down').Options} options
358
+ * @returns {SpeedLimitService} The service instance for chaining.
359
+ */
360
+ set(path, options) {
361
+ let opts;
362
+ // get profile
363
+ if (options.profile) {
364
+ opts = this.serviceConfiguration.profiles.get(options.profile);
365
+ } else {
366
+ // or options defined inline
367
+ opts = options;
368
+ }
369
+ const slowDownOptions = Object.assign({
370
+ windowMs: 5 * 60 * 1000, // 5 minutes
371
+ delayAfter: 20, // 20 requests
372
+ delayMs: 500, // 500 ms
373
+ maxDelayMs: 10000 // 10 seconds
374
+ }, opts, {
375
+ keyGenerator: (req) => {
376
+ let remoteAddress;
377
+ if (this.proxyAddressForwarding) {
378
+ // get proxy headers or remote address
379
+ remoteAddress = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
380
+ } else {
381
+ // get remote address
382
+ remoteAddress = req.connection ? req.connection.remoteAddress : req.socket.remoteAddress;
383
+ }
384
+ return `${path}:${remoteAddress}`;
385
+ }
386
+ });
387
+ if (Array.isArray(slowDownOptions.randomDelayMs)) {
388
+ slowDownOptions.delayMs = () => {
389
+ const delayMs = Math.floor(Math.random() * (slowDownOptions.randomDelayMs[1] - slowDownOptions.randomDelayMs[0] + 1) + slowDownOptions.randomDelayMs[0]);
390
+ return delayMs;
391
+ };
392
+ }
393
+ if (Array.isArray(slowDownOptions.randomMaxDelayMs)) {
394
+ slowDownOptions.maxDelayMs = () => {
395
+ const maxDelayMs = Math.floor(Math.random() * (slowDownOptions.randomMaxDelayMs[1] - slowDownOptions.randomMaxDelayMs[0] + 1) + slowDownOptions.randomMaxDelayMs[0]);
396
+ return maxDelayMs;
397
+ };
398
+ }
399
+ if (typeof slowDownOptions.store === 'undefined') {
400
+ const StoreClass = this.getStoreType();
401
+ if (typeof StoreClass === 'function') {
402
+ slowDownOptions.store = new StoreClass(this, slowDownOptions);
403
+ }
404
+ }
405
+ this.router.use(path, slowDown(slowDownOptions));
406
+ return this;
407
+ }
408
+
409
+
410
+ /**
411
+ * Unsets the speed limit configuration for a specific path.
412
+ * @param {string} path
413
+ * @return {SpeedLimitService} The service instance for chaining.
414
+ */
415
+ unset(path) {
416
+ const index = this.router.stack.findIndex((layer) => {
417
+ return layer.route && layer.route.path === path;
418
+ });
419
+ if (index !== -1) {
420
+ this.router.stack.splice(index, 1);
421
+ }
422
+ return this;
423
+ }
424
+
425
+ /**
426
+ *
427
+ * @returns {{extends?: string, profiles?: Array, paths?: Array}} The service configuration.
428
+ */
429
+ getServiceConfiguration() {
430
+ if (this.serviceConfiguration) {
431
+ return this.serviceConfiguration;
432
+ }
433
+ let serviceConfiguration = {
434
+ profiles: [],
435
+ paths: []
436
+ };
437
+ // get service configuration
438
+ const serviceConfigurationSource = this.getApplication().getConfiguration().getSourceAt('settings/universis/janitor/speedLimit');
439
+ if (serviceConfigurationSource) {
440
+ if (typeof serviceConfigurationSource.extends === 'string') {
441
+ // get additional configuration
442
+ const configurationPath = this.getApplication().getConfiguration().getConfigurationPath();
443
+ const extendsPath = path.resolve(configurationPath, serviceConfigurationSource.extends);
444
+ TraceUtils.log(`${this.getServiceName()} will try to extend service configuration using ${extendsPath}`);
445
+ serviceConfiguration = Object.assign({}, {
446
+ profiles: [],
447
+ paths: []
448
+ }, require(extendsPath));
449
+ } else {
450
+ TraceUtils.log(`${this.getServiceName()} will use service configuration from settings/universis/janitor/speedLimit`);
451
+ serviceConfiguration = Object.assign({}, {
452
+ profiles: [],
453
+ paths: []
454
+ }, serviceConfigurationSource);
455
+ }
456
+ }
457
+ const profilesArray = serviceConfiguration.profiles || [];
458
+ serviceConfiguration.profiles = new Map(profilesArray);
459
+ const pathsArray = serviceConfiguration.paths || [];
460
+ serviceConfiguration.paths = new Map(pathsArray);
461
+
462
+ Object.defineProperty(this, 'serviceConfiguration', {
463
+ value: serviceConfiguration,
464
+ writable: false,
465
+ enumerable: false,
466
+ configurable: true
467
+ });
468
+ return this.serviceConfiguration;
469
+ }
470
+
227
471
  }
228
472
 
229
473
  function _defineProperty(e, r, t) {
@@ -902,5 +1146,41 @@ class RemoteAddressValidator extends ApplicationService {
902
1146
 
903
1147
  }
904
1148
 
905
- export { DefaultScopeAccessConfiguration, EnableScopeAccessConfiguration, ExtendScopeAccessConfiguration, HttpRemoteAddrForbiddenError, OAuth2ClientService, RateLimitService, RedisClientStore, RemoteAddressValidator, ScopeAccessConfiguration, ScopeString, SpeedLimitService, validateScope };
1149
+ class AppRateLimitService extends RateLimitService {
1150
+ /**
1151
+ *
1152
+ * @param {import('@themost/common').ApplicationBase} app
1153
+ */
1154
+ constructor(app) {
1155
+ super(app);
1156
+ }
1157
+
1158
+ getServiceName() {
1159
+ return '@universis/janitor#AppRateLimitService';
1160
+ }
1161
+
1162
+ getServiceContainer() {
1163
+ return this.getApplication() && this.getApplication().container;
1164
+ }
1165
+ }
1166
+
1167
+ class AppSpeedLimitService extends SpeedLimitService {
1168
+ /**
1169
+ *
1170
+ * @param {import('@themost/common').ApplicationBase} app
1171
+ */
1172
+ constructor(app) {
1173
+ super(app);
1174
+ }
1175
+
1176
+ getServiceName() {
1177
+ return '@universis/janitor#AppSpeedLimitService';
1178
+ }
1179
+
1180
+ getServiceContainer() {
1181
+ return this.getApplication() && this.getApplication().container;
1182
+ }
1183
+ }
1184
+
1185
+ export { AppRateLimitService, AppSpeedLimitService, DefaultScopeAccessConfiguration, EnableScopeAccessConfiguration, ExtendScopeAccessConfiguration, HttpRemoteAddrForbiddenError, OAuth2ClientService, RateLimitService, RedisClientStore, RemoteAddressValidator, ScopeAccessConfiguration, ScopeString, SpeedLimitService, validateScope };
906
1186
  //# sourceMappingURL=index.esm.js.map