whistle.mock-plugins 1.3.1 → 1.4.0-beta.2

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 (70) hide show
  1. package/VERSION.md +29 -0
  2. package/app/cgi-bin/interfaces.js +2 -0
  3. package/app/cgi-bin/settings.js +1 -0
  4. package/app/cgi-bin/stats.js +22 -12
  5. package/app/cgi-bin/webdav.js +56 -0
  6. package/app/public/index.html +1 -1
  7. package/app/public/js/11.index.js +2 -0
  8. package/app/public/js/11.index.js.LICENSE.txt +5 -0
  9. package/app/public/js/704.index.js +2 -0
  10. package/app/public/js/704.index.js.LICENSE.txt +1 -0
  11. package/app/public/js/798.index.js +2 -0
  12. package/app/public/js/798.index.js.LICENSE.txt +53 -0
  13. package/app/public/js/807.index.js +2 -0
  14. package/app/public/js/807.index.js.LICENSE.txt +1 -0
  15. package/app/public/js/968.index.js +2 -0
  16. package/app/public/js/968.index.js.LICENSE.txt +12 -0
  17. package/app/public/js/index.js +1 -1
  18. package/app/public/js/index.js.LICENSE.txt +0 -173
  19. package/lib/routes/settings.js +1 -0
  20. package/lib/webdav-sync.js +806 -0
  21. package/package.json +9 -2
  22. package/API.md +0 -247
  23. package/COLUMN_RESIZE_IMPLEMENTATION.md +0 -162
  24. package/FEATURE_ENHANCEMENT.md +0 -157
  25. package/TEST_GUIDE.md +0 -156
  26. package/data/features.json +0 -1
  27. package/data/interfaces.json +0 -1
  28. package/index.html +0 -52
  29. package/initialize.js +0 -32
  30. package/public/bundle.js +0 -2
  31. package/public/bundle.js.LICENSE.txt +0 -38
  32. package/public/index.html +0 -1
  33. package/public/plugin.mock-plugin.html +0 -14
  34. package/rules.txt +0 -22
  35. package/ui/src/App.css +0 -173
  36. package/ui/src/components/AppLayout.js +0 -407
  37. package/ui/src/components/FeatureCard.js +0 -58
  38. package/ui/src/components/KeyboardShortcutsHelp.js +0 -58
  39. package/ui/src/components/Layout.js +0 -94
  40. package/ui/src/components/MainNav.js +0 -42
  41. package/ui/src/components/PageContainer.js +0 -16
  42. package/ui/src/components/VersionModal.js +0 -99
  43. package/ui/src/components/interface-management/DataManagementDrawer.js +0 -115
  44. package/ui/src/components/interface-management/DataManagementModal.js +0 -110
  45. package/ui/src/components/interface-management/InterfaceTestModal.js +0 -313
  46. package/ui/src/components/interface-management/PreviewModal.js +0 -48
  47. package/ui/src/components/interface-management/README.md +0 -184
  48. package/ui/src/components/interface-management/RealResponseRecorder.js +0 -165
  49. package/ui/src/components/interface-management/ResponseContentEditor.js +0 -645
  50. package/ui/src/components/interface-management/constants.js +0 -36
  51. package/ui/src/components/interface-management/index.js +0 -7
  52. package/ui/src/components/interface-management/utils.js +0 -103
  53. package/ui/src/hooks/useKeyboardShortcuts.js +0 -99
  54. package/ui/src/index.html +0 -14
  55. package/ui/src/index.js +0 -50
  56. package/ui/src/pages/InterfaceManagementV2.js +0 -2351
  57. package/ui/src/pages/MockDataV2.js +0 -1089
  58. package/ui/src/pages/Settings.js +0 -299
  59. package/ui/src/pages/Sniffer.js +0 -521
  60. package/ui/src/pages/Stats.js +0 -288
  61. package/ui/src/services/del_api.js +0 -192
  62. package/ui/src/styles/app-layout.css +0 -373
  63. package/ui/src/styles/cyber-theme.css +0 -1234
  64. package/ui/src/styles/earth-theme.css +0 -618
  65. package/ui/src/styles/global.css +0 -224
  66. package/ui/src/styles/interface-management.css +0 -1321
  67. package/ui/src/styles/mock-data.css +0 -1185
  68. package/ui/src/styles/settings.css +0 -252
  69. package/ui/src/styles/version-modal.css +0 -116
  70. package/webpack.config.js +0 -54
