import { spawn } from 'node:child_process' import path from 'node:path' import { fileURLToPath } from 'node:url' const serverDir = path.dirname(fileURLToPath(import.meta.url)) export function startQuicServer({ onEvent } = {}) { if (process.env.QUIC_ENABLED === 'false') return null const script = path.join(serverDir, 'quic', 'server.py') const python = process.env.QUIC_PYTHON || (process.platform === 'win32' ? 'py' : 'python3') const args = process.platform === 'win32' && !process.env.QUIC_PYTHON ? ['-3', script] : [script] const child = spawn(python, args, { cwd: path.resolve(serverDir, '..'), env: { ...process.env, QUIC_HOST: process.env.QUIC_HOST || '0.0.0.0', QUIC_PORT: process.env.QUIC_PORT || '5175', QUIC_CERT_FILE: process.env.QUIC_CERT_FILE || path.join(serverDir, 'certs', 'quic-gateway.crt'), QUIC_KEY_FILE: process.env.QUIC_KEY_FILE || path.join(serverDir, 'certs', 'quic-gateway.key') }, stdio: ['ignore', 'pipe', 'pipe'] }) child.stdout.setEncoding('utf8') child.stderr.setEncoding('utf8') let stdoutBuffer = '' child.stdout.on('data', data => { stdoutBuffer += data const lines = stdoutBuffer.split(/\r?\n/) stdoutBuffer = lines.pop() || '' for (const line of lines) { if (!line) continue console.log(line) try { onEvent?.(JSON.parse(line)) } catch { // Keep non-JSON Python diagnostics visible without feeding the registry. } } }) child.stderr.on('data', data => process.stderr.write(data)) child.on('error', error => console.error('[quic] 无法启动 Python 服务:', error.message)) child.on('exit', (code, signal) => { if (code !== 0 && signal !== 'SIGTERM') { console.error(`[quic] 服务异常退出 (code=${code}, signal=${signal || 'none'})`) } }) const stop = () => { if (!child.killed) child.kill() } process.once('SIGINT', stop) process.once('SIGTERM', stop) process.once('exit', stop) return child }