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.
- package/LICENSE +128 -0
- package/README.cn.md +1519 -0
- package/README.md +1483 -0
- package/bin/app.js +17 -0
- package/bin/loadinfo.sh +18 -0
- package/bin/new-ctl.js +234 -0
- package/bin/newapp.js +22 -0
- package/demo/allow.js +98 -0
- package/demo/cert/localhost-cert.pem +19 -0
- package/demo/cert/localhost-privkey.pem +28 -0
- package/demo/controller/api.js +15 -0
- package/demo/extends.js +5 -0
- package/demo/group-api.js +161 -0
- package/demo/group-api2.js +109 -0
- package/demo/http2.js +34 -0
- package/demo/http2_proxy_backend.js +45 -0
- package/demo/http2proxy.js +48 -0
- package/demo/http_proxy_backend.js +44 -0
- package/demo/httpproxy.js +47 -0
- package/demo/loader.js +27 -0
- package/demo/log.js +118 -0
- package/demo/memlimit.js +31 -0
- package/demo/min.js +7 -0
- package/demo/serv.js +15 -0
- package/images/middleware.jpg +0 -0
- package/images/topbit-middleware.png +0 -0
- package/images/topbit.png +0 -0
- package/package.json +42 -11
- package/src/_loadExtends.js +21 -0
- package/src/bodyparser.js +420 -0
- package/src/connfilter.js +125 -0
- package/src/context1.js +166 -0
- package/src/context2.js +182 -0
- package/src/ctxpool.js +39 -0
- package/src/ext.js +318 -0
- package/src/extends/Http2Pool.js +365 -0
- package/src/extends/__randstring.js +24 -0
- package/src/extends/cookie.js +44 -0
- package/src/extends/cors.js +334 -0
- package/src/extends/errorlog.js +252 -0
- package/src/extends/http2limit.js +126 -0
- package/src/extends/http2proxy.js +691 -0
- package/src/extends/jwt.js +217 -0
- package/src/extends/mixlogger.js +63 -0
- package/src/extends/paramcheck.js +266 -0
- package/src/extends/proxy.js +662 -0
- package/src/extends/realip.js +34 -0
- package/src/extends/referer.js +68 -0
- package/src/extends/resource.js +398 -0
- package/src/extends/session.js +174 -0
- package/src/extends/setfinal.js +50 -0
- package/src/extends/sni.js +48 -0
- package/src/extends/sse.js +293 -0
- package/src/extends/timing.js +111 -0
- package/src/extends/tofile.js +123 -0
- package/src/fastParseUrl.js +426 -0
- package/src/headerLimit.js +18 -0
- package/src/http1.js +336 -0
- package/src/http2.js +337 -0
- package/src/httpc.js +251 -0
- package/src/lib/npargv.js +354 -0
- package/src/lib/zipdata.js +45 -0
- package/src/loader/loader.js +999 -0
- package/src/logger.js +32 -0
- package/src/loggermsg.js +349 -0
- package/src/makeId.js +200 -0
- package/src/midcore.js +213 -0
- package/src/middleware1.js +103 -0
- package/src/middleware2.js +116 -0
- package/src/monitor.js +380 -0
- package/src/movefile.js +30 -0
- package/src/optionsCheck.js +54 -0
- package/src/randstring.js +23 -0
- package/src/router.js +682 -0
- package/src/sendmsg.js +27 -0
- package/src/strong.js +72 -0
- package/src/token/token.js +461 -0
- package/src/topbit.js +1293 -0
- package/src/versionCheck.js +31 -0
- package/test/test-bigctx.js +29 -0
- package/test/test-daemon-args.js +7 -0
- package/test/test-ext.js +81 -0
- package/test/test-find.js +69 -0
- package/test/test-route-sort.js +71 -0
- package/test/test-route.js +49 -0
- package/test/test-route2.js +51 -0
- package/test/test-run-args.js +7 -0
- package/test/test-url.js +52 -0
- package/main.js +0 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs')
|
|
4
|
+
const tls = require('node:tls')
|
|
5
|
+
|
|
6
|
+
class SNI {
|
|
7
|
+
constructor(certs = {}) {
|
|
8
|
+
this.certs = {}
|
|
9
|
+
|
|
10
|
+
if (typeof certs !== 'object') {
|
|
11
|
+
throw new Error('必须传递key-value形式的配置,key是域名,value是证书路径')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let t = '';
|
|
15
|
+
for (let h in certs) {
|
|
16
|
+
t = certs[h]
|
|
17
|
+
|
|
18
|
+
if (t.key === undefined || t.cert === undefined) {
|
|
19
|
+
console.error(`${h} 没有设置key和cert`)
|
|
20
|
+
continue
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
this.certs[h] = {
|
|
25
|
+
key : fs.readFileSync(t.key),
|
|
26
|
+
cert : fs.readFileSync(t.cert)
|
|
27
|
+
}
|
|
28
|
+
} catch (err) {
|
|
29
|
+
console.error(h, err.message)
|
|
30
|
+
continue
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
callback() {
|
|
37
|
+
return (servername, cb) => {
|
|
38
|
+
return cb(null, tls.createSecureContext(this.certs[servername]))
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
init(app) {
|
|
43
|
+
app.config.server.SNICallback = this.callback()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = SNI
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function fmtMessage(msg, withEnd=true) {
|
|
4
|
+
let text = ''
|
|
5
|
+
|
|
6
|
+
if (Array.isArray(msg)) {
|
|
7
|
+
let textarr = []
|
|
8
|
+
|
|
9
|
+
for (let m of msg) {
|
|
10
|
+
text = fmtMessage(m, false)
|
|
11
|
+
text && textarr.push(text)
|
|
12
|
+
}
|
|
13
|
+
if (textarr.length === 0) return ''
|
|
14
|
+
|
|
15
|
+
return textarr.join('\n\n') + '\n\n'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (msg === null || msg === undefined || msg === '') {
|
|
19
|
+
return ''
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let typ = typeof msg
|
|
23
|
+
|
|
24
|
+
if (typ === 'number') {
|
|
25
|
+
msg = `${msg}`
|
|
26
|
+
typ = 'string'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (typ === 'object') {
|
|
30
|
+
if (!(msg.event || msg.data || msg.retry || msg.id)) return ''
|
|
31
|
+
|
|
32
|
+
if (msg.data === undefined) msg.data = ''
|
|
33
|
+
|
|
34
|
+
let datatype = typeof msg.data
|
|
35
|
+
|
|
36
|
+
switch (datatype) {
|
|
37
|
+
case 'number':
|
|
38
|
+
msg.data = msg.data.toString()
|
|
39
|
+
break
|
|
40
|
+
case 'object':
|
|
41
|
+
msg.data = JSON.stringify(msg.data).replaceAll('\n', '%0A')
|
|
42
|
+
break
|
|
43
|
+
case 'function':
|
|
44
|
+
msg.data = msg.data.toString().replaceAll('\n', '%0A')
|
|
45
|
+
break
|
|
46
|
+
case 'string':
|
|
47
|
+
msg.data = msg.data.replaceAll('\n', '%0A')
|
|
48
|
+
break
|
|
49
|
+
|
|
50
|
+
default:
|
|
51
|
+
msg.data = `${msg.data}`
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
text = `event: ${msg.event || 'message'}\ndata: ${msg.data}\n`
|
|
55
|
+
if (msg.id) {
|
|
56
|
+
text += `id: ${msg.id}\n`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (msg.retry) {
|
|
60
|
+
text += `retry: ${msg.retry}\n`
|
|
61
|
+
}
|
|
62
|
+
} else if (typ === 'string') {
|
|
63
|
+
if (msg[0] !== ':') {
|
|
64
|
+
text = `data: ${msg.replaceAll('\n', '%0A')}\n`
|
|
65
|
+
} else {
|
|
66
|
+
text = msg.replaceAll('\n', '%0A') + '\n'
|
|
67
|
+
}
|
|
68
|
+
} else if (typ === 'function') {
|
|
69
|
+
text = `event: function\ndata: ${msg.toString().replaceAll('\n', '%0A')}\n`
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (withEnd) {
|
|
73
|
+
text += `\n\n`
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return text
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
class SSE {
|
|
80
|
+
|
|
81
|
+
constructor(options = {}) {
|
|
82
|
+
this.timer = null
|
|
83
|
+
this.handle = null
|
|
84
|
+
this.timeSlice = 1000
|
|
85
|
+
|
|
86
|
+
this.retry = 0
|
|
87
|
+
this.timeout = 15000
|
|
88
|
+
|
|
89
|
+
this.fmtMsg = fmtMessage
|
|
90
|
+
|
|
91
|
+
this.handleClose = null
|
|
92
|
+
this.handleError = null
|
|
93
|
+
|
|
94
|
+
this.mode = 'timer'
|
|
95
|
+
|
|
96
|
+
for (let k in options) {
|
|
97
|
+
switch (k) {
|
|
98
|
+
case 'timeSlice':
|
|
99
|
+
case 'timeout':
|
|
100
|
+
case 'retry':
|
|
101
|
+
if (typeof options[k] === 'number' && options[k] >= 0) {
|
|
102
|
+
this[k] = options[k]
|
|
103
|
+
}
|
|
104
|
+
break
|
|
105
|
+
|
|
106
|
+
case 'handle':
|
|
107
|
+
case 'handleClose':
|
|
108
|
+
case 'handleError':
|
|
109
|
+
if (typeof options[k] === 'function') this[k] = optionsp[k]
|
|
110
|
+
break
|
|
111
|
+
|
|
112
|
+
case 'mode':
|
|
113
|
+
if (['timer', 'generator', 'yield'].indexOf(options[k]) >= 0)
|
|
114
|
+
this[k] = options[k]
|
|
115
|
+
break
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async interval(ctx) {
|
|
122
|
+
if (!this.handle || typeof this.handle !== 'function') {
|
|
123
|
+
throw new Error('请设置handle为要处理的函数,然后再次运行。')
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let self = this
|
|
127
|
+
|
|
128
|
+
if (self.timer) {
|
|
129
|
+
clearInterval(this.timer)
|
|
130
|
+
this.timer = null
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return new Promise((rv, rj) => {
|
|
134
|
+
ctx.res.on('error', err => {
|
|
135
|
+
clearInterval(self.timer)
|
|
136
|
+
self.timer = null
|
|
137
|
+
rj(err)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
ctx.res.on('close', () => {
|
|
141
|
+
clearInterval(self.timer)
|
|
142
|
+
self.timer = null
|
|
143
|
+
rv('sse closed')
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
self.timer = setInterval(async () => {
|
|
147
|
+
ctx.box.sseCount += 1
|
|
148
|
+
if (self.timeout > 0 && ctx.box.sseCount * self.timeSlice > self.timeout) {
|
|
149
|
+
if (self.retry > 0) {
|
|
150
|
+
ctx.sendmsg({data: 'timeout', retry: self.retry})
|
|
151
|
+
}
|
|
152
|
+
return ctx.res.end()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
await self.handle(ctx)
|
|
157
|
+
} catch (err) {
|
|
158
|
+
clearInterval(self.timer)
|
|
159
|
+
self.timer = null
|
|
160
|
+
rj(err)
|
|
161
|
+
}
|
|
162
|
+
}, self.timeSlice || 1000)
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async moment(t) {
|
|
168
|
+
return new Promise((rv) => {
|
|
169
|
+
setTimeout(rv, t)
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
gn(ctx) {
|
|
174
|
+
if (!this.handle || typeof this.handle !== 'function') {
|
|
175
|
+
throw new Error('请设置handle为要处理的函数,然后再次运行。')
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
let self = this
|
|
179
|
+
|
|
180
|
+
ctx.box.sseNext = true
|
|
181
|
+
|
|
182
|
+
ctx.res.on('error', err => {
|
|
183
|
+
ctx.box.sseNext = false
|
|
184
|
+
ctx.box.sseError = err
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
ctx.res.on('close', () => {
|
|
188
|
+
ctx.box.sseNext = false
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
return async function * () {
|
|
192
|
+
while (true) {
|
|
193
|
+
let tm = Date.now()
|
|
194
|
+
|
|
195
|
+
if (self.timeout > 0 && (tm - ctx.box.sseTime) > self.timeout) {
|
|
196
|
+
if (self.retry > 0) {
|
|
197
|
+
ctx.sendmsg({data: 'timeout', retry: self.retry})
|
|
198
|
+
}
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
ctx.box.sseCount += 1
|
|
202
|
+
try {
|
|
203
|
+
await self.handle(ctx)
|
|
204
|
+
} catch (err) {
|
|
205
|
+
ctx.box.sseNext = false
|
|
206
|
+
ctx.box.sseError = err
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (ctx.box.sseNext) {
|
|
210
|
+
yield tm
|
|
211
|
+
} else {
|
|
212
|
+
break
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async rungn(ctx) {
|
|
220
|
+
let yn = this.gn(ctx)
|
|
221
|
+
let r
|
|
222
|
+
let y = yn()
|
|
223
|
+
|
|
224
|
+
while (true) {
|
|
225
|
+
r = await y.next()
|
|
226
|
+
|
|
227
|
+
if (r.done) break
|
|
228
|
+
|
|
229
|
+
if (this.timeSlice > 0) await this.moment(this.timeSlice)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (ctx.box.sseError) {
|
|
233
|
+
if (this.handleError && typeof this.handleError === 'function')
|
|
234
|
+
this.handleError(ctx.box.sseError, ctx)
|
|
235
|
+
else throw ctx.box.sseError
|
|
236
|
+
} else if (this.handleClose && typeof this.handleClose === 'function') {
|
|
237
|
+
this.handleClose(ctx)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
autoRun(ctx) {
|
|
242
|
+
if (this.mode === 'timer') {
|
|
243
|
+
return this.interval(ctx)
|
|
244
|
+
.then(data => {
|
|
245
|
+
if (typeof this.handleClose === 'function') this.handleClose(ctx)
|
|
246
|
+
})
|
|
247
|
+
.catch(err => {
|
|
248
|
+
if (typeof this.handleError === 'function') {
|
|
249
|
+
this.handleError(err, ctx)
|
|
250
|
+
} else {
|
|
251
|
+
throw err
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
} else {
|
|
255
|
+
ctx.box.sseTime = Date.now()
|
|
256
|
+
return this.rungn(ctx)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
mid() {
|
|
261
|
+
let self = this
|
|
262
|
+
|
|
263
|
+
return async (ctx, next) => {
|
|
264
|
+
ctx.setHeader('content-type', 'text/event-stream;charset=utf-8').sendHeader()
|
|
265
|
+
ctx.sse = self
|
|
266
|
+
//用于统计是否超时断开并发送retry
|
|
267
|
+
ctx.box.sseCount = 0
|
|
268
|
+
if (!ctx.sendmsg || typeof ctx.sendmsg !== 'function') {
|
|
269
|
+
ctx.sendmsg = (msg, cb=undefined) => {
|
|
270
|
+
let emsg = fmtMessage(msg)
|
|
271
|
+
if (emsg) return ctx.res.write(emsg, cb)
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
ctx.res.setTimeout(self.timeout, () => {
|
|
276
|
+
if (ctx.res.writable) ctx.res.end()
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
//http2协议需要设置session超时,否则如果默认的服务超时设置比self.timeout短,会导致无法收到消息。
|
|
280
|
+
if (ctx.major == 2 && ctx.res.session && ctx.res.session.listenerCount) {
|
|
281
|
+
//http2的session会保持连接,如果stream超时关闭后,session可能会维持连接,此时有可能会复用session。
|
|
282
|
+
if (ctx.res.session.listenerCount('timeout') < 2) {
|
|
283
|
+
ctx.res.session.setTimeout(self.timeout, () => {})
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
await self.autoRun(ctx)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
module.exports = SSE
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs')
|
|
4
|
+
|
|
5
|
+
class Timing {
|
|
6
|
+
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* route保存所有路由的最近耗时记录。
|
|
11
|
+
* maxLimit是保存记录上限。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
this.route = {
|
|
15
|
+
GET : new Map(),
|
|
16
|
+
POST : new Map(),
|
|
17
|
+
PUT : new Map(),
|
|
18
|
+
DELETE : new Map(),
|
|
19
|
+
OPTIONS : new Map(),
|
|
20
|
+
PATCH : new Map()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
this.maxLimit = 100
|
|
24
|
+
|
|
25
|
+
this.test = false
|
|
26
|
+
|
|
27
|
+
this.logfile = ''
|
|
28
|
+
|
|
29
|
+
if (typeof options !== 'object') {
|
|
30
|
+
options = {}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (let k in options) {
|
|
34
|
+
switch (k) {
|
|
35
|
+
case 'test':
|
|
36
|
+
this.test = options[k]
|
|
37
|
+
break
|
|
38
|
+
|
|
39
|
+
case 'logfile':
|
|
40
|
+
this.logfile = options[k]
|
|
41
|
+
break
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
mid() {
|
|
48
|
+
let self = this
|
|
49
|
+
|
|
50
|
+
return async (c, next) => {
|
|
51
|
+
|
|
52
|
+
let start_time = Date.now()
|
|
53
|
+
|
|
54
|
+
await next()
|
|
55
|
+
|
|
56
|
+
let end_time = Date.now()
|
|
57
|
+
|
|
58
|
+
let time_consume = end_time - start_time
|
|
59
|
+
|
|
60
|
+
if (self.route[c.method] !== undefined) {
|
|
61
|
+
if (!self.route[c.method].has(c.routepath)) {
|
|
62
|
+
self.route[c.method].set(c.routepath, {
|
|
63
|
+
total : 1,
|
|
64
|
+
consume: time_consume
|
|
65
|
+
})
|
|
66
|
+
} else {
|
|
67
|
+
let t = self.route[c.method].get(c.routepath)
|
|
68
|
+
if (t.total > self.maxLimit) {
|
|
69
|
+
t.total = 0
|
|
70
|
+
t.consume = 0
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
t.total += 1
|
|
74
|
+
t.consume += time_consume
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let last = self.route[c.method].get(c.routepath)
|
|
81
|
+
|
|
82
|
+
if (self.test) {
|
|
83
|
+
|
|
84
|
+
console.log(c.method, c.path, time_consume, 'ms')
|
|
85
|
+
|
|
86
|
+
console.log(c.method, c.path,'\n',
|
|
87
|
+
` Count: ${last.total} Total: ${last.consume} ms `,
|
|
88
|
+
`Average: ${(last.consume/last.total).toFixed(2)} ms`
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (self.logfile) {
|
|
93
|
+
|
|
94
|
+
let log_text = `${c.method} - ${c.path} - count: ${last.total}, `
|
|
95
|
+
+ `total: ${last.consume}ms, `
|
|
96
|
+
+ `average: ${(last.consume/last.total).toFixed(2)}ms\n`
|
|
97
|
+
|
|
98
|
+
fs.writeFile(self.logfile, log_text, {flag: 'a+'}, err => {
|
|
99
|
+
if (self.test && err) {
|
|
100
|
+
console.error(err)
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = Timing
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs')
|
|
4
|
+
const crypto = require('node:crypto')
|
|
5
|
+
|
|
6
|
+
function extName(fname) {
|
|
7
|
+
let ind = fname.length - 2
|
|
8
|
+
|
|
9
|
+
while (ind > 0 && fname[ind] !== '.') {
|
|
10
|
+
ind -= 1
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (ind <= 0) return ''
|
|
14
|
+
|
|
15
|
+
return fname.substring(ind)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let fmtbits = (n) => {
|
|
19
|
+
return n < 10 ? `0${n}` : n
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function makeName(filename = '') {
|
|
23
|
+
let tm = new Date()
|
|
24
|
+
|
|
25
|
+
let orgname = `${tm.getFullYear()}-${fmtbits(tm.getMonth()+1)}-${fmtbits(tm.getDate())}_`
|
|
26
|
+
+ `${fmtbits(tm.getHours())}-${fmtbits(tm.getMinutes())}-${fmtbits(tm.getSeconds())}`
|
|
27
|
+
+ `_${tm.getMilliseconds()}${parseInt(Math.random() * 1000) + 1}${parseInt(Math.random() * 100000) + 10000}`
|
|
28
|
+
|
|
29
|
+
if (filename) return (orgname + extName(filename))
|
|
30
|
+
|
|
31
|
+
return orgname
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function moveFile(target, filename = null) {
|
|
35
|
+
if (!this || !this.rawBody) return false
|
|
36
|
+
|
|
37
|
+
if (!filename) filename = makeName(this.filename || '')
|
|
38
|
+
|
|
39
|
+
let ds = ''
|
|
40
|
+
if (target[target.length-1] !== '/') ds = '/'
|
|
41
|
+
|
|
42
|
+
let pathfile = `${target}${ds}${filename}`
|
|
43
|
+
|
|
44
|
+
let fd = await new Promise((rv, rj) => {
|
|
45
|
+
fs.open(pathfile, 'w+', 0o644, (err, fd) => {
|
|
46
|
+
if (err) {
|
|
47
|
+
rj(err)
|
|
48
|
+
} else {
|
|
49
|
+
rv(fd)
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
return new Promise((rv, rj) => {
|
|
55
|
+
fs.write(fd, this.rawBody, this.start, this.length,
|
|
56
|
+
(err, bytesWritten, buffer) => {
|
|
57
|
+
if (err) {
|
|
58
|
+
rj(err)
|
|
59
|
+
} else {
|
|
60
|
+
rv(filename)
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
.finally(() => {
|
|
65
|
+
fs.close(fd, (err) => {})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getFile(name, ind=0) {
|
|
71
|
+
if (!this || !this.files) return null
|
|
72
|
+
|
|
73
|
+
if (this.files[name] === undefined) {
|
|
74
|
+
return ind < 0 ? [] : null
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (ind >= this.files[name].length) {
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let flist = this.files[name]
|
|
82
|
+
|
|
83
|
+
if (ind < 0) {
|
|
84
|
+
for (let i = 0; i < flist.length; i++) {
|
|
85
|
+
if (flist[i].toFile === undefined) {
|
|
86
|
+
flist[i].rawBody = this.rawBody
|
|
87
|
+
flist[i].toFile = moveFile
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return flist
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (flist[ind].toFile === undefined) {
|
|
94
|
+
flist[ind].rawBody = this.rawBody
|
|
95
|
+
flist[ind].toFile = moveFile
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return flist[ind]
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ToFile {
|
|
103
|
+
|
|
104
|
+
constructor() {
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
mid() {
|
|
109
|
+
let self = this
|
|
110
|
+
return async (c, next) => {
|
|
111
|
+
if (!c.isUpload) {
|
|
112
|
+
return await next()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
c.getFile = getFile
|
|
116
|
+
await next()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = ToFile
|