# Linux 服务器部署运维设计

## 1. 背景

`system-ops/deploy` 当前只提供系统部署菜单的基础入口：

- `GET /deploy/overview`
- `DeployController`
- `DeployService`
- `DeployOverviewVO`

本次需求是在 `deploy` 模块中补充 Linux 服务器远程运维能力。用户可以维护 Linux 服务器连接配置，基于 SSH/SFTP 连接服务器，完成远程文件上传、下载和基础命令终端操作。命令终端目标体验接近 Xshell 的基础能力。

当前仓库没有前端代码，本设计只定义页面布局、接口契约、后端模块拆分、数据存储和验证方式，不实现真实前端页面。

## 2. 范围确认

本期只支持 Linux 服务器。

本期包含：

- Linux SSH 连接配置的新增、编辑、删除、查询。
- SSH 连接测试。
- SFTP 文件列表、上传、下载、删除、新建目录。
- SSH Shell 基础命令交互。
- 前端单页运维工作台布局设计。
- 连接配置使用文件方式存储，不新建数据库表。

本期不包含：

- Windows 服务器。
- FTP 协议。
- 数据库存储连接配置。
- 部署任务编排。
- 命令审批、命令黑名单、命令历史。
- 批量文件压缩下载。
- 数据库专用客户端封装。
- Maven 编译、打包、测试。

说明：需求中提到的 “FPT” 本期按 Linux 服务器常用能力理解为 SFTP。SFTP 复用 SSH 账号、密码和端口，比单独 FTP 更适合本期场景。

## 3. 总体方案

推荐采用 “SSH/SFTP + WebSocket 终端” 方案：

- 服务器连接配置保存到本地 JSON 文件。
- 后端通过 SSH 建立 Linux 连接。
- 文件操作通过 SFTP 通道完成。
- 终端操作通过 SSH Shell 通道完成。
- 前端通过 WebSocket 与后端交换终端输入输出。

该方案可以复用同一份服务器连接配置，不需要引入 Windows 远程协议，也能满足类 Xshell 的基础交互需求。

## 4. 前端页面布局

页面路径建议沿用当前菜单路径：

```text
/systemOps/deploy
```

页面采用三块工作区：

- 左侧：服务器列表。
- 中间：远程文件管理。
- 右侧：连接详情和快捷操作。
- 底部：SSH 终端区。

推荐布局：

```text
┌──────────────────────────────────────────────────────────────┐
│ 顶部工具栏：新增连接  测试连接  刷新  当前连接状态              │
├──────────────┬──────────────────────────────┬────────────────┤
│ 服务器列表     │ 远程文件管理                    │ 连接详情/操作     │
│              │                              │                │
│ Linux-测试    │ 路径栏：/opt/app               │ 主机/IP          │
│ Linux-生产    │ 上传  下载  新建目录  删除  刷新 │ 用户名           │
│              │                              │ 端口             │
│              │ 文件表格                       │ 测试连接         │
│              │                              │ 打开终端         │
├──────────────┴──────────────────────────────┴────────────────┤
│ 终端 Tabs：Linux-测试                                         │
│ $ pwd                                                        │
│ /opt/app                                                     │
└──────────────────────────────────────────────────────────────┘
```

### 4.1 服务器列表

左侧服务器列表用于选择当前操作目标。

展示字段：

| 字段 | 说明 |
|---|---|
| 名称 | 服务器显示名称 |
| 主机地址 | IP 或域名 |
| SSH 端口 | 默认 22 |
| 连接状态 | 未测试、连接成功、连接失败 |

交互：

- 支持按名称、主机地址搜索。
- 点击服务器后加载连接详情，并将文件管理区切换到该服务器。
- 列表项提供编辑、删除、测试连接入口。
- 删除连接前必须二次确认。

### 4.2 连接配置弹窗

新增和编辑使用同一个弹窗。

字段：

