mm_os 3.2.9 → 3.3.1

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 (56) hide show
  1. package/README.md +47 -1
  2. package/core/base/mqtt/index.js +1109 -1106
  3. package/core/base/web/index.js +245 -156
  4. package/core/com/event/README.md +4 -4
  5. package/core/com/event/com.json +3 -3
  6. package/core/com/event/config.tpl.json +18 -18
  7. package/core/com/event/drive.js +132 -132
  8. package/core/com/event/index.js +344 -344
  9. package/core/com/event/script.js +25 -25
  10. package/core/com/middleware/com.js +152 -151
  11. package/core/com/socket/config.tpl.json +2 -2
  12. package/core/com/socket/drive.js +2 -2
  13. package/core/com/socket/index.js +1 -1
  14. package/core/com/sql/drive.js +7 -7
  15. package/core/com/static/index.js +1 -1
  16. package/index.js +34 -5
  17. package/middleware/cors/index.js +112 -96
  18. package/middleware/cors/middleware.json +18 -7
  19. package/middleware/csrf/index.js +202 -0
  20. package/middleware/csrf/middleware.json +24 -0
  21. package/middleware/ip_firewall/index.js +476 -0
  22. package/middleware/ip_firewall/middleware.json +109 -0
  23. package/middleware/mqtt_base/middleware.json +2 -1
  24. package/middleware/security_audit/index.js +543 -0
  25. package/middleware/security_audit/middleware.json +48 -0
  26. package/middleware/waf/index.js +273 -7
  27. package/middleware/waf/middleware.json +2 -1
  28. package/middleware/waf_ddos/index.js +520 -0
  29. package/middleware/waf_ddos/middleware.json +38 -0
  30. package/middleware/waf_xss/index.js +269 -0
  31. package/middleware/waf_xss/middleware.json +18 -0
  32. package/middleware/web_after/middleware.json +2 -1
  33. package/middleware/web_base/middleware.json +2 -1
  34. package/middleware/web_before/middleware.json +3 -2
  35. package/middleware/web_check/middleware.json +2 -1
  36. package/middleware/web_main/middleware.json +2 -1
  37. package/middleware/web_proxy/middleware.json +2 -1
  38. package/middleware/web_render/middleware.json +2 -1
  39. package/middleware/web_socket/middleware.json +4 -3
  40. package/middleware/web_static/middleware.json +2 -1
  41. package/package.json +28 -15
  42. package/middleware/log/index.js +0 -32
  43. package/middleware/log/middleware.json +0 -9
  44. package/middleware/performance/index.js +0 -143
  45. package/middleware/performance/middleware.json +0 -16
  46. package/middleware/rate_limit/index.js +0 -112
  47. package/middleware/rate_limit/middleware.json +0 -10
  48. package/middleware/waf_ip/index.js +0 -168
  49. package/middleware/waf_ip/middleware.json +0 -10
  50. package/nodemon.json +0 -31
  51. package/package.txt +0 -1
  52. package/rps.bat +0 -3
  53. package/test.js +0 -10
  54. package/tps.bat +0 -3
  55. package/update.bat +0 -1
  56. package//347/263/273/347/273/237/346/236/266/346/236/204/350/257/204/344/274/260/344/270/216/344/274/230/345/214/226/345/273/272/350/256/256.md +0 -599
