api-proxy-mock/index.api.js

293 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
var http = require("http");
var https = require("https");
var fs = require("fs");
var path = require("path");
// 配置文件路径
var CONFIG_FILE = path.join(__dirname, 'config.json');
// 存储当前的路由配置
var MOCK_ROUTES = {};
var CONFIG = {
cacheConfig: true,
reloadOnChange: true,
defaultContentType: "application/json",
proxyPort: 9443,
targetHost: "devrmtapp.resmart.cn"
};
// 过滤掉以 # 开头的路由(视为注释,不参与 mock
function filterActiveRoutes(routes) {
var filtered = {};
for (var _i = 0, _a = Object.entries(routes); _i < _a.length; _i++) {
var _b = _a[_i], route = _b[0], filePath = _b[1];
if (route.startsWith('#'))
continue;
filtered[route] = filePath;
}
return filtered;
}
// 加载配置文件
function loadConfig() {
try {
if (!fs.existsSync(CONFIG_FILE)) {
console.warn("[CONFIG] \u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728: ".concat(CONFIG_FILE));
console.warn("[CONFIG] \u6B63\u5728\u521B\u5EFA\u9ED8\u8BA4\u914D\u7F6E\u6587\u4EF6...");
// 创建默认配置
var defaultConfig = {
routes: {
"/api2/test": "mock/test.txt"
},
config: {
cacheConfig: true,
reloadOnChange: true,
defaultContentType: "application/json",
proxyPort: 9443,
targetHost: "devrmtapp.resmart.cn"
}
};
// 确保mock目录存在
var mockDir = path.join(__dirname, 'mock');
if (!fs.existsSync(mockDir)) {
fs.mkdirSync(mockDir, { recursive: true });
}
// 保存配置文件
fs.writeFileSync(CONFIG_FILE, JSON.stringify(defaultConfig, null, 2), 'utf-8');
console.log("[CONFIG] \u5DF2\u521B\u5EFA\u9ED8\u8BA4\u914D\u7F6E\u6587\u4EF6: ".concat(CONFIG_FILE));
// 加载配置(# 开头的路由视为注释,不参与 mock
var configData = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
MOCK_ROUTES = filterActiveRoutes(configData.routes || {});
CONFIG = configData.config || CONFIG;
}
else {
var configData = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
MOCK_ROUTES = filterActiveRoutes(configData.routes || {});
CONFIG = configData.config || CONFIG;
console.log("[CONFIG] \u914D\u7F6E\u6587\u4EF6\u5DF2\u52A0\u8F7D\uFF0C\u5171 ".concat(Object.keys(MOCK_ROUTES).length, " \u4E2A\u8DEF\u7531").concat(CONFIG.mockEnabled !== false ? '' : 'mock 已关闭,全部走代理)'));
}
// 验证mock文件是否存在
validateMockFiles();
}
catch (error) {
console.error("[CONFIG] \u52A0\u8F7D\u914D\u7F6E\u6587\u4EF6\u5931\u8D25:", error);
// 使用默认配置
MOCK_ROUTES = {
"/api2/user/list": "mock/user_list.txt"
};
CONFIG = {
cacheConfig: true,
reloadOnChange: true,
defaultContentType: "application/json",
proxyPort: 9443,
targetHost: "devrmtapp.resmart.cn"
};
}
}
// 验证mock文件是否存在
function validateMockFiles() {
console.log("[CONFIG] \u9A8C\u8BC1mock\u6587\u4EF6...");
var missingFiles = [];
for (var _i = 0, _a = Object.entries(MOCK_ROUTES); _i < _a.length; _i++) {
var _b = _a[_i], route = _b[0], filePath = _b[1];
var fullPath = path.join(__dirname, filePath);
if (!fs.existsSync(fullPath)) {
missingFiles.push({ route: route, filePath: filePath, fullPath: fullPath });
console.warn("[CONFIG] \u8B66\u544A: mock\u6587\u4EF6\u4E0D\u5B58\u5728 - ".concat(filePath, " (\u7528\u4E8E\u8DEF\u7531: ").concat(route, ")"));
}
}
if (missingFiles.length > 0) {
console.log("[CONFIG] \u7F3A\u5C11 ".concat(missingFiles.length, " \u4E2Amock\u6587\u4EF6\uFF0C\u8BF7\u521B\u5EFA\u8FD9\u4E9B\u6587\u4EF6"));
}
else {
console.log("[CONFIG] \u6240\u6709mock\u6587\u4EF6\u9A8C\u8BC1\u901A\u8FC7");
}
}
// 检查是否为mock路由的函数
function isMockRoute(requestPath) {
if (CONFIG.mockEnabled === false)
return false;
return MOCK_ROUTES.hasOwnProperty(requestPath);
}
// 获取mock文件路径
function getMockFilePath(requestPath) {
return MOCK_ROUTES[requestPath];
}
// 代理服务器
var proxyServer = http.createServer(function (clientReq, clientRes) {
// 解析客户端请求的 URL
var parsedUrl = new URL("http://localhost".concat(clientReq.url));
var requestPath = parsedUrl.pathname;
// 检查是否为需要mock的路由
if (isMockRoute(requestPath)) {
var mockFile = getMockFilePath(requestPath);
console.log("[MOCK] \u62E6\u622A\u8DEF\u7531: ".concat(requestPath, " -> \u4F7F\u7528\u6587\u4EF6: ").concat(mockFile));
try {
// 构建完整的文件路径
var mockFilePath = path.join(__dirname, mockFile);
// 检查文件是否存在
if (!fs.existsSync(mockFilePath)) {
console.error("[MOCK] Mock\u6587\u4EF6\u4E0D\u5B58\u5728: ".concat(mockFilePath));
clientRes.writeHead(404, { 'Content-Type': 'application/json' });
clientRes.end(JSON.stringify({
error: 'Mock file not found',
route: requestPath,
file: mockFile,
timestamp: new Date().toISOString()
}));
return;
}
// 读取本地mock文件
var mockData = fs.readFileSync(mockFilePath, 'utf-8');
// 设置响应头
var contentType = CONFIG.defaultContentType || 'application/json';
clientRes.writeHead(200, {
'Content-Type': contentType,
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
'X-Mock-Source': mockFile,
'X-Mock-Timestamp': new Date().toISOString()
});
// 返回mock数据
clientRes.end(mockData);
console.log("[MOCK] \u6210\u529F\u8FD4\u56DEmock\u6570\u636E: ".concat(mockFile));
}
catch (error) {
console.error("[MOCK] \u8BFB\u53D6mock\u6587\u4EF6\u5931\u8D25: ".concat(mockFile), error);
clientRes.writeHead(500, { 'Content-Type': 'application/json' });
clientRes.end(JSON.stringify({
error: 'Failed to read mock data',
route: requestPath,
file: mockFile,
message: error.message,
timestamp: new Date().toISOString()
}));
}
return; // 直接返回,不转发到目标服务器
}
// 如果不是mock路由正常代理转发
console.log("[PROXY] \u8F6C\u53D1\u8BF7\u6C42: ".concat(requestPath));
// 目标服务器的选项
var options = {
hostname: CONFIG.targetHost,
port: 443, // HTTPS 默认端口
method: clientReq.method,
path: parsedUrl.pathname + parsedUrl.search,
headers: __assign(__assign({}, clientReq.headers), { host: CONFIG.targetHost })
};
// 创建到目标服务器的 HTTPS 请求
var proxyReq = https.request(options, function (proxyRes) {
// 将目标服务器的响应头复制到客户端响应
clientRes.writeHead(proxyRes.statusCode, proxyRes.headers);
// 将目标服务器的响应数据管道传输到客户端
proxyRes.pipe(clientRes);
});
// 错误处理
proxyReq.on('error', function (err) {
console.error('Proxy request error:', err);
clientRes.writeHead(500, { 'Content-Type': 'application/json' });
clientRes.end(JSON.stringify({
error: 'Proxy error',
message: err.message,
timestamp: new Date().toISOString()
}));
});
// 将客户端请求体管道传输到代理请求
clientReq.pipe(proxyReq);
});
// 添加管理接口用于重新加载配置
proxyServer.on('request', function (req, res) {
var parsedUrl = new URL("http://localhost".concat(req.url));
var pathname = parsedUrl.pathname;
// 管理接口:重新加载配置
if (pathname === '/__reload-config' && req.method === 'POST') {
try {
var oldCount = Object.keys(MOCK_ROUTES).length;
loadConfig();
var newCount = Object.keys(MOCK_ROUTES).length;
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
success: true,
message: 'Configuration reloaded successfully',
routesCount: newCount,
routesChanged: newCount - oldCount
}));
console.log("[ADMIN] \u901A\u8FC7API\u91CD\u65B0\u52A0\u8F7D\u914D\u7F6E (\u8DEF\u7531\u6570: ".concat(oldCount, " -> ").concat(newCount, ")"));
}
catch (error) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
success: false,
error: error.message
}));
}
}
// 管理接口:查看当前配置
if (pathname === '/__config' && req.method === 'GET') {
try {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
routes: MOCK_ROUTES,
config: CONFIG,
timestamp: new Date().toISOString(),
totalRoutes: Object.keys(MOCK_ROUTES).length
}, null, 2));
}
catch (error) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
success: false,
error: error.message
}));
}
}
});
// 初始化:第一次加载配置
loadConfig();
// 如果配置了文件监视,则监听配置文件变化
if (CONFIG.reloadOnChange) {
fs.watchFile(CONFIG_FILE, function (curr, prev) {
console.log("[CONFIG] \u914D\u7F6E\u6587\u4EF6\u5DF2\u4FEE\u6539\uFF0C\u91CD\u65B0\u52A0\u8F7D...");
try {
var oldRoutesCount = Object.keys(MOCK_ROUTES).length;
loadConfig();
var newRoutesCount = Object.keys(MOCK_ROUTES).length;
console.log("[CONFIG] \u914D\u7F6E\u91CD\u8F7D\u5B8C\u6210 (\u8DEF\u7531\u6570: ".concat(oldRoutesCount, " -> ").concat(newRoutesCount, ")"));
}
catch (error) {
console.error("[CONFIG] \u91CD\u65B0\u52A0\u8F7D\u914D\u7F6E\u6587\u4EF6\u5931\u8D25:", error);
}
});
console.log("[CONFIG] \u5DF2\u542F\u7528\u914D\u7F6E\u6587\u4EF6\u76D1\u89C6: ".concat(CONFIG_FILE));
}
proxyServer.listen(CONFIG.proxyPort, "0.0.0.0", function () {
console.log("========================================");
console.log("\u4EE3\u7406\u670D\u52A1\u5668\u8FD0\u884C\u5728: http://localhost:".concat(CONFIG.proxyPort));
console.log("\u76EE\u6807\u670D\u52A1\u5668: https://".concat(CONFIG.targetHost));
console.log("\u914D\u7F6E\u6587\u4EF6: ".concat(CONFIG_FILE));
console.log("\u5DF2\u914D\u7F6EMock\u8DEF\u7531: ".concat(Object.keys(MOCK_ROUTES).length, " \u4E2A").concat(CONFIG.mockEnabled !== false ? '' : '(当前 mockEnabled=false未生效'));
console.log("========================================");
console.log("\u7BA1\u7406\u63A5\u53E3:");
console.log(" GET http://localhost:".concat(CONFIG.proxyPort, "/__config \u67E5\u770B\u5F53\u524D\u914D\u7F6E"));
console.log(" POST http://localhost:".concat(CONFIG.proxyPort, "/__reload-config \u91CD\u65B0\u52A0\u8F7D\u914D\u7F6E"));
console.log("========================================");
console.log("Mock\u8DEF\u7531\u5217\u8868:");
for (var _i = 0, _a = Object.entries(MOCK_ROUTES); _i < _a.length; _i++) {
var _b = _a[_i], route = _b[0], file = _b[1];
var filePath = path.join(__dirname, file);
var exists = fs.existsSync(filePath) ? '✓' : '✗';
console.log(" ".concat(exists, " ").concat(route, " -> ").concat(file));
}
console.log("========================================");
});