soajs.controller 4.2.4 → 4.2.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/Gruntfile.js CHANGED
@@ -56,7 +56,7 @@ let lib = {
56
56
  module.exports = function (grunt) {
57
57
  //Loading the needed plugins to run the grunt tasks
58
58
  let pluginsRootPath = lib.findRoot();
59
- lib.loadTasks(grunt, pluginsRootPath, ['grunt-contrib-jshint', 'grunt-jsdoc', 'grunt-contrib-clean', 'grunt-mocha-test', 'grunt-env', 'grunt-istanbul', 'grunt-coveralls', 'grunt-contrib-copy']);
59
+ lib.loadTasks(grunt, pluginsRootPath, ['grunt-contrib-jshint', 'grunt-jsdoc', 'grunt-contrib-clean', 'grunt-mocha-test', 'grunt-env', 'grunt-istanbul', 'grunt-coveralls', 'grunt-babel', 'grunt-contrib-copy']);
60
60
  grunt.initConfig({
61
61
  //Defining jshint tasks
62
62
  jshint: {
@@ -65,7 +65,7 @@ module.exports = function (grunt) {
65
65
  "curly": true,
66
66
  "eqeqeq": true,
67
67
  "eqnull": true,
68
- "esversion": 6,
68
+ "esversion": 8,
69
69
  "forin": true,
70
70
  "latedef": "nofunc",
71
71
  "leanswitch": true,
@@ -89,7 +89,7 @@ module.exports = function (grunt) {
89
89
  }
90
90
  },
91
91
  files: {
92
- src: ['index.js', '_index.js', 'config.js', 'Gruntfile.js', 'lib/**/*.js', 'modules/driver/*.js', 'modules/driver/kubernetes/*.js', 'modules/driver/kubernetes/bl/*.js', 'modules/driver/kubernetes/model/*.js', 'modules/registry/*.js', 'mw/**/*.js', 'server/**/*.js', 'utilities/**/*.js']
92
+ src: ['index.js', '_index.js', 'config.js', 'Gruntfile.js', 'lib/**/*.js', 'modules/**/*.js', 'mw/**/*.js', 'server/**/*.js', 'utilities/**/*.js']
93
93
  },
94
94
  gruntfile: {
95
95
  src: 'Gruntfile.js'
@@ -109,7 +109,7 @@ module.exports = function (grunt) {
109
109
  SOAJS_ENV: "dev",
110
110
  SOAJS_TEST: true,
111
111
  SOAJS_PROFILE: __dirname + "/profiles/single.js",
112
- APP_DIR_FOR_CODE_COVERAGE: '../test/coverage/instrument/'
112
+ APP_DIR_FOR_CODE_COVERAGE: '../test/coverage/instrument/test/dist/'
113
113
  }
114
114
  },
115
115
 
@@ -119,23 +119,78 @@ module.exports = function (grunt) {
119
119
  },
120
120
  coverage: {
121
121
  src: ['test/coverage/']
122
+ },
123
+ dist: {
124
+ src: ['test/dist/']
122
125
  }
123
126
  },
124
127
 
128
+ babel: {
129
+ options: {
130
+ sourceMap: true,
131
+ presets: ['@babel/preset-env'],
132
+ plugins: ["@babel/plugin-transform-runtime"],
133
+ },
134
+ dist: {
135
+ files: [
136
+
137
+ {
138
+ expand: true,
139
+ cwd: 'lib/',
140
+ src: ['*.js'],
141
+ dest: 'test/dist/lib/'
142
+ },
143
+ {
144
+ expand: true,
145
+ cwd: 'modules/',
146
+ src: ['**/*.js'],
147
+ dest: 'test/dist/modules/'
148
+ },
149
+ {
150
+ expand: true,
151
+ cwd: 'mw/',
152
+ src: ['**/*.js'],
153
+ dest: 'test/dist/mw/'
154
+ },
155
+ {
156
+ expand: true,
157
+ cwd: 'profiles/',
158
+ src: ['*.js'],
159
+ dest: 'test/dist/profiles/'
160
+ },
161
+ {
162
+ expand: true,
163
+ cwd: 'server/',
164
+ src: ['*.js'],
165
+ dest: 'test/dist/server/'
166
+ },
167
+ {
168
+ expand: true,
169
+ cwd: 'utilities/',
170
+ src: ['*.js'],
171
+ dest: 'test/dist/utilities/'
172
+ },
173
+ { 'test/dist/index.js': 'index.js' },
174
+ { 'test/dist/_index.js': '_index.js' },
175
+ { 'test/dist/config.js': 'config.js' },
176
+ ]
177
+ }
178
+ },
179
+
125
180
  copy: {
126
181
  main: {
127
182
  files: [
128
- {expand: true, src: ['package.json'], dest: 'test/coverage/instrument/', filter: 'isFile'},
183
+ {expand: true, src: ['package.json'], dest: 'test/coverage/instrument/test/dist/', filter: 'isFile'},
129
184
  {
130
185
  cwd: 'modules/driver/kubernetes/model/swagger/',
131
186
  src: '**/*',
132
- dest: 'test/coverage/instrument/modules/driver/kubernetes/model/swagger/',
187
+ dest: 'test/coverage/instrument/test/dist/modules/driver/kubernetes/model/swagger/',
133
188
  expand: true
134
189
  },
135
190
  {
136
191
  cwd: 'modules/driver/kubernetes/model/clients/', // set working folder / root to copy
137
192
  src: '**/*',
138
- dest: 'test/coverage/instrument/modules/driver/kubernetes/model/clients/', // destination folder
193
+ dest: 'test/coverage/instrument/test/dist/modules/driver/kubernetes/model/clients/', // destination folder
139
194
  expand: true // required when using cwd
140
195
  }
141
196
  ]
@@ -143,7 +198,8 @@ module.exports = function (grunt) {
143
198
  },
144
199
 
145
200
  instrument: {
146
- files: ['index.js', '_index.js', 'config.js', 'lib/**/*.js', 'modules/driver/*.js', 'modules/driver/kubernetes/*.js', 'modules/driver/kubernetes/bl/*.js', 'modules/driver/kubernetes/model/*.js', 'modules/registry/*.js', 'mw/**/*.js', 'server/**/*.js', 'utilities/**/*.js'],
201
+ // files: ['index.js', '_index.js', 'config.js', 'lib/**/*.js', 'modules/driver/*.js', 'modules/driver/kubernetes/*.js', 'modules/driver/kubernetes/bl/*.js', 'modules/driver/kubernetes/model/*.js', 'modules/registry/*.js', 'mw/**/*.js', 'server/**/*.js', 'utilities/**/*.js'],
202
+ files: ['test/dist/index.js', 'test/dist/_index.js', 'test/dist/config.js', 'test/dist/lib/**/*.js', 'test/dist/modules/**/*.js', 'test/dist/mw/**/*.js', 'test/dist/server/**/*.js', 'test/dist/utilities/**/*.js'],
147
203
  options: {
148
204
  lazy: false,
149
205
  basePath: 'test/coverage/instrument/'
@@ -202,11 +258,11 @@ module.exports = function (grunt) {
202
258
  process.env.SHOW_LOGS = grunt.option('showLogs');
203
259
  grunt.registerTask("default", ['jshint']);
204
260
  grunt.registerTask("integration", ['env:mochaTest', 'mochaTest:integration']);
205
- grunt.registerTask("integration-coverage", ['clean', 'copy', 'env:coverage', 'instrument', 'mochaTest:integration', 'storeCoverage', 'makeReport']);
261
+ grunt.registerTask("integration-coverage", ['clean', 'babel', 'copy', 'env:coverage', 'instrument', 'mochaTest:integration', 'storeCoverage', 'makeReport']);
206
262
  grunt.registerTask("unit", ['env:mochaTest', 'mochaTest:unit']);
207
- grunt.registerTask("unit-coverage", ['clean', 'copy', 'env:coverage', 'instrument', 'mochaTest:unit', 'storeCoverage', 'makeReport']);
208
- grunt.registerTask("test", ['clean', 'copy', 'env:coverage', 'instrument', 'mochaTest:unit', 'mochaTest:integration', 'storeCoverage', 'makeReport']);
209
- grunt.registerTask("coverage", ['clean', 'copy', 'env:coverage', 'instrument', 'mochaTest:unit', 'mochaTest:integration', 'storeCoverage', 'makeReport', 'coveralls']);
263
+ grunt.registerTask("unit-coverage", ['clean', 'babel', 'copy', 'env:coverage', 'instrument', 'mochaTest:unit', 'storeCoverage', 'makeReport']);
264
+ grunt.registerTask("test", ['clean', 'babel', 'copy', 'env:coverage', 'instrument', 'mochaTest:unit', 'mochaTest:integration', 'storeCoverage', 'makeReport']);
265
+ grunt.registerTask("coverage", ['clean', 'babel', 'copy', 'env:coverage', 'instrument', 'mochaTest:unit', 'mochaTest:integration', 'storeCoverage', 'makeReport', 'coveralls']);
210
266
 
211
267
  };
212
268
 
@@ -8,17 +8,25 @@
8
8
  * found in the LICENSE file at the root of this repository
9
9
  */
10
10
 
11
+
12
+ const get = (p, o) => p.reduce((xs, x) => (xs && xs[x]) ? xs[x] : null, o);
13
+ const memory = require('./model/memory.js');
14
+ const mongo = require('./model/mongo.js');
15
+
16
+ let model = {
17
+ memory, mongo
18
+ };
11
19
  /**
12
20
  *
13
21
  * @param configuration
14
22
  * @returns {Function}
15
23
  */
16
- module.exports = function () {
17
-
18
- // var registryThrottling = {
19
- // "publicAPIStrategy" : "default", // can be null means throttling is off
20
- // "privateAPIStrategy": "heavy", // can be null means throttling is off
21
- // "default": {
24
+ module.exports = function (configuration) {
25
+
26
+ // let registry.throttling = {
27
+ // 'publicAPIStrategy' : 'default', // can be null means throttling is off
28
+ // 'privateAPIStrategy': 'heavy', // can be null means throttling is off
29
+ // 'default': {
22
30
  // 'status': 1, // 0=Off, 1=On
23
31
  // 'type': 1, // 0= tenant, 1= tenant -> ip
24
32
  // 'window': 60000,
@@ -26,7 +34,7 @@ module.exports = function () {
26
34
  // 'retries': 2,
27
35
  // 'delay': 1000
28
36
  // },
29
- // "off": {
37
+ // 'off': {
30
38
  // 'status': 0, // 0=Off, 1=On
31
39
  // 'type': 1, // 0= tenant, 1= tenant -> ip
32
40
  // 'window': 60000,
@@ -34,7 +42,7 @@ module.exports = function () {
34
42
  // 'retries': 2,
35
43
  // 'delay': 1000
36
44
  // },
37
- // "heavy": {
45
+ // 'heavy': {
38
46
  // 'status': 1, // 0=Off, 1=On
39
47
  // 'type': 1, // 0= tenant, 1= tenant -> ip
40
48
  // 'window': 60000,
@@ -43,64 +51,65 @@ module.exports = function () {
43
51
  // 'delay': 1000
44
52
  // }
45
53
  // };
46
- // var provisionThrottling = {
47
- // "publicAPIStrategy" : "default", // can be null means throttling is off, if not set means inherit from registry
48
- // "privateAPIStrategy": null, // can be null means throttling is off, if not set means inherit from registry
54
+ // let provision.gateway.throttling = {
55
+ // 'publicAPIStrategy' : 'default', // can be null means throttling is off, if not set means inherit from registry
56
+ // 'privateAPIStrategy': null, // can be null means throttling is off, if not set means inherit from registry
57
+ // 'oauth' : {
58
+ // 'publicAPIStrategy' : 'default'
59
+ // }
49
60
  // };
50
-
51
- let dataHolder = {};
52
- let checkThrottling = (obj, cb) => {
61
+ // let customRegistry.gateway = {
62
+ // "traffic": {
63
+ // "ip2ban": [
64
+ // "::1"
65
+ // ],
66
+ // "model": "mongo",
67
+ // "throttling": {
68
+ // "oauth": {
69
+ // "publicAPIStrategy": "test",
70
+ // "apis": [
71
+ // "/token"
72
+ // ]
73
+ // }
74
+ // }
75
+ // },
76
+ // };
77
+
78
+ model.memory.initThrottling(configuration.log);
79
+ model.mongo.initThrottling(configuration.log, configuration.gatewayDB);
80
+
81
+ let checkThrottling = async (obj, cb) => {
53
82
  let trafficKey = obj.trafficKey;
54
83
  let throttling = obj.throttling;
55
-
56
- let throttlingObj = {};
57
-
58
- if (!dataHolder[trafficKey.l1]) {
59
- dataHolder[trafficKey.l1] = {};
60
- if (throttling.type === 0) {
61
- dataHolder[trafficKey.l1] = {
62
- 'firstReqTime': new Date(Date.now()),
63
- 'count': 0
64
- };
65
- }
66
- }
67
-
68
- if (throttling.type === 1) {
69
- if (!dataHolder[trafficKey.l1][trafficKey.l2]) {
70
- dataHolder[trafficKey.l1][trafficKey.l2] = {
71
- 'firstReqTime': new Date(Date.now()),
72
- 'count': 0
73
- };
74
- }
75
- throttlingObj = dataHolder[trafficKey.l1][trafficKey.l2];
76
- } else {
77
- throttlingObj = dataHolder[trafficKey.l1];
78
- }
79
-
84
+ let strategy = obj.strategy;
85
+ let modelType = obj.throttling.model || 'memory';
86
+
87
+ let throttlingObj = await model[modelType].getThrottling(throttling, trafficKey);
88
+
80
89
  let remainingLifetime = throttling.window - Math.floor(Date.now() - throttlingObj.firstReqTime.getTime());
81
-
90
+
82
91
  if (remainingLifetime < 1) {
83
- throttlingObj.firstReqTime = new Date(Date.now());
84
- throttlingObj.count = 0;
92
+ throttlingObj = await model[modelType].resetThrottling(throttling, trafficKey);
85
93
  }
86
-
94
+ let throttlingCount = throttlingObj.count;
87
95
  if (obj.retry === 0) {
88
- throttlingObj.count++;
89
- throttlingObj.lastReqTime = new Date(Date.now());
96
+ throttlingCount = throttlingObj.count + 1;
97
+ await model[modelType].incrementThrottling(throttling, trafficKey);
90
98
  }
91
-
92
- if (throttlingObj.count > throttling.limit) {
99
+
100
+ if (throttlingCount > throttling.limit) {
93
101
  obj.retry++;
94
102
  if (obj.retry <= throttling.retries) {
95
103
  setTimeout(function () {
96
- checkThrottling({'trafficKey': trafficKey, 'throttling': throttling, 'retry': obj.retry}, cb);
104
+ checkThrottling({ trafficKey, throttling, 'retry': obj.retry, strategy }, cb);
97
105
  }, throttling.delay);
98
106
  } else {
107
+ await model[modelType].logTooManyRequests(trafficKey, strategy);
99
108
  return cb({
100
109
  'result': false, 'headObj': {
101
110
  'Retry-After': remainingLifetime / 1000,
102
111
  'X-RateLimit-Limit': throttling.limit,
103
- 'X-RateLimit-Remaining': throttling.limit - throttlingObj.count
112
+ 'X-RateLimit-Remaining': throttling.limit - throttlingCount
104
113
  }
105
114
  });
106
115
  }
@@ -109,43 +118,73 @@ module.exports = function () {
109
118
  'result': true, 'headObj': {
110
119
  'Retry-After': remainingLifetime / 1000,
111
120
  'X-RateLimit-Limit': throttling.limit,
112
- 'X-RateLimit-Remaining': throttling.limit - throttlingObj.count
121
+ 'X-RateLimit-Remaining': throttling.limit - throttlingCount
113
122
  }
114
123
  });
115
124
  }
116
125
  };
117
-
126
+
118
127
  return (req, res, next) => {
128
+
129
+ let ip2ban = get(["soajs", "registry", "custom", "gateway", "value", "traffic", "ip2ban"], req);
130
+ let clientIP = req.getClientIP();
131
+
132
+ if (ip2ban && Array.isArray(ip2ban) && ip2ban.includes(clientIP)) {
133
+ req.soajs.controllerResponse({
134
+ 'status': 403,
135
+ 'msg': 'banned'
136
+ });
137
+ return;
138
+ }
139
+
119
140
  if (req && req.soajs && req.soajs.registry && req.soajs.registry.serviceConfig && req.soajs.registry.serviceConfig.throttling && req.soajs.tenant && req.soajs.controller) {
141
+
120
142
  let serviceName = req.soajs.controller.serviceParams.name;
121
- let strategy = (req.soajs.controller.serviceParams.isAPIPublic ? "publicAPIStrategy" : "privateAPIStrategy");
143
+ let api = req.soajs.controller.serviceParams.path;
144
+ let strategy = (req.soajs.controller.serviceParams.isAPIPublic ? 'publicAPIStrategy' : 'privateAPIStrategy');
122
145
  let throttlingStrategy = req.soajs.registry.serviceConfig.throttling[strategy];
123
-
124
- if (req.soajs.servicesConfig && req.soajs.servicesConfig.gateway && req.soajs.servicesConfig.gateway.throttling) {
125
- if (req.soajs.servicesConfig.gateway.throttling.hasOwnProperty(strategy)) {
126
- throttlingStrategy = req.soajs.servicesConfig.gateway.throttling[strategy];
127
- }
128
- if (req.soajs.servicesConfig.gateway.throttling[serviceName]) {
129
- if (req.soajs.servicesConfig.gateway.throttling[serviceName].hasOwnProperty(strategy)) {
130
- throttlingStrategy = req.soajs.servicesConfig.gateway.throttling[strategy];
146
+
147
+ let throttlingConfiguration = get(["soajs", "servicesConfig", "gateway", "throttling"], req);
148
+ if (!throttlingConfiguration) {
149
+ throttlingConfiguration = get(["soajs", "registry", "custom", "gateway", "value", "traffic", "throttling"], req);
150
+ }
151
+ if (throttlingConfiguration) {
152
+ if (throttlingConfiguration[serviceName]) {
153
+ if (throttlingConfiguration[serviceName].hasOwnProperty(strategy)) {
154
+ if (throttlingConfiguration[serviceName].apis && Array.isArray(throttlingConfiguration[serviceName].apis)) {
155
+ if (throttlingConfiguration[serviceName].apis.includes(api)) {
156
+ throttlingStrategy = throttlingConfiguration[serviceName][strategy];
157
+ }
158
+ } else {
159
+ throttlingStrategy = throttlingConfiguration[serviceName][strategy];
160
+ }
131
161
  }
132
162
  }
133
163
  }
164
+
134
165
  if (!throttlingStrategy) {
135
166
  return next();
136
167
  }
137
168
  let throttling = req.soajs.registry.serviceConfig.throttling[throttlingStrategy];
138
-
169
+
139
170
  if (throttling) {
140
- let trafficKey = {"l1": req.soajs.tenant.id, "l2": req.getClientIP()};
141
-
142
- checkThrottling({'trafficKey': trafficKey, 'throttling': throttling, 'retry': 0}, function (response) {
171
+ let trafficModel = get(["soajs", "registry", "custom", "gateway", "value", "traffic", "model"], req);
172
+ if (trafficModel) {
173
+ throttling.model = trafficModel;
174
+ if (!model[throttling.model]) {
175
+ req.soajs.log.warn('Throttling:', 'Unkown model [' + throttling.model + ']. It can only be [memory || mongo]');
176
+ return next();
177
+ }
178
+ }
179
+ let trafficKey = { 'l1': req.soajs.tenant.id, 'l2': clientIP };
180
+
181
+ checkThrottling({ trafficKey, throttling, 'retry': 0, 'strategy': throttlingStrategy }, function (response) {
143
182
  if (response.result) {
144
183
  return next();
145
184
  } else {
146
185
  req.soajs.controllerResponse({
147
186
  'status': 429,
148
- 'msg': "too many requests",
187
+ 'msg': 'too many requests',
149
188
  'headObj': response.headObj
150
189
  });
151
190
  }
@@ -0,0 +1,73 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @license
5
+ * Copyright SOAJS All Rights Reserved.
6
+ *
7
+ * Use of this source code is governed by an Apache license that can be
8
+ * found in the LICENSE file at the root of this repository
9
+ */
10
+
11
+ let dataHolder = {};
12
+
13
+ let model = {
14
+ 'log': null,
15
+ 'initThrottling': (log) => {
16
+ model.log = log;
17
+ dataHolder = {};
18
+ },
19
+
20
+ 'getThrottling': (throttling, trafficKey) => {
21
+
22
+ let throttlingObj = {};
23
+ if (!dataHolder[trafficKey.l1]) {
24
+ dataHolder[trafficKey.l1] = {
25
+ 'firstReqTime': new Date(Date.now()),
26
+ 'count': 0
27
+ };
28
+ }
29
+ if (throttling.type === 1) {
30
+ if (!dataHolder[trafficKey.l1][trafficKey.l2]) {
31
+ dataHolder[trafficKey.l1][trafficKey.l2] = {
32
+ 'firstReqTime': new Date(Date.now()),
33
+ 'count': 0
34
+ };
35
+ }
36
+ throttlingObj = dataHolder[trafficKey.l1][trafficKey.l2];
37
+ } else {
38
+ throttlingObj = dataHolder[trafficKey.l1];
39
+ }
40
+ return throttlingObj;
41
+ },
42
+
43
+ 'resetThrottling': (throttling, trafficKey) => {
44
+ if (throttling.type === 1) {
45
+ dataHolder[trafficKey.l1][trafficKey.l2] = {
46
+ 'firstReqTime': new Date(Date.now()),
47
+ 'count': 0
48
+ };
49
+ return (dataHolder[trafficKey.l1][trafficKey.l2]);
50
+ } else {
51
+ dataHolder[trafficKey.l1] = {
52
+ 'firstReqTime': new Date(Date.now()),
53
+ 'count': 0
54
+ };
55
+ return (dataHolder[trafficKey.l1]);
56
+ }
57
+ },
58
+
59
+ 'incrementThrottling': (throttling, trafficKey) => {
60
+ if (throttling.type === 1) {
61
+ dataHolder[trafficKey.l1][trafficKey.l2].count++;
62
+ } else {
63
+ dataHolder[trafficKey.l1].count++;
64
+ }
65
+ },
66
+
67
+ 'logTooManyRequests': (trafficKey, throttlingStrategy) => {
68
+ model.log.error('Throttling [' + throttlingStrategy + ']rejected:', 'using tenant id [' + trafficKey.l1 + '] from ip [' + trafficKey.l2 + ']');
69
+ }
70
+ };
71
+
72
+
73
+ module.exports = model;
@@ -0,0 +1,115 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @license
5
+ * Copyright SOAJS All Rights Reserved.
6
+ *
7
+ * Use of this source code is governed by an Apache license that can be
8
+ * found in the LICENSE file at the root of this repository
9
+ */
10
+
11
+ const core = require('soajs.core.modules');
12
+ const Mongo = core.mongo;
13
+
14
+ const throttling_monitor = 'throttling_monitor';
15
+ const throttling_error = 'throttling_error';
16
+
17
+ let model = {
18
+ 'mongo': null,
19
+ 'log': null,
20
+
21
+ 'initThrottling': (log, dbConfiguration) => {
22
+ model.log = log;
23
+ if (!model.mongo) {
24
+ model.mongo = new Mongo(dbConfiguration);
25
+ }
26
+ },
27
+
28
+ 'getThrottling': async (throttling, trafficKey) => {
29
+
30
+ let condition = {
31
+ 'l1': trafficKey.l1
32
+ };
33
+ if (throttling.type === 1) {
34
+ condition.l2 = trafficKey.l2;
35
+ } else {
36
+ condition.l2 = '_ALL_';
37
+ }
38
+ let throttlingObj = null;
39
+ try {
40
+ throttlingObj = await model.mongo.findOne(throttling_monitor, condition);
41
+ if (!throttlingObj) {
42
+ throttlingObj = await model.resetThrottling(throttling, trafficKey);
43
+ }
44
+ } catch (e) {
45
+ model.log.error('Throttling:', e.message);
46
+ throttlingObj = {
47
+ 'firstReqTime': new Date(Date.now()),
48
+ 'count': 0
49
+ };
50
+ }
51
+ return throttlingObj;
52
+ },
53
+
54
+ 'resetThrottling': async (throttling, trafficKey) => {
55
+ let options = {
56
+ 'upsert': true
57
+ };
58
+ let condition = {
59
+ 'l1': trafficKey.l1
60
+ };
61
+ if (throttling.type === 1) {
62
+ condition.l2 = trafficKey.l2;
63
+ } else {
64
+ condition.l2 = '_ALL_';
65
+ }
66
+ let throttlingObj = {
67
+ 'firstReqTime': new Date(Date.now()),
68
+ 'count': 0
69
+ };
70
+ let s = {
71
+ '$set': throttlingObj
72
+ };
73
+ try {
74
+ await model.mongo.updateOne(throttling_monitor, condition, s, options);
75
+ } catch (e) {
76
+ model.log.error('Throttling:', e.message);
77
+ }
78
+ return throttlingObj;
79
+ },
80
+
81
+ 'incrementThrottling': async (throttling, trafficKey) => {
82
+ let options = {
83
+ };
84
+ let condition = {
85
+ 'l1': trafficKey.l1
86
+ };
87
+ if (throttling.type === 1) {
88
+ condition.l2 = trafficKey.l2;
89
+ } else {
90
+ condition.l2 = '_ALL_';
91
+ }
92
+ let s = {
93
+ '$inc': { 'count': 1 }
94
+ };
95
+ try {
96
+ await model.mongo.updateOne(throttling_monitor, condition, s, options);
97
+ } catch (e) {
98
+ model.log.error('Throttling:', e.message);
99
+ }
100
+ },
101
+
102
+ 'logTooManyRequests': (trafficKey, strategy) => {
103
+ let s = {
104
+ "staregy": strategy,
105
+ "l1": trafficKey.l1,
106
+ "l2": trafficKey.l2,
107
+ "ts": new Date(Date.now())
108
+ };
109
+ model.mongo.insertOne(throttling_error, s, {}, () => {
110
+ });
111
+ }
112
+ };
113
+
114
+
115
+ module.exports = model;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "soajs.controller",
3
3
  "description": "soajs multi tenant API gateway",
4
- "version": "4.2.4",
4
+ "version": "4.2.5",
5
5
  "author": {
6
6
  "name": "soajs team",
7
7
  "email": "team@soajs.org"
@@ -35,17 +35,21 @@
35
35
  "useragent": "2.3.0"
36
36
  },
37
37
  "devDependencies": {
38
+ "@babel/core": "^7.22.5",
39
+ "@babel/plugin-transform-runtime": "7.19.6",
40
+ "@babel/preset-env": "7.20.2",
38
41
  "express": "4.18.2",
39
- "grunt": "1.0.4",
40
- "grunt-contrib-clean": "2.0.0",
42
+ "grunt": "1.5.3",
43
+ "grunt-babel": "8.0.0",
44
+ "grunt-contrib-clean": "2.0.1",
41
45
  "grunt-contrib-copy": "1.0.0",
42
- "grunt-contrib-jshint": "2.1.0",
46
+ "grunt-contrib-jshint": "3.2.0",
43
47
  "grunt-coveralls": "2.0.0",
44
48
  "grunt-env": "1.0.1",
45
49
  "grunt-istanbul": "0.8.0",
46
50
  "grunt-jsdoc": "2.4.0",
47
51
  "grunt-mocha-test": "0.13.3",
48
- "mocha": "6.2.1",
52
+ "mocha": "10.2.0",
49
53
  "sinon": "7.5.0"
50
54
  }
51
55
  }
@@ -41,7 +41,7 @@ const traffic_mw = require("../mw/traffic/index");
41
41
  const utils = require("../utilities/utils");
42
42
 
43
43
  let autoRegHost = process.env.SOAJS_SRV_AUTOREGISTERHOST || true;
44
- if (autoRegHost && typeof(autoRegHost) !== 'boolean') {
44
+ if (autoRegHost && typeof (autoRegHost) !== 'boolean') {
45
45
  autoRegHost = (autoRegHost === 'true');
46
46
  }
47
47
  let regEnvironment = (process.env.SOAJS_ENV || "dev");
@@ -49,10 +49,10 @@ regEnvironment = regEnvironment.toLowerCase();
49
49
 
50
50
  function Controller(config) {
51
51
  let _self = this;
52
-
52
+
53
53
  config = config || {};
54
54
  let param = lib.param(config);
55
- _self.soajs = {"param": param};
55
+ _self.soajs = { "param": param };
56
56
  }
57
57
 
58
58
  Controller.prototype.init = function (callback) {
@@ -77,7 +77,7 @@ Controller.prototype.init = function (callback) {
77
77
  log.info("The IP registered for service [" + _self.soajs.param.init.serviceName + "] awareness : ", service.fetched.ip);
78
78
  }
79
79
  }
80
-
80
+
81
81
  let app = connect();
82
82
  app.use(favicon_mw());
83
83
  app.use(soajs_mw({
@@ -85,9 +85,9 @@ Controller.prototype.init = function (callback) {
85
85
  "core": core
86
86
  }));
87
87
  app.use(cors_mw());
88
- app.use(response_mw({"controllerResponse": true}));
88
+ app.use(response_mw({ "controllerResponse": true }));
89
89
  app.use(maintenanceMode_mw()); //NOTE: this is for global maintenance mode
90
-
90
+
91
91
  log.info("Loading Provision ...");
92
92
  let dbConfig = registry.coreDB.provision;
93
93
  if (registry.coreDB.oauth) {
@@ -101,11 +101,11 @@ Controller.prototype.init = function (callback) {
101
101
  if (loaded) {
102
102
  log.info("Service provision loaded.");
103
103
  let server = http.createServer(app);
104
-
104
+
105
105
  // Create maintenance routes
106
106
  let maintenance = require("./maintenance.js");
107
107
  let serverMaintenance = maintenance(core, log, _self.soajs.param.init, service.ip, regEnvironment, awareness_mw, soajsLib, provision);
108
-
108
+
109
109
  // Add gateway middleware
110
110
  app.use(awareness_mw.getMw({
111
111
  "awareness": _self.soajs.param.init.awareness,
@@ -116,7 +116,7 @@ Controller.prototype.init = function (callback) {
116
116
  "serviceIp": service.ip
117
117
  }));
118
118
  log.info("Awareness middleware initialization done.");
119
-
119
+
120
120
  //added mw awarenessEnv so that proxy can use req.soajs.awarenessEnv.getHost('dev', cb)
121
121
  app.use(awarenessEnv_mw.getMw({
122
122
  "awarenessEnv": true,
@@ -126,17 +126,17 @@ Controller.prototype.init = function (callback) {
126
126
  "log": log
127
127
  }));
128
128
  log.info("AwarenessEnv middleware initialization done.");
129
-
129
+
130
130
  app.use(enhancer_mw());
131
-
132
- app.use(url_mw({"core": core}));
133
-
134
- app.use(key_mw({"provision": provision, "regEnvironment": regEnvironment}));
135
-
131
+
132
+ app.use(url_mw({ "core": core }));
133
+
134
+ app.use(key_mw({ "provision": provision, "regEnvironment": regEnvironment }));
135
+
136
136
  //TODO: add maintenance by tenant here req.soajs.controller.serviceParams.keyObj.config
137
137
 
138
138
  app.use(keyACL_mw());
139
-
139
+
140
140
  app.use(gotoService_mw({
141
141
  "provision": provision,
142
142
  "serviceName": _self.soajs.param.init.serviceName,
@@ -144,7 +144,7 @@ Controller.prototype.init = function (callback) {
144
144
  "serviceGroup": _self.soajs.param.init.serviceGroup,
145
145
  "core": core
146
146
  }));
147
-
147
+
148
148
  if (registry.serviceConfig.oauth) {
149
149
  let oauth_mw = require("../mw/oauth/index");
150
150
  // NOTE: oauth_mw is set on soajs.oauth and is triggered from inside mt_mw
@@ -153,10 +153,10 @@ Controller.prototype.init = function (callback) {
153
153
  "serviceConfig": registry.serviceConfig,
154
154
  "model": provision.oauthModel
155
155
  });
156
-
156
+
157
157
  log.info("oAuth middleware initialization done.");
158
158
  }
159
-
159
+
160
160
  app.use(mt_mw({
161
161
  "soajs": _self.soajs,
162
162
  "app": app,
@@ -166,14 +166,17 @@ Controller.prototype.init = function (callback) {
166
166
  "provision": provision
167
167
  }));
168
168
  log.info("SOAJS MT middleware initialization done.");
169
-
170
- app.use(traffic_mw({}));
171
-
169
+
170
+ app.use(traffic_mw({
171
+ "log": log,
172
+ "gatewayDB": registry.coreDB.gateway || registry.coreDB.provision
173
+ }));
174
+
172
175
  app.use((req, res, next) => {
173
176
  setImmediate(() => {
174
177
  req.soajs.controller.gotoservice(req, res, next);
175
178
  });
176
-
179
+
177
180
  req.on("error", (error) => {
178
181
  req.soajs.log.error("Error @ controller:", error);
179
182
  if (req.soajs.controller.redirectedRequest) {
@@ -181,7 +184,7 @@ Controller.prototype.init = function (callback) {
181
184
  req.soajs.controller.redirectedRequest = null;
182
185
  }
183
186
  });
184
-
187
+
185
188
  req.on("close", () => {
186
189
  if (req.soajs.controller.redirectedRequest) {
187
190
  req.soajs.log.info("Request closed:", req.url);
@@ -190,11 +193,11 @@ Controller.prototype.init = function (callback) {
190
193
  }
191
194
  });
192
195
  });
193
-
196
+
194
197
  app.use(utils.logErrors);
195
198
  app.use(utils.controllerClientErrorHandler);
196
199
  app.use(utils.controllerErrorHandler);
197
-
200
+
198
201
  callback(registry, log, service, server, serverMaintenance);
199
202
  } else {
200
203
  log.error('Unable to load provision. controller will not start :(');
@@ -217,13 +220,13 @@ Controller.prototype.start = function (registry, log, service, server, serverMai
217
220
  log.error(err.message);
218
221
  }
219
222
  });
220
-
223
+
221
224
  let getAwarenessInfo = (terminate, cb) => {
222
225
  let tmp = registryModule.get();
223
226
  if (tmp && (tmp.services || tmp.daemons)) {
224
227
  let awarenessStatData = {
225
228
  "ts": Date.now(),
226
- "data": soajsUtils.cloneObj({"services": tmp.services, "daemons": tmp.daemons})
229
+ "data": soajsUtils.cloneObj({ "services": tmp.services, "daemons": tmp.daemons })
227
230
  };
228
231
  if (terminate) {
229
232
  for (let serviceName in awarenessStatData.data.services) {
@@ -252,7 +255,7 @@ Controller.prototype.start = function (registry, log, service, server, serverMai
252
255
  }, cb);
253
256
  }
254
257
  };
255
-
258
+
256
259
  server.listen(registry.services.controller.port, (err) => {
257
260
  if (err) {
258
261
  log.error(err.message);
@@ -268,7 +271,7 @@ Controller.prototype.start = function (registry, log, service, server, serverMai
268
271
  }, registry, (registered) => {
269
272
  if (registered) {
270
273
  log.info("Host IP [" + service.ip + "] for service [" + _self.soajs.param.init.serviceName + "@" + _self.soajs.param.init.serviceVersion + "] successfully registered.");
271
-
274
+
272
275
  //update the database with the awareness Response generated.
273
276
  setTimeout(() => {
274
277
  getAwarenessInfo(false, (error) => {
@@ -277,7 +280,7 @@ Controller.prototype.start = function (registry, log, service, server, serverMai
277
280
  }
278
281
  });
279
282
  }, registry.serviceConfig.awareness.healthCheckInterval);
280
-
283
+
281
284
  //update the database with the awareness Response generated.
282
285
  //controller has been terminated.
283
286
  process.on('SIGINT', () => {
@@ -313,7 +316,7 @@ Controller.prototype.start = function (registry, log, service, server, serverMai
313
316
  log.info(_self.soajs.param.init.serviceName + " service maintenance is listening on port: " + maintenancePort);
314
317
  }
315
318
  });
316
-
319
+
317
320
  }
318
321
  };
319
322
 
@@ -321,7 +324,7 @@ Controller.prototype.stop = function (registry, log, service, server, serverMain
321
324
  let maintenancePort = registry.services.controller.port + registry.serviceConfig.ports.maintenanceInc;
322
325
  log.info('stopping Server on port:', registry.services.controller.port);
323
326
  log.info('stopping MaintenanceServer on port:', maintenancePort);
324
-
327
+
325
328
  server.close((err) => {
326
329
  serverMaintenance.close(() => {
327
330
  if (callback) {
@@ -9,6 +9,9 @@ describe("Unit test for: mw - traffic", () => {
9
9
  it("success - default from environment", (done) => {
10
10
  let res = {};
11
11
  let req = {
12
+ getClientIP: () => {
13
+ return "127.0.0.1"
14
+ },
12
15
  soajs: {
13
16
  registry: {
14
17
  serviceConfig: {