# Web 终端

> 通过浏览器直接访问服务器的 Shell 终端。

URL: https://docs.serverbee.app/zh/docs/terminal

ServerBee 内置基于 Web 的远程终端，管理员可以通过浏览器直接访问被监控服务器的 Shell，无需额外的 SSH 客户端，点击即用。

## 工作原理 [#工作原理]

Web 终端采用三段式 WebSocket 中转架构，Server 作为中间代理在浏览器和 Agent 之间转发终端数据：

```
浏览器 (xterm.js)  <--WebSocket-->  Server (代理转发)  <--WebSocket-->  Agent (PTY)
```

1. 浏览器通过 WebSocket 连接到 Server：`/api/ws/terminal/{server_id}`
2. Server 校验用户身份（Session Cookie 或 API Key）并验证管理员角色
3. Server 通过已建立的 Agent WebSocket 连接，向 Agent 下发 `TerminalOpen` 指令
4. Agent 使用 `portable-pty` 库创建伪终端（PTY）进程，并启动系统默认 Shell
5. 浏览器输入的字符经 Server 转发到 Agent 的 PTY，PTY 的输出再经 Server 原路转发回浏览器

Shell 进程完全运行在 Agent 所在机器上。终端数据通过 WebSocket 中转传输。

## 使用方法 [#使用方法]

1. 登录 ServerBee 管理面板
2. 进入服务器列表，点击目标服务器
3. 在服务器详情页点击「终端」按钮（仅在线服务器可用）
4. 连接建立后，即可在浏览器中使用完整的交互式 Shell

<Callout type="warn">
  Web 终端仅对**管理员**（Admin）角色可用。普通成员（Member）无法访问终端，尝试打开会收到 403 Forbidden 响应。
</Callout>

## 功能特性 [#功能特性]

### 完整 Shell 体验 [#完整-shell-体验]

* 支持标准 Shell（bash、zsh 等，取决于 Agent 所在系统的默认 Shell）
* 支持 Tab 补全、历史命令和快捷键
* 支持运行交互式程序，如 `vim`、`top`、`htop` 等
* 支持彩色输出和 ANSI 转义序列

### 自动调整大小 [#自动调整大小]

终端窗口大小会自动适配浏览器窗口。调整浏览器窗口时，终端的行数和列数会同步到远端 PTY，确保显示效果始终正确。

### Tokyo Night 主题 [#tokyo-night-主题]

Web 终端使用 Tokyo Night 配色方案，与 ServerBee 的暗色主题风格一致。

### 多会话支持 [#多会话支持]

同一台服务器支持同时打开多个终端会话，方便在不同标签页中执行不同任务。

## 认证与访问控制 [#认证与访问控制]

<Callout type="warn">
  Web 终端还要求 **terminal** 能力（`CAP_TERMINAL`），该能力默认关闭，且必须在 Agent 主机上开启 —— 在 Agent 的 `[capabilities]` allow 列表中加入 `terminal`（或传 `--allow-cap terminal`）并重启 Agent。能力由 Agent 拥有，Server 无法开启。能力关闭时，WebSocket 升级请求会被 403 Forbidden 拒绝。见 [功能开关](/zh/docs/capabilities)。
</Callout>

终端认证与 REST API 使用相同的机制：

* **Session Cookie**：登录后由浏览器自动携带
* **API Key**：通过 `X-API-Key` 请求头传递（适用于程序化访问）

Server 会在升级 WebSocket 连接前校验用户身份和角色。

## 会话限制 [#会话限制]

为防止资源耗尽，系统强制以下限制：

| 限制项           | 值     |
| ------------- | ----- |
| 每台服务器最大并发终端会话 | 3     |
| 空闲超时          | 10 分钟 |
| 最大消息大小        | 1 MB  |
| 访问权限          | 仅管理员  |

### 空闲超时 [#空闲超时]

无任何输入超过 10 分钟后，会话会自动关闭：浏览器收到超时错误，Agent 端的 PTY 进程被终止。任何输入（包括调整大小事件）都会重置空闲计时器。

## 终端协议 [#终端协议]

浏览器与 Server 之间交换 JSON 控制消息。

### 浏览器 → Server [#浏览器--server]

**输入**——向 Shell 发送按键：

```json
{ "type": "input", "data": "bHM=" }
```

`data` 字段为 base64 编码的输入。

**调整大小**——通知终端窗口尺寸变化：

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

### Server → 浏览器 [#server--浏览器]

**会话已建立：**

```json
{ "type": "session", "session_id": "uuid-here" }
```

**终端已启动：**

```json
{ "type": "started" }
```

**终端输出：**

```json
{ "type": "output", "data": "base64-encoded-output" }
```

**错误：**

```json
{ "type": "error", "error": "Session timed out due to inactivity" }
```

## 会话生命周期 [#会话生命周期]

1. **打开**——浏览器连接，Server 分配 `session_id` 并指令 Agent 创建 PTY
2. **活跃**——输入由浏览器流向 Agent，输出由 Agent 流回浏览器
3. **关闭**——由以下任一情况触发：
   * 用户关闭终端面板
   * 浏览器 WebSocket 断开
   * 空闲超时（10 分钟）
   * Agent 断开连接
   * Server 向 Agent 发送 `TerminalClose`

关闭时，Server 向 Agent 发送 `TerminalClose` 消息，Agent 终止 PTY 进程，会话从 Agent 管理器中注销。

## 安全说明 [#安全说明]

### PTY 运行用户 [#pty-运行用户]

PTY 进程以 Agent 运行时的系统用户身份执行。安装脚本默认将 Agent 配置为以 root 身份运行的 systemd 服务，因此终端也以 root 权限运行。

如有安全顾虑，可以用 systemd 的 `User=` 指令让 Agent 以专用的低权限用户运行：

```ini title="serverbee-agent.service"
[Service]
User=serverbee
Group=serverbee
ExecStart=/usr/local/bin/serverbee-agent
```

<Callout type="warn">
  以非 root 用户运行 Agent 时，终端操作会受到该用户权限的约束，部分系统指标采集也可能受限（如 TCP/UDP 连接数）。此外，ICMP Ping 探测需要 `CAP_NET_RAW` 权限。
</Callout>

### 审计日志 [#审计日志]

终端的连接和断开事件会写入审计日志。管理员可以在 Settings → Audit Logs 页面查看所有终端会话的访问记录，包括操作者用户名、目标服务器、连接与断开时间，以及来源 IP 地址。

### 连接加密 [#连接加密]

生产环境中务必通过 HTTPS 反向代理保护终端数据传输。未加密的 WebSocket 连接可能导致终端输入输出被窃听。

## 故障排查 [#故障排查]

### "Agent is offline" [#agent-is-offline]

终端要求目标服务器的 Agent 处于在线状态。Agent 离线时，WebSocket 升级请求会被拒绝并提示错误。

### 连接频繁断开 [#连接频繁断开]

在反向代理后部署时，请确保正确转发 WebSocket 连接，并将读写超时设为至少 86400 秒（24 小时）。nginx 配置示例见 [Server 配置](/zh/docs/server) 指南。

### 终端卡顿或延迟高 [#终端卡顿或延迟高]

终端数据经由中心 Server 中转。若 Server 与浏览器或 Agent 地理距离较远，可能出现延迟，这是中转架构的固有特性。对延迟敏感的操作建议改用直连 SSH。

<Cards>
  <Card title="Server 配置" href="/zh/docs/server" />

  <Card title="架构设计" href="/zh/docs/architecture" />

  <Card title="部署指南" href="/zh/docs/deployment" />
</Cards>
