whistle.mock-plugins 1.0.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.
Files changed (68) hide show
  1. package/API.md +247 -0
  2. package/README.md +91 -0
  3. package/app/cgi-bin/features.js +285 -0
  4. package/app/cgi-bin/index.js +31 -0
  5. package/app/cgi-bin/interfaces.js +409 -0
  6. package/app/cgi-bin/logs.js +151 -0
  7. package/app/cgi-bin/test-interface.js +152 -0
  8. package/app/public/index.html +1 -0
  9. package/app/public/js/index.js +2 -0
  10. package/app/public/js/index.js.LICENSE.txt +150 -0
  11. package/app/util/index.js +24 -0
  12. package/index.html +52 -0
  13. package/initialize.js +32 -0
  14. package/lib/dataManager.js +500 -0
  15. package/lib/index.js +3 -0
  16. package/lib/models.js +64 -0
  17. package/lib/routes/features.js +213 -0
  18. package/lib/routes/interfaces.js +304 -0
  19. package/lib/routes/proxy.js +239 -0
  20. package/lib/ruleManager.js +551 -0
  21. package/lib/rules-server.js +267 -0
  22. package/lib/server.js +805 -0
  23. package/lib/storage.js +16 -0
  24. package/lib/ui-server.js +68 -0
  25. package/package.json +51 -0
  26. package/public/bundle.js +2 -0
  27. package/public/bundle.js.LICENSE.txt +38 -0
  28. package/public/index.html +1 -0
  29. package/public/plugin.mock-plugin.html +14 -0
  30. package/rules.txt +22 -0
  31. package/test/rulesServer.test.js +120 -0
  32. package/ui/src/App.css +173 -0
  33. package/ui/src/App.js +24 -0
  34. package/ui/src/components/AppLayout.js +149 -0
  35. package/ui/src/components/FeatureCard.js +53 -0
  36. package/ui/src/components/Layout.js +94 -0
  37. package/ui/src/components/MainNav.js +42 -0
  38. package/ui/src/components/PageContainer.js +16 -0
  39. package/ui/src/index.html +11 -0
  40. package/ui/src/index.js +43 -0
  41. package/ui/src/pages/FileProxy.js +235 -0
  42. package/ui/src/pages/FilesPage.js +292 -0
  43. package/ui/src/pages/Home.js +128 -0
  44. package/ui/src/pages/HomePage.js +195 -0
  45. package/ui/src/pages/InterfaceManagement.js +829 -0
  46. package/ui/src/pages/InterfacePage.js +342 -0
  47. package/ui/src/pages/MockData.js +472 -0
  48. package/ui/src/pages/Settings.js +192 -0
  49. package/ui/src/pages/SettingsPage.js +165 -0
  50. package/ui/src/pages/UrlRedirect.js +225 -0
  51. package/ui/src/pages/Welcome.js +155 -0
  52. package/ui/src/services/api.js +186 -0
  53. package/ui/src/styles/app-layout.css +337 -0
  54. package/ui/src/styles/feature-card.css +172 -0
  55. package/ui/src/styles/file-proxy.css +301 -0
  56. package/ui/src/styles/global.css +224 -0
  57. package/ui/src/styles/home.css +123 -0
  58. package/ui/src/styles/index.css +419 -0
  59. package/ui/src/styles/interface-management.css +694 -0
  60. package/ui/src/styles/layout.css +159 -0
  61. package/ui/src/styles/logs-page.css +427 -0
  62. package/ui/src/styles/main-nav.css +52 -0
  63. package/ui/src/styles/mock-data.css +850 -0
  64. package/ui/src/styles/page-container.css +11 -0
  65. package/ui/src/styles/settings.css +208 -0
  66. package/ui/src/styles/url-redirect.css +279 -0
  67. package/ui/src/styles/welcome.css +282 -0
  68. package/webpack.config.js +49 -0
