// server.js —— 零依赖反向代理版 const http = require('http'); const https = require('https'); const fs = require('fs'); const path = require('path'); const url = require('url'); // 读取配置 const configPath = process.env.CONFIG_PATH || './runtime-config.json'; const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); //const ROOT = path.join(__dirname, 'dist', 'h5'); const PORT = config["local-port"]; const REMOTE = config["remote-host"];//'http://101.43.219.60:7700'; 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' }; /* ------- 代理函数 ------- */ function proxyToRemote(req, res) { const parsed = url.parse(req.url); // /dr/xxx?query const path = parsed.path; const target = url.parse(REMOTE); const options = { hostname: target.hostname, port: target.port || (target.protocol === 'https:' ? 443 : 80), path: path + (parsed.search || ''), method: req.method, headers: { ...req.headers } // 原样带过去 }; // 删除本地头,避免 host 不匹配 delete options.headers.host; delete options.headers.origin; delete options.headers.referer; console.log('Options:', options); console.log('Path:', options.path); const proxyReq = (target.protocol === 'https:' ? https : http).request(options, (proxyRes) => { // 把远端响应头原样写给浏览器 res.writeHead(proxyRes.statusCode, proxyRes.headers); proxyRes.pipe(res); }).on('error', e => { console.error('[proxy error]', e.message); res.writeHead(502).end('Bad Gateway'); }); req.pipe(proxyReq); // 把浏览器发来的 body 也转发过去 } /* ------- 静态文件服务函数 ------- */ function serveStatic(req, res) { const parsed = url.parse(req.url, true); let pathname = parsed.pathname; // 默认索引 if (pathname === '/') pathname = '/index.html'; // 安全限制:禁止跳出 static const safePath = path.normalize(decodeURIComponent(pathname)); const STATIC_DIR = path.resolve(config.staticPath || './static'); const filePath = path.join(STATIC_DIR, safePath); if (!filePath.startsWith(STATIC_DIR)) { res.statusCode = 403; res.end('Forbidden'); return; } fs.readFile(filePath, (err, data) => { if (err) { if (err.code === 'ENOENT') { res.statusCode = 404; res.end('Not Found'); } else { res.statusCode = 500; res.end('Internal Server Error'); } return; } const ext = path.extname(filePath).toLowerCase(); res.setHeader('Content-Type', mime[ext] || 'application/octet-stream'); res.end(data); }); } /* ------- 主入口 ------- */ http.createServer((req, res) => { // 1. 预检直接 200 if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } // 2. 路径以 /dr 开头 → 代理 if (req.url.startsWith('/dr')) { proxyToRemote(req, res); return; } // 3. 其它 → 静态文件 serveStatic(req, res); }).listen(PORT, () => console.log(`> H5 running at http://localhost:${PORT}`));