Files
geg-gas-web/docker/server.js

179 lines
5.6 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.

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}`);
});