mock-service-cli 3.7.0 → 4.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/bin/mock-service-cli +1 -266
- package/{lib → dist}/asyncTaskQueue.js +6 -18
- package/dist/cli.js +6608 -0
- package/dist/fileExplorerServer.js +28186 -0
- package/dist/manageMockFiles.js +5020 -0
- package/dist/mockServer.js +53214 -0
- package/dist/packageInfo.js +19 -0
- package/dist/staticServer.js +26688 -0
- package/dist/utils.js +857 -0
- package/package.json +21 -17
- package/lib/fileExplorerServer.js +0 -330
- package/lib/manageMockFiles.js +0 -252
- package/lib/mockServer.js +0 -497
- package/lib/staticServer.js +0 -104
- package/lib/utils.js +0 -297
- /package/{lib → dist}/api-overview.html +0 -0
- /package/{lib → dist}/file-explorer.html +0 -0
package/lib/mockServer.js
DELETED
|
@@ -1,497 +0,0 @@
|
|
|
1
|
-
const express = require('express'), // 引入express
|
|
2
|
-
bodyParser = require('body-parser'),
|
|
3
|
-
{ readdirSync, existsSync } = require('fs'),
|
|
4
|
-
os = require('os'),
|
|
5
|
-
path = require('path'),
|
|
6
|
-
colors = require('colors/safe'),
|
|
7
|
-
portfinder = require('portfinder'),
|
|
8
|
-
{ exec } = require('child_process');
|
|
9
|
-
const { createProxyMiddleware } = require('http-proxy-middleware');
|
|
10
|
-
const ifaces = os.networkInterfaces();
|
|
11
|
-
const { Server } = require('socket.io');
|
|
12
|
-
const {
|
|
13
|
-
dateFormat,
|
|
14
|
-
logger,
|
|
15
|
-
SupportMethods,
|
|
16
|
-
DefaultHeaders,
|
|
17
|
-
getFileLatestContent,
|
|
18
|
-
filePath2ApiUrl,
|
|
19
|
-
isEmptyObj
|
|
20
|
-
} = require('./utils');
|
|
21
|
-
const { genMockFiles, getMockStatFromDir, getMockStatFromFile } = require('./manageMockFiles');
|
|
22
|
-
const registeredApis = [];
|
|
23
|
-
const methodRegExp = new RegExp(`((${SupportMethods.join('|')}) +)?([^?]*)`, 'i');
|
|
24
|
-
|
|
25
|
-
const app = express();
|
|
26
|
-
const log = logger(process.env.SILENT);
|
|
27
|
-
const argv = JSON.parse(process.env.ARGV);
|
|
28
|
-
let socketServer = null; // Socket server instance
|
|
29
|
-
let corsOrigin = [],
|
|
30
|
-
corsHeaders = '';
|
|
31
|
-
|
|
32
|
-
const httpsRE = /^https:\/\//;
|
|
33
|
-
let count = 0,
|
|
34
|
-
fileCount = 0, // 记录mock api个数,mock file个数
|
|
35
|
-
mockDirStat = {}, // 缓存mock目录的统计信息
|
|
36
|
-
mockFileStat = {}; // 缓存mock文件的统计信息
|
|
37
|
-
|
|
38
|
-
// whether include web serve or not
|
|
39
|
-
const includesWebServe = process.env.STATIC_DIRECTORY || process.env.WEB_ROOT;
|
|
40
|
-
|
|
41
|
-
const mockFileOrDir = process.env.SPECIFIED_FILE || process.env.SPECIFIED_DIR;
|
|
42
|
-
|
|
43
|
-
if (!process.env.PORT) {
|
|
44
|
-
portfinder.basePort = 8090;
|
|
45
|
-
portfinder.getPort(function (err, port) {
|
|
46
|
-
if (err) {
|
|
47
|
-
throw err;
|
|
48
|
-
}
|
|
49
|
-
process.env.PORT = port;
|
|
50
|
-
init();
|
|
51
|
-
});
|
|
52
|
-
} else {
|
|
53
|
-
init();
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Init mock server
|
|
58
|
-
*/
|
|
59
|
-
function init() {
|
|
60
|
-
if (process.env.CORS_ORIGIN) {
|
|
61
|
-
process.env.CORS_ORIGIN.split(/\s*,\s*/).forEach(function (h) {
|
|
62
|
-
corsOrigin.push(h);
|
|
63
|
-
}, this);
|
|
64
|
-
|
|
65
|
-
corsHeaders = DefaultHeaders;
|
|
66
|
-
}
|
|
67
|
-
if (process.env.CORS_HEADERS) {
|
|
68
|
-
process.env.CORS_HEADERS.split(/\s*,\s*/).forEach(function (h) {
|
|
69
|
-
corsHeaders += corsHeaders ? ', ' + h : h;
|
|
70
|
-
}, this);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
app.use(crossDomain()); // 允许跨域
|
|
74
|
-
app.use(bodyParser.json()); // 解析body
|
|
75
|
-
|
|
76
|
-
// API 概览页面路由
|
|
77
|
-
app.get('/__api-overview', (req, res) => {
|
|
78
|
-
const htmlPath = path.resolve(__dirname, './api-overview.html');
|
|
79
|
-
if (existsSync(htmlPath)) {
|
|
80
|
-
res.sendFile(htmlPath);
|
|
81
|
-
} else {
|
|
82
|
-
res.status(404).send('API overview page not found');
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// API 数据路由
|
|
87
|
-
app.get('/__api-data', (req, res) => {
|
|
88
|
-
// 按目录分组 API
|
|
89
|
-
const groupedApis = {};
|
|
90
|
-
registeredApis.forEach(api => {
|
|
91
|
-
// 从文件路径中提取相对路径作为目录
|
|
92
|
-
const dirPath = path.dirname(api.file);
|
|
93
|
-
if (!groupedApis[dirPath]) {
|
|
94
|
-
groupedApis[dirPath] = [];
|
|
95
|
-
}
|
|
96
|
-
groupedApis[dirPath].push(api);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
res.json({
|
|
100
|
-
apis: registeredApis,
|
|
101
|
-
groupedApis: groupedApis,
|
|
102
|
-
port: process.env.PORT
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
let requireMockServe = true;
|
|
107
|
-
if (process.env.SPECIFIED_FILE) {
|
|
108
|
-
composeRouteFromJsFile(process.env.SPECIFIED_FILE);
|
|
109
|
-
} else if (includesWebServe && !existsSync(path.resolve(process.cwd(), process.env.SPECIFIED_DIR))) {
|
|
110
|
-
// 若包含web服务,则允许不启动mock服务
|
|
111
|
-
requireMockServe = false;
|
|
112
|
-
} else {
|
|
113
|
-
parseMockFiles(process.env.SPECIFIED_DIR);
|
|
114
|
-
}
|
|
115
|
-
requireMockServe && argv.a && log.info(colors.yellow(`${fileCount} mock file are parsed in total.`));
|
|
116
|
-
startServer();
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* @description: 跨域设置
|
|
121
|
-
* @param {*}
|
|
122
|
-
* @return {*}
|
|
123
|
-
*/
|
|
124
|
-
function crossDomain() {
|
|
125
|
-
return (req, res, next) => {
|
|
126
|
-
if (typeof argv.A === 'string') {
|
|
127
|
-
const resHeaders = {};
|
|
128
|
-
argv.A.split(/\s*,\s*/).forEach(function (h) {
|
|
129
|
-
const [key, value] = h.split('=');
|
|
130
|
-
resHeaders[key] = value;
|
|
131
|
-
}, this);
|
|
132
|
-
// 添加自定义响应头
|
|
133
|
-
for (const key in resHeaders) {
|
|
134
|
-
if (resHeaders.hasOwnProperty(key)) {
|
|
135
|
-
res.header(key, resHeaders[key]);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// 设置withCredentials: true时,需设置以下两项
|
|
141
|
-
res.header('Access-Control-Allow-Credentials', true);
|
|
142
|
-
// withCredentials, must be specified value instead of *
|
|
143
|
-
res.header('Access-Control-Allow-Origin', corsOrigin.includes(req.headers.origin) ? req.headers.origin : '*');
|
|
144
|
-
// res.header('Access-Control-Allow-Origin', '*');
|
|
145
|
-
res.header('Access-Control-Allow-Methods', SupportMethods.join(','));
|
|
146
|
-
res.header('Access-Control-Allow-Headers', corsHeaders ? corsHeaders : '*');
|
|
147
|
-
// res.header('Access-Control-Allow-Headers', '*');
|
|
148
|
-
if (req.method === 'OPTIONS') res.status(200); // 让OPTIONS快速返回
|
|
149
|
-
next();
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* @description: 构建本地服务的路由请求
|
|
155
|
-
* @param {*} file
|
|
156
|
-
* @returns {*}
|
|
157
|
-
*/
|
|
158
|
-
function composeRouteFromJsFile(file) {
|
|
159
|
-
// 先删除require之前导入的缓存文件,否则获取到的文件内容还是旧内容
|
|
160
|
-
const fileObject = getFileLatestContent(file);
|
|
161
|
-
|
|
162
|
-
argv.a && log.info(colors.green(`Mockfile ${++fileCount}: `), file, colors.yellow('all API URL is follows: '));
|
|
163
|
-
if (fileObject === 'getFileLatestContent_ERROR') {
|
|
164
|
-
// console.error(colors.red(`[warning] "${file}" 文件解析失败,请检查并确保文件中的js语法正确`));
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
Object.keys(fileObject).forEach(item => {
|
|
168
|
-
let reqMethod = 'get',
|
|
169
|
-
reqUrl = item;
|
|
170
|
-
// 支持(GET|POST|PUT|DELETE|HEAD|PATCH|OPTIONS|COPY|LINK|UNLINK|PURGE)常用方法, 默认GET请求
|
|
171
|
-
const [, , method, url] = methodRegExp.exec(item);
|
|
172
|
-
if (method) {
|
|
173
|
-
reqMethod = method.toLowerCase();
|
|
174
|
-
}
|
|
175
|
-
reqUrl = url;
|
|
176
|
-
argv.a &&
|
|
177
|
-
log.info(
|
|
178
|
-
colors.grey(`path ${String(++count).padStart(3, ' ')}: `),
|
|
179
|
-
colors.cyan(reqMethod.padEnd(8, ' ')),
|
|
180
|
-
colors.cyan(reqUrl),
|
|
181
|
-
colors.grey(typeof fileObject[item])
|
|
182
|
-
);
|
|
183
|
-
log.assert(typeof app[reqMethod] === 'function', colors.red(`${file}, ${reqMethod}`));
|
|
184
|
-
if (typeof fileObject[item] === 'function') {
|
|
185
|
-
app[reqMethod](reqUrl, fileObject[item]);
|
|
186
|
-
} else if (typeof fileObject[item] === 'object') {
|
|
187
|
-
app[reqMethod](reqUrl, function (req, res) {
|
|
188
|
-
res.json(fileObject[item]);
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
// 收集 API 信息
|
|
192
|
-
registeredApis.push({
|
|
193
|
-
method: reqMethod.toUpperCase(),
|
|
194
|
-
url: reqUrl,
|
|
195
|
-
file: file,
|
|
196
|
-
data: fileObject[item]
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* @description: 解析mock目录下的js文件,为构建路由请求做准备
|
|
202
|
-
* @param {string} specialDir 目录路径
|
|
203
|
-
* @return {void}
|
|
204
|
-
*/
|
|
205
|
-
function parseMockFiles(specialDir = '../mock') {
|
|
206
|
-
const files = readdirSync(path.resolve(process.cwd(), specialDir), { withFileTypes: true });
|
|
207
|
-
const curFilesSize = files.length;
|
|
208
|
-
for (let index = 0; index < curFilesSize; index++) {
|
|
209
|
-
const el = files[index];
|
|
210
|
-
const filePath = path.normalize(`${specialDir}/${el.name}`);
|
|
211
|
-
// 递归目录
|
|
212
|
-
if (!el.isFile()) {
|
|
213
|
-
parseMockFiles(filePath);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// 处理存放mock数据的json文件
|
|
217
|
-
if (el.name.endsWith('.json') && el.name !== 'mock-list.json') {
|
|
218
|
-
argv.a &&
|
|
219
|
-
log.info(colors.green(`Mockfile ${++fileCount}: `), filePath, colors.yellow('all API URL is follows: '));
|
|
220
|
-
const fileObject = getFileLatestContent(filePath);
|
|
221
|
-
if (fileObject === 'getFileLatestContent_ERROR') {
|
|
222
|
-
// console.error(colors.red(`[warning] "${filePath}" 文件解析失败,请检查并确保文件中的js语法正确`));
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
// eslint-disable-next-line no-loop-func
|
|
226
|
-
Object.keys(fileObject).forEach(method => {
|
|
227
|
-
const apiUrl = filePath2ApiUrl(decodeURIComponent(el.name.split('.')[0]));
|
|
228
|
-
argv.a &&
|
|
229
|
-
log.info(
|
|
230
|
-
colors.grey(`path ${String(++count).padStart(3, ' ')}: `),
|
|
231
|
-
colors.cyan(method.padEnd(8, ' ')),
|
|
232
|
-
colors.cyan(apiUrl),
|
|
233
|
-
colors.grey(typeof fileObject[method])
|
|
234
|
-
);
|
|
235
|
-
log.assert(typeof app[method] === 'function', colors.red(`${filePath}, ${method}`));
|
|
236
|
-
app[method](apiUrl, function (req, res) {
|
|
237
|
-
res.json(fileObject[method]);
|
|
238
|
-
});
|
|
239
|
-
// 收集 API 信息
|
|
240
|
-
registeredApis.push({
|
|
241
|
-
method: method.toUpperCase(),
|
|
242
|
-
url: apiUrl,
|
|
243
|
-
file: filePath,
|
|
244
|
-
data: fileObject[method]
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// 非js文件跳过
|
|
251
|
-
if (!el.name.endsWith('.js') && !el.name.endsWith('.cjs')) {
|
|
252
|
-
continue;
|
|
253
|
-
}
|
|
254
|
-
composeRouteFromJsFile(filePath);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Start web server、socket server
|
|
260
|
-
*/
|
|
261
|
-
function startServer() {
|
|
262
|
-
let webApp = null,
|
|
263
|
-
webPublicPath = '/',
|
|
264
|
-
proxyTable = {};
|
|
265
|
-
// Enable Web Server
|
|
266
|
-
if (process.env.WEB_ROOT) {
|
|
267
|
-
webApp = express();
|
|
268
|
-
// Enable web proxy
|
|
269
|
-
if (process.env.PROXY_OPTIONS) {
|
|
270
|
-
try {
|
|
271
|
-
const options = JSON.parse(process.env.PROXY_OPTIONS);
|
|
272
|
-
Object.keys(options).forEach(function (prefix) {
|
|
273
|
-
proxyTable[prefix] = options[prefix];
|
|
274
|
-
const opts = {
|
|
275
|
-
target: options[prefix],
|
|
276
|
-
changeOrigin: true,
|
|
277
|
-
ws: true
|
|
278
|
-
};
|
|
279
|
-
if (httpsRE.test(options[prefix])) {
|
|
280
|
-
// https is require secure=false
|
|
281
|
-
opts.secure = false;
|
|
282
|
-
}
|
|
283
|
-
if (process.env.PREFIX_REWRITE) {
|
|
284
|
-
opts.pathRewrite = path => path.replace(new RegExp(`^${prefix}`), '');
|
|
285
|
-
}
|
|
286
|
-
// 接口代理
|
|
287
|
-
// https://github.com/http-party/node-http-proxy#options
|
|
288
|
-
webApp.use(prefix, createProxyMiddleware(opts));
|
|
289
|
-
}, this);
|
|
290
|
-
} catch (error) {
|
|
291
|
-
console.warn(colors.yellow('\nEnable web proxy Failed:', error));
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
// 添加自定义响应头
|
|
295
|
-
if (typeof argv.A === 'string') {
|
|
296
|
-
webApp.use((req, res, next) => {
|
|
297
|
-
const resHeaders = {};
|
|
298
|
-
argv.A.split(/\s*,\s*/).forEach(function (h) {
|
|
299
|
-
const [key, value] = h.split('=');
|
|
300
|
-
resHeaders[key] = value;
|
|
301
|
-
}, this);
|
|
302
|
-
// 添加自定义响应头
|
|
303
|
-
for (const key in resHeaders) {
|
|
304
|
-
if (resHeaders.hasOwnProperty(key)) {
|
|
305
|
-
res.header(key, resHeaders[key]);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
next();
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
// console.log(process.env.WEB_PUBLIC_PATH, process.env.WEB_ROOT);
|
|
312
|
-
if (process.env.WEB_PUBLIC_PATH) {
|
|
313
|
-
webPublicPath = process.env.WEB_PUBLIC_PATH;
|
|
314
|
-
}
|
|
315
|
-
webApp.use(webPublicPath, express.static(process.env.WEB_ROOT)); // 将dist目录下所有文件作为静态文件来管理
|
|
316
|
-
const defaultStaticEntry = `${process.env.WEB_ROOT}/index.html`;
|
|
317
|
-
if (existsSync(defaultStaticEntry)) {
|
|
318
|
-
// Used as static HTTP server for SPA
|
|
319
|
-
webApp.get('*', (req, res) => {
|
|
320
|
-
res.sendFile(defaultStaticEntry);
|
|
321
|
-
});
|
|
322
|
-
} else {
|
|
323
|
-
// Used as api server for test on browser console or api request tools
|
|
324
|
-
webApp.use(webPublicPath, (req, res) => {
|
|
325
|
-
res.status(200)
|
|
326
|
-
.send(`Hi, welcome to web server entrance ui, you can test http proxy on browser console or api request tools. Examples as follows:<br />
|
|
327
|
-
fetch("http://localhost:${process.env.WEB_PORT}/api/permission/auth/login", {
|
|
328
|
-
method: 'post',
|
|
329
|
-
headers: {
|
|
330
|
-
"accept": "*/*",
|
|
331
|
-
"Content-type": "application/json; charset=UTF-8",
|
|
332
|
-
},
|
|
333
|
-
body:JSON.stringify({"loginName":"admin","password":"admin"})
|
|
334
|
-
})
|
|
335
|
-
`);
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
const http = require('http').createServer(app);
|
|
340
|
-
if (process.env.SOCKET_SERVER) {
|
|
341
|
-
const AsyncTaskQueue = require('./asyncTaskQueue');
|
|
342
|
-
const saveDataAsyncTask = new AsyncTaskQueue();
|
|
343
|
-
|
|
344
|
-
socketServer = new Server(http, { path: '/ws/mock-service' });
|
|
345
|
-
log.info(colors.bgBlue(`Socket server has started. path: /ws/mock-service, `));
|
|
346
|
-
socketServer.of('/mock-data').on('connection', function (socket) {
|
|
347
|
-
const { headers, address } = socket.handshake;
|
|
348
|
-
const clientIp = headers.hasOwnProperty('x-forwarded-for') ? headers['x-forwarded-for'] : address;
|
|
349
|
-
log.info(
|
|
350
|
-
colors.green(
|
|
351
|
-
`Socket client ${clientIp} has connected. --- ${dateFormat('YYYY-mm-dd HH:MM:SS', new Date())}`
|
|
352
|
-
)
|
|
353
|
-
);
|
|
354
|
-
socket.on('mock-dir-stat', function (dir) {
|
|
355
|
-
socket.emit('mock-dir-stat', isEmptyObj(mockDirStat) ? (mockDirStat = getMockStatFromDir(dir)) : mockDirStat);
|
|
356
|
-
});
|
|
357
|
-
socket.on('mock-file-stat', function (filePath) {
|
|
358
|
-
socket.emit(
|
|
359
|
-
'mock-file-stat',
|
|
360
|
-
isEmptyObj(mockFileStat) ? (mockFileStat = getMockStatFromFile(filePath)) : mockFileStat
|
|
361
|
-
);
|
|
362
|
-
});
|
|
363
|
-
const async = args => {
|
|
364
|
-
return async next => {
|
|
365
|
-
await genMockFiles(args);
|
|
366
|
-
next();
|
|
367
|
-
};
|
|
368
|
-
};
|
|
369
|
-
socket.on('save-data', function (data) {
|
|
370
|
-
// console.log('lis save-data:', data);
|
|
371
|
-
saveDataAsyncTask.add(async(data));
|
|
372
|
-
|
|
373
|
-
if (saveDataAsyncTask.list.length === 1) {
|
|
374
|
-
saveDataAsyncTask.run();
|
|
375
|
-
} else {
|
|
376
|
-
saveDataAsyncTask.next();
|
|
377
|
-
}
|
|
378
|
-
// await genMockFiles(data);
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
socket.on('disconnect', function () {
|
|
382
|
-
log.info(
|
|
383
|
-
colors.gray(
|
|
384
|
-
`Socket client ${clientIp} has disconnected. --- ${dateFormat('YYYY-mm-dd HH:MM:SS', new Date())}`
|
|
385
|
-
)
|
|
386
|
-
);
|
|
387
|
-
});
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
if (existsSync(mockFileOrDir)) {
|
|
392
|
-
http.listen(Number.parseInt(process.env.PORT, 10), () => {
|
|
393
|
-
console.info(colors.red('\n Mock File仅支持commonjs规范的js、cjs文件,不支持ES Module'));
|
|
394
|
-
|
|
395
|
-
console.info(
|
|
396
|
-
[
|
|
397
|
-
colors.yellow(`\n${process.env.RESTARTED ? 'Restarting' : 'Starting'} up mock-server, serving `),
|
|
398
|
-
colors.cyan(process.env.SPECIFIED_FILE || process.env.SPECIFIED_DIR),
|
|
399
|
-
colors.yellow(` ${dateFormat('YYYY-mm-dd HH:MM:SS', new Date())}`)
|
|
400
|
-
].join('')
|
|
401
|
-
);
|
|
402
|
-
if (!process.env.RESTARTED) {
|
|
403
|
-
console.info(
|
|
404
|
-
[colors.yellow('\n🌍 mock-server version: '), colors.cyan(require('../package.json').version), '\n'].join('')
|
|
405
|
-
);
|
|
406
|
-
console.info(colors.yellow(`\n Mock server available on:\n`));
|
|
407
|
-
console.info(' http://localhost:' + colors.green(process.env.PORT));
|
|
408
|
-
console.info(
|
|
409
|
-
' ' +
|
|
410
|
-
colors.blue('API Overview: ') +
|
|
411
|
-
'http://localhost:' +
|
|
412
|
-
colors.green(process.env.PORT) +
|
|
413
|
-
colors.green('/__api-overview')
|
|
414
|
-
);
|
|
415
|
-
Object.keys(ifaces).forEach(function (dev) {
|
|
416
|
-
ifaces[dev].forEach(function (details) {
|
|
417
|
-
if (details.family === 'IPv4') {
|
|
418
|
-
console.info(' http://' + details.address + ':' + colors.green(process.env.PORT));
|
|
419
|
-
}
|
|
420
|
-
});
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// 自动打开浏览器
|
|
425
|
-
if (process.env.OPEN_API_OVERVIEW && !process.env.RESTARTED) {
|
|
426
|
-
const apiOverviewUrl = `http://localhost:${process.env.PORT}/__api-overview`;
|
|
427
|
-
console.info(colors.yellow(`\nOpening API overview page...`));
|
|
428
|
-
let openCommand;
|
|
429
|
-
switch (process.platform) {
|
|
430
|
-
case 'darwin':
|
|
431
|
-
openCommand = `open ${apiOverviewUrl}`;
|
|
432
|
-
break;
|
|
433
|
-
case 'win32':
|
|
434
|
-
openCommand = `start ${apiOverviewUrl}`;
|
|
435
|
-
break;
|
|
436
|
-
case 'linux':
|
|
437
|
-
openCommand = `xdg-open ${apiOverviewUrl}`;
|
|
438
|
-
break;
|
|
439
|
-
default:
|
|
440
|
-
console.warn(colors.yellow(`Could not automatically open browser. Please visit: ${apiOverviewUrl}`));
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
exec(openCommand, error => {
|
|
444
|
-
if (error) {
|
|
445
|
-
console.warn(colors.yellow(`Could not automatically open browser. Please visit: ${apiOverviewUrl}`));
|
|
446
|
-
}
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
if (webApp && !process.env.RESTARTED) {
|
|
453
|
-
webApp.listen(Number.parseInt(process.env.WEB_PORT, 10), () => {
|
|
454
|
-
console.info(colors.yellow(`\n Web server available on:\n`));
|
|
455
|
-
console.info(' http://localhost:' + colors.green(process.env.WEB_PORT) + webPublicPath);
|
|
456
|
-
Object.keys(ifaces).forEach(function (dev) {
|
|
457
|
-
ifaces[dev].forEach(function (details) {
|
|
458
|
-
if (details.family === 'IPv4') {
|
|
459
|
-
console.info(' http://' + details.address + ':' + colors.green(process.env.WEB_PORT) + webPublicPath);
|
|
460
|
-
}
|
|
461
|
-
});
|
|
462
|
-
});
|
|
463
|
-
if (!isEmptyObj(proxyTable)) {
|
|
464
|
-
console.info(colors.yellow(`\n Enable proxy at web server on:`));
|
|
465
|
-
Object.keys(proxyTable).forEach(prefix => {
|
|
466
|
-
console.info(` ${prefix} -> ${proxyTable[prefix]}`);
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
if (process.platform === 'win32') {
|
|
474
|
-
require('readline')
|
|
475
|
-
.createInterface({
|
|
476
|
-
input: process.stdin,
|
|
477
|
-
output: process.stdout
|
|
478
|
-
})
|
|
479
|
-
.on('SIGINT', function () {
|
|
480
|
-
process.emit('SIGINT');
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
process.on('SIGINT', function () {
|
|
485
|
-
log.info(colors.red('mock-server process stopped.'));
|
|
486
|
-
process.exit();
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
process.on('SIGTERM', function () {
|
|
490
|
-
log.info(colors.red('mock-server process stopped.'));
|
|
491
|
-
process.exit();
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
module.exports = {
|
|
495
|
-
composeRouteFromJsFile,
|
|
496
|
-
parseMockFiles
|
|
497
|
-
};
|
package/lib/staticServer.js
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Static Server
|
|
3
|
-
* @Date: 2023-12-03 18:02:24
|
|
4
|
-
* @LastEditors: chendq
|
|
5
|
-
* @LastEditTime: 2025-07-29 19:20:21
|
|
6
|
-
* @Author : chendq
|
|
7
|
-
*/
|
|
8
|
-
const express = require('express'), // 引入express
|
|
9
|
-
os = require('os'),
|
|
10
|
-
colors = require('colors/safe'),
|
|
11
|
-
portfinder = require('portfinder');
|
|
12
|
-
const { dateFormat, logger } = require('./utils');
|
|
13
|
-
const ifaces = os.networkInterfaces();
|
|
14
|
-
const log = logger(process.env.SILENT);
|
|
15
|
-
const argv = JSON.parse(process.env.ARGV);
|
|
16
|
-
|
|
17
|
-
const resHeaders = {};
|
|
18
|
-
|
|
19
|
-
if (!process.env.PORT) {
|
|
20
|
-
portfinder.basePort = 8090;
|
|
21
|
-
portfinder.getPort(function (err, port) {
|
|
22
|
-
if (err) {
|
|
23
|
-
throw err;
|
|
24
|
-
}
|
|
25
|
-
process.env.PORT = port;
|
|
26
|
-
startServer();
|
|
27
|
-
});
|
|
28
|
-
} else {
|
|
29
|
-
startServer();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @description: 添加自定义响应头
|
|
34
|
-
* @param {*}
|
|
35
|
-
* @return {*}
|
|
36
|
-
*/
|
|
37
|
-
function appendResHeaders() {
|
|
38
|
-
return (req, res, next) => {
|
|
39
|
-
for (const key in resHeaders) {
|
|
40
|
-
if (resHeaders.hasOwnProperty(key)) {
|
|
41
|
-
res.header(key, resHeaders[key]);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
next();
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Start server
|
|
50
|
-
*/
|
|
51
|
-
function startServer() {
|
|
52
|
-
const app = express();
|
|
53
|
-
|
|
54
|
-
if (typeof argv.A === 'string') {
|
|
55
|
-
argv.A.split(/\s*,\s*/).forEach(function (h) {
|
|
56
|
-
const [key, value] = h.split('=');
|
|
57
|
-
resHeaders[key] = value;
|
|
58
|
-
}, this);
|
|
59
|
-
app.use(appendResHeaders()); // 添加响应头
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
app.use(express.static(process.env.STATIC_DIRECTORY)); // 将dist目录下所有文件作为静态文件来管理
|
|
63
|
-
|
|
64
|
-
app.listen(Number.parseInt(process.env.PORT, 10), () => {
|
|
65
|
-
console.info(
|
|
66
|
-
[
|
|
67
|
-
colors.yellow(`\nStarting up Static Server, serving `),
|
|
68
|
-
colors.cyan(process.env.STATIC_DIRECTORY),
|
|
69
|
-
colors.yellow(` ${dateFormat('YYYY-mm-dd HH:MM:SS', new Date())}`)
|
|
70
|
-
].join('')
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
console.info(colors.yellow(`\n Static Server available on:\n`));
|
|
74
|
-
console.info(' http://localhost:' + colors.green(process.env.PORT));
|
|
75
|
-
Object.keys(ifaces).forEach(function (dev) {
|
|
76
|
-
ifaces[dev].forEach(function (details) {
|
|
77
|
-
if (details.family === 'IPv4') {
|
|
78
|
-
console.info(' http://' + details.address + ':' + colors.green(process.env.PORT));
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (process.platform === 'win32') {
|
|
86
|
-
require('readline')
|
|
87
|
-
.createInterface({
|
|
88
|
-
input: process.stdin,
|
|
89
|
-
output: process.stdout
|
|
90
|
-
})
|
|
91
|
-
.on('SIGINT', function () {
|
|
92
|
-
process.emit('SIGINT');
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
process.on('SIGINT', function () {
|
|
97
|
-
log.info(colors.red('static-server process stopped.'));
|
|
98
|
-
process.exit();
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
process.on('SIGTERM', function () {
|
|
102
|
-
log.info(colors.red('static-server process stopped.'));
|
|
103
|
-
process.exit();
|
|
104
|
-
});
|