1 | var STORE = require('store-ttl');
|
2 | var _ = require('lodash');
|
3 | var util = require('./util');
|
4 | var async = require('async');
|
5 | function randomToken(len) {
|
6 | return Math.random().toString(len || 26).slice(2);
|
7 | }
|
8 |
|
9 | function Token(config) {
|
10 | if (!_.isObject(config)) {
|
11 | config = {};
|
12 | }
|
13 | config.storeConfig = config.storeConfig || {};
|
14 | config.count = config.count || 5;
|
15 | this.config = config;
|
16 | this.store = new STORE(config.storeConfig);
|
17 | this.config.webTokenVarName = this.config.webTokenVarName || 'encrypt_api_tokenStr'
|
18 | }
|
19 |
|
20 | Token.prototype.issue = function(ttl, count, token, cb) {
|
21 | var store = this.store;
|
22 | if (_.isFunction(ttl)) {
|
23 | return ttl('issue api must be set ttl.');
|
24 | }
|
25 | if(_.isFunction(count)) {
|
26 | cb = count;
|
27 | token = randomToken();
|
28 | count = this.config.count;;
|
29 | }
|
30 | if (_.isString(count) && _.isFunction(token)) {
|
31 | cb = token;
|
32 | token = count;
|
33 | count = this.config.count;
|
34 | }
|
35 | if (_.isNumber(count) && _.isFunction(token)) {
|
36 | cb = token;
|
37 | token = randomToken();
|
38 | }
|
39 | if (!_.isFunction(cb)) {
|
40 | throw 'issue api is async function, you must have callback function.'
|
41 | }
|
42 | store.set(token, count, ttl, function(err, data) {
|
43 | if (!err && data) {
|
44 | cb(null, token);
|
45 | } else {
|
46 | cb(err || 'issue token error : ' + data);
|
47 | }
|
48 | });
|
49 | }
|
50 |
|
51 | Token.prototype.verify = function(token, cb) {
|
52 | var store = this.store;
|
53 | store.get(token, function(err, data) {
|
54 | if (!err && data) {
|
55 | cb(null, data.data);
|
56 | } else {
|
57 | cb(err || 'token : ' + token + ' haved expire.')
|
58 | }
|
59 | });
|
60 | }
|
61 |
|
62 | Token.prototype.remove = function(token, cb) {
|
63 | var store = this.store;
|
64 | store.remove(token, function(err, data) {
|
65 | cb(err, data);
|
66 | });
|
67 | }
|
68 |
|
69 | Token.prototype.decline = function(token, cb) {
|
70 | var store = this.store;
|
71 | store.get(token, function(err, data) {
|
72 | if (!err && data) {
|
73 | store.update(token, --data.data, function(err, data) {
|
74 | cb(err, (data && data.data));
|
75 | });
|
76 | } else {
|
77 | cb(err || 'not exists or expired token : ' + token + '.')
|
78 | }
|
79 | })
|
80 | }
|
81 |
|
82 | Token.prototype.webInject = function(html, token, callback) {
|
83 | if (!_.isString(html)) {
|
84 | return callback('html must be string', html);
|
85 | }
|
86 | var webTokenVarName = this.config.webTokenVarName;
|
87 | var webInject = this.config.webInject || function(html, token, callback) {
|
88 | var bodyEndStr = '</body>';
|
89 | var bodyStartStr = '<body';
|
90 | var tokenId = '_' + util.generateSalt(Math.ceil(Math.random() * 10));
|
91 | var bodyStarIndex = html.indexOf(bodyStartStr);
|
92 | var bodyEndIndex = html.indexOf(bodyEndStr);
|
93 | var htmlTagLastIndexs = util.matchHtmlTags(html.substring(bodyStarIndex, bodyEndIndex + 1));
|
94 | if (!htmlTagLastIndexs.length) {
|
95 | htmlTagLastIndexs[0] = bodyEndIndex - bodyStarIndex;
|
96 | }
|
97 | var htmlTagLen = htmlTagLastIndexs.length;
|
98 | var tokenLen = token.length;
|
99 | var times = Math.floor(htmlTagLen / tokenLen);
|
100 | var randomIndex = 0;
|
101 | var addLen = 0;
|
102 | var scriptStr = 'var r="";function g(id){return document.getElementById(id).innerHTML}';
|
103 | for (var i = 0; i < tokenLen; i++) {
|
104 | var htmlTag = util.makeHtmlTag(tokenId + i, token[i]);
|
105 | randomIndex += Math.floor(Math.random() * times);
|
106 | if (!htmlTagLastIndexs[randomIndex]) {
|
107 | randomIndex = htmlTagLen - 1;
|
108 | }
|
109 | var curIndex = bodyStarIndex + htmlTagLastIndexs[randomIndex] + addLen;
|
110 | var prevHtml = html.substring(0, curIndex);
|
111 | var nextHtml = html.substr(curIndex);
|
112 | scriptStr += 'r += g("' + (tokenId + i) + '");';
|
113 | if (i === tokenLen - 1) {
|
114 | scriptStr += 'w.' + webTokenVarName + '=r;';
|
115 | html = prevHtml.concat(htmlTag, '<script>(function(w){' + scriptStr + '})(window)</script>', nextHtml);
|
116 | } else {
|
117 | html = prevHtml.concat(htmlTag, nextHtml);
|
118 | }
|
119 | addLen += htmlTag.length;
|
120 | }
|
121 | callback(null, html);
|
122 | };
|
123 | webInject.call(this, html, token, callback);
|
124 | }
|
125 |
|
126 | Token.prototype.pass = function(token, cb) {
|
127 | var that = this;
|
128 | this.verify(token, function(err, data) {
|
129 | if (err) {
|
130 | return cb(err, {
|
131 | code: -1,
|
132 | msg: err,
|
133 | passed: false
|
134 | });
|
135 | }
|
136 | if (data) {
|
137 | that.decline(token, function(err, count) {
|
138 | if (!err) {
|
139 | return cb(null, {
|
140 | code: 0,
|
141 | msg: 'ok',
|
142 | passed: true,
|
143 | count: count
|
144 | })
|
145 | }
|
146 | cb(err, {
|
147 | code: -2,
|
148 | msg: 'decline token times error : ' + err,
|
149 | passed: false
|
150 | })
|
151 | });
|
152 | } else {
|
153 | cb(null, {
|
154 | code: 1,
|
155 | msg: 'access token times haved used out.',
|
156 | passed: false,
|
157 | count: 0
|
158 | })
|
159 | }
|
160 | })
|
161 | }
|
162 |
|
163 |
|
164 | Token.prototype.limit = function(apiName, ttl, callCount, cb) {
|
165 | var _this = this;
|
166 | if (!_.isString(apiName)) {
|
167 | cb = arguments[arguments.length-1];
|
168 | return cb('the first param must be string');
|
169 | }
|
170 | async.waterfall([
|
171 | function(cb) {
|
172 | var code = 0;
|
173 | _this.verify(apiName, function(err, count) {
|
174 | if (err) {
|
175 | code = 1;
|
176 | }
|
177 | if (count === 0) {
|
178 | code = 2;
|
179 | }
|
180 | cb(null, code);
|
181 | })
|
182 | },
|
183 | function(code, cb) {
|
184 | switch (code) {
|
185 | case 0:
|
186 | _this.decline(apiName, function(err, count) {
|
187 | cb(err);
|
188 | })
|
189 | break;
|
190 | case 1:
|
191 | _this.issue(ttl, callCount, apiName, function(err, data) {
|
192 | cb(err);
|
193 | })
|
194 | break;
|
195 | case 2:
|
196 | cb(apiName + 'limited number of calls.');
|
197 | break;
|
198 | default:
|
199 | cb(apiName + 'called error.');
|
200 | }
|
201 | }
|
202 | ], function(err) {
|
203 | cb(err);
|
204 | })
|
205 | }
|
206 |
|
207 | module.exports = Token; |
\ | No newline at end of file |