whistle.mock-plugins 1.0.6 → 1.1.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.
@@ -75,6 +75,17 @@ module.exports = function(req, res) {
75
75
 
76
76
  const filteredInterfaces = interfacesData.interfaces.filter(item => item.featureId === featureId);
77
77
 
78
+ console.log('GET获取接口列表:', {
79
+ featureId,
80
+ totalCount: filteredInterfaces.length,
81
+ interfacesWithResponses: filteredInterfaces.map(item => ({
82
+ name: item.name,
83
+ hasResponses: !!item.responses,
84
+ responsesCount: item.responses ? item.responses.length : 0,
85
+ activeResponseId: item.activeResponseId
86
+ }))
87
+ });
88
+
78
89
  return res.json({
79
90
  code: 0,
80
91
  message: '成功',
@@ -175,6 +186,8 @@ module.exports = function(req, res) {
175
186
  urlPattern: interfaceData.urlPattern,
176
187
  proxyType: interfaceData.proxyType,
177
188
  responseContent: interfaceData.responseContent,
189
+ responses: interfaceData.responses || interfacesData.interfaces[index].responses || [],
190
+ activeResponseId: interfaceData.activeResponseId !== undefined ? interfaceData.activeResponseId : interfacesData.interfaces[index].activeResponseId,
178
191
  targetUrl: interfaceData.targetUrl,
179
192
  customHeaders: interfaceData.customHeaders || {},
180
193
  filePath: interfaceData.filePath,
@@ -184,6 +197,12 @@ module.exports = function(req, res) {
184
197
  active: interfaceData.active !== false
185
198
  };
186
199
 
200
+ console.log('更新接口,包含多响应数据:', {
201
+ name: interfacesData.interfaces[index].name,
202
+ responsesCount: interfacesData.interfaces[index].responses.length,
203
+ activeResponseId: interfacesData.interfaces[index].activeResponseId
204
+ });
205
+
187
206
  fs.writeJsonSync(interfacesFile, interfacesData, { spaces: 2 });
188
207
 
189
208
  return res.json({
@@ -200,6 +219,8 @@ module.exports = function(req, res) {
200
219
  urlPattern: interfaceData.urlPattern,
201
220
  proxyType: interfaceData.proxyType || 'response',
202
221
  responseContent: interfaceData.responseContent || '',
222
+ responses: interfaceData.responses || [],
223
+ activeResponseId: interfaceData.activeResponseId || null,
203
224
  targetUrl: interfaceData.targetUrl || '',
204
225
  customHeaders: interfaceData.customHeaders || {},
205
226
  filePath: interfaceData.filePath || '',
@@ -210,6 +231,12 @@ module.exports = function(req, res) {
210
231
  contentType: interfaceData.contentType || 'text/plain'
211
232
  };
212
233
 
234
+ console.log('创建新接口,包含多响应数据:', {
235
+ name: newInterface.name,
236
+ responsesCount: newInterface.responses.length,
237
+ activeResponseId: newInterface.activeResponseId
238
+ });
239
+
213
240
  // 确保interfaces数组存在
214
241
  if (!Array.isArray(interfacesData.interfaces)) {
215
242
  interfacesData.interfaces = [];
@@ -317,6 +344,8 @@ module.exports = function(req, res) {
317
344
  urlPattern: interfaceData.urlPattern || interfacesData.interfaces[index].urlPattern,
318
345
  proxyType: interfaceData.proxyType || interfacesData.interfaces[index].proxyType,
319
346
  responseContent: interfaceData.responseContent !== undefined ? interfaceData.responseContent : interfacesData.interfaces[index].responseContent,
347
+ responses: interfaceData.responses !== undefined ? interfaceData.responses : (interfacesData.interfaces[index].responses || []),
348
+ activeResponseId: interfaceData.activeResponseId !== undefined ? interfaceData.activeResponseId : interfacesData.interfaces[index].activeResponseId,
320
349
  targetUrl: interfaceData.targetUrl !== undefined ? interfaceData.targetUrl : interfacesData.interfaces[index].targetUrl,
321
350
  customHeaders: interfaceData.customHeaders !== undefined ? interfaceData.customHeaders : interfacesData.interfaces[index].customHeaders || {},
322
351
  filePath: interfaceData.filePath !== undefined ? interfaceData.filePath : interfacesData.interfaces[index].filePath,
@@ -328,6 +357,12 @@ module.exports = function(req, res) {
328
357
  updatedAt: new Date().toISOString()
329
358
  };
330
359
 
360
+ console.log('PUT更新接口,包含多响应数据:', {
361
+ name: updatedInterface.name,
362
+ responsesCount: updatedInterface.responses.length,
363
+ activeResponseId: updatedInterface.activeResponseId
364
+ });
365
+
331
366
  interfacesData.interfaces[index] = updatedInterface;
332
367
  fs.writeJsonSync(interfacesFile, interfacesData, { spaces: 2 });
333
368
 
@@ -3,6 +3,26 @@ const path = require('path');
3
3
  const Mock = require('mockjs');
4
4
  const url = require('url');
5
5
 
6
+ // 缓存控制
7
+ const CONFIG = {
8
+ // 缓存有效期(毫秒),默认 60 秒
9
+ CACHE_INTERVAL: process.env.MOCK_PLUGIN_CACHE_INTERVAL || 60000
10
+ };
11
+
12
+ // 缓存控制变量
13
+ let enabledInterfacesCache = null; // 仅缓存启用状态的接口
14
+ let cacheTime = 0;
15
+
16
+ // 获取文件修改时间
17
+ const getFileModTime = (filePath) => {
18
+ try {
19
+ const stats = fs.statSync(filePath);
20
+ return stats.mtimeMs;
21
+ } catch (err) {
22
+ return 0;
23
+ }
24
+ };
25
+
6
26
  // 生成随机数工具函数(与rules-server.js中保持一致)
7
27
  const generateRandomValue = (pattern) => {
8
28
  // 如果不是以@开头的模式,直接返回原值
@@ -11,13 +31,160 @@ const generateRandomValue = (pattern) => {
11
31
  }
12
32
 
13
33
  const formatPattern = pattern.substring(1); // 去掉@前缀
14
- // 为每个x生成一个随机字符(字母或数字)
34
+ // 使用单次正则替换优化性能
15
35
  return formatPattern.replace(/x/g, () => {
16
36
  const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
17
37
  return chars.charAt(Math.floor(Math.random() * chars.length));
18
38
  });
19
39
  };
20
40
 
41
+ // 正则表达式缓存
42
+ const regexCache = new Map();
43
+
44
+ // URL匹配函数,提取为纯函数减少重复代码
45
+ const isUrlMatchPattern = (url, pattern, proxyType) => {
46
+ if (!url || !pattern) return false;
47
+ console.log(`isUrlMatchPattern ${url} - ${pattern}`);
48
+ try {
49
+ // 对于url_redirect类型,需要完全匹配
50
+ if (proxyType === 'url_redirect') {
51
+ return url === pattern;
52
+ }
53
+
54
+ // 对于redirect类型,只要url以pattern开头即可命中(前缀匹配)
55
+ if (proxyType === 'redirect') {
56
+ return url.startsWith(pattern);
57
+ }
58
+
59
+ // 默认的匹配逻辑(用于response类型等)
60
+ // 精确匹配
61
+ if (pattern === url) {
62
+ return true;
63
+ }
64
+
65
+ // 正则表达式
66
+ if (pattern.startsWith('/') && pattern.length > 2 && pattern.endsWith('/')) {
67
+ // 使用缓存的正则表达式
68
+ const cacheKey = `regex:${pattern}`;
69
+ let regex = regexCache.get(cacheKey);
70
+
71
+ if (!regex) {
72
+ regex = new RegExp(pattern.slice(1, -1));
73
+ regexCache.set(cacheKey, regex);
74
+ }
75
+
76
+ return regex.test(url);
77
+ }
78
+
79
+ // 通配符模式
80
+ if (pattern.includes('*')) {
81
+ // 使用缓存的正则表达式
82
+ const cacheKey = `wildcard:${pattern}`;
83
+ let regex = regexCache.get(cacheKey);
84
+
85
+ if (!regex) {
86
+ const regexPattern = pattern
87
+ .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
88
+ .replace(/\*/g, '.*');
89
+ regex = new RegExp(`^${regexPattern}$`);
90
+ regexCache.set(cacheKey, regex);
91
+ }
92
+
93
+ return regex.test(url);
94
+ }
95
+
96
+ return false;
97
+ } catch (e) {
98
+ console.error('URL匹配检查失败:', e);
99
+ return false;
100
+ }
101
+ };
102
+
103
+ // 加载启用的接口数据,带缓存
104
+ const loadEnabledInterfaces = (dataDir, interfacesFile, featuresFile) => {
105
+ try {
106
+ const currentTime = Date.now();
107
+ const interfacesFileModTime = getFileModTime(interfacesFile);
108
+ const featuresFileModTime = getFileModTime(featuresFile);
109
+
110
+ // 取两个文件的最后修改时间的最大值
111
+ const lastModTime = Math.max(interfacesFileModTime, featuresFileModTime);
112
+
113
+ // 使用缓存
114
+ if (enabledInterfacesCache &&
115
+ currentTime - cacheTime < CONFIG.CACHE_INTERVAL &&
116
+ lastModTime <= cacheTime) {
117
+ return enabledInterfacesCache;
118
+ }
119
+
120
+ // 1. 读取并解析所有功能模块
121
+ let enabledFeatureIds = [];
122
+ if (fs.existsSync(featuresFile)) {
123
+ try {
124
+ const featuresData = fs.readJsonSync(featuresFile);
125
+ let features = [];
126
+
127
+ if (featuresData && Array.isArray(featuresData)) {
128
+ features = featuresData;
129
+ } else if (featuresData && featuresData.features && Array.isArray(featuresData.features)) {
130
+ features = featuresData.features;
131
+ }
132
+
133
+ // 过滤出启用状态的功能ID
134
+ enabledFeatureIds = features
135
+ .filter(feature => feature.active === true)
136
+ .map(feature => feature.id);
137
+ } catch (err) {
138
+ console.error('读取功能模块失败:', err);
139
+ }
140
+ }
141
+
142
+ // 2. 读取并解析所有接口
143
+ let enabledInterfaces = [];
144
+ if (fs.existsSync(interfacesFile)) {
145
+ try {
146
+ const interfacesData = fs.readJsonSync(interfacesFile);
147
+ let interfaces = [];
148
+
149
+ if (interfacesData && Array.isArray(interfacesData)) {
150
+ interfaces = interfacesData;
151
+ } else if (interfacesData && interfacesData.interfaces && Array.isArray(interfacesData.interfaces)) {
152
+ interfaces = interfacesData.interfaces;
153
+ }
154
+
155
+ // 过滤出启用状态的接口
156
+ enabledInterfaces = interfaces.filter(intf => {
157
+ // 首先检查接口本身是否启用
158
+ const isInterfaceEnabled = intf.active !== false; // 默认为true
159
+
160
+ if (!isInterfaceEnabled) {
161
+ return false;
162
+ }
163
+
164
+ // 如果接口有关联功能,检查功能是否启用
165
+ if (intf.featureId) {
166
+ return enabledFeatureIds.includes(intf.featureId);
167
+ }
168
+
169
+ // 如果接口没有关联功能,只要接口本身启用就可以
170
+ return true;
171
+ });
172
+ } catch (err) {
173
+ console.error('读取接口配置失败:', err);
174
+ }
175
+ }
176
+
177
+ // 更新缓存
178
+ enabledInterfacesCache = enabledInterfaces;
179
+ cacheTime = currentTime;
180
+
181
+ return enabledInterfaces;
182
+ } catch (err) {
183
+ console.error('加载启用接口失败:', err);
184
+ return enabledInterfacesCache || [];
185
+ }
186
+ };
187
+
21
188
  module.exports = async function(req, res) {
22
189
  const dataDir = this.dataDir;
23
190
  const interfacesFile = path.join(dataDir, 'interfaces.json');
@@ -45,27 +212,19 @@ module.exports = async function(req, res) {
45
212
  });
46
213
  }
47
214
 
48
- // 读取接口数据
49
- let interfacesData;
50
- try {
51
- interfacesData = fs.readJsonSync(interfacesFile);
52
- if (!interfacesData || !interfacesData.interfaces) {
53
- return res.status(404).json({
54
- code: 404,
55
- message: '未找到接口数据',
56
- data: null
57
- });
58
- }
59
- } catch (err) {
60
- return res.status(500).json({
61
- code: 500,
62
- message: '读取接口数据失败',
215
+ // 读取接口数据(使用缓存)
216
+ const interfaces = loadEnabledInterfaces(dataDir, interfacesFile, featuresFile);
217
+
218
+ if (!interfaces || interfaces.length === 0) {
219
+ return res.status(404).json({
220
+ code: 404,
221
+ message: '未找到接口数据',
63
222
  data: null
64
223
  });
65
224
  }
66
225
 
67
226
  // 查找指定接口
68
- const targetInterface = interfacesData.interfaces.find(item => item.id === interfaceId);
227
+ const targetInterface = interfaces.find(item => item.id === interfaceId);
69
228
 
70
229
  if (!targetInterface) {
71
230
  return res.status(404).json({
@@ -75,270 +234,109 @@ module.exports = async function(req, res) {
75
234
  });
76
235
  }
77
236
 
78
- // 检查接口是否已禁用
79
- if (targetInterface.active === false) {
80
- return res.status(400).json({
81
- code: 400,
82
- message: '接口已禁用,无法测试',
83
- data: null
84
- });
85
- }
86
-
87
- // 检查功能模块是否已禁用
88
- try {
89
- const featuresData = fs.readJsonSync(featuresFile);
90
-
91
- if (featuresData && featuresData.features) {
92
- const featureItem = featuresData.features.find(item => item.id === targetInterface.featureId);
93
-
94
- if (featureItem && featureItem.active === false) {
95
- return res.status(400).json({
96
- code: 400,
97
- message: '所属功能模块已禁用,接口无法使用',
98
- data: null
99
- });
100
- }
101
- }
102
- } catch (err) {
103
- // 忽略读取错误,继续处理
104
- }
237
+ // 检查接口URL是否与测试URL匹配
238
+ const pattern = targetInterface.pattern;
239
+ const proxyType = targetInterface.proxyType || 'response';
105
240
 
106
- const startTime = Date.now();
241
+ // 使用URL匹配检查
242
+ const isMatch = isUrlMatchPattern(url, pattern, proxyType);
107
243
 
108
- // 根据代理类型处理不同的响应
109
- let responseData;
244
+ // 测试接口只验证URL模式匹配,不验证HTTP方法
245
+ // 因为这里的req.method是测试请求的POST方法,与接口配置的HTTP方法无关
246
+ const matchResult = isMatch;
110
247
 
111
- // 检查 URL 是否匹配模式
112
- const isUrlMatchPattern = (url, pattern, proxyType) => {
113
- if (!url || !pattern) return false;
114
-
115
- try {
116
- // 对于url_redirect类型,需要完全匹配
117
- if (proxyType === 'url_redirect') {
118
- return url === pattern;
119
- }
120
-
121
- // 对于redirect类型,只要url以pattern开头即可命中(前缀匹配)
122
- if (proxyType === 'redirect') {
123
- return url.indexOf(pattern) === 0;
124
- }
125
-
126
- // 默认的匹配逻辑(用于response类型等)
127
- // 如果是正则表达式
128
- if (pattern.startsWith('/') && pattern.endsWith('/')) {
129
- const regex = new RegExp(pattern.slice(1, -1));
130
- return regex.test(url);
131
- }
132
-
133
- // 如果是通配符模式
134
- if (pattern.includes('*')) {
135
- const regexPattern = pattern
136
- .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
137
- .replace(/\*/g, '.*');
138
- const regex = new RegExp(`^${regexPattern}$`);
139
- return regex.test(url);
140
- }
141
-
142
- // 精确匹配
143
- return url === pattern;
144
- } catch (e) {
145
- console.error('URL匹配检查失败:', e);
146
- return false;
248
+ // 构建测试结果
249
+ const result = {
250
+ interfaceId: targetInterface.id,
251
+ interfaceName: targetInterface.name,
252
+ proxyType: targetInterface.proxyType,
253
+ pattern: targetInterface.pattern,
254
+ url: url,
255
+ isMatch: matchResult,
256
+ matchDetail: {
257
+ urlMatch: isMatch,
258
+ note: 'HTTP方法匹配在测试模式下不进行验证,仅验证URL模式匹配'
147
259
  }
148
260
  };
149
261
 
150
- // 处理不同的代理类型
151
- switch (targetInterface.proxyType) {
152
- case 'response':
153
- // 处理内容,支持 Mock.js 模板语法
154
- let responseBody = targetInterface.responseContent;
155
-
156
- try {
157
- // 如果响应内容是JSON,则尝试应用Mock.js模板
158
- if (targetInterface.contentType && targetInterface.contentType.includes('json')) {
159
- // 尝试解析JSON
160
- const jsonTemplate = JSON.parse(responseBody);
161
- // 使用Mock.js生成随机数据
162
- const mockData = Mock.mock(jsonTemplate);
163
- // 将结果转换回字符串
164
- responseBody = JSON.stringify(mockData);
165
- }
166
- } catch (err) {
167
- // 如果Mock失败,使用原始内容
168
- }
169
-
170
- // 模拟接口处理延迟
171
- const delay = targetInterface.responseDelay || 0;
172
- if (delay > 0) {
173
- await new Promise(resolve => setTimeout(resolve, delay));
174
- }
175
-
176
- const endTime = Date.now();
177
- const duration = endTime - startTime;
178
-
179
- // 模拟接口处理
180
- responseData = {
181
- statusCode: targetInterface.httpStatus || 200,
182
- contentType: targetInterface.contentType || 'application/json; charset=utf-8',
183
- responseBody: responseBody,
184
- mockInfo: {
185
- matchedPattern: targetInterface.urlPattern,
186
- requestMethod: targetInterface.httpMethod,
187
- delay: delay,
188
- duration: duration,
189
- timestamp: new Date().toISOString()
190
- }
191
- };
192
- break;
193
-
194
- case 'redirect':
195
- // 处理重定向
196
- const redirectTarget = targetInterface.targetUrl || '';
197
-
198
- // 验证目标URL是否有效
199
- if (!redirectTarget) {
200
- return res.status(400).json({
201
- code: 400,
202
- message: '重定向目标URL不能为空',
203
- data: null
204
- });
262
+ // 根据代理类型处理额外信息
263
+ if (matchResult) {
264
+ if (proxyType === 'response') {
265
+ // 添加响应数据(如果有)
266
+ if (targetInterface.response) {
267
+ result.responseData = targetInterface.response;
205
268
  }
206
-
207
- try {
208
- // 验证URL格式
209
- new URL(redirectTarget);
210
- } catch (err) {
211
- return res.status(400).json({
212
- code: 400,
213
- message: '重定向目标URL格式无效,必须包含http://或https://',
214
- data: null
215
- });
216
- }
217
-
218
- // 验证URL匹配(前缀匹配)
219
- if (url.indexOf(targetInterface.urlPattern) !== 0) {
220
- return res.status(400).json({
221
- code: 400,
222
- message: `测试URL不匹配重定向规则,URL必须以 ${targetInterface.urlPattern} 开头`,
223
- data: null
224
- });
225
- }
226
-
227
- // 处理自定义请求头,包括随机值
228
- const redirectHeaders = targetInterface.customHeaders || {};
229
- const processedHeaders = {};
230
-
231
- // 处理随机值,转换为实际值
232
- Object.entries(redirectHeaders).forEach(([key, value]) => {
233
- processedHeaders[key] = generateRandomValue(value);
234
- });
235
-
236
- const formattedRedirectHeaders = Object.entries(processedHeaders)
237
- .map(([key, value]) => `${key}: ${value}`)
238
- .join('\n');
239
-
240
- responseData = {
241
- statusCode: 302,
242
- contentType: 'application/json; charset=utf-8',
243
- proxyType: 'redirect',
244
- targetUrl: redirectTarget,
245
- customHeaders: processedHeaders,
246
- formattedHeaders: formattedRedirectHeaders,
247
- mockInfo: {
248
- matchedPattern: targetInterface.urlPattern,
249
- requestMethod: targetInterface.httpMethod,
250
- timestamp: new Date().toISOString()
269
+ } else if (proxyType === 'redirect' || proxyType === 'url_redirect') {
270
+ // 添加重定向信息(如果有)
271
+ if (targetInterface.targetUrl) {
272
+ // 处理动态生成的目标URL
273
+ const targetUrl = generateRandomValue(targetInterface.targetUrl);
274
+ result.redirectInfo = {
275
+ targetUrl: targetUrl,
276
+ targetFormatted: targetUrl
277
+ };
278
+
279
+ // 添加自定义请求头(如果有)
280
+ if (targetInterface.customHeaders) {
281
+ try {
282
+ result.redirectInfo.customHeaders = targetInterface.customHeaders;
283
+ } catch (e) {
284
+ console.error('解析自定义请求头失败:', e);
285
+ }
251
286
  }
252
- };
253
- break;
254
-
255
- case 'url_redirect':
256
- // 处理URL重定向
257
- const baseUrl = targetInterface.targetUrl || '';
258
-
259
- // 验证基础URL是否有效
260
- if (!baseUrl) {
261
- return res.status(400).json({
262
- code: 400,
263
- message: 'URL重定向目标URL不能为空',
264
- data: null
265
- });
266
- }
267
-
268
- try {
269
- // 验证URL格式
270
- new URL(baseUrl);
271
- } catch (err) {
272
- return res.status(400).json({
273
- code: 400,
274
- message: 'URL重定向目标URL格式无效,必须包含http://或https://',
275
- data: null
276
- });
277
287
  }
278
-
279
- // 验证URL完全匹配
280
- if (url !== targetInterface.urlPattern) {
281
- return res.status(400).json({
282
- code: 400,
283
- message: `测试URL不匹配URL重定向规则,URL必须完全匹配 ${targetInterface.urlPattern}`,
284
- data: null
285
- });
286
- }
287
-
288
- // 处理自定义请求头
289
- const urlRedirectHeaders = targetInterface.customHeaders || {};
290
- const processedUrlRedirectHeaders = {};
291
-
292
- // 处理随机值,转换为实际值
293
- Object.entries(urlRedirectHeaders).forEach(([key, value]) => {
294
- processedUrlRedirectHeaders[key] = generateRandomValue(value);
295
- });
296
-
297
- const formattedUrlRedirectHeaders = Object.entries(processedUrlRedirectHeaders)
298
- .map(([key, value]) => `${key}: ${value}`)
299
- .join('\n');
300
-
301
- responseData = {
302
- statusCode: 302,
303
- contentType: 'application/json; charset=utf-8',
304
- proxyType: 'url_redirect',
305
- targetUrl: baseUrl,
306
- customHeaders: processedUrlRedirectHeaders,
307
- formattedHeaders: formattedUrlRedirectHeaders,
308
- mockInfo: {
309
- matchedPattern: targetInterface.urlPattern,
310
- requestMethod: targetInterface.httpMethod,
311
- timestamp: new Date().toISOString()
312
- }
313
- };
314
- break;
315
-
316
- default:
317
- // 不支持的代理类型
318
- return res.status(400).json({
319
- code: 400,
320
- message: `不支持的代理类型: ${targetInterface.proxyType || 'undefined'}`,
321
- data: null
322
- });
288
+ }
323
289
  }
324
290
 
291
+ // 返回测试结果
325
292
  return res.json({
326
- code: 0,
327
- message: '成功',
328
- data: responseData
293
+ code: 200,
294
+ message: matchResult ? '接口匹配成功' : '接口匹配失败',
295
+ data: result
329
296
  });
330
- } else {
331
- // 不支持的请求方法
332
- return res.status(405).json({
333
- code: 405,
334
- message: '不支持的请求方法',
297
+ }
298
+
299
+ // 处理缓存刷新请求
300
+ if (req.path === '/_flush_cache') {
301
+ // 清空缓存
302
+ enabledInterfacesCache = null;
303
+ cacheTime = 0;
304
+
305
+ return res.json({
306
+ code: 200,
307
+ message: '缓存已刷新',
335
308
  data: null
336
309
  });
337
310
  }
311
+
312
+ // 处理缓存状态请求
313
+ if (req.path === '/_cache_status') {
314
+ return res.json({
315
+ code: 200,
316
+ message: '缓存状态',
317
+ data: {
318
+ enabledInterfaces: {
319
+ cached: !!enabledInterfacesCache,
320
+ lastUpdate: cacheTime,
321
+ count: enabledInterfacesCache ? enabledInterfacesCache.length : 0
322
+ },
323
+ cacheInterval: CONFIG.CACHE_INTERVAL
324
+ }
325
+ });
326
+ }
327
+
328
+ // 其他请求返回405
329
+ return res.status(405).json({
330
+ code: 405,
331
+ message: '方法不允许',
332
+ data: null
333
+ });
338
334
  } catch (err) {
335
+ console.error('测试接口处理错误:', err);
336
+
339
337
  return res.status(500).json({
340
338
  code: 500,
341
- message: '服务器内部错误: ' + err.message,
339
+ message: '服务器内部错误: ' + (err.message || err),
342
340
  data: null
343
341
  });
344
342
  }