| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- const http = require('http')
- const fs = require('fs')
- const path = require('path')
- const PORT = 3001
- const BACKEND = 'http://127.0.0.1:8583'
- const STATIC_DIR = path.join(__dirname, 'dist', 'build', 'h5')
- const API_PREFIX = '/forge-h5-api'
- const APP_BASE = '/forge-h5'
- const MIME_TYPES = {
- '.html': 'text/html; charset=utf-8',
- '.js': 'application/javascript; charset=utf-8',
- '.css': 'text/css; charset=utf-8',
- '.json': 'application/json; charset=utf-8',
- '.png': 'image/png',
- '.jpg': 'image/jpeg',
- '.jpeg': 'image/jpeg',
- '.gif': 'image/gif',
- '.svg': 'image/svg+xml',
- '.ico': 'image/x-icon',
- '.woff': 'font/woff',
- '.woff2': 'font/woff2',
- '.ttf': 'font/ttf',
- '.map': 'application/json',
- }
- function getMimeType(filePath) {
- const ext = path.extname(filePath).toLowerCase()
- return MIME_TYPES[ext] || 'application/octet-stream'
- }
- function proxyRequest(req, res) {
- const url = req.url.replace(API_PREFIX, '')
- const options = {
- hostname: '127.0.0.1',
- port: 8583,
- path: url,
- method: req.method,
- headers: {
- ...req.headers,
- host: '127.0.0.1:8583',
- },
- }
- const proxyReq = http.request(options, (proxyRes) => {
- // 复制响应头
- const headers = {}
- for (const [key, value] of Object.entries(proxyRes.headers)) {
- if (key !== 'transfer-encoding') {
- headers[key] = value
- }
- }
- res.writeHead(proxyRes.statusCode, headers)
- proxyRes.pipe(res)
- })
- proxyReq.on('error', (err) => {
- console.error(`[API Proxy Error] ${req.method} ${url}:`, err.message)
- res.writeHead(502, { 'Content-Type': 'application/json' })
- res.end(JSON.stringify({ error: 'Backend service unavailable' }))
- })
- req.pipe(proxyReq)
- }
- function serveStatic(req, res) {
- let urlPath = req.url
- // 去掉 /forge-h5 前缀,映射到静态文件目录
- if (urlPath.startsWith(APP_BASE)) {
- urlPath = urlPath.slice(APP_BASE.length) || '/'
- }
- let filePath = path.join(STATIC_DIR, urlPath === '/' ? '/index.html' : urlPath)
- // 安全检查:防止路径遍历
- if (!filePath.startsWith(STATIC_DIR)) {
- res.writeHead(403)
- res.end('Forbidden')
- return
- }
- fs.readFile(filePath, (err, data) => {
- if (err) {
- // SPA 回退:如果文件不存在,返回 index.html
- fs.readFile(path.join(STATIC_DIR, 'index.html'), (err2, data2) => {
- if (err2) {
- res.writeHead(500)
- res.end('Internal Server Error')
- return
- }
- res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })
- res.end(data2)
- })
- return
- }
- res.writeHead(200, { 'Content-Type': getMimeType(filePath) })
- res.end(data)
- })
- }
- const server = http.createServer((req, res) => {
- // API 请求代理到后端
- if (req.url.startsWith(API_PREFIX)) {
- console.log(`[API] ${req.method} ${req.url} -> ${BACKEND}${req.url.replace(API_PREFIX, '')}`)
- proxyRequest(req, res)
- } else {
- // 静态文件服务
- console.log(`[Static] ${req.method} ${req.url}`)
- serveStatic(req, res)
- }
- })
- server.listen(PORT, '0.0.0.0', () => {
- console.log(`Server running at http://0.0.0.0:${PORT}`)
- console.log(`Static files: ${STATIC_DIR}`)
- console.log(`API proxy: ${API_PREFIX} -> ${BACKEND}`)
- })
|