@universis/janitor 1.9.0 → 1.11.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.
@@ -2,6 +2,7 @@ import { ApplicationService, TraceUtils } from '@themost/common';
2
2
  import { rateLimit } from 'express-rate-limit';
3
3
  import express from 'express';
4
4
  import path from 'path';
5
+ import { BehaviorSubject } from 'rxjs';
5
6
 
6
7
  export class RateLimitService extends ApplicationService {
7
8
  /**
@@ -9,87 +10,71 @@ export class RateLimitService extends ApplicationService {
9
10
  */
10
11
  constructor(app) {
11
12
  super(app);
12
- app.serviceRouter.subscribe(serviceRouter => {
13
- if (serviceRouter == null) {
13
+
14
+ // get proxy address forwarding option
15
+ const proxyAddressForwarding = app.getConfiguration().getSourceAt('settings/universis/api/proxyAddressForwarding');
16
+ this.proxyAddressForwarding = typeof proxyAddressForwarding !== 'boolean'? false : proxyAddressForwarding;
17
+
18
+ /**
19
+ * @type {BehaviorSubject<{ target: RateLimitService }>}
20
+ */
21
+ this.loaded = new BehaviorSubject(null);
22
+
23
+ const serviceContainer = this.getServiceContainer();
24
+ if (serviceContainer == null) {
25
+ TraceUtils.warn(`${this.getServiceName()} is being started but the parent router seems to be unavailable.`);
26
+ return;
27
+ }
28
+ serviceContainer.subscribe((router) => {
29
+ if (router == null) {
14
30
  return;
15
31
  }
16
32
  try {
17
- const addRouter = express.Router();
18
- let serviceConfiguration = app.getConfiguration().getSourceAt('settings/universis/janitor/rateLimit') || {
19
- profiles: [],
20
- paths: []
21
- };
22
- if (serviceConfiguration.extends) {
23
- // get additional configuration
24
- const configurationPath = app.getConfiguration().getConfigurationPath();
25
- const extendsPath = path.resolve(configurationPath, serviceConfiguration.extends);
26
- TraceUtils.log(`@universis/janitor#RateLimitService will try to extend service configuration from ${extendsPath}`);
27
- serviceConfiguration = require(extendsPath);
28
- }
29
- const pathsArray = serviceConfiguration.paths || [];
30
- const profilesArray = serviceConfiguration.profiles || [];
33
+ // set router for further processing
34
+ Object.defineProperty(this, 'router', {
35
+ value: express.Router(),
36
+ writable: false,
37
+ enumerable: false,
38
+ configurable: true
39
+ });
40
+ const serviceConfiguration = this.getServiceConfiguration();
31
41
  // create maps
32
- const paths = new Map(pathsArray);
33
- const profiles = new Map(profilesArray);
42
+ const paths = serviceConfiguration.paths;
34
43
  if (paths.size === 0) {
35
- TraceUtils.warn('@universis/janitor#RateLimitService is being started but the collection of paths is empty.');
36
- }
37
- // get proxy address forwarding option
38
- let proxyAddressForwarding = app.getConfiguration().getSourceAt('settings/universis/api/proxyAddressForwarding');
39
- if (typeof proxyAddressForwarding !== 'boolean') {
40
- proxyAddressForwarding = false;
44
+ TraceUtils.warn(`${this.getServiceName()} is being started but the collection of paths is empty.`);
41
45
  }
42
46
  paths.forEach((value, path) => {
43
- let profile;
44
- // get profile
45
- if (value.profile) {
46
- profile = profiles.get(value.profile);
47
- } else {
48
- // or options defined inline
49
- profile = value
50
- }
51
- if (profile != null) {
52
- const rateLimitOptions = Object.assign({
53
- windowMs: 5 * 60 * 1000, // 5 minutes
54
- limit: 50, // 50 requests
55
- legacyHeaders: true // send headers
56
- }, profile, {
57
- keyGenerator: (req) => {
58
- let remoteAddress;
59
- if (proxyAddressForwarding) {
60
- // get proxy headers or remote address
61
- remoteAddress = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
62
- } else {
63
- // get remote address
64
- remoteAddress = (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
65
- }
66
- return `${path}:${remoteAddress}`;
67
- }
47
+ this.set(path, value);
48
+ });
49
+ if (router.stack) {
50
+ router.stack.unshift.apply(router.stack, this.router.stack);
51
+ } else {
52
+ // use router
53
+ router.use(this.router);
54
+ // get router stack (use a workaround for express 4.x)
55
+ const stack = router._router && router._router.stack;
56
+ if (Array.isArray(stack)) {
57
+ // stage #1 find logger middleware (for supporting request logging)
58
+ let index = stack.findIndex((item) => {
59
+ return item.name === 'logger';
68
60
  });
69
- if (typeof rateLimitOptions.store === 'string') {
70
- // load store
71
- const store = rateLimitOptions.store.split('#');
72
- let StoreClass;
73
- if (store.length === 2) {
74
- const storeModule = require(store[0]);
75
- if (Object.prototype.hasOwnProperty.call(storeModule, store[1])) {
76
- StoreClass = storeModule[store[1]];
77
- rateLimitOptions.store = new StoreClass(this, rateLimitOptions);
78
- } else {
79
- throw new Error(`${store} cannot be found or is inaccessible`);
80
- }
81
- } else {
82
- StoreClass = require(store[0]);
83
- // create store
84
- rateLimitOptions.store = new StoreClass(this, rateLimitOptions);
85
- }
61
+ if (index === -1) {
62
+ // stage #2 find expressInit middleware
63
+ index = stack.findIndex((item) => {
64
+ return item.name === 'expressInit';
65
+ });
66
+ }
67
+ // if found, move the last middleware to be after expressInit
68
+ if (index > -1) {
69
+ // move the last middleware to be after expressInit
70
+ stack.splice(index + 1, 0, stack.pop());
86
71
  }
87
- addRouter.use(path, rateLimit(rateLimitOptions));
72
+ } else {
73
+ TraceUtils.warn(`${this.getServiceName()} is being started but the container stack is not available.`);
88
74
  }
89
- });
90
- if (addRouter.stack.length) {
91
- serviceRouter.stack.unshift.apply(serviceRouter.stack, addRouter.stack);
92
75
  }
76
+ // notify that the service is loaded
77
+ this.loaded.next({ target: this });
93
78
  } catch (err) {
94
79
  TraceUtils.error('An error occurred while validating rate limit configuration.');
95
80
  TraceUtils.error(err);
@@ -98,4 +83,150 @@ export class RateLimitService extends ApplicationService {
98
83
  });
99
84
  }
100
85
 
86
+ /**
87
+ * Returns the service router that is used to register rate limit middleware.
88
+ * @returns {import('rxjs').BehaviorSubject<import('express').Router | import('express').Application>} The service router.
89
+ */
90
+ getServiceContainer() {
91
+ return this.getApplication() && this.getApplication().serviceRouter;
92
+ }
93
+
94
+ /**
95
+ * Returns the service name.
96
+ * @returns {string} The service name.
97
+ */
98
+ getServiceName() {
99
+ return '@universis/janitor#RateLimitService';
100
+ }
101
+ /**
102
+ * Returns the service configuration.
103
+ * @returns {{extends?: string, profiles?: Array, paths?: Array}} The service configuration.
104
+ */
105
+ getServiceConfiguration() {
106
+ if (this.serviceConfiguration) {
107
+ return this.serviceConfiguration;
108
+ }
109
+ let serviceConfiguration = {
110
+ profiles: [],
111
+ paths: []
112
+ };
113
+ // get service configuration
114
+ const serviceConfigurationSource = this.getApplication().getConfiguration().getSourceAt('settings/universis/janitor/rateLimit');
115
+ if (serviceConfigurationSource) {
116
+ if (typeof serviceConfigurationSource.extends === 'string') {
117
+ // get additional configuration
118
+ const configurationPath = this.getApplication().getConfiguration().getConfigurationPath();
119
+ const extendsPath = path.resolve(configurationPath, serviceConfigurationSource.extends);
120
+ TraceUtils.log(`${this.getServiceName()} will try to extend service configuration using ${extendsPath}`);
121
+ serviceConfiguration = Object.assign({}, {
122
+ profiles: [],
123
+ paths: []
124
+ }, require(extendsPath));
125
+ } else {
126
+ TraceUtils.log(`${this.getServiceName()} will use service configuration from settings/universis/janitor/rateLimit`);
127
+ serviceConfiguration = Object.assign({}, {
128
+ profiles: [],
129
+ paths: []
130
+ }, serviceConfigurationSource);
131
+ }
132
+ }
133
+ const pathsArray = serviceConfiguration.paths || [];
134
+ const profilesArray = serviceConfiguration.profiles || [];
135
+ // create maps
136
+ serviceConfiguration.paths = new Map(pathsArray);
137
+ serviceConfiguration.profiles = new Map(profilesArray);
138
+ // set service configuration
139
+ Object.defineProperty(this, 'serviceConfiguration', {
140
+ value: serviceConfiguration,
141
+ writable: false,
142
+ enumerable: false,
143
+ configurable: true
144
+ });
145
+ return this.serviceConfiguration;
146
+ }
147
+
148
+ /**
149
+ * Sets the rate limit configuration for a specific path.
150
+ * @param {string} path
151
+ * @param {{ profile: string } | import('express-rate-limit').Options} options
152
+ * @returns {RateLimitService} The service instance for chaining.
153
+ */
154
+ set(path, options) {
155
+ let opts;
156
+ // get profile
157
+ if (options.profile) {
158
+ opts = this.serviceConfiguration.profiles.get(options.profile);
159
+ } else {
160
+ // or options defined inline
161
+ opts = options
162
+ }
163
+ /**
164
+ * @type { import('express-rate-limit').Options }
165
+ */
166
+ const rateLimitOptions = Object.assign({
167
+ windowMs: 5 * 60 * 1000, // 5 minutes
168
+ limit: 50, // 50 requests
169
+ legacyHeaders: true // send headers
170
+ }, opts, {
171
+ keyGenerator: (req) => {
172
+ let remoteAddress;
173
+ if (this.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
+ }
182
+ });
183
+ if (typeof rateLimitOptions.store === 'undefined') {
184
+ const StoreClass = this.getStoreType();
185
+ if (typeof StoreClass === 'function') {
186
+ rateLimitOptions.store = new StoreClass(this, rateLimitOptions);
187
+ }
188
+ }
189
+ this.router.use(path, rateLimit(rateLimitOptions));
190
+ return this;
191
+ }
192
+
193
+ /**
194
+ * @returns {function} The type of store used for rate limiting.
195
+ */
196
+ getStoreType() {
197
+ const serviceConfiguration = this.getServiceConfiguration();
198
+ if (typeof serviceConfiguration.storeType !== 'string') {
199
+ return;
200
+ }
201
+ let StoreClass;
202
+ const store = serviceConfiguration.storeType.split('#');
203
+ if (store.length === 2) {
204
+ const storeModule = require(store[0]);
205
+ if (Object.prototype.hasOwnProperty.call(storeModule, store[1])) {
206
+ StoreClass = storeModule[store[1]];
207
+ return StoreClass
208
+ } else {
209
+ throw new Error(`${store} cannot be found or is inaccessible`);
210
+ }
211
+ } else {
212
+ StoreClass = require(store[0]);
213
+ return StoreClass;
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Unsets the rate limit configuration for a specific path.
219
+ * @param {string} path
220
+ * @returns {RateLimitService} The service instance for chaining.
221
+ */
222
+ unset(path) {
223
+ const index =this.router.stack.findIndex((layer) => {
224
+ return layer.route && layer.route.path === path;
225
+ });
226
+ if (index !== -1) {
227
+ this.router.stack.splice(index, 1);
228
+ }
229
+ return this;
230
+ }
231
+
101
232
  }
@@ -1,4 +1,22 @@
1
- import { ApplicationService } from '@themost/common';
2
- export declare class SpeedLimitService extends ApplicationService {
1
+ import { ApplicationBase, ApplicationService } from '@themost/common';
2
+ import { Application, Router } from 'express';
3
+ import { BehaviorSubject } from 'rxjs';
4
+ import { Options as SpeedLimitProfile, Store } from 'express-slow-down';
5
+
6
+ export declare type SpeedLimitStoreConstructor = new (...arg: unknown[]) => Store;
3
7
 
8
+ export declare interface SpeedLimitServiceConfiguration {
9
+ extends?: string;
10
+ storeType?: string;
11
+ profiles?: Map<string, SpeedLimitProfile>;
12
+ paths?: Map<string, ({ profile: string } | SpeedLimitProfile)>;
13
+ }
14
+
15
+ export declare class SpeedLimitService extends ApplicationService {
16
+ constructor(app: ApplicationBase);
17
+ getServiceContainer(): BehaviorSubject<Router | Application>;
18
+ getServiceName(): string;
19
+ getServiceConfiguration(): SpeedLimitServiceConfiguration;
20
+ set(path: string, options: { profile?: string } | SpeedLimitProfile): this;
21
+ unset(path: string): this;
4
22
  }
@@ -2,105 +2,72 @@ import { ApplicationService, TraceUtils } from '@themost/common';
2
2
  import slowDown from 'express-slow-down';
3
3
  import express from 'express';
4
4
  import path from 'path';
5
+ import { BehaviorSubject } from 'rxjs';
5
6
 
6
7
  export class SpeedLimitService extends ApplicationService {
7
8
  constructor(app) {
8
9
  super(app);
9
10
 
10
- app.serviceRouter.subscribe(serviceRouter => {
11
- if (serviceRouter == null) {
11
+ // get proxy address forwarding option
12
+ const proxyAddressForwarding = app.getConfiguration().getSourceAt('settings/universis/api/proxyAddressForwarding');
13
+ this.proxyAddressForwarding = typeof proxyAddressForwarding !== 'boolean'? false : proxyAddressForwarding;
14
+
15
+ /**
16
+ * @type {BehaviorSubject<{ target: SpeedLimitService }>}
17
+ */
18
+ this.loaded = new BehaviorSubject(null);
19
+
20
+ const serviceContainer = this.getServiceContainer();
21
+ if (serviceContainer == null) {
22
+ TraceUtils.warn(`${this.getServiceName()} is being started but the parent router seems to be unavailable.`);
23
+ return;
24
+ }
25
+
26
+ serviceContainer.subscribe(router => {
27
+ if (router == null) {
12
28
  return;
13
29
  }
14
30
  try {
15
- const addRouter = express.Router();
16
- let serviceConfiguration = app.getConfiguration().getSourceAt('settings/universis/janitor/speedLimit') || {
17
- profiles: [],
18
- paths: []
19
- };
20
- if (serviceConfiguration.extends) {
21
- // get additional configuration
22
- const configurationPath = app.getConfiguration().getConfigurationPath();
23
- const extendsPath = path.resolve(configurationPath, serviceConfiguration.extends);
24
- TraceUtils.log(`@universis/janitor#SpeedLimitService will try to extend service configuration from ${extendsPath}`);
25
- serviceConfiguration = require(extendsPath);
26
- }
27
- const pathsArray = serviceConfiguration.paths || [];
28
- const profilesArray = serviceConfiguration.profiles || [];
31
+ // set router for further processing
32
+ Object.defineProperty(this, 'router', {
33
+ value: express.Router(),
34
+ writable: false,
35
+ enumerable: false,
36
+ configurable: true
37
+ });
38
+ const serviceConfiguration = this.getServiceConfiguration();
29
39
  // create maps
30
- const paths = new Map(pathsArray);
31
- const profiles = new Map(profilesArray);
40
+ const paths = serviceConfiguration.paths;
32
41
  if (paths.size === 0) {
33
- TraceUtils.warn('@universis/janitor#SpeedLimitService is being started but the collection of paths is empty.');
34
- }
35
- // get proxy address forwarding option
36
- let proxyAddressForwarding = app.getConfiguration().getSourceAt('settings/universis/api/proxyAddressForwarding');
37
- if (typeof proxyAddressForwarding !== 'boolean') {
38
- proxyAddressForwarding = false;
42
+ TraceUtils.warn(`${this.getServiceName()} is being started but the collection of paths is empty.`);
39
43
  }
40
44
  paths.forEach((value, path) => {
41
- let profile;
42
- // get profile
43
- if (value.profile) {
44
- profile = profiles.get(value.profile);
45
- } else {
46
- // or options defined inline
47
- profile = value
48
- }
49
- if (profile != null) {
50
- const slowDownOptions = Object.assign({
51
- windowMs: 5 * 60 * 1000, // 5 minutes
52
- delayAfter: 20, // 20 requests
53
- delayMs: 500, // 500 ms
54
- maxDelayMs: 10000 // 10 seconds
55
- }, profile, {
56
- keyGenerator: (req) => {
57
- let remoteAddress;
58
- if (proxyAddressForwarding) {
59
- // get proxy headers or remote address
60
- remoteAddress = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
61
- } else {
62
- // get remote address
63
- remoteAddress = (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
64
- }
65
- return `${path}:${remoteAddress}`;
66
- }
45
+ this.set(path, value);
46
+ });
47
+ if (router.stack) {
48
+ router.stack.unshift.apply(router.stack, this.router.stack);
49
+ } else {
50
+ // use router
51
+ router.use(this.router);
52
+ // get router stack (use a workaround for express 4.x)
53
+ const stack = router._router && router._router.stack;
54
+ if (Array.isArray(stack)) {
55
+ // stage #1 find logger middleware (for supporting request logging)
56
+ let index = stack.findIndex((item) => {
57
+ return item.name === 'logger';
67
58
  });
68
- if (Array.isArray(slowDownOptions.randomDelayMs)) {
69
- slowDownOptions.delayMs = () => {
70
- const delayMs = Math.floor(Math.random() * (slowDownOptions.randomDelayMs[1] - slowDownOptions.randomDelayMs[0] + 1) + slowDownOptions.randomDelayMs[0]);
71
- return delayMs;
72
- }
59
+ if (index === -1) {
60
+ // stage #2 find expressInit middleware
61
+ index = stack.findIndex((item) => {
62
+ return item.name === 'expressInit';
63
+ });
73
64
  }
74
- if (Array.isArray(slowDownOptions.randomMaxDelayMs)) {
75
- slowDownOptions.maxDelayMs = () => {
76
- const maxDelayMs = Math.floor(Math.random() * (slowDownOptions.randomMaxDelayMs[1] - slowDownOptions.randomMaxDelayMs[0] + 1) + slowDownOptions.randomMaxDelayMs[0]);
77
- return maxDelayMs;
78
- }
79
- }
80
- if (typeof slowDownOptions.store === 'string') {
81
- // load store
82
- const store = slowDownOptions.store.split('#');
83
- let StoreClass;
84
- if (store.length === 2) {
85
- const storeModule = require(store[0]);
86
- if (Object.prototype.hasOwnProperty.call(storeModule, store[1])) {
87
- StoreClass = storeModule[store[1]];
88
- slowDownOptions.store = new StoreClass(this, slowDownOptions);
89
- } else {
90
- throw new Error(`${store} cannot be found or is inaccessible`);
91
- }
92
- } else {
93
- StoreClass = require(store[0]);
94
- // create store
95
- slowDownOptions.store = new StoreClass(this, slowDownOptions);
96
- }
97
- }
98
- addRouter.use(path, slowDown(slowDownOptions));
65
+ } else {
66
+ TraceUtils.warn(`${this.getServiceName()} is being started but the container stack is not available.`);
99
67
  }
100
- });
101
- if (addRouter.stack.length) {
102
- serviceRouter.stack.unshift.apply(serviceRouter.stack, addRouter.stack);
103
68
  }
69
+ // notify that the service is loaded
70
+ this.loaded.next({ target: this });
104
71
  } catch (err) {
105
72
  TraceUtils.error('An error occurred while validating speed limit configuration.');
106
73
  TraceUtils.error(err);
@@ -109,4 +76,161 @@ export class SpeedLimitService extends ApplicationService {
109
76
  });
110
77
  }
111
78
 
79
+ /**
80
+ * @returns {function} The type of store used for rate limiting.
81
+ */
82
+ getStoreType() {
83
+ const serviceConfiguration = this.getServiceConfiguration();
84
+ if (typeof serviceConfiguration.storeType !== 'string') {
85
+ return;
86
+ }
87
+ let StoreClass;
88
+ const store = serviceConfiguration.storeType.split('#');
89
+ if (store.length === 2) {
90
+ const storeModule = require(store[0]);
91
+ if (Object.prototype.hasOwnProperty.call(storeModule, store[1])) {
92
+ StoreClass = storeModule[store[1]];
93
+ return StoreClass
94
+ } else {
95
+ throw new Error(`${store} cannot be found or is inaccessible`);
96
+ }
97
+ } else {
98
+ StoreClass = require(store[0]);
99
+ return StoreClass;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Returns the service name.
105
+ * @returns {string} The service name.
106
+ */
107
+ getServiceName() {
108
+ return '@universis/janitor#SpeedLimitService';
109
+ }
110
+
111
+ /**
112
+ * Returns the service router that is used to register speed limit middleware.
113
+ * @returns {import('express').Router | import('express').Application} The service router.
114
+ */
115
+ getServiceContainer() {
116
+ return this.getApplication() && this.getApplication().serviceRouter;
117
+ }
118
+
119
+ /**
120
+ * Sets the speed limit configuration for a specific path.
121
+ * @param {string} path
122
+ * @param {{ profile: string } | import('express-slow-down').Options} options
123
+ * @returns {SpeedLimitService} The service instance for chaining.
124
+ */
125
+ set(path, options) {
126
+ let opts;
127
+ // get profile
128
+ if (options.profile) {
129
+ opts = this.serviceConfiguration.profiles.get(options.profile);
130
+ } else {
131
+ // or options defined inline
132
+ opts = options
133
+ }
134
+ const slowDownOptions = Object.assign({
135
+ windowMs: 5 * 60 * 1000, // 5 minutes
136
+ delayAfter: 20, // 20 requests
137
+ delayMs: 500, // 500 ms
138
+ maxDelayMs: 10000 // 10 seconds
139
+ }, opts, {
140
+ keyGenerator: (req) => {
141
+ let remoteAddress;
142
+ if (this.proxyAddressForwarding) {
143
+ // get proxy headers or remote address
144
+ remoteAddress = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
145
+ } else {
146
+ // get remote address
147
+ remoteAddress = (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
148
+ }
149
+ return `${path}:${remoteAddress}`;
150
+ }
151
+ });
152
+ if (Array.isArray(slowDownOptions.randomDelayMs)) {
153
+ slowDownOptions.delayMs = () => {
154
+ const delayMs = Math.floor(Math.random() * (slowDownOptions.randomDelayMs[1] - slowDownOptions.randomDelayMs[0] + 1) + slowDownOptions.randomDelayMs[0]);
155
+ return delayMs;
156
+ }
157
+ }
158
+ if (Array.isArray(slowDownOptions.randomMaxDelayMs)) {
159
+ slowDownOptions.maxDelayMs = () => {
160
+ const maxDelayMs = Math.floor(Math.random() * (slowDownOptions.randomMaxDelayMs[1] - slowDownOptions.randomMaxDelayMs[0] + 1) + slowDownOptions.randomMaxDelayMs[0]);
161
+ return maxDelayMs;
162
+ }
163
+ }
164
+ if (typeof slowDownOptions.store === 'undefined') {
165
+ const StoreClass = this.getStoreType();
166
+ if (typeof StoreClass === 'function') {
167
+ slowDownOptions.store = new StoreClass(this, slowDownOptions);
168
+ }
169
+ }
170
+ this.router.use(path, slowDown(slowDownOptions));
171
+ return this;
172
+ }
173
+
174
+
175
+ /**
176
+ * Unsets the speed limit configuration for a specific path.
177
+ * @param {string} path
178
+ * @return {SpeedLimitService} The service instance for chaining.
179
+ */
180
+ unset(path) {
181
+ const index =this.router.stack.findIndex((layer) => {
182
+ return layer.route && layer.route.path === path;
183
+ });
184
+ if (index !== -1) {
185
+ this.router.stack.splice(index, 1);
186
+ }
187
+ return this;
188
+ }
189
+
190
+ /**
191
+ *
192
+ * @returns {{extends?: string, profiles?: Array, paths?: Array}} The service configuration.
193
+ */
194
+ getServiceConfiguration() {
195
+ if (this.serviceConfiguration) {
196
+ return this.serviceConfiguration;
197
+ }
198
+ let serviceConfiguration = {
199
+ profiles: [],
200
+ paths: []
201
+ };
202
+ // get service configuration
203
+ const serviceConfigurationSource = this.getApplication().getConfiguration().getSourceAt('settings/universis/janitor/speedLimit');
204
+ if (serviceConfigurationSource) {
205
+ if (typeof serviceConfigurationSource.extends === 'string') {
206
+ // get additional configuration
207
+ const configurationPath = this.getApplication().getConfiguration().getConfigurationPath();
208
+ const extendsPath = path.resolve(configurationPath, serviceConfigurationSource.extends);
209
+ TraceUtils.log(`${this.getServiceName()} will try to extend service configuration using ${extendsPath}`);
210
+ serviceConfiguration = Object.assign({}, {
211
+ profiles: [],
212
+ paths: []
213
+ }, require(extendsPath));
214
+ } else {
215
+ TraceUtils.log(`${this.getServiceName()} will use service configuration from settings/universis/janitor/speedLimit`);
216
+ serviceConfiguration = Object.assign({}, {
217
+ profiles: [],
218
+ paths: []
219
+ }, serviceConfigurationSource);
220
+ }
221
+ }
222
+ const profilesArray = serviceConfiguration.profiles || [];
223
+ serviceConfiguration.profiles = new Map(profilesArray);
224
+ const pathsArray = serviceConfiguration.paths || [];
225
+ serviceConfiguration.paths = new Map(pathsArray);
226
+
227
+ Object.defineProperty(this, 'serviceConfiguration', {
228
+ value: serviceConfiguration,
229
+ writable: false,
230
+ enumerable: false,
231
+ configurable: true
232
+ });
233
+ return this.serviceConfiguration;
234
+ }
235
+
112
236
  }
package/src/index.d.ts CHANGED
@@ -5,3 +5,7 @@ export * from './ScopeAccessConfiguration';
5
5
  export * from './validateScope';
6
6
  export * from './OAuth2ClientService';
7
7
  export * from './RemoteAddressValidator';
8
+ export * from './AppRateLimitService';
9
+ export * from './AppSpeedLimitService';
10
+ export * from './HttpBearerStategy';
11
+ export * from './PassportService';
package/src/index.js CHANGED
@@ -6,3 +6,7 @@ export * from './ScopeAccessConfiguration';
6
6
  export * from './validateScope';
7
7
  export * from './OAuth2ClientService';
8
8
  export * from './RemoteAddressValidator';
9
+ export * from './AppRateLimitService';
10
+ export * from './AppSpeedLimitService';
11
+ export * from './HttpBearerStategy';
12
+ export * from './PassportService';