| 字段 | 是否必填 | 说明 |
|---|---|---|
| 名称 | 是 | 页面展示名称 |
| 主机地址 | 是 | Linux 服务器 IP 或域名 |
| SSH 端口 | 是 | 默认 22，范围 1-65535 |
| 用户名 | 是 | SSH 登录用户 |
| 密码 | 新增必填 | 编辑时留空表示不修改 |
| 备注 | 否 | 环境说明 |

按钮：

- 测试连接。
- 保存。
- 取消。

密码规则：

- 新增连接时密码必填。
- 编辑连接时密码不回显。
- 编辑时密码为空表示沿用原密码。
- 查询列表和详情接口均不返回密码。

### 4.3 远程文件管理

中间文件管理区基于当前选中的服务器工作。

顶部路径栏：

- 展示当前远程目录，例如 `/opt/app`。
- 支持返回上级目录。
- 支持点击面包屑跳转到上级路径。

工具栏：

- 上传。
- 下载。
- 新建目录。
- 删除。
- 刷新。

文件表格字段：

| 字段 | 说明 |
|---|---|
| 名称 | 文件或目录名称 |
| 类型 | 文件、目录、软链接 |
| 大小 | 文件大小，目录可为空 |
| 权限 | Linux 权限字符串 |
| 修改时间 | 远程文件修改时间 |

交互规则：

- 双击目录进入下级目录。
- 下载只支持普通文件。
- 删除文件或目录前必须二次确认。
- 本期支持单文件上传和单文件下载。
- 上传目标目录为当前路径。
- 下载目录、批量压缩下载不在本期范围。

### 4.4 SSH 终端区

底部终端区用于执行 Linux 命令。

交互规则：

- 点击“打开终端”后创建 SSH Shell 会话。
- 前端输入通过 WebSocket 实时发送给后端。
- 后端将 Shell 输出通过 WebSocket 实时推送给前端。
- 本期建议限制为每台服务器最多一个终端会话。
- 关闭终端 Tab 时通知后端释放 SSH 会话。
- 终端断开后显示状态，不自动重连。

用户可以在终端中自行执行数据库命令，例如：

```bash
mysql -uroot -p
psql -h 127.0.0.1 -U postgres
redis-cli
```

后端不解析数据库命令，也不保存命令历史。

## 5. 后端结构设计

在 `system-ops/deploy` 模块内按职责新增类，保留现有 `DeployController` 的 `/deploy/overview`。

建议结构：

```text
system-ops/deploy/src/main/java/com/njcn/gather/systemops/deploy/
├── config/
├── controller/
├── pojo/param/
├── pojo/vo/
├── pojo/dto/
├── repository/
├── service/
├── service/impl/
└── websocket/
```

职责拆分：

| 类 | 职责 |
|---|---|
| `DeployServerController` | 连接配置查询、新增、编辑、删除、测试连接 |
| `DeployFileController` | SFTP 文件列表、上传、下载、删除、新建目录 |
| `DeployTerminalWebSocketHandler` | SSH 终端 WebSocket 输入输出转发 |
| `DeployServerConfigService` | 连接配置业务校验和编排 |
| `DeployServerConfigRepository` | JSON 文件读写 |
| `DeploySftpService` | SFTP 文件操作 |
| `DeploySshTerminalService` | SSH Shell 会话创建、输入、输出、关闭 |
| `DeployCryptoService` | 密码加密和解密 |
| `DeployProperties` | deploy 配置项绑定 |

## 6. 连接配置存储

连接配置不入库，使用 JSON 文件落盘。存储目录通过配置指定。

建议配置：

```yaml
deploy:
  storage-dir: ${log.homeDir}/deploy
  terminal-idle-timeout-minutes: 30
```

`deploy.crypto-key` 不建议在默认 `application.yml` 中配置明文值。后续实现时可通过环境覆盖或外部配置提供，业务代码只读取配置，不写死密钥。

落盘文件：

```text
D:\logs\deploy\deploy-server-connections.json
```

JSON 结构：

