mHost v0.3.0
mHost v0.3.0
DNS mode hardening (capture/restore + mid-session upstream refresh) and DNS profile detail page UI fixes.
安装包见下方 Assets。Apple Silicon →
mHost_0.3.0_aarch64.dmg;Intel Mac →mHost_0.3.0_x64.dmg。
Highlights
1. DNS capture/restore 按用户原始配置分流(PR #96)
之前 enable DNS mode 时,三层 fallback (networksetup → ipconfig → 公共 DNS) 合并返回成一个 Vec<String> 丢掉了「用户是否手动配过」这个信号。后果:在家 WiFi(DHCP-only,没动过 DNS)enable DNS mode 之后,到公司 WiFi disable 时,会把家里的 DHCP 推的 IP 错误地写回到公司 DNS 上。
现在把 capture 拆成两部分:
capture_dns_state() → OriginalDns- Tier 1 (
networksetup -getdnsservers <iface>) 非空 →Manual(servers):用户在 System Settings 里手动配的 - 其余情况 →
DhcpEmpty:用户没手动配(DHCP default / 真的空)
- Tier 1 (
get_upstream_resolvers() → Vec<String>- 完整 fallback chain(含公共兜底),仅供 DNS server 转发用
Manual snapshot 的 restore 行为不变(写回原 IP 列表);DhcpEmpty 的 restore 写 Empty(DHCP default),避免跨网切换时泄漏上次抓的 DHCP 推 IP。
2. Mid-session 上游 DNS 自动刷新(PR #96)
- Manual snapshot → 上游 freeze 在 enable 时刻的那组 IP(用户自己的设定,不替他做主)
- DhcpEmpty snapshot → DnsServer 启动后台 task,每 60s 调一次
get_upstream_resolvers(),对比当前上游,变化才 hot-swap resolver(parking_lot::RwLock<Arc<TokioAsyncResolver>>);stop()通过tokio::sync::Notify让后台 task 立即退出
用户场景:enable DNS mode 后长期不 disable,但中途切换 WiFi 忘记——上游会自动跟上新网络。
3. DNS 详情页补回 RuleEditor 和 Edit 按钮(PR #97)
PR #92(Phase 2)拆分 ProfileView 的时候,DnsProfileView 缺了一块:
- header 缺独立的「Edit」(→ 规则编辑器)按钮
- main 缺
<RuleEditor>渲染区
(v0.2 早期注释说「编辑走独立 dialog」,但 dialog 从未实现。)
这次镜像 HostsProfileView 集成规则编辑器,header 同时保留 Edit(→ 规则)和 Edit Info(→ name/description/tags)两个独立按钮。
4. ACTIVE 状态条改回 sidebar 底部(PR #97)
之前 <StatusBar /> 在 Layout.tsx 被渲染成 display: flex 行布局的第三个 column——但 CSS class 名字叫 .sidebarFooter,语义上是 sidebar 底部。这次把它从 Layout 移进 Sidebar 内部,用 sidebarSpacer { flex: 1 } 推到 sidebar 底部。
5. Manifest migration(向后兼容)
Manifest.original_dns 从 Option<Vec<String>> 改成 Option<OriginalDns>。自定义 Deserialize 兼容五种磁盘格式:
| 磁盘格式 | 解码为 |
|---|---|
"original_dns": null / 字段缺失 |
None |
"original_dns": [] |
Some(DhcpEmpty) |
"original_dns": ["Empty"] |
Some(DhcpEmpty) (v2.0 占位符) |
"original_dns": ["1.1.1.1"] |
Some(Manual(["1.1.1.1"])) |
{"kind":"manual","servers":[...]} (新格式) |
Some(Manual(vec)) |
{"kind":"dhcp_empty"} (新格式) |
Some(DhcpEmpty) |
无强升迁;下次 enable 时 capture_dns_state 看到 Tier 1 空就会把 stale vec 改成 DhcpEmpty,self-heals。
Files changed (cumulative for v0.3.0)
PR #96(Rust)
crates/mhost-core/src/models.rs— 新OriginalDnstagged enum + 自定义 serde + 9 个测试crates/mhost-dns/src/platform.rs—get_system_dns拆成capture_dns_state+get_upstream_resolvers;enable/disable_dns_mode/osascript_restore签名改&OriginalDnscrates/mhost-dns/src/server.rs— DnsConfig 加refresh_upstream;DnsServer resolver 改parking_lot::RwLock<Arc<...>>;新增run_upstream_refresh_loop后台 taskcrates/mhost-dns/src/config.rs— DnsConfig 新字段crates/mhost-storage/src/manifest.rs—Option<OriginalDns>+ migration 测试src/state/mod.rs—Mutex<OriginalDns>+try_recover_dns重写src/commands/dns.rs—set_dns_mode_enable用capture_dns_state,按 variant 设refresh_upstream
PR #96(前端)
src/types/index.ts—OriginalDnsdiscriminated unionsrc/pages/Settings.tsx— 三种状态渲染分支src/pages/__tests__/Settings.test.tsx— Settings fixturessrc/components/__tests__/Layout.test.tsx— fixture
PR #97(前端)
src/components/DnsProfileView.tsx— 集成<RuleEditor>+ Edit 按钮(保留 Edit Info)src/components/Layout.tsx— 移除<StatusBar />src/components/Sidebar.tsx— 在sidebarSpacer后挂<StatusBar />src/pages/__tests__/ProfileView.test.tsx— DNS-mode 新增 6 个规则编辑测试
数字
- Rust: 339 测试通过(含 9 + 5 + 3 个新增)
- 前端: 166 测试通过(含 6 个新增 DNS 模式测试)
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.0:
- 数据:
Manifest.original_dns自动按上面 migration 表兼容旧格式,无强升迁。 - 网络行为变化:之前 enable 的 DNS mode 在 v0.3.0 第一次重启时会重新跑
capture_dns_state:- 如果你在 System Settings 里配过 DNS → 沿用为
Manual,行为不变 - 如果之前是 DHCP/empty → 升级为
DhcpEmpty,本次 session 内不会改变 networksetup,且 disable 会写回Empty
- 如果你在 System Settings 里配过 DNS → 沿用为
- 不需要停用 DNS mode:升级过程中 DNS server 会自动重启;如有强需求,disable 再升级也安全。