// server.js —— 主进程内嵌的本地 HTTP 服务: // 静态托管 renderer-dist(前端构建产物)+ 把 /api 反代到可配置后端。 // 仅监听 127.0.0.1,不做公网暴露。反代参数对齐 deploy/nginx/plantai.conf: // SSE 流(问答/生信)不缓冲、长超时由后端自控,大文件上传不受限。 const path = require('path'); const express = require('express'); const { createProxyMiddleware } = require('http-proxy-middleware'); function createServer({ staticDir, backendUrl }) { const app = express(); const proxy = createProxyMiddleware({ target: backendUrl, changeOrigin: true, ws: false, xfwd: true, // http-proxy 侧不设响应/套接字超时(SSE 看门狗 1200s / 上传 600s 由后端自断) proxyTimeout: 0, timeout: 0, on: { error: (err, req, res) => { // 后端不可达时返回结构化 502 信封(对齐前端 http.js 的错误契约),避免进程崩溃。 // 注意:http-proxy 的 error 事件里 res 可能是 net.Socket(连接失败),需防御式判断。 if (!res || typeof res.writeHead !== 'function') return; if (res.headersSent) return res.end(); res.writeHead(502, { 'Content-Type': 'application/json; charset=utf-8' }); res.end( JSON.stringify({ ok: false, error: { code: 'NETWORK', message: '后端不可达', degraded: false }, }) ); }, }, }); // 只代理 /api,并保留完整路径(/api/xxx → backend/api/xxx)。 // 注意:不能写成 app.use('/api', proxy) —— Express 会剥掉 /api 前缀,导致后端收到 /xxx 而 404。 app.use((req, res, next) => { if (req.path.startsWith('/api')) return proxy(req, res, next); next(); }); // 静态托管 + SPA fallback(前端无 vue-router,单页;仍兜底到 index.html) app.use(express.static(staticDir)); app.use((req, res, next) => { if (req.method !== 'GET' || req.path.startsWith('/api')) return next(); res.sendFile(path.join(staticDir, 'index.html')); }); return app; } module.exports = { createServer };