```json
{
  "servers": [
    {
      "id": "uuid",
      "name": "测试服务器",
      "host": "192.168.1.10",
      "sshPort": 22,
      "username": "root",
      "password": "加密密文",
      "description": "测试环境",
      "createdTime": "2026-05-21 14:00:00",
      "updatedTime": "2026-05-21 14:00:00"
    }
  ]
}
```

写文件规则：

- 启动时如果文件不存在，自动创建空配置文件。
- 读写方法集中在 `DeployServerConfigRepository`。
- 写入时先写临时文件，再替换正式文件，避免进程中断导致 JSON 损坏。
- 保存和删除操作需要加进程内锁，避免并发写入互相覆盖。

密码规则：

- 密码必须加密后落盘。
- 接口返回不包含密码。
- 日志不打印密码。
- 优先复用项目已有加密能力；如没有合适工具，则在 `deploy` 内封装 AES 加解密组件。
- 加密密钥通过配置提供，不在业务代码中硬编码。

## 7. 接口设计

接口风格沿用当前仓库常见写法：查询和变更优先使用 `POST`，返回 `HttpResult<T>`。

### 7.1 连接配置接口

| 方法 | 路径 | 说明 |
|---|---|---|
| `POST` | `/deploy/server/list` | 查询服务器连接配置列表 |
| `POST` | `/deploy/server/add` | 新增服务器连接配置 |
| `POST` | `/deploy/server/update` | 修改服务器连接配置 |
| `POST` | `/deploy/server/delete` | 删除服务器连接配置 |
| `POST` | `/deploy/server/test` | 测试 SSH 连接 |

列表返回字段：

| 字段 | 说明 |
|---|---|
| `id` | 连接 ID |
| `name` | 服务器名称 |
| `host` | 主机地址 |
| `sshPort` | SSH 端口 |
| `username` | 用户名 |
| `description` | 备注 |
| `createdTime` | 创建时间 |
| `updatedTime` | 更新时间 |

新增参数：

| 字段 | 是否必填 |
|---|---|
| `name` | 是 |
| `host` | 是 |
| `sshPort` | 是 |
| `username` | 是 |
| `password` | 是 |
| `description` | 否 |

编辑参数：

| 字段 | 是否必填 | 说明 |
|---|---|---|
| `id` | 是 | 连接 ID |
| `name` | 是 | 服务器名称 |
| `host` | 是 | 主机地址 |
| `sshPort` | 是 | SSH 端口 |
| `username` | 是 | 用户名 |
| `password` | 否 | 为空表示不修改 |
| `description` | 否 | 备注 |

### 7.2 文件接口

| 方法 | 路径 | 说明 |
|---|---|---|
| `POST` | `/deploy/file/list` | 查询远程目录文件列表 |
| `POST` | `/deploy/file/mkdir` | 新建远程目录 |
| `POST` | `/deploy/file/delete` | 删除远程文件或目录 |
| `POST` | `/deploy/file/upload` | 上传本地文件到远程目录 |
| `POST` | `/deploy/file/download` | 下载远程普通文件 |

文件列表参数：

| 字段 | 是否必填 | 说明 |
|---|---|---|
| `serverId` | 是 | 服务器连接 ID |
| `path` | 是 | 远程目录路径 |

文件列表返回字段：

| 字段 | 说明 |
|---|---|
| `name` | 文件名 |
| `path` | 完整路径 |
| `type` | `FILE`、`DIRECTORY`、`LINK` |
| `size` | 文件大小 |
| `permissions` | 权限字符串 |
| `modifiedTime` | 修改时间 |

下载接口直接写入 `HttpServletResponse`。下载文件名沿用远程文件名，不追加日期；仓库“导出或生成文件追加日期”的规则适用于后端生成或导出文件，本功能是下载远程已有文件，不改变原文件名。

### 7.3 终端 WebSocket

终端连接：

```text
WebSocket /deploy/terminal?serverId={serverId}
```

前端发送输入：

```json
{
  "type": "input",
  "data": "ls -la\n"
}
```

前端发送窗口大小：

```json
{
  "type": "resize",
  "cols": 120,
  "rows": 30
}
```

