Files
cdp_hub/docs/04-architecture.md
T

61 lines
8.9 KiB
Markdown
Raw Normal View History

2026-07-22 14:51:11 +08:00
# 架构设计
```text
Gio UI / CLI
↓ DTO + commands
Application BrowserManager
↓ ports
Platform Windows
├─ executable discovery
├─ launcher
├─ process identity / query
├─ graceful close / force kill
├─ Job Object
2026-07-27 10:35:32 +08:00
├─ profile occupancy inspector
2026-07-25 15:36:23 +08:00
├─ loopback CDP endpoint inspector
2026-07-27 09:46:21 +08:00
├─ loopback CDP target reader (read-only `/json/list`)
2026-07-25 16:21:38 +08:00
├─ remote-debug port allocator
2026-07-27 10:35:32 +08:00
├─ instance status refresher
2026-07-27 11:25:59 +08:00
├─ portable config path resolver / legacy migrator
├─ Windows executable file picker
2026-07-27 10:35:32 +08:00
└─ notification-area adapter (Windows Shell)
2026-07-22 14:51:11 +08:00
↓
Chrome / Edge processes
```
UI/CLI 不直接操作 `exec.Cmd`、Win32 handle 或数据库。Application 层维护实例状态和授权边界;Platform 层只提供系统能力。
## 实例身份
2026-07-25 15:36:23 +08:00
每个实例至少记录 `instance_id`、browser kind、executable、root PID、user data dir、profile directory、非敏感参数摘要、关联的 loopback CDP 端口、归属(托管或外部关联)、创建时间、状态和退出码。
2026-07-22 14:51:11 +08:00
进程身份校验至少比较 PID 存活、executable、命令行中的规范化 `--user-data-dir` 和注册记录。无法确认身份时只能报告 UNKNOWN,不允许关闭。
## 生命周期
`CREATED -> STARTING -> RUNNING -> STOPPING -> EXITED`;启动失败为 `FAILED`,无法确认的外部实例为 `UNKNOWN`。
- Start:规范化路径和参数,先检查 user data dir。未占用时优先从实例持久化的首选端口分配;UI 在启动快照中提供其他 Chub 实例的逻辑预留/实际端口,启动器跳过这些端口后检查 loopback 可用性。注册 pending 后以受控的 `--remote-debugging-address=127.0.0.1` 和 `--remote-debugging-port` 启动,并直接请求该已知端口的 `/json/version` 验证实际端点;发生外部端口冲突且回退成功时才更新当前实例首选端口。
2026-07-25 15:36:23 +08:00
- 外部关联:目录被占用时只检查该目录中的 `DevToolsActivePort` 和其 loopback `/json/version`;端点与浏览器类型均匹配才报告 `EXTERNAL_ASSOCIATED`,不注册、关闭、重启或强制终止该 PID。无法验证时保持 `UNKNOWN`/占用并拒绝启动。
2026-07-22 14:51:11 +08:00
- Stop:校验身份,优先窗口关闭/浏览器协议,超时后经明确授权强制终止。
- Force stop:使用实例 Job Object 或已校验进程树,不按名称全局终止。
- Restart:旧实例退出或完成超时处理后重新执行同一配置。
- App shutdown:取消监控,不默认终止用户浏览器。
2026-07-25 16:21:38 +08:00
- Refresh:以 UI 传入的保存实例快照为范围,在后台读取 Chub registry、对应 profile 占用证据与已知 loopback CDP 端点,返回状态 DTO。刷新不按浏览器进程名或端口范围枚举;结果以实例 ID 与快照版本关联,UI 丢弃已删除或已编辑行的过期结果。
- Edit:UI 只提交可持久化的实例配置 DTO;运行时 PID、关联端口、占用来源和状态由刷新/启动结果返回,不作为编辑表单保存字段。编辑时保留未公开的非敏感启动选项,活跃或外部关联实例的身份约束字段保持只读。
- Proxy:本地配置保存稳定 `proxy_id` 指向的代理库项(必填、大小写无关唯一的显示名称与无认证端点);启动 adapter 在后台解析为当前 `LaunchSpec.ProxyServer` 快照。名称与地址同次校验/保存,`proxy_id` 不因改名或改地址而改变;代理不能含 userinfo,删除被引用项会被拒绝;修改代理只影响后续启动,不影响已运行实例。Gio 列表按当前 `proxy_id` 实时解析名称,不把可变显示名反规范化写入实例配置。
2026-07-27 23:50:24 +08:00
- Portable defaults and instance naming:platform config 以实际 `os.Executable()` 的父目录(不是当前工作目录)解析 `user_data_dirs`、`logs` 两个跟随程序目录默认值。`Settings` 持久化每个目录的 `portable/custom` 模式:portable 在每次启动重新解析当前 exe 目录,custom 保留用户选择的绝对路径;旧空值及历史内置默认迁移为 portable,其他非空路径保留 custom。Gio 只显示已解析路径和模式,不自行读取/创建目录。新实例先分配稳定 ID,并由 application helper 生成 `<default-user-data-dir>\\instance-<safe-name>-<short-id>`;safe-name 独立于显示名称,处理 Windows 非法字符、设备名、尾部句点/空格、长度与安全化冲突。实例名称在 UI 与 config 写入边界按 Windows 不区分大小写规则唯一;编辑改名不移动既有 profile。启动 adapter 在启动浏览器前后台 `MkdirAll` 并验证该实例目录可写,失败不调用 launcher;日志目录初始化同样在 platform/application 后台处理,不能静默改到工作目录。
2026-07-25 16:53:44 +08:00
- GUI stop:主操作只对 Chub registry 中可验证的托管实例调用 `StopProfile(userDataDir, false)`,随后有限等待 registry 释放。外部关联、占用和未知实例永远不调用停止接口;超时或失败不自动强制关闭。
- 受管退出监控:仅为当前会话由 Chub 启动并获得进程句柄的实例建立一个 `Wait` 监控任务。退出事件携带实例 ID、启动代次和 PID,UI 仅在三者仍匹配且当前未处于 Chub 请求的停止中时,将运行时状态改为“已退出”并排队显示提示;过期事件、外部实例和应用退出后的监控取消均不得生成意外退出提示。
2026-07-27 09:46:21 +08:00
- CDP 页面目标查看:UI 仅将当前已保存实例快照交给后台 adapter;adapter 只接受“运行中”或“外部已关联”的有效实际端口,先复核对应 browser kind 的 loopback `/json/version`,再读取同一端口的 `/json/list`。结果 DTO 仅含 target ID、标题、类型和脱敏 URL;UI 以实例 ID、端口和请求代次丢弃过期结果。该链路不建立 WebSocket、不开启页面控制,也不提供物理窗口 ID。
2026-07-27 10:35:32 +08:00
- 通知区域:Windows platform adapter 在独立原生消息循环中管理一个图标、Tooltip 和原生菜单,向 `cmd/chub` 发送仅含“显示主窗口 / 刷新状态 / 退出应用”的 typed command。应用 coordinator 决定如何显示 Gio 窗口、发起既有刷新或执行退出清理;adapter 不持有浏览器进程句柄,不调用浏览器启动/停止接口。图标状态仅来自非敏感实例计数;Explorer 重启后重新注册,应用结束前删除图标。首版不拦截 Gio 的窗口关闭消息,也不隐藏主窗口。
2026-07-27 11:25:59 +08:00
- 配置定位与迁移:`config.OpenDefault()` 先用 `os.Executable()` 的目录解析目标 `config.json`,不依赖工作目录。目标文件已存在时直接使用它;目标不存在时仅尝试读取旧 `%APPDATA%\\chub\\config.json`,通过现有 decode/normalize 校验后以 Store 原子写入目标,并把旧文件保留为只读恢复备份。迁移失败不会写入目标、不会删除旧文件、不会回退到 AppData 继续运行;应用层只显示可恢复的启动诊断,不显示配置内容。CLI 与 GUI 共用该入口,避免出现不同配置来源。
- 浏览器可执行文件选择:`internal/platform/files.ExecutablePicker` 仅负责通过 Windows 原生 `OpenFileDialog` 返回已存在的绝对 `.exe` 路径;Gio shell 通过后台 adapter 调用并以字段和请求代次接收结果,不在 UI frame 中打开对话框或执行系统 I/O。Chrome 与 Edge 各自保存选择中状态和字段级反馈;取消、失败和不合规结果不改写 editor。标准路径搜索沿用独立可取消任务,但成功结果(包括与当前输入相同的确认结果)必须显示在发起字段旁,避免被页面下方的无关状态区遮蔽。
2026-07-27 22:59:10 +08:00
- Delete instance data:实例删除默认只移除本地配置。用户在确认弹层显式勾选后,Gio shell 以实例 ID、目录快照和请求代次向后台 `InstanceDataDeleter` 发起不可取消的数据删除;执行期间弹层保留并禁用重复/取消操作。`cmd/chub` adapter 在 `RemoveAll` 前重新检查 Chub registry 和 Windows 外部 profile 占用,任一检查失败或发现占用均拒绝;`internal/platform/files.InstanceDataRemover` 只接受绝对、清理后的非卷根目录,并拒绝普通文件、符号链接或重解析点。删除成功后 shell 才删除实例行并持久化配置;任何检查或文件删除失败都保留实例行、配置和目录。该路径不枚举、关闭或接管浏览器进程。
2026-07-22 14:51:11 +08:00
2026-07-27 09:46:21 +08:00
Chrome 和 Edge 共用大部分 Chromium 参数,但 executable 默认路径、进程名称和安装方式不同,通过 `BrowserDefinition` 封装差异。CDP 端点只绑定 `127.0.0.1`;端口号和 `/json/version` 是诊断数据,WebSocket URL 不进入持久化、事件或普通 UI 文案。`/json/list` 仅用于只读页面目标信息;URL 在 DTO 边界移除 userinfo、query 和 fragment。
2026-07-22 14:51:11 +08:00
## 风险
Chromium 可能复用已有进程;浏览器是多进程架构;管理员权限进程可能无法查询;强制终止可能损坏 profile;CDP 端口开放到非 loopback 会形成控制风险,MVP 默认关闭。