-
-
Notifications
You must be signed in to change notification settings - Fork 40
Architecture
Juwan-Hwang edited this page Apr 10, 2026
·
27 revisions
┌──────────────────────────────────────────────────────────┐
│ 前端 (JavaScript) │
│ ┌─────────┐ ┌──────────┐ ┌───────────┐ ┌─────────────┐ │
│ │ ui.js │ │ api.js │ │websocket │ │ traffic- │ │
│ │ UI 逻辑 │ │ API 调用 │ │ .js │ │ chart.js │ │
│ └────┬─────┘ └────┬─────┘ └─────┬─────┘ └──────┬──────┘ │
│ │ │ │ │ │
│ └────────────┴──────┬──────┴───────────────┘ │
│ │ invoke / listen │
├──────────────────────────┼─────────────────────────────────┤
│ Tauri IPC 层 │
├──────────────────────────┼─────────────────────────────────┤
│ Rust 后端 │
│ ┌──────────┐ ┌──────────────┐ ┌──────────┐ ┌──────────┐ │
│ │ lib.rs │ │core_manager │ │ config_ │ │ sys_ │ │
│ │ 入口/状态│ │ .rs 核心 │ │ manager │ │ proxy.rs │ │
│ │ 管理 │ │ 进程管理 │ │ .rs │ │ 系统代理 │ │
│ └──────────┘ └──────┬───────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────┴───────┐ ┌──────────┐ │
│ │ tray.rs │ │ updater.rs │ │ uwp_ │ │
│ │ 系统托盘 │ │ 内核/Geo更新 │ │ loopback │ │
│ └──────────┘ └──────────────┘ └──────────┘ │
├──────────────────────────────────────────────────────────┤
│ Mihomo 内核 │
│ RESTful API (:9090) · /traffic (Stream) · /proxies · ... │
└──────────────────────────────────────────────────────────┘
职责:应用生命周期管理、状态初始化、命令注册。
核心组件:
-
RateLimiter— 命令级限流器(HashMap<String, Instant>,60s 自动清理) -
rate_limit!宏 — 声明式限流装饰器 -
Settings结构体 — 应用配置(close_to_tray, auto_update, autostart, theme, last_config, custom_args, dns_nameservers, dns_fallbacks) -
SettingsState(Arc<Mutex<Settings>>)— 线程安全的状态容器
生命周期:
- Panic Hook 注册(确保异常时清理)
- 插件注册(autostart, dialog, opener)
- 状态初始化(MihomoState, TrayState, RateLimiter)
- 应用存储目录创建
- 设置加载
- 托盘初始化
- 窗口事件绑定(关闭到托盘、拖拽导入 YAML)
- 命令注册(~30 个 Tauri Command)
- 退出清理(停止核心、清除系统代理)
职责:Mihomo 内核的完整生命周期管理。
全局状态:
-
TUN_MODE_ACTIVE: AtomicBool— TUN 模式状态 -
CORE_STARTING: AtomicBool— 启动锁(防并发启动) -
DERIVED_KEY: OnceLock<Vec<u8>>— 派生加密密钥(延迟初始化)
核心数据结构:
struct CoreData {
process: Option<Child>, // Mihomo 子进程
last_secret: String, // API 密钥
last_config_path: String, // 当前配置文件路径
last_custom_args: String, // 自定义启动参数
last_port: u16, // API 端口
}
struct AppPaths {
app_data_dir: PathBuf, // 应用数据目录
core_dir: PathBuf, // 内核目录
profiles_dir: PathBuf, // 配置文件目录
}核心启动流程 (start_core):
1. 限流检查(3s 冷却)
2. 原子锁获取(10s 超时 + ResetGuard)
3. TUN 模式检查 → macOS 需要 root 权限
4. 终止现有 Mihomo 进程
5. 确保应用存储目录存在
6. macOS: 等待端口 9090 释放
7. 解析配置文件路径
8. 验证自定义启动参数
9. 测试模式验证(mihomo -t)
10. 准备运行时配置(注入 external-controller + secret + unified-delay)
11. 写入 run_config.yaml
12. 启动 Mihomo 子进程(日志输出到临时文件)
13. cache.db 锁重试
14. TCP 健康检查(20 次轮询)
15. 更新全局状态
订阅下载流程 (download_sub):
1. 限流检查(5s 冷却)
2. SSRF 验证(DNS 解析 + 私有 IP 检查)
3. 尝试直接下载
4. 失败 → 通过 Mihomo 代理下载
5. 再失败 → 通过系统代理下载
6. Base64 自动检测与解码
7. YAML + proxies 字段验证
8. 清除危险配置键
9. 加密存储元数据
10. 安全写入文件
职责:配置文件的读取、合并、写入和热重载。
核心流程 (update_config):
1. 读取当前 run_config.yaml
2. JSON patch → YAML 转换
3. 递归深度合并(深度限制 50 层)
4. 安全键保护:
- 保存 external-controller(强制 127.0.0.1)
- 保存 secret
- 保存 TUN enable 状态
5. 合并后恢复安全键
6. 写入 run_config.yaml
7. 同步更新原始 profile 文件
8. PATCH 到 Mihomo API(/configs?force=true)
9. 返回详细结果
YAML 合并规则:
-
null值删除对应键 - 嵌套对象递归合并
- 数组整体替换(非元素级合并)
- 深度限制 50 层(防止栈溢出)
安全验证 (validate_proxy_server):
- 仅允许
127.0.0.1、::1、localhost - 拒绝欺骗性主机名(如
127.0.0.1.evil.com) - 最大 512 字符
Windows 实现:
- 注册表路径:
HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings - 原子写入:先备份当前值 → 写入新值 → 刷新
InternetSetOptionW→ 失败则回滚 - Bypass 默认包含:
localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.2*;172.3*;192.168.*
macOS 实现:
- 动态获取网络服务列表(
-listallnetworkservices) - 跳过禁用的服务(以
*开头) - 逐服务设置 HTTP/HTTPS/SOCKS 代理
- 任一服务成功即视为成功
Linux 实现:
- GNOME:
gsettings原子写入(先设所有值再切 mode=manual,失败回滚) - KDE:
kwriteconfig5/6+ DBus 通知 KIODaemon/KWin - XFCE:
xfconf-query设置 HTTP/HTTPS/SOCKS
状态结构:
struct TrayMenuState {
sys_proxy_enabled: bool,
tun_enabled: bool,
current_mode: String, // "rule" / "global" / "direct"
active_config: String,
active_proxy: String,
}菜单构建 (update_tray_full_menu):
- 动态获取代理组和节点(每组最多 15 个)
- 订阅列表从 profiles 目录读取
- 模式状态用 ●/○ 标记
事件系统:
- 托盘操作通过 Tauri Event 通知前端
- 前端监听事件后同步 UI 状态
- 实现前后端双向状态同步
更新流程 (update_core):
1. 解析 GitHub Release URL
2. 验证 URL 结构(MetaCubeX/mihomo/releases/download/)
3. 验证版本格式
4. 下载到 UUID 临时文件(进度 24-80%)
5. SHA256 校验
6. 解压到 UUID 临时文件
7. 停止 Mihomo 核心
8. 替换二进制文件(Windows 重试 5 次)
9. 安装内核二进制
10. 使用上次配置重启核心
平台标签映射:
| OS | arch | Tag |
|---|---|---|
| Windows | x86_64 | windows-amd64 |
| Windows | aarch64 | windows-arm64 |
| macOS | x86_64 | darwin-amd64 |
| macOS | aarch64 | darwin-arm64 |
| Linux | x86_64 | linux-amd64 |
Windows 优先选择
compatible架构的资产。
- 5 分钟限流 — 防止频繁调用
- 合法性检查 — 确认核心正在运行且主窗口存在
- 用户确认 — 通过 Tauri Dialog 插件弹出确认对话框
-
执行方式 — Base64 编码的 PowerShell 脚本 +
CheckNetIsolation.exe LoopbackExempt -a
用户操作 → ui.js (事件处理)
↓
api.js (invoke 调用)
↓
Tauri IPC → Rust Command → 执行 → 返回结果
↓
ui.js (更新 DOM)
↓
Tauri Event (后端推送) → ui.js (监听处理)
-
API 缓存 —
apiCache对象,TTL 2 秒,覆盖 config/proxies/settings/configs -
托盘菜单缓存 —
trayMenuCache,TTL 2 秒 -
缓存失效 — 操作后主动调用
invalidate*Cache()
1. 禁用右键菜单
2. 应用翻译 (i18n)
3. 初始化窗口控制
4. 显示主窗口 (50ms 延迟)
5. 启动核心 → 获取 secret + port
6. 设置 API 基地址和密钥
7. 初始化各模块(导航 → 图表 → 托盘 → 代理 → DNS → 模式 → TUN → 设置 → UWP → 节点滚轮)
8. 检查加密密钥持久化
9. 同步核心配置 + 更新托盘
10. 启动统一周期同步 (10s 间隔)
11. 监听配置解析错误事件
12. 连接流量监控
每 10 秒并行检查:
- 系统代理状态是否与 UI 一致
- 托盘图标模式是否与预期一致
| 命令 | 模块 | 说明 |
|---|---|---|
get_settings |
lib | 获取应用设置 |
save_settings |
lib | 保存应用设置 |
show_main_window |
lib | 显示主窗口 |
get_tray_status |
lib | 获取托盘状态 |
start_core |
core | 启动 Mihomo 核心 |
stop_core |
core | 停止核心 |
restart_core |
core | 重启核心 |
get_core_version |
core | 获取内核版本 |
download_sub |
core | 下载订阅 |
list_configs |
core | 列出配置文件 |
delete_config |
core | 删除配置文件 |
read_config |
core | 读取运行时配置 |
read_config_file |
core | 读取配置文件内容 |
write_config_file |
core | 写入配置文件内容 |
update_config |
config | 更新配置并热重载 |
open_config_folder |
core | 打开配置文件夹 |
get_sys_proxy |
proxy | 获取系统代理状态 |
enable_sysproxy |
proxy | 启用系统代理 |
disable_sysproxy |
proxy | 禁用系统代理 |
change_tray_icon |
tray | 更改托盘图标 |
update_tray_full_menu |
tray | 更新托盘完整菜单 |
update_tray_toggle_states |
tray | 更新托盘开关状态 |
get_latest_version |
updater | 获取最新内核版本 |
update_core |
updater | 更新内核 |
update_geo_data |
updater | 更新 Geo 数据 |
get_latest_client_versions |
updater | 获取客户端版本号 |
exempt_uwp_apps |
uwp | UWP 环回免除 |
restart_core_as_root_cmd |
core | root 权限重启(macOS TUN) |
set_tun_enabled |
core | 设置 TUN 状态 |
disable_tun_cmd |
core | 禁用 TUN |
is_machine_key_persisted |
core | 检查密钥持久化 |
fetch_text |
core | 后端代理获取文本 |
| 事件 | 触发场景 |
|---|---|
config-parse-error |
配置文件 YAML 解析失败 |
profiles-imported |
拖拽导入配置文件完成 |
tray-sysproxy-changed |
托盘切换系统代理 |
tray-tun-changed |
托盘切换 TUN 模式 |
tray-mode-changed |
托盘切换运行模式 |
tray-subscription-changed |
托盘切换订阅 |
tray-proxy-changed |
托盘切换代理节点 |
core-download-progress |
内核下载进度 |
core-download-status |
内核下载状态文本 |
geo-download-progress |
Geo 数据下载进度 |