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