# GHCS v0.2 终端排障 SOP（SSH Web Terminal Debug SOP）

<aside>
🎯

本页用途：当 GHCS v0.2 的 SSH Web 终端出现「页面能开但连不上 / 能连但不能输入 / 焦点丢失 / 反复修不好」时，按固定流程快速定位。

原则：**先排通路，再看交互**；先用可观测证据（HTTP 状态码 / 命令回显），不要凭“光标看起来在不在”。

</aside>

---

## 1｜本次问题的具体原因（复盘结论）

### 1.1 真正卡住不是一个 bug，而是三层叠加

1) **页面 200 ≠ 终端可用**

- `/console/terminal` 返回 200 只说明 Next 页面渲染成功。
- 终端真正依赖：`GET /ws/terminal/servers` + `WS /ws/terminal/{server_key}`。
- 只要后端路由未挂载、或前端没打到正确后端，页面仍 200，但终端必失败。

2) **环境变量未生效 / [localhost](http://localhost) 解析导致“像玄学”**

- `NEXT_PUBLIC_CONSOLE_API` 改了但未重启前端 → Next 继续用旧值。
- `localhost` 在部分环境可能走 IPv6（`::1`）而后端仅在 IPv4（`127.0.0.1`）监听 → 表现为偶发连不上 / 重试。

3) **焦点判断误区：光标渲染 ≠ 键盘输入通道**

- xterm 光标是渲染状态，点别处后仍可能停在原位。
- 真正需要判断的是：键盘输入是否进入终端（用 `echo` 回显验证）。

### 1.2 之前陷入的误区（导致一直修不动）

- **误区 A：把“页面能打开”当成“后端都通了”**
- **误区 B：用光标/感觉判断焦点**，而不是用命令回显判定输入通道
- **误区 C：改 `.env.local` 不重启前端**，导致“我明明改了但没变化”
- **误区 D：一次改太多（前端/后端/依赖/配置一起动）**，失去最小证据链

### 1.3 本次为什么修得顺利（方法）

把问题拆成 4 层，并且每层只用一个“最小证据”验收：

- 后端活着：`/docs` 能开
- 路由挂载：`curl -i /ws/terminal/servers` 看 200/404
- 前端指向正确后端：`.env.local` + 重启
- 交互与焦点：`whoami / pwd / echo ...` 回显

---

## 2｜GHCS v0.2 终端排障 SOP（五段式，不跳步）

### Step 0｜准备（两窗口）

- 终端 A：backend（FastAPI）
- 终端 B：frontend（Next.js）

---

### Step 1｜确认后端在跑

```bash
cd ~/guanghu-dev/ghcs-mono/backend
source venv/bin/activate
python -m uvicorn main:app --host 127.0.0.1 --port 5002 --reload
```

验收：浏览器打开 `http://127.0.0.1:5002/docs`

---

### Step 2｜确认 terminal_router 已挂载（最关键一锤）

```bash
curl -i http://127.0.0.1:5002/ws/terminal/servers
```

- **200 + JSON**：后端终端路由 OK
- **404**：检查 `backend/main.py` 是否包含：
    - `from terminal_router import router as terminal_router`
    - `app.include_router(terminal_router)`

---

### Step 3｜确认前端 API_BASE 指向正确，并且已重启生效

```bash
cd ~/guanghu-dev/ghcs-mono/frontend
cat -n .env.local
```

推荐：

```
NEXT_PUBLIC_CONSOLE_API=http://127.0.0.1:5002
```

**改完必须重启前端**：

```bash
npm run dev
```

---

### Step 4｜确认“页面可用”与“后端可达”不是一回事

打开：`http://localhost:3000/console/terminal`

验收点：服务器下拉是否能显示（来自 Step 2 的 servers 列表）。

---

### Step 5｜交互与焦点验收（只看回显，不看光标）

连接成功后输入：

```bash
whoami
pwd
echo FOCUS_OK
```

期望回显：用户名、路径、`FOCUS_OK`

#### 5.1 点击聚焦验收（对应历史 bug）

1) 点页面别处

2) 点回黑框

3) 输入：

```bash
echo CLICK_FOCUS_OK
```

期望回显 `CLICK_FOCUS_OK`

#### 5.2 额外：判断是否一直保持输入通道

点页面别处后，**不点回终端**，直接键盘敲：

```bash
echo SHOULD_APPEAR
```

- 若回显：说明输入通道仍在
- 若不回显：说明焦点确实丢失，需要启用“保险带补丁”

---

## 3｜常见故障 → 快速定位表

- **页面能开，但服务器下拉空**：优先回到 Step 2/3（路由未挂载或 API_BASE 未生效）
- **连接按钮报 WS error**：先跑 `curl -i /ws/terminal/servers` 判 200/404，再看浏览器 Network → WS
- **能连但不能输入**：先用 `echo` 判输入通道是否断，再考虑焦点补丁
- **输入重复**：检查是否每次 connect 都重复绑定 xterm 的 `onData`（应只绑定一次或重连前清理）

---

## 3.5｜关键数据备忘（防遗忘）

### 3.5.1 本地端口与环境变量

- 后端（console-api）：`http://127.0.0.1:5002`
- 前端（Next dev）：`http://localhost:3000`
- 前端环境变量文件：`~/guanghu-dev/ghcs-mono/frontend/.env.local`
    - `NEXT_PUBLIC_CONSOLE_API=http://127.0.0.1:5002`
    - ⊢ 改 `.env.local` 后必须重启 `npm run dev` 才会生效

### 3.5.2 登录与 Bearer token（API Keys 写操作必用）

- 登录接口：`POST /api/login`
- 返回字段：`access_token` + `token_type: bearer`
- 本地默认管理员账号（见 `backend/auth.py`）：`awen / guanghu2026`
- 前端 token 存储位置（本次约定）：`localStorage.ghcs_access_token`
- 写请求必须带 header：`Authorization: Bearer <access_token>`

### 3.5.3 API Keys 关键接口（本次已验收）

- `GET /api/keys/suppliers`（当前可不鉴权）
- `GET /api/keys/list`（当前可不鉴权）
- `POST /api/keys/add`（需鉴权）
- `PUT /api/keys/{id}`（需鉴权；启停开关）
- `DELETE /api/keys/{id}`（需鉴权）

---

## 4｜推荐的“保险带补丁”（可选，但建议保留）

若遇到“点回黑框无法找回输入”，给终端容器外层加：

```tsx
onMouseUp={() => term.current?.focus()}
```

这能把“点黑框回焦”变成确定性行为。

---

## 5｜本次会话验收回执（可复制到执行记录）

- `curl -i http://127.0.0.1:5002/ws/terminal/servers` → 200
- 终端回显：`whoami` / `pwd` / `echo FOCUS_OK` / `echo CLICK_FOCUS_OK` / `echo SHOULD_APPEAR` 全通过