--- AIGC: ContentProducer: '001191110102MAD55U9H0F10002' ContentPropagator: '001191110102MAD55U9H0F10002' Label: '1' ProduceID: '1148f2f2-715c-4f80-80d7-010cab5af4b0' PropagateID: '1148f2f2-715c-4f80-80d7-010cab5af4b0' ReservedCode1: '57f1878c-c8f1-41ec-9020-31dae82dcead' ReservedCode2: '57f1878c-c8f1-41ec-9020-31dae82dcead' --- # WFJ Panel 项目说明文档 ## 一、项目概述 WFJ Panel 是一款基于 Electron + React + ssh2 构建的**远程服务器管理面板**桌面应用,通过 SSH 协议远程连接 Linux 服务器,提供服务器概览监控、文件管理、交互式终端、Docker 容器管理、应用商店一键部署、防火墙管理等功能。 - **产品名称**:WFJ Panel - **版本**:v0.0.1 - **许可**:MIT - **构建产物**:NSIS 安装程序(Windows x64,约 107MB) --- ## 二、技术栈 | 层级 | 技术 | 说明 | |------|------|------| | 桌面框架 | Electron 44 + electron-vite 5 | 跨平台桌面应用,Vite 驱动构建 | | 前端框架 | React 19 | 函数组件 + Hooks | | 终端模拟 | @xterm/xterm 5 + @xterm/addon-fit | 交互式终端渲染 | | SSH 通信 | ssh2 1.17 | 纯 JS 实现的 SSH2 客户端 | | 打包工具 | electron-builder 26 | NSIS 安装包 | | 后端服务 | Flask 3 + mysql-connector-python | 应用商店 API 服务 | | 数据库 | MySQL 8 | 应用商店模板与版本数据 | --- ## 三、项目结构 ``` ssh_panel/ ├── package.json # 项目元数据、依赖、构建配置 ├── electron.vite.config.mjs # electron-vite 配置(main/preload/renderer 三入口) ├── .npmrc # Electron 国内镜像配置 ├── .gitignore ├── build/ │ └── icon.png # 应用图标 (256x256) ├── src/ │ ├── main/ │ │ └── index.js # ★ 主进程(1377行)—— SSH 连接、IPC、Docker、部署、SFTP、终端 │ ├── preload/ │ │ └── index.js # ★ Preload 桥接层 —— 暴露 window.panel API(90行) │ └── renderer/ │ ├── index.html # HTML 入口 │ └── src/ │ ├── main.jsx # React 挂载入口 │ ├── App.jsx # ★ 根组件 —— 登录/面板切换、连接状态管理 │ ├── theme.css # 设计令牌(色彩/间距/阴影/按钮体系) │ ├── main.css # 全局样式 │ └── pages/ │ ├── Login.jsx # SSH 登录页(凭据保存/自动填充) │ ├── Panel.jsx # ★ 主面板(导航栏 + 功能模块切换 + 版本更新) │ ├── overview/ # 服务器概览监控 │ │ ├── Overview.jsx │ │ ├── useMetric.js # 通用指标采集 Hook │ │ └── cards/ # CPU/内存/硬盘/网络/进程 五张卡片 │ ├── files/ # SFTP 远程文件管理 │ │ └── FileManager.jsx │ ├── terminal/ # 交互式终端 │ │ └── TerminalPanel.jsx │ ├── container/ # Docker 容器管理 │ │ ├── ContainerPanel.jsx # 容器面板入口(状态检测+子菜单) │ │ ├── ContainerList.jsx # 容器列表(启动/停止/删除/日志) │ │ ├── ImageList.jsx # 镜像列表 │ │ ├── AppStore.jsx # ★ 应用商店(在线模板+一键部署) │ │ ├── TaskCenter.jsx # 后台部署任务中心 │ │ ├── DockerConfig.jsx # Docker 配置文件查看/编辑 │ │ ├── NetworkPanel.jsx # Docker 网络管理 │ │ ├── VolumePanel.jsx # Docker 卷管理 │ │ ├── ContainerCreate.jsx # 手动创建容器 │ │ ├── DockerLog.jsx # 容器日志查看 │ │ ├── cmdParser.js # docker run 命令解析 │ │ └── appTemplates.js # 内置应用模板(离线备用) │ └── firewall/ # 系统防火墙管理 │ └── Firewall.jsx ├── appstore-server/ # 应用商店后端服务 │ ├── app.py # Flask API(/api/apps、/api/version、/api/health) │ ├── init.sql # 数据库建表 + 初始数据(Redis/MySQL/Halo 模板) │ ├── requirements.txt # Python 依赖 │ ├── Dockerfile # API 服务容器化 │ └── deploy.sh # 一键部署脚本 └── release/ # 打包产物输出目录(.gitignore) ``` --- ## 四、架构设计 ### 4.1 Electron 三层架构 ``` ┌─────────────────────────────────────────────────────────┐ │ 渲染进程 (Renderer) │ │ React 19 + xterm.js + CSS │ │ ┌──────────┬──────────┬──────────┬────────┬──────────┐ │ │ │ 概览监控 │ 文件管理 │ 交互终端 │ Docker │ 防火墙 │ │ │ └─────┬────┴─────┬────┴─────┬────┴───┬────┴─────┬────┘ │ │ │ │ │ │ │ │ │ └──────────┴──────────┴────────┴──────────┘ │ │ window.panel.* │ ├─────────────────────────────────────────────────────────┤ │ Preload (桥接层) │ │ contextBridge.exposeInMainWorld │ │ ipcRenderer.invoke / on │ ├─────────────────────────────────────────────────────────┤ │ 主进程 (Main) │ │ ┌─────────────────────────────────────────────────┐ │ │ │ SSH 连接管理 (ssh2 Client) │ │ │ │ ┌──────────┬──────────┬───────────┐ │ │ │ │ │ 主连接 │ Docker │ 部署连接 │ │ │ │ │ │ (exec + │ 专用连接 │ 专用连接 │ │ │ │ │ │ sftp + │ (独立队列)│ (后台任务) │ │ │ │ │ │ shell) │ │ │ │ │ │ │ └──────────┴──────────┴───────────┘ │ │ │ │ 串行队列 (execQueue) │ │ │ └─────────────────────────────────────────────────┘ │ │ IPC Handlers │ │ ssh / cred / sftp / shell / │ │ docker / appstore / app │ ├─────────────────────────────────────────────────────────┤ │ 应用商店后端 (appstore-server) │ │ Flask API ←→ MySQL 8 │ │ /api/apps · /api/version · /api/health │ └─────────────────────────────────────────────────────────┘ ``` ### 4.2 三条 SSH 连接设计 这是本项目的核心架构决策,通过连接隔离避免不同模块互相阻塞: | 连接 | 变量名 | 用途 | 特点 | |------|--------|------|------| | **主连接** | `activeConnection` | 概览监控、SFTP 文件管理、交互式终端 | 登录时建立;exec + sftp 共用串行队列 | | **Docker 连接** | `dockerConnection` | Docker 容器/镜像/网络/卷管理 | 登录时自动建立;独立串行队列 | | **部署连接** | `deployConnection` | 应用商店后台部署任务 | 懒创建(首次部署时建立);直接并发 exec | **串行队列机制**: - 主连接和 Docker 连接各有独立的串行队列(`execQueue` / `dockerQueue`) - 同一连接同一时刻只执行一个 channel,避免并发打开过多 channel 导致 `Channel open failure: open failed` 错误 - 命令轻量(如 `free -m`),串行执行几乎无感知延迟 - 部署连接不串行(部署任务内部已 `await` 顺序执行,任务间可并行) ### 4.3 安全设计 - **contextIsolation: true**:渲染进程与 Node.js 隔离,无法直接 `require` - **nodeIntegration: false**:渲染进程无 Node.js 权限 - **sandbox: false**:preload 可用 Node.js(暴露受控 API) - **密码加密存储**:使用 Electron `safeStorage` API(Windows 上为 DPAPI 加密),不可用时降级 base64 - **API 鉴权**:应用商店 API 通过 `Authorization: Bearer ` 校验 --- ## 五、功能模块详解 ### 5.1 登录与凭据管理 - SSH 连接表单(host / port / username / password) - 「记住密码」:连接成功后加密保存到本机 `userData/credentials.json` - 已保存服务器列表:点击快速填充、删除 - 打开时自动预填最近一次保存的凭据 ### 5.2 服务器概览监控 五张实时卡片,每张卡片独立轮询: | 卡片 | 数据源命令 | 刷新间隔 | |------|-----------|----------| | CPU | `top -bn1` / `mpstat` / `vmstat` | 2s | | 内存 | `free -m` | 3s | | 硬盘 | `df -h` | 15s | | 网络 | `/proc/net/dev` 两次采样计算速率 | 2s | | 进程 | `ps aux` + `ss -tlnp`/`netstat -tlnp`(监听端口) | 5s | - **依赖检测**:`useMetric` Hook 检测命令是否存在(如 `sysstat` 未安装时卡片置灰,提供一键安装按钮) - **CPU% / 内存% 点击排序**(降序,表头高亮箭头) - 进程卡片显示监听端口(蓝色徽章) ### 5.3 SFTP 文件管理 - 远程目录浏览(目录优先排序) - 文件上传(支持多选,实时进度推送 `sftp:progress`) - 文件下载(单文件保存对话框,批量下载到指定目录) - 新建远程目录 - 删除(统一用 `rm -rf`,路径用单引号包裹防止注入) - SFTP 会话缓存复用(同一个 channel) ### 5.4 交互式终端 - 基于 xterm.js + ssh2 PTY 实现 - 深色主题(`#1a1d29` 背景,靛蓝光标) - 支持窗口大小自适应(FitAddon) - 切换导航/断开连接/切换服务器/关窗时自动清理 Shell - React StrictMode 双挂载防护(`setTimeout(0)` + `disposed` 标记) ### 5.5 Docker 容器管理 子菜单包含 8 个功能页: | 子菜单 | 功能 | |--------|------| | 概览 | 容器数/镜像数/CPU/内存/缓存/存储路径/配置文件 | | 容器列表 | 启动/停止/删除/查看日志/进入终端 | | 镜像 | 镜像列表/删除/清理 | | 应用商店 | ★ 在线模板同步 + 一键部署 | | 任务中心 | 后台部署任务进度(步骤+日志) | | 配置 | Docker `daemon.json` 查看/编辑 | | 网络 | Docker 网络列表/创建/删除 | | 卷 | Docker 数据卷列表/删除 | **Docker 状态管理**: - 自动检测:`command -v docker` → `docker info` - 状态机:checking → running / stopped / notInstalled / error - 启动/停止按钮(停止时先杀容器再 kill dockerd,防止卡死) ### 5.6 应用商店一键部署 这是项目的核心特色功能: **数据流**: ``` MySQL (app_templates + 子表) → Flask API → Electron 主进程 (HTTP GET) → 本地缓存 (appstore-cache.json) → 前端渲染应用卡片 → 用户填写参数 → 提交部署 → 后台任务执行 → 实时进度推送 ``` **部署步骤**(4 步): 1. **拉取镜像** `docker pull `(超时 5 分钟) 2. **创建目录** `mkdir -p` 宿主机挂载目录 3. **写入配置** `cat > file << 'EOF'` heredoc 方式写入配置文件 4. **创建容器** 动态构建 `docker run` 命令并执行 **模板参数系统**: - `{{}}` 占位符替换(如 `{{REDIS_PASSWORD}}`、`{{PORT}}`) - 特殊占位符 `{{REDIS_PASSWORD_LINE}}`:根据密码是否存在动态生成配置行 - 条件渲染 `show_when`:如 Halo 选择 MySQL 数据库时才显示 MySQL 配置字段 - 下拉选项 `options`(JSON 数组):如 Halo 数据库类型选择 H2/MySQL - 环境变量值含空格时自动加单引号包裹(修复 `-Xmx256m -Xms256m` 的问题) - `extra_args` 额外参数在镜像名前插入(如 Halo 数据库连接参数) **内置模板**: | 应用 | 镜像 | 说明 | |------|------|------| | Redis | `redis:8.10.0` | 密码可选,自定义配置文件 | | MySQL | `mysql:8.4.11` | root 密码必填,自定义 my.cnf | | Halo | `registry.fit2cloud.com/halo/halo:2.26` | 支持 H2/MySQL 双模式,JVM 参数可调 | ### 5.7 防火墙管理 - 自动检测防火墙类型:`firewalld` / `ufw` / `iptables` - firewalld:状态开关、服务/端口管理(添加端口弹窗) - ufw / iptables:仅展示规则列表 - 全程复用 `window.panel.exec`,零新增 IPC ### 5.8 客户端版本管理 - 主进程内置 `CLIENT_VERSION` 常量 - 「更新」按钮触发 `/api/version` 接口检查 - 弹窗显示三种状态:有新版本(显示更新说明+前往下载)、已是最新、检查失败 - 下载链接通过 `shell.openExternal` 在系统浏览器打开 --- ## 六、IPC API 参考 ### 6.1 SSH 连接 | IPC 通道 | Preload API | 说明 | |----------|-------------|------| | `ssh:connect` | `panel.connect(config)` | 建立 SSH 连接,自动建立 Docker/部署连接 | | `ssh:disconnect` | `panel.disconnect()` | 断开所有连接 | | `ssh:session` | `panel.getSession()` | 获取当前会话信息 | | `ssh:exec` | `panel.exec(cmd, timeout)` | 在主连接上执行命令(串行队列) | | `ssh:disconnected` | `panel.onDisconnected(cb)` | 监听连接断开事件 | ### 6.2 凭据管理 | IPC 通道 | Preload API | 说明 | |----------|-------------|------| | `cred:save` | `panel.saveCredential(cred)` | 保存凭据(密码加密) | | `cred:list` | `panel.listCredentials()` | 列出已保存凭据 | | `cred:delete` | `panel.deleteCredential(key)` | 删除凭据 | ### 6.3 SFTP 文件管理 | IPC 通道 | Preload API | 说明 | |----------|-------------|------| | `sftp:list` | `panel.sftpList(path)` | 列目录 | | `sftp:upload` | `panel.sftpUpload(local, remote)` | 上传文件 | | `sftp:download` | `panel.sftpDownload(remote, local)` | 下载文件 | | `sftp:mkdir` | `panel.sftpMkdir(path)` | 创建目录 | | `sftp:delete` | `panel.sftpDelete(path, isDir, recursive)` | 删除文件/目录 | | `sftp:selectLocalFile` | `panel.sftpSelectLocalFile()` | 选择本地文件(多选) | | `sftp:selectSavePath` | `panel.sftpSelectSavePath(name)` | 选择保存路径 | | `sftp:selectDownloadDir` | `panel.sftpSelectDownloadDir()` | 选择下载目录 | | `sftp:progress` | `panel.onSftpProgress(cb)` | 上传/下载进度事件 | ### 6.4 交互式终端 | IPC 通道 | Preload API | 说明 | |----------|-------------|------| | `shell:start` | `panel.shellStart(cols, rows)` | 启动 PTY Shell | | `shell:write` | `panel.shellWrite(data)` | 向 Shell 写入数据 | | `shell:resize` | `panel.shellResize(cols, rows)` | 调整终端窗口大小 | | `shell:close` | `panel.shellClose()` | 关闭 Shell | | `shell:data` | `panel.onShellData(cb)` | 监听 Shell 输出 | | `shell:close` | `panel.onShellClose(cb)` | 监听 Shell 关闭 | ### 6.5 Docker 管理 | IPC 通道 | Preload API | 说明 | |----------|-------------|------| | `docker:exec` | `panel.dockerExec(cmd, timeout)` | 在 Docker 连接上执行命令 | | `docker:disconnect` | `panel.dockerDisconnect()` | 断开 Docker 连接 | | `docker:deployApp` | `panel.dockerDeployApp(deploy, params)` | 提交部署任务 | | `docker:checkConnections` | `panel.dockerCheckConnections()` | 检查 SSH 连接数 | | `docker:listTasks` | `panel.dockerListTasks()` | 列出所有部署任务 | | `deploy:taskUpdate` | `panel.onDeployTaskUpdate(cb)` | 监听部署进度更新 | | `docker:ready` | `panel.onDockerReady(cb)` | Docker 连接就绪事件 | | `docker:disconnected` | `panel.onDockerDisconnected(cb)` | Docker 连接断开事件 | ### 6.6 应用商店与版本管理 | IPC 通道 | Preload API | 说明 | |----------|-------------|------| | `appstore:sync` | `panel.appstoreSync()` | 从远程 API 同步模板到本地 | | `appstore:getLocal` | `panel.appstoreGetLocal()` | 获取本地缓存模板 | | `appstore:getApiUrl` | `panel.appstoreGetApiUrl()` | 获取 API 地址 | | `appstore:setApiUrl` | `panel.appstoreSetApiUrl(url)` | 设置 API 地址 | | `appstore:checkInstalled` | `panel.appstoreCheckInstalled()` | 检查已安装应用 | | `app:checkUpdate` | `panel.appCheckUpdate()` | 检查客户端版本更新 | | `app:getVersion` | `panel.appGetVersion()` | 获取当前版本号 | | `app:openUrl` | `panel.appOpenUrl(url)` | 用系统浏览器打开 URL | --- ## 七、应用商店后端服务 ### 7.1 API 端点 | 路由 | 方法 | 说明 | |------|------|------| | `/api/apps` | GET | 获取所有应用模板(含完整详情和子表) | | `/api/apps/` | GET | 获取单个应用模板详情 | | `/api/version` | GET | 获取客户端最新版本信息 | | `/api/health` | GET | 健康检查 | ### 7.2 数据库表结构 ``` app_templates (主表) ├── app_ports (端口映射子表) ├── app_volumes (挂载卷子表) ├── app_envs (环境变量子表) ├── app_mkdirs (自动创建目录子表) ├── app_write_files (配置文件子表) └── app_params (用户参数子表,含下拉选项和条件显示) app_version (客户端版本管理表) ``` ### 7.3 部署方式 ```bash # 在服务器上执行 cd appstore-server bash deploy.sh # 流程:建表 → 构建镜像 → 启动容器(3001 端口) ``` 环境变量配置: - `DB_HOST` / `DB_PORT` / `DB_USER` / `DB_PASSWORD` / `DB_NAME`:数据库连接 - `API_PORT`:API 监听端口(默认 3001) - `API_TOKEN`:鉴权 token(可选) --- ## 八、开发与构建 ### 8.1 环境要求 - Node.js 18+ - npm(国内网络建议配置 `.npmrc` 中的 Electron 镜像) - Python 3.12+(应用商店后端) - MySQL 8(应用商店数据库) ### 8.2 开发 ```bash npm install # 安装依赖(Electron 自动走国内镜像) npm run dev # 启动开发服务器(Vite HMR 热更新) ``` 开发模式下按 `F12` 打开开发者工具。 ### 8.3 构建 ```bash npm run build # 构建产物(输出到 out/) npm run build:win # 构建 + 打包 Windows 安装程序(输出到 release/) ``` ### 8.4 关键配置说明 **`.npmrc`(Electron 国内镜像)**: ``` electron_mirror=https://npmmirror.com/mirrors/electron/ ``` **`electron-builder` 配置要点**: - `npmRebuild: false`:跳过 native 模块编译(ssh2 纯 JS 无需编译) - `target: nsis`:Windows NSIS 安装程序 - `icon: build/icon.png`:256x256 应用图标 - 国内打包需配置 `ELECTRON_BUILDER_BINARIES_MIRROR` 镜像 --- ## 九、关键设计决策与踩坑记录 ### 9.1 SSH Channel 并发问题 **问题**:概览页 5 张卡片各自独立轮询,同时打开的 channel 数超出服务器上限(通常 10 个),报错 `Channel open failure: open failed`。 **方案**:在主进程 `execSsh` 函数增加串行队列(`execQueue`),所有请求逐个执行,同一时刻只占一个 channel。命令轻量,串行执行几乎无感知延迟。 ### 9.2 React StrictMode 双挂载 **问题**:开发模式下 `React.StrictMode` 会执行两次 `useEffect`(mount→unmount→remount),导致终端首次打开输出重复。 **方案**:`shellStart` 放入 `setTimeout(0)` 延迟执行并配合 `disposed` 标记,第一次同步 mount→unmount 时被 cleanup 拦截,仅真正 mount 时连接一次。 ### 9.3 Electron 二进制下载失败 **问题**:`npm install` 时 Electron postinstall 从 GitHub 下载二进制,国内网络经常失败。 **方案**:项目根目录创建 `.npmrc` 写入 `electron_mirror=https://npmmirror.com/mirrors/electron/`,安装时自动走国内镜像。 ### 9.4 Docker 环境变量含空格 **问题**:Halo 部署时 `JVM_OPTS=-Xmx256m -Xms256m` 中空格导致 `docker run` 拆分参数报 `unknown shorthand flag`。 **方案**:环境变量值含空格时自动加单引号包裹(`parts.push('-e', "'${pair}'")`)。 ### 9.5 部署连接隔离 **问题**:部署任务耗时较长(拉取镜像可能数分钟),如果复用主连接的串行队列会阻塞概览监控等常规操作。 **方案**:使用独立的第三条 SSH 连接(`deployConnection`),懒创建,部署任务直接 `exec` 不走串行队列,任务内部 `await` 顺序执行,任务间可并行。 --- ## 十、扩展指南 ### 10.1 新增应用模板 在 `appstore-server/init.sql` 中或通过 API 添加新行到 `app_templates` 主表及对应子表即可,前端无需改代码。`mapDbTemplateToFront` 函数自动将数据库格式转换为前端 deploy 格式。 ### 10.2 新增功能页面 1. 在 `src/renderer/src/pages/` 下新建组件目录 2. 在 `Panel.jsx` 的 `NAV_ITEMS` 数组添加导航项 3. 在 `Panel.jsx` 的 `
` 区域添加条件渲染 4. 如需主进程支持,在 `src/main/index.js` 添加 IPC handler,在 `src/preload/index.js` 暴露 API ### 10.3 新增 IPC 通道 ```js // 主进程 (src/main/index.js) ipcMain.handle('my:action', async (event, { param }) => { // 业务逻辑 return { ok: true, data: result } }) // Preload (src/preload/index.js) myAction: (param) => ipcRenderer.invoke('my:action', { param }) // 渲染进程 const result = await window.panel.myAction(param) ``` > AI生成