@maiyunnet/kebab 9.13.5 → 9.13.7
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/doc/kebab-rag.md +172 -84
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/lib/net.d.ts +8 -0
- package/lib/net.js +20 -0
- package/lib/undici.d.ts +8 -0
- package/lib/undici.js +20 -0
- package/package.json +1 -1
- package/sys/route.js +106 -49
- package/www/example/ctr/test.d.ts +8 -27
- package/www/example/ctr/test.js +65 -495
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* --- 本文件用来定义每个目录实体地址的常量 ---
|
|
7
7
|
*/
|
|
8
8
|
/** --- 当前系统版本号 --- */
|
|
9
|
-
export const VER = '9.13.
|
|
9
|
+
export const VER = '9.13.7';
|
|
10
10
|
// --- 服务端用的路径 ---
|
|
11
11
|
const imu = decodeURIComponent(import.meta.url).replace('file://', '').replace(/^\/(\w:)/, '$1');
|
|
12
12
|
/** --- /xxx/xxx --- */
|
package/lib/net.d.ts
CHANGED
|
@@ -48,6 +48,14 @@ export declare function postJson(u: string, data: kebab.Json[] | Record<string,
|
|
|
48
48
|
* @returns JSON 数据,失败时返回 null
|
|
49
49
|
*/
|
|
50
50
|
export declare function postJsonResponseJson(u: string, data: kebab.Json[] | Record<string, kebab.Json>, opt?: IRequestOptions): Promise<kebab.Json | null>;
|
|
51
|
+
/**
|
|
52
|
+
* --- 发起 POST 请求并解析 JSON 响应 ---
|
|
53
|
+
* @param u 网址
|
|
54
|
+
* @param data 数据
|
|
55
|
+
* @param opt 选项
|
|
56
|
+
* @returns JSON 数据,失败时返回 null
|
|
57
|
+
*/
|
|
58
|
+
export declare function postResponseJson(u: string, data: Record<string, kebab.Json>, opt?: IRequestOptions): Promise<kebab.Json | null>;
|
|
51
59
|
/**
|
|
52
60
|
* --- 发起 GET 请求并解析 JSON 响应 ---
|
|
53
61
|
* @param u 网址
|
package/lib/net.js
CHANGED
|
@@ -97,6 +97,26 @@ export async function postJsonResponseJson(u, data, opt = {}) {
|
|
|
97
97
|
}
|
|
98
98
|
return json;
|
|
99
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* --- 发起 POST 请求并解析 JSON 响应 ---
|
|
102
|
+
* @param u 网址
|
|
103
|
+
* @param data 数据
|
|
104
|
+
* @param opt 选项
|
|
105
|
+
* @returns JSON 数据,失败时返回 null
|
|
106
|
+
*/
|
|
107
|
+
export async function postResponseJson(u, data, opt = {}) {
|
|
108
|
+
opt.method = 'POST';
|
|
109
|
+
const res = await request(u, data, opt);
|
|
110
|
+
const rtn = await res.getContent();
|
|
111
|
+
if (!rtn) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
const json = lText.parseJson(rtn.toString());
|
|
115
|
+
if (!json) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
return json;
|
|
119
|
+
}
|
|
100
120
|
/**
|
|
101
121
|
* --- 发起 GET 请求并解析 JSON 响应 ---
|
|
102
122
|
* @param u 网址
|
package/lib/undici.d.ts
CHANGED
|
@@ -42,6 +42,14 @@ export declare function postJson(u: string, data: kebab.Json[] | Record<string,
|
|
|
42
42
|
* @param opt 选项
|
|
43
43
|
*/
|
|
44
44
|
export declare function postJsonResponseJson(u: string, data: kebab.Json[] | Record<string, kebab.Json>, opt?: IRequestOptions): Promise<kebab.Json | null>;
|
|
45
|
+
/**
|
|
46
|
+
* --- 发起 POST 请求并解析 JSON 响应 ---
|
|
47
|
+
* @param u 网址
|
|
48
|
+
* @param data 数据
|
|
49
|
+
* @param opt 选项
|
|
50
|
+
* @returns JSON 数据,失败时返回 null
|
|
51
|
+
*/
|
|
52
|
+
export declare function postResponseJson(u: string, data: Record<string, kebab.Json>, opt?: IRequestOptions): Promise<kebab.Json | null>;
|
|
45
53
|
/**
|
|
46
54
|
* --- 发起 GET 请求并解析 JSON 响应 ---
|
|
47
55
|
* @param u 网址
|
package/lib/undici.js
CHANGED
|
@@ -115,6 +115,26 @@ export async function postJsonResponseJson(u, data, opt = {}) {
|
|
|
115
115
|
}
|
|
116
116
|
return json;
|
|
117
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* --- 发起 POST 请求并解析 JSON 响应 ---
|
|
120
|
+
* @param u 网址
|
|
121
|
+
* @param data 数据
|
|
122
|
+
* @param opt 选项
|
|
123
|
+
* @returns JSON 数据,失败时返回 null
|
|
124
|
+
*/
|
|
125
|
+
export async function postResponseJson(u, data, opt = {}) {
|
|
126
|
+
opt.method = 'POST';
|
|
127
|
+
const res = await request(u, data, opt);
|
|
128
|
+
const rtn = await res.getContent();
|
|
129
|
+
if (!rtn) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const json = lText.parseJson(rtn.toString());
|
|
133
|
+
if (!json) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
return json;
|
|
137
|
+
}
|
|
118
138
|
/**
|
|
119
139
|
* --- 发起 GET 请求并解析 JSON 响应 ---
|
|
120
140
|
* @param u 网址
|
package/package.json
CHANGED
package/sys/route.js
CHANGED
|
@@ -902,7 +902,7 @@ export function getPost(req) {
|
|
|
902
902
|
* @param limits 文件上传限制
|
|
903
903
|
*/
|
|
904
904
|
export function getFormData(req, events = {}, limits = {}) {
|
|
905
|
-
return new Promise(
|
|
905
|
+
return new Promise(resolve => {
|
|
906
906
|
if (req.readableEnded) {
|
|
907
907
|
resolve({ 'post': {}, 'files': {} });
|
|
908
908
|
return;
|
|
@@ -954,6 +954,38 @@ export function getFormData(req, events = {}, limits = {}) {
|
|
|
954
954
|
let writeFileLength = 0;
|
|
955
955
|
/** --- 当前读取是否已经完全结束 --- */
|
|
956
956
|
let readEnd = false;
|
|
957
|
+
/** --- 是否有文件被限制拒绝(整体返回 false) --- */
|
|
958
|
+
let rejected = false;
|
|
959
|
+
/** --- 清理 rtn.files 中所有已写入的临时文件 --- */
|
|
960
|
+
function cleanupFiles() {
|
|
961
|
+
for (const key in rtn.files) {
|
|
962
|
+
let files = rtn.files[key];
|
|
963
|
+
if (!Array.isArray(files)) {
|
|
964
|
+
files = [files];
|
|
965
|
+
}
|
|
966
|
+
for (const file of files) {
|
|
967
|
+
lFs.unlink(file.path).catch(() => { });
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
/** --- 拒绝当前文件:销毁流、删临时文件、标记 rejected --- */
|
|
972
|
+
function rejectFile() {
|
|
973
|
+
rejected = true;
|
|
974
|
+
ftmpStream.destroy();
|
|
975
|
+
lFs.unlink(kebab.FTMP_CWD + ftmpName).catch(() => { });
|
|
976
|
+
ftmpName = '';
|
|
977
|
+
--writeFileLength;
|
|
978
|
+
}
|
|
979
|
+
/** --- 最终输出 --- */
|
|
980
|
+
function finalize() {
|
|
981
|
+
if (rejected) {
|
|
982
|
+
cleanupFiles();
|
|
983
|
+
resolve(false);
|
|
984
|
+
}
|
|
985
|
+
else {
|
|
986
|
+
resolve(rtn);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
957
989
|
// --- 开始读取 ---
|
|
958
990
|
req.on('data', function (chunk) {
|
|
959
991
|
buffer = Buffer.concat([buffer, chunk], buffer.length + chunk.length);
|
|
@@ -983,12 +1015,18 @@ export function getFormData(req, events = {}, limits = {}) {
|
|
|
983
1015
|
++writeFileLength;
|
|
984
1016
|
state = EState.FILE;
|
|
985
1017
|
fileName = match[1];
|
|
1018
|
+
// --- 已被拒绝则不创建文件流,仅丢弃数据 ---
|
|
1019
|
+
if (rejected) {
|
|
1020
|
+
ftmpName = '';
|
|
1021
|
+
break;
|
|
1022
|
+
}
|
|
986
1023
|
// --- 检查文件扩展名限制 ---
|
|
987
1024
|
if (limits.allowedExts?.length) {
|
|
988
1025
|
const extIo = fileName.lastIndexOf('.');
|
|
989
1026
|
const ext = extIo !== -1 ? fileName.slice(extIo).toLowerCase() : '';
|
|
990
1027
|
if (!limits.allowedExts.includes(ext)) {
|
|
991
|
-
// ---
|
|
1028
|
+
// --- 扩展名不允许,拒绝整体上传 ---
|
|
1029
|
+
rejected = true;
|
|
992
1030
|
ftmpName = '';
|
|
993
1031
|
break;
|
|
994
1032
|
}
|
|
@@ -1003,6 +1041,7 @@ export function getFormData(req, events = {}, limits = {}) {
|
|
|
1003
1041
|
date.getUTCHours().toString().padStart(2, '0') +
|
|
1004
1042
|
date.getUTCMinutes().toString().padStart(2, '0') + '_' + lCore.random() + '.ftmp';
|
|
1005
1043
|
ftmpStream = lFs.createWriteStream(kebab.FTMP_CWD + ftmpName);
|
|
1044
|
+
ftmpStream.on('error', () => { });
|
|
1006
1045
|
ftmpSize = 0;
|
|
1007
1046
|
}
|
|
1008
1047
|
else {
|
|
@@ -1042,22 +1081,23 @@ export function getFormData(req, events = {}, limits = {}) {
|
|
|
1042
1081
|
// --- 没找到结束标语,将预留 boundary 长度之前的写入到文件 ---
|
|
1043
1082
|
const writeBuffer = buffer.subarray(0, -boundary.length - 4);
|
|
1044
1083
|
if (ftmpName) {
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
ftmpSize += Buffer.byteLength(writeBuffer);
|
|
1084
|
+
if (writeBuffer.length > 0) {
|
|
1085
|
+
// --- 检查文件大小限制 ---
|
|
1086
|
+
if (limits.maxFileSize &&
|
|
1087
|
+
(ftmpSize + Buffer.byteLength(writeBuffer) > limits.maxFileSize)) {
|
|
1088
|
+
rejectFile();
|
|
1089
|
+
}
|
|
1090
|
+
else {
|
|
1091
|
+
ftmpStream.write(writeBuffer);
|
|
1092
|
+
ftmpSize += Buffer.byteLength(writeBuffer);
|
|
1093
|
+
}
|
|
1056
1094
|
}
|
|
1057
1095
|
}
|
|
1058
1096
|
else {
|
|
1059
1097
|
// --- 跳过该文件 ---
|
|
1060
|
-
|
|
1098
|
+
if (writeBuffer.length > 0) {
|
|
1099
|
+
events.onfiledata?.(writeBuffer);
|
|
1100
|
+
}
|
|
1061
1101
|
}
|
|
1062
1102
|
buffer = buffer.subarray(-boundary.length - 4);
|
|
1063
1103
|
return;
|
|
@@ -1065,44 +1105,51 @@ export function getFormData(req, events = {}, limits = {}) {
|
|
|
1065
1105
|
// --- 找到结束标语,结束标语之前的写入文件,之后的重新放回 buffer ---
|
|
1066
1106
|
const writeBuffer = buffer.subarray(0, io);
|
|
1067
1107
|
if (ftmpName) {
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
if (!readEnd) {
|
|
1073
|
-
// --- request 没读完,不管 ---
|
|
1074
|
-
return;
|
|
1075
|
-
}
|
|
1076
|
-
if (writeFileLength) {
|
|
1077
|
-
// --- req 读完了但文件还没写完,不管 ---
|
|
1078
|
-
return;
|
|
1079
|
-
}
|
|
1080
|
-
// --- 文件也写完了 ---
|
|
1081
|
-
resolve(rtn);
|
|
1082
|
-
});
|
|
1083
|
-
// --- POST 部分 ---
|
|
1084
|
-
let fname = fileName.replace(/\\/g, '/');
|
|
1085
|
-
const nlio = fname.lastIndexOf('/');
|
|
1086
|
-
if (nlio !== -1) {
|
|
1087
|
-
fname = fname.slice(nlio + 1);
|
|
1108
|
+
// --- 检查文件大小限制(最后一个分片) ---
|
|
1109
|
+
if (limits.maxFileSize &&
|
|
1110
|
+
(ftmpSize + Buffer.byteLength(writeBuffer) > limits.maxFileSize)) {
|
|
1111
|
+
rejectFile();
|
|
1088
1112
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1113
|
+
else {
|
|
1114
|
+
ftmpStream.write(writeBuffer);
|
|
1115
|
+
ftmpSize += Buffer.byteLength(writeBuffer);
|
|
1116
|
+
ftmpStream.end(() => {
|
|
1117
|
+
--writeFileLength;
|
|
1118
|
+
if (!readEnd) {
|
|
1119
|
+
// --- request 没读完,不管 ---
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
if (writeFileLength) {
|
|
1123
|
+
// --- req 读完了但文件还没写完,不管 ---
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
// --- 文件也写完了 ---
|
|
1127
|
+
finalize();
|
|
1128
|
+
});
|
|
1129
|
+
// --- POST 部分 ---
|
|
1130
|
+
let fname = fileName.replace(/\\/g, '/');
|
|
1131
|
+
const nlio = fname.lastIndexOf('/');
|
|
1132
|
+
if (nlio !== -1) {
|
|
1133
|
+
fname = fname.slice(nlio + 1);
|
|
1134
|
+
}
|
|
1135
|
+
const val = {
|
|
1136
|
+
'name': fname,
|
|
1137
|
+
'origin': fileName,
|
|
1138
|
+
'size': ftmpSize,
|
|
1139
|
+
'path': kebab.FTMP_CWD + ftmpName
|
|
1140
|
+
};
|
|
1141
|
+
if (rtn.files[name]) {
|
|
1142
|
+
if (Array.isArray(rtn.files[name])) {
|
|
1143
|
+
rtn.files[name].push(val);
|
|
1144
|
+
}
|
|
1145
|
+
else {
|
|
1146
|
+
rtn.files[name] = [rtn.files[name], val];
|
|
1147
|
+
}
|
|
1098
1148
|
}
|
|
1099
1149
|
else {
|
|
1100
|
-
rtn.files[name] =
|
|
1150
|
+
rtn.files[name] = val;
|
|
1101
1151
|
}
|
|
1102
1152
|
}
|
|
1103
|
-
else {
|
|
1104
|
-
rtn.files[name] = val;
|
|
1105
|
-
}
|
|
1106
1153
|
}
|
|
1107
1154
|
else {
|
|
1108
1155
|
// --- 跳过该文件 ---
|
|
@@ -1114,6 +1161,10 @@ export function getFormData(req, events = {}, limits = {}) {
|
|
|
1114
1161
|
break;
|
|
1115
1162
|
}
|
|
1116
1163
|
}
|
|
1164
|
+
// --- 被拒绝则丢弃剩余数据,等待 'end' 事件后 resolve ---
|
|
1165
|
+
if (rejected) {
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1117
1168
|
}
|
|
1118
1169
|
});
|
|
1119
1170
|
req.on('error', function (e) {
|
|
@@ -1122,17 +1173,23 @@ export function getFormData(req, events = {}, limits = {}) {
|
|
|
1122
1173
|
}
|
|
1123
1174
|
lCore.debug('[ROUTE][GETFORMDATA] request error before getFormData: ' + e.message);
|
|
1124
1175
|
lCore.log({}, '[ROUTE][GETFORMDATA] request error before getFormData: ' + (e.stack ?? ''), '-error');
|
|
1176
|
+
cleanupFiles();
|
|
1125
1177
|
resolve(false);
|
|
1126
1178
|
});
|
|
1127
1179
|
req.on('end', function () {
|
|
1128
1180
|
readEnd = true;
|
|
1181
|
+
// --- 被拒绝则清理文件并 resolve(false) ---
|
|
1182
|
+
if (rejected) {
|
|
1183
|
+
finalize();
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1129
1186
|
if (state === EState.FILE) {
|
|
1130
1187
|
if (ftmpName) {
|
|
1131
1188
|
ftmpStream.end(() => {
|
|
1132
1189
|
--writeFileLength;
|
|
1133
1190
|
if (!writeFileLength) {
|
|
1134
1191
|
// --- 文件也写完了 ---
|
|
1135
|
-
|
|
1192
|
+
finalize();
|
|
1136
1193
|
}
|
|
1137
1194
|
});
|
|
1138
1195
|
return;
|
|
@@ -1146,7 +1203,7 @@ export function getFormData(req, events = {}, limits = {}) {
|
|
|
1146
1203
|
return;
|
|
1147
1204
|
}
|
|
1148
1205
|
// --- 文件写完了 ----
|
|
1149
|
-
|
|
1206
|
+
finalize();
|
|
1150
1207
|
});
|
|
1151
1208
|
});
|
|
1152
1209
|
}
|
|
@@ -64,33 +64,6 @@ export default class extends sCtr.Ctr {
|
|
|
64
64
|
private _dbTable;
|
|
65
65
|
vector(): Promise<any>;
|
|
66
66
|
kv(): Promise<kebab.Json>;
|
|
67
|
-
net(): Promise<string>;
|
|
68
|
-
netPipe(): Promise<kebab.Json>;
|
|
69
|
-
netPost(): Promise<kebab.Json>;
|
|
70
|
-
netPost1(): string;
|
|
71
|
-
netPostString(): Promise<string>;
|
|
72
|
-
netPostString1(): kebab.Json[];
|
|
73
|
-
netOpen(): Promise<kebab.Json>;
|
|
74
|
-
netFormTest(): Promise<string>;
|
|
75
|
-
netUpload(): Promise<string>;
|
|
76
|
-
netUpload1(): Promise<string>;
|
|
77
|
-
netCookie(): Promise<string>;
|
|
78
|
-
netCookie1(): string;
|
|
79
|
-
netCookie2(): string;
|
|
80
|
-
netSave(): Promise<string>;
|
|
81
|
-
netFollow(): Promise<string>;
|
|
82
|
-
netFollow1(): void;
|
|
83
|
-
netFollow2(): kebab.Json;
|
|
84
|
-
netReuse(): Promise<string>;
|
|
85
|
-
netError(): Promise<string>;
|
|
86
|
-
netHosts(): Promise<string>;
|
|
87
|
-
netMproxy(): Promise<string | boolean>;
|
|
88
|
-
netMproxy1(): Promise<string | boolean>;
|
|
89
|
-
netMproxy2(): any[];
|
|
90
|
-
netFilterheaders(): string;
|
|
91
|
-
netFetch(): Promise<string | boolean>;
|
|
92
|
-
netFetch1(): any[];
|
|
93
|
-
netGetResponseJson(): Promise<string>;
|
|
94
67
|
getResponseJson1(): any[];
|
|
95
68
|
getResponseJson2(): any[];
|
|
96
69
|
undici(): Promise<string>;
|
|
@@ -192,6 +165,14 @@ export default class extends sCtr.Ctr {
|
|
|
192
165
|
* --- CORS 精细化配置测试 ---
|
|
193
166
|
*/
|
|
194
167
|
ctrCrossFine(): string;
|
|
168
|
+
/**
|
|
169
|
+
* --- 文件上传限制演示页面 ---
|
|
170
|
+
*/
|
|
171
|
+
ctrUploadLimits(): string;
|
|
172
|
+
/**
|
|
173
|
+
* --- 文件上传限制演示接口 ---
|
|
174
|
+
*/
|
|
175
|
+
ctrUploadLimits1(): Promise<kebab.Json[]>;
|
|
195
176
|
/**
|
|
196
177
|
* --- END ---
|
|
197
178
|
*/
|