后端输出：

```json
{
  "type": "output",
  "data": "total 20\r\n..."
}
```

后端状态：

```json
{
  "type": "status",
  "status": "CONNECTED"
}
```

异常消息：

```json
{
  "type": "error",
  "message": "SSH连接失败"
}
```

## 8. 参数校验

后端至少补充以下校验：

- 服务器名称不能为空。
- 主机地址不能为空。
- SSH 端口范围为 `1-65535`。
- 用户名不能为空。
- 新增连接时密码不能为空。
- 编辑连接时 `id` 必须存在。
- 删除连接时 `id` 必须存在。
- 同一主机、端口、用户名组合不建议重复保存。
- 文件路径不能为空。
- 文件上传目标必须是远程目录。
- 下载目标必须是远程普通文件。
- 删除路径不能为空，不能删除空路径或根目录 `/`。
- 新建目录名称不能为空，不能包含路径分隔符。

## 9. 安全与资源控制

安全规则：

- 密码不明文落盘。
- 接口返回不包含密码。
- 日志不打印密码、终端输入内容、文件内容。
- 终端不保存命令历史。
- 文件路径需要做基础规范化，避免空路径、非法路径和目录穿越。
- 下载只允许下载普通文件。

资源规则：

- SSH 连接测试设置连接超时，例如 5 秒。
- SFTP 操作每次请求创建短连接，操作完成后释放。
- 终端会话保持长连接，关闭 WebSocket 后释放 SSH Session 和 Channel。
- 终端会话设置空闲超时，默认 30 分钟。
- 本期每台服务器最多保留一个终端会话。

## 10. 依赖建议

后续实现 SSH/SFTP 时建议优先选择 Java 8 可用、项目易接入的 SSH 客户端库，例如 JSch 或 sshj。

选择标准：

- 支持 SSH 密码登录。
- 支持 SFTP 文件操作。
- 支持 Shell Channel。
- 能在 Spring Boot 2.3 和 Java 8 下稳定使用。

最终依赖需要写入 `system-ops/deploy/pom.xml`，不影响其他模块。

## 11. 错误处理

连接测试需要区分常见错误：

| 场景 | 返回说明 |
|---|---|
| 主机不可达 | 连接服务器失败 |
| 端口不通 | SSH端口连接失败 |
| 账号或密码错误 | SSH认证失败 |
| SFTP 打开失败 | 文件通道打开失败 |
| 终端打开失败 | Shell通道打开失败 |

接口层仍使用项目现有 `HttpResult` 和 `CommonResponseEnum` 风格。具体错误文案由 Service 返回给 Controller，不新增全局异常体系。

## 12. 验证方式

默认不执行 Maven 编译、打包、测试命令。后续实现完成后按以下方式验证：

- 检查 `deploy` 新增代码只位于 `system-ops/deploy`。
- 新增连接后，接口返回和 JSON 文件内容一致。
- 编辑连接时密码留空不会覆盖原密码。
- 删除连接后，JSON 文件同步移除对应记录。
- 查询接口不返回密码。
- JSON 文件中密码不是明文。
- 测试连接能识别成功、主机不可达、端口不通、账号密码错误。
- 文件列表能展示远程目录内容。
- 上传文件后远程目录可见。
- 下载普通文件内容与远程文件一致。
- 删除文件或目录后远程路径不存在。
- 新建目录后远程路径存在。
- 终端能打开 Linux Shell，执行 `pwd`、`ls -la`、`mysql --version` 等基础命令。
- 关闭终端后，后端 SSH 会话被释放。

## 13. 后续扩展

后续如需求增加，可以在当前方案基础上扩展：

- SSH 私钥登录。
- 多终端 Tab。
- 命令审计和历史记录。
- 命令黑名单或审批。
- 部署脚本编排。
- 文件批量上传和批量下载。
- Windows WinRM 或 PowerShell Remoting。

这些能力不进入本期实现，避免当前 `deploy` 模块从基础入口一次扩张为完整运维平台。
