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.
- package/API.md +247 -0
- package/README.md +91 -0
- package/app/cgi-bin/features.js +285 -0
- package/app/cgi-bin/index.js +31 -0
- package/app/cgi-bin/interfaces.js +409 -0
- package/app/cgi-bin/logs.js +151 -0
- package/app/cgi-bin/test-interface.js +152 -0
- package/app/public/index.html +1 -0
- package/app/public/js/index.js +2 -0
- package/app/public/js/index.js.LICENSE.txt +150 -0
- package/app/util/index.js +24 -0
- package/index.html +52 -0
- package/initialize.js +32 -0
- package/lib/dataManager.js +500 -0
- package/lib/index.js +3 -0
- package/lib/models.js +64 -0
- package/lib/routes/features.js +213 -0
- package/lib/routes/interfaces.js +304 -0
- package/lib/routes/proxy.js +239 -0
- package/lib/ruleManager.js +551 -0
- package/lib/rules-server.js +267 -0
- package/lib/server.js +805 -0
- package/lib/storage.js +16 -0
- package/lib/ui-server.js +68 -0
- package/package.json +51 -0
- package/public/bundle.js +2 -0
- package/public/bundle.js.LICENSE.txt +38 -0
- package/public/index.html +1 -0
- package/public/plugin.mock-plugin.html +14 -0
- package/rules.txt +22 -0
- package/test/rulesServer.test.js +120 -0
- package/ui/src/App.css +173 -0
- package/ui/src/App.js +24 -0
- package/ui/src/components/AppLayout.js +149 -0
- package/ui/src/components/FeatureCard.js +53 -0
- package/ui/src/components/Layout.js +94 -0
- package/ui/src/components/MainNav.js +42 -0
- package/ui/src/components/PageContainer.js +16 -0
- package/ui/src/index.html +11 -0
- package/ui/src/index.js +43 -0
- package/ui/src/pages/FileProxy.js +235 -0
- package/ui/src/pages/FilesPage.js +292 -0
- package/ui/src/pages/Home.js +128 -0
- package/ui/src/pages/HomePage.js +195 -0
- package/ui/src/pages/InterfaceManagement.js +829 -0
- package/ui/src/pages/InterfacePage.js +342 -0
- package/ui/src/pages/MockData.js +472 -0
- package/ui/src/pages/Settings.js +192 -0
- package/ui/src/pages/SettingsPage.js +165 -0
- package/ui/src/pages/UrlRedirect.js +225 -0
- package/ui/src/pages/Welcome.js +155 -0
- package/ui/src/services/api.js +186 -0
- package/ui/src/styles/app-layout.css +337 -0
- package/ui/src/styles/feature-card.css +172 -0
- package/ui/src/styles/file-proxy.css +301 -0
- package/ui/src/styles/global.css +224 -0
- package/ui/src/styles/home.css +123 -0
- package/ui/src/styles/index.css +419 -0
- package/ui/src/styles/interface-management.css +694 -0
- package/ui/src/styles/layout.css +159 -0
- package/ui/src/styles/logs-page.css +427 -0
- package/ui/src/styles/main-nav.css +52 -0
- package/ui/src/styles/mock-data.css +850 -0
- package/ui/src/styles/page-container.css +11 -0
- package/ui/src/styles/settings.css +208 -0
- package/ui/src/styles/url-redirect.css +279 -0
- package/ui/src/styles/welcome.css +282 -0
- package/webpack.config.js +49 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const Mock = require('mockjs');
|
|
5
|
+
const storage = require('../storage');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 代理路由处理程序
|
|
9
|
+
* 负责处理文件和 Mock 数据相关的 API 请求
|
|
10
|
+
*/
|
|
11
|
+
const FILES_DIR = storage.FILES_DIR;
|
|
12
|
+
|
|
13
|
+
// 确保文件目录存在
|
|
14
|
+
fs.ensureDirSync(FILES_DIR);
|
|
15
|
+
|
|
16
|
+
// 初始化路由器
|
|
17
|
+
const router = express.Router();
|
|
18
|
+
|
|
19
|
+
// 解析 JSON 请求体
|
|
20
|
+
router.use(express.json({ limit: '100mb' }));
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 获取文件内容
|
|
24
|
+
* GET /api/proxy/file?path=example.json
|
|
25
|
+
*/
|
|
26
|
+
router.get('/proxy/file', (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const filePath = req.query.path;
|
|
29
|
+
if (!filePath) {
|
|
30
|
+
return res.status(400).json({ error: '缺少文件路径参数' });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 处理相对路径和绝对路径
|
|
34
|
+
const fullPath = path.isAbsolute(filePath)
|
|
35
|
+
? filePath
|
|
36
|
+
: path.join(FILES_DIR, filePath);
|
|
37
|
+
|
|
38
|
+
// 确保路径安全(防止目录遍历攻击)
|
|
39
|
+
const normalizedPath = path.normalize(fullPath);
|
|
40
|
+
if (!normalizedPath.startsWith(FILES_DIR) && !path.isAbsolute(filePath)) {
|
|
41
|
+
return res.status(403).json({ error: '访问被拒绝:路径超出允许范围' });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!fs.existsSync(normalizedPath)) {
|
|
45
|
+
return res.status(404).json({ error: '文件不存在' });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (fs.statSync(normalizedPath).isDirectory()) {
|
|
49
|
+
return res.status(400).json({ error: '不能读取目录,请指定文件' });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const content = fs.readFileSync(normalizedPath, 'utf-8');
|
|
53
|
+
const stats = fs.statSync(normalizedPath);
|
|
54
|
+
|
|
55
|
+
res.json({
|
|
56
|
+
path: filePath,
|
|
57
|
+
name: path.basename(filePath),
|
|
58
|
+
content,
|
|
59
|
+
size: stats.size,
|
|
60
|
+
mtime: stats.mtime,
|
|
61
|
+
isDirectory: false
|
|
62
|
+
});
|
|
63
|
+
} catch (err) {
|
|
64
|
+
console.error('获取文件内容失败:', err);
|
|
65
|
+
res.status(500).json({ error: '获取文件内容失败: ' + err.message });
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 保存文件内容
|
|
71
|
+
* POST /api/proxy/file
|
|
72
|
+
* body: { path: 'example.json', content: '{"key": "value"}' }
|
|
73
|
+
*/
|
|
74
|
+
router.post('/proxy/file', (req, res) => {
|
|
75
|
+
try {
|
|
76
|
+
const { path: filePath, content } = req.body;
|
|
77
|
+
|
|
78
|
+
if (!filePath) {
|
|
79
|
+
return res.status(400).json({ error: '缺少文件路径参数' });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (content === undefined) {
|
|
83
|
+
return res.status(400).json({ error: '缺少文件内容参数' });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 处理相对路径和绝对路径
|
|
87
|
+
const fullPath = path.isAbsolute(filePath)
|
|
88
|
+
? filePath
|
|
89
|
+
: path.join(FILES_DIR, filePath);
|
|
90
|
+
|
|
91
|
+
// 确保路径安全(防止目录遍历攻击)
|
|
92
|
+
const normalizedPath = path.normalize(fullPath);
|
|
93
|
+
if (!normalizedPath.startsWith(FILES_DIR) && !path.isAbsolute(filePath)) {
|
|
94
|
+
return res.status(403).json({ error: '访问被拒绝:路径超出允许范围' });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 确保目录存在
|
|
98
|
+
fs.ensureDirSync(path.dirname(normalizedPath));
|
|
99
|
+
|
|
100
|
+
// 写入文件
|
|
101
|
+
fs.writeFileSync(normalizedPath, content, 'utf-8');
|
|
102
|
+
const stats = fs.statSync(normalizedPath);
|
|
103
|
+
|
|
104
|
+
res.json({
|
|
105
|
+
path: filePath,
|
|
106
|
+
name: path.basename(filePath),
|
|
107
|
+
size: stats.size,
|
|
108
|
+
mtime: stats.mtime,
|
|
109
|
+
isDirectory: false
|
|
110
|
+
});
|
|
111
|
+
} catch (err) {
|
|
112
|
+
console.error('保存文件内容失败:', err);
|
|
113
|
+
res.status(500).json({ error: '保存文件内容失败: ' + err.message });
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 获取文件列表
|
|
119
|
+
* GET /api/proxy/files?path=directory
|
|
120
|
+
*/
|
|
121
|
+
router.get('/proxy/files', (req, res) => {
|
|
122
|
+
try {
|
|
123
|
+
const dirPath = req.query.path || '';
|
|
124
|
+
|
|
125
|
+
// 处理相对路径和绝对路径
|
|
126
|
+
const fullPath = path.isAbsolute(dirPath)
|
|
127
|
+
? dirPath
|
|
128
|
+
: path.join(FILES_DIR, dirPath);
|
|
129
|
+
|
|
130
|
+
// 确保路径安全(防止目录遍历攻击)
|
|
131
|
+
const normalizedPath = path.normalize(fullPath);
|
|
132
|
+
if (!normalizedPath.startsWith(FILES_DIR) && !path.isAbsolute(dirPath)) {
|
|
133
|
+
return res.status(403).json({ error: '访问被拒绝:路径超出允许范围' });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 确保目录存在
|
|
137
|
+
if (!fs.existsSync(normalizedPath)) {
|
|
138
|
+
fs.ensureDirSync(normalizedPath);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!fs.statSync(normalizedPath).isDirectory()) {
|
|
142
|
+
return res.status(400).json({ error: '指定路径不是目录' });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const files = fs.readdirSync(normalizedPath)
|
|
146
|
+
.filter(file => !file.startsWith('.') || file === '.placeholder')
|
|
147
|
+
.map(file => {
|
|
148
|
+
const filePath = path.join(normalizedPath, file);
|
|
149
|
+
const stats = fs.statSync(filePath);
|
|
150
|
+
const isDirectory = stats.isDirectory();
|
|
151
|
+
const relativePath = path.join(dirPath, file);
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
path: relativePath,
|
|
155
|
+
name: file,
|
|
156
|
+
size: stats.size,
|
|
157
|
+
mtime: stats.mtime,
|
|
158
|
+
isDirectory
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
res.json(files);
|
|
163
|
+
} catch (err) {
|
|
164
|
+
console.error('获取文件列表失败:', err);
|
|
165
|
+
res.status(500).json({ error: '获取文件列表失败: ' + err.message });
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 删除文件
|
|
171
|
+
* DELETE /api/proxy/file?path=example.json
|
|
172
|
+
*/
|
|
173
|
+
router.delete('/proxy/file', (req, res) => {
|
|
174
|
+
try {
|
|
175
|
+
const filePath = req.query.path;
|
|
176
|
+
if (!filePath) {
|
|
177
|
+
return res.status(400).json({ error: '缺少文件路径参数' });
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 处理相对路径和绝对路径
|
|
181
|
+
const fullPath = path.isAbsolute(filePath)
|
|
182
|
+
? filePath
|
|
183
|
+
: path.join(FILES_DIR, filePath);
|
|
184
|
+
|
|
185
|
+
// 确保路径安全(防止目录遍历攻击)
|
|
186
|
+
const normalizedPath = path.normalize(fullPath);
|
|
187
|
+
if (!normalizedPath.startsWith(FILES_DIR) && !path.isAbsolute(filePath)) {
|
|
188
|
+
return res.status(403).json({ error: '访问被拒绝:路径超出允许范围' });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!fs.existsSync(normalizedPath)) {
|
|
192
|
+
return res.status(404).json({ error: '文件不存在' });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// 删除文件或目录
|
|
196
|
+
fs.removeSync(normalizedPath);
|
|
197
|
+
|
|
198
|
+
res.json({ success: true, message: '文件已删除' });
|
|
199
|
+
} catch (err) {
|
|
200
|
+
console.error('删除文件失败:', err);
|
|
201
|
+
res.status(500).json({ error: '删除文件失败: ' + err.message });
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 测试 Mock 数据生成
|
|
207
|
+
* POST /api/proxy/test
|
|
208
|
+
* body: { template: '{"name": "@cname", "age|18-60": 1}' }
|
|
209
|
+
*/
|
|
210
|
+
router.post('/proxy/test', (req, res) => {
|
|
211
|
+
try {
|
|
212
|
+
const { template } = req.body;
|
|
213
|
+
|
|
214
|
+
if (!template) {
|
|
215
|
+
return res.status(400).json({ error: '缺少 template 参数' });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 解析模板
|
|
219
|
+
let templateObj;
|
|
220
|
+
try {
|
|
221
|
+
templateObj = typeof template === 'string' ? JSON.parse(template) : template;
|
|
222
|
+
} catch (e) {
|
|
223
|
+
return res.status(400).json({ error: 'template 不是有效的 JSON: ' + e.message });
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// 生成 Mock 数据
|
|
227
|
+
const mockData = Mock.mock(templateObj);
|
|
228
|
+
|
|
229
|
+
res.json({
|
|
230
|
+
template: templateObj,
|
|
231
|
+
data: mockData
|
|
232
|
+
});
|
|
233
|
+
} catch (err) {
|
|
234
|
+
console.error('生成 Mock 数据失败:', err);
|
|
235
|
+
res.status(500).json({ error: '生成 Mock 数据失败: ' + err.message });
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
module.exports = router;
|