// PowerSync 同域反代 (0.2.1): 把 /sync、/socket 转发到内网同步服务, // 对外只暴露应用本身一个端口 (无需单独的 powersync 端口)。 // - 未配置 POWERSYNC_UPSTREAM 时直接跳过 (本地开发/无同步服务时行为不变) // - 必须挂在 express.json()/cors 之前: 同步走长连接流式响应, 不能被 body 解析打断 import http from 'node:http' import https from 'node:https' const PREFIXES = ['/sync', '/socket'] export function powersyncProxy() { const upstream = process.env.POWERSYNC_UPSTREAM if (!upstream) return (_req, _res, next) => next() let target try { target = new URL(upstream) } catch { console.error('[powersync-proxy] POWERSYNC_UPSTREAM 非法:', upstream) return (_req, _res, next) => next() } const transport = target.protocol === 'https:' ? https : http const basePort = Number(target.port) || (target.protocol === 'https:' ? 443 : 80) console.log('[powersync-proxy] 同步请求转发到', target.origin) return (req, res, next) => { if (!PREFIXES.some((p) => req.url === p || req.url.startsWith(p + '/') || req.url.startsWith(p + '?'))) { return next() } const headers = { ...req.headers, host: target.host } const upstreamReq = transport.request( { protocol: target.protocol, hostname: target.hostname, port: basePort, method: req.method, path: req.url, headers, }, (upstreamRes) => { res.writeHead(upstreamRes.statusCode || 502, upstreamRes.headers) upstreamRes.pipe(res) }, ) upstreamReq.on('error', (e) => { console.error('[powersync-proxy] 转发失败:', e.message) if (!res.headersSent) res.writeHead(502, { 'content-type': 'text/plain' }) res.end('powersync upstream error') }) // 长连接: 客户端断开时同步断开上游 req.on('aborted', () => upstreamReq.destroy()) res.on('close', () => upstreamReq.destroy()) req.pipe(upstreamReq) } }