基于 Tauri 2 + Rust + React 复刻的 FinalShell —— SSH 终端 / SFTP 文件管理 / 服务器监控一体化工具。
⬇️ 最新版 Release —— 两种形态:
| 资产 | 用途 |
|---|---|
FinalShell-X_x.y.z_x64-setup.exe |
NSIS 安装包,装到 %LOCALAPPDATA%\FinalShell-X,带开始菜单/卸载项 |
FinalShell-X_x.y.z_x64-portable.zip |
便携包,解压后双击 finalshell-x.exe 直接跑,不写注册表 |
(Windows x64,需要 WebView2 运行时;Win10/11 已自带。)
# 直链下载(把版本号换成你要的)
$v = '0.4.1'
curl.exe -L -o FinalShell-X_${v}_x64-setup.exe `
"https://github.com/qgx1992/finalshell-clone/releases/download/v$v/FinalShell-X_${v}_x64-setup.exe"
# 校验
Get-FileHash .\FinalShell-X_0.4.1_x64-setup.exe -Algorithm SHA256
# 0.4.1 setup => 53534A1B8C3A78F0CEE33B0793D19A5EC27E29FF8EE4CAC2890A97FEA39497DB
# 0.4.1 portable=> B7EA69EB8E8CD3FA87456AD5ED755FA93DB0EA6E9C7D67F259C9C57F9CF31671- 静默安装:
FinalShell-X_0.4.1_x64-setup.exe /S(可用/D=安装目录,且必须是最后一个参数) - 静默卸载:
"<安装目录>\uninstall.exe" /S - 从旧版升级直接覆盖安装即可:服务器列表、凭据库(Windows 凭据管理器里的条目)与
known_hosts都保留,配置目录不变(com.finalshell.clone)
- 多标签连接管理,密码 / 私钥认证
- xterm.js 渲染,支持中文、鼠标选择、链接点击
- 终端尺寸自适应(窗口缩放自动 resize)
- 标签页保活:切换标签不重连,终端历史保留
- 终端与文件管理同屏显示(底部面板)
- 目录列表、新建、重命名、删除(递归)
- 拖拽式上传 / 下载(带实时进度条)
- 双击远程文件直接编辑(CodeMirror 语法高亮,保存即写回)
- 右键一键打包:在文件列表右键菜单对文件 / 目录(支持 Ctrl 多选、Shift 连选)
生成
ZIP或TAR.GZ,直接在服务器侧压缩,不经过本地中转;服务器缺少zip时 自动回落到本地 SFTP 流式打包,两者都带进度条
- CPU / 内存 / 磁盘 / 网络实时图表(自绘 Canvas 折线图)
- 负载、运行时间、主机名
- 每 2 秒自动采样
原理:单次 SSH 通道批量执行
cat /proc/stat、/proc/meminfo、/proc/net/dev、df -Pk、/proc/loadavg、/proc/uptime等并解析;CPU / 网络速率靠两次采样差值计算。
- 顶栏连接 IP 显示:当前活动标签页的 IP / 主机名显示在顶栏中央,一键复制
- 快捷命令面板(可自定义):点击后直接在终端窗口执行并回显输出(写入当前 SSH 会话,由 shell 真正运行),不再在侧边栏面板里显示
- 10 套内置终端配色方案(Monokai / Dracula / Solarized / One Dark / Nord 等)
- 深色 / 浅色界面主题、字号调节
- 最近连接、服务器分组管理
| 层 | 技术 |
|---|---|
| 桌面框架 | Tauri 2(2.11.x / tao 0.35 / wry 0.55) |
| SSH 库 | russh 0.62(纯 Rust,ring 加密) |
| SFTP | russh-sftp |
| 前端 | React 18 + TypeScript + Vite 6 |
| 终端 | xterm.js + addon-fit + addon-web-links |
| 编辑器 | CodeMirror 6(多语言语法高亮) |
| 状态管理 | zustand |
| 运行时 | tokio(异步) / base64 / serde_json |
┌────────────── 前端 (src/, React + zustand) ──────────────┐
│ App ── Sidebar / TabBar + TerminalPanel + SftpPanel / MonitorPanel │
│ store/app.ts ── 全局状态(servers / settings / tabs) │
│ api.ts ── 25 个 Tauri invoke 封装 + listen 事件订阅 │
└───────────┬───────────────────────────────────────────────┘
│ Tauri IPC:invoke 请求 + emit 事件推送
┌───────────▼─────────────── Rust 后端 (src-tauri/) ────────┐
│ lib.rs ── 装配命令 + SshManager 全局状态 │
│ ├─ store.rs 配置持久化(servers.json / settings.json) │
│ ├─ ssh/manager.rs russh 会话 / 终端通道 / 读循环 / exec │
│ ├─ ssh/sftp.rs SFTP 列目录 / 增删改 / 上传下载 / 编辑 │
│ └─ monitor.rs 监控采样与解析 │
└────────────────────────────────────────────────────────────┘
一次 SSH 连接的关键数据流:
- 前端
openTab→connect_server(invoke)→ russh 认证 + PTY + shell - 后端 spawn 读循环,16ms / 32KB 批量合并 →
emit("term-data", base64) - 前端
listen("term-data")流式解码(TextDecoder {stream:true})写入 xterm.js - 键盘输入前端缓冲 12ms →
write_terminal批量写入 - 断开 →
emit("term-exit")→ Tab 状态置closed(历史保留,可重连)
监听的事件:
| 事件 | 发送方 | 消费方 | 说明 |
|---|---|---|---|
term-data |
后端读循环 | TerminalPanel | 终端输出(base64,批量) |
term-exit |
后端 | TerminalPanel | 连接状态变更 |
sftp-progress |
后端传输出流 | SftpPanel | 上传/下载进度(节流) |
sftp-archive-progress |
后端打包任务 | SftpPanel | 远程打包进度(百分比 + 已处理字节) |
- 标签页保活:所有标签的终端 / SFTP 面板常驻挂载,切换标签只切换可见性(
.tab-workspace),不再断开重连,终端历史保留 - 终端输出批量合并:后端读循环 16ms / 32KB 批量发送
term-data,IPC 事件数降 10~50 倍 - 按键输入批量发送:前端缓冲 12ms 批量写入,消除每击键一次的 IPC 往返
- 流式解码:
TextDecoder {stream:true}流式解码,跨包多字节 UTF-8 不再乱码 - SFTP 进度节流:进度事件 1MB / 100ms 一次(原每 64KB 一次,传 1GB 文件事件数 ~16000 → ~10),结束时补发 100%;传输缓冲 64KB → 256KB
- 前端分包 + 懒加载:CodeMirror 1.7MB 独立 chunk 且懒加载(仅打开远程编辑器时),首屏只加载 index / react / xterm;Vite
manualChunks将 xterm / react / codemirror / tauri / vendor 分包
注:曾尝试复用 SSH exec 通道减少监控轮询开销,但 RFC 4254 规定一个 channel 只能 exec 一次(OpenSSH 拒绝重复请求),已回退为每次独立通道。
# 依赖安装
npm install
# 前端 + 后端联调开发
npm run tauri dev
⚠️ Windows 构建需要 mingw-w64 工具链(Rust GNU target),已配置于C:\Users\QIU\mingw64。 编译前请确保 PATH 包含其 bin 目录。
# 推荐:MSVC 工具链一键打包(自动设置 RUSTUP_TOOLCHAIN + target-msvc 目录)
npm run build:msvc # 生成 NSIS 安装包
# 或使用默认(当前 GNU)工具链
npm run tauri build💡
npm run build:msvc通过scripts/build-msvc.mjs固定使用 MSVC 工具链 (stable-x86_64-pc-windows-msvc)并指向独立的src-tauri/target-msvc/编译目录, 有效规避下面「项目路径含空格」导致的 windres 失败 —— 推荐优先使用这条命令。
-
项目路径不能包含空格(仅 GNU 工具链)! 若用默认 GNU 工具链,本项目位于
D:\A-my project\...(含空格),windres在预处理 rc 资源文件时会把带空格的--include-dir路径在空格处截断,导致资源编译失败:cc1.exe: fatal error: ...\out: No such file or directory解法 A(推荐,已固化): 直接用
npm run build:msvc走 MSVC 工具链,不受路径空格影响。 解法 B(遗留,gNU): 建一个无空格路径的 junction,再从 junction 路径构建:mklink /J "%TEMP%\fsc" "D:\A-my project\finalshell-clone" Set-Location "%TEMP%\fsc" # 后端在无空格路径下构建,windres 正常 npm run tauri build
注意:前端 vite 构建不支持 junction 路径(vite 会解析成真实绝对路径而报错),需在真实路径先
npm run build生成 dist, 再在 junction 路径npx tauri build --config '{"build":{"beforeBuildCommand":""}}'跳过前端构建,直接编后端 + 打包。 -
webview2-hook.nsh的路径必须与构建机一致。 该 hook 负责把WebView2Loader.dll复制进安装目录(GNU 工具链下 tauri-bundler 默认漏打包)。 文件里写死了构建机的绝对路径,项目移动后必须同步修改,否则 NSIS 打包报no files found。NSIS 宏环境里没有
PROJECTDIR之类的变量,只能写死绝对路径;若路径含空格,File "..."需加引号。 -
msvc缺包 / 其它: GNU 工具链下编译需要mingw-w64(含windres、ld),见上。
项目附带 test_server.py —— 一个基于 paramiko 的模拟 SSH 服务器
(127.0.0.1:2222,账号 test / 密码 test123),内置迷你 Linux shell 和
虚拟 /proc 文件系统,可完整测试终端、SFTP、监控功能:
python test_server.pye2e_test.py 通过 WebView2 CDP(端口 9222)驱动 UI。运行前需在
src-tauri/tauri.conf.json 的窗口配置中临时添加:
"additionalBrowserArgs": "--remote-debugging-port=9222 --remote-allow-origins=*"
⚠️ 注意:环境变量WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS对 Tauri 2 无效(创建 WebView2 时会用空参数覆盖);新版本 WebView2 还必须加--remote-allow-origins=*否则 CDP WebSocket 握手返回 403。测试完务必移除该参数,避免调试端口打进生产包。
修改配置后 npm run tauri dev 会自动重编译重启,然后执行:
python e2e_test.py现象: 双击(或从开始菜单)运行安装好的 FinalShell-X,没有任何窗口出现, 进程可能在后台挂着,事件日志里也查不到崩溃。
最常见根因:WebView2 依赖缺失或过旧。
Tauri 2 应用依赖系统 WebView2 Runtime 渲染界面,而整个 WebView2 浏览器进程序列
依赖 Microsoft Visual C++ 2022 Runtime(vcruntime140.dll / msvcp140.dll 等)。
当这些组件缺失或版本过旧时,WebView2 环境创建会失败(CreateCoreWebView2EnvironmentWithOptions
返回 0x800700AA / ERROR_BUSY),窗口便无法显示。
排查步骤:
-
检查 WebView2 Runtime 是否安装及版本 —— 注册表查询:
Get-ItemProperty "HKLM:\SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" | Select pv
没有输出或在
C:\Program Files (x86)\Microsoft\EdgeWebView\Application无版本目录 → 需要安装 WebView2 Runtime。 -
检查 VC++ 2022 Runtime 是否安装 —— 控制面板「应用」里搜「Microsoft Visual C++ 2022」; 缺失则安装 Visual C++ Redistributable。
-
手工验证应用能否出窗 —— 直接运行安装目录的
%LOCALAPPDATA%\FinalShell-X\finalshell-x.exe,若窗口出现说明应用本身没问题, 只是依赖环境未就绪。
排查小技巧: 若开发环境(C# / Edge / 其它 WebView2 应用)能正常用,而本应用独独无界面,
优先怀疑应用数据目录(%LOCALAPPDATA%\com.finalshell.clone\EBWebView)被残留的 WebView2 浏览器进程
锁定(旧实例异常退出后未清干净)。删除该目录再启动通常可恢复(不影响已保存的服务器配置——
配置存于 %APPDATA%\com.finalshell.clone\servers.json / settings.json,与 WebView2 数据目录分开)。
finalshell-clone/
├── src/ # React 前端
│ ├── components/ # Sidebar / TerminalPanel / SftpPanel / MonitorPanel
│ │ │ # / ConnectionDialog / EditorModal / QuickCommandPanel
│ │ │ # / SettingsModal / ContextMenu / TabBar / Sparkline
│ ├── store/app.ts # zustand 全局状态(tabs / servers / settings)
│ ├── api.ts # Tauri invoke 封装 + 结构化错误
│ ├── events/terminalRouter.ts # 全局终端/SFTP 事件路由
│ ├── generated/ # 由 Rust 类型生成的 TS 定义
│ ├── themes.ts # 10 套终端配色方案
│ ├── types.ts # 与 Rust 后端对应的类型定义
│ ├── main.tsx / App.tsx # 入口与布局
│ └── styles.css # 全局样式
├── src-tauri/ # Rust 后端
│ ├── src/
│ │ ├── lib.rs # 命令注册(app 入口)
│ │ ├── main.rs # Windows 子系统入口
│ │ ├── ssh/
│ │ │ ├── manager.rs # russh 会话管理 + 终端读循环 + 主机指纹校验
│ │ │ ├── proxy.rs # SOCKS5 / HTTP CONNECT 代理
│ │ │ ├── sftp.rs # SFTP 文件操作(原子写入/原生递归删除)
│ │ │ └── archive.rs # 远程打包(zip/tar.gz,服务器侧命令 + SFTP 流式回落)
│ │ ├── security/
│ │ │ ├── credentials.rs # 系统凭据库封装
│ │ │ └── known_hosts.rs # TOFU 主机密钥校验
│ │ ├── monitor.rs # 跨平台监控采样与解析
│ │ ├── error.rs # 结构化错误类型
│ │ └── store.rs # 配置持久化(servers.json / settings.json)
│ ├── webview2-hook.nsh # NSIS 钩子:打包 WebView2Loader.dll
│ ├── tauri.conf.json # Tauri 配置
│ └── capabilities/default.json
├── scripts/
│ └── build-msvc.mjs # MSVC 工具链打包脚本(build:msvc)
├── test_server.py # 端到端测试用 SSH 服务器
└── e2e_test.py # CDP 驱动的 UI 自动化测试
- 端口转发 / 海外加速尚未实现
- 未实现终端 cd 与 SFTP 面板目录自动同步(有
sftp_pwd命令预留,可手动刷新) - RDP 依赖系统客户端:Windows 用
mstsc.exe,Linux/macOS 需自行安装 FreeRDP(xfreerdp) - 内置编辑器仅支持文本文件,上限 5 MB;更大的文件请下载后用专业工具编辑
- 监控依赖远程系统的内置命令,Linux/Windows/macOS 之外的系统不支持
- 主机密钥校验(TOFU):首次连接展示 SHA256 指纹,由用户确认后写入本应用的
known_hosts; 之后指纹不一致会拒绝连接并提示新旧指纹对比,避免中间人攻击。 - 凭据存系统凭据库:登录密码、私钥口令、代理密码保存在操作系统凭据管理中, 不写入配置文件,也不会下发到前端。
- 内容安全策略已启用:
tauri.conf.json配置了 CSP,插件权限收敛到实际使用的范围。 - SFTP 递归删除不跟随符号链接,且限制单次删除条目数;编辑器保存采用原子写入。
- 日志不记录任何凭据。