fix(desktop): 动态分配 sidecar 端口替代硬编码,避免端口冲突

使用 net.createServer().listen(0) 探测可用端口,通过 PORT 环境变量
传递给 sidecar binary(VS Code language server 同款模式)
This commit is contained in:
2026-02-08 18:38:45 +08:00
parent 888f20fdab
commit 894fd17d1a

View File

@@ -1,13 +1,24 @@
import { spawn } from 'node:child_process' import { spawn } from 'node:child_process'
import type { AddressInfo } from 'node:net'
import { createServer } from 'node:net'
import { join } from 'node:path' import { join } from 'node:path'
import { app, BrowserWindow, shell } from 'electron' import { app, BrowserWindow, shell } from 'electron'
const DEV_SERVER_URL = 'http://localhost:3000' const DEV_SERVER_URL = 'http://localhost:3000'
const PROD_SERVER_PORT = 23_410
let mainWindow: BrowserWindow | null = null let mainWindow: BrowserWindow | null = null
let serverProcess: ReturnType<typeof spawn> | null = null let serverProcess: ReturnType<typeof spawn> | null = null
const getAvailablePort = (): Promise<number> =>
new Promise((resolve, reject) => {
const server = createServer()
server.listen(0, () => {
const { port } = server.address() as AddressInfo
server.close(() => resolve(port))
})
server.on('error', reject)
})
const isServerReady = async (url: string): Promise<boolean> => { const isServerReady = async (url: string): Promise<boolean> => {
try { try {
const response = await fetch(url, { method: 'HEAD' }) const response = await fetch(url, { method: 'HEAD' })
@@ -29,14 +40,14 @@ const waitForServer = async (
return false return false
} }
const spawnServer = (): string => { const spawnServer = (port: number): string => {
const binaryName = process.platform === 'win32' ? 'server.exe' : 'server' const binaryName = process.platform === 'win32' ? 'server.exe' : 'server'
const binaryPath = join(process.resourcesPath, binaryName) const binaryPath = join(process.resourcesPath, binaryName)
serverProcess = spawn(binaryPath, [], { serverProcess = spawn(binaryPath, [], {
env: { env: {
...process.env, ...process.env,
PORT: String(PROD_SERVER_PORT), PORT: String(port),
HOST: '127.0.0.1', HOST: '127.0.0.1',
}, },
stdio: 'pipe', stdio: 'pipe',
@@ -54,14 +65,15 @@ const spawnServer = (): string => {
console.error('Failed to start server:', err) console.error('Failed to start server:', err)
}) })
return `http://127.0.0.1:${PROD_SERVER_PORT}` return `http://127.0.0.1:${port}`
} }
const getServerUrl = (): string => { const getServerUrl = async (): Promise<string> => {
if (!app.isPackaged) { if (!app.isPackaged) {
return DEV_SERVER_URL return DEV_SERVER_URL
} }
return spawnServer() const port = await getAvailablePort()
return spawnServer(port)
} }
const createWindow = async () => { const createWindow = async () => {
@@ -87,7 +99,7 @@ const createWindow = async () => {
} }
mainWindow.show() mainWindow.show()
const serverUrl = getServerUrl() const serverUrl = await getServerUrl()
console.log(`Waiting for server at ${serverUrl}...`) console.log(`Waiting for server at ${serverUrl}...`)
const ready = await waitForServer(serverUrl) const ready = await waitForServer(serverUrl)