json-api-mocker 3.0.0 → 3.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.
- package/CONFIG.md +38 -0
- package/README.ch.md +328 -95
- package/README.md +331 -98
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +6 -41
- package/dist/index.d.ts +3 -7
- package/dist/index.js +43 -48
- package/dist/public/admin.html +908 -0
- package/dist/server.d.ts +24 -21
- package/dist/server.js +260 -328
- package/dist/types.d.ts +45 -36
- package/dist/types.js +2 -2
- package/package.json +12 -20
- package/DESIGN.md +0 -227
- package/web/assets/index-C4eZNDku.js +0 -25
- package/web/assets/index-CGhPNH5w.css +0 -1
- package/web/index.html +0 -15
- package/web/monaco-editor-worker-loader.js +0 -8
- package/web/vite.svg +0 -1
package/dist/server.d.ts
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
private
|
|
5
|
-
private
|
|
6
|
-
private
|
|
7
|
-
private
|
|
8
|
-
private
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
private
|
|
12
|
-
|
|
13
|
-
private
|
|
14
|
-
private
|
|
15
|
-
private
|
|
16
|
-
private
|
|
17
|
-
private
|
|
18
|
-
private
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
|
|
1
|
+
import { Express } from 'express';
|
|
2
|
+
import { Config } from './types';
|
|
3
|
+
export declare class MockServer {
|
|
4
|
+
private app;
|
|
5
|
+
private server;
|
|
6
|
+
private wss;
|
|
7
|
+
private config;
|
|
8
|
+
private configPath;
|
|
9
|
+
constructor(config: Config, configPath?: string);
|
|
10
|
+
getApp(): Express;
|
|
11
|
+
private setupMiddleware;
|
|
12
|
+
private logRequest;
|
|
13
|
+
private generateMockData;
|
|
14
|
+
private handleRequest;
|
|
15
|
+
private setupAdminRoutes;
|
|
16
|
+
private getAdminHtml;
|
|
17
|
+
private setupRoutes;
|
|
18
|
+
private createRoute;
|
|
19
|
+
private findRouteConfig;
|
|
20
|
+
private generateMockResponse;
|
|
21
|
+
private setupWebSocket;
|
|
22
|
+
start(): void;
|
|
23
|
+
close(): void;
|
|
24
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -1,328 +1,260 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.MockServer = void 0;
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const path_1 = require("path");
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
this.
|
|
36
|
-
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
//
|
|
106
|
-
this.app.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
res.status(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
res.setHeader(key, value);
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
// 处理响应数据
|
|
265
|
-
let responseData = config.response;
|
|
266
|
-
if (typeof responseData === 'string') {
|
|
267
|
-
try {
|
|
268
|
-
responseData = JSON.parse(responseData);
|
|
269
|
-
}
|
|
270
|
-
catch (e) {
|
|
271
|
-
console.error('Failed to parse response string:', e);
|
|
272
|
-
responseData = { error: 'Invalid response format' };
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
// 使用 Mock.js 处理数据
|
|
276
|
-
const mockedData = mockjs_1.default.mock(responseData);
|
|
277
|
-
res.status(config.status || 200).json(mockedData);
|
|
278
|
-
}
|
|
279
|
-
catch (error) {
|
|
280
|
-
console.error('Error processing response:', error);
|
|
281
|
-
res.status(500).json({
|
|
282
|
-
error: 'Internal server error',
|
|
283
|
-
details: error instanceof Error ? error.message : 'Unknown error'
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
setupUploadRoute() {
|
|
288
|
-
// 处理文件上传
|
|
289
|
-
this.app.post('/api/upload', this.upload.single('file'), (req, res) => {
|
|
290
|
-
if (!req.file) {
|
|
291
|
-
return res.status(400).json({
|
|
292
|
-
code: 400,
|
|
293
|
-
message: 'No file uploaded',
|
|
294
|
-
data: null
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
// 只返回模拟数据,不保存文件
|
|
298
|
-
res.json({
|
|
299
|
-
code: 200,
|
|
300
|
-
message: 'success',
|
|
301
|
-
data: {
|
|
302
|
-
url: mockjs_1.default.Random.image('200x200'),
|
|
303
|
-
filename: req.file.originalname,
|
|
304
|
-
size: req.file.size
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
start(port) {
|
|
310
|
-
const serverPort = port || this.serverConfig.port || 3000;
|
|
311
|
-
this.app.listen(serverPort, () => {
|
|
312
|
-
console.log(`Mock server is running on http://localhost:${serverPort}`);
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
// 广播日志给所有客户端
|
|
316
|
-
broadcastLog(log) {
|
|
317
|
-
const message = JSON.stringify({
|
|
318
|
-
type: 'log',
|
|
319
|
-
data: log
|
|
320
|
-
});
|
|
321
|
-
this.clients.forEach(client => {
|
|
322
|
-
if (client.readyState === ws_1.WebSocket.OPEN) {
|
|
323
|
-
client.send(message);
|
|
324
|
-
}
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
exports.MockServer = MockServer;
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MockServer = void 0;
|
|
7
|
+
const mockjs_1 = __importDefault(require("mockjs"));
|
|
8
|
+
const express_1 = __importDefault(require("express"));
|
|
9
|
+
const cors_1 = __importDefault(require("cors"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const fs_1 = __importDefault(require("fs"));
|
|
12
|
+
const ws_1 = require("ws");
|
|
13
|
+
const http_1 = __importDefault(require("http"));
|
|
14
|
+
class MockServer {
|
|
15
|
+
constructor(config, configPath = 'data.json') {
|
|
16
|
+
this.app = (0, express_1.default)();
|
|
17
|
+
this.wss = null;
|
|
18
|
+
this.logRequest = (req, res, next) => {
|
|
19
|
+
const startTime = Date.now();
|
|
20
|
+
const requestId = Math.random().toString(36).substring(7);
|
|
21
|
+
console.log(`[${new Date().toISOString()}] Request ${requestId}:`);
|
|
22
|
+
console.log(` Method: ${req.method}`);
|
|
23
|
+
console.log(` URL: ${req.url}`);
|
|
24
|
+
console.log(` Query Params: ${JSON.stringify(req.query)}`);
|
|
25
|
+
console.log(` Body: ${JSON.stringify(req.body)}`);
|
|
26
|
+
res.on('finish', () => {
|
|
27
|
+
const duration = Date.now() - startTime;
|
|
28
|
+
console.log(`[${new Date().toISOString()}] Response ${requestId}:`);
|
|
29
|
+
console.log(` Status: ${res.statusCode}`);
|
|
30
|
+
console.log(` Duration: ${duration}ms`);
|
|
31
|
+
console.log('----------------------------------------');
|
|
32
|
+
});
|
|
33
|
+
next();
|
|
34
|
+
};
|
|
35
|
+
this.config = config;
|
|
36
|
+
this.configPath = configPath;
|
|
37
|
+
this.server = http_1.default.createServer(this.app);
|
|
38
|
+
this.setupMiddleware();
|
|
39
|
+
this.setupAdminRoutes();
|
|
40
|
+
this.setupRoutes();
|
|
41
|
+
if (config.websocket?.enabled) {
|
|
42
|
+
this.setupWebSocket();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
getApp() {
|
|
46
|
+
return this.app;
|
|
47
|
+
}
|
|
48
|
+
setupMiddleware() {
|
|
49
|
+
this.app.use((0, cors_1.default)());
|
|
50
|
+
this.app.use(express_1.default.json());
|
|
51
|
+
this.app.use('/uploads', express_1.default.static('uploads'));
|
|
52
|
+
this.app.use(this.logRequest);
|
|
53
|
+
}
|
|
54
|
+
generateMockData(config) {
|
|
55
|
+
try {
|
|
56
|
+
if (config.mock?.enabled && config.mock.template) {
|
|
57
|
+
const { total, template } = config.mock;
|
|
58
|
+
return mockjs_1.default.mock({
|
|
59
|
+
[`data|${total}`]: [template]
|
|
60
|
+
}).data;
|
|
61
|
+
}
|
|
62
|
+
return config.response;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
console.error('Error generating mock data:', error);
|
|
66
|
+
return config.response;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
handleRequest(config) {
|
|
70
|
+
return (req, res) => {
|
|
71
|
+
try {
|
|
72
|
+
let responseData = this.generateMockData(config);
|
|
73
|
+
if (config.pagination?.enabled && Array.isArray(responseData)) {
|
|
74
|
+
const page = parseInt(req.query.page) || 1;
|
|
75
|
+
const pageSize = parseInt(req.query.pageSize) || config.pagination.pageSize;
|
|
76
|
+
const startIndex = (page - 1) * pageSize;
|
|
77
|
+
const endIndex = startIndex + pageSize;
|
|
78
|
+
const paginatedData = responseData.slice(startIndex, endIndex);
|
|
79
|
+
res.header('X-Total-Count', responseData.length.toString());
|
|
80
|
+
responseData = paginatedData;
|
|
81
|
+
}
|
|
82
|
+
res.json(responseData);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
console.error('Error handling request:', error);
|
|
86
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
setupAdminRoutes() {
|
|
91
|
+
// 管理后台页面
|
|
92
|
+
this.app.get('/admin', (req, res) => {
|
|
93
|
+
const html = this.getAdminHtml();
|
|
94
|
+
if (html) {
|
|
95
|
+
res.type('html').send(html);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
res.status(404).send('Admin page not found');
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
// 获取当前配置
|
|
102
|
+
this.app.get('/admin/api/config', (req, res) => {
|
|
103
|
+
res.json(this.config);
|
|
104
|
+
});
|
|
105
|
+
// 保存配置
|
|
106
|
+
this.app.put('/admin/api/config', (req, res) => {
|
|
107
|
+
try {
|
|
108
|
+
const newConfig = req.body;
|
|
109
|
+
// 简单校验
|
|
110
|
+
if (!newConfig.server || !newConfig.routes) {
|
|
111
|
+
res.status(400).json({ success: false, error: '配置格式不正确:缺少 server 或 routes' });
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// 写入文件
|
|
115
|
+
const fullPath = path_1.default.resolve(process.cwd(), this.configPath);
|
|
116
|
+
fs_1.default.writeFileSync(fullPath, JSON.stringify(newConfig, null, 2), 'utf-8');
|
|
117
|
+
// 更新内存中的配置
|
|
118
|
+
this.config = newConfig;
|
|
119
|
+
console.log(`配置已保存到: ${fullPath}`);
|
|
120
|
+
res.json({ success: true, message: '配置已保存,部分改动(如新增路由)需重启服务生效' });
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.error('保存配置失败:', error);
|
|
124
|
+
res.status(500).json({ success: false, error: error.message });
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
getAdminHtml() {
|
|
129
|
+
const candidates = [
|
|
130
|
+
path_1.default.join(__dirname, 'public', 'admin.html'),
|
|
131
|
+
path_1.default.join(__dirname, '..', 'public', 'admin.html'),
|
|
132
|
+
path_1.default.join(process.cwd(), 'public', 'admin.html'),
|
|
133
|
+
];
|
|
134
|
+
for (const p of candidates) {
|
|
135
|
+
if (fs_1.default.existsSync(p)) {
|
|
136
|
+
return fs_1.default.readFileSync(p, 'utf-8');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
setupRoutes() {
|
|
142
|
+
this.config.routes.forEach((route) => {
|
|
143
|
+
Object.entries(route.methods).forEach(([method, methodConfig]) => {
|
|
144
|
+
this.createRoute(route.path, method, methodConfig);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
createRoute(path, method, config) {
|
|
149
|
+
const fullPath = `${this.config.server.baseProxy}${path}`;
|
|
150
|
+
console.log(`创建路由: ${method.toUpperCase()} ${fullPath}`);
|
|
151
|
+
switch (method.toLowerCase()) {
|
|
152
|
+
case 'get':
|
|
153
|
+
this.app.get(fullPath, this.handleRequest(config));
|
|
154
|
+
break;
|
|
155
|
+
case 'post':
|
|
156
|
+
if (path === '/upload/avatar') {
|
|
157
|
+
// 对于文件上传路由,使用特殊处理
|
|
158
|
+
this.app.post(fullPath, (req, res) => {
|
|
159
|
+
const mockResponse = this.generateMockData(config);
|
|
160
|
+
res.json(mockResponse);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
this.app.post(fullPath, this.handleRequest(config));
|
|
165
|
+
}
|
|
166
|
+
break;
|
|
167
|
+
case 'put':
|
|
168
|
+
this.app.put(`${fullPath}/:id`, this.handleRequest(config));
|
|
169
|
+
break;
|
|
170
|
+
case 'delete':
|
|
171
|
+
this.app.delete(`${fullPath}/:id`, this.handleRequest(config));
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
findRouteConfig(path, method) {
|
|
176
|
+
const route = this.config.routes.find(r => r.path === path);
|
|
177
|
+
return route?.methods[method] || null;
|
|
178
|
+
}
|
|
179
|
+
generateMockResponse(config) {
|
|
180
|
+
if (config.mock?.enabled && config.mock.template) {
|
|
181
|
+
return mockjs_1.default.mock(config.mock.template);
|
|
182
|
+
}
|
|
183
|
+
return config.response;
|
|
184
|
+
}
|
|
185
|
+
setupWebSocket() {
|
|
186
|
+
if (!this.config.websocket)
|
|
187
|
+
return;
|
|
188
|
+
this.wss = new ws_1.Server({
|
|
189
|
+
server: this.server,
|
|
190
|
+
path: this.config.websocket.path
|
|
191
|
+
});
|
|
192
|
+
this.wss.on('connection', (ws) => {
|
|
193
|
+
console.log('WebSocket client connected');
|
|
194
|
+
// 处理客户端消息
|
|
195
|
+
ws.on('message', (message) => {
|
|
196
|
+
try {
|
|
197
|
+
const data = JSON.parse(message.toString());
|
|
198
|
+
const eventConfig = this.config.websocket?.events?.[data.event];
|
|
199
|
+
if (eventConfig?.mock.enabled) {
|
|
200
|
+
const response = mockjs_1.default.mock(eventConfig.mock.template);
|
|
201
|
+
ws.send(JSON.stringify({
|
|
202
|
+
event: data.event,
|
|
203
|
+
data: response
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
console.error('Error handling WebSocket message:', error);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
// 设置自动发送数据的定时器
|
|
212
|
+
if (this.config.websocket && this.config.websocket.events) {
|
|
213
|
+
Object.entries(this.config.websocket.events).forEach(([event, config]) => {
|
|
214
|
+
if (config.mock.interval) {
|
|
215
|
+
setInterval(() => {
|
|
216
|
+
const response = mockjs_1.default.mock(config.mock.template);
|
|
217
|
+
ws.send(JSON.stringify({
|
|
218
|
+
event,
|
|
219
|
+
data: response
|
|
220
|
+
}));
|
|
221
|
+
}, config.mock.interval);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
ws.on('close', () => {
|
|
226
|
+
// 移除日志,避免测试完成后的日志输出
|
|
227
|
+
// console.log('WebSocket client disconnected');
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
start() {
|
|
232
|
+
this.server.listen(this.config.server.port, () => {
|
|
233
|
+
console.log(`Mock 服务器已启动:`);
|
|
234
|
+
console.log(`- HTTP 地址: http://localhost:${this.config.server.port}`);
|
|
235
|
+
console.log(`- 管理后台: http://localhost:${this.config.server.port}/admin`);
|
|
236
|
+
if (this.config.websocket?.enabled) {
|
|
237
|
+
console.log(`- WebSocket 地址: ws://localhost:${this.config.server.port}${this.config.websocket.path}`);
|
|
238
|
+
}
|
|
239
|
+
console.log(`- 基础路径: ${this.config.server.baseProxy}`);
|
|
240
|
+
console.log('可用的接口:');
|
|
241
|
+
this.config.routes.forEach(route => {
|
|
242
|
+
Object.keys(route.methods).forEach(method => {
|
|
243
|
+
console.log(` ${method.toUpperCase()} http://localhost:${this.config.server.port}${this.config.server.baseProxy}${route.path}`);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
close() {
|
|
249
|
+
// 关闭所有 WebSocket 连接
|
|
250
|
+
if (this.wss) {
|
|
251
|
+
this.wss.clients.forEach(client => {
|
|
252
|
+
client.close();
|
|
253
|
+
});
|
|
254
|
+
this.wss.close();
|
|
255
|
+
}
|
|
256
|
+
// 关闭 HTTP 服务器
|
|
257
|
+
this.server.close();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
exports.MockServer = MockServer;
|