Skip to content

mHost v0.3.1

Choose a tag to compare

@github-actions github-actions released this 08 Jul 08:11

mHost v0.3.1

DNS 多 Profile 支持(issue #67 / PR #99)+ 8 次 app-exit DNS 清理修复终于打通 Cmd-Q 路径。

安装包见下方 Assets。Apple Silicon → mHost_0.3.1_aarch64.dmg;Intel Mac → mHost_0.3.1_x64.dmg。

Highlights

1. DNS 多 Profile(PR #99 / issue #67)

DNS mode 现在支持多 profile,类比 Hosts mode 的 profile 切换体验。包含 UI redesign、RuleEditor 集成、确定性 conflict 排序,加上 4 轮 follow-up fix。

  • 多 profile 同时启用:disable 一个 profile 不再 reload DNS 规则,避免 disable 其他 profile 的副作用(dual-file 持久化 + no-reload-on-disable)
  • 确定性 conflict 排序:rule 冲突时按稳定顺序处理,行为可预测
  • Profile-level 开关 UI:每个 profile 有独立的 enable/disable
  • Edit / Edit Info 双按钮:header 同时保留 Edit(→ 规则编辑器)和 Edit Info(→ name/description/tags)两个独立按钮
  • Review follow-ups:clippy 警告清理、duplicate mode 守卫、save_profile crash safety

PR #99 = 1 个 feature commit + 4 轮 follow-up(UX 4 bugs、manual-testing 3 bugs、union-not-reloading、review clippy/safety)

2. App-exit DNS 清理修复(8 commits,最终走通 Cmd-Q)

v0.3.0 在 enable DNS mode 之后,通过 tray Quit / Cmd-Q / Ctrl+C 三条路径退出时,DNS 不一定能 restore 回原始状态。v0.3.1 经过 8 次迭代终于打通。

  • 同步 disable DNS mode 再退出:在 ExitRequested 之前同步 disable,避免 cleanup 跑在 async runtime 关闭之后
  • 三路径统一用 interactive=true:让 proxy 自愈超时分支在 privileged proxy 死亡时弹 sudo
  • Cmd-Q 走 SIGTERM handler:让 Path A 和 Path B 复用同一套 async 路径
  • Path A 走 dedicated std::thread:避免 tao 主线程 + tokio runtime race(之前 block_on 在 ExitRequested 上下文里死锁)
  • 保留 Regular activation policy:Accessory 模式下 Cmd-Q 不触发 Tauri 的 ExitRequested,所以先回到 Regular(UX 妥协:会保留 Dock icon;issue #100 后续用 objc2 delegate 重新关闭 Dock)
  • objc2 NSApplicationDelegate override:runtime 装一个 MhostQuitHandlerDelegate,覆盖 applicationShouldTerminate: 在 OS 层拦截 Cmd-Q,同步跑 DNS cleanup 后返回 NSTerminateNow(这才是 OS 级 Cmd-Q 的真正出路)
  • Box::leak 存 stable AppHandle 指针:之前用 &local_clone 会在 setup 返回后变 dangling pointer 导致 SIGSEGV
  • Box cast 修正:原始 &Box<AppHandle> as *const _ as *mut () 错把 Arc 指针当 Box 指针,dereference 时触发 misaligned_pointer_dereference panic;改成直接 Box::leak(Box::new(app.handle().clone())) 存 AppHandle 本身

成功路径:tray Quit / Cmd-Q / SIGINT / SIGTERM 全部走 cleanup_dns_on_exit(state, interactive=true),幂等(dns_enabled=false 是 no-op)。失败兜底:400ms watchdog 强杀。

3. DNS LRU response cache 在 reload_rules 时失效

reload_rules 之后继续返回 stale query 响应(hit cache 返回了过期的上游 IP)。改成 reload 时清空 LRU cache。

4. CLAUDE.md 更新 Exit/DNS cleanup 契约

记录 Cmd-Q 在 Tauri 2 on macOS 上 不 触发 RunEvent::ExitRequested(tao 直接返回 NSTerminateNow)。所有新加 exit 路径必须 hook OS 级事件。

Files changed

PR #99(DNS multi-profile)

  • crates/mhost-storage/src/manifest.rs — DnsProfile 持久化 + dual-file 拆分
  • crates/mhost-dns/src/{config,server,platform}.rs — multi-profile 支持
  • src/components/DnsProfileView.tsx — Edit 按钮 + RuleEditor
  • src/components/RuleEditor.tsx — DNS rule editor UI
  • src/stores/dns.ts — multi-profile state
  • 4 轮 follow-up commits:review clippy、duplicate 守卫、save_profile crash safety、union-not-reloading、UX 4 bugs、manual-testing 3 bugs

App-exit cleanup

  • src-tauri/src/lib.rs — Path A: dedicated std::thread + Box::leak 存 AppHandle + 修复 Box cast
  • src-tauri/src/platform/macos.rs — install_quit_handler 用 objc2 ClassBuilder 装 MhostQuitHandlerDelegate
  • src-tauri/src/tray.rs — 同步调用 cleanup_dns_on_exit
  • src-tauri/src/commands/dns.rs — cleanup_dns_on_exit 幂等
  • 4 处 cleanup 路径(tray Quit / Cmd-Q / SIGINT / SIGTERM)统一 interactive=true

其他

  • crates/mhost-dns/src/server.rs — LRU cache invalidate on reload_rules
  • CLAUDE.md — 退出契约文档

数字

  • Rust: 354 测试通过(v0.3.0 的 339 + 15 新增)
  • 前端: 194 测试通过(v0.3.0 的 166 + 28 新增)
  • cargo fmt --check clean
  • cargo clippy --all-targets --all-features -- -D warnings clean
  • pnpm build clean
  • cargo build --release clean
  • CI: macos-latest(aarch64 + x86_64 matrix build)通过

Migration notes

升级到 v0.3.1:

  1. 数据:DNS mode 多 profile 持久化格式向前兼容 v0.3.0;升级后第一次 enable DNS mode 时会按新 dual-file schema 重写 manifest
  2. 不需要停用 DNS mode:升级过程中 DNS server 会自动重启;如有强需求,disable 再升级也安全