Skip to content

mHost v0.3.0

Choose a tag to compare

@github-actions github-actions released this 06 Jul 15:27

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 / 真的空)
  • 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 — 新 OriginalDns tagged enum + 自定义 serde + 9 个测试
  • crates/mhost-dns/src/platform.rs — get_system_dns 拆成 capture_dns_state + get_upstream_resolvers;enable/disable_dns_mode / osascript_restore 签名改 &OriginalDns
  • crates/mhost-dns/src/server.rs — DnsConfig 加 refresh_upstream;DnsServer resolver 改 parking_lot::RwLock<Arc<...>>;新增 run_upstream_refresh_loop 后台 task
  • crates/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 — OriginalDns discriminated union
  • src/pages/Settings.tsx — 三种状态渲染分支
  • src/pages/__tests__/Settings.test.tsx — Settings fixtures
  • src/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 --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.0:

  1. 数据:Manifest.original_dns 自动按上面 migration 表兼容旧格式,无强升迁。
  2. 网络行为变化:之前 enable 的 DNS mode 在 v0.3.0 第一次重启时会重新跑 capture_dns_state:
    • 如果你在 System Settings 里配过 DNS → 沿用为 Manual,行为不变
    • 如果之前是 DHCP/empty → 升级为 DhcpEmpty,本次 session 内不会改变 networksetup,且 disable 会写回 Empty
  3. 不需要停用 DNS mode:升级过程中 DNS server 会自动重启;如有强需求,disable 再升级也安全。