# 告警推送策略（产品决策）

**状态**: ✅ 已实现（2026-05-22）

## 通道分工

| 通道 | 职责 | 实现 |
|------|------|------|
| **Telegram** | 主动通知（告警/恢复/系统） | `send_telegram_*`；同机同指标 **5 分钟** 防抖 |
| **浏览器 Dashboard** | 仅展示汇总状态 | 统计卡「告警中」+「运行状态」文案；**无声音、无实时告警列表** |
| **WebSocket `/ws/alerts`** | 触发仪表盘刷新 | 收到事件后 `loadDashboard()`；不弹窗、不响铃 |

## 第 1 项（已定）

- 浏览器：**不需要声音**，不需要逐条告警横幅。
- 通知：**以 Telegram 为准**。
- 浏览器：只看 **在线/离线/告警台数** 等状态（`/servers/stats` + Redis `alerts:*` 计数）。

## 第 2 项（已定）

WS 收到消息后 **只** `loadDashboard()`，**不** `loadSyncs()`。「最近同步」靠 30s 定时器 + 手动刷新。

## 第 3 项（已定）

- 告警 WS 继续 `?token=access_token`（**不做换票**）。
- 缓解：**强制 HTTPS** + Nginx 对 `/ws/` **单独 access_log，不记录 query**（见 `deploy/nginx_https.conf`）。

## 实现清单

| 文件 | 改动 |
|------|------|
| `web/app/index.html` | 运行状态面板；WS 只 `loadDashboard()`；移除声音/告警列表 |
| `server/api/websocket.py` | 注释：浏览器仅刷新 stats，Telegram 防抖 |
| `deploy/nginx_https.conf` | `/ws/` 专用 log_format（无 `$args`） |

### 图 5 — 改后运行时（定稿）

```mermaid
flowchart TB
  subgraph notify [通知]
    TG[Telegram 手机]
  end

  subgraph dash [浏览器仪表盘]
    NUM[告警中 / 在线 / 离线]
    SYNC[最近同步 — 30s 或手动刷新]
    WS["/ws/alerts 长连"]
  end

  HB[子机心跳] --> API[中心检测]
  API -->|超阈值| TG
  API -->|broadcast| WS
  WS -->|仅 loadDashboard| NUM
  TIMER[每 30s] --> NUM
  TIMER --> SYNC
```

## 架构图

### 图 1 — 仪表盘上的两块数据（互不替代）

```mermaid
flowchart TB
  subgraph browser [浏览器 仪表盘 index.html]
    UI_TOP[上方：统计卡 + 运行状态]
    UI_BOT[下方：最近同步 8 条]
  end

  API_STATS["GET /api/servers/stats"]
  API_LOGS["GET /api/servers/logs?limit=8"]

  UI_TOP -->|"loadDashboard() ①"| API_STATS
  UI_BOT -->|"loadSyncs() ②"| API_LOGS

  API_STATS --> REDIS_H[(Redis heartbeat:* + alerts:*)]
  API_LOGS --> MYSQL[(MySQL sync_logs)]
```

### 图 2 — 告警从子机到 Telegram + 浏览器

```mermaid
sequenceDiagram
  participant Agent as 子机 Agent
  participant API as 中心 POST /api/agent/heartbeat
  participant Redis as Redis
  participant TG as Telegram
  participant WS as WebSocket 服务
  participant Dash as 浏览器仪表盘

  Agent->>API: 心跳 system_info
  API->>Redis: 写 heartbeat:{id}
  alt CPU/内存/磁盘 超阈值
    API->>TG: 发消息（5分钟同机同指标防抖）
    API->>WS: broadcast alert JSON
    WS->>Dash: 推送 type=alert
    Dash->>Dash: 仅 loadDashboard() ①
    Note over Dash: 更新「告警中」数字
  end
```

### 图 3 — 浏览器三种「刷新」方式

```mermaid
flowchart LR
  subgraph triggers [谁在触发刷新]
    T30[每 30 秒定时器]
    BTN[点「刷新」按钮]
    WS[收到 /ws/alerts 消息]
  end

  subgraph actions [执行哪些请求]
    D[loadDashboard ①]
    S[loadSyncs ②]
  end

  T30 --> D
  T30 --> S
  BTN --> D
  BTN --> S
  WS --> D

  style WS fill:#1e3a5f
  style D fill:#1e3a5f
```

说明：**只有** 30 秒定时器和「刷新」会同时做 ①+②；**告警 WS 只做 ①**。

### 图 4 — 改前 vs 改后（第 1 项）

```mermaid
flowchart TB
  subgraph before [改前：收到一条 WS]
    B1[loadDashboard ①]
    B2[loadSyncs ②]
    B3[弹告警列表 + 声音 + 标题闪]
    WS1[WS 消息] --> B1
    WS1 --> B2
    WS1 --> B3
  end

  subgraph after [改后：收到一条 WS]
    A1[loadDashboard ①]
    WS2[WS 消息] --> A1
    Note2[Telegram 负责通知]
    Note3[页面只显示状态数字]
  end
```

---

## 第 5 项（已定）

| 角色 | 职责 |
|------|------|
| **中心** `server/api/agent.py` | **唯一**判定告警/恢复/Telegram/WS；阈值来自 `settings`（可 MySQL 覆盖） |
| **子机** `web/agent/agent.py` | 不判告警、不推 Telegram；仅用 **相同 `>` 阈值** 决定「是否加急发心跳」（避免指标略高但变化&lt;10% 时不上报） |
| **子机配置** | `config.json` → `alert_thresholds`（默认 80/80/80，需与中心 settings 手动一致） |

已移除：子机 10 次连续采样、disk 90% 硬编码、无用的 `payload.alert` 字段。

---

## 后续（可选）

- 中心改阈值后，子机 `alert_thresholds` 需运维同步（暂无自动下发）
