chore: focus delivery on source and docker
This commit is contained in:
19
.gitignore
vendored
19
.gitignore
vendored
@@ -7,14 +7,17 @@ backend/data/*.mv.db
|
|||||||
frontend/node_modules/
|
frontend/node_modules/
|
||||||
frontend/dist/
|
frontend/dist/
|
||||||
|
|
||||||
# Logs & IDE
|
# Logs & IDE
|
||||||
*.log
|
*.log
|
||||||
.idea
|
.idea
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.local
|
*.local
|
||||||
|
.codex
|
||||||
# Worktrees
|
release/
|
||||||
.worktrees/
|
.opencode/package-lock.json
|
||||||
|
|
||||||
|
# Worktrees
|
||||||
|
.worktrees/
|
||||||
|
|
||||||
# Keep frontend .gitignore for frontend-specific rules
|
# Keep frontend .gitignore for frontend-specific rules
|
||||||
!frontend/.gitignore
|
!frontend/.gitignore
|
||||||
|
|||||||
312
README.md
312
README.md
@@ -1,206 +1,166 @@
|
|||||||
# SSH 管理器
|
# 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+
|
- NAS / 云主机用户
|
||||||
- Node.js 18+
|
- 想找 FinalShell / MobaXterm 替代方案的人
|
||||||
- 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 会代理到后端)
|
|
||||||
|
|
||||||
默认登录后进入 `/moba` 工作区。历史路径 `/connections`、`/terminal` 会跳转到 `/moba`;带连接 ID 的旧深链接 `/terminal/:id`、`/sftp/:id` 会先打开对应工作区,再进入 `/moba`。
|
## 这项目能做什么
|
||||||
在 `/moba` 中,点击左侧连接会创建新的工作区实例;同一连接可重复打开多个实例。
|
|
||||||
|
|
||||||
## 交付形态
|
- SSH 终端
|
||||||
|
- SFTP 文件管理
|
||||||
|
- 批量命令执行
|
||||||
|
- 连接和会话树备份恢复
|
||||||
|
- 历史日志与传输记录
|
||||||
|
- 默认管理员首次登录强制改密
|
||||||
|
|
||||||
当前仓库支持两种交付方向:
|
## 建议怎么卖
|
||||||
|
|
||||||
- 源码仓库版:适合会自己部署、要二开的买家
|
建议你对外只卖这一种:
|
||||||
- Windows 安装版:适合普通买家,安装后双击快捷方式即可使用
|
|
||||||
|
|
||||||
## 源码交付建议
|
`源码交付 + Docker 部署版`
|
||||||
|
|
||||||
如果你按源码版本售卖,建议交付时至少包含:
|
建议交付给买家的内容只有这几样:
|
||||||
|
|
||||||
- 当前仓库源码
|
1. 当前仓库源码
|
||||||
- `README.md` 或独立部署文档
|
2. 这份 `README.md`
|
||||||
- Docker 启动方式
|
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+
|
- Docker
|
||||||
- Maven 3.6+
|
- Docker Compose
|
||||||
- Inno Setup
|
|
||||||
- 一个已解压的 Windows JRE 目录,并设置环境变量 `SSH_MANAGER_WINDOWS_JRE_DIR`
|
|
||||||
- 发布版本号取自 `backend/pom.xml` 的 `<version>`
|
|
||||||
|
|
||||||
建议先执行预检:
|
在项目根目录执行:
|
||||||
|
|
||||||
```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
|
```bash
|
||||||
# 前端
|
docker compose -f docker/docker-compose.yml up -d --build
|
||||||
cd frontend && npm run build
|
|
||||||
|
|
||||||
# 后端
|
|
||||||
cd backend && mvn -Pembed-frontend-dist package
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 验证建议
|
启动后访问:
|
||||||
|
|
||||||
|
`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
|
```bash
|
||||||
cd frontend
|
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`:覆盖加密密钥
|
`http://localhost:5173`
|
||||||
- `SSHMANAGER_JWT_SECRET`:覆盖 JWT 密钥
|
|
||||||
- `DATA_DIR`:数据目录(默认 `/app/data`,Docker 下应保持卷挂载)
|
|
||||||
|
|
||||||
### Docker 持久化说明
|
后端开发地址:
|
||||||
|
|
||||||
- 默认通过 `make up` / `make restart` 运行,数据存放在 `app-data` 命名卷。
|
`http://localhost:48080`
|
||||||
- `make down` 仅停止并移除容器,不删除卷数据。
|
|
||||||
- 不要使用 `docker compose ... down -v`,该命令会删除卷并导致数据丢失。
|
## 环境变量
|
||||||
|
|
||||||
## 安全说明
|
- `SSHMANAGER_ENCRYPTION_KEY`:连接密码加密密钥
|
||||||
|
- `SSHMANAGER_JWT_SECRET`:JWT 密钥
|
||||||
- 连接密码与私钥均以 AES-256-GCM 加密存储
|
- `DATA_DIR`:数据目录
|
||||||
- 所有 API 接口需 JWT 认证
|
|
||||||
- WebSocket 连接在握手时校验 JWT
|
Docker 默认已经在 `docker/docker-compose.yml` 里给了可运行示例。正式卖给客户时,建议你改成自己的密钥再交付。
|
||||||
- CORS 仅允许前端来源
|
|
||||||
|
## 发货前自己至少检查一遍
|
||||||
|
|
||||||
|
1. Docker 版能正常启动
|
||||||
|
2. 能正常登录并修改密码
|
||||||
|
3. 能创建一条连接
|
||||||
|
4. 能打开终端
|
||||||
|
5. 能打开 SFTP
|
||||||
|
6. 能导出一次备份
|
||||||
|
|||||||
@@ -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` 中的 `<version>`
|
|
||||||
|
|
||||||
### 发布前预检
|
|
||||||
|
|
||||||
建议先执行:
|
|
||||||
|
|
||||||
- `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/`
|
|
||||||
|
|
||||||
## 后续建议
|
|
||||||
|
|
||||||
- 再补一个“首次启动引导页”
|
|
||||||
- 再补一个“关于 / 交付信息 / 诊断摘要”页面
|
|
||||||
@@ -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 页面应避免状态来源不一致
|
|
||||||
@@ -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 条都满足时,才建议上架:
|
|
||||||
|
|
||||||
- 构建通过
|
|
||||||
- 核心功能回归通过
|
|
||||||
- 源码交付说明验证通过
|
|
||||||
- 交付包和商品素材都已准备完成
|
|
||||||
@@ -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
|
|
||||||
- 批量命令执行
|
|
||||||
- 批量文件分发
|
|
||||||
- 传输历史
|
|
||||||
|
|
||||||
## 已开始落地
|
|
||||||
|
|
||||||
- 已新增可卖版产品路线文档
|
|
||||||
- 已开始实现“完整备份导出 / 导入”,覆盖连接与会话树布局
|
|
||||||
@@ -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 管理界面的完整能力
|
|
||||||
@@ -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`
|
|
||||||
- 页面中的“关于与交付信息”诊断摘要
|
|
||||||
@@ -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
|
```text
|
||||||
这不是源码练手项目,而是一套已经做成产品化体验的 SSH/SFTP 管理工具。
|
这是一套可以直接部署使用的 SSH / SFTP 管理项目,不是练手 demo。
|
||||||
支持 Moba 风格工作区、SFTP 文件传输、批量命令、备份恢复、历史日志、源码交付与部署说明。
|
支持终端、SFTP、批量命令、备份恢复,买回去后按说明执行 docker compose 就能启动。
|
||||||
适合开发者、小团队运维、NAS/云主机用户,支持 Windows 本地版和 Docker 版交付。
|
适合开发者、小团队运维、NAS / 云主机用户,也适合继续二开。
|
||||||
```
|
```
|
||||||
|
|
||||||
## 详情页文案
|
## 详情页正文
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
这套项目适合卖给有服务器管理需求、又想自己掌控数据和部署环境的人。
|
||||||
|
|
||||||
核心功能:
|
核心功能:
|
||||||
1. Moba 风格工作区,支持多标签、多实例、终端/SFTP 分屏
|
1. SSH 终端
|
||||||
2. SFTP 文件管理,支持上传、下载、远程传输、隐藏文件、路径直达
|
2. SFTP 文件管理
|
||||||
3. 批量命令执行,可同时对多台机器执行命令并汇总结果
|
3. 批量命令执行
|
||||||
4. 完整备份恢复,支持连接和会话树整体导入导出
|
4. 连接和会话树备份恢复
|
||||||
5. 历史日志与诊断信息,方便售后排查
|
5. 历史日志与传输记录
|
||||||
6. 终端自动重连、设置中心、首次启动引导
|
6. 首次登录强制改密
|
||||||
|
|
||||||
适合人群:
|
适合人群:
|
||||||
- 经常 SSH 管服务器的开发者
|
- 经常 SSH 管服务器的开发者
|
||||||
- 小团队运维
|
- 小团队运维
|
||||||
- NAS / 软路由 / 云主机用户
|
- NAS / 云主机用户
|
||||||
- 想找 FinalShell / MobaXterm 替代品的人
|
- 想找 FinalShell / MobaXterm 替代方案的人
|
||||||
|
|
||||||
交付方式:
|
交付方式:
|
||||||
- Windows 本地版
|
- 仓库源码
|
||||||
- Docker 一键版
|
- Docker 部署说明
|
||||||
|
- 默认账号和初始化说明
|
||||||
|
|
||||||
售后说明:
|
售后范围:
|
||||||
- 提供基础使用指导
|
- 基础部署指导
|
||||||
- 提供源码、部署说明和初始化文档
|
- 基础启动排查
|
||||||
- 提供版本更新支持(可按你的实际策略改)
|
- 不包含远程代部署
|
||||||
```
|
```
|
||||||
|
|
||||||
## 标准答疑话术
|
## 常见问答
|
||||||
|
|
||||||
### 1. 这是源码还是成品?
|
### 1. 这是源码还是成品?
|
||||||
|
|
||||||
```text
|
```text
|
||||||
主推的是 Windows 可安装成品版,买家下载安装后直接用。
|
这是源码交付版,主打 Docker 部署。
|
||||||
如果需要二开,也可以额外提供源码版。
|
买家拿到源码和说明后,可以自己部署,也可以继续二开。
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 怎么交付?
|
### 2. 怎么启动?
|
||||||
|
|
||||||
```text
|
```text
|
||||||
默认发 Windows 安装包,安装后双击快捷方式即可使用。
|
按文档执行 docker compose 命令就能启动。
|
||||||
如购买源码版,再额外交付仓库代码、部署文档和初始化说明。
|
不需要安装 Windows 客户端,也不是双击安装包那种交付方式。
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. 需要联网吗?
|
### 3. 需要联网吗?
|
||||||
|
|
||||||
```text
|
```text
|
||||||
日常使用不依赖外网。Windows 安装版也是本地运行,本机保存数据,不走云端。
|
部署完成后,日常使用不依赖外部云服务。
|
||||||
|
数据保存在你自己的 Docker 环境里。
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. 支持什么系统?
|
### 4. 适合什么人买?
|
||||||
|
|
||||||
```text
|
```text
|
||||||
当前主推 Windows 本地版,也支持 Docker 版部署。
|
适合会用 Docker、会自己管理服务器或 NAS 的用户。
|
||||||
|
如果你要的是纯小白双击安装版,这个版本不适合。
|
||||||
```
|
```
|
||||||
|
|
||||||
## 截图建议
|
## 建议截图
|
||||||
|
|
||||||
建议至少准备这 6 张图:
|
保留这 6 张就够了:
|
||||||
|
|
||||||
1. 登录页商品化首页
|
1. 登录页
|
||||||
2. Moba 工作区主界面
|
2. 工作区主界面
|
||||||
3. 终端 + SFTP 分屏
|
3. 终端 + SFTP 分屏
|
||||||
4. 批量命令执行结果
|
4. 批量命令结果
|
||||||
5. 历史日志与传输记录
|
5. 历史日志与传输记录
|
||||||
6. 关于与交付信息
|
6. 关于与交付信息
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -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"
|
|
||||||
@@ -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
|
|
||||||
@@ -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"
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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 <project><version> from $resolvedPath"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Output $version.Trim()
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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"
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
Set shell = CreateObject("WScript.Shell")
|
|
||||||
scriptPath = Replace(WScript.ScriptFullName, ".vbs", ".cmd")
|
|
||||||
shell.Run Chr(34) & scriptPath & Chr(34), 0, False
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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 已停止。'
|
|
||||||
Reference in New Issue
Block a user