Files
iboard/server/powersync-proxy.js
T

55 lines
2.0 KiB
JavaScript

// 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)
}
}