UNPKG

4.86 kBJavaScriptView Raw
1'use strict';
2
3// QUOTA
4// module: microgateway-plugins/quota
5
6
7var async = require('async');
8var Quota = require('volos-quota-apigee');
9var debug = require('debug')('gateway:quota');
10var url = require('url');
11
12
13module.exports.init = function(config /*, logger, stats */) {
14
15 const { product_to_proxy, proxies } = config;
16 const prodsObj = {};
17 var quotas = {}; // productName -> connectMiddleware
18 var options = {
19 key: function(req) {
20 return req.token.application_name+'.'+req.productName;
21 }
22 };
23
24 var quotaManagers = {}
25
26 if ( (product_to_proxy === undefined) || (proxies === undefined) ) {
27 //
28 debug("quota plugin did not recieve valid produc-proxy map or list of proxies")
29 return(undefined)
30 }
31
32 Object.keys(config).forEach(function(productName) {
33 var product = config[productName];
34 if (!product.uri && !product.key && !product.secret && !product.allow && !product.interval || product.interval === "null") {
35 // skip non-quota config
36 debug('Quota not configured on the API product, skipping. This message is safe to ignore');
37 return;
38 }
39
40 if ( product.timeUnit === 'month' ) {
41 //product.timeUnit = '30days'; // this is broken - volos does not consider 30days as an option, but tries to process it anyway.
42 }
43
44 const prodProxiesArr = product_to_proxy[productName];
45
46 const prodObj = {};
47 if (Array.isArray(prodProxiesArr)) {
48 prodProxiesArr.reduce((acc, val) => {
49 acc[val] = true;
50 return acc;
51 }, prodObj);
52 }
53
54 const basePaths = {};
55
56 if (Array.isArray(proxies)) {
57 proxies.reduce((acc, prox) => {
58 if (prox.name !== 'edgemicro-auth' && prodObj[prox.name] === true) acc[prox.base_path] = true;
59 return acc;
60 }, basePaths);
61 }
62
63 prodObj.basePaths = basePaths;
64 prodsObj[productName] = prodObj;
65
66 config[productName].request = config.request;
67 var quota = Quota.create(config[productName]);
68 quotas[productName] = quota.connectMiddleware().apply(options);
69 //
70 quotaManagers[productName] = quota;
71 debug('created quota for', productName);
72 });
73
74 var middleware = function(req, res, next) {
75
76 if (!req.token || !req.token.api_product_list || !req.token.api_product_list.length) {
77 return next();
78 }
79
80 debug('quota checking products', req.token.api_product_list);
81
82 req.originalUrl = req.originalUrl || req.url; // emulate connect
83
84 let proxyPath = res.proxy ? res.proxy.base_path : undefined;
85 let proxyUrl = req.url ? url.parse(req.url).pathname : undefined;
86 let matchedPathProxy = proxyPath || proxyUrl || '';
87 debug('matchedPathProxy',matchedPathProxy);
88
89 const prodList = [];
90 if (Array.isArray(req.token.api_product_list)) {
91 req.token.api_product_list.reduce((acc, prod) => {
92 if (prodsObj[prod] &&
93 prodsObj[prod].basePaths &&
94 prodsObj[prod].basePaths[matchedPathProxy] === true) acc.push(prod);
95 return acc;
96 }, prodList);
97
98 debug('prodList', prodList);
99 }
100
101 // this is arbitrary, but not sure there's a better way?
102 // async.eachSeries(req.token.api_product_list,
103 async.eachSeries(prodList,
104 function(productName, cb) {
105 var connectMiddleware = quotas[productName];
106 debug('applying quota for', productName);
107 req['productName'] = productName; // to be used for quota identifier generation
108 if ( connectMiddleware ){ connectMiddleware(req, res, cb) } else cb();
109 },
110 function(err) {
111 next(err);
112 }
113 );
114 }
115
116 return {
117
118 testprobe: function() {
119 return quotas
120 },
121
122 onrequest: function(req, res, next) {
123 if ( process.env.EDGEMICRO_LOCAL !== undefined ) {
124 debug("MG running in local mode. Skipping Quota");
125 next();
126 } else {
127 middleware(req, res, next);
128 }
129 },
130
131 shutdown: function() {
132 // look for extant timers ... for global graceful shutdown...
133 for ( var qmKey in quotaManagers ) {
134 var q = quotaManagers[qmKey];
135 if ( q.quota ) {
136 q = q.quota;
137 }
138 if ( q.bucketTimer ) {
139 clearInterval(q.bucketTimer)
140 }
141 if ( q.flushTimer ) {
142 clearInterval(q.flushTimer)
143 }
144 }
145 }
146
147 }
148};