mHost v0.3.1
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_dereferencepanic;改成直接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 按钮 + RuleEditorsrc/components/RuleEditor.tsx— DNS rule editor UIsrc/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: dedicatedstd::thread+Box::leak存 AppHandle + 修复 Box castsrc-tauri/src/platform/macos.rs—install_quit_handler用 objc2ClassBuilder装MhostQuitHandlerDelegatesrc-tauri/src/tray.rs— 同步调用cleanup_dns_on_exitsrc-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_rulesCLAUDE.md— 退出契约文档
数字
- Rust: 354 测试通过(v0.3.0 的 339 + 15 新增)
- 前端: 194 测试通过(v0.3.0 的 166 + 28 新增)
cargo fmt --checkcleancargo clippy --all-targets --all-features -- -D warningscleanpnpm buildcleancargo build --releaseclean- CI: macos-latest(aarch64 + x86_64 matrix build)通过
Migration notes
升级到 v0.3.1:
- 数据:DNS mode 多 profile 持久化格式向前兼容 v0.3.0;升级后第一次 enable DNS mode 时会按新 dual-file schema 重写 manifest
- 不需要停用 DNS mode:升级过程中 DNS server 会自动重启;如有强需求,disable 再升级也安全