diff --git a/.gitignore b/.gitignore index fcfdd48..fcd75ef 100644 --- a/.gitignore +++ b/.gitignore @@ -7,14 +7,17 @@ backend/data/*.mv.db frontend/node_modules/ frontend/dist/ -# Logs & IDE -*.log -.idea -.DS_Store -*.local - -# Worktrees -.worktrees/ +# Logs & IDE +*.log +.idea +.DS_Store +*.local +.codex +release/ +.opencode/package-lock.json + +# Worktrees +.worktrees/ # Keep frontend .gitignore for frontend-specific rules !frontend/.gitignore diff --git a/README.md b/README.md index 6eed210..2ac4c3d 100644 --- a/README.md +++ b/README.md @@ -1,206 +1,166 @@ # SSH 管理器 -基于 Web 的 SSH / SFTP 工作区项目,适合源码交付、私有部署和二次开发。技术栈:Vue 3、Spring Boot(JDK 8)、JSch、xterm.js。 +这是一个适合按“源码 + Docker 部署”方式售卖的 SSH / SFTP 管理项目。 -## 功能 +买家拿到后,不需要装 Windows 客户端,只要会用 Docker,就能按说明启动。后面如果要二开,也可以直接在源码上改。 -- **认证**:本地用户登录(JWT) -- **账户安全**:默认管理员登录后会提示修改密码 -- **连接管理**:SSH 连接的增删改查(密码或私钥) -- **Moba 工作区**:统一入口 `/moba`,支持多工作区实例、多标签和终端/SFTP 分屏 -- **Web 终端**:通过 WebSocket + xterm.js 实现实时 SSH 终端 -- **SFTP**:文件浏览、搜索、隐藏文件切换、上传/下载、远程传输、删除、创建目录 -- **批量能力**:批量命令执行、操作日志、传输历史 -- **交付辅助**:首次启动引导、关于与交付信息、诊断摘要、备份恢复 +## 这项目适合谁 -## 环境要求 - -- JDK 8+ -- Node.js 18+ -- Maven 3.6+ - -## 快速开始 - -### 后端 - -```bash -cd backend -mvn spring-boot:run -``` - -后端运行在 http://localhost:48080 - -默认登录:`admin` / `admin123` - -### 前端 - -```bash -cd frontend -npm install -npm run dev -``` - -前端运行在 http://localhost:5173(API 与 WebSocket 会代理到后端) +- 经常连服务器的开发者 +- 小团队运维 +- NAS / 云主机用户 +- 想找 FinalShell / MobaXterm 替代方案的人 -默认登录后进入 `/moba` 工作区。历史路径 `/connections`、`/terminal` 会跳转到 `/moba`;带连接 ID 的旧深链接 `/terminal/:id`、`/sftp/:id` 会先打开对应工作区,再进入 `/moba`。 -在 `/moba` 中,点击左侧连接会创建新的工作区实例;同一连接可重复打开多个实例。 +## 这项目能做什么 -## 交付形态 +- SSH 终端 +- SFTP 文件管理 +- 批量命令执行 +- 连接和会话树备份恢复 +- 历史日志与传输记录 +- 默认管理员首次登录强制改密 -当前仓库支持两种交付方向: +## 建议怎么卖 -- 源码仓库版:适合会自己部署、要二开的买家 -- Windows 安装版:适合普通买家,安装后双击快捷方式即可使用 +建议你对外只卖这一种: -## 源码交付建议 +`源码交付 + Docker 部署版` -如果你按源码版本售卖,建议交付时至少包含: +建议交付给买家的内容只有这几样: -- 当前仓库源码 -- `README.md` 或独立部署文档 -- Docker 启动方式 -- 默认账号与修改密码说明 -- 环境变量说明 -- 备份导入导出示例 -- 售后排查时用到的诊断摘要入口说明 +1. 当前仓库源码 +2. 这份 `README.md` +3. 默认账号说明 +4. Docker 部署说明 -源码版当前不依赖授权码,买家拿到后可自行部署、二开和迁移环境。 +不再提供 Windows 安装包,也不再提供双击启动脚本。 -## Windows 安装版 - -Windows 成品版仍然是本地 Web 应用,但可封装为安装包: - -- 安装程序:`Setup.exe` -- 安装目录:`C:\Program Files\SSH Manager\` -- 数据目录:`%LOCALAPPDATA%\SSHManager\data` -- 日志目录:`%LOCALAPPDATA%\SSHManager\logs` - -安装后双击快捷方式会: - -- 自动检测程序是否已启动 -- 首次生成本地密钥 -- 后台启动内置 jar -- 自动打开浏览器到 `http://127.0.0.1:48080` - -## 启动方式 - -### Windows 本地验证 - -- 先准备 Java 8+ -- 构建后可直接运行: - - `scripts/windows/start-local.bat` -- 本地打包验证: - - `scripts/release/build-local-package.bat` - -脚本会自动: -- 创建 `runtime/` 和 `data/` -- 首次生成本地密钥 -- 启动后端 jar - -### Windows 安装版构建 +## 最简单的启动方式 先准备: -- Node.js 18+ -- Maven 3.6+ -- Inno Setup -- 一个已解压的 Windows JRE 目录,并设置环境变量 `SSH_MANAGER_WINDOWS_JRE_DIR` -- 发布版本号取自 `backend/pom.xml` 的 `` +- Docker +- Docker Compose -建议先执行预检: - -```bat -scripts\release\check-windows-release.bat -``` - -执行: - -```bat -scripts\release\build-windows-installer.bat -``` - -脚本会自动: - -- 构建前端 -- 以 `embed-frontend-dist` profile 打包后端 -- 组装 `release/windows-app/` -- 如检测到 `ISCC.exe`,生成 `release/windows-installer/SSHManager-Setup-*.exe` -- 自动带上安装器图标和买家 / 售后说明文档 - -### Docker 版 - -- Windows 一键启动验证: - - `scripts/windows/start-docker.bat` -- 停止: - - `scripts/windows/stop-docker.bat` - -更完整的交付说明见: -- `docs/delivery-guide.md` - -### 生产构建 +在项目根目录执行: ```bash -# 前端 -cd frontend && npm run build - -# 后端 -cd backend && mvn -Pembed-frontend-dist package +docker compose -f docker/docker-compose.yml up -d --build ``` -## 验证建议 +启动后访问: + +`http://localhost:48080` + +默认登录账号: + +- 用户名:`admin` +- 密码:`admin123` + +首次登录后请先修改密码。 + +## 常用命令 + +启动: + +```bash +docker compose -f docker/docker-compose.yml up -d --build +``` + +查看日志: + +```bash +docker compose -f docker/docker-compose.yml logs -f +``` + +停止: + +```bash +docker compose -f docker/docker-compose.yml down +``` + +查看状态: + +```bash +docker compose -f docker/docker-compose.yml ps +``` + +如果你习惯 `make`,也可以直接用: + +```bash +make up +make logs +make down +``` + +## 数据会放在哪里 + +Docker 默认把数据放在命名卷 `app-data` 里。 + +日常停止服务用: + +```bash +docker compose -f docker/docker-compose.yml down +``` + +不要执行: + +```bash +docker compose -f docker/docker-compose.yml down -v +``` + +因为这会把数据一起删掉。 + +## 给买家时可以直接这样说 + +```text +这是源码交付 + Docker 部署版,不是练手 demo。 +买回去后按说明执行一条 docker compose 命令就能跑起来。 +支持 SSH、SFTP、批量命令、备份恢复,适合开发者、运维和 NAS 用户。 +``` + +闲鱼商品文案见: + +- `docs/xianyu-sales-copy.md` + +## 如果你自己要开发 + +后端: + +```bash +cd backend +mvn spring-boot:run +``` + +前端: -- 前端改动后至少运行: ```bash cd frontend -npm run build +npm install +npm run dev ``` -- Moba 工作区相关回归项见: - - `docs/moba-regression-checklist.md` - -## 项目结构 - -``` -ssh-manager/ -├── backend/ # Spring Boot(JDK 8) -│ └── src/main/java/com/sshmanager/ -│ ├── config/ # 安全、WebSocket、CORS -│ ├── controller/ -│ ├── service/ -│ ├── entity/ -│ └── repository/ -├── frontend/ # Vue 3 + Vite + Tailwind -│ └── src/ -│ ├── views/ -│ ├── components/ -│ ├── stores/ -│ └── api/ -└── docs/design-system/ # UI/UX 规范 -``` - -## 配置 - -### 后端(application.yml) - -- `sshmanager.encryption-key`:用于加密连接密码的 Base64 32 字节密钥 -- `sshmanager.jwt-secret`:JWT 签名密钥 -- `spring.datasource.url`:H2 数据库路径(默认:`${DATA_DIR:/app/data}/sshmanager`) - -### 环境变量 +前端开发地址: -- `SSHMANAGER_ENCRYPTION_KEY`:覆盖加密密钥 -- `SSHMANAGER_JWT_SECRET`:覆盖 JWT 密钥 -- `DATA_DIR`:数据目录(默认 `/app/data`,Docker 下应保持卷挂载) +`http://localhost:5173` -### Docker 持久化说明 +后端开发地址: -- 默认通过 `make up` / `make restart` 运行,数据存放在 `app-data` 命名卷。 -- `make down` 仅停止并移除容器,不删除卷数据。 -- 不要使用 `docker compose ... down -v`,该命令会删除卷并导致数据丢失。 - -## 安全说明 - -- 连接密码与私钥均以 AES-256-GCM 加密存储 -- 所有 API 接口需 JWT 认证 -- WebSocket 连接在握手时校验 JWT -- CORS 仅允许前端来源 +`http://localhost:48080` + +## 环境变量 + +- `SSHMANAGER_ENCRYPTION_KEY`:连接密码加密密钥 +- `SSHMANAGER_JWT_SECRET`:JWT 密钥 +- `DATA_DIR`:数据目录 + +Docker 默认已经在 `docker/docker-compose.yml` 里给了可运行示例。正式卖给客户时,建议你改成自己的密钥再交付。 + +## 发货前自己至少检查一遍 + +1. Docker 版能正常启动 +2. 能正常登录并修改密码 +3. 能创建一条连接 +4. 能打开终端 +5. 能打开 SFTP +6. 能导出一次备份 diff --git a/docs/delivery-guide.md b/docs/delivery-guide.md deleted file mode 100644 index 85a10a8..0000000 --- a/docs/delivery-guide.md +++ /dev/null @@ -1,115 +0,0 @@ -# SSH Manager 交付指南 - -更新时间:2026-04-14 - -## 目标 - -把当前仓库整理成三种可直接交付给买家的形态: - -- 源码仓库版 -- Docker 一键版 -- Windows 安装版 - -## 源码仓库版 - -适合会自己部署、需要二开或想长期自主管理的买家。 - -### 交付内容 - -- 当前仓库源码 -- `README.md` 或独立部署文档 -- 环境变量说明 -- 初始化账号说明 -- 备份示例或演示数据说明 - -### 推荐说明 - -- 默认提供 Docker 启动方式 -- 明确 Java / Node / Maven 版本要求 -- 明确默认账号、密码修改和数据目录位置 -- 保留“关于与交付信息”页面给买家查看诊断摘要 - -## Docker 一键版 - -适合想快速运行源码版的买家。 - -### 直接启动 - -Windows: - -- `scripts/windows/start-docker.bat` - -停止: - -- `scripts/windows/stop-docker.bat` - -### 说明 - -- 数据默认保存在 Docker volume `app-data` -- 不要执行 `docker compose down -v` - -## Windows 安装版 - -适合完全不想部署环境的买家。 - -### 构建前准备 - -- 安装 Node.js 18+ -- 安装 Maven 3.6+ -- 安装 Inno Setup -- 准备一个已解压的 Windows JRE,并设置环境变量: - - `SSH_MANAGER_WINDOWS_JRE_DIR=D:\runtime\jdk-17-jre` -- 安装包版本号默认读取 `backend/pom.xml` 中的 `` - -### 发布前预检 - -建议先执行: - -- `scripts\release\check-windows-release.bat` - -作用: - -- 检查 Node.js / Maven / PowerShell / JRE / Inno Setup -- 检查图标、安装器脚本、买家说明、售后 FAQ 是否齐全 -- 构建前端 -- 以内嵌前端静态资源的方式打包后端 - -### 构建命令 - -Windows: - -- `scripts\release\build-windows-installer.bat` - -### 产物 - -- `release/windows-app/` - - 安装前的应用目录 - - 包含 jar、启动器脚本、安装器图标、说明文档和内置 JRE -- `release/windows-installer/` - - 若安装了 Inno Setup,则输出 `SSHManager-Setup-*.exe` - -### 安装后行为 - -- 程序文件默认安装到 `C:\Program Files\SSH Manager\` -- 用户数据默认保存在 `%LOCALAPPDATA%\SSHManager\` -- 首次启动自动生成本地密钥 -- 双击快捷方式会后台启动服务并自动打开浏览器 -- 开始菜单会同时提供“买家使用说明”和“售后排查 FAQ” - -## 本地打包 - -已提供本地打包脚本,可用于你自己验证交付前构建是否完整: - -- `scripts/release/build-local-package.sh` -- `scripts/release/build-local-package.bat` - -作用: - -- 构建前端 -- 以内嵌前端静态资源的方式打包后端 -- 输出到 `release/local-package/` - -## 后续建议 - -- 再补一个“首次启动引导页” -- 再补一个“关于 / 交付信息 / 诊断摘要”页面 diff --git a/docs/moba-regression-checklist.md b/docs/moba-regression-checklist.md deleted file mode 100644 index 7b50823..0000000 --- a/docs/moba-regression-checklist.md +++ /dev/null @@ -1,156 +0,0 @@ -# Moba Workspace 回归清单 - -适用范围: -- `frontend/src/layouts/MobaLayout.vue` -- `frontend/src/components/SessionTree.vue` -- `frontend/src/components/WorkspacePanel.vue` -- `frontend/src/components/SftpPanel.vue` -- `frontend/src/views/TransfersView.vue` -- 旧深链接兼容入口 `/terminal/:id`、`/sftp/:id` - -## 基础验证 - -### 构建 -```bash -cd frontend -npm run build -``` - -### 启动 -```bash -cd backend -mvn spring-boot:run - -cd frontend -npm run dev -``` - -访问: -- `http://localhost:5173/login` -- 登录后应进入 `/moba` - -## 登录与入口 - -### 登录 -- 使用有效账号登录 -- 首次管理员登录时,若触发强制改密,应能弹出改密弹窗 -- 登录后默认进入 `/moba` - -### 兼容入口 -- 访问 `/connections` 应进入 `/moba` -- 访问 `/terminal` 应进入 `/moba` -- 访问 `/transfers` 应进入 `/moba?tool=transfers` -- 访问 `/terminal/:id` 应打开对应连接的终端工作区后进入 `/moba` -- 访问 `/sftp/:id` 应打开对应连接的文件工作区后进入 `/moba` - -## 工作区 - -### 多实例 -- 在左侧连续点击同一连接两次,应创建两个独立工作区实例 -- 两个实例的顶部标签应能区分序号 -- 关闭其中一个实例,不应影响另一个实例 - -### 标签行为 -- 点击标签可切换活动工作区 -- 右键菜单中的“关闭当前 / 关闭其他 / 关闭右侧 / 关闭全部”行为正确 -- 顶部“复制会话”可基于当前实例创建一个新实例 - -### 面板控制 -- 顶部“终端”按钮可显隐终端面板 -- 顶部“文件”按钮可显隐 SFTP 面板 -- 两个面板都隐藏时,工作区应显示空态提示 -- “重置分屏”应把终端/SFTP 比例恢复为默认值 - -## 会话树 - -### 基础操作 -- 创建根文件夹 -- 创建子文件夹 -- 重命名文件夹 -- 删除文件夹 -- 删除连接节点 - -### 排序与拖拽 -- 手动排序模式下,节点可拖拽排序 -- 名称排序模式下,拖拽应禁用 -- 切换为名称排序后,文件夹应排在连接前面 - -### 搜索 -- 输入关键字后,应只显示匹配结果和必要层级 -- 匹配节点应显示高亮标记 -- 清空搜索后,应恢复完整树 - -### 同步 -- 修改连接名称后,会话树中的连接节点名称应同步更新 -- 删除连接后,会话树中的对应节点应被移除 - -## SFTP - -### 基础功能 -- 打开文件面板后应能列出目录 -- 可进入子目录 -- 可返回上级目录 -- 可直接输入路径并跳转 -- 可切换显示/隐藏隐藏文件 -- 可搜索当前目录文件 - -### 文件操作 -- 上传单个文件 -- 上传多个文件 -- 查看上传进度 -- 下载文件 -- 创建目录 -- 删除文件 -- 删除目录 - -### 远程传输 -- 从当前连接选择文件并打开远程传输弹窗 -- 选择目标连接和目标路径后可开始传输 -- 传输过程中应显示进度 -- 取消传输后应显示取消状态或取消提示 - -## Transfers - -### Local -> Many -- 选择本机文件 -- 选择目标目录 -- 选择多个目标连接 -- 调整并发 -- 开始后应生成队列任务 - -### Remote -> Many -- 选择源连接 -- 手输源文件路径并添加 -- 使用远程文件浏览器选择源文件 -- 选择目标目录或精确路径 -- 选择多个目标连接 -- 开始后应生成队列任务 - -### 队列 -- 队列应显示运行状态、进度、明细项 -- 运行中任务应可取消 -- 已完成/失败任务应保留在最近任务列表 -- 点击“清空队列”后,最近任务列表应被清空 - -## 响应式 - -### 窄屏 -- 小屏下左侧会话树应通过顶部按钮展开/收起 -- 打开工作区后,小屏侧栏应自动收起 -- Transfers 区域在小屏下不应出现明显横向溢出 -- 目标连接选择区、队列卡片和源文件按钮区应可正常折行 - -## 持久化 - -### 刷新恢复 -- 刷新页面后,工作区标签顺序应保留 -- 活动工作区应保留 -- SFTP 当前路径应保留 -- 分屏比例应保留 -- 会话树排序模式应保留 - -## 风险关注点 -- 同一连接多实例下,不应误复用旧工作区 -- 会话树在大量节点下不应出现明显卡顿或错位渲染 -- `/terminal/:id` 与 `/sftp/:id` 兼容入口不应丢失目标连接语义 -- SFTP 远程传输与 Transfers 页面应避免状态来源不一致 diff --git a/docs/release-checklist.md b/docs/release-checklist.md deleted file mode 100644 index 392e9cb..0000000 --- a/docs/release-checklist.md +++ /dev/null @@ -1,227 +0,0 @@ -# SSH Manager 发布前检查表 - -更新时间:2026-04-15 - -## 发布目标 - -用于发布可销售版本前的最终检查,覆盖: - -- 构建与测试 -- 核心功能回归 -- 源码交付说明验证 -- 交付包生成 -- 销售素材准备 - -## 一、构建与测试 - -### 前端 - -```bash -cd frontend -npm run build -``` - -### 后端 - -```bash -cd backend -mvn -Dtest=AuthControllerTest,BatchCommandServiceTest,ConnectionControllerTest test -``` - -### 脚本语法检查 - -```bash -bash -n scripts/release/build-local-package.sh -``` - -## 二、核心功能回归 - -发布前至少手工走一遍下面流程: - -### 登录与基础入口 - -- 打开 `/login` -- 检查商品化登录首页展示是否正常 -- 使用默认账号登录 -- 检查首次启动引导是否正常显示 - -### 工作区 - -- 创建第一条连接 -- 左侧树点击连接,右侧终端 / SFTP 正常刷新 -- 打开多个工作区实例 -- 关闭当前 / 关闭其他 / 关闭右侧 / 关闭全部 -- 重置分屏比例 - -### 终端 - -- 打开终端 -- 执行基本命令 -- 模拟断开后验证自动重连 -- 修改终端字体和字号后检查是否生效 - -### SFTP - -- 浏览目录 -- 上传文件 -- 下载文件 -- 删除文件 -- 新建目录 -- 测试上传同名文件策略 - -### 批量能力 - -- 打开批量命令弹窗 -- 选择多台机器 -- 执行命令并检查结果汇总 -- 复制输出内容 - -### 备份恢复 - -- 导出备份 -- 清空或切换到空环境 -- 导入备份 -- 检查连接和会话树是否恢复 - -### 历史与日志 - -- 打开历史日志弹窗 -- 检查传输历史是否保留 -- 检查操作日志是否保留 -- 检查诊断信息是否可复制 - -## 三、源码交付与诊断 - -### 关于与交付信息 - -- 打开“关于与交付信息” -- 检查版本、环境指纹、交付状态显示 -- 复制诊断信息 - -### 源码交付说明 - -- README、部署文档、环境变量说明齐全 -- 默认账号说明齐全 -- Docker 启动方式可跑通 -- 诊断摘要可用于售后排查 - -## 四、交付包生成 - -### Windows 本地版 - -```bat -scripts\release\build-local-package.bat -``` - -检查: - -- `release/local-package/` 是否生成 -- 是否包含后端 jar -- 是否包含 `start-local.bat` -- 是否包含 `README.txt` - -### Windows 本地启动 - -- 双击 `start-local.bat` -- 检查是否自动生成 `runtime/` 和 `data/` -- 检查是否能访问 `http://localhost:48080` - -### Windows 安装版 - -建议先运行: - -```bat -scripts\release\check-windows-release.bat -``` - -再执行: - -```bat -scripts\release\build-windows-installer.bat -``` - -检查: - -- 当前版本是否已在 `backend\pom.xml` 中更新 -- 已设置 `SSH_MANAGER_WINDOWS_JRE_DIR` -- `release/windows-app/` 是否生成 -- 是否包含 `ssh-manager.jar` -- 是否包含 `jre\bin\javaw.exe` -- 是否包含 `start-installed.vbs` -- 是否包含 `ssh-manager.ico` -- 是否包含 `BUYER-GUIDE.txt` -- 是否包含 `AFTER-SALES-FAQ.txt` -- 如果本机安装了 Inno Setup,`release/windows-installer/` 下是否生成 `SSHManager-Setup-*.exe` - -### Windows 安装回归 - -- 安装 `SSHManager-Setup-*.exe` -- 通过桌面或开始菜单快捷方式启动 -- 检查浏览器是否自动打开 -- 检查 `%LOCALAPPDATA%\SSHManager\data` 是否生成 -- 检查 `%LOCALAPPDATA%\SSHManager\logs\backend.log` 是否生成 -- 再次点击快捷方式时,不应重复拉起多个实例 -- 检查开始菜单里的“买家使用说明”和“售后排查 FAQ”是否可打开 - -### Docker 版 - -- 运行 `scripts/windows/start-docker.bat` 或 `make up` -- 检查容器是否正常启动 -- 检查页面是否可访问 -- 检查数据卷是否正常持久化 - -## 五、销售素材准备 - -### 商品截图 - -建议至少准备这 7 张: - -1. 登录首页 -2. `/showcase` 头图 -3. Moba 工作区主界面 -4. 终端 + SFTP 分屏 -5. 批量命令执行结果 -6. 历史日志与传输记录 -7. 关于与交付信息 - -### 商品文案 - -参考文档: - -- `docs/xianyu-sales-copy.md` - -至少准备: - -- 标题 -- 前 3 行卖点 -- 详情页正文 -- 常见问答 - -## 六、发货前最终确认 - -- JWT 与加密密钥生成方式已确认 -- 默认账号说明已写清楚 -- 交付包里没有私钥、token、测试数据或敏感配置 -- 你自己完整演练过一次“买家购买后流程” - -## 七、买家购买后流程 - -建议你自己按这个顺序演练一次: - -1. 生成本地版交付包 -2. 打包源码、部署文档和环境变量模板 -3. 启动本地版或 Docker 版 -4. 登录默认账号 -5. 修改密码 -6. 创建连接 -7. 打开终端和 SFTP -8. 导出一次备份 - -## 发布结论 - -只有在下面 4 条都满足时,才建议上架: - -- 构建通过 -- 核心功能回归通过 -- 源码交付说明验证通过 -- 交付包和商品素材都已准备完成 diff --git a/docs/sellable-product-plan.md b/docs/sellable-product-plan.md deleted file mode 100644 index 036521f..0000000 --- a/docs/sellable-product-plan.md +++ /dev/null @@ -1,107 +0,0 @@ -# SSH Manager 可卖版改造计划 - -更新时间:2026-04-14 - -## 目标定位 - -把当前项目从“可用的自用工具”推进到“可销售、可交付、可售后”的轻量 SSH/SFTP 产品。 - -建议定位: -- 国产轻量版 MobaXterm / FinalShell 替代品 -- 面向开发者、小团队运维、NAS 与云主机用户 -- 强调中文体验、易安装、批量运维、数据安全 - -## 分阶段路线 - -### 第一阶段:先做到能卖 - -目标:降低安装门槛,补齐用户信任感和售后必需能力。 - -1. 交付形态 -- Windows 一键启动版 -- Docker 一键部署版 -- 首次启动引导 -- 默认账号安全引导 - -2. 数据安全 -- 连接与会话树完整备份导出 -- 连接与会话树完整恢复导入 -- 自动备份策略 -- 恢复前风险提示 - -3. 稳定性 -- 终端断线重连 -- SFTP 超时与失败重试 -- 连接失败原因细化 -- 导入恢复后的工作区状态清理 - -4. 设置中心 -- 终端字体、字号、主题 -- 默认下载目录 -- 上传冲突策略 -- 分屏默认配置 - -### 第二阶段:做出付费理由 - -目标:让用户愿意为效率买单。 - -1. 批量运维 -- 批量执行命令 -- 批量打开会话 -- 批量上传与分发 -- 结果汇总视图 - -2. 连接管理增强 -- 标签、收藏、最近使用 -- 环境分类 -- 备注与颜色标识 -- 高级搜索 - -3. 高级传输 -- 拖拽上传 -- 传输队列 -- 断点续传 -- 冲突处理 -- 历史记录 - -### 第三阶段:提高客单价 - -目标:支持团队和更高单价销售。 - -1. 多用户与权限 -- 角色管理 -- 连接可见范围 -- 只读与可编辑权限 - -2. 审计与日志 -- 登录日志 -- 连接与传输日志 -- 文件操作日志 -- 命令执行留痕 - -3. 团队共享 -- 共享连接组 -- 共享模板 -- 共享标签 - -## 当前开发顺序 - -### P0 -- 完整备份导出 / 导入 -- 导入后的界面与工作区刷新 -- 文档化产品改造路线 - -### P1 -- 设置中心 -- 终端重连与错误诊断 -- Windows / Docker 交付脚本 - -### P2 -- 批量命令执行 -- 批量文件分发 -- 传输历史 - -## 已开始落地 - -- 已新增可卖版产品路线文档 -- 已开始实现“完整备份导出 / 导入”,覆盖连接与会话树布局 diff --git a/docs/windows-after-sales-faq.md b/docs/windows-after-sales-faq.md deleted file mode 100644 index 7d71731..0000000 --- a/docs/windows-after-sales-faq.md +++ /dev/null @@ -1,128 +0,0 @@ -# SSH Manager Windows 版售后 FAQ - -更新时间:2026-04-16 - -## 1. 安装包双击没反应怎么办 - -- 确认系统为 Windows 10 / 11 -- 右键安装包,选择“以管理员身份运行” -- 确认没有被安全软件直接拦截 -- 如果提示“未知发布者”,属于未签名安装包的常见提示,可继续安装 - -## 2. 安装完成后,点快捷方式没有打开页面怎么办 - -先等 5 到 15 秒,因为程序会先启动本地服务,再打开浏览器。 - -如果还是没打开: - -- 手动访问 `http://127.0.0.1:48080` -- 再看日志文件: - - `%LOCALAPPDATA%\SSHManager\logs\backend.log` - -## 3. 浏览器提示无法访问 `127.0.0.1:48080` 怎么办 - -通常是本地服务没有成功启动,重点检查: - -- `%LOCALAPPDATA%\SSHManager\logs\backend.log` -- `%LOCALAPPDATA%\SSHManager\runtime\` - -常见原因: - -- 杀毒软件拦截了 `javaw.exe` -- 本机 48080 端口被别的软件占用 -- 上次异常退出,服务还没完全结束 - -建议处理: - -- 先从开始菜单执行“停止 SSH Manager” -- 再重新打开桌面快捷方式 - -## 4. 如果 48080 端口被占用了怎么办 - -当前安装版默认使用 `48080` 端口。 - -如果端口冲突: - -- 先关闭占用该端口的软件 -- 或联系卖家,给你提供改端口后的新包 - -如果你自己会改: - -- 修改启动脚本中的启动参数 -- 同时把访问地址改成新的本地端口 - -## 5. 关闭浏览器后程序还在吗 - -可能还在。 - -- 浏览器只是界面 -- 本地服务仍可能在后台运行 - -再次点击快捷方式时: - -- 如果服务已在运行,会直接重新打开页面 -- 不会重复启动多份实例 - -## 6. 如何彻底退出程序 - -任选一种方式: - -- 从开始菜单执行 `停止 SSH Manager` -- 运行安装目录下的 `stop-installed.cmd` - -## 7. 升级新版本会不会丢连接数据 - -正常覆盖安装一般不会丢。 - -因为数据默认保存在: - -- `%LOCALAPPDATA%\SSHManager\data` - -不是保存在安装目录里。 - -## 8. 换电脑怎么迁移数据 - -把下面整个目录备份出来,再复制到新电脑同位置即可: - -- `%LOCALAPPDATA%\SSHManager\data` - -如果希望更稳妥,建议同时备份: - -- `%LOCALAPPDATA%\SSHManager\runtime` -- `%LOCALAPPDATA%\SSHManager\logs` - -## 9. 卸载软件会不会删掉我的数据 - -默认不会。 - -卸载主要删除的是安装目录和快捷方式,用户数据目录默认保留,避免误删连接信息。 - -## 10. 启动失败时,联系卖家要提供什么 - -建议一次性提供下面这些信息: - -- 当前软件版本 -- 复现步骤 -- 是否首次安装 -- `%LOCALAPPDATA%\SSHManager\logs\backend.log` -- 错误截图 -- 是否能打开 `http://127.0.0.1:48080` - -## 11. 我想备份连接配置,最简单的方法是什么 - -程序里可直接使用“导出备份”。 - -另外也建议定期备份: - -- `%LOCALAPPDATA%\SSHManager\data` - -## 12. 为什么这不是传统桌面窗口程序 - -这是“本地安装 + 浏览器界面”的交付方式。 - -优点是: - -- 安装成本低 -- 不依赖外网 -- 升级快 -- 保留现有 Web 管理界面的完整能力 diff --git a/docs/windows-buyer-guide.md b/docs/windows-buyer-guide.md deleted file mode 100644 index bf1023d..0000000 --- a/docs/windows-buyer-guide.md +++ /dev/null @@ -1,101 +0,0 @@ -# SSH Manager Windows 成品版使用说明 - -更新时间:2026-04-16 - -## 适用对象 - -适合不想自己部署环境、希望下载安装后直接使用的买家。 - -## 安装步骤 - -1. 双击 `SSHManager-Setup-*.exe` -2. 按提示完成安装 -3. 可选择创建桌面快捷方式 -4. 安装完成后点击“立即启动 SSH Manager” - -## 首次启动会发生什么 - -首次启动时,程序会自动: - -- 初始化本地运行目录 -- 生成本地密钥 -- 启动内置服务 -- 自动打开浏览器到 `http://127.0.0.1:48080` - -如果浏览器没有自动打开,也可以手动访问: - -- `http://127.0.0.1:48080` - -## 默认账号 - -- 用户名:`admin` -- 密码:`admin123` - -首次登录后建议立刻修改密码。 - -## 数据保存位置 - -程序不会把你的连接数据放到安装目录,默认保存在当前 Windows 用户目录下: - -- 数据目录:`%LOCALAPPDATA%\SSHManager\data` -- 运行时配置:`%LOCALAPPDATA%\SSHManager\runtime` -- 日志目录:`%LOCALAPPDATA%\SSHManager\logs` - -这意味着: - -- 升级安装一般不会丢数据 -- 卸载程序默认也不会删除你的数据目录 - -## 常见操作 - -### 启动程序 - -- 双击桌面快捷方式 `SSH Manager` -- 或从开始菜单打开 `SSH Manager` - -### 停止程序 - -- 从开始菜单执行 `停止 SSH Manager` -- 或运行安装目录下的 `stop-installed.cmd` - -### 看说明 - -安装目录会自带两份说明文本: - -- `BUYER-GUIDE.txt` -- `AFTER-SALES-FAQ.txt` - -如果遇到启动失败、浏览器没弹出、端口冲突、换机迁移等问题,优先先看 `AFTER-SALES-FAQ.txt`。 - -### 看日志 - -如果程序启动失败,请查看: - -- `%LOCALAPPDATA%\SSHManager\logs\backend.log` - -## 常见问题 - -### 1. 为什么打开的是浏览器,不是传统桌面窗口? - -这是本地安装版,不依赖外网,程序核心运行在你自己的电脑上。浏览器只是显示界面,数据仍然保存在本机。 - -### 2. 关闭浏览器后,程序还在吗? - -如果本地服务仍在运行,再次点击快捷方式会直接重新打开页面,不会重复启动多个实例。 - -### 3. 升级新版本会清空连接吗? - -正常覆盖安装不会清空数据,因为数据默认保存在 `%LOCALAPPDATA%\SSHManager\data`。 - -### 4. 卸载后数据会一起删除吗? - -默认不会自动删除数据目录,避免误删。 - -## 售后排查建议 - -联系卖家时建议同时提供: - -- 当前软件版本 -- 复现步骤 -- `%LOCALAPPDATA%\SSHManager\logs\backend.log` -- 页面中的“关于与交付信息”诊断摘要 diff --git a/docs/xianyu-sales-copy.md b/docs/xianyu-sales-copy.md index 31718b5..9a26a60 100644 --- a/docs/xianyu-sales-copy.md +++ b/docs/xianyu-sales-copy.md @@ -1,87 +1,94 @@ -# 闲鱼商品文案模板 +# 闲鱼商品文案 -更新时间:2026-04-15 +这份文案只卖一个方向: -## 标题模板 +`源码交付 + Docker 部署` -可直接选一个改: +不卖 Windows 安装包,不卖双击版。 -- SSH 管理器 本地版 MobaXterm 替代 中文 SSH+SFTP+批量命令 -- SSH/SFTP 运维工具 本地部署版 批量命令 备份恢复 自动重连 -- 国产轻量 SSH 管理器 Windows 本地版 支持 SFTP 批量运维 +## 标题 -## 主卖点短文案 +直接用下面任意一个: -适合放在商品前 3 行: +- SSH/SFTP 管理器 源码交付 Docker部署 批量命令 备份恢复 +- SSH 管理项目 源码版 Docker一键启动 支持SFTP 批量运维 +- SSH 运维工具 源码交付 支持Docker部署 SFTP 批量命令 + +## 前 3 行卖点 ```text -这不是源码练手项目,而是一套已经做成产品化体验的 SSH/SFTP 管理工具。 -支持 Moba 风格工作区、SFTP 文件传输、批量命令、备份恢复、历史日志、源码交付与部署说明。 -适合开发者、小团队运维、NAS/云主机用户,支持 Windows 本地版和 Docker 版交付。 +这是一套可以直接部署使用的 SSH / SFTP 管理项目,不是练手 demo。 +支持终端、SFTP、批量命令、备份恢复,买回去后按说明执行 docker compose 就能启动。 +适合开发者、小团队运维、NAS / 云主机用户,也适合继续二开。 ``` -## 详情页文案 +## 详情页正文 ```text +这套项目适合卖给有服务器管理需求、又想自己掌控数据和部署环境的人。 + 核心功能: -1. Moba 风格工作区,支持多标签、多实例、终端/SFTP 分屏 -2. SFTP 文件管理,支持上传、下载、远程传输、隐藏文件、路径直达 -3. 批量命令执行,可同时对多台机器执行命令并汇总结果 -4. 完整备份恢复,支持连接和会话树整体导入导出 -5. 历史日志与诊断信息,方便售后排查 -6. 终端自动重连、设置中心、首次启动引导 +1. SSH 终端 +2. SFTP 文件管理 +3. 批量命令执行 +4. 连接和会话树备份恢复 +5. 历史日志与传输记录 +6. 首次登录强制改密 适合人群: - 经常 SSH 管服务器的开发者 - 小团队运维 -- NAS / 软路由 / 云主机用户 -- 想找 FinalShell / MobaXterm 替代品的人 +- NAS / 云主机用户 +- 想找 FinalShell / MobaXterm 替代方案的人 交付方式: -- Windows 本地版 -- Docker 一键版 +- 仓库源码 +- Docker 部署说明 +- 默认账号和初始化说明 -售后说明: -- 提供基础使用指导 -- 提供源码、部署说明和初始化文档 -- 提供版本更新支持(可按你的实际策略改) +售后范围: +- 基础部署指导 +- 基础启动排查 +- 不包含远程代部署 ``` -## 标准答疑话术 +## 常见问答 ### 1. 这是源码还是成品? ```text -主推的是 Windows 可安装成品版,买家下载安装后直接用。 -如果需要二开,也可以额外提供源码版。 +这是源码交付版,主打 Docker 部署。 +买家拿到源码和说明后,可以自己部署,也可以继续二开。 ``` -### 2. 怎么交付? +### 2. 怎么启动? ```text -默认发 Windows 安装包,安装后双击快捷方式即可使用。 -如购买源码版,再额外交付仓库代码、部署文档和初始化说明。 +按文档执行 docker compose 命令就能启动。 +不需要安装 Windows 客户端,也不是双击安装包那种交付方式。 ``` ### 3. 需要联网吗? ```text -日常使用不依赖外网。Windows 安装版也是本地运行,本机保存数据,不走云端。 +部署完成后,日常使用不依赖外部云服务。 +数据保存在你自己的 Docker 环境里。 ``` -### 4. 支持什么系统? +### 4. 适合什么人买? ```text -当前主推 Windows 本地版,也支持 Docker 版部署。 +适合会用 Docker、会自己管理服务器或 NAS 的用户。 +如果你要的是纯小白双击安装版,这个版本不适合。 ``` -## 截图建议 +## 建议截图 -建议至少准备这 6 张图: +保留这 6 张就够了: -1. 登录页商品化首页 -2. Moba 工作区主界面 +1. 登录页 +2. 工作区主界面 3. 终端 + SFTP 分屏 -4. 批量命令执行结果 +4. 批量命令结果 5. 历史日志与传输记录 6. 关于与交付信息 diff --git a/scripts/installer/assets/ssh-manager.ico b/scripts/installer/assets/ssh-manager.ico deleted file mode 100644 index e846887..0000000 Binary files a/scripts/installer/assets/ssh-manager.ico and /dev/null differ diff --git a/scripts/installer/ssh-manager.iss b/scripts/installer/ssh-manager.iss deleted file mode 100644 index ffca24b..0000000 --- a/scripts/installer/ssh-manager.iss +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef MyAppVersion - #define MyAppVersion "1.0.0" -#endif - -#ifndef StageDir - #define StageDir "..\..\release\windows-app" -#endif - -#ifndef OutputDir - #define OutputDir "..\..\release\windows-installer" -#endif - -#ifndef IconFile - #define IconFile "assets\ssh-manager.ico" -#endif - -#define MyAppName "SSH Manager" -#define MyAppPublisher "SSH Manager" -#define MyAppURL "http://127.0.0.1:48080" - -[Setup] -AppId={{7EFC73C3-79F8-4CC2-92A1-8C3A56E4E8B1} -AppName={#MyAppName} -AppVerName={#MyAppName} {#MyAppVersion} -AppVersion={#MyAppVersion} -AppPublisher={#MyAppPublisher} -AppPublisherURL={#MyAppURL} -AppSupportURL={#MyAppURL} -AppUpdatesURL={#MyAppURL} -UninstallDisplayName={#MyAppName} -UninstallDisplayIcon={app}\ssh-manager.ico -SetupIconFile={#IconFile} -DefaultDirName={autopf}\SSH Manager -DefaultGroupName=SSH Manager -ArchitecturesInstallIn64BitMode=x64compatible -PrivilegesRequired=admin -AppMutex=SSHManagerLocalApp -Compression=lzma -SolidCompression=yes -WizardStyle=modern -SetupLogging=yes -OutputDir={#OutputDir} -OutputBaseFilename=SSHManager-Setup-{#MyAppVersion} -VersionInfoVersion={#MyAppVersion} -VersionInfoCompany={#MyAppPublisher} -VersionInfoDescription=SSH Manager Windows 本地管理工具 -VersionInfoProductName={#MyAppName} -VersionInfoProductVersion={#MyAppVersion} -VersionInfoCopyright=SSH Manager - -[Languages] -Name: "chinesesimp"; MessagesFile: "compiler:Languages\ChineseSimplified.isl" - -[Tasks] -Name: "desktopicon"; Description: "创建桌面快捷方式"; GroupDescription: "附加任务:"; Flags: unchecked - -[Files] -Source: "{#StageDir}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs - -[Icons] -Name: "{group}\SSH Manager"; Filename: "{app}\start-installed.vbs"; WorkingDir: "{app}"; IconFilename: "{app}\ssh-manager.ico" -Name: "{group}\停止 SSH Manager"; Filename: "{app}\stop-installed.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\ssh-manager.ico" -Name: "{group}\买家使用说明"; Filename: "{app}\BUYER-GUIDE.txt"; WorkingDir: "{app}"; IconFilename: "{app}\ssh-manager.ico" -Name: "{group}\售后排查 FAQ"; Filename: "{app}\AFTER-SALES-FAQ.txt"; WorkingDir: "{app}"; IconFilename: "{app}\ssh-manager.ico" -Name: "{group}\打开安装目录"; Filename: "{app}" -Name: "{autodesktop}\SSH Manager"; Filename: "{app}\start-installed.vbs"; WorkingDir: "{app}"; Tasks: desktopicon; IconFilename: "{app}\ssh-manager.ico" - -[Run] -Filename: "{app}\start-installed.vbs"; Description: "立即启动 SSH Manager"; Flags: nowait postinstall skipifsilent - -[UninstallDelete] -Type: files; Name: "{localappdata}\SSHManager\runtime\app.pid" diff --git a/scripts/release/build-local-package.bat b/scripts/release/build-local-package.bat deleted file mode 100644 index 85b99d1..0000000 --- a/scripts/release/build-local-package.bat +++ /dev/null @@ -1,64 +0,0 @@ -@echo off -setlocal - -set "ROOT=%~dp0..\.." -for %%I in ("%ROOT%") do set "ROOT=%%~fI" -set "OUT_DIR=%ROOT%\release\local-package" - -cd /d "%ROOT%" - -where npm >nul 2>nul -if errorlevel 1 ( - echo [ERROR] 未检测到 npm,请先安装 Node.js 18+ - pause - exit /b 1 -) - -where mvn >nul 2>nul -if errorlevel 1 ( - echo [ERROR] 未检测到 Maven,请先安装 Maven 3.6+ - pause - exit /b 1 -) - -if exist "%OUT_DIR%" rmdir /s /q "%OUT_DIR%" -mkdir "%OUT_DIR%" - -echo [1/3] 构建前端... -cd /d "%ROOT%\frontend" -call npm run build -if errorlevel 1 ( - echo [ERROR] 前端构建失败 - pause - exit /b 1 -) - -echo [2/3] 打包后端... -cd /d "%ROOT%\backend" -call mvn -Pembed-frontend-dist -DskipTests package -if errorlevel 1 ( - echo [ERROR] 后端打包失败 - pause - exit /b 1 -) - -echo [3/3] 组装本地版交付包... -copy /Y "%ROOT%\backend\target\*.jar" "%OUT_DIR%\" >nul -copy /Y "%ROOT%\scripts\windows\start-local.bat" "%OUT_DIR%\" >nul - -( - echo SSH Manager 本地版 - echo. - echo 1. 安装 Java 8+ - echo 2. 双击 start-local.bat - echo 3. 浏览器访问 http://localhost:48080 - echo. - echo 首次启动会自动在 runtime 目录生成本地密钥,在 data 目录保存数据库数据。 -) > "%OUT_DIR%\README.txt" - -echo. -echo 本地版交付包已生成: -echo %OUT_DIR% -pause - -endlocal diff --git a/scripts/release/build-local-package.sh b/scripts/release/build-local-package.sh deleted file mode 100644 index 8d7b35c..0000000 --- a/scripts/release/build-local-package.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -ROOT="$(cd "$(dirname "$0")/../.." && pwd)" -OUT_DIR="$ROOT/release/local-package" - -rm -rf "$OUT_DIR" -mkdir -p "$OUT_DIR" - -cd "$ROOT/frontend" -npm run build - -cd "$ROOT/backend" -mvn -Pembed-frontend-dist -DskipTests package - -cp target/*.jar "$OUT_DIR/" -cp "$ROOT/scripts/windows/start-local.bat" "$OUT_DIR/" - -cat > "$OUT_DIR/README.txt" <<'EOF' -SSH Manager 本地版 - -1. 安装 Java 8+ -2. 双击 start-local.bat -3. 浏览器访问 http://localhost:48080 - -首次启动会自动在 runtime 目录生成本地密钥,在 data 目录保存数据库数据。 -EOF - -echo "Local package created at: $OUT_DIR" diff --git a/scripts/release/build-windows-installer.bat b/scripts/release/build-windows-installer.bat deleted file mode 100644 index 3f9fee0..0000000 --- a/scripts/release/build-windows-installer.bat +++ /dev/null @@ -1,146 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -set "ROOT=%~dp0..\.." -for %%I in ("%ROOT%") do set "ROOT=%%~fI" -set "STAGE_DIR=%ROOT%\release\windows-app" -set "OUT_DIR=%ROOT%\release\windows-installer" -set "JRE_DIR=%SSH_MANAGER_WINDOWS_JRE_DIR%" -set "VERSION_SCRIPT=%ROOT%\scripts\release\get-app-version.ps1" - -cd /d "%ROOT%" - -where powershell >nul 2>nul -if errorlevel 1 ( - echo [ERROR] 未检测到 PowerShell,无法解析版本号 - pause - exit /b 1 -) - -for /f "usebackq delims=" %%V in (`powershell -NoProfile -ExecutionPolicy Bypass -File "%VERSION_SCRIPT%"`) do set "APP_VERSION=%%V" -if not defined APP_VERSION ( - echo [ERROR] 无法从 backend\pom.xml 读取版本号 - pause - exit /b 1 -) - -echo [INFO] 当前安装包版本: %APP_VERSION% - -where npm >nul 2>nul -if errorlevel 1 ( - echo [ERROR] 未检测到 npm,请先安装 Node.js 18+ - pause - exit /b 1 -) - -where mvn >nul 2>nul -if errorlevel 1 ( - echo [ERROR] 未检测到 Maven,请先安装 Maven 3.6+ - pause - exit /b 1 -) - -if not defined JRE_DIR ( - echo [ERROR] 请先设置环境变量 SSH_MANAGER_WINDOWS_JRE_DIR,指向已解压的 Windows JRE 目录 - pause - exit /b 1 -) - -if not exist "%JRE_DIR%\bin\javaw.exe" ( - echo [ERROR] %JRE_DIR% 不是有效的 Windows JRE 目录,缺少 bin\javaw.exe - pause - exit /b 1 -) - -if exist "%STAGE_DIR%" rmdir /s /q "%STAGE_DIR%" -if exist "%OUT_DIR%" rmdir /s /q "%OUT_DIR%" -mkdir "%STAGE_DIR%" -mkdir "%OUT_DIR%" - -echo [1/5] 构建前端... -cd /d "%ROOT%\frontend" -call npm run build -if errorlevel 1 ( - echo [ERROR] 前端构建失败 - pause - exit /b 1 -) - -echo [2/5] 打包后端(内嵌前端静态资源)... -cd /d "%ROOT%\backend" -call mvn -Pembed-frontend-dist -DskipTests package -if errorlevel 1 ( - echo [ERROR] 后端打包失败 - pause - exit /b 1 -) - -echo [3/5] 组装 Windows 应用目录... -copy /Y "%ROOT%\backend\target\*.jar" "%STAGE_DIR%\ssh-manager.jar" >nul -if errorlevel 1 ( - echo [ERROR] 复制后端 jar 失败 - pause - exit /b 1 -) - -xcopy "%JRE_DIR%" "%STAGE_DIR%\jre\" /E /I /Y >nul -if errorlevel 1 ( - echo [ERROR] 复制 JRE 失败 - pause - exit /b 1 -) - -copy /Y "%ROOT%\scripts\windows\start-installed.ps1" "%STAGE_DIR%\" >nul -copy /Y "%ROOT%\scripts\windows\start-installed.cmd" "%STAGE_DIR%\" >nul -copy /Y "%ROOT%\scripts\windows\start-installed.vbs" "%STAGE_DIR%\" >nul -copy /Y "%ROOT%\scripts\windows\stop-installed.ps1" "%STAGE_DIR%\" >nul -copy /Y "%ROOT%\scripts\windows\stop-installed.cmd" "%STAGE_DIR%\" >nul -copy /Y "%ROOT%\scripts\installer\assets\ssh-manager.ico" "%STAGE_DIR%\" >nul -copy /Y "%ROOT%\docs\windows-buyer-guide.md" "%STAGE_DIR%\BUYER-GUIDE.txt" >nul -copy /Y "%ROOT%\docs\windows-after-sales-faq.md" "%STAGE_DIR%\AFTER-SALES-FAQ.txt" >nul - -( - echo SSH Manager Windows 成品版 - echo. - echo 1. 运行安装包或直接双击 start-installed.vbs - echo 2. 程序会自动启动本地服务并打开浏览器 - echo 3. 默认访问地址: http://127.0.0.1:48080 - echo. - echo 数据目录: - echo %%LOCALAPPDATA%%\SSHManager\data - echo 日志目录: - echo %%LOCALAPPDATA%%\SSHManager\logs - echo. - echo 买家使用说明: - echo BUYER-GUIDE.txt - echo 售后排查 FAQ: - echo AFTER-SALES-FAQ.txt - echo. - echo 如需停止服务,可执行 stop-installed.cmd -) > "%STAGE_DIR%\README.txt" - -echo [4/5] 检测 Inno Setup... -where ISCC.exe >nul 2>nul -if errorlevel 1 ( - echo [WARN] 未检测到 ISCC.exe,已生成 Windows 应用目录: - echo %STAGE_DIR% - echo [WARN] 安装 Inno Setup 后可手动执行: - echo ISCC.exe /DStageDir="%STAGE_DIR%" /DOutputDir="%OUT_DIR%" /DMyAppVersion="%APP_VERSION%" "%ROOT%\scripts\installer\ssh-manager.iss" - pause - exit /b 0 -) - -echo [5/5] 生成安装包... -ISCC.exe /DStageDir="%STAGE_DIR%" /DOutputDir="%OUT_DIR%" /DMyAppVersion="%APP_VERSION%" "%ROOT%\scripts\installer\ssh-manager.iss" -if errorlevel 1 ( - echo [ERROR] Inno Setup 打包失败 - pause - exit /b 1 -) - -echo. -echo Windows 安装包已生成: -echo %OUT_DIR% -pause - -endlocal diff --git a/scripts/release/check-windows-release.bat b/scripts/release/check-windows-release.bat deleted file mode 100644 index ec5f6ff..0000000 --- a/scripts/release/check-windows-release.bat +++ /dev/null @@ -1,149 +0,0 @@ -@echo off -setlocal - -set "ROOT=%~dp0..\.." -for %%I in ("%ROOT%") do set "ROOT=%%~fI" -set "VERSION_SCRIPT=%ROOT%\scripts\release\get-app-version.ps1" -set "JRE_DIR=%SSH_MANAGER_WINDOWS_JRE_DIR%" - -cd /d "%ROOT%" - -where powershell >nul 2>nul -if errorlevel 1 ( - echo [ERROR] 未检测到 PowerShell,无法解析版本号 - pause - exit /b 1 -) - -for /f "usebackq delims=" %%V in (`powershell -NoProfile -ExecutionPolicy Bypass -File "%VERSION_SCRIPT%"`) do set "APP_VERSION=%%V" -if not defined APP_VERSION ( - echo [ERROR] 无法从 backend\pom.xml 读取版本号 - pause - exit /b 1 -) - -echo [INFO] 当前发布版本: %APP_VERSION% - -where npm >nul 2>nul -if errorlevel 1 ( - echo [ERROR] 未检测到 npm,请先安装 Node.js 18+ - pause - exit /b 1 -) - -where mvn >nul 2>nul -if errorlevel 1 ( - echo [ERROR] 未检测到 Maven,请先安装 Maven 3.6+ - pause - exit /b 1 -) - -if not defined JRE_DIR ( - echo [ERROR] 请先设置环境变量 SSH_MANAGER_WINDOWS_JRE_DIR,指向已解压的 Windows JRE 目录 - pause - exit /b 1 -) - -if not exist "%JRE_DIR%\bin\javaw.exe" ( - echo [ERROR] %JRE_DIR% 不是有效的 Windows JRE 目录,缺少 bin\javaw.exe - pause - exit /b 1 -) - -if not exist "%ROOT%\scripts\installer\ssh-manager.iss" ( - echo [ERROR] 缺少安装器脚本 scripts\installer\ssh-manager.iss - pause - exit /b 1 -) - -if not exist "%ROOT%\scripts\installer\assets\ssh-manager.ico" ( - echo [ERROR] 缺少安装器图标 scripts\installer\assets\ssh-manager.ico - pause - exit /b 1 -) - -if not exist "%ROOT%\docs\windows-buyer-guide.md" ( - echo [ERROR] 缺少 docs\windows-buyer-guide.md - pause - exit /b 1 -) - -if not exist "%ROOT%\docs\windows-after-sales-faq.md" ( - echo [ERROR] 缺少 docs\windows-after-sales-faq.md - pause - exit /b 1 -) - -where ISCC.exe >nul 2>nul -if errorlevel 1 ( - echo [WARN] 未检测到 ISCC.exe,本次只能验证构建链路,暂时无法生成 Setup.exe -) else ( - echo [INFO] 已检测到 Inno Setup 编译器 ISCC.exe -) - -echo [1/4] 构建前端... -cd /d "%ROOT%\frontend" -call npm run build -if errorlevel 1 ( - echo [ERROR] 前端构建失败 - pause - exit /b 1 -) - -if not exist "%ROOT%\frontend\dist\index.html" ( - echo [ERROR] 前端 dist 产物不完整,缺少 frontend\dist\index.html - pause - exit /b 1 -) - -echo [2/4] 打包后端(内嵌前端静态资源)... -cd /d "%ROOT%\backend" -call mvn -Pembed-frontend-dist -DskipTests package -if errorlevel 1 ( - echo [ERROR] 后端打包失败 - pause - exit /b 1 -) - -dir /b "%ROOT%\backend\target\*.jar" >nul 2>nul -if errorlevel 1 ( - echo [ERROR] 后端产物不完整,未找到 target\*.jar - pause - exit /b 1 -) - -echo [3/4] 检查安装器输入文件... -if not exist "%ROOT%\scripts\windows\start-installed.ps1" ( - echo [ERROR] 缺少 scripts\windows\start-installed.ps1 - pause - exit /b 1 -) -if not exist "%ROOT%\scripts\windows\start-installed.cmd" ( - echo [ERROR] 缺少 scripts\windows\start-installed.cmd - pause - exit /b 1 -) -if not exist "%ROOT%\scripts\windows\start-installed.vbs" ( - echo [ERROR] 缺少 scripts\windows\start-installed.vbs - pause - exit /b 1 -) -if not exist "%ROOT%\scripts\windows\stop-installed.ps1" ( - echo [ERROR] 缺少 scripts\windows\stop-installed.ps1 - pause - exit /b 1 -) -if not exist "%ROOT%\scripts\windows\stop-installed.cmd" ( - echo [ERROR] 缺少 scripts\windows\stop-installed.cmd - pause - exit /b 1 -) - -echo [4/4] 发布预检完成 -echo [OK] Windows 发布预检通过 -echo [OK] 版本号: %APP_VERSION% -echo [OK] 下一步执行: -echo scripts\release\build-windows-installer.bat -pause - -endlocal diff --git a/scripts/release/get-app-version.ps1 b/scripts/release/get-app-version.ps1 deleted file mode 100644 index a297ab5..0000000 --- a/scripts/release/get-app-version.ps1 +++ /dev/null @@ -1,14 +0,0 @@ -param( - [string]$PomPath = (Join-Path $PSScriptRoot '..\..\backend\pom.xml') -) - -$resolvedPath = Resolve-Path -LiteralPath $PomPath -ErrorAction Stop -[xml]$pom = Get-Content -LiteralPath $resolvedPath -Raw -Encoding UTF8 -$version = $pom.project.version - -if ([string]::IsNullOrWhiteSpace($version)) { - Write-Error "Failed to read from $resolvedPath" - exit 1 -} - -Write-Output $version.Trim() diff --git a/scripts/windows/start-docker.bat b/scripts/windows/start-docker.bat deleted file mode 100644 index 728615d..0000000 --- a/scripts/windows/start-docker.bat +++ /dev/null @@ -1,28 +0,0 @@ -@echo off -setlocal - -set "ROOT=%~dp0..\.." -for %%I in ("%ROOT%") do set "ROOT=%%~fI" -cd /d "%ROOT%" - -docker compose version >nul 2>nul -if errorlevel 1 ( - echo [ERROR] 未检测到 docker compose,请先安装 Docker Desktop - pause - exit /b 1 -) - -echo 启动 Docker 版 SSH Manager... -docker compose -f docker/docker-compose.yml up -d --build -if errorlevel 1 ( - echo [ERROR] Docker 启动失败 - pause - exit /b 1 -) - -echo. -echo 已启动: http://localhost:48080 -echo 查看日志: docker compose -f docker/docker-compose.yml logs -f -pause - -endlocal diff --git a/scripts/windows/start-installed.cmd b/scripts/windows/start-installed.cmd deleted file mode 100644 index dc24ca0..0000000 --- a/scripts/windows/start-installed.cmd +++ /dev/null @@ -1,13 +0,0 @@ -@echo off -setlocal - -powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0start-installed.ps1" -if errorlevel 1 ( - echo. - echo [ERROR] SSH Manager 启动失败 - echo 日志目录: %LOCALAPPDATA%\SSHManager\logs - pause - exit /b 1 -) - -endlocal diff --git a/scripts/windows/start-installed.ps1 b/scripts/windows/start-installed.ps1 deleted file mode 100644 index e2bf93f..0000000 --- a/scripts/windows/start-installed.ps1 +++ /dev/null @@ -1,186 +0,0 @@ -$ErrorActionPreference = 'Stop' - -param( - [switch]$NoBrowser -) - -$appRoot = Split-Path -Parent $MyInvocation.MyCommand.Path -$productName = 'SSH Manager' -$baseUrl = 'http://127.0.0.1:48080' -$healthUrl = "$baseUrl/api/auth/health" -$localRoot = Join-Path $env:LOCALAPPDATA 'SSHManager' -$dataDir = Join-Path $localRoot 'data' -$runtimeDir = Join-Path $localRoot 'runtime' -$logsDir = Join-Path $localRoot 'logs' -$stateFile = Join-Path $runtimeDir 'launcher-state.json' -$pidFile = Join-Path $runtimeDir 'app.pid' -$logFile = Join-Path $logsDir 'backend.log' -$jarPath = Join-Path $appRoot 'ssh-manager.jar' -$bundledJavaw = Join-Path $appRoot 'jre\bin\javaw.exe' -$bundledJava = Join-Path $appRoot 'jre\bin\java.exe' - -function Ensure-Directory([string]$path) { - if (-not (Test-Path $path)) { - New-Item -ItemType Directory -Path $path | Out-Null - } -} - -function Test-Healthy { - try { - $response = Invoke-RestMethod -Uri $healthUrl -Method Get -TimeoutSec 2 - return $response.app -eq 'ssh-manager' -and $response.status -eq 'ok' - } catch { - return $false - } -} - -function Get-JavaCommand { - if (Test-Path $bundledJavaw) { - return $bundledJavaw - } - if (Test-Path $bundledJava) { - return $bundledJava - } - - $javaw = Get-Command javaw.exe -ErrorAction SilentlyContinue - if ($javaw) { - return $javaw.Source - } - - $java = Get-Command java.exe -ErrorAction SilentlyContinue - if ($java) { - return $java.Source - } - - throw '未找到可用的 Java 运行时,请重新安装带内置 JRE 的版本。' -} - -function New-State { - $jwtBytes = New-Object byte[] 48 - [Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($jwtBytes) - - $encBytes = New-Object byte[] 32 - [Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($encBytes) - - $state = @{ - jwtSecret = [Convert]::ToBase64String($jwtBytes) - encryptionKey = [Convert]::ToBase64String($encBytes) - createdAt = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() - } - - $state | ConvertTo-Json | Set-Content -Path $stateFile -Encoding ASCII - return $state -} - -function Get-State { - if (-not (Test-Path $stateFile)) { - return New-State - } - - $state = Get-Content -Path $stateFile -Raw | ConvertFrom-Json - if (-not $state.jwtSecret -or -not $state.encryptionKey) { - return New-State - } - - return $state -} - -function Clear-StalePid { - if (-not (Test-Path $pidFile)) { - return - } - - $rawPid = Get-Content -Path $pidFile -Raw - $pidInfo = $null - - try { - $pidInfo = $rawPid | ConvertFrom-Json - } catch { - $legacyPid = 0 - if ([int]::TryParse($rawPid, [ref]$legacyPid)) { - $pidInfo = @{ - pid = $legacyPid - startedAt = 0 - } - } - } - - if (-not $pidInfo) { - Remove-Item -Path $pidFile -Force -ErrorAction SilentlyContinue - return - } - - $process = Get-Process -Id ([int]$pidInfo.pid) -ErrorAction SilentlyContinue - if (-not $process) { - Remove-Item -Path $pidFile -Force -ErrorAction SilentlyContinue - return - } - - if ($pidInfo.startedAt -and $process.StartTime) { - $processStartedAt = [DateTimeOffset]$process.StartTime - if ($processStartedAt.ToUnixTimeMilliseconds() -ne [int64]$pidInfo.startedAt) { - Remove-Item -Path $pidFile -Force -ErrorAction SilentlyContinue - } - } -} - -Ensure-Directory $localRoot -Ensure-Directory $dataDir -Ensure-Directory $runtimeDir -Ensure-Directory $logsDir - -if (-not (Test-Path $jarPath)) { - throw "未找到程序包:$jarPath" -} - -Clear-StalePid - -if (Test-Healthy) { - if (-not $NoBrowser) { - Start-Process $baseUrl | Out-Null - } - exit 0 -} - -$state = Get-State -$javaCommand = Get-JavaCommand - -$arguments = @( - '-Dfile.encoding=UTF-8' - '-Dserver.address=127.0.0.1' - "-Dlogging.file.name=$logFile" - "-DDATA_DIR=$dataDir" - "-Dsshmanager.jwt-secret=$($state.jwtSecret)" - "-Dsshmanager.encryption-key=$($state.encryptionKey)" - '-jar' - $jarPath -) - -$process = Start-Process -FilePath $javaCommand -ArgumentList $arguments -WorkingDirectory $appRoot -PassThru -$pidInfo = @{ - pid = $process.Id - startedAt = ([DateTimeOffset]$process.StartTime).ToUnixTimeMilliseconds() -} -$pidInfo | ConvertTo-Json | Set-Content -Path $pidFile -Encoding ASCII - -for ($i = 0; $i -lt 45; $i++) { - Start-Sleep -Seconds 1 - - if (Test-Healthy) { - if (-not $NoBrowser) { - Start-Process $baseUrl | Out-Null - } - exit 0 - } - - $process.Refresh() - if ($process.HasExited) { - break - } -} - -if (-not $process.HasExited) { - Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue -} - -throw "SSH Manager 启动失败,请检查日志:$logFile" diff --git a/scripts/windows/start-installed.vbs b/scripts/windows/start-installed.vbs deleted file mode 100644 index 275286b..0000000 --- a/scripts/windows/start-installed.vbs +++ /dev/null @@ -1,3 +0,0 @@ -Set shell = CreateObject("WScript.Shell") -scriptPath = Replace(WScript.ScriptFullName, ".vbs", ".cmd") -shell.Run Chr(34) & scriptPath & Chr(34), 0, False diff --git a/scripts/windows/start-local.bat b/scripts/windows/start-local.bat deleted file mode 100644 index 426e5d7..0000000 --- a/scripts/windows/start-local.bat +++ /dev/null @@ -1,56 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -set "ROOT=%~dp0..\.." -for %%I in ("%ROOT%") do set "ROOT=%%~fI" -set "RUNTIME_DIR=%ROOT%\runtime" -set "DATA_DIR=%ROOT%\data" -set "ENV_FILE=%RUNTIME_DIR%\local-env.bat" - -if not exist "%RUNTIME_DIR%" mkdir "%RUNTIME_DIR%" -if not exist "%DATA_DIR%" mkdir "%DATA_DIR%" - -where java >nul 2>nul -if errorlevel 1 ( - echo [ERROR] 未检测到 Java,请先安装 JDK/JRE 8+ - pause - exit /b 1 -) - -if not exist "%ENV_FILE%" ( - echo 首次启动,正在生成本地运行密钥... - powershell -NoProfile -ExecutionPolicy Bypass -Command ^ - "$jwt=[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(([Guid]::NewGuid().ToString('N')+[Guid]::NewGuid().ToString('N'))));" ^ - "$bytes=New-Object byte[] 32; [Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes); $enc=[Convert]::ToBase64String($bytes);" ^ - "$content=@('set SSHMANAGER_JWT_SECRET='+$jwt,'set SSHMANAGER_ENCRYPTION_KEY='+$enc); Set-Content -Path '%ENV_FILE%' -Value $content -Encoding ASCII" - if errorlevel 1 ( - echo [ERROR] 生成本地密钥失败 - pause - exit /b 1 - ) -) - -call "%ENV_FILE%" -set "DATA_DIR=%DATA_DIR%" - -set "APP_JAR=" -for %%F in ("%ROOT%\backend\target\*.jar") do ( - set "APP_JAR=%%~fF" -) - -if not defined APP_JAR ( - echo [ERROR] 未找到 backend\target\*.jar - echo 请先执行发布构建,或将打包后的 jar 放到 backend\target 目录。 - pause - exit /b 1 -) - -echo 启动 SSH Manager... -echo 数据目录: %DATA_DIR% -echo 程序包: %APP_JAR% -echo 访问地址: http://localhost:48080 -echo. - -java -jar "%APP_JAR%" - -endlocal diff --git a/scripts/windows/stop-docker.bat b/scripts/windows/stop-docker.bat deleted file mode 100644 index 5120080..0000000 --- a/scripts/windows/stop-docker.bat +++ /dev/null @@ -1,11 +0,0 @@ -@echo off -setlocal - -set "ROOT=%~dp0..\.." -for %%I in ("%ROOT%") do set "ROOT=%%~fI" -cd /d "%ROOT%" - -docker compose -f docker/docker-compose.yml down -pause - -endlocal diff --git a/scripts/windows/stop-installed.cmd b/scripts/windows/stop-installed.cmd deleted file mode 100644 index 8b9e883..0000000 --- a/scripts/windows/stop-installed.cmd +++ /dev/null @@ -1,14 +0,0 @@ -@echo off -setlocal - -powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0stop-installed.ps1" -if errorlevel 1 ( - echo. - echo [ERROR] SSH Manager 停止失败 - pause - exit /b 1 -) - -pause - -endlocal diff --git a/scripts/windows/stop-installed.ps1 b/scripts/windows/stop-installed.ps1 deleted file mode 100644 index ddafa2f..0000000 --- a/scripts/windows/stop-installed.ps1 +++ /dev/null @@ -1,50 +0,0 @@ -$ErrorActionPreference = 'Stop' - -$runtimeDir = Join-Path (Join-Path $env:LOCALAPPDATA 'SSHManager') 'runtime' -$pidFile = Join-Path $runtimeDir 'app.pid' - -if (-not (Test-Path $pidFile)) { - Write-Output 'SSH Manager 当前未运行。' - exit 0 -} - -$rawPid = Get-Content -Path $pidFile -Raw -$pidInfo = $null - -try { - $pidInfo = $rawPid | ConvertFrom-Json -} catch { - $legacyPid = 0 - if ([int]::TryParse($rawPid, [ref]$legacyPid)) { - $pidInfo = @{ - pid = $legacyPid - startedAt = 0 - } - } -} - -if (-not $pidInfo) { - Remove-Item -Path $pidFile -Force -ErrorAction SilentlyContinue - Write-Output '已清理无效 PID 文件。' - exit 0 -} - -$process = Get-Process -Id ([int]$pidInfo.pid) -ErrorAction SilentlyContinue -if (-not $process) { - Remove-Item -Path $pidFile -Force -ErrorAction SilentlyContinue - Write-Output 'SSH Manager 当前未运行。' - exit 0 -} - -if ($pidInfo.startedAt -and $process.StartTime) { - $processStartedAt = ([DateTimeOffset]$process.StartTime).ToUnixTimeMilliseconds() - if ($processStartedAt -ne [int64]$pidInfo.startedAt) { - Remove-Item -Path $pidFile -Force -ErrorAction SilentlyContinue - Write-Output '已清理过期 PID 文件,未停止任何进程。' - exit 0 - } -} - -Stop-Process -Id ([int]$pidInfo.pid) -Force -Remove-Item -Path $pidFile -Force -ErrorAction SilentlyContinue -Write-Output 'SSH Manager 已停止。'