mock-service-plugin
Version:
Simulated backend interface service
295 lines (289 loc) • 8.73 kB
JavaScript
import Mock2 from 'mockjs';
import fs from 'fs';
import xml2js from 'xml2js';
import { stringify } from 'csv-stringify/sync';
// src/response/json-handler.ts
var BaseResponseHandler = class {
constructor(options = {}) {
this.options = options;
}
/**
* 设置响应头
* @param {Response} res - 响应对象
* @param {string} contentType - 内容类型
*/
setHeaders(res, contentType) {
res.set("Content-Type", contentType);
res.set("Access-Control-Allow-Origin", "*");
res.set("Access-Control-Allow-Methods", "GET,HEAD,PUT,POST,DELETE,PATCH");
}
/**
* 处理文件响应
* @param {string} filePath - 文件路径
* @returns {Promise<Buffer>}
*/
async readFile(filePath) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, (err, data) => {
if (err) reject(err);
else resolve(data);
});
});
}
/**
* 处理错误
* @param {Response} res - 响应对象
* @param {Error} error - 错误对象
*/
handleError(res, error) {
console.error(`[Response Error] ${error.message}`);
res.status(500).json({
error: "Internal Server Error",
message: error.message
});
}
};
// src/response/json-handler.ts
var JsonResponseHandler = class extends BaseResponseHandler {
async handle(req, res, route) {
try {
this.setHeaders(res, "application/json");
const responseTemplate = typeof route.responseTemplate === "string" ? JSON.parse(route.responseTemplate) : route.responseTemplate;
const mockData = Mock2.mock(responseTemplate);
res.json(mockData);
} catch (error) {
this.handleError(
res,
error instanceof Error ? error : new Error(String(error))
);
}
}
};
var SseResponseHandler = class extends BaseResponseHandler {
async handle(req, res, route) {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive"
});
const sendEvent = (item) => {
let message = "";
if (item.id) message += `id: ${item.id}
`;
if (item.event) message += `event: ${item.event}
`;
if (item.data) {
const dataStr = typeof item.data === "string" ? item.data : JSON.stringify(item.data);
message += `data: ${dataStr}
`;
}
if (item.retry) message += `retry: ${item.retry}
`;
res.write(message + "\n");
};
try {
const mockData = new Function(
"return (" + route.responseTemplate + ")"
)();
if (mockData) {
const data = Mock2.mock(mockData);
const interval = data.interval || 1e3;
if (Array.isArray(data.items)) {
let index = 0;
const sendNext = () => {
if (index < data.items.length) {
sendEvent(data.items[index]);
index++;
setTimeout(sendNext, interval);
} else {
res.end();
}
};
sendNext();
} else {
sendEvent({ data });
res.end();
}
} else {
sendEvent({ data: Mock2.mock(mockData) });
res.end();
}
} catch (e) {
console.error("[SSE Error]:", e);
res.write(`data: ${JSON.stringify({ error: "Mock data error" })}
`);
res.end();
}
req.on("close", () => {
console.log("Client closed SSE connection");
});
}
};
var TextResponseHandler = class extends BaseResponseHandler {
async handle(req, res, route) {
try {
const contentType = this.getContentType(route);
this.setHeaders(res, contentType);
if (typeof route.responseTemplate === "string") {
res.send(route.responseTemplate);
return;
}
const mockData = Mock2.mock(route.responseTemplate);
res.send(mockData);
return;
} catch (error) {
this.handleError(
res,
error instanceof Error ? error : new Error(String(error))
);
}
}
getContentType(route) {
const extension = route.filepath ? route.filepath.split(".").pop()?.toLowerCase() || "" : "";
const contentTypeMap = {
txt: "text/plain",
html: "text/html",
md: "text/markdown",
css: "text/css",
js: "application/javascript"
};
return contentTypeMap[extension] || "text/plain";
}
};
var XmlResponseHandler = class extends BaseResponseHandler {
constructor(options = {}) {
super(options);
this.builder = new xml2js.Builder();
}
async handle(req, res, route) {
try {
this.setHeaders(res, "application/xml");
const responseTemplate = typeof route.responseTemplate === "string" ? JSON.parse(route.responseTemplate) : route.responseTemplate;
const mockData = Mock2.mock(responseTemplate);
const xml = this.builder.buildObject(mockData);
res.send(xml);
} catch (error) {
this.handleError(
res,
error instanceof Error ? error : new Error(String(error))
);
}
}
};
var CsvResponseHandler = class extends BaseResponseHandler {
async handle(req, res, route) {
try {
this.setHeaders(res, "text/csv");
const responseTemplate = typeof route.responseTemplate === "string" ? JSON.parse(route.responseTemplate) : route.responseTemplate;
const mockData = Mock2.mock(responseTemplate);
const dataArray = Array.isArray(mockData) ? mockData : [mockData];
const csv = stringify(dataArray, {
header: true,
columns: this.getColumns(dataArray[0])
});
res.send(csv);
} catch (error) {
this.handleError(
res,
error instanceof Error ? error : new Error(String(error))
);
}
}
getColumns(data) {
if (!data) return {};
return Object.keys(data).reduce((acc, key) => {
acc[key] = key;
return acc;
}, {});
}
};
var ImageResponseHandler = class extends BaseResponseHandler {
async handle(req, res, route) {
try {
const contentType = this.getContentType(route);
this.setHeaders(res, contentType);
if (typeof route.responseTemplate === "string") {
const imagePath = route.responseTemplate;
const imageBuffer = await this.readFile(imagePath);
res.send(imageBuffer);
return;
}
const mockData = Mock2.mock(route.responseTemplate);
if (typeof mockData === "string") {
const imagePath = mockData;
const imageBuffer = await this.readFile(imagePath);
res.send(imageBuffer);
return;
}
throw new Error("Invalid image responseTemplate format");
} catch (error) {
this.handleError(
res,
error instanceof Error ? error : new Error(String(error))
);
}
}
getContentType(route) {
const extension = route.filepath ? route.filepath.split(".").pop()?.toLowerCase() || "" : "";
const contentTypeMap = {
png: "image/png",
jpg: "image/jpeg",
jpeg: "image/jpeg",
gif: "image/gif",
svg: "image/svg+xml"
};
return contentTypeMap[extension] || "image/png";
}
};
// src/constant.ts
var CONTENT_TYPES = {
".json": "application/json",
".txt": "text/plain",
".html": "text/html",
".xml": "application/xml",
".csv": "text/csv",
".md": "text/markdown",
".pdf": "application/pdf",
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".svg": "image/svg+xml",
".css": "text/css",
".js": "application/javascript",
".yaml": "application/x-yaml",
".yml": "application/x-yaml",
".sse": "text/event-stream"
};
// src/response/handler-factory.ts
var CONTENT_TYPE_HANDLERS = {
"application/json": JsonResponseHandler,
"text/event-stream": SseResponseHandler,
"text/plain": TextResponseHandler,
"text/html": TextResponseHandler,
"text/markdown": TextResponseHandler,
"text/css": TextResponseHandler,
"application/javascript": TextResponseHandler,
"application/xml": XmlResponseHandler,
"text/csv": CsvResponseHandler,
"image/png": ImageResponseHandler,
"image/jpeg": ImageResponseHandler,
"image/gif": ImageResponseHandler,
"image/svg+xml": ImageResponseHandler
// 可以在这里添加更多处理器
};
var ResponseHandlerFactory = class {
static getHandler(contentType) {
const HandlerClass = CONTENT_TYPE_HANDLERS[contentType];
if (!HandlerClass) {
throw new Error(`No handler found for content type: ${contentType}`);
}
return new HandlerClass();
}
static getContentTypeByExtension(extension) {
return CONTENT_TYPES[extension.toLowerCase()] || "application/json";
}
};
export { ResponseHandlerFactory };
//# sourceMappingURL=handler-factory.js.map
//# sourceMappingURL=handler-factory.js.map