feat: 网盘浏览下载 + 远程功能修复 + 连接状态同步 + Linux 图标打包
新增网盘功能 - FileServer 增加 GET /drive/meta|list|download, 每机长期 token 校验 + 目录穿越防护 - 新增 drive.ts 客户端: listDrive / getDriveRoot / downloadDriveFile / collectDriveFiles - ipc 暴露 drive:list|root|download|openFolder|pickDir, DB 增 drive_token 列 + ALTER 迁移 - 新增 DrivePanel.vue, 聊天框加"网盘" tab, 设置页加网盘分区 - 支持整目录递归下载并保持层级 修复远程功能 - 终端: 控制端收到的 output 帧被直接丢弃 (case 'output': return), 导致 xterm 全空白 - ChatServer: ws 构造即 listen, 'listening' 可能早于 start() 挂监听, 导致永久挂起 - USB: serverAttach 自行生成 sessionId 与控制端不一致 (数据帧查表落空); libusb 枚举误用 getDevices() (WebUSB 语义, 只返回已授权设备) 改 loadDevices(); control transfer 传数字 bmRequestType (node-usb v3 要字符串枚举); detachKernelDriver 挪到 selectConfiguration 之前 (否则 EBUSY) - 转发 UI: store 的 onOpened 是 no-op 致对方发起的转发不显示、无轮询致流量/TTL 不刷新、 对方发起的会话关不掉 (新增 closeSession, server 侧关闭需通知对端否则端口泄漏)、 入口/目标补归属标注、方向标签语义统一、监听端口输入框加宽 修复"在线却显示等待重连" - 根因: 启动时主进程先连上对端 (connectTo 早于 createMainWindow), device:wsState 在渲染层订阅前发出去被丢弃; 之后连接一直保持, 再无 open 事件 -> UI 永久停在等待重连 - 改为推全量快照 + 渲染层 mount 时主动拉取 (app:state); 渲染层先注册监听再 await - 通道语义改为 outgoing 或 inbound 任一 WS 活着即可用 (原来只看 outgoing) 修复 Linux 安装后没图标 - linux.icon 指向单张 PNG 时 app-builder 解析不出尺寸 (size=0), 图标被装进 hicolor/0x0/ 非法目录, 桌面环境找不到; 改为指向 resources/icons 目录 (8 个标准尺寸) - StartupWMClass 填的是 productName, 与窗口真实 WM_CLASS (可执行文件名) 对不上, 任务栏找不到启动器; 改为 local-net-msg 其他 - 新增 scripts/selftest 自测框架 (两个虚拟节点 + 真实 ChatServer/ChatClient), 30 条用例全过 - 重写 scripts/make-icon.js 生成多尺寸图标集 (原脚本引用的源图已不存在, sharp 依赖也缺) - 新增 docs/drive.md, docs/connection-status.md
This commit is contained in:
@@ -594,6 +594,46 @@ CREATE INDEX idx_audit_ts ON audit_log(ts DESC);
|
||||
|
||||
## 11. 验证清单(手动两机)
|
||||
|
||||
### 11.1 自动化自测(单进程双节点)
|
||||
|
||||
`npm run selftest` 会在一个进程里拉起两个虚拟节点(A=控制端、B=被控端),走真实的
|
||||
`ChatServer` + `ChatClient` + `TerminalManager` / `ForwardManager` / `UsbBridgeManager`,
|
||||
接线方式与 `src/main/ipc.ts` 完全一致,不需要第二台机器:
|
||||
|
||||
| # | 用例 | 覆盖 |
|
||||
|---|---|---|
|
||||
| T1–T5 | 终端 open / 输出回显 / 输入 / resize / close | PTY 生命周期与 `terminal.output` 帧 |
|
||||
| F1–F4 | 转发 self-out / self-in / 端口黑名单 / 字节计数 | 真实 TCP 隧道往返 |
|
||||
| U1–U2c | 列设备 / libusb 枚举 / 控制传输 setup 翻译 | node-usb v3 API 适配 |
|
||||
| U3–U5 | 串口 attach + 双向字节流 | 用 socat 造 PTY 对冒充真串口 |
|
||||
| A1 | 关键操作写进 `audit_log` | 审计链路 |
|
||||
|
||||
> 跑测试用 `ELECTRON_RUN_AS_NODE=1` 起 Electron 的 node:`better-sqlite3` 是按 Electron ABI
|
||||
> 编译的,用系统 node 会报 `NODE_MODULE_VERSION` 不匹配。
|
||||
|
||||
### 11.2 实现约定(踩过的坑)
|
||||
|
||||
- **sessionId 两端必须一致**:控制端生成 sessionId 并随 `attach` 帧发出,被控端**必须原样沿用**
|
||||
作为自己 session 的 key。反过来自己 `randomUUID()` 会导致数据帧查表落空、被当成"无此 session"丢掉
|
||||
(表现为串口/USB 字节流永远不通、detach 报"session 不存在")。
|
||||
- **终端 output 帧要在控制端落地**:`handleIncoming` 收到 `output` 帧后要 `emit('output', ...)`,
|
||||
`ipc.ts` 靠这个事件把数据广播给渲染层;只在被控端 emit 的话控制端 xterm 永远是空白。
|
||||
- **node-usb v3(Rust 重写版)**:
|
||||
- 枚举用 `webusb.loadDevices()`;`webusb.getDevices()` 是 WebUSB 语义,只返回本进程已授权的设备。
|
||||
- 控制传输的 `setup` 用字符串枚举(`requestType: 'standard'|'class'|'vendor'`、
|
||||
`recipient: 'device'|'interface'|'endpoint'|'other'`),协议里传的标准 `bmRequestType`
|
||||
数值要翻译一次(`toUsbControlSetup`)。
|
||||
- 设备被内核驱动占着时,必须**先 `detachKernelDriver` 再 `selectConfiguration`**,否则 EBUSY。
|
||||
- **转发端口的归属**:只有两种拓扑,UI 必须按视角标注,否则用户会连错机器——
|
||||
- `self-out`:发起方 bind 监听端口,目标在**被控端**机器上
|
||||
- `self-in`:被控端 bind 监听端口,目标在**发起方**机器上
|
||||
- 列表里 `side='client'` 表示"我发起",`side='server'` 表示"对方发起";
|
||||
入口/目标列都要写清"本机"还是"对方"。
|
||||
- **转发 session 两侧都要能关**:UI 的"关闭"按钮走 `ForwardManager.closeSession()`,
|
||||
关闭 server 侧时必须给对端发 `close` 帧,否则对端的监听端口不会释放(端口一直被占)。
|
||||
|
||||
### 11.3 手动清单
|
||||
|
||||
| # | 场景 | 期望 |
|
||||
|---|---|---|
|
||||
| T1 | 控制端请求被控端终端 → 被控端允许 | xterm 渲染 PowerShell/zsh/bash 提示符 |
|
||||
|
||||
Reference in New Issue
Block a user