mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-10-25 17:54:34 +08:00
feat(cli): pre-check port availability before startup with friendly error
启动前主动探测端口是否可用,被占用时立即给出可操作的引导(换端口 / lsof 命令), 避免先初始化 DB/路由后才抛 EADDRINUSE 的延迟报错体验。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
0a916e4b17
commit
8c98daa654
2 files changed
+42
-1
No files matched your search
+9
-1
@@ -24,6 +24,7 @@ import {
|
||||
} from '@openchatlab/core'
|
||||
import { getVersion } from './version'
|
||||
import { resolveCliPath } from './paths'
|
||||
import { isPortAvailable, formatPortInUseError } from './http/port'
|
||||
|
||||
const program = new Command()
|
||||
|
||||
@@ -303,6 +304,12 @@ program
|
||||
const { startHttpServer } = await import('./http')
|
||||
const port = parseInt(options.port, 10)
|
||||
|
||||
// 启动前预检端口,快速失败,避免无谓的初始化后再报 EADDRINUSE
|
||||
if (!(await isPortAvailable(port, options.host))) {
|
||||
console.error(formatPortInUseError(port))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
let webRoot: string | undefined
|
||||
if (!options.headless) {
|
||||
const webDir = resolveCliPath('dist-web')
|
||||
@@ -353,7 +360,8 @@ program
|
||||
} catch (err: unknown) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
if (message.includes('EADDRINUSE')) {
|
||||
console.error(`Error: port ${port} is already in use`)
|
||||
// 极低概率的 TOCTOU 竞态(预检通过后端口被占),复用统一文案
|
||||
console.error(formatPortInUseError(port))
|
||||
} else {
|
||||
console.error(`Startup failed: ${message}`)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 端口可用性检测与错误文案
|
||||
*/
|
||||
|
||||
import * as net from 'net'
|
||||
|
||||
/**
|
||||
* 检测指定端口在给定 host 上是否可用。
|
||||
* 使用 net.createServer() 试探性绑定,无副作用(成功后立即释放)。
|
||||
*/
|
||||
export function isPortAvailable(port: number, host: string): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
const server = net.createServer()
|
||||
server.once('error', () => resolve(false))
|
||||
server.once('listening', () => server.close(() => resolve(true)))
|
||||
server.listen(port, host)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回端口被占用时统一的友好报错文案。
|
||||
*/
|
||||
export function formatPortInUseError(port: number): string {
|
||||
return [
|
||||
``,
|
||||
` ✖ Error: port ${port} is already in use.`,
|
||||
``,
|
||||
` Another process (possibly a ChatLab instance) is using this port. You can:`,
|
||||
` • Use another port: chatlab start --port <port>`,
|
||||
` • Find the process: lsof -iTCP:${port} -sTCP:LISTEN`,
|
||||
``,
|
||||
].join('\n')
|
||||
}
|
||||
Reference in new issue
Block a user