@@ -0,0 +1,213 @@
1
+ const express = require('express');
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+ const { v4: uuidv4 } = require('uuid');
5
+ const storage = require('../storage');
6
+
7
+ // 使用存储模块定义的数据目录
8
+ const DATA_DIR = storage.DATA_DIR;
9
+ const FEATURES_FILE = path.join(DATA_DIR, 'features.json');
10
+
11
+ // 确保数据目录存在
12
+ fs.ensureDirSync(DATA_DIR);
13
+
14
+ // 初始化数据文件
15
+ if (!fs.existsSync(FEATURES_FILE)) {
16
+ fs.writeJsonSync(FEATURES_FILE, []);
17
+ }
18
+
19
+ // 读取功能数据
20
+ const getFeatures = () => {
21
+ try {
22
+ return fs.readJsonSync(FEATURES_FILE);
23
+ } catch (err) {
24
+ console.error('读取功能数据失败:', err);
25
+ return [];
26
+ }
27
+ };
28
+
29
+ // 保存功能数据
30
+ const saveFeatures = (features) => {
31
+ try {
32
+ fs.writeJsonSync(FEATURES_FILE, features, { spaces: 2 });
33
+ return true;
34
+ } catch (err) {
35
+ console.error('保存功能数据失败:', err);
36
+ return false;
37
+ }
38
+ };
39
+
40
+ // 初始化路由
41
+ const initRouter = () => {
42
+ const router = express.Router();
43
+
44
+ // 解析 JSON 请求体
45
+ router.use(express.json({ limit: '100mb' }));
46
+
47
+ // 获取所有功能
48
+ router.get('/features', (req, res) => {
49
+ try {
50
+ const features = getFeatures();
51
+
52
+ // 获取每个功能的接口数量
53
+ const featuresWithCount = features.map(feature => {
54
+ try {
55
+ const interfacesFile = path.join(DATA_DIR, `interfaces-${feature.id}.json`);
56
+ const interfaces = fs.existsSync(interfacesFile)
57
+ ? fs.readJsonSync(interfacesFile)
58
+ : [];
59
+
60
+ return {
61
+ ...feature,
62
+ interfaceCount: interfaces.length
63
+ };
64
+ } catch (err) {
65
+ console.error(`获取功能 ${feature.id} 的接口数量失败:`, err);
66
+ return {
67
+ ...feature,
68
+ interfaceCount: 0
69
+ };
70
+ }
71
+ });
72
+
73
+ res.json(featuresWithCount);
74
+ } catch (err) {
75
+ console.error('获取功能列表失败:', err);
76
+ res.status(500).json({ error: '获取功能列表失败' });
77
+ }
78
+ });
79
+
80
+ // 获取单个功能
81
+ router.get('/features/:id', (req, res) => {
82
+ try {
83
+ const features = getFeatures();
84
+ const feature = features.find(f => f.id === req.params.id);
85
+
86
+ if (!feature) {
87
+ return res.status(404).json({ error: '功能不存在' });
88
+ }
89
+
90
+ res.json(feature);
91
+ } catch (err) {
92
+ console.error('获取功能详情失败:', err);
93
+ res.status(500).json({ error: '获取功能详情失败' });
94
+ }
95
+ });
96
+
97
+ // 创建功能
98
+ router.post('/features', (req, res) => {
99
+ try {
100
+ const features = getFeatures();
101
+ const newFeature = {
102
+ id: uuidv4(),
103
+ name: req.body.name,
104
+ description: req.body.description || '',
105
+ createdAt: new Date().toISOString(),
106
+ updatedAt: new Date().toISOString()
107
+ };
108
+
109
+ features.push(newFeature);
110
+
111
+ if (!saveFeatures(features)) {
112
+ return res.status(500).json({ error: '保存功能失败' });
113
+ }
114
+
115
+ // 初始化接口文件
116
+ const interfacesFile = path.join(DATA_DIR, `interfaces-${newFeature.id}.json`);
117
+ fs.writeJsonSync(interfacesFile, [], { spaces: 2 });
118
+
119
+ res.status(201).json(newFeature);
120
+ } catch (err) {
121
+ console.error('创建功能失败:', err);
122
+ res.status(500).json({ error: '创建功能失败' });
123
+ }
124
+ });
125
+
126
+ // 更新功能
127
+ router.put('/features/:id', (req, res) => {
128
+ try {
129
+ const features = getFeatures();
130
+ const index = features.findIndex(f => f.id === req.params.id);
131
+
132
+ if (index === -1) {
133
+ return res.status(404).json({ error: '功能不存在' });
134
+ }
135
+
136
+ const updatedFeature = {
137
+ ...features[index],
138
+ name: req.body.name || features[index].name,
139
+ description: req.body.description !== undefined ? req.body.description : features[index].description,
140
+ updatedAt: new Date().toISOString()
141
+ };
142
+
143
+ features[index] = updatedFeature;
144
+
145
+ if (!saveFeatures(features)) {
146
+ return res.status(500).json({ error: '更新功能失败' });
147
+ }
148
+
149
+ res.json(updatedFeature);
150
+ } catch (err) {
151
+ console.error('更新功能失败:', err);
152
+ res.status(500).json({ error: '更新功能失败' });
153
+ }
154
+ });
155
+
156
+ // 删除功能
157
+ router.delete('/features/:id', (req, res) => {
158
+ try {
159
+ const features = getFeatures();
160
+ const index = features.findIndex(f => f.id === req.params.id);
161
+
162
+ if (index === -1) {
163
+ return res.status(404).json({ error: '功能不存在' });
164
+ }
165
+
166
+ features.splice(index, 1);
167
+
168
+ if (!saveFeatures(features)) {
169
+ return res.status(500).json({ error: '删除功能失败' });
170
+ }
171
+
172
+ // 删除关联的接口文件
173
+ const interfacesFile = path.join(DATA_DIR, `interfaces-${req.params.id}.json`);
174
+ if (fs.existsSync(interfacesFile)) {
175
+ fs.removeSync(interfacesFile);
176
+ }
177
+
178
+ res.json({ success: true, message: '功能已删除' });
179
+ } catch (err) {
180
+ console.error('删除功能失败:', err);
181
+ res.status(500).json({ error: '删除功能失败' });
182
+ }
183
+ });
184
+
185
+ // 获取功能的所有接口
186
+ router.get('/features/:id/interfaces', (req, res) => {
187
+ try {
188
+ const features = getFeatures();
189
+ const feature = features.find(f => f.id === req.params.id);
190
+
191
+ if (!feature) {
192
+ return res.status(404).json({ error: '功能不存在' });
193
+ }
194
+
195
+ const interfacesFile = path.join(DATA_DIR, `interfaces-${req.params.id}.json`);
196
+
197
+ if (!fs.existsSync(interfacesFile)) {
198
+ fs.writeJsonSync(interfacesFile, [], { spaces: 2 });
199
+ return res.json([]);
200
+ }
201
+
202
+ const interfaces = fs.readJsonSync(interfacesFile);
203
+ res.json(interfaces);
204
+ } catch (err) {
205
+ console.error('获取功能接口列表失败:', err);
206
+ res.status(500).json({ error: '获取功能接口列表失败' });
207
+ }
208
+ });
209
+
210
+ return router;
211
+ };
212
+
213
+ module.exports = initRouter;
@@ -0,0 +1,304 @@
1
+ /**
2
+ * 接口路由处理程序
3
+ * 负责处理接口相关的 API 请求
4
+ */
5
+ const express = require('express');
6
+ const fs = require('fs-extra');
7
+ const path = require('path');
8
+ const { v4: uuidv4 } = require('uuid');
9
+ const Mock = require('mockjs');
10
+ const storage = require('../storage');
11
+
12
+ // 使用存储模块定义的数据目录
13
+ const DATA_DIR = storage.DATA_DIR;
14
+
15
+ // 确保数据目录存在
16
+ fs.ensureDirSync(DATA_DIR);
17
+
18
+ // 初始化路由
19
+ const initRouter = () => {
20
+ const router = express.Router();
21
+
22
+ // 解析 JSON 请求体
23
+ router.use(express.json({ limit: '100mb' }));
24
+
25
+ // 获取所有接口
26
+ router.get('/interfaces', (req, res) => {
27
+ try {
28
+ // 读取所有功能
29
+ const featuresFile = path.join(DATA_DIR, 'features.json');
30
+ if (!fs.existsSync(featuresFile)) {
31
+ return res.json([]);
32
+ }
33
+
34
+ const features = fs.readJsonSync(featuresFile);
35
+ let allInterfaces = [];
36
+
37
+ // 读取每个功能的接口
38
+ features.forEach(feature => {
39
+ const interfacesFile = path.join(DATA_DIR, `interfaces-${feature.id}.json`);
40
+ if (fs.existsSync(interfacesFile)) {
41
+ const interfaces = fs.readJsonSync(interfacesFile);
42
+ // 添加功能名称到接口数据中
43
+ const interfacesWithFeature = interfaces.map(item => ({
44
+ ...item,
45
+ featureName: feature.name
46
+ }));
47
+ allInterfaces = allInterfaces.concat(interfacesWithFeature);
48
+ }
49
+ });
50
+
51
+ res.json(allInterfaces);
52
+ } catch (err) {
53
+ console.error('获取接口列表失败:', err);
54
+ res.status(500).json({ error: '获取接口列表失败' });
55
+ }
56
+ });
57
+
58
+ // 获取单个接口
59
+ router.get('/interfaces/:id', (req, res) => {
60
+ try {
61
+ // 读取所有功能
62
+ const featuresFile = path.join(DATA_DIR, 'features.json');
63
+ if (!fs.existsSync(featuresFile)) {
64
+ return res.status(404).json({ error: '接口不存在' });
65
+ }
66
+
67
+ const features = fs.readJsonSync(featuresFile);
68
+ let foundInterface = null;
69
+
70
+ // 在每个功能的接口中查找
71
+ for (const feature of features) {
72
+ const interfacesFile = path.join(DATA_DIR, `interfaces-${feature.id}.json`);
73
+ if (fs.existsSync(interfacesFile)) {
74
+ const interfaces = fs.readJsonSync(interfacesFile);
75
+ const found = interfaces.find(item => item.id === req.params.id);
76
+ if (found) {
77
+ foundInterface = {
78
+ ...found,
79
+ featureName: feature.name
80
+ };
81
+ break;
82
+ }
83
+ }
84
+ }
85
+
86
+ if (!foundInterface) {
87
+ return res.status(404).json({ error: '接口不存在' });
88
+ }
89
+
90
+ res.json(foundInterface);
91
+ } catch (err) {
92
+ console.error('获取接口详情失败:', err);
93
+ res.status(500).json({ error: '获取接口详情失败' });
94
+ }
95
+ });
96
+
97
+ // 创建接口
98
+ router.post('/interfaces', (req, res) => {
99
+ try {
100
+ const { featureId, path: interfacePath, method, status, delay, description, headers, responseBody } = req.body;
101
+
102
+ if (!featureId || !interfacePath || !method) {
103
+ return res.status(400).json({ error: '功能ID、路径和请求方法为必填项' });
104
+ }
105
+
106
+ // 检查功能是否存在
107
+ const featuresFile = path.join(DATA_DIR, 'features.json');
108
+ if (!fs.existsSync(featuresFile)) {
109
+ return res.status(404).json({ error: '功能不存在' });
110
+ }
111
+
112
+ const features = fs.readJsonSync(featuresFile);
113
+ const feature = features.find(f => f.id === featureId);
114
+ if (!feature) {
115
+ return res.status(404).json({ error: '功能不存在' });
116
+ }
117
+
118
+ // 读取接口列表
119
+ const interfacesFile = path.join(DATA_DIR, `interfaces-${featureId}.json`);
120
+ const interfaces = fs.existsSync(interfacesFile)
121
+ ? fs.readJsonSync(interfacesFile)
122
+ : [];
123
+
124
+ // 创建新接口
125
+ const newInterface = {
126
+ id: uuidv4(),
127
+ featureId,
128
+ path: interfacePath,
129
+ method,
130
+ status: status || 200,
131
+ delay: delay || 0,
132
+ description: description || '',
133
+ headers: headers || { 'Content-Type': 'application/json' },
134
+ responseBody: responseBody || {},
135
+ createdAt: new Date().toISOString(),
136
+ updatedAt: new Date().toISOString()
137
+ };
138
+
139
+ interfaces.push(newInterface);
140
+
141
+ // 保存接口列表
142
+ fs.writeJsonSync(interfacesFile, interfaces, { spaces: 2 });
143
+
144
+ res.status(201).json(newInterface);
145
+ } catch (err) {
146
+ console.error('创建接口失败:', err);
147
+ res.status(500).json({ error: '创建接口失败' });
148
+ }
149
+ });
150
+
151
+ // 更新接口
152
+ router.put('/interfaces/:id', (req, res) => {
153
+ try {
154
+ const { featureId, path: interfacePath, method, status, delay, description, headers, responseBody } = req.body;
155
+
156
+ if (!featureId) {
157
+ return res.status(400).json({ error: '功能ID为必填项' });
158
+ }
159
+
160
+ // 检查功能是否存在
161
+ const featuresFile = path.join(DATA_DIR, 'features.json');
162
+ if (!fs.existsSync(featuresFile)) {
163
+ return res.status(404).json({ error: '功能不存在' });
164
+ }
165
+
166
+ const features = fs.readJsonSync(featuresFile);
167
+ const feature = features.find(f => f.id === featureId);
168
+ if (!feature) {
169
+ return res.status(404).json({ error: '功能不存在' });
170
+ }
171
+
172
+ // 读取接口列表
173
+ const interfacesFile = path.join(DATA_DIR, `interfaces-${featureId}.json`);
174
+ if (!fs.existsSync(interfacesFile)) {
175
+ return res.status(404).json({ error: '接口不存在' });
176
+ }
177
+
178
+ const interfaces = fs.readJsonSync(interfacesFile);
179
+ const index = interfaces.findIndex(item => item.id === req.params.id);
180
+
181
+ if (index === -1) {
182
+ return res.status(404).json({ error: '接口不存在' });
183
+ }
184
+
185
+ // 更新接口
186
+ const updatedInterface = {
187
+ ...interfaces[index],
188
+ featureId,
189
+ path: interfacePath !== undefined ? interfacePath : interfaces[index].path,
190
+ method: method !== undefined ? method : interfaces[index].method,
191
+ status: status !== undefined ? status : interfaces[index].status,
192
+ delay: delay !== undefined ? delay : interfaces[index].delay,
193
+ description: description !== undefined ? description : interfaces[index].description,
194
+ headers: headers !== undefined ? headers : interfaces[index].headers,
195
+ responseBody: responseBody !== undefined ? responseBody : interfaces[index].responseBody,
196
+ updatedAt: new Date().toISOString()
197
+ };
198
+
199
+ interfaces[index] = updatedInterface;
200
+
201
+ // 保存接口列表
202
+ fs.writeJsonSync(interfacesFile, interfaces, { spaces: 2 });
203
+
204
+ res.json(updatedInterface);
205
+ } catch (err) {
206
+ console.error('更新接口失败:', err);
207
+ res.status(500).json({ error: '更新接口失败' });
208
+ }
209
+ });
210
+
211
+ // 删除接口
212
+ router.delete('/interfaces/:id', (req, res) => {
213
+ try {
214
+ // 读取所有功能
215
+ const featuresFile = path.join(DATA_DIR, 'features.json');
216
+ if (!fs.existsSync(featuresFile)) {
217
+ return res.status(404).json({ error: '接口不存在' });
218
+ }
219
+
220
+ const features = fs.readJsonSync(featuresFile);
221
+ let found = false;
222
+
223
+ // 在每个功能的接口中查找并删除
224
+ for (const feature of features) {
225
+ const interfacesFile = path.join(DATA_DIR, `interfaces-${feature.id}.json`);
226
+ if (fs.existsSync(interfacesFile)) {
227
+ const interfaces = fs.readJsonSync(interfacesFile);
228
+ const index = interfaces.findIndex(item => item.id === req.params.id);
229
+
230
+ if (index !== -1) {
231
+ interfaces.splice(index, 1);
232
+ fs.writeJsonSync(interfacesFile, interfaces, { spaces: 2 });
233
+ found = true;
234
+ break;
235
+ }
236
+ }
237
+ }
238
+
239
+ if (!found) {
240
+ return res.status(404).json({ error: '接口不存在' });
241
+ }
242
+
243
+ res.json({ success: true, message: '接口已删除' });
244
+ } catch (err) {
245
+ console.error('删除接口失败:', err);
246
+ res.status(500).json({ error: '删除接口失败' });
247
+ }
248
+ });
249
+
250
+ // 测试接口
251
+ router.post('/interfaces/:id/test', (req, res) => {
252
+ try {
253
+ // 读取所有功能
254
+ const featuresFile = path.join(DATA_DIR, 'features.json');
255
+ if (!fs.existsSync(featuresFile)) {
256
+ return res.status(404).json({ error: '接口不存在' });
257
+ }
258
+
259
+ const features = fs.readJsonSync(featuresFile);
260
+ let foundInterface = null;
261
+
262
+ // 在每个功能的接口中查找
263
+ for (const feature of features) {
264
+ const interfacesFile = path.join(DATA_DIR, `interfaces-${feature.id}.json`);
265
+ if (fs.existsSync(interfacesFile)) {
266
+ const interfaces = fs.readJsonSync(interfacesFile);
267
+ const found = interfaces.find(item => item.id === req.params.id);
268
+ if (found) {
269
+ foundInterface = found;
270
+ break;
271
+ }
272
+ }
273
+ }
274
+
275
+ if (!foundInterface) {
276
+ return res.status(404).json({ error: '接口不存在' });
277
+ }
278
+
279
+ // 使用 Mock.js 生成响应数据
280
+ let responseBody = foundInterface.responseBody;
281
+ if (typeof responseBody === 'string') {
282
+ try {
283
+ responseBody = JSON.parse(responseBody);
284
+ } catch (e) {
285
+ return res.status(400).json({ error: '响应体JSON格式错误' });
286
+ }
287
+ }
288
+
289
+ const mockData = Mock.mock(responseBody);
290
+
291
+ res.json({
292
+ original: foundInterface.responseBody,
293
+ mocked: mockData
294
+ });
295
+ } catch (err) {
296
+ console.error('测试接口失败:', err);
297
+ res.status(500).json({ error: '测试接口失败' });
298
+ }
299
+ });
300
+
301
+ return router;
302
+ };
303
+
304
+ module.exports = initRouter;