Skip to content

Releases: igevin/mHost

mHost v0.3.3

Choose a tag to compare

@github-actions github-actions released this 16 Jul 06:57

v0.3.3 更新说明

本期相对 v0.3.2 主要是一次安全加固 + System Hosts 体验优化,并附带了一轮发版/版本号文档的修正。

🔒 安全修复:escapeHtml 加固(#110,#117)

RuleEditor.tsx 和 SystemHosts.tsx 各自带了一份相同的 escapeHtml,只转义 &、<、> — 文本节点够用,但任何后续把值塞进属性插值(href、title、onclick)就构成 stored XSS。这次:

  • 合并到单一 src/lib/escape.ts
  • 顺手把 " 和 ' 也补上(顺序仍是 & 在前,避免 &amp; 被二次编码)
  • 12 个新单测覆盖五个字符 + 双重编码回归 + <script> 和属性逃逸中和

✨ 新增:System Hosts 行号 gutter(#111,#118)

System Hosts 页面现在常驻显示左侧 44px 行号栏,1-based。开启搜索后,当前活动匹配对应的行号加粗 + 主色高亮。原本搜索栏右侧的 "Line N of M" 小标签下线,gutter 成为主要的"我在第几行"affordance。

实现要点:

  • 布局沿用 RuleEditor 的同款 wrapper + 绝对定位 gutter,保持视觉一致
  • gutter overflow: hidden,通过 onScroll 把 <pre>.scrollTop 同步过去,无独立滚动条
  • lineNumbers memo 仅依赖 totalLines,搜索打字不会重建数组

📖 文档(无运行时变更,#119)

doc/build-and-version.md 重新梳理:

  • 画清楚 __APP_VERSION__ 同时驱动 About 卡片 和 检查更新 的链路
  • 修正 CI 矩阵表中第二个 runner 被误标为 "Intel" 的描述(两个 runner 都是 macos-latest)
  • 列出全部 6 个 Cargo.toml 路径,明确说"不要碰"

下载

平台 DMG
Apple Silicon mHost_0.3.3_aarch64.dmg
Intel mHost_0.3.3_x64.dmg

.app.tar.gz 同帖提供(mHost_aarch64.app.tar.gz、mHost_x64.app.tar.gz)。

升级方式

  • 已安装 v0.3.x:打开 mHost → Settings → "检查更新" → 看到 v0.3.3 后点击跳转下载页
  • 首次安装或从 v0.2.x 升级:直接下载对应架构的 DMG 拖入 Applications 即可,不会影响 /etc/hosts 中 # ---- mHost start ---- managed block

已知问题

  • 安装包未做 Apple 代码签名(同 v0.3.x 一直的状态),首次打开如遇 Gatekeeper 拦截请右键 → 打开
  • Linux / Windows 不在当前发版范围(#67)

mHost v0.3.2

Choose a tag to compare

@github-actions github-actions released this 16 Jul 06:55

See the assets to download this version.

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 再升级也安全

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 再升级也安全。

mHost v0.2.1

mHost v0.2.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 01 Jul 05:11
2648b87

What's New

ApplyConfirmDialog with Diff Preview (#59)

应用 hosts 前增加 diff 预览确认对话框,避免误操作覆盖系统 hosts。

  • sidebar、ManagementDrawer、ProfileList 中的 Enable/Disable 开关统一接入预览流程
  • 全局错误提示从各组件移至 Layout 统一渲染
  • 对话框组件统一使用 createPortal 渲染到 document.body

重复规则检测 (#61)

同一 profile 内自动检测重复规则,防止冲突配置被应用到系统。

  • SameIp:相同 IP 对应不同域名,提示警告(黄色边框)
  • DifferentIp:相同域名对应不同 IP,阻断保存(红色边框)
  • RuleEditor 和 ImportDialog 均支持检测和提示

Search & Replace in RuleEditor (#62)

规则编辑器集成查找替换功能,提升批量编辑效率。

  • Cmd+F 呼出搜索栏,Esc 关闭
  • 支持 Enter / Shift+Enter 切换匹配项
  • 支持 Replace / Replace All

Configuration Snapshots (#64)

引入配置快照机制,支持 hosts 配置的版本管理和回滚。

  • 新增 Tauri 命令:save_snapshot、list_snapshots、load_snapshot、delete_snapshot
  • apply / enable_and_apply 后自动创建快照(列表为空,或最新快照超过 3 天)
  • 独立 /snapshot 页面管理所有快照

UI 调整 (#65)

优化 Settings 页面和规则编辑器布局。

  • Settings 移除 Hosts Management rollback(功能已迁移至 ApplyConfirmDialog)
  • System Hosts Preview 拆分为独立 /hosts 页面,独占整页高度
  • RuleEditor 左侧新增行号栏,同步滚动

Bug Fixes

  • WebView 点击被吞没:ProfileView Save/Cancel 按钮添加 onPointerDown,修复 Tauri WKWebView 中输入后点击失效的问题 (#60)

CI/CD

  • 升级 Node.js 20 → 22、pnpm 9 → 10,修复 GitHub Actions 弃用警告
  • 清理未使用的导入,确保 TypeScript 编译通过

Downloads

文件 说明
mHost_0.2.1_aarch64.dmg macOS Apple Silicon (ARM64) 安装包
mHost_0.2.1_x64.dmg macOS Intel (x86_64) 安装包
mHost_aarch64.app.tar.gz macOS Apple Silicon 应用归档
mHost_x64.app.tar.gz macOS Intel 应用归档

mHost v0.2.0

mHost v0.2.0 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 27 Jun 15:58

What's New

UI Redesign — Sidebar + Management Drawer Layout (#55)

全新的界面布局,采用侧边栏 + 管理抽屉的设计,提供更清晰的操作体验。

Bug Fixes

  • 对话框点击失效问题:修复 Tauri WKWebView 中在输入框输入后点击按钮需要两次点击的问题,通过 pointerdown 事件绑定解决 WebKit 焦点转移吞噬点击的兼容性问题
  • CreateProfileDialog 无法弹出:修复侧边栏 overflow:hidden 导致创建对话框被裁剪的问题,改用 createPortal 渲染到 document.body
  • 空状态下无法创建 Profile:修复无 Profile 时点击「+ New Profile」无响应的问题
  • 重复点击防护:为所有操作按钮(Create、Duplicate、Delete、Save 等)添加 loading 状态锁,防止异步操作期间的重复点击
  • 对话框组件统一使用 Portal:CreateProfileDialog、ManagementDrawer、ImportDialog 统一使用 createPortal 渲染

Improvements

  • WebKit 兼容性优化:提取 WebKit pointerdown 修复为共享 hook (useWebKitPointerDown),供所有对话框复用
  • 权限精细化:编辑 Profile 的名称/描述/标签不再要求管理员权限,仅修改规则或启用状态时才需要

CI/CD

  • 升级 Node.js 20 → 22、pnpm 9 → 10,修复 GitHub Actions 弃用警告
  • 清理未使用的导入,确保 TypeScript 编译通过

Downloads

文件 说明
mHost_0.2.0_aarch64.dmg macOS Apple Silicon (ARM64) 安装包
mHost_0.2.0_x64.dmg macOS Intel (x86_64) 安装包
mHost_aarch64.app.tar.gz macOS Apple Silicon 应用归档
mHost_x64.app.tar.gz macOS Intel 应用归档

mHost v0.1.1

mHost v0.1.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 26 Jun 09:12

See the assets to download this version.

mHost v0.1.0

mHost v0.1.0 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 21 Jun 12:47

See the assets to download this version.