@@ -1,313 +0,0 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { Modal, Button, Form, Input, Alert, Space } from 'antd';
3
- import { ExclamationCircleOutlined } from '@ant-design/icons';
4
- import { proxyTypes } from './constants';
5
- import { isUrlMatchPattern, refreshCacheAfterUpdate } from './utils';
6
- import axios from 'axios';
7
-
8
- const InterfaceTestModal = ({
9
- visible,
10
- onCancel,
11
- editingInterface
12
- }) => {
13
- const [testForm] = Form.useForm();
14
- const [testResult, setTestResult] = useState(null);
15
- const [testLoading, setTestLoading] = useState(false);
16
-
17
- // 清理函数:模态框关闭时重置状态
18
- useEffect(() => {
19
- if (!visible) {
20
- setTestResult(null);
21
- setTestLoading(false);
22
- testForm.resetFields();
23
- }
24
- }, [visible, testForm]);
25
-
26
- const handleTestSubmit = async () => {
27
- try {
28
- const values = await testForm.validateFields();
29
- const { testUrl } = values;
30
-
31
- if (!testUrl) {
32
- return;
33
- }
34
-
35
- setTestLoading(true);
36
-
37
- try {
38
- // 测试前刷新缓存,确保使用最新的接口定义
39
- await refreshCacheAfterUpdate();
40
- } catch (error) {
41
- // 即使刷新缓存失败,也继续测试
42
- console.warn('刷新缓存失败,将使用现有的缓存数据:', error);
43
- }
44
-
45
- try {
46
- // 验证测试 URL 是否匹配当前接口的匹配规则
47
- if (!isUrlMatchPattern(testUrl, editingInterface.urlPattern, editingInterface.proxyType)) {
48
- let errorMessage = '测试URL与接口匹配规则不匹配';
49
-
50
- // 根据不同的代理类型提供更具体的错误信息
51
- if (editingInterface.proxyType === 'url_redirect') {
52
- errorMessage = `测试URL必须完全匹配 ${editingInterface.urlPattern}`;
53
- } else if (editingInterface.proxyType === 'redirect') {
54
- errorMessage = `测试URL必须以 ${editingInterface.urlPattern} 开头`;
55
- }
56
-
57
- setTestResult({
58
- success: false,
59
- error: errorMessage
60
- });
61
- setTestLoading(false);
62
- return;
63
- }
64
-
65
- // 调用实际测试接口
66
- const response = await axios.post(`/cgi-bin/test-interface`, {
67
- url: testUrl,
68
- interfaceId: editingInterface.id
69
- });
70
-
71
- if (response.data && response.data.code === 0) {
72
- // 设置测试结果
73
- setTestResult({
74
- success: true,
75
- ...response.data.data,
76
- matchedRule: editingInterface.urlPattern,
77
- httpMethod: editingInterface.httpMethod,
78
- requestUrl: testUrl,
79
- mockInfo: response.data.data.mockInfo
80
- });
81
- } else {
82
- throw new Error(response.data?.message || '测试响应格式不正确');
83
- }
84
- } catch (error) {
85
- setTestResult({
86
- success: false,
87
- error: error.response?.data?.message || error.message || '测试失败'
88
- });
89
- }
90
-
91
- setTestLoading(false);
92
- } catch (error) {
93
- if (error.errorFields) {
94
- return; // 表单验证错误
95
- }
96
- console.error('测试失败:', error);
97
- setTestLoading(false);
98
- }
99
- };
100
-
101
- const formatResponseContent = (content, contentType) => {
102
- if (contentType && contentType.includes('json')) {
103
- try {
104
- const parsed = JSON.parse(content);
105
- return JSON.stringify(parsed, null, 2);
106
- } catch (e) {
107
- return content;
108
- }
109
- }
110
- return content;
111
- };
112
-
113
- return (
114
- <Modal
115
- title="测试接口"
116
- open={visible}
117
- onCancel={onCancel}
118
- footer={[
119
- <Button key="back" onClick={onCancel}>
120
- 关闭
121
- </Button>,
122
- <Button key="submit" type="primary" loading={testLoading} onClick={handleTestSubmit}>
123
- 测试
124
- </Button>
125
- ]}
126
- width={800}
127
- >
128
- {editingInterface && (
129
- <div className="test-interface-container">
130
- <Alert
131
- message="URL匹配规则说明"
132
- description={
133
- <div>
134
- <p>当前接口匹配规则:<code>{editingInterface.urlPattern}</code></p>
135
- <p>代理类型:{
136
- proxyTypes.find(item => item.value === editingInterface.proxyType)?.label ||
137
- editingInterface.proxyType || '模拟响应'
138
- }</p>
139
-
140
- {editingInterface.proxyType === 'url_redirect' && (
141
- <div>
142
- <p><b>URL重定向模式规则:</b></p>
143
- <ul>
144
- <li>URL匹配规则必须是完整URL(包括http://或https://)</li>
145
- <li>需要完全匹配URL路径,测试URL必须与匹配规则<b>完全一致</b></li>
146
- <li>命中后将直接跳转到目标URL: {editingInterface.targetUrl}</li>
147
- </ul>
148
- </div>
149
- )}
150
-
151
- {editingInterface.proxyType === 'redirect' && (
152
- <div>
153
- <p><b>重定向模式规则:</b></p>
154
- <ul>
155
- <li>URL匹配规则必须以http://或https://开头</li>
156
- <li>测试URL必须以匹配规则开头(<b>前缀匹配</b>)</li>
157
- <li>命中后将直接跳转到目标URL: {editingInterface.targetUrl}</li>
158
- </ul>
159
- </div>
160
- )}
161
-
162
- {editingInterface.proxyType === 'response' && (
163
- <div>
164
- <p>在Whistle规则中,您需要配置完整URL或域名指向whistle.mock-plugins://,然后插件会根据接口的匹配规则处理请求。</p>
165
- <p>插件支持以下匹配方式:</p>
166
- <ul>
167
- <li>精确匹配:完全匹配URL路径部分</li>
168
- <li>通配符匹配:使用*代表任意字符(例如:/api/users/*)</li>
169
- <li>正则表达式:使用/pattern/格式(例如:/\/api\/users\/\d+/)</li>
170
- </ul>
171
- </div>
172
- )}
173
-
174
- <p>请在下方输入完整测试URL进行测试</p>
175
- </div>
176
- }
177
- type="info"
178
- showIcon
179
- style={{ marginBottom: 16 }}
180
- />
181
-
182
- <Form
183
- form={testForm}
184
- layout="vertical"
185
- >
186
- <Form.Item
187
- name="testUrl"
188
- label="测试URL"
189
- rules={[{ required: true, message: '请输入测试URL' }]}
190
- >
191
- <Input
192
- placeholder="输入完整URL,例如:https://example.com/api/users"
193
- />
194
- </Form.Item>
195
- </Form>
196
-
197
- {testResult && (
198
- <div className="test-result">
199
- <div className="result-header">
200
- <h3>测试结果</h3>
201
- {testResult.success ? (
202
- <span className="status success">成功</span>
203
- ) : (
204
- <span className="status error">失败</span>
205
- )}
206
- </div>
207
-
208
- {testResult.success ? (
209
- <div className="result-content">
210
- <div className="result-item">
211
- <span className="label">请求URL:</span>
212
- <span className="value">{testResult.requestUrl}</span>
213
- </div>
214
- <div className="result-item">
215
- <span className="label">匹配规则:</span>
216
- <span className="value">{testResult.matchedRule}</span>
217
- </div>
218
- <div className="result-item">
219
- <span className="label">请求方法:</span>
220
- <span className="value">{testResult.httpMethod || 'ALL'}</span>
221
- </div>
222
- <div className="result-item">
223
- <span className="label">处理方式:</span>
224
- <span className="value">
225
- {(() => {
226
- const found = proxyTypes.find(item => item.value === editingInterface.proxyType);
227
- return found ? found.label : editingInterface.proxyType || '模拟响应';
228
- })()}
229
- </span>
230
- </div>
231
-
232
- {(editingInterface.proxyType === 'redirect' || editingInterface.proxyType === 'url_redirect') && (
233
- <div className="result-item">
234
- <span className="label">重定向目标:</span>
235
- <span className="value">{testResult.targetUrl || editingInterface.targetUrl}</span>
236
- </div>
237
- )}
238
-
239
- {(editingInterface.proxyType === 'redirect' || editingInterface.proxyType === 'url_redirect') &&
240
- testResult.formattedHeaders && (
241
- <div className="result-item">
242
- <span className="label">自定义请求头:</span>
243
- <div className="value" style={{ whiteSpace: 'pre-wrap', fontFamily: 'monospace' }}>
244
- {
245
- editingInterface.customHeaders &&
246
- Object.entries(editingInterface.customHeaders)
247
- .map(([key, value]) => {
248
- const generatedValue = testResult.customHeaders[key];
249
- return (
250
- <div key={key}>
251
- {key}: {value.startsWith('@') ? (
252
- <span>
253
- <span style={{ color: '#d9d9d9' }}>{value}</span>
254
- {' '} → {' '}
255
- <span style={{ color: '#52c41a', fontWeight: 'bold' }}>{generatedValue}</span>
256
- <span style={{ color: '#8c8c8c', fontSize: '12px' }}> (随机生成)</span>
257
- </span>
258
- ) : generatedValue}
259
- </div>
260
- );
261
- })
262
- }
263
- </div>
264
- </div>
265
- )}
266
-
267
- {editingInterface.proxyType === 'response' && (
268
- <>
269
- <div className="result-item">
270
- <span className="label">状态码:</span>
271
- <span className="value">{testResult.statusCode}</span>
272
- </div>
273
- <div className="result-item">
274
- <span className="label">内容类型:</span>
275
- <span className="value">{testResult.contentType}</span>
276
- </div>
277
- </>
278
- )}
279
-
280
- {testResult.mockInfo && (
281
- <>
282
- <div className="result-item">
283
- <span className="label">模拟延迟:</span>
284
- <span className="value">{testResult.mockInfo.delay}ms</span>
285
- </div>
286
- <div className="result-item">
287
- <span className="label">响应时间:</span>
288
- <span className="value">{new Date(testResult.mockInfo.timestamp).toLocaleString()}</span>
289
- </div>
290
- </>
291
- )}
292
-
293
- {editingInterface.proxyType === 'response' && (
294
- <div className="result-body">
295
- <div className="label">响应内容:</div>
296
- <pre>{formatResponseContent(testResult.responseBody, testResult.contentType)}</pre>
297
- </div>
298
- )}
299
- </div>
300
- ) : (
301
- <div className="result-error">
302
- <ExclamationCircleOutlined /> {testResult.error}
303
- </div>
304
- )}
305
- </div>
306
- )}
307
- </div>
308
- )}
309
- </Modal>
310
- );
311
- };
312
-
313
- export default InterfaceTestModal;
@@ -1,48 +0,0 @@
1
- import React from 'react';
2
- import { Modal, Button } from 'antd';
3
-
4
- const PreviewModal = ({
5
- visible,
6
- onCancel,
7
- previewContent
8
- }) => {
9
- return (
10
- <Modal
11
- title={previewContent?.title || "预览响应内容"}
12
- open={visible}
13
- onCancel={onCancel}
14
- footer={[
15
- <Button key="close" onClick={onCancel}>
16
- 关闭
17
- </Button>
18
- ]}
19
- width={600}
20
- >
21
- {previewContent?.description && (
22
- <div style={{
23
- marginBottom: '16px',
24
- padding: '8px',
25
- borderRadius: '4px',
26
- borderLeft: '4px solid #00f0ff'
27
- }}>
28
- <div style={{ fontWeight: 'bold', marginBottom: '4px' }}>描述:</div>
29
- <div style={{ opacity: 0.7 }}>{previewContent.description}</div>
30
- </div>
31
- )}
32
-
33
- <div style={{
34
- maxHeight: '60vh',
35
- overflow: 'auto',
36
- padding: '16px',
37
- borderRadius: '4px',
38
- fontFamily: "'Share Tech Mono', monospace",
39
- }}>
40
- <pre style={{ margin: 0, whiteSpace: 'pre-wrap', wordWrap: 'break-word' }}>
41
- {previewContent?.content}
42
- </pre>
43
- </div>
44
- </Modal>
45
- );
46
- };
47
-
48
- export default PreviewModal;
@@ -1,184 +0,0 @@
1
- # 接口管理组件拆分与优化
2
-
3
- ## 概述
4
- 将原本的 `InterfaceManagement.js` 文件(2100多行)拆分为多个更小、更易维护的组件文件,并优化响应管理UI,简化用户界面。
5
-
6
- ## 文件结构
7
-
8
- ```
9
- src/components/interface-management/
10
- ├── index.js # 统一导出文件
11
- ├── constants.js # 常量定义(选项数组)
12
- ├── utils.js # 工具函数
13
- ├── ResponseContentEditor.js # 合并的响应内容编辑器(包含管理功能)
14
- ├── InterfaceTestModal.js # 接口测试模态框
15
- ├── PreviewModal.js # 预览模态框
16
- └── README.md # 说明文档
17
- ```
18
-
19
- ## 组件优化说明
20
-
21
- ### 1. 响应管理UI优化历程
22
-
23
- #### 第一阶段:组件合并
24
- - **合并组件**:将原来的 `ResponseManager` 和 `ResponseEditor` 合并为一个 `ResponseContentEditor` 组件
25
- - **简化界面**:移除响应描述字段和内容统计信息,只保留响应名称设置
26
- - **一体化操作**:响应的添加、删除、切换和内容编辑都在同一个Card组件中完成
27
-
28
- #### 第二阶段:交互优化
29
- - **智能下拉框**:将响应选择和名称编辑合并为一个既可选择又可编辑的交互组件
30
- - **双模式切换**:
31
- - **选择模式**:下拉选择器用于切换不同响应,右侧编辑按钮可进入编辑模式
32
- - **编辑模式**:输入框直接编辑响应名称,确认或取消按钮完成操作
33
- - **更简洁的布局**:从三列布局(选择/名称/操作)优化为两列布局(选择编辑/删除)
34
-
35
- ### 2. 各组件职责
36
-
37
- #### constants.js
38
- - `contentTypes`: 内容类型选项
39
- - `proxyTypes`: 代理类型选项
40
- - `statusCodes`: 状态码选项
41
- - `httpMethods`: HTTP方法选项
42
-
43
- #### utils.js
44
- - `cleanJsonResponse()`: 清理JSON响应
45
- - `isValidUrl()`: 验证URL格式
46
- - `flushCache()`: 刷新缓存
47
- - `refreshCacheAfterUpdate()`: 更新后刷新缓存
48
- - `isUrlMatchPattern()`: URL匹配检查
49
- - `formatResponseContent()`: 格式化响应内容
50
- - `generateResponseId()`: 生成唯一响应ID
51
-
52
- #### ResponseContentEditor.js
53
- **优化后的响应内容编辑器组件**,负责:
54
- - 响应的增删改查操作
55
- - 智能响应选择和名称编辑(双模式交互)
56
- - 响应内容编辑和格式化
57
- - 预览功能集成
58
-
59
- Props:
60
- - `form`: 表单实例
61
- - `responses`: 响应数组
62
- - `activeResponseId`: 当前激活的响应ID
63
- - `onPreview`: 预览回调
64
-
65
- 特色功能:
66
- - **双模式交互界面**:选择模式下可快速切换响应,编辑模式下可直接修改名称
67
- - **一键切换**:点击编辑按钮立即进入编辑模式,支持回车确认和失焦保存
68
- - **优雅的状态管理**:使用useState管理编辑状态,实现流畅的模式切换
69
-
70
- #### InterfaceTestModal.js
71
- 接口测试模态框组件,负责:
72
- - 接口测试表单
73
- - 测试结果显示
74
- - URL匹配规则说明
75
- - 测试失败错误处理
76
-
77
- Props:
78
- - `visible`: 是否显示
79
- - `onCancel`: 取消回调
80
- - `editingInterface`: 当前编辑的接口
81
-
82
- #### PreviewModal.js
83
- 预览模态框组件,负责:
84
- - 响应内容预览
85
- - 格式化内容显示
86
-
87
- Props:
88
- - `visible`: 是否显示
89
- - `onCancel`: 取消回调
90
- - `previewContent`: 预览内容对象
91
-
92
- ## 主文件变更
93
-
94
- 新的 `InterfaceManagement.js` 文件:
95
- - 从 2126 行减少到约 800 行
96
- - 导入优化后的 `ResponseContentEditor` 组件
97
- - 简化响应相关的状态管理
98
- - 保持完整的功能性
99
-
100
- ## 使用方式
101
-
102
- ```javascript
103
- // 导入优化后的组件
104
- import {
105
- ResponseContentEditor,
106
- InterfaceTestModal,
107
- PreviewModal,
108
- contentTypes,
109
- proxyTypes,
110
- statusCodes,
111
- httpMethods,
112
- refreshCacheAfterUpdate,
113
- formatResponseContent,
114
- generateResponseId
115
- } from '../components/interface-management';
116
-
117
- // 使用优化后的响应编辑器
118
- <ResponseContentEditor
119
- form={form}
120
- responses={responses}
121
- activeResponseId={activeResponseId}
122
- onPreview={handlePreview}
123
- />
124
- ```
125
-
126
- ## 优化效果
127
-
128
- ### UI简化与交互优化
129
- 1. **智能交互**:一个组件同时支持选择和编辑功能,减少界面元素
130
- 2. **操作直观**:下拉选择 + 编辑按钮的组合,用户操作更自然
131
- 3. **状态清晰**:选择模式和编辑模式有明确的视觉区分
132
- 4. **快速操作**:支持回车确认、失焦保存等快捷操作
133
-
134
- ### 布局优化
135
- - **从3列布局简化为2列**:选择编辑区域(span=20)+ 删除操作(span=4)
136
- - **更好的空间利用**:合并后的组件占据更大空间,操作更便捷
137
- - **视觉统一**:保持与整体设计风格的一致性
138
-
139
- ### 代码优化
140
- 1. **状态管理增强**:新增编辑状态和名称编辑状态管理
141
- 2. **交互逻辑清晰**:分离选择、编辑开始、编辑完成、编辑取消等逻辑
142
- 3. **事件处理完善**:支持多种编辑完成方式(回车、点击确认、失焦)
143
-
144
- ### 功能保持
145
- 1. **完整功能**:所有原有功能都得到保留和增强
146
- 2. **兼容性**:保持与现有数据结构的完全兼容
147
- 3. **稳定性**:优化过程中不破坏任何现有功能
148
-
149
- ## 新的UI布局
150
-
151
- 优化后的响应模块布局:
152
-
153
- ```
154
- ┌─ 响应内容编辑 ─ [添加响应] [预览] [格式化JSON] ─┐
155
- │ │
156
- │ ┌─ 选择响应 / 编辑名称 ────────────┐ ┌─ 操作 ─┐ │
157
- │ │ 选择模式:[下拉选择器] [编辑按钮] │ │ [删除] │ │
158
- │ │ 编辑模式:[输入框] [确认] [取消] │ │ │ │
159
- │ └─────────────────────────────┘ └──────┘ │
160
- │ │
161
- │ 响应内容: │
162
- │ ┌──────────────────────────────────────────┐ │
163
- │ │ │ │
164
- │ │ JSON内容编辑区域 │ │
165
- │ │ │ │
166
- │ └──────────────────────────────────────────┘ │
167
- └──────────────────────────────────────────────┘
168
- ```
169
-
170
- ## 优势
171
-
172
- 1. **用户体验**:更智能的交互设计,一个组件完成多种操作
173
- 2. **界面简洁**:减少界面元素,视觉更清爽
174
- 3. **操作效率**:快速切换选择和编辑模式,提升工作效率
175
- 4. **代码质量**:清晰的状态管理和事件处理逻辑
176
- 5. **扩展性**:良好的组件设计,便于后续功能扩展
177
-
178
- ## 兼容性
179
-
180
- - 完全兼容原有功能和数据格式
181
- - 保持所有API接口不变
182
- - 用户界面和交互逻辑保持一致
183
- - 构建和打包正常工作
184
- - 向前兼容,新功能不影响现有数据