206 lines
5.8 KiB
Markdown
206 lines
5.8 KiB
Markdown
|
|
# 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` 全通过
|