---初始化后台管理web页面项目
This commit is contained in:
179
docker/server.js
Normal file
179
docker/server.js
Normal file
@ -0,0 +1,179 @@
|
||||
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}`);
|
||||
});
|
||||
Reference in New Issue
Block a user