@maiyunnet/kebab 2.0.3 → 2.0.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/index.d.ts +14 -0
- package/index.js +1 -1
- package/lib/buffer.d.ts +25 -0
- package/lib/captcha.d.ts +12 -0
- package/lib/consistent.d.ts +20 -0
- package/lib/core.d.ts +85 -0
- package/lib/crypto.d.ts +47 -0
- package/lib/db.d.ts +88 -0
- package/lib/dns.d.ts +75 -0
- package/lib/fs.d.ts +49 -0
- package/lib/jwt.d.ts +30 -0
- package/lib/kv.d.ts +111 -0
- package/lib/lan.d.ts +10 -0
- package/lib/net/formdata.d.ts +23 -0
- package/lib/net/request.d.ts +23 -0
- package/lib/net/response.d.ts +14 -0
- package/lib/net.d.ts +65 -0
- package/lib/s3.d.ts +35 -0
- package/lib/scan.d.ts +31 -0
- package/lib/session.d.ts +22 -0
- package/lib/sql.d.ts +57 -0
- package/lib/ssh/sftp.d.ts +40 -0
- package/lib/ssh/shell.d.ts +13 -0
- package/lib/ssh.d.ts +24 -0
- package/lib/text.d.ts +40 -0
- package/lib/time.d.ts +22 -0
- package/lib/ws.d.ts +87 -0
- package/lib/zip.d.ts +40 -0
- package/lib/zlib.d.ts +25 -0
- package/main.d.ts +1 -0
- package/package.json +1 -1
- package/sys/child.d.ts +1 -0
- package/sys/cmd.d.ts +1 -0
- package/sys/ctr.d.ts +96 -0
- package/sys/master.d.ts +1 -0
- package/sys/mod.d.ts +205 -0
- package/sys/route.d.ts +31 -0
- package/tsconfig.json +1 -1
- package/www/example/ctr/main.d.ts +4 -0
- package/www/example/ctr/middle.d.ts +6 -0
- package/www/example/ctr/test.d.ts +94 -0
- package/www/example/mod/test.d.ts +20 -0
- package/www/example/mod/testdata.d.ts +9 -0
- package/www/example/ws/mproxy.d.ts +4 -0
- package/www/example/ws/rproxy.d.ts +4 -0
- package/www/example/ws/test.d.ts +7 -0
- package/index.ts +0 -33
- package/lib/buffer.ts +0 -152
- package/lib/captcha.ts +0 -63
- package/lib/consistent.ts +0 -219
- package/lib/core.ts +0 -880
- package/lib/crypto.ts +0 -384
- package/lib/db.ts +0 -719
- package/lib/dns.ts +0 -405
- package/lib/fs.ts +0 -527
- package/lib/jwt.ts +0 -276
- package/lib/kv.ts +0 -1489
- package/lib/lan.ts +0 -87
- package/lib/net/formdata.ts +0 -166
- package/lib/net/request.ts +0 -150
- package/lib/net/response.ts +0 -59
- package/lib/net.ts +0 -662
- package/lib/s3.ts +0 -235
- package/lib/scan.ts +0 -364
- package/lib/session.ts +0 -230
- package/lib/sql.ts +0 -1149
- package/lib/ssh/sftp.ts +0 -508
- package/lib/ssh/shell.ts +0 -123
- package/lib/ssh.ts +0 -191
- package/lib/text.ts +0 -626
- package/lib/time.ts +0 -254
- package/lib/ws.ts +0 -523
- package/lib/zip.ts +0 -447
- package/lib/zlib.ts +0 -350
- package/main.ts +0 -27
- package/sys/child.ts +0 -678
- package/sys/cmd.ts +0 -225
- package/sys/ctr.ts +0 -904
- package/sys/master.ts +0 -355
- package/sys/mod.ts +0 -1871
- package/sys/route.ts +0 -1113
- package/www/example/ctr/main.ts +0 -9
- package/www/example/ctr/middle.ts +0 -26
- package/www/example/ctr/test.ts +0 -3218
- package/www/example/mod/test.ts +0 -47
- package/www/example/mod/testdata.ts +0 -30
- package/www/example/ws/mproxy.ts +0 -16
- package/www/example/ws/rproxy.ts +0 -14
- package/www/example/ws/test.ts +0 -36
package/sys/ctr.ts
DELETED
|
@@ -1,904 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Project: Kebab, User: JianSuoQiYue
|
|
3
|
-
* Date: 2020-3-14 17:24:38
|
|
4
|
-
* Last: 2020-3-30 15:31:40, 2022-07-22 16:59:00, 2022-09-12 23:51:56, 2022-09-23 15:53:58, 2022-12-29 01:18:08, 2023-2-28 20:07:57, 2023-12-27 18:39:35, 2024-3-1 19:38:53, 2024-4-9 16:03:58, 2025-2-12 18:55:44, 2025-6-12 16:56:08
|
|
5
|
-
*/
|
|
6
|
-
import * as http from 'http';
|
|
7
|
-
import * as http2 from 'http2';
|
|
8
|
-
import * as ejs from 'ejs';
|
|
9
|
-
import * as lCore from '../lib/core';
|
|
10
|
-
import * as fs from '../lib/fs';
|
|
11
|
-
import * as crypto from '../lib/crypto';
|
|
12
|
-
import * as session from '../lib/session';
|
|
13
|
-
import * as db from '../lib/db';
|
|
14
|
-
import * as kv from '../lib/kv';
|
|
15
|
-
import * as lWs from '../lib/ws';
|
|
16
|
-
import * as text from '../lib/text';
|
|
17
|
-
import * as sRoute from '../sys/route';
|
|
18
|
-
import * as types from '../types';
|
|
19
|
-
|
|
20
|
-
/** --- 已加载的 DATA 数据缓存(不是语言包)-- */
|
|
21
|
-
let loadedData: Record<
|
|
22
|
-
string, // 文件名
|
|
23
|
-
Record<string, types.Json>
|
|
24
|
-
> = {};
|
|
25
|
-
|
|
26
|
-
/** --- 已加载的语言文件列表 --- */
|
|
27
|
-
let localeFiles: string[] = [];
|
|
28
|
-
|
|
29
|
-
/** --- 已经加载的语言包(全局) --- */
|
|
30
|
-
let localeData: Record<
|
|
31
|
-
string, // 文件名
|
|
32
|
-
Record<string, string>
|
|
33
|
-
> = {};
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* --- 清除已经加载的 data 与语言包文件缓存 ---
|
|
37
|
-
*/
|
|
38
|
-
export function clearLocaleData(): void {
|
|
39
|
-
loadedData = {};
|
|
40
|
-
localeFiles = [];
|
|
41
|
-
localeData = {};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export class Ctr {
|
|
45
|
-
|
|
46
|
-
/** --- 路由参数序列数组 --- */
|
|
47
|
-
protected _param: string[] = [];
|
|
48
|
-
|
|
49
|
-
/** --- 当前的 action 名 --- */
|
|
50
|
-
protected _action = '';
|
|
51
|
-
|
|
52
|
-
/** --- 请求的 header 列表,key 均为小写 --- */
|
|
53
|
-
protected _headers: http.IncomingHttpHeaders = {};
|
|
54
|
-
|
|
55
|
-
/** --- GET 数据 --- */
|
|
56
|
-
protected _get!: Record<string, string>;
|
|
57
|
-
|
|
58
|
-
/** --- 原始 POST 数据 --- */
|
|
59
|
-
protected _rawPost: Record<string, types.Json> = {};
|
|
60
|
-
|
|
61
|
-
/** --- POST 数据 --- */
|
|
62
|
-
protected _post: Record<string, types.Json> = {};
|
|
63
|
-
|
|
64
|
-
/** --- 原始 input 字符串 */
|
|
65
|
-
protected _input: string = '';
|
|
66
|
-
|
|
67
|
-
/** --- 上传的文件列表 --- */
|
|
68
|
-
protected _files: Record<string, types.IPostFile | types.IPostFile[]> = {};
|
|
69
|
-
|
|
70
|
-
/** --- Cookie 数组 --- */
|
|
71
|
-
protected _cookie: Record<string, string> = {};
|
|
72
|
-
|
|
73
|
-
/** --- Jwt 数组 --- */
|
|
74
|
-
protected _jwt: Record<string, types.Json> = {};
|
|
75
|
-
|
|
76
|
-
/** --- Session 数组 --- */
|
|
77
|
-
protected _session: Record<string, types.Json> = {};
|
|
78
|
-
|
|
79
|
-
/** --- Session --- 对象 */
|
|
80
|
-
protected _sess: session.Session | null = null;
|
|
81
|
-
|
|
82
|
-
/** --- 页面浏览器客户端缓存 --- */
|
|
83
|
-
protected _cacheTTL!: number;
|
|
84
|
-
|
|
85
|
-
/** --- XSRF TOKEN 值 --- */
|
|
86
|
-
protected _xsrf: string = '';
|
|
87
|
-
|
|
88
|
-
/** --- 自定义 http code --- */
|
|
89
|
-
protected _httpCode: number = 0;
|
|
90
|
-
|
|
91
|
-
// --- Kebab: true,Mutton: false,全局常量等对象 ---
|
|
92
|
-
|
|
93
|
-
/** --- 当前语言名 --- */
|
|
94
|
-
protected _locale: string = 'en';
|
|
95
|
-
|
|
96
|
-
/** --- vhost 的 kebab.json 以及全局常量 --- */
|
|
97
|
-
protected readonly _config!: types.IConfig;
|
|
98
|
-
|
|
99
|
-
protected readonly _req!: http2.Http2ServerRequest | http.IncomingMessage;
|
|
100
|
-
|
|
101
|
-
protected readonly _res!: http2.Http2ServerResponse | http.ServerResponse;
|
|
102
|
-
|
|
103
|
-
protected readonly _socket!: lWs.Socket;
|
|
104
|
-
|
|
105
|
-
/** --- 本 ctr 已加载的语言文件列表 --- */
|
|
106
|
-
protected _localeFiles: string[] = [];
|
|
107
|
-
|
|
108
|
-
/** --- 本 ctr 的 locale data --- */
|
|
109
|
-
protected _localeData: Record<string, Record<string, string>> = {};
|
|
110
|
-
|
|
111
|
-
public constructor(
|
|
112
|
-
config: types.IConfig,
|
|
113
|
-
req: http2.Http2ServerRequest | http.IncomingMessage,
|
|
114
|
-
res?: http2.Http2ServerResponse | http.ServerResponse
|
|
115
|
-
) {
|
|
116
|
-
this._config = config;
|
|
117
|
-
this._req = req;
|
|
118
|
-
if (res) {
|
|
119
|
-
this._res = res;
|
|
120
|
-
}
|
|
121
|
-
this._cacheTTL = config.set.cacheTtl;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/** --- 当前用户连接是否还在连接中 --- */
|
|
125
|
-
public get isAvail(): boolean {
|
|
126
|
-
return this._req.socket.writable;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/** --- timeout 的 timer --- */
|
|
130
|
-
protected _timer?: {
|
|
131
|
-
'timer': NodeJS.Timeout;
|
|
132
|
-
'timeout': number;
|
|
133
|
-
'callback': () => void;
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
/** --- 获取当前过期时间 --- */
|
|
137
|
-
public get timeout(): number {
|
|
138
|
-
return this._timer?.timeout ?? 30_000;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* --- 设置当前过期时间 ---
|
|
143
|
-
*/
|
|
144
|
-
public set timeout(num: number) {
|
|
145
|
-
if (!this._timer) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
this._timer.timeout = num;
|
|
149
|
-
clearTimeout(this._timer.timer);
|
|
150
|
-
this._timer.timer = setTimeout(this._timer.callback, num);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/** --- 一些需要等待的事项的记录(异步任务、事务) --- */
|
|
154
|
-
private readonly _waitInfo = {
|
|
155
|
-
'asyncTask': {
|
|
156
|
-
'count': 0,
|
|
157
|
-
'resolve': () => {
|
|
158
|
-
// --- NOTHING ---
|
|
159
|
-
},
|
|
160
|
-
'callback': function() {
|
|
161
|
-
return new Promise<void>((resolve) => {
|
|
162
|
-
this.resolve = resolve;
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
},
|
|
166
|
-
'transaction': 0
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* --- 执行一段跳出堆栈的异步代码,代码执行完成前,热更新不会杀死当面进程 且 ftmp 临时文件不会被清除 ---
|
|
171
|
-
* @param func 异步代码
|
|
172
|
-
*/
|
|
173
|
-
protected _asyncTask(func: () => void | Promise<void>): void {
|
|
174
|
-
++this._waitInfo.asyncTask.count;
|
|
175
|
-
setTimeout((): void => {
|
|
176
|
-
(async () => {
|
|
177
|
-
await func();
|
|
178
|
-
--this._waitInfo.asyncTask.count;
|
|
179
|
-
if (!this._waitInfo.asyncTask.count) {
|
|
180
|
-
this._waitInfo.asyncTask.resolve();
|
|
181
|
-
}
|
|
182
|
-
})().catch(async (e) => {
|
|
183
|
-
lCore.display('[ERROR][CTR][ASYNCTASK]', e);
|
|
184
|
-
await lCore.log(this, '(ctr.asyncTask)' + text.stringifyJson(e.stack).slice(1, -1), '-error');
|
|
185
|
-
--this._waitInfo.asyncTask.count;
|
|
186
|
-
if (!this._waitInfo.asyncTask.count) {
|
|
187
|
-
this._waitInfo.asyncTask.resolve();
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
}, 0);
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// --- Kebab 结束 ---
|
|
195
|
-
|
|
196
|
-
/** --- 获取类内部的 prototype --- */
|
|
197
|
-
public getPrototype(name: '_config'): types.IConfig;
|
|
198
|
-
public getPrototype(name: '_sess'): session.Session | null;
|
|
199
|
-
public getPrototype(name: '_headers'): http.IncomingHttpHeaders;
|
|
200
|
-
public getPrototype(name: '_req'): http2.Http2ServerRequest | http.IncomingMessage;
|
|
201
|
-
public getPrototype(name: '_res'): http2.Http2ServerResponse | http.ServerResponse;
|
|
202
|
-
public getPrototype(name: '_socket'): lWs.Socket;
|
|
203
|
-
public getPrototype(name: '_rawPost' | '_post' | '_get' | '_session'): Record<string, types.Json>;
|
|
204
|
-
public getPrototype(name: '_input'): string;
|
|
205
|
-
public getPrototype(name: string): types.Json;
|
|
206
|
-
public getPrototype(name: string): types.Json {
|
|
207
|
-
return (this as types.Json)[name];
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/** --- 设置类内部的 prototype --- */
|
|
211
|
-
public setPrototype(
|
|
212
|
-
name: string,
|
|
213
|
-
val: string | string[] |
|
|
214
|
-
http.IncomingHttpHeaders | Record<string, types.Json> | session.Session | lWs.Socket | null
|
|
215
|
-
): void {
|
|
216
|
-
(this as types.Json)[name] = val;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* --- 实例化后会执行的方法,可重写此方法 ---
|
|
221
|
-
*/
|
|
222
|
-
public onLoad(): boolean | string | types.DbValue[] |
|
|
223
|
-
Promise<boolean | string | types.DbValue[]> {
|
|
224
|
-
return true;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* --- 整个结束前会执行本方法,可重写此方法对输出结果再处理一次(Websocket 模式无效) ---
|
|
229
|
-
* @param rtn 之前用户的输出结果
|
|
230
|
-
*/
|
|
231
|
-
public onUnload(rtn: boolean | string | types.DbValue[]): boolean | string | types.DbValue[] |
|
|
232
|
-
Promise<boolean | string | types.DbValue[]> {
|
|
233
|
-
return rtn;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* --- WebSocket 下在建立 Server 连接之前可对 WebSocket 的信息进行配置 ---
|
|
238
|
-
*/
|
|
239
|
-
public onUpgrade(): {
|
|
240
|
-
'headers'?: http.OutgoingHttpHeaders;
|
|
241
|
-
'timeout'?: number;
|
|
242
|
-
} {
|
|
243
|
-
return {};
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* --- WebSocket 下当收到数据时会自动被调用的事件,即只文本和二进制数据,返回内容会被发送给 socket,但返回 false 连接会被中断 ---
|
|
248
|
-
*/
|
|
249
|
-
public onData(data: Buffer | string, opcode: lWs.EOpcode): types.Json;
|
|
250
|
-
public onData(): string {
|
|
251
|
-
return '';
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* --- 包含所有 opcode 的消息,若要发送数据需自行调用 write 方法,返回 false 则不会执行默认方法 ---
|
|
256
|
-
* @param data 数据
|
|
257
|
-
* @param opcode opcode
|
|
258
|
-
*/
|
|
259
|
-
public onMessage(data: Buffer | string, opcode: lWs.EOpcode): undefined | boolean | Promise<undefined | boolean>;
|
|
260
|
-
public onMessage(): undefined {
|
|
261
|
-
return;
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* --- WebSocket 下连接恢复可写入状态后会调用此事件,可重写此方法 ---
|
|
267
|
-
*/
|
|
268
|
-
public onDrain(): void | Promise<void> {
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* --- WebSocket 下连接被终止后会自动被调用的事件,可重写此方法 ---
|
|
274
|
-
*/
|
|
275
|
-
public onClose(): void | Promise<void> {
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* --- 获取截止当前时间的总运行时间 ---
|
|
281
|
-
* @param ms 为 true 为毫秒,否则为秒
|
|
282
|
-
*/
|
|
283
|
-
protected _getRunTime(ms: boolean = false): number {
|
|
284
|
-
const t = process.hrtime.bigint() - this._config.const.startTime;
|
|
285
|
-
return ms ? Number(t) / 1000000 : Number(t) / 1000000000;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* --- 获取截止当前内存的使用情况 ---
|
|
290
|
-
*/
|
|
291
|
-
protected _getMemoryUsage(): number {
|
|
292
|
-
return process.memoryUsage().rss - this._config.const.startMemory;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* --- 加载视图 ---
|
|
297
|
-
* @param path
|
|
298
|
-
* @param data
|
|
299
|
-
*/
|
|
300
|
-
protected async _loadView(path: string, data: types.Json = {}): Promise<string> {
|
|
301
|
-
const content = await fs.getContent(this._config.const.viewPath + path + '.ejs', 'utf8');
|
|
302
|
-
if (!content) {
|
|
303
|
-
return '';
|
|
304
|
-
}
|
|
305
|
-
data._urlBase = this._config.const.urlBase;
|
|
306
|
-
data._urlFull = this._config.const.urlFull;
|
|
307
|
-
data._urlStcFill = this._config.const.urlStcFull;
|
|
308
|
-
data._staticVer = this._config.set.staticVer;
|
|
309
|
-
data._staticPath = this._config.set.staticPath;
|
|
310
|
-
data._staticPathFull = this._config.set.staticPathFull;
|
|
311
|
-
// --- 语言包 ---
|
|
312
|
-
data.l = (key: string, data?: string[]): string => {
|
|
313
|
-
return this._l(key, data);
|
|
314
|
-
};
|
|
315
|
-
return lCore.purify(ejs.render(content, data));
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* --- 检测提交的数据类型 ---
|
|
320
|
-
* @param input 要校验的输入项
|
|
321
|
-
* @param rule 规则
|
|
322
|
-
* @param rtn 返回值
|
|
323
|
-
*/
|
|
324
|
-
protected _checkInput(
|
|
325
|
-
input: Record<string, types.Json>,
|
|
326
|
-
rule: Record<string, types.Json[]>, rtn: types.Json[]
|
|
327
|
-
): boolean {
|
|
328
|
-
// --- 遍历规则 ---
|
|
329
|
-
// --- input, {'xx': ['require', '> 6', [0, 'xx 必须大于 6']], 'yy': [], '_xsrf': []], rtn ---
|
|
330
|
-
for (const key in rule) {
|
|
331
|
-
const val = rule[key];
|
|
332
|
-
// --- key 就是上面的 xx ---
|
|
333
|
-
if (input[key] === undefined) {
|
|
334
|
-
// --- 原值不存在则设定为 null ---
|
|
335
|
-
input[key] = null;
|
|
336
|
-
}
|
|
337
|
-
// --- 判断是否需要遍历 val ---
|
|
338
|
-
const c = val.length;
|
|
339
|
-
if (c === 0) {
|
|
340
|
-
continue;
|
|
341
|
-
}
|
|
342
|
-
// --- ['require', '> 6', [0, 'xx 必须大于 6']] ---
|
|
343
|
-
const lastK = c - 1;
|
|
344
|
-
if ((val[lastK][0] === undefined) || (val[lastK][1] === undefined) || !Number.isInteger(val[lastK][0]) || (typeof val[lastK][1] !== 'string')) {
|
|
345
|
-
rtn[0] = 0;
|
|
346
|
-
rtn[1] = 'Param error(' + key + ')';
|
|
347
|
-
return false;
|
|
348
|
-
}
|
|
349
|
-
for (let k = 0; k < lastK; ++k) {
|
|
350
|
-
const v = val[k] ?? '';
|
|
351
|
-
if (Array.isArray(v)) {
|
|
352
|
-
if (v.length === 0) {
|
|
353
|
-
rtn[0] = val[lastK][0];
|
|
354
|
-
rtn[1] = val[lastK][1];
|
|
355
|
-
if (val[lastK][2]) {
|
|
356
|
-
rtn[2] = val[lastK][2];
|
|
357
|
-
}
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
// --- 判断提交的数据是否在此 array 之内,若没有提交数据,则自动设置为第一个项 ---
|
|
361
|
-
if (input[key] === null) {
|
|
362
|
-
input[key] = v[0];
|
|
363
|
-
}
|
|
364
|
-
else if (!v.includes(input[key])) {
|
|
365
|
-
// --- 不在 ---
|
|
366
|
-
rtn[0] = val[lastK][0];
|
|
367
|
-
rtn[1] = val[lastK][1];
|
|
368
|
-
if (val[lastK][2]) {
|
|
369
|
-
rtn[2] = val[lastK][2];
|
|
370
|
-
}
|
|
371
|
-
return false;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
else if (v instanceof RegExp) {
|
|
375
|
-
// --- 正则 ---
|
|
376
|
-
if (input[key] !== null && !v.test(input[key])) {
|
|
377
|
-
rtn[0] = val[lastK][0];
|
|
378
|
-
rtn[1] = val[lastK][1];
|
|
379
|
-
if (val[lastK][2]) {
|
|
380
|
-
rtn[2] = val[lastK][2];
|
|
381
|
-
}
|
|
382
|
-
return false;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
else if (typeof v === 'object' && v.type !== undefined) {
|
|
386
|
-
// --- core.checkType ---
|
|
387
|
-
if (input[key] !== null) {
|
|
388
|
-
const r = lCore.checkType(input[key], v.type);
|
|
389
|
-
if (r) {
|
|
390
|
-
rtn[0] = val[lastK][0];
|
|
391
|
-
rtn[1] = typeof val[lastK][1] === 'string' ? val[lastK][1] + '(' + r + ')' : val[lastK][1];
|
|
392
|
-
if (val[lastK][2]) {
|
|
393
|
-
rtn[2] = val[lastK][2];
|
|
394
|
-
}
|
|
395
|
-
return false;
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
else {
|
|
400
|
-
switch (v) {
|
|
401
|
-
case 'require': {
|
|
402
|
-
if ((input[key] === null) || (input[key] === '')) {
|
|
403
|
-
rtn[0] = val[lastK][0];
|
|
404
|
-
rtn[1] = val[lastK][1];
|
|
405
|
-
if (val[lastK][2]) {
|
|
406
|
-
rtn[2] = val[lastK][2];
|
|
407
|
-
}
|
|
408
|
-
return false;
|
|
409
|
-
}
|
|
410
|
-
break;
|
|
411
|
-
}
|
|
412
|
-
case 'int':
|
|
413
|
-
case 'integer': {
|
|
414
|
-
if (input[key] && (typeof input[key] !== 'number') && !Number.isSafeInteger(input[key])) {
|
|
415
|
-
rtn[0] = val[lastK][0];
|
|
416
|
-
rtn[1] = val[lastK][1];
|
|
417
|
-
if (val[lastK][2]) {
|
|
418
|
-
rtn[2] = val[lastK][2];
|
|
419
|
-
}
|
|
420
|
-
return false;
|
|
421
|
-
}
|
|
422
|
-
break;
|
|
423
|
-
}
|
|
424
|
-
case 'float':
|
|
425
|
-
case 'double': {
|
|
426
|
-
if (input[key] && (typeof input[key] !== 'number')) {
|
|
427
|
-
rtn[0] = val[lastK][0];
|
|
428
|
-
rtn[1] = val[lastK][1];
|
|
429
|
-
if (val[lastK][2]) {
|
|
430
|
-
rtn[2] = val[lastK][2];
|
|
431
|
-
}
|
|
432
|
-
return false;
|
|
433
|
-
}
|
|
434
|
-
break;
|
|
435
|
-
}
|
|
436
|
-
case 'num':
|
|
437
|
-
case 'number': {
|
|
438
|
-
if (input[key] && (typeof input[key] !== 'number') && !/^[0-9]+\.?[0-9]*$/.test(input[key])) {
|
|
439
|
-
rtn[0] = val[lastK][0];
|
|
440
|
-
rtn[1] = val[lastK][1];
|
|
441
|
-
if (val[lastK][2]) {
|
|
442
|
-
rtn[2] = val[lastK][2];
|
|
443
|
-
}
|
|
444
|
-
return false;
|
|
445
|
-
}
|
|
446
|
-
break;
|
|
447
|
-
}
|
|
448
|
-
case 'array': {
|
|
449
|
-
if (input[key] !== null && !Array.isArray(input[key])) {
|
|
450
|
-
rtn[0] = val[lastK][0];
|
|
451
|
-
rtn[1] = val[lastK][1];
|
|
452
|
-
if (val[lastK][2]) {
|
|
453
|
-
rtn[2] = val[lastK][2];
|
|
454
|
-
}
|
|
455
|
-
return false;
|
|
456
|
-
}
|
|
457
|
-
break;
|
|
458
|
-
}
|
|
459
|
-
case 'bool':
|
|
460
|
-
case 'boolean': {
|
|
461
|
-
if (input[key] !== null && (typeof input[key] !== 'boolean')) {
|
|
462
|
-
// --- 如果不是 bool 直接失败,字符串的 true, false 也会失败 ---
|
|
463
|
-
rtn[0] = val[lastK][0];
|
|
464
|
-
rtn[1] = val[lastK][1];
|
|
465
|
-
if (val[lastK][2]) {
|
|
466
|
-
rtn[2] = val[lastK][2];
|
|
467
|
-
}
|
|
468
|
-
return false;
|
|
469
|
-
}
|
|
470
|
-
break;
|
|
471
|
-
}
|
|
472
|
-
case 'string': {
|
|
473
|
-
if (input[key] !== null && (typeof input[key] !== 'string')) {
|
|
474
|
-
// --- 如果不是 string 直接失败 ---
|
|
475
|
-
rtn[0] = val[lastK][0];
|
|
476
|
-
rtn[1] = val[lastK][1];
|
|
477
|
-
if (val[lastK][2]) {
|
|
478
|
-
rtn[2] = val[lastK][2];
|
|
479
|
-
}
|
|
480
|
-
return false;
|
|
481
|
-
}
|
|
482
|
-
break;
|
|
483
|
-
}
|
|
484
|
-
default: {
|
|
485
|
-
let match: RegExpExecArray | null;
|
|
486
|
-
if (input[key] !== null) {
|
|
487
|
-
if (v[0] === '/') {
|
|
488
|
-
// --- 正则 ---
|
|
489
|
-
if (!(new RegExp(v.slice(1, -1))).test(input[key])) {
|
|
490
|
-
rtn[0] = val[lastK][0];
|
|
491
|
-
rtn[1] = val[lastK][1];
|
|
492
|
-
if (val[lastK][2]) {
|
|
493
|
-
rtn[2] = val[lastK][2];
|
|
494
|
-
}
|
|
495
|
-
return false;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
else if ((match = /^([><=]+) *([0-9]+)$/.exec(v))) {
|
|
499
|
-
// --- 判断表达式 ---
|
|
500
|
-
let needReturn = false;
|
|
501
|
-
const inputNum = Number(input[key]);
|
|
502
|
-
const num = Number(match[2]);
|
|
503
|
-
switch (match[1]) {
|
|
504
|
-
case '>': {
|
|
505
|
-
if (inputNum <= num) {
|
|
506
|
-
needReturn = true;
|
|
507
|
-
}
|
|
508
|
-
break;
|
|
509
|
-
}
|
|
510
|
-
case '<': {
|
|
511
|
-
if (inputNum >= num) {
|
|
512
|
-
needReturn = true;
|
|
513
|
-
}
|
|
514
|
-
break;
|
|
515
|
-
}
|
|
516
|
-
case '>=': {
|
|
517
|
-
if (inputNum < num) {
|
|
518
|
-
needReturn = true;
|
|
519
|
-
}
|
|
520
|
-
break;
|
|
521
|
-
}
|
|
522
|
-
case '<=': {
|
|
523
|
-
if (inputNum > num) {
|
|
524
|
-
needReturn = true;
|
|
525
|
-
}
|
|
526
|
-
break;
|
|
527
|
-
}
|
|
528
|
-
case '=':
|
|
529
|
-
case '==':
|
|
530
|
-
case '===': {
|
|
531
|
-
if (inputNum !== num) {
|
|
532
|
-
needReturn = true;
|
|
533
|
-
}
|
|
534
|
-
break;
|
|
535
|
-
}
|
|
536
|
-
case '!=':
|
|
537
|
-
case '<>': {
|
|
538
|
-
if (inputNum === num) {
|
|
539
|
-
needReturn = true;
|
|
540
|
-
}
|
|
541
|
-
break;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
if (needReturn) {
|
|
545
|
-
rtn[0] = val[lastK][0];
|
|
546
|
-
rtn[1] = val[lastK][1];
|
|
547
|
-
if (val[lastK][2]) {
|
|
548
|
-
rtn[2] = val[lastK][2];
|
|
549
|
-
}
|
|
550
|
-
return false;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
else {
|
|
554
|
-
if (input[key] !== v) {
|
|
555
|
-
rtn[0] = val[lastK][0];
|
|
556
|
-
rtn[1] = val[lastK][1];
|
|
557
|
-
if (val[lastK][2]) {
|
|
558
|
-
rtn[2] = val[lastK][2];
|
|
559
|
-
}
|
|
560
|
-
return false;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
return true;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
/**
|
|
573
|
-
* --- 检测提交的数据类型(会检测 XSRF) ---
|
|
574
|
-
* @param input 要校验的输入项
|
|
575
|
-
* @param rule 规则
|
|
576
|
-
* @param rtn 返回值
|
|
577
|
-
*/
|
|
578
|
-
protected _checkXInput(
|
|
579
|
-
input: Record<string, types.Json>, rule: Record<string, types.Json[]>, rtn: types.Json[]
|
|
580
|
-
): boolean {
|
|
581
|
-
rule['_xsrf'] ??= ['require', this._cookie['XSRF-TOKEN'], [0, 'Bad request, no permission.']];
|
|
582
|
-
return this._checkInput(input, rule, rtn);
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
/**
|
|
586
|
-
* --- 当前页面开启 XSRF 支持(主要检测 cookie 是否存在) ---
|
|
587
|
-
* --- 如果当前页面有 CDN,请不要使用 ---
|
|
588
|
-
*/
|
|
589
|
-
protected _enabledXsrf(): void {
|
|
590
|
-
// --- 设置 XSRF 值 ---
|
|
591
|
-
if (this._cookie['XSRF-TOKEN'] === undefined) {
|
|
592
|
-
const xsrf = lCore.random(16, lCore.RANDOM_LUN);
|
|
593
|
-
this._xsrf = xsrf;
|
|
594
|
-
lCore.setCookie(this, 'XSRF-TOKEN', xsrf, {
|
|
595
|
-
'path': '/',
|
|
596
|
-
'httponly': true
|
|
597
|
-
});
|
|
598
|
-
this._cookie['XSRF-TOKEN'] = xsrf;
|
|
599
|
-
}
|
|
600
|
-
else {
|
|
601
|
-
this._xsrf = this._cookie['XSRF-TOKEN'];
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
/**
|
|
606
|
-
* --- 获取 Auth 字符串,用于客户端提交 ---
|
|
607
|
-
* @param user 用户名
|
|
608
|
-
* @param pwd 密码
|
|
609
|
-
*/
|
|
610
|
-
protected _getBasicAuth(user: string, pwd: string): string {
|
|
611
|
-
return 'Basic ' + crypto.base64Encode(user + ':' + pwd);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
/**
|
|
615
|
-
* --- 根据用户 ua 获取当前用户的设备类型 ---
|
|
616
|
-
*/
|
|
617
|
-
protected _device(): 'android' | 'windows' | 'linux' | 'macintosh' | 'ipad' | 'unknown' {
|
|
618
|
-
const ua = this._req.headers['user-agent']?.toLowerCase();
|
|
619
|
-
if (!ua) {
|
|
620
|
-
return 'unknown';
|
|
621
|
-
}
|
|
622
|
-
const list = ['android', 'windows', 'linux', 'macintosh', 'ipad', 'unknown'];
|
|
623
|
-
for (const item of list) {
|
|
624
|
-
if (!ua.includes(item)) {
|
|
625
|
-
continue;
|
|
626
|
-
}
|
|
627
|
-
return item as any;
|
|
628
|
-
}
|
|
629
|
-
return 'unknown';
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
/** --- auth 对象,user, pwd --- */
|
|
633
|
-
private _authorization: { 'user': string; 'pwd': string; } | null = null;
|
|
634
|
-
|
|
635
|
-
/**
|
|
636
|
-
* --- 通过 header 或 _auth 获取鉴权信息或 JWT 信息(不解析) ---
|
|
637
|
-
*/
|
|
638
|
-
public getAuthorization(): { 'user': string; 'pwd': string; } | false | string {
|
|
639
|
-
if (this._authorization !== null) {
|
|
640
|
-
return this._authorization;
|
|
641
|
-
}
|
|
642
|
-
let auth = '';
|
|
643
|
-
if (this._headers['authorization']) {
|
|
644
|
-
auth = this._headers['authorization'];
|
|
645
|
-
}
|
|
646
|
-
else if (this._get['_auth']) {
|
|
647
|
-
auth = this._get['_auth'];
|
|
648
|
-
}
|
|
649
|
-
else if (this._post['_auth']) {
|
|
650
|
-
auth = this._post['_auth'];
|
|
651
|
-
}
|
|
652
|
-
if (typeof auth !== 'string') {
|
|
653
|
-
return false;
|
|
654
|
-
}
|
|
655
|
-
let authArr = auth.split(' ');
|
|
656
|
-
if (authArr[1] === undefined) {
|
|
657
|
-
return false;
|
|
658
|
-
}
|
|
659
|
-
if (authArr[1].includes('.')) {
|
|
660
|
-
// --- 不解析,解析使用 JWT 类解析 ---
|
|
661
|
-
return authArr[1];
|
|
662
|
-
}
|
|
663
|
-
if (!(auth = crypto.base64Decode(authArr[1]))) {
|
|
664
|
-
return false;
|
|
665
|
-
}
|
|
666
|
-
authArr = auth.split(':');
|
|
667
|
-
this._authorization = { 'user': authArr[0], 'pwd': authArr[1] ?? '' };
|
|
668
|
-
return this._authorization;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
/**
|
|
672
|
-
* --- 获取 data 数据 ---
|
|
673
|
-
* @param path 文件路径(不含扩展名)
|
|
674
|
-
*/
|
|
675
|
-
protected async _loadData(path: string): Promise<Record<string, string> | null> {
|
|
676
|
-
const realPath = this._config.const.dataPath + path + '.json';
|
|
677
|
-
if (loadedData[realPath]) {
|
|
678
|
-
return loadedData[realPath];
|
|
679
|
-
}
|
|
680
|
-
const content = await fs.getContent(realPath, 'utf8');
|
|
681
|
-
if (!content) {
|
|
682
|
-
return null;
|
|
683
|
-
}
|
|
684
|
-
const json = text.parseJson(content);
|
|
685
|
-
loadedData[realPath] = json;
|
|
686
|
-
return json;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
/**
|
|
690
|
-
* --- 跳转(302临时跳转),支持相对本项目根路径的路径或绝对路径 ---
|
|
691
|
-
* @param location 相对或绝对网址
|
|
692
|
-
*/
|
|
693
|
-
protected _location(location: string): false {
|
|
694
|
-
if (this._res) {
|
|
695
|
-
this._res.setHeader('location', text.urlResolve(this._config.const.urlBase, location));
|
|
696
|
-
// this._res.writeHead(302); Kebab 中要在最后设置,否则会报错:ERR_HTTP_HEADERS_SENT
|
|
697
|
-
}
|
|
698
|
-
return false;
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
/**
|
|
702
|
-
* --- 开启 Session ---
|
|
703
|
-
* @param link Kv 或 Db 实例
|
|
704
|
-
* @param auth 设为 true 则从头 Authorization 或 post _auth 值读取 token
|
|
705
|
-
* @param opt name, ttl, ssl, sqlPre
|
|
706
|
-
*/
|
|
707
|
-
protected async _startSession(
|
|
708
|
-
link: db.Pool | kv.Pool,
|
|
709
|
-
auth: boolean = false,
|
|
710
|
-
opt: session.IOptions = {}
|
|
711
|
-
): Promise<void> {
|
|
712
|
-
this._sess = new session.Session();
|
|
713
|
-
await this._sess.init(this, link, auth, opt);
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
// --- 本地化 ---
|
|
717
|
-
|
|
718
|
-
/**
|
|
719
|
-
* --- 设定语言并加载语言包 ---
|
|
720
|
-
* @param loc 要加载的目标语言
|
|
721
|
-
* @param pkg 包名,为空自动填充为 default
|
|
722
|
-
*/
|
|
723
|
-
protected async _loadLocale(loc: string, pkg: string = 'default'): Promise<boolean> {
|
|
724
|
-
const lName = loc + '.' + pkg;
|
|
725
|
-
this._locale = loc;
|
|
726
|
-
if (!this._localeFiles.includes(lName)) {
|
|
727
|
-
// --- 检测全局缓存是否加载 ---
|
|
728
|
-
const lPath = this._config.const.dataPath + 'locale/' + lName;
|
|
729
|
-
if (!localeFiles.includes(lPath)) {
|
|
730
|
-
// --- 全局缓存没有,先加载全局缓存 ---
|
|
731
|
-
const locData = await this._loadData('locale/' + lName);
|
|
732
|
-
if (locData === null) {
|
|
733
|
-
return false;
|
|
734
|
-
}
|
|
735
|
-
this._locale = loc;
|
|
736
|
-
localeData[lPath] ??= {};
|
|
737
|
-
this._loadLocaleDeep(lPath, locData);
|
|
738
|
-
localeFiles.push(lPath);
|
|
739
|
-
}
|
|
740
|
-
else {
|
|
741
|
-
this._locale = loc;
|
|
742
|
-
}
|
|
743
|
-
// --- 缓存中一定有文件 ---
|
|
744
|
-
this._localeData[loc] ??= {};
|
|
745
|
-
for (const key in localeData[lPath]) {
|
|
746
|
-
this._localeData[loc][key] = localeData[lPath][key];
|
|
747
|
-
}
|
|
748
|
-
this._localeFiles.push(lName);
|
|
749
|
-
}
|
|
750
|
-
else {
|
|
751
|
-
this._locale = loc;
|
|
752
|
-
}
|
|
753
|
-
return true;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
private _loadLocaleDeep(lPath: string, locData: Record<string, types.Json>, pre: string = ''): void {
|
|
757
|
-
for (const k in locData) {
|
|
758
|
-
const v = locData[k];
|
|
759
|
-
if (typeof v === 'object') {
|
|
760
|
-
this._loadLocaleDeep(lPath, v, pre + k + '.');
|
|
761
|
-
}
|
|
762
|
-
else {
|
|
763
|
-
localeData[lPath][pre + k] = v;
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
/**
|
|
769
|
-
* --- 根据当前后台语言包设置情况获取 JSON 字符串传输到前台 ---
|
|
770
|
-
* @return string
|
|
771
|
-
*/
|
|
772
|
-
protected _getLocaleJsonString(): string {
|
|
773
|
-
if (this._localeData[this._locale] !== undefined) {
|
|
774
|
-
return text.stringifyJson(this._localeData[this._locale]);
|
|
775
|
-
}
|
|
776
|
-
else {
|
|
777
|
-
return '{}';
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
/**
|
|
782
|
-
* --- 获取当前语言名 ---
|
|
783
|
-
*/
|
|
784
|
-
protected _getLocale(): string {
|
|
785
|
-
return this._locale;
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
/**
|
|
789
|
-
* --- 开启跨域请求 ---
|
|
790
|
-
* 返回 true 接续执行,返回 false 需要中断用户本次访问(options请求)
|
|
791
|
-
*/
|
|
792
|
-
protected _cross(): boolean {
|
|
793
|
-
this._res.setHeader('access-control-allow-origin', '*');
|
|
794
|
-
this._res.setHeader('access-control-allow-headers', '*');
|
|
795
|
-
this._res.setHeader('access-control-allow-methods', '*');
|
|
796
|
-
if (this._req.method === 'OPTIONS') {
|
|
797
|
-
this._res.setHeader('access-control-max-age', '600');
|
|
798
|
-
return false;
|
|
799
|
-
}
|
|
800
|
-
return true;
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
// --- 以下:Mutton: false, Kebab: true ---
|
|
804
|
-
|
|
805
|
-
/**
|
|
806
|
-
* --- 获取语言包值 ---
|
|
807
|
-
* @param key
|
|
808
|
-
* @param data 要替换的数据
|
|
809
|
-
*/
|
|
810
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
811
|
-
public _l(key: string, data?: string[]): string {
|
|
812
|
-
if (!this._localeData[this._locale]) {
|
|
813
|
-
return '[LocaleError]' + key;
|
|
814
|
-
}
|
|
815
|
-
if (!this._localeData[this._locale][key]) {
|
|
816
|
-
return '[LocaleError]' + key;
|
|
817
|
-
}
|
|
818
|
-
if (data) {
|
|
819
|
-
let i: number = -1;
|
|
820
|
-
return this._localeData[this._locale][key].replace(/\?/g, function() {
|
|
821
|
-
++i;
|
|
822
|
-
if (!data[i]) {
|
|
823
|
-
return '';
|
|
824
|
-
}
|
|
825
|
-
return data[i];
|
|
826
|
-
});
|
|
827
|
-
}
|
|
828
|
-
else {
|
|
829
|
-
return this._localeData[this._locale][key];
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
/**
|
|
834
|
-
* --- 发送 socket 文本 ---
|
|
835
|
-
* @param data 要发送的信息
|
|
836
|
-
*/
|
|
837
|
-
protected _writeText(data: string): boolean {
|
|
838
|
-
return this._socket.writeText(data);
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
/**
|
|
842
|
-
* --- 发送结果对象文本 ---
|
|
843
|
-
* @param data 要发送的结果对象,如 [0, 'Failed.']
|
|
844
|
-
*/
|
|
845
|
-
protected _writeResult(data: types.Json): boolean {
|
|
846
|
-
return this._socket.writeResult(data);
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
/**
|
|
850
|
-
* --- 发送 socket 二进制 ---
|
|
851
|
-
* @param data 要发送的信息
|
|
852
|
-
*/
|
|
853
|
-
protected _writeBinary(data: Buffer | string | Array<Buffer | string>): boolean {
|
|
854
|
-
return this._socket.writeBinary(data);
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
/**
|
|
858
|
-
* --- 发送 socket ping ---
|
|
859
|
-
* @param data 要发送的信息
|
|
860
|
-
*/
|
|
861
|
-
protected _ping(): boolean {
|
|
862
|
-
return this._socket.ping();
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
/**
|
|
866
|
-
* --- 发送 socket pong ---
|
|
867
|
-
* @param data 要发送的信息
|
|
868
|
-
*/
|
|
869
|
-
protected _pong(): boolean {
|
|
870
|
-
return this._socket.pong();
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
/**
|
|
874
|
-
* --- 主动关闭当前 socket 连接 ---
|
|
875
|
-
*/
|
|
876
|
-
protected _end(): void {
|
|
877
|
-
this._socket.end();
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
/**
|
|
881
|
-
* --- 获取 formdata 的信息 ---
|
|
882
|
-
* @param events 文件处理情况
|
|
883
|
-
*/
|
|
884
|
-
protected async _handleFormData(
|
|
885
|
-
events: {
|
|
886
|
-
onfilestart?: (name: string) => boolean | undefined;
|
|
887
|
-
onfiledata?: (chunk: Buffer) => void;
|
|
888
|
-
onfileend?: () => void;
|
|
889
|
-
} = {}
|
|
890
|
-
): Promise<boolean> {
|
|
891
|
-
const rtn = await sRoute.getFormData(this._req, events);
|
|
892
|
-
if (!rtn) {
|
|
893
|
-
return false;
|
|
894
|
-
}
|
|
895
|
-
for (const key in rtn.post) {
|
|
896
|
-
this._post[key] = rtn.post[key];
|
|
897
|
-
}
|
|
898
|
-
for (const key in rtn.files) {
|
|
899
|
-
this._files[key] = rtn.files[key];
|
|
900
|
-
}
|
|
901
|
-
return true;
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
}
|