5.6 KiB
5.6 KiB
Code Review:启动端口聚合实现
1. 总体评价
- 聚合度:端口默认值、配置键、解析规则集中在
shared/startupPorts.ts;主进程通过main/services/startupPorts.ts对接 DB 与 lsof/netstat,启动与重启均使用getConfiguredPorts(),逻辑集中、无散落硬编码。 - 可维护性:新增/修改端口只需改 shared 默认值与解析,main 与脚本行为一致。
- 安全性:端口来自配置或常量,
execSync仅传入数字端口,无 shell 注入风险;脚本内 SQL 的 key 仅用内部常量并做引号转义。
2. 文件审阅
2.1 src/shared/startupPorts.ts
| 项 | 结论 |
|---|---|
| step1 解析 | step1?.agentPort ?? default 正确;若 DB 存的是字符串或空对象会回退默认值。 |
| mcp 解析 | 兼容 number / string,parseInt 失败用默认值,逻辑正确。 |
| 类型 | GetSettingFn、StartupPorts 清晰,便于 main/测试复用。 |
| STORAGE_KEYS | 与 constants.ts 一致,避免键名硬编码。 |
可选增强:若将来需要严格校验,可对端口做范围检查(1–65535)并在解析后统一校验。
2.2 src/main/services/startupPorts.ts
| 项 | 结论 |
|---|---|
| getConfiguredPorts() | 在 readSetting 可用后调用(startup 在 DB 初始化之后),无时序问题。 |
| isPortInUse | port 为 number,拼接进命令安全;Windows 使用 :${port} 带空格,避免 60001 误匹配 600011。 |
| 多 PID | lsof -t 可能多行,只取第一行 PID 足够用于“是否占用”判断。 |
| 异常 | execSync 失败时 catch 返回 { inUse: false },避免进程崩溃。 |
跨平台:Windows 用 netstat + findstr,且必须传 shell: process.env.ComSpec || 'cmd.exe' 才能正确解析管道与 2>nul;macOS/Linux 用 lsof(单命令无需 shell)。netstat 输出取首行最后一列作为 PID;行尾为 \r\n 时用 split(/\r?\n/) 兼容。
2.3 scripts/tools/check-startup-ports.js
| 项 | 结论 |
|---|---|
| 与 shared 一致 | 默认值、键名、解析顺序与 shared/startupPorts.ts 一致,注释已说明需同步修改。 |
| getSetting | 使用 key.replace(/'/g, "''") 转义,key 仅来自内部常量,安全。 |
| DB 不存在 | 使用默认端口并打日志,行为合理。 |
| sqlite3 不可用 | 若 resolvePortsFromSettings 内调用 getSetting 时 execSync('sqlite3 ...') 抛错(如未安装 sqlite3),main() 的 try/catch 会捕获并回退默认端口 + 提示“读取 DB 失败”。 |
已做修改:删除未使用的 projectRoot;为 key 做 String(key) + 引号转义并加注释;DB 存在但读取失败时统一走默认端口并打明确日志。
2.4 调用方
| 位置 | 用法 | 结论 |
|---|---|---|
| startup.ts | getConfiguredPorts().agent 作为 ComputerServer 端口 |
正确,在 DB 已初始化后调用。 |
| serviceManager.ts | getConfiguredPorts().fileServer 启动 File Server |
正确;其余仍用 step1Config(workspaceDir 等)无重复。 |
| processHandlers.ts | 动态 import('../services/startupPorts') 后取 fileServer |
正确,避免循环依赖。 |
3. 边界情况
- step1_config 为 null/undefined:解析得到全部默认端口,符合预期。
- mcp_proxy_port 为非法字符串:
parseInt得 NaN,使用默认 18099。 - 端口被占用:由各服务自己的
listen()报 EADDRINUSE;checkPortsInUse/ 脚本仅做启动前检查与提示。 - Windows 无 lsof:使用 netstat,逻辑正确;脚本与 main 一致。
4. Windows 兼容性(已落实)
| 项 | 说明 |
|---|---|
| 管道与 shell | 主进程与脚本在 Windows 上执行 netstat 与 findstr 管道命令时,必须传入 shell: true(脚本)或 shell: process.env.ComSpec || 'cmd.exe'(main,满足 @types/node 的 shell: string),否则管道不生效。 |
| PID 解析 | netstat 输出可能多行(IPv4/IPv6),取首行并用 split(/\s+/).pop() 取最后一列作为 PID;行分隔用 /\r?\n/ 兼容 CRLF。 |
| findstr 无匹配 | 无匹配时 exit code 1,execSync 抛错,catch 后返回 inUse: false,行为正确。 |
| DB 路径 | 脚本用 path.join(home, '.qimingclaw', 'qimingclaw.db'),Windows 为 %USERPROFILE%\\.qimingclaw\\qimingclaw.db;调用 sqlite3 时路径可转为正斜杠以规避反斜杠转义,且 Windows 下传 shell: true 便于执行 sqlite3.cmd。 |
| sqlite3 | Windows 上多数环境未预装 sqlite3,脚本读 DB 失败时已回退到默认端口并提示。 |
5. 建议(可选)
- 端口范围:若需严格校验,可在
resolvePortsFromSettings返回前或startComputerServer/startFileServer前统一校验1 <= port <= 65535。 - 脚本与 shared 同步:修改
shared/startupPorts.ts的默认值或键时,记得同步改scripts/tools/check-startup-ports.js的 DEFAULTS/LABELS 和键名;必要时可考虑从 build 产物 require 解析逻辑以彻底单源(需解决 path alias 与运行环境)。 - IPC 暴露:若需要“设置页一键检查端口”,可在 preload 暴露
startup:getConfiguredPortsWithStatus,由渲染进程调 main 的getConfiguredPortsWithStatus(includeVite)并展示结果。
6. 小结
实现满足“逻辑尽量聚合”的目标,类型与异常处理合理,调用关系清晰。本次 review 中已做的小改动:脚本去掉未使用变量、key 转义与注释、DB 存在但读取失败时的回退与日志。其余为可选增强,可按需再做。