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,342 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Card, Table, Button, Space, Select, Tabs, Form, Input, Modal, Divider, message } from 'antd';
3
+ import { PlusOutlined, EditOutlined, DeleteOutlined, SaveOutlined, ExclamationCircleOutlined, CopyOutlined, PlayCircleOutlined } from '@ant-design/icons';
4
+ import { FeatureAPI, InterfaceAPI } from '../services/api';
5
+
6
+ const { Option } = Select;
7
+ const { TabPane } = Tabs;
8
+ const { confirm } = Modal;
9
+ const { TextArea } = Input;
10
+
11
+ const InterfacePage = () => {
12
+ const [features, setFeatures] = useState([]);
13
+ const [interfaces, setInterfaces] = useState([]);
14
+ const [loading, setLoading] = useState(false);
15
+ const [selectedFeature, setSelectedFeature] = useState(null);
16
+ const [modalVisible, setModalVisible] = useState(false);
17
+ const [currentInterface, setCurrentInterface] = useState(null);
18
+ const [form] = Form.useForm();
19
+
20
+ useEffect(() => {
21
+ fetchFeatures();
22
+ }, []);
23
+
24
+ useEffect(() => {
25
+ if (selectedFeature) {
26
+ fetchInterfaces(selectedFeature);
27
+ }
28
+ }, [selectedFeature]);
29
+
30
+ const fetchFeatures = async () => {
31
+ setLoading(true);
32
+ try {
33
+ const data = await FeatureAPI.getAllFeatures();
34
+ setFeatures(data);
35
+ if (data.length > 0 && !selectedFeature) {
36
+ setSelectedFeature(data[0].id);
37
+ }
38
+ } catch (error) {
39
+ console.error("获取功能列表失败:", error);
40
+ } finally {
41
+ setLoading(false);
42
+ }
43
+ };
44
+
45
+ const fetchInterfaces = async (featureId) => {
46
+ setLoading(true);
47
+ try {
48
+ const data = await FeatureAPI.getFeatureInterfaces(featureId);
49
+ setInterfaces(data);
50
+ } catch (error) {
51
+ console.error("获取接口列表失败:", error);
52
+ } finally {
53
+ setLoading(false);
54
+ }
55
+ };
56
+
57
+ const handleFeatureChange = (value) => {
58
+ setSelectedFeature(value);
59
+ };
60
+
61
+ const handleAdd = () => {
62
+ form.resetFields();
63
+ form.setFieldsValue({
64
+ featureId: selectedFeature,
65
+ method: 'GET',
66
+ status: 200,
67
+ delay: 0,
68
+ headers: '{\n "Content-Type": "application/json"\n}',
69
+ responseBody: '{\n "code": 0,\n "message": "success",\n "data": {}\n}'
70
+ });
71
+ setCurrentInterface(null);
72
+ setModalVisible(true);
73
+ };
74
+
75
+ const handleEdit = (record) => {
76
+ form.setFieldsValue({
77
+ ...record,
78
+ headers: typeof record.headers === 'object'
79
+ ? JSON.stringify(record.headers, null, 2)
80
+ : record.headers || '{}',
81
+ responseBody: typeof record.responseBody === 'object'
82
+ ? JSON.stringify(record.responseBody, null, 2)
83
+ : record.responseBody || '{}'
84
+ });
85
+ setCurrentInterface(record);
86
+ setModalVisible(true);
87
+ };
88
+
89
+ const handleDelete = (record) => {
90
+ confirm({
91
+ title: '确认删除',
92
+ icon: <ExclamationCircleOutlined />,
93
+ content: `确定要删除接口 "${record.path}" 吗?`,
94
+ okText: '确认',
95
+ cancelText: '取消',
96
+ onOk: async () => {
97
+ try {
98
+ await InterfaceAPI.deleteInterface(record.id);
99
+ fetchInterfaces(selectedFeature);
100
+ } catch (error) {
101
+ console.error("删除接口失败:", error);
102
+ }
103
+ },
104
+ });
105
+ };
106
+
107
+ const handleCancel = () => {
108
+ setModalVisible(false);
109
+ };
110
+
111
+ const handleOk = async () => {
112
+ try {
113
+ const values = await form.validateFields();
114
+
115
+ try {
116
+ // 尝试解析 JSON
117
+ if (values.headers) {
118
+ values.headers = JSON.parse(values.headers);
119
+ }
120
+ if (values.responseBody) {
121
+ values.responseBody = JSON.parse(values.responseBody);
122
+ }
123
+ } catch (e) {
124
+ message.error('JSON 格式错误,请检查');
125
+ return;
126
+ }
127
+
128
+ if (currentInterface) {
129
+ await InterfaceAPI.updateInterface(currentInterface.id, values);
130
+ } else {
131
+ await InterfaceAPI.createInterface(values);
132
+ }
133
+
134
+ setModalVisible(false);
135
+ fetchInterfaces(selectedFeature);
136
+ } catch (error) {
137
+ console.error("保存接口失败:", error);
138
+ }
139
+ };
140
+
141
+ const handleTest = async (record) => {
142
+ try {
143
+ const result = await InterfaceAPI.testInterface(record.id);
144
+ Modal.info({
145
+ title: '测试结果',
146
+ width: 800,
147
+ content: (
148
+ <div>
149
+ <pre style={{ maxHeight: '500px', overflow: 'auto' }}>
150
+ {JSON.stringify(result, null, 2)}
151
+ </pre>
152
+ </div>
153
+ ),
154
+ okText: '关闭'
155
+ });
156
+ } catch (error) {
157
+ console.error("测试失败:", error);
158
+ message.error('测试失败: ' + error.message);
159
+ }
160
+ };
161
+
162
+ const columns = [
163
+ {
164
+ title: 'URL 路径',
165
+ dataIndex: 'path',
166
+ key: 'path',
167
+ ellipsis: true,
168
+ },
169
+ {
170
+ title: '请求方法',
171
+ dataIndex: 'method',
172
+ key: 'method',
173
+ width: 100,
174
+ },
175
+ {
176
+ title: '状态码',
177
+ dataIndex: 'status',
178
+ key: 'status',
179
+ width: 100,
180
+ },
181
+ {
182
+ title: '延迟(ms)',
183
+ dataIndex: 'delay',
184
+ key: 'delay',
185
+ width: 100,
186
+ },
187
+ {
188
+ title: '描述',
189
+ dataIndex: 'description',
190
+ key: 'description',
191
+ ellipsis: true,
192
+ },
193
+ {
194
+ title: '操作',
195
+ key: 'action',
196
+ width: 240,
197
+ render: (_, record) => (
198
+ <Space size="small">
199
+ <Button type="text" icon={<EditOutlined />} onClick={() => handleEdit(record)}>
200
+ 编辑
201
+ </Button>
202
+ <Button type="text" icon={<PlayCircleOutlined />} onClick={() => handleTest(record)}>
203
+ 测试
204
+ </Button>
205
+ <Button type="text" danger icon={<DeleteOutlined />} onClick={() => handleDelete(record)}>
206
+ 删除
207
+ </Button>
208
+ </Space>
209
+ ),
210
+ },
211
+ ];
212
+
213
+ return (
214
+ <div className="interface-page">
215
+ <Card
216
+ title="接口管理"
217
+ extra={
218
+ <Space>
219
+ <span>功能:</span>
220
+ <Select
221
+ style={{ width: 200 }}
222
+ value={selectedFeature}
223
+ onChange={handleFeatureChange}
224
+ loading={loading}
225
+ >
226
+ {features.map(feature => (
227
+ <Option key={feature.id} value={feature.id}>{feature.name}</Option>
228
+ ))}
229
+ </Select>
230
+ <Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
231
+ 添加接口
232
+ </Button>
233
+ </Space>
234
+ }
235
+ >
236
+ <Table
237
+ columns={columns}
238
+ dataSource={interfaces}
239
+ rowKey="id"
240
+ loading={loading}
241
+ />
242
+ </Card>
243
+
244
+ <Modal
245
+ title={currentInterface ? "编辑接口" : "添加接口"}
246
+ open={modalVisible}
247
+ onOk={handleOk}
248
+ onCancel={handleCancel}
249
+ width={800}
250
+ okText="保存"
251
+ cancelText="取消"
252
+ >
253
+ <Form
254
+ form={form}
255
+ layout="vertical"
256
+ >
257
+ <Form.Item
258
+ name="featureId"
259
+ label="所属功能"
260
+ rules={[{ required: true, message: '请选择所属功能!' }]}
261
+ >
262
+ <Select>
263
+ {features.map(feature => (
264
+ <Option key={feature.id} value={feature.id}>{feature.name}</Option>
265
+ ))}
266
+ </Select>
267
+ </Form.Item>
268
+
269
+ <Form.Item
270
+ name="path"
271
+ label="URL 路径"
272
+ rules={[{ required: true, message: '请输入 URL 路径!' }]}
273
+ >
274
+ <Input placeholder="/api/your-path" />
275
+ </Form.Item>
276
+
277
+ <div style={{ display: 'flex', gap: '16px' }}>
278
+ <Form.Item
279
+ name="method"
280
+ label="请求方法"
281
+ rules={[{ required: true, message: '请选择请求方法!' }]}
282
+ style={{ flex: 1 }}
283
+ >
284
+ <Select>
285
+ <Option value="GET">GET</Option>
286
+ <Option value="POST">POST</Option>
287
+ <Option value="PUT">PUT</Option>
288
+ <Option value="DELETE">DELETE</Option>
289
+ <Option value="PATCH">PATCH</Option>
290
+ </Select>
291
+ </Form.Item>
292
+
293
+ <Form.Item
294
+ name="status"
295
+ label="状态码"
296
+ rules={[{ required: true, message: '请输入状态码!' }]}
297
+ style={{ flex: 1 }}
298
+ >
299
+ <Input type="number" placeholder="200" />
300
+ </Form.Item>
301
+
302
+ <Form.Item
303
+ name="delay"
304
+ label="延迟(ms)"
305
+ rules={[{ required: true, message: '请输入延迟时间!' }]}
306
+ style={{ flex: 1 }}
307
+ >
308
+ <Input type="number" placeholder="0" />
309
+ </Form.Item>
310
+ </div>
311
+
312
+ <Form.Item
313
+ name="description"
314
+ label="描述"
315
+ >
316
+ <Input placeholder="接口描述" />
317
+ </Form.Item>
318
+
319
+ <Tabs defaultActiveKey="1">
320
+ <TabPane tab="响应体" key="1">
321
+ <Form.Item
322
+ name="responseBody"
323
+ rules={[{ required: true, message: '请输入响应体!' }]}
324
+ >
325
+ <TextArea rows={10} placeholder="响应JSON数据" />
326
+ </Form.Item>
327
+ </TabPane>
328
+ <TabPane tab="响应头" key="2">
329
+ <Form.Item
330
+ name="headers"
331
+ >
332
+ <TextArea rows={10} placeholder="响应头JSON数据" />
333
+ </Form.Item>
334
+ </TabPane>
335
+ </Tabs>
336
+ </Form>
337
+ </Modal>
338
+ </div>
339
+ );
340
+ };
341
+
342
+ export default InterfacePage;