topbit 1.0.0 → 3.0.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.
Files changed (89) hide show
  1. package/LICENSE +128 -0
  2. package/README.cn.md +1519 -0
  3. package/README.md +1483 -0
  4. package/bin/app.js +17 -0
  5. package/bin/loadinfo.sh +18 -0
  6. package/bin/new-ctl.js +234 -0
  7. package/bin/newapp.js +22 -0
  8. package/demo/allow.js +98 -0
  9. package/demo/cert/localhost-cert.pem +19 -0
  10. package/demo/cert/localhost-privkey.pem +28 -0
  11. package/demo/controller/api.js +15 -0
  12. package/demo/extends.js +5 -0
  13. package/demo/group-api.js +161 -0
  14. package/demo/group-api2.js +109 -0
  15. package/demo/http2.js +34 -0
  16. package/demo/http2_proxy_backend.js +45 -0
  17. package/demo/http2proxy.js +48 -0
  18. package/demo/http_proxy_backend.js +44 -0
  19. package/demo/httpproxy.js +47 -0
  20. package/demo/loader.js +27 -0
  21. package/demo/log.js +118 -0
  22. package/demo/memlimit.js +31 -0
  23. package/demo/min.js +7 -0
  24. package/demo/serv.js +15 -0
  25. package/images/middleware.jpg +0 -0
  26. package/images/topbit-middleware.png +0 -0
  27. package/images/topbit.png +0 -0
  28. package/package.json +42 -11
  29. package/src/_loadExtends.js +21 -0
  30. package/src/bodyparser.js +420 -0
  31. package/src/connfilter.js +125 -0
  32. package/src/context1.js +166 -0
  33. package/src/context2.js +182 -0
  34. package/src/ctxpool.js +39 -0
  35. package/src/ext.js +318 -0
  36. package/src/extends/Http2Pool.js +365 -0
  37. package/src/extends/__randstring.js +24 -0
  38. package/src/extends/cookie.js +44 -0
  39. package/src/extends/cors.js +334 -0
  40. package/src/extends/errorlog.js +252 -0
  41. package/src/extends/http2limit.js +126 -0
  42. package/src/extends/http2proxy.js +691 -0
  43. package/src/extends/jwt.js +217 -0
  44. package/src/extends/mixlogger.js +63 -0
  45. package/src/extends/paramcheck.js +266 -0
  46. package/src/extends/proxy.js +662 -0
  47. package/src/extends/realip.js +34 -0
  48. package/src/extends/referer.js +68 -0
  49. package/src/extends/resource.js +398 -0
  50. package/src/extends/session.js +174 -0
  51. package/src/extends/setfinal.js +50 -0
  52. package/src/extends/sni.js +48 -0
  53. package/src/extends/sse.js +293 -0
  54. package/src/extends/timing.js +111 -0
  55. package/src/extends/tofile.js +123 -0
  56. package/src/fastParseUrl.js +426 -0
  57. package/src/headerLimit.js +18 -0
  58. package/src/http1.js +336 -0
  59. package/src/http2.js +337 -0
  60. package/src/httpc.js +251 -0
  61. package/src/lib/npargv.js +354 -0
  62. package/src/lib/zipdata.js +45 -0
  63. package/src/loader/loader.js +999 -0
  64. package/src/logger.js +32 -0
  65. package/src/loggermsg.js +349 -0
  66. package/src/makeId.js +200 -0
  67. package/src/midcore.js +213 -0
  68. package/src/middleware1.js +103 -0
  69. package/src/middleware2.js +116 -0
  70. package/src/monitor.js +380 -0
  71. package/src/movefile.js +30 -0
  72. package/src/optionsCheck.js +54 -0
  73. package/src/randstring.js +23 -0
  74. package/src/router.js +682 -0
  75. package/src/sendmsg.js +27 -0
  76. package/src/strong.js +72 -0
  77. package/src/token/token.js +461 -0
  78. package/src/topbit.js +1293 -0
  79. package/src/versionCheck.js +31 -0
  80. package/test/test-bigctx.js +29 -0
  81. package/test/test-daemon-args.js +7 -0
  82. package/test/test-ext.js +81 -0
  83. package/test/test-find.js +69 -0
  84. package/test/test-route-sort.js +71 -0
  85. package/test/test-route.js +49 -0
  86. package/test/test-route2.js +51 -0
  87. package/test/test-run-args.js +7 -0
  88. package/test/test-url.js +52 -0
  89. package/main.js +0 -0
