server.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. // server.js —— 零依赖反向代理版
  2. const http = require('http');
  3. const https = require('https');
  4. const fs = require('fs');
  5. const path = require('path');
  6. const url = require('url');
  7. // 读取配置
  8. const configPath = process.env.CONFIG_PATH || './runtime-config.json';
  9. const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
  10. //const ROOT = path.join(__dirname, 'dist', 'h5');
  11. const PORT = config["local-port"];
  12. const REMOTE = config["remote-host"];//'http://101.43.219.60:7700';
  13. const mime = { '.html': 'text/html', '.js': 'application/javascript', '.css': 'text/css', '.json': 'application/json', '.png': 'image/png', '.jpg': 'image/jpeg', '.gif': 'image/gif', '.svg': 'image/svg+xml', '.ico': 'image/x-icon' };
  14. /* ------- 代理函数 ------- */
  15. function proxyToRemote(req, res) {
  16. const parsed = url.parse(req.url); // /dr/xxx?query
  17. const path = parsed.path;
  18. const target = url.parse(REMOTE);
  19. const options = {
  20. hostname: target.hostname,
  21. port: target.port || (target.protocol === 'https:' ? 443 : 80),
  22. path: path + (parsed.search || ''),
  23. method: req.method,
  24. headers: { ...req.headers } // 原样带过去
  25. };
  26. // 删除本地头,避免 host 不匹配
  27. delete options.headers.host;
  28. delete options.headers.origin;
  29. delete options.headers.referer;
  30. console.log('Options:', options);
  31. console.log('Path:', options.path);
  32. const proxyReq = (target.protocol === 'https:' ? https : http).request(options, (proxyRes) => {
  33. // 把远端响应头原样写给浏览器
  34. res.writeHead(proxyRes.statusCode, proxyRes.headers);
  35. proxyRes.pipe(res);
  36. }).on('error', e => {
  37. console.error('[proxy error]', e.message);
  38. res.writeHead(502).end('Bad Gateway');
  39. });
  40. req.pipe(proxyReq); // 把浏览器发来的 body 也转发过去
  41. }
  42. /* ------- 静态文件服务函数 ------- */
  43. function serveStatic(req, res) {
  44. const parsed = url.parse(req.url, true);
  45. let pathname = parsed.pathname;
  46. // 默认索引
  47. if (pathname === '/') pathname = '/index.html';
  48. // 安全限制:禁止跳出 static
  49. const safePath = path.normalize(decodeURIComponent(pathname));
  50. const STATIC_DIR = path.resolve(config.staticPath || './static');
  51. const filePath = path.join(STATIC_DIR, safePath);
  52. if (!filePath.startsWith(STATIC_DIR)) {
  53. res.statusCode = 403;
  54. res.end('Forbidden');
  55. return;
  56. }
  57. fs.readFile(filePath, (err, data) => {
  58. if (err) {
  59. if (err.code === 'ENOENT') {
  60. res.statusCode = 404;
  61. res.end('Not Found');
  62. } else {
  63. res.statusCode = 500;
  64. res.end('Internal Server Error');
  65. }
  66. return;
  67. }
  68. const ext = path.extname(filePath).toLowerCase();
  69. res.setHeader('Content-Type', mime[ext] || 'application/octet-stream');
  70. res.end(data);
  71. });
  72. }
  73. /* ------- 主入口 ------- */
  74. http.createServer((req, res) => {
  75. // 1. 预检直接 200
  76. if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; }
  77. // 2. 路径以 /dr 开头 → 代理
  78. if (req.url.startsWith('/dr')) { proxyToRemote(req, res); return; }
  79. // 3. 其它 → 静态文件
  80. serveStatic(req, res);
  81. }).listen(PORT, () => console.log(`> H5 running at http://localhost:${PORT}`));