|
|
/**
|
|
|
* 企业信息查询接口调试代理(零依赖,Node >= 14)
|
|
|
*
|
|
|
* 用途:
|
|
|
* 1. 托管同目录下的 index.html(访问 http://localhost:5500)
|
|
|
* 2. 将其余请求原样转发到后端 127.0.0.1:8081,并在响应上附加 CORS 头,
|
|
|
* 规避浏览器跨域限制,方便前端页面直接联调。
|
|
|
*
|
|
|
* 启动: node proxy.js
|
|
|
* 自定义:PORT=5501 TARGET_PORT=9090 node proxy.js
|
|
|
*/
|
|
|
"use strict";
|
|
|
|
|
|
const http = require("http");
|
|
|
const https = require("https");
|
|
|
const fs = require("fs");
|
|
|
const path = require("path");
|
|
|
|
|
|
const PORT = Number(process.env.PORT) || 9527;
|
|
|
const TARGET_HOST = process.env.TARGET_HOST || "127.0.0.1";
|
|
|
const TARGET_PORT = Number(process.env.TARGET_PORT) || 8081;
|
|
|
const CX_HOST = process.env.CX_HOST || "api-cx.dingtax.cn";
|
|
|
const PUB_HOST = process.env.PUB_HOST || "apit-dsb.dingtax.cn";
|
|
|
|
|
|
const MIME = {
|
|
|
".html": "text/html; charset=utf-8",
|
|
|
".js": "text/javascript; charset=utf-8",
|
|
|
".css": "text/css; charset=utf-8",
|
|
|
".json": "application/json; charset=utf-8",
|
|
|
".png": "image/png",
|
|
|
".svg": "image/svg+xml",
|
|
|
".ico": "image/x-icon"
|
|
|
};
|
|
|
|
|
|
function corsHeaders() {
|
|
|
return {
|
|
|
"Access-Control-Allow-Origin": "*",
|
|
|
"Access-Control-Allow-Methods": "GET,POST,PUT,DELETE,PATCH,OPTIONS",
|
|
|
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With"
|
|
|
};
|
|
|
}
|
|
|
|
|
|
const server = http.createServer((req, res) => {
|
|
|
const cors = corsHeaders();
|
|
|
|
|
|
if (req.method === "OPTIONS") {
|
|
|
res.writeHead(204, cors);
|
|
|
return res.end();
|
|
|
}
|
|
|
|
|
|
// 静态文件:/ 及 /index.html,其余静态路径按扩展名尝试读取
|
|
|
const urlPath = decodeURIComponent((req.url || "/").split("?")[0]);
|
|
|
if (req.method === "GET" && !urlPath.startsWith("/dsb/") && !urlPath.startsWith("/cx/") && !urlPath.startsWith("/pub/")) {
|
|
|
let file = path.join(__dirname, urlPath === "/" ? "index.html" : urlPath);
|
|
|
if (!file.startsWith(__dirname)) file = path.join(__dirname, "index.html");
|
|
|
fs.readFile(file, (err, buf) => {
|
|
|
if (err) {
|
|
|
res.writeHead(404, { ...cors, "Content-Type": "text/plain; charset=utf-8" });
|
|
|
return res.end("404 Not Found: " + urlPath);
|
|
|
}
|
|
|
res.writeHead(200, { ...cors, "Content-Type": MIME[path.extname(file)] || "application/octet-stream" });
|
|
|
res.end(buf);
|
|
|
});
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// /cx/* → 公网 api-cx.dingtax.cn (HTTPS);/dsb/* → 本机后端 (HTTP)
|
|
|
const isCx = urlPath.startsWith("/cx/");
|
|
|
const isPub = urlPath.startsWith("/pub/");
|
|
|
const qs = (req.url || "").split("?").slice(1).join("?");
|
|
|
// apit-dsb 后端会校验 Origin/Referer(Invalid CORS request / Rap csrf Verify),转发前剥离浏览器来源头
|
|
|
const fwdHeaders = { ...req.headers };
|
|
|
["origin", "referer", "sec-fetch-site", "sec-fetch-mode", "sec-fetch-dest", "sec-fetch-user", "accept-language"].forEach(h => delete fwdHeaders[h]);
|
|
|
const options = isCx
|
|
|
? {
|
|
|
host: CX_HOST,
|
|
|
port: 443,
|
|
|
path: req.url,
|
|
|
method: req.method,
|
|
|
headers: { ...fwdHeaders, host: CX_HOST },
|
|
|
rejectUnauthorized: false
|
|
|
}
|
|
|
: isPub
|
|
|
? {
|
|
|
host: PUB_HOST,
|
|
|
port: 443,
|
|
|
path: "/dsb" + urlPath.slice(4) + (qs ? "?" + qs : ""),
|
|
|
method: req.method,
|
|
|
headers: { ...fwdHeaders, host: PUB_HOST },
|
|
|
rejectUnauthorized: false
|
|
|
}
|
|
|
: {
|
|
|
host: TARGET_HOST,
|
|
|
port: TARGET_PORT,
|
|
|
path: req.url,
|
|
|
method: req.method,
|
|
|
headers: { ...fwdHeaders, host: `${TARGET_HOST}:${TARGET_PORT}` }
|
|
|
};
|
|
|
|
|
|
const upstream = (isCx || isPub ? https : http).request(options, (proxyRes) => {
|
|
|
res.writeHead(proxyRes.statusCode || 502, { ...proxyRes.headers, ...cors });
|
|
|
proxyRes.pipe(res);
|
|
|
});
|
|
|
|
|
|
upstream.on("error", (e) => {
|
|
|
res.writeHead(502, { ...cors, "Content-Type": "application/json; charset=utf-8" });
|
|
|
res.end(JSON.stringify({
|
|
|
code: "-1",
|
|
|
message: `代理转发失败: ${e.message}(后端 ${TARGET_HOST}:${TARGET_PORT} 不可达?)`
|
|
|
}));
|
|
|
});
|
|
|
|
|
|
req.pipe(upstream);
|
|
|
});
|
|
|
|
|
|
server.listen(PORT, () => {
|
|
|
console.log("企业信息接口调试代理已启动");
|
|
|
console.log(` 测试页面: http://localhost:${PORT}`);
|
|
|
console.log(` 转发目标: http://${TARGET_HOST}:${TARGET_PORT}`);
|
|
|
console.log(" 按 Ctrl+C 停止");
|
|
|
});
|