@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/index.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * --- 本文件用来定义每个目录实体地址的常量 ---
6
6
  */
7
7
  /** --- 当前系统版本号 --- */
8
- export declare const VER = "9.13.5";
8
+ export declare const VER = "9.13.7";
9
9
  /** --- 框架根目录,以 / 结尾 --- */
10
10
  export declare const ROOT_PATH: string;
11
11
  /** --- 框架的 LIB,以 / 结尾 --- */
package/index.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * --- 本文件用来定义每个目录实体地址的常量 ---
7
7
  */
8
8
  /** --- 当前系统版本号 --- */
9
- export const VER = '9.13.5';
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maiyunnet/kebab",
3
- "version": "9.13.5",
3
+ "version": "9.13.7",
4
4
  "description": "Simple, easy-to-use, and fully-featured Node.js framework that is ready-to-use out of the box.",
5
5
  "type": "module",
6
6
  "keywords": [
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(function (resolve) {
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
- if (limits.maxFileSize &&
1047
- (ftmpSize + Buffer.byteLength(writeBuffer) > limits.maxFileSize)) {
1048
- ftmpStream.destroy();
1049
- lFs.unlink(kebab.FTMP_CWD + ftmpName).catch(() => { });
1050
- ftmpName = '';
1051
- --writeFileLength;
1052
- }
1053
- else {
1054
- ftmpStream.write(writeBuffer);
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
- events.onfiledata?.(writeBuffer);
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
- ftmpStream.write(writeBuffer);
1069
- ftmpSize += Buffer.byteLength(writeBuffer);
1070
- ftmpStream.end(() => {
1071
- --writeFileLength;
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
- const val = {
1090
- 'name': fname,
1091
- 'origin': fileName,
1092
- 'size': ftmpSize,
1093
- 'path': kebab.FTMP_CWD + ftmpName
1094
- };
1095
- if (rtn.files[name]) {
1096
- if (Array.isArray(rtn.files[name])) {
1097
- rtn.files[name].push(val);
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] = [rtn.files[name], val];
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
- resolve(rtn);
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
- resolve(rtn);
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
  */