@@ -0,0 +1,217 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ *
5
+ * jwt格式:
6
+ *
7
+ * base64UrlEncoded(header).base64UrlEncoded(payload).signature
8
+ *
9
+ * header: {
10
+ * "alg": "hs256",
11
+ * "typ": "JWT"
12
+ * }
13
+ *
14
+ * payload是任何object类型的数据
15
+ *
16
+ * payload 示例 {
17
+ * "id": "1234",
18
+ * "name": "okk"
19
+ * }
20
+ *
21
+ * signature是hamacsha256(base64UrlEncoded(header).base64UrlEncoded(payload), key)
22
+ *
23
+ */
24
+
25
+ const randstring = require('./__randstring.js')
26
+
27
+ const crypto = require('node:crypto')
28
+
29
+ class JWT {
30
+
31
+ constructor(options = {}) {
32
+ this.expires = 3600000
33
+ this.autoTimeout = true
34
+ this.header = ''
35
+
36
+ this.algMap = {
37
+ HS256: 'sha256',
38
+ HS384: 'sha384',
39
+ HS512: 'sha512',
40
+ SM3: 'sm3',
41
+ SHA256: 'sha256',
42
+ SHA512: 'sha512',
43
+ SHA384: 'sha384'
44
+ }
45
+
46
+ this.algKeys = Object.keys(this.algMap)
47
+
48
+ Object.defineProperty(this, '__key__', {
49
+ value: randstring(16),
50
+ enumerable: false,
51
+ configurable: false,
52
+ writable: true
53
+ })
54
+
55
+ Object.defineProperty(this, '__alg__', {
56
+ value: 'SM3',
57
+ enumerable: false,
58
+ configurable: false,
59
+ writable: true
60
+ })
61
+
62
+ Object.defineProperties(this, {
63
+ key: {
64
+ get: () => {
65
+ return this.__key__
66
+ },
67
+
68
+ set: (key) => {
69
+ this.__key__ = key
70
+ this.makeHeader()
71
+ }
72
+ },
73
+
74
+ alg: {
75
+ get: () => {
76
+ return this.__alg__
77
+ },
78
+
79
+ set: (a) => {
80
+ a = a.toUpperCase()
81
+ if (this.algKeys.indexOf(a) >= 0) {
82
+ this.__alg__ = a
83
+ this.makeHeader()
84
+ } else {
85
+ console.error(`无法支持的算法:${a}`)
86
+ }
87
+ }
88
+ }
89
+ })
90
+
91
+ for (let k in options) {
92
+ switch(k) {
93
+ case 'expires':
94
+ case 'autoTimeout':
95
+ case 'alg':
96
+ case 'key':
97
+ this[k] = options[k];
98
+ break;
99
+ }
100
+ }
101
+
102
+ this.makeHeader()
103
+ }
104
+
105
+ makeHeader() {
106
+ let hdata = `{"alg":"${this.__alg__}","typ":"JWT"}`
107
+ this.header = Buffer.from(hdata).toString('base64url')
108
+ }
109
+
110
+ make(data) {
111
+ if (typeof data === 'object') {
112
+ if (this.autoTimeout) {
113
+ data.__timeout__ = Date.now() + this.expires
114
+ }
115
+
116
+ data = JSON.stringify(data)
117
+ }
118
+
119
+ let org_str = `${this.header}.${Buffer.from(data).toString('base64url')}`
120
+ return `${org_str}.${this.sign(org_str, this.algMap[this.__alg__])}`
121
+ }
122
+
123
+ sign(org_str, a = 'sm3') {
124
+ let h = crypto.createHmac(a, this.__key__)
125
+ h.update(org_str)
126
+ return h.digest('base64url')
127
+ }
128
+
129
+ verify(token) {
130
+ let arr = token.split('.')
131
+ if (arr.length !== 3) {
132
+ return {
133
+ ok: false,
134
+ errcode: 'ILLEGAL'
135
+ }
136
+ }
137
+
138
+ let alg = this.__alg__
139
+
140
+ try {
141
+ let header = JSON.parse(Buffer.from(arr[0], 'base64url').toString('utf8'))
142
+ alg = header.alg
143
+ } catch (err) {
144
+ return {
145
+ ok: false,
146
+ errcode: 'ERR_HEADER'
147
+ }
148
+ }
149
+
150
+ let hs = this.algMap[alg]
151
+
152
+ if (!hs) {
153
+ return {
154
+ ok: false,
155
+ errcode: 'UNKNOW_ALG'
156
+ }
157
+ }
158
+
159
+ let hstr = this.sign(`${arr[0]}.${arr[1]}`, hs)
160
+
161
+ if (hstr !== arr[2]) {
162
+ return {
163
+ ok: false,
164
+ errcode: 'FAILED'
165
+ }
166
+ }
167
+
168
+ let data = Buffer.from(arr[1], 'base64url').toString('utf8')
169
+
170
+ try {
171
+ data = JSON.parse(data)
172
+ } catch (err) {
173
+ return {
174
+ ok: false,
175
+ errcode: 'ERR_DATA'
176
+ }
177
+ }
178
+
179
+ if (data.__timeout__ !== undefined && (Date.now() > data.__timeout__)) {
180
+ return {
181
+ ok: false,
182
+ errcode: 'TIMEOUT'
183
+ }
184
+ }
185
+
186
+ return {
187
+ ok: true,
188
+ data: data
189
+ }
190
+
191
+ }
192
+
193
+ mid() {
194
+ let self = this
195
+
196
+ return async (ctx, next) => {
197
+ let token = ctx.headers.authorization || ctx.query.token
198
+ if (!token) {
199
+ return ctx.status(401).send('unauthorized')
200
+ }
201
+
202
+ let r = self.verify(token)
203
+
204
+ if (!r.ok) {
205
+ return ctx.status(401).send(r.errcode)
206
+ }
207
+
208
+ ctx.box.user = r.data
209
+ ctx.user = r.data
210
+
211
+ await next(ctx)
212
+ }
213
+ }
214
+
215
+ }
216
+
217
+ module.exports = JWT
@@ -0,0 +1,63 @@
1
+ 'use strict'
2
+
3
+ const cluster = require('node:cluster')
4
+
5
+ class MixLogger {
6
+
7
+ constructor(options = {}) {
8
+ this.logHandle = (w, msg, handle) => {
9
+ return true
10
+ }
11
+
12
+ this.quiet = true
13
+
14
+ for (let k in options) {
15
+ switch(k) {
16
+ case 'logHandle':
17
+ if (typeof options[k] === 'function') {
18
+ this.logHandle = options[k]
19
+ }
20
+ break
21
+
22
+ case 'quiet':
23
+ this.quiet = !!options[k]
24
+ break
25
+ }
26
+ }
27
+
28
+ }
29
+
30
+ init(app) {
31
+ if (cluster.isWorker) {
32
+ return
33
+ }
34
+
35
+ //版本兼容
36
+ let mse = app.daeMsgEvent ? app.daeMsgEvent : app.msgEvent
37
+
38
+ if (mse['_log'] === undefined) {
39
+ if (!this.quiet) {
40
+ return console.error(`Warning: mixlogger must be running in daemon mode`)
41
+ }
42
+ return
43
+ }
44
+
45
+ let self = this
46
+
47
+ let org_log = mse['_log'].callback
48
+
49
+ let log_handle = (w, msg, handle) => {
50
+ if (false === self.logHandle(w, msg, handle) ) {
51
+ return
52
+ }
53
+
54
+ org_log(w, msg, handle)
55
+ }
56
+
57
+ app.setMsgEvent('_log', log_handle)
58
+ }
59
+
60
+ }
61
+
62
+ module.exports = MixLogger
63
+
@@ -0,0 +1,266 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * 用于param或者query检测的中间件扩展,启用此扩展,会自动进行属性值的检测。
5
+ * 支持声明式的设计,避免重复劳动。
6
+ */
7
+
8
+ let TYPE_STRING = 1
9
+ let TYPE_NUMBER = 2
10
+
11
+ class ParamCheck {
12
+
13
+ constructor(options = {}) {
14
+ this.type = ['query', 'param', 'body']
15
+ this.key = 'param'
16
+ this.data = {}
17
+ this.errorMessage = "提交数据不符合要求"
18
+ //设置禁止提交的字段
19
+ this.deny = null
20
+ this.denyMessage = '存在禁止提交的数据'
21
+ this.deleteDeny = false
22
+
23
+ for (let k in options) {
24
+ switch (k) {
25
+ case 'key':
26
+ if (this.type.indexOf(options[k]) >= 0) {
27
+ this.key = options[k]
28
+ }
29
+ break
30
+
31
+ case 'data':
32
+ if (typeof options[k] === 'object') {
33
+ this.data = options[k]
34
+ }
35
+ break
36
+
37
+ case 'errorMessage':
38
+ this[k] = options[k]
39
+ break
40
+
41
+ case 'deny':
42
+ if (typeof options[k] === 'string') {
43
+ options[k] = [options[k]]
44
+ }
45
+ if (Array.isArray(options[k])) {
46
+ this.deny = options[k]
47
+ }
48
+ break
49
+
50
+ case 'deleteDeny':
51
+ this.deleteDeny = !!options[k]
52
+ break
53
+
54
+ default:
55
+ this[k] = options[k]
56
+ }
57
+ }
58
+
59
+ let data_type = ''
60
+ for (let k in this.data) {
61
+ data_type = typeof this.data[k]
62
+
63
+ if (data_type === 'string' || data_type === 'number') {
64
+ this.data[k] = {
65
+ __is_value__: true,
66
+ __value__: this.data[k],
67
+ __type__: data_type === 'string' ? TYPE_STRING : TYPE_NUMBER
68
+ }
69
+
70
+ continue
71
+ }
72
+
73
+ this.data[k].__is_value__ = false
74
+
75
+ if (this.data[k].callback && typeof this.data[k].callback === 'function') {
76
+ this.data[k].__is_call__ = true
77
+ } else {
78
+ this.data[k].__is_call__ = false
79
+ }
80
+ }
81
+
82
+ }
83
+
84
+ /**
85
+ *
86
+ * @param {object} obj c.param or c.query
87
+ * @param {*} k key name
88
+ * @param {*} rule filter rule
89
+ *
90
+ * rule描述了如何进行数据的过滤,如果rule是字符串或数字则要求严格相等。
91
+ * 否则rule应该是一个object,可以包括的属性如下:
92
+ * - callback 用于过滤的回调函数,在验证时,会传递数据,除此之外没有其他参数。
93
+ * - must false|true,表示是不是必须,如果must为true,则表示数据不能是undefined。
94
+ * - default 默认值,如果存在,而参数属性未定义,则赋予默认值。
95
+ * - to int|float 要转换成哪种类型。
96
+ * - min 最小值,可以 >= 此值,数字或字符串。
97
+ * - max 最大值,可以 <= 此值,数字或字符串。
98
+ */
99
+ checkData(obj, k, rule, method, ost) {
100
+ let typ = typeof rule
101
+
102
+ ost.ok = true
103
+ ost.key = k
104
+
105
+ if (!rule.__is_value__) {
106
+ if (obj[k] === undefined) {
107
+ if (rule.must) {
108
+ ost.ok = false
109
+ return false
110
+ }
111
+
112
+ if (rule.default !== undefined) {
113
+ obj[k] = rule.default
114
+ return true
115
+ }
116
+
117
+ } else {
118
+ //数据初始必然是字符串,转换只能是整数或浮点数或boolean。
119
+ if (rule.to) {
120
+ if (isNaN(obj[k])) {
121
+ ost.ok = false
122
+ return false
123
+ }
124
+
125
+ switch(rule.to) {
126
+ case 'int':
127
+ obj[k] = parseInt(obj[k])
128
+ if (isNaN(obj[k])) {
129
+ if (rule.default !== undefined) {
130
+ obj[k] = rule.default
131
+ return true
132
+ }
133
+ ost.ok = false
134
+ return false
135
+ }
136
+ break
137
+
138
+ case 'float':
139
+ obj[k] = parseFloat(obj[k])
140
+ if (isNaN(obj[k])) {
141
+ if (rule.default !== undefined) {
142
+ obj[k] = rule.default
143
+ return true
144
+ }
145
+ ost.ok = false
146
+ return false
147
+ }
148
+ break
149
+
150
+ case 'boolean':
151
+ case 'bool':
152
+ obj[k] = obj[k] === 'true' ? true : false
153
+ break
154
+ }
155
+ }
156
+
157
+ if (rule.min !== undefined && obj[k] < rule.min) {
158
+ ost.ok = false
159
+ return false
160
+ }
161
+
162
+ if (rule.max !== undefined && obj[k] > rule.max) {
163
+ ost.ok = false
164
+ return false
165
+ }
166
+ }
167
+
168
+ //无论obj[k]是否存在,只要存在callback,就要执行。
169
+ if (rule.__is_call__) {
170
+ if (rule.callback(obj, k, method) !== false) {
171
+ return true
172
+ }
173
+ ost.ok = false
174
+ return false
175
+ }
176
+
177
+ } else if (rule.__type__ === TYPE_STRING) {
178
+ if (obj[k] === undefined || obj[k] !== rule.__value__) {
179
+ ost.ok = false
180
+ return false
181
+ }
182
+ } else if (rule.__type__ === TYPE_NUMBER) {
183
+ if (obj[k] === undefined || obj[k] != rule.__value__) {
184
+ ost.ok = false
185
+ return false
186
+ }
187
+ }
188
+
189
+ return true
190
+ }
191
+
192
+ dataFilter(c) {
193
+ let d = c[this.key]
194
+ let ost = {ok: true, key: ''}
195
+
196
+ if (this.key !== 'body' || (c.body !== c.rawBody && typeof c.body === 'object')) {
197
+ for (let k in this.data) {
198
+ if (!this.checkData(d, k, this.data[k], c.method, ost)) {
199
+ return ost
200
+ }
201
+ }
202
+ }
203
+
204
+ return ost
205
+ }
206
+
207
+ mid() {
208
+ let self = this
209
+ let dataObject = this.data
210
+
211
+ if (!Array.isArray(this.deny) || this.deny.length === 0) this.deny = null
212
+
213
+ if (this.deny) {
214
+ if (this.deleteDeny) {
215
+ return async (c, next) => {
216
+
217
+ if (self.key !== 'body' || (c.body !== c.rawBody && typeof c.body === 'object')) {
218
+ let obj = c[self.key]
219
+
220
+ for (let k of self.deny) {
221
+ if (obj[k] !== undefined) delete obj[k]
222
+ }
223
+ }
224
+
225
+ let r = self.dataFilter(c)
226
+ if (!r.ok) {
227
+ return c.status(400).to(dataObject[r.key].errorMessage || self.errorMessage)
228
+ }
229
+
230
+ await next(c)
231
+ }
232
+
233
+ }
234
+
235
+ return async (c, next) => {
236
+ if (self.key !== 'body' || (c.body !== c.rawBody && typeof c.body === 'object')) {
237
+ let obj = c[self.key]
238
+
239
+ for (let k of self.deny) {
240
+ if (obj[k] !== undefined) return c.status(400).to(self.denyMessage)
241
+ }
242
+ }
243
+
244
+ let r = self.dataFilter(c)
245
+ if (!r.ok) {
246
+ return c.status(400).to(dataObject[r.key].errorMessage || self.errorMessage)
247
+ }
248
+
249
+ await next(c)
250
+ }
251
+
252
+ }
253
+
254
+ return async (c, next) => {
255
+ let r = self.dataFilter(c)
256
+ if (!r.ok) {
257
+ return c.status(400).to(dataObject[r.key].errorMessage || self.errorMessage)
258
+ }
259
+
260
+ await next(c)
261
+ }
262
+ }
263
+
264
+ }
265
+
266
+ module.exports = ParamCheck