Files
geg-gas-web/server.js

179 lines
5.6 KiB
JavaScript
Raw Normal View History

const http = require('http');
const httpServer = require('http-server');
const { createProxyMiddleware } = require('http-proxy-middleware');
const path = require('path');
const fs = require('fs');
const { PassThrough } = require('stream');
const staticRoot = './html';
const log = (message) => {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${message}`);
};
const staticServer = httpServer.createServer({
root: staticRoot,
showDir: false,
cors: true,
autoIndex: false
});
const proxyConfig = {
path: "/api",
target: "http://172.20.0.2:8090"
};
// 跟踪响应是否已处理
const responseHandled = new WeakMap();
const apiProxy = createProxyMiddleware({
target: proxyConfig.target,
changeOrigin: true,
pathRewrite: {'^/api': '' },
logLevel: 'silent',
timeout: 120000,
// 禁用内置的分块处理,手动控制
selfHandleResponse: true,
onTimeout: (req, res) => {
if (!responseHandled.has(res)) {
responseHandled.set(res, true);
res.writeHead(504, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Proxy timeout' }));
}
},
on: {
proxyReq: (proxyReq, req, res) => {
const forwardedUrl = `${proxyReq.protocol}//${proxyReq.host}:${proxyReq.port}${proxyReq.path}`;
log(`转发请求到: ${proxyReq.method} ${forwardedUrl}`);
log(`请求头: ${JSON.stringify(req.headers, null, 2)}`);
const contentType = req.headers['content-type'] || '';
if (contentType.includes('application/json')) {
let requestBody = '';
req.on('data', (chunk) => { requestBody += chunk.toString(); });
req.on('end', () => { log(`请求体: ${requestBody}`); });
}
},
proxyRes: (proxyRes, req, res) => {
if (responseHandled.has(res)) {
log(`已处理响应,跳过重复处理: ${req.url}`);
return;
}
responseHandled.set(res, false);
log(`后端响应: ${proxyRes.statusCode} (${req.url})`);
log(`响应头: ${JSON.stringify(proxyRes.headers, null, 2)}`);
const contentType = proxyRes.headers['content-type'] || '';
const isChunked = proxyRes.headers['transfer-encoding'] === 'chunked';
// 复制响应头(排除可能引起冲突的头)
if (!res.headersSent) {
res.statusCode = proxyRes.statusCode;
Object.keys(proxyRes.headers).forEach(key => {
// 排除分块传输和连接控制头由Node.js自动处理
if (!['transfer-encoding', 'connection', 'content-length'].includes(key)) {
res.setHeader(key, proxyRes.headers[key]);
}
});
}
// 处理JSON响应
if (contentType.includes('application/json')) {
let responseBody = '';
const logStream = new PassThrough();
// 收集数据用于日志
logStream.on('data', (chunk) => {
responseBody += chunk.toString();
});
logStream.on('end', () => {
log(`响应体: ${responseBody}`);
});
// 分块传输处理
if (isChunked) {
// 直接通过流转发,保留分块特性
proxyRes.pipe(logStream).pipe(res);
} else {
// 非分块传输,收集完再发送
proxyRes.on('data', (chunk) => {
responseBody += chunk.toString();
});
proxyRes.on('end', () => {
if (!responseHandled.get(res)) {
responseHandled.set(res, true);
log(`响应体: ${responseBody}`);
res.end(responseBody);
}
});
}
} else {
// 非JSON响应直接转发
proxyRes.pipe(res);
}
// 监听响应结束
proxyRes.on('end', () => {
responseHandled.set(res, true);
});
proxyRes.on('error', (err) => {
if (!responseHandled.get(res)) {
responseHandled.set(res, true);
log(`响应错误: ${err.message}`);
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: '响应处理错误' }));
}
});
},
error: (err, req, res) => {
if (!responseHandled.has(res) || !responseHandled.get(res)) {
responseHandled.set(res, true);
log(`代理错误 [${err.code}]: ${err.message} (请求: ${req.url})`);
if (!res.headersSent) {
res.writeHead(500, { 'Content-Type': 'application/json' });
}
res.end(JSON.stringify({
error: '代理服务错误',
message: err.message
}));
}
}
}
});
const server = http.createServer((req, res) => {
if (req.url.startsWith(proxyConfig.path)) {
log(`收到代理请求: ${req.method} ${req.url}`);
return apiProxy(req, res);
}
else if (req.url === '' || req.url === '/' || req.url.endsWith('/')) {
log(`访问根路径返回index.html: ${req.url}`);
const indexPath = path.join(staticRoot, 'index.html');
if (fs.existsSync(indexPath)) {
res.writeHead(200, { 'Content-Type': 'text/html' });
fs.createReadStream(indexPath).pipe(res);
} else {
log(`警告: index.html不存在于 ${staticRoot}`);
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('index.html not found');
}
}
else {
log(`处理静态文件请求: ${req.url}`);
staticServer.server.emit('request', req, res);
}
});
const port = 8080;
server.listen(port, () => {
log(`静态服务器已启动,监听端口 ${port}`);
log(`静态文件目录: ${path.resolve(staticRoot)}`);
log(`API代理目标: ${proxyConfig.target}`);
log(`访问地址: http://localhost:${port}`);
});