@@ -0,0 +1,269 @@
1
+ const mm_expand = require('mm_expand');
2
+
3
+ /**
4
+ * XSS攻击防护中间件
5
+ */
6
+ class Middleware {
7
+ /**
8
+ * 构造函数
9
+ */
10
+ constructor() {
11
+ this.default = {
12
+ // 过滤模式: sanitize(清理) | encode(编码) | both(同时进行)
13
+ mode: 'both',
14
+ // 是否过滤URL参数
15
+ query: true,
16
+ // 是否过滤表单数据
17
+ body: true,
18
+ // 是否过滤Cookie
19
+ cookie: true,
20
+ // 白名单标签
21
+ allowedTags: ['p', 'span', 'b', 'i', 'u', 'strong', 'em', 'br'],
22
+ // 白名单属性
23
+ allowedAttributes: ['id', 'class', 'style'],
24
+ // 是否记录XSS攻击尝试
25
+ log: true,
26
+ // 是否阻止恶意请求
27
+ block: true
28
+ };
29
+ }
30
+
31
+ /**
32
+ * 初始化中间件
33
+ * @param {Object} config 配置项
34
+ */
35
+ init(config) {
36
+ config = Object.assign({}, this.default, config);
37
+ this.config = config;
38
+ return function() {
39
+ // 返回中间件的run方法作为实际的处理函数
40
+ return function(ctx, next) {
41
+ return middleware.run(ctx, next);
42
+ };
43
+ };
44
+ }
45
+
46
+ /**
47
+ * 获取客户端真实IP
48
+ * 在代理环境中,优先从代理头获取真实IP
49
+ * @param {Object} ctx Koa上下文
50
+ * @returns {String} 客户端真实IP
51
+ */
52
+ getClientIp(ctx) {
53
+ const headers = ctx.headers;
54
+ // 优先检查常用代理头获取真实IP
55
+ // 注意:X-Forwarded-For 可能包含多个IP,取第一个非未知IP的值
56
+ const xForwardedFor = headers['x-forwarded-for'];
57
+ if (xForwardedFor) {
58
+ // X-Forwarded-For 格式通常为: client, proxy1, proxy2
59
+ const ips = xForwardedFor.split(',').map(ip => ip.trim());
60
+ for (let i = 0; i < ips.length; i++) {
61
+ const ip = ips[i];
62
+ if (ip && ip !== 'unknown' && ip !== '127.0.0.1' && ip !== '::1') {
63
+ return ip;
64
+ }
65
+ }
66
+ }
67
+
68
+ // 检查其他常见的代理头
69
+ return headers['x-real-ip'] ||
70
+ headers['x-client-ip'] ||
71
+ headers['cf-connecting-ip'] || // CloudFlare
72
+ headers['fastly-client-ip'] || // Fastly
73
+ headers['true-client-ip'] || // Akamai and Cloudflare
74
+ ctx.ip; // 默认回退到ctx.ip
75
+ }
76
+
77
+ /**
78
+ * 执行中间件
79
+ * @param {Object} ctx Koa上下文
80
+ * @param {Function} next 下一个中间件
81
+ */
82
+ async run(ctx, next) {
83
+ const config = this.config;
84
+ let hasXSS = false;
85
+ let attackInfo = {};
86
+
87
+ // 清理URL查询参数
88
+ if (config.query && ctx.query) {
89
+ for (let key in ctx.query) {
90
+ const original = ctx.query[key];
91
+ const sanitized = this.sanitizeInput(original, config);
92
+ if (original !== sanitized) {
93
+ ctx.query[key] = sanitized;
94
+ hasXSS = true;
95
+ attackInfo[`query.${key}`] = { original, sanitized };
96
+ }
97
+ }
98
+ }
99
+
100
+ // 清理请求体数据
101
+ if (config.body && ctx.request.body) {
102
+ this.cleanObject(ctx.request.body, config, attackInfo, 'body', hasXSS);
103
+ }
104
+
105
+ // 清理Cookie
106
+ if (config.cookie && ctx.cookies) {
107
+ const cookies = ctx.headers.cookie;
108
+ if (cookies) {
109
+ // 记录Cookie中的XSS尝试但不修改,因为修改Cookie需要重新设置
110
+ const cookieArr = cookies.split(';');
111
+ for (let cookie of cookieArr) {
112
+ const [name, value] = cookie.split('=').map(item => item.trim());
113
+ const sanitized = this.sanitizeInput(decodeURIComponent(value), config);
114
+ if (value !== sanitized) {
115
+ hasXSS = true;
116
+ attackInfo[`cookie.${name}`] = { original: value, sanitized };
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ // 记录XSS攻击尝试
123
+ if (hasXSS && config.log && $.log) {
124
+ $.log.warn('XSS Attack Attempt Detected:', {
125
+ ip: this.getClientIp(ctx),
126
+ path: ctx.path,
127
+ method: ctx.method,
128
+ attackInfo
129
+ });
130
+ }
131
+
132
+ // 阻止恶意请求
133
+ if (hasXSS && config.block) {
134
+ ctx.status = 403;
135
+ ctx.body = {
136
+ code: 403,
137
+ msg: 'Forbidden: Potential XSS attack detected'
138
+ };
139
+ return;
140
+ }
141
+
142
+ // 添加安全响应头
143
+ this.addSecurityHeaders(ctx);
144
+
145
+ await next();
146
+ }
147
+
148
+ /**
149
+ * 递归清理对象中的XSS内容
150
+ * @param {Object} obj 要清理的对象
151
+ * @param {Object} config 配置项
152
+ * @param {Object} attackInfo 攻击信息收集对象
153
+ * @param {String} prefix 路径前缀
154
+ * @param {Boolean} hasXSS 是否已发现XSS
155
+ */
156
+ cleanObject(obj, config, attackInfo, prefix, hasXSS) {
157
+ if (!obj || typeof obj !== 'object') return;
158
+
159
+ for (let key in obj) {
160
+ const path = `${prefix}.${key}`;
161
+ if (typeof obj[key] === 'string') {
162
+ const original = obj[key];
163
+ const sanitized = this.sanitizeInput(original, config);
164
+ if (original !== sanitized) {
165
+ obj[key] = sanitized;
166
+ hasXSS = true;
167
+ attackInfo[path] = { original, sanitized };
168
+ }
169
+ } else if (typeof obj[key] === 'object') {
170
+ this.cleanObject(obj[key], config, attackInfo, path, hasXSS);
171
+ }
172
+ }
173
+ }
174
+
175
+ /**
176
+ * 清理输入内容
177
+ * @param {String} input 输入内容
178
+ * @param {Object} config 配置项
179
+ * @returns {String} 清理后的内容
180
+ */
181
+ sanitizeInput(input, config) {
182
+ if (!input || typeof input !== 'string') return input;
183
+
184
+ let output = input;
185
+
186
+ // 根据模式选择清理方式
187
+ if (config.mode === 'sanitize' || config.mode === 'both') {
188
+ output = this.sanitizeHTML(output, config);
189
+ }
190
+
191
+ if (config.mode === 'encode' || config.mode === 'both') {
192
+ output = this.encodeHTML(output);
193
+ }
194
+
195
+ return output;
196
+ }
197
+
198
+ /**
199
+ * 清理HTML内容
200
+ * @param {String} html HTML内容
201
+ * @param {Object} config 配置项
202
+ * @returns {String} 清理后的内容
203
+ */
204
+ sanitizeHTML(html, config) {
205
+ // 简单的HTML标签清理实现
206
+ // 在生产环境中,可以使用更强大的库如DOMPurify
207
+ let clean = html;
208
+
209
+ // 移除所有脚本标签
210
+ clean = clean.replace(/<script[^>]*>.*?<\/script>/gi, '');
211
+
212
+ // 移除所有事件处理器
213
+ clean = clean.replace(/\s+on\w+\s*=\s*["\']?[^"\'>\s]+/gi, '');
214
+
215
+ // 移除危险的URL协议
216
+ clean = clean.replace(/(src|href|data|action)\s*=\s*["\']?(javascript|data|vbscript):/gi, '$1=');
217
+
218
+ return clean;
219
+ }
220
+
221
+ /**
222
+ * 编码HTML特殊字符
223
+ * @param {String} html HTML内容
224
+ * @returns {String} 编码后的内容
225
+ */
226
+ encodeHTML(html) {
227
+ return String(html)
228
+ .replace(/&/g, '&amp;')
229
+ .replace(/</g, '&lt;')
230
+ .replace(/>/g, '&gt;')
231
+ .replace(/"/g, '&quot;')
232
+ .replace(/'/g, '&#39;');
233
+ }
234
+
235
+ /**
236
+ * 添加安全响应头
237
+ * @param {Object} ctx Koa上下文
238
+ */
239
+ addSecurityHeaders(ctx) {
240
+ // X-XSS-Protection 头
241
+ ctx.set('X-XSS-Protection', '1; mode=block');
242
+
243
+ // Content-Security-Policy 头
244
+ ctx.set('Content-Security-Policy', "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;");
245
+ }
246
+ }
247
+
248
+ // 创建中间件实例
249
+ const middleware = new Middleware();
250
+
251
+ // 导出符合系统期望的函数
252
+ exports = module.exports = function(server, config) {
253
+ // 初始化中间件
254
+ const handlerFactory = middleware.init(config);
255
+ const middlewareHandler = handlerFactory();
256
+
257
+ // 直接使用server.use注册中间件
258
+ server.use(middlewareHandler);
259
+
260
+ // 记录中间件初始化信息
261
+ if ($.log && $.log.info) {
262
+ $.log.info(`XSS防护中间件已加载: 模式=${middleware.config.mode}, 过滤=${middleware.config.query ? 'query' : ''}${middleware.config.body ? ' body' : ''}${middleware.config.cookie ? ' cookie' : ''}`);
263
+ }
264
+
265
+ return server;
266
+ };
267
+
268
+ // 保留原始实例,以便其他方式调用
269
+ exports.middleware = middleware;
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "waf_xss",
3
+ "title": "XSS攻击防护中间件",
4
+ "type": "web_before",
5
+ "state": 1,
6
+ "sort": 10,
7
+ "desc": "提供全面的XSS攻击防护,包括输入过滤和安全响应头设置",
8
+ "config": {
9
+ "mode": "both",
10
+ "query": true,
11
+ "body": true,
12
+ "cookie": true,
13
+ "allowedTags": ["p", "span", "b", "i", "u", "strong", "em", "br"],
14
+ "allowedAttributes": ["id", "class", "style"],
15
+ "log": true,
16
+ "block": true
17
+ }
18
+ }
@@ -5,5 +5,6 @@
5
5
  "version": "1.0",
6
6
  "type": "web",
7
7
  "process_type": "common_before",
8
- "sort": 150
8
+ "sort": 150,
9
+ "state": 1
9
10
  }
@@ -5,5 +5,6 @@
5
5
  "version": "1.0",
6
6
  "type": "web",
7
7
  "process_type": "common_before",
8
- "sort": 70
8
+ "sort": 70,
9
+ "state": 1
9
10
  }
@@ -1,9 +1,10 @@
1
1
  {
2
- "name": "web_berofe",
2
+ "name": "web_before",
3
3
  "title": "web请求前",
4
4
  "description": "用于",
5
5
  "version": "1.0",
6
6
  "type": "web",
7
7
  "process_type": "common_before",
8
- "sort": 80
8
+ "sort": 80,
9
+ "state": 1
9
10
  }
@@ -5,5 +5,6 @@
5
5
  "version": "1.0",
6
6
  "type": "web",
7
7
  "process_type": "common_before",
8
- "sort": 90
8
+ "sort": 90,
9
+ "state": 1
9
10
  }
@@ -5,5 +5,6 @@
5
5
  "version": "1.0",
6
6
  "type": "web",
7
7
  "process_type": "common_before",
8
- "sort": 100
8
+ "sort": 100,
9
+ "state": 1
9
10
  }
@@ -5,5 +5,6 @@
5
5
  "version": "1.0",
6
6
  "type": "web",
7
7
  "process_type": "worker",
8
- "sort": 110
8
+ "sort": 110,
9
+ "state": 1
9
10
  }
@@ -5,5 +5,6 @@
5
5
  "version": "1.0",
6
6
  "type": "web",
7
7
  "process_type": "common_before",
8
- "sort": 120
8
+ "sort": 120,
9
+ "state": 1
9
10
  }
@@ -1,9 +1,10 @@
1
1
  {
2
- "name": "web_scoket",
3
- "title": "webscoket通讯",
2
+ "name": "web_socket",
3
+ "title": "websocket通讯",
4
4
  "description": "用于即时通讯",
5
5
  "version": "1.0",
6
6
  "type": "web",
7
7
  "process_type": "master",
8
- "sort": 130
8
+ "sort": 130,
9
+ "state": 1
9
10
  }
@@ -5,5 +5,6 @@
5
5
  "version": "1.0",
6
6
  "type": "web",
7
7
  "process_type": "worker",
8
- "sort": 140
8
+ "sort": 140,
9
+ "state": 1
9
10
  }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "mm_os",
3
- "version": "3.2.9",
3
+ "version": "3.3.1",
4
4
  "description": "这是超级美眉服务端框架,用于快速构建应用程序。",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "cross-env NODE_ENV=test node ./demo/index.js",
8
- "start": "node --trace-warnings ./demo/index.js",
9
- "dev": "cross-env NODE_ENV=development nodemon ./demo/index.js"
7
+ "test": "cross-env NODE_ENV=test node --no-warnings=DEP0060 ./demo/index.js",
8
+ "start": "node --trace-warnings --no-warnings=DEP0060 ./demo/index.js",
9
+ "dev": "cross-env NODE_ENV=development nodemon --no-warnings=DEP0060 ./demo/index.js"
10
10
  },
11
11
  "repository": {
12
12
  "type": "git",
@@ -29,30 +29,43 @@
29
29
  ],
30
30
  "author": "邱文武",
31
31
  "license": "ISC",
32
+ "files": [
33
+ "index.js",
34
+ "core/**",
35
+ "lib/**",
36
+ "middleware/**",
37
+ "conf.json",
38
+ "README.md",
39
+ "README.en.md",
40
+ "LICENSE"
41
+ ],
32
42
  "dependencies": {
33
- "compressing": "^1.10.3",
34
- "koa": "^3.0.0",
35
- "koa-body": "^6.0.1",
43
+ "compressing": "^2.0.0",
44
+ "js-beautify": "^1.15.4",
45
+ "koa": "^3.1.1",
46
+ "koa-body": "^7.0.0",
36
47
  "koa-compress": "^5.1.1",
37
48
  "koa-send": "^5.0.1",
38
49
  "koa-websocket": "^7.0.0",
39
50
  "mm_check": "^1.5.0",
51
+ "mm_email": "^1.0.5",
40
52
  "mm_excel": "^1.2.2",
41
- "mm_expand": "^1.8.3",
53
+ "mm_expand": "^1.8.8",
42
54
  "mm_html": "^1.1.7",
55
+ "mm_https": "^1.6.3",
43
56
  "mm_koa_proxy": "^1.0.1",
44
- "mm_logs": "^1.2.0",
45
- "mm_machine": "^2.0.1",
46
- "mm_mongodb": "^1.4.4",
57
+ "mm_logs": "^1.5.3",
58
+ "mm_machine": "^2.0.5",
59
+ "mm_mongodb": "^1.5.0",
47
60
  "mm_mqtt": "^1.0.7",
48
- "mm_mysql": "^2.0.2",
49
- "mm_redis": "^1.4.2",
61
+ "mm_mysql": "^2.1.0",
62
+ "mm_redis": "^2.0.2",
50
63
  "mm_ret": "^1.4.1",
51
64
  "mm_session": "^1.5.0",
52
65
  "mm_statics": "^1.5.1",
53
66
  "mm_tpl": "^2.4.3",
54
67
  "mm_xml": "^1.1.7",
55
68
  "mosca": "^2.8.3",
56
- "mqtt": "^5.13.1"
69
+ "mqtt": "^5.14.1"
57
70
  }
58
- }
71
+ }
@@ -1,32 +0,0 @@
1
- /**
2
- * web日志
3
- * @param {Object} server 服务
4
- * @param {Object} config 配置参数
5
- */
6
- module.exports = function(server, config) {
7
- /* 跨域限制(web防火墙) */
8
- server.use(async (ctx, next) => {
9
- try {
10
- var url = ctx.path + ctx.querystring;
11
- var body = "";
12
- if (ctx.request.body) {
13
- try {
14
- body = JSON.stringify(ctx.request.body);
15
- if (body.length > 1024) {
16
- body = body.substring(0, 1024) + "..."
17
- }
18
- } catch (jsonError) {
19
- body = "[无法序列化请求体]";
20
- $.log.error('日志中间件JSON序列化错误:', jsonError);
21
- }
22
- }
23
- $.log.http(`${ctx.method}\t${url}\t${body}`);
24
- await next();
25
- } catch (error) {
26
- $.log.error('log中间件错误:', error);
27
- // 确保请求可以继续处理
28
- await next();
29
- }
30
- });
31
- return server;
32
- };
@@ -1,9 +0,0 @@
1
- {
2
- "name": "web_log",
3
- "title": "web请求日志",
4
- "description": "用于记录http请求",
5
- "version": "1.0",
6
- "type": "web",
7
- "process_type": "common_before",
8
- "sort": 40
9
- }
@@ -1,143 +0,0 @@
1
- /**
2
- * 请求性能监控中间件
3
- * 记录请求响应时间,监控慢请求,提供性能数据收集
4
- */
5
- module.exports = function(server, config) {
6
- if(config.web && !config.web.performance) {
7
- return server;
8
- }
9
-
10
- // 默认配置
11
- const cg = Object.assign({
12
- slowThreshold: 1000, // 慢请求阈值,单位毫秒
13
- enableMetrics: true, // 是否启用性能指标收集
14
- ignorePaths: [] // 忽略监控的路径
15
- }, config);
16
-
17
- // 性能监控数据收集器
18
- const performanceData = {
19
- counters: new Map(), // 请求计数器
20
- responseTimes: new Map(), // 响应时间数据
21
- slowRequests: [] // 慢请求记录
22
- };
23
-
24
- // 全局性能监控对象
25
- $.performanceMonitor = {
26
- record: function(path, responseTime) {
27
- if (!cg.enableMetrics) return;
28
-
29
- // 更新请求计数
30
- if (!performanceData.counters.has(path)) {
31
- performanceData.counters.set(path, 1);
32
- performanceData.responseTimes.set(path, { sum: responseTime, count: 1, min: responseTime, max: responseTime });
33
- } else {
34
- performanceData.counters.set(path, performanceData.counters.get(path) + 1);
35
- const stats = performanceData.responseTimes.get(path);
36
- stats.sum += responseTime;
37
- stats.count += 1;
38
- stats.min = Math.min(stats.min, responseTime);
39
- stats.max = Math.max(stats.max, responseTime);
40
- }
41
-
42
- // 记录慢请求
43
- if (responseTime > cg.slowThreshold) {
44
- performanceData.slowRequests.push({
45
- path: path,
46
- time: new Date(),
47
- responseTime: responseTime
48
- });
49
- // 限制慢请求记录数量
50
- if (performanceData.slowRequests.length > 1000) {
51
- performanceData.slowRequests.shift();
52
- }
53
- }
54
- },
55
-
56
- getStats: function() {
57
- const result = {};
58
- performanceData.counters.forEach((count, path) => {
59
- const times = performanceData.responseTimes.get(path);
60
- result[path] = {
61
- count: count,
62
- avg: times.sum / times.count,
63
- min: times.min,
64
- max: times.max
65
- };
66
- });
67
- return result;
68
- },
69
-
70
- getSlowRequests: function(limit = 100) {
71
- return performanceData.slowRequests.slice(-limit);
72
- },
73
-
74
- reset: function() {
75
- performanceData.counters.clear();
76
- performanceData.responseTimes.clear();
77
- performanceData.slowRequests = [];
78
- }
79
- };
80
-
81
- // 性能监控中间件
82
- server.use(async (ctx, next) => {
83
- // 检查是否需要忽略此路径
84
- const shouldIgnore = cg.ignorePaths.some(pattern => {
85
- if (typeof pattern === 'string') {
86
- return ctx.path === pattern;
87
- } else if (pattern instanceof RegExp) {
88
- return pattern.test(ctx.path);
89
- }
90
- return false;
91
- });
92
-
93
- if (shouldIgnore) {
94
- await next();
95
- return;
96
- }
97
-
98
- const start = Date.now();
99
- const startTime = process.hrtime();
100
-
101
- try {
102
- await next();
103
- } finally {
104
- const ms = Date.now() - start;
105
- const [seconds, nanoseconds] = process.hrtime(startTime);
106
- const executionTime = seconds * 1000 + nanoseconds / 1000000;
107
-
108
- // 设置响应时间头
109
- ctx.set('X-Response-Time', `${ms}ms`);
110
-
111
- // 记录性能数据
112
- $.performanceMonitor.record(ctx.path, ms);
113
-
114
- // 慢请求报警
115
- if (ms > cg.slowThreshold) {
116
- const clientIP = ctx.headers['x-forwarded-for'] || ctx.ip;
117
- $.log.warn(`【慢请求】 ${ctx.method} ${ctx.path} - ${ms}ms - ${clientIP}`);
118
-
119
- // 如果是非常慢的请求(超过阈值的2倍),记录更多信息
120
- if (ms > cg.slowThreshold * 2) {
121
- const userAgent = ctx.headers['user-agent'] || 'unknown';
122
- let requestData = '';
123
- try {
124
- if (ctx.method !== 'GET' && ctx.request.body) {
125
- const bodyStr = JSON.stringify(ctx.request.body);
126
- // 限制记录的请求体大小
127
- requestData = bodyStr.length > 1024 ? bodyStr.substring(0, 1024) + '...' : bodyStr;
128
- }
129
- } catch (e) {
130
- requestData = '[无法序列化请求体]';
131
- }
132
-
133
- $.log.warn(`【慢请求详情】路径: ${ctx.path}, 方法: ${ctx.method}, 耗时: ${ms}ms, IP: ${clientIP}, UA: ${userAgent.substring(0, 200)}`);
134
- if (requestData) {
135
- $.log.warn(`【慢请求数据】${requestData}`);
136
- }
137
- }
138
- }
139
- }
140
- });
141
-
142
- return server;
143
- };
@@ -1,16 +0,0 @@
1
- {
2
- "name": "performance",
3
- "title": "性能监控",
4
- "description": "记录请求响应时间,监控慢请求,提供性能数据收集",
5
- "mode": "web",
6
- "priority": 10,
7
- "config": {
8
- "slowThreshold": 1000,
9
- "enableMetrics": true,
10
- "ignorePaths": [
11
- "/favicon.ico",
12
- "/robots.txt",
13
- "/static/"
14
- ]
15
- }
16
- }