1010 lines
34 KiB
Markdown
1010 lines
34 KiB
Markdown
# Admin 多站点环境状态总览总控面板设计
|
||
|
||
## 背景
|
||
|
||
Admin 当前 `/dashboard/analytics` 仍是 Vben 示例分析页,展示用户量、访问量、下载量和图表等静态数据。它不能回答线上排障最常见的问题:
|
||
|
||
- 当前 API/Admin/QQBot/NapCat/插件平台是否真实可用。
|
||
- Jenkins 构建、K8s 发布和线上 Pod 是否对齐到目标提交。
|
||
- NAS、本地开发、腾讯云和 r4se 这些不同站点之间的 WireGuard、Caddy、OpenClash/Mihomo 等基础链路是否可观测。
|
||
- 哪些节点是健康、降级、隔离、未配置、无权限、只读可查或高风险禁用。
|
||
|
||
用户已确认采用 **A:Site Command Center** 作为页面布局与架构主线:左侧站点导航,中心环境拓扑,右侧证据/动作抽屉,底部事件流。目标不是“做一个漂亮图表页”,而是把多环境状态、证据来源、低风险只读动作和高风险边界集中到一个工作台。
|
||
|
||
本设计替换前序“单环境总览 + 安全总控”口径,保留 Jenkins/K8s 只读观测结论,并扩展到腾讯云和 r4se 远程环境。
|
||
|
||
## 官方依据
|
||
|
||
- Ant Design 设计规范提供企业级产品原型、布局、数据展示和组件使用原则;布局采用 8px grid,卡片数据展示需要控制信息密度和行数。
|
||
- Ant Design 提供企业级中后台设计规范;组件实现以当前 Admin 实际技术栈 `antdv-next` 为准。
|
||
- `antdv-next` 提供 Card、Statistic、Tag、Button、Tooltip、Drawer、Descriptions、Table 等中后台基础组件。
|
||
- Vben Admin 定位为 Vue3/Vite/TypeScript 的中后台工程方案,当前 Admin 已采用 Vben + `antdv-next`。
|
||
- Jenkins Remote Access API 支持只读获取 job/build 信息。
|
||
- Kubernetes API 支持读取 Deployment、Pod、Event 等资源,RBAC 可限制到 `get/list/watch`。
|
||
- Tencent Cloud CVM/Cloud Monitor API 可作为腾讯云主机状态和监控指标来源。
|
||
- Caddy Admin API 可读取当前配置和状态,但属于高权限配置 API,必须限制在内网/WireGuard 与只读路径。
|
||
- WireGuard 没有通用远程 HTTP 观测 API,只能通过本机 `wg`、隧道连通性或未来站点探针获得证据。
|
||
- OpenClash 本身不是稳定观测 API,底层 Mihomo/Clash external controller 可作为 r4se 只读观测来源。
|
||
|
||
参考链接:
|
||
|
||
- https://ant.design/docs/spec/introduce/
|
||
- https://ant.design/docs/spec/layout/
|
||
- https://ant.design/docs/spec/data-display/
|
||
- https://ant.design/components/overview/
|
||
- https://doc.vben.pro/en/
|
||
- https://www.jenkins.io/doc/book/using/remote-access-api/
|
||
- https://kubernetes.io/docs/concepts/overview/kubernetes-api/
|
||
- https://kubernetes.io/docs/reference/access-authn-authz/rbac/
|
||
- https://www.tencentcloud.com/document/product/213/5165
|
||
- https://caddyserver.com/docs/api
|
||
- https://www.wireguard.com/quickstart/
|
||
- https://wiki.metacubex.one/en/api/
|
||
|
||
## 已确认决策
|
||
|
||
1. `/dashboard/analytics` 改造成“环境总览”工作台,不保留 Vben 示例静态图表。
|
||
2. 页面采用 A:Site Command Center。
|
||
3. 页面第一屏必须同时看到:
|
||
- 全局状态条。
|
||
- 左侧站点导航。
|
||
- 中心环境拓扑。
|
||
- 右侧证据与安全动作。
|
||
- 底部最近事件。
|
||
4. 站点模型采用 `Site -> Node/Host -> Service -> Signal`,不再只围绕 NAS 单环境建模。
|
||
5. 第一版站点包含:
|
||
- `nas-prod`:NAS/K8s/Jenkins/API/Admin/MySQL/Redis/Loki/MinIO/WordPress/QQBot/NapCat/插件平台。
|
||
- `local-dev`:本地 API/Admin/dev proxy/本地 MySQL 或开发依赖。
|
||
- `tencent-cloud`:腾讯云 CVM、WireGuard、Caddy。
|
||
- `r4se`:WireGuard、OpenClash/Mihomo。
|
||
6. API 统一聚合,Admin 不直接访问 Jenkins、K8s、腾讯云、Caddy、Mihomo 或远程主机。
|
||
7. 第一版只允许低风险动作:刷新、只读自检、打开日志、跳转已有页面、打开外部只读入口。
|
||
8. 重启 Pod、触发 Jenkins 部署、DB 写入、重建 NapCat 容器、修改 Caddy/OpenClash、启停插件或定时任务等高风险动作,第一版必须显示为 disabled action,并写明原因。
|
||
9. Jenkins/K8s 必须接入只读观测;缺配置、403、超时或网络失败时显示失败证据,不能伪造成健康。
|
||
10. 腾讯云/r4se 第一版以只读观测为目标;无法直接观测的 WireGuard 全局 peer 状态必须明确显示证据缺口,不抓取私有 WebUI。
|
||
11. Figma 官方写入不作为本轮主链路;页面布局通过本地 visual companion 和中文 spec 收敛,避免 Figma 官方限流。
|
||
12. 事件层采用 HTTP 快照 + 后端 MQTT 收口 + API SSE 推送的混合方案:`GET /system/environment/dashboard` 和 `POST /system/environment/self-check` 继续作为状态快照与兜底;MQTT 只负责后端 topic 事件订阅、recent event materialize 和 cache invalidation;`GET /system/environment/events/stream` 负责把已经脱敏和聚合后的事件推给 Admin。Admin 前端不直连 MQTT,也不跑轮询或定时刷新。
|
||
|
||
## 目标
|
||
|
||
- 将 `/dashboard/analytics` 改为多站点环境状态工作台。
|
||
- 提供统一 API 合同,输出站点、摘要卡、拓扑节点、拓扑边、动作目录和事件流。
|
||
- 每个节点必须带证据:来源、检查时间、实时/缓存/推导状态、失败原因、是否脱敏。
|
||
- 让 Jenkins/K8s、腾讯云、r4se 不再是“未接入健康假象”,而是明确展示只读证据或缺口。
|
||
- 引入平台级 MQTT 事件入口,统一收口 QQBot/NapCat、Plugin Task、Jenkins/K8s、腾讯云、Caddy、WireGuard、Mihomo/OpenClash 等运行态事件。
|
||
- 提供 API SSE 实时出口,让 Admin 页面在无轮询、无定时刷新条件下接收状态变化和事件流增量。
|
||
- 后续可扩展远程站点探针,但第一版不引入写权限和高风险远程控制。
|
||
|
||
## 非目标
|
||
|
||
- 不在第一版执行部署、重启、数据库写入、插件启停、NapCat 容器重建、Caddy/OpenClash 修改。
|
||
- 不把 API Pod 暴露给 Docker socket、fnOS 私有 WebUI 或远程主机 root 权限。
|
||
- 不读取 K8s Secret、Jenkins console 全量日志、QQBot 大字段、原始 env、token、kubeconfig、SSH key。
|
||
- 不做营销式 hero、装饰图表、大面积单色主题或静态展示页。
|
||
- 不新增长期后台巡检表或历史趋势表,除非实施计划后续单独确认。
|
||
- 不让 MQTT 成为唯一状态真相源;broker 断连、retained message 过期或远程 agent 未接入时,dashboard 必须回退到 HTTP 快照/只读自检和明确缺口证据。
|
||
- 不让 Admin 浏览器直接连接 MQTT broker,不向前端暴露 broker 用户名、密码、token 或内部 topic。
|
||
- 不在 Admin 页面使用轮询、定时刷新或后台 `setInterval` 拉取 dashboard;SSE 断线后的浏览器自动重连和服务端连接保活不属于状态刷新轮询。
|
||
|
||
## 页面布局
|
||
|
||
### 桌面端第一屏
|
||
|
||
```text
|
||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||
│ 全局状态条:overall status / checkedAt / degraded / isolated / unknown / 刷新 │
|
||
├──────────┬───────────────────────────────────────────────┬───────────────────┤
|
||
│ 站点导航 │ 环境拓扑主视图 │ 证据 + 安全动作 │
|
||
│ NAS │ 入口 -> 网关 -> Admin/API -> 数据/观测/业务 │ 当前选中节点 │
|
||
│ 本地 │ Jenkins -> K8s -> API Pod │ metrics/evidence │
|
||
│ 腾讯云 │ Tencent CVM -> WireGuard -> Caddy │ enabled/disabled │
|
||
│ r4se │ r4se WireGuard -> OpenClash/Mihomo │ actions │
|
||
├──────────┴───────────────────────────────────────────────┴───────────────────┤
|
||
│ 最近事件流:按严重度、站点、来源、时间排序 │
|
||
└──────────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 移动端/窄屏
|
||
|
||
- 全局状态条固定在顶部。
|
||
- 站点导航变为横向 segmented control。
|
||
- 拓扑按站点纵向分组。
|
||
- 证据抽屉改为底部 Drawer。
|
||
- 事件流位于拓扑之后,保留筛选与跳转。
|
||
|
||
### 视觉原则
|
||
|
||
- 工具型中后台,不做 hero。
|
||
- 8px spacing grid,固定节点尺寸,避免状态文案导致布局跳动。
|
||
- 使用 `antdv-next` 组件:Card、Statistic、Tag、Button、Tooltip、Drawer、Descriptions、Table、Timeline、Alert。
|
||
- 状态色克制一致:
|
||
- `ok`:绿色。
|
||
- `degraded`:橙色。
|
||
- `down` / `blocked`:红色。
|
||
- `isolated`:紫/蓝灰。
|
||
- `unknown` / `unwired`:灰色。
|
||
- disabled 高风险动作必须可见,不隐藏。
|
||
|
||
## 信息架构
|
||
|
||
### 全局状态条
|
||
|
||
展示:
|
||
|
||
- 总体状态。
|
||
- 最近检查时间。
|
||
- 当前选择站点。
|
||
- `degraded/down/blocked/isolated/unknown/unwired` 计数。
|
||
- 刷新。
|
||
- 只读自检。
|
||
- 站点配置缺口提示。
|
||
|
||
### 左侧站点导航
|
||
|
||
每个站点展示:
|
||
|
||
- 站点名称。
|
||
- 站点状态。
|
||
- 核心服务短指标。
|
||
- 最近检查时间。
|
||
- 证据来源类型。
|
||
|
||
站点状态:
|
||
|
||
```ts
|
||
type EnvironmentSiteStatus =
|
||
| 'online'
|
||
| 'degraded'
|
||
| 'isolated'
|
||
| 'unknown';
|
||
```
|
||
|
||
语义:
|
||
|
||
- `online`:站点入口或核心服务读态成功,且关键节点没有阻断。
|
||
- `degraded`:站点可达,但部分服务异常或缺少观测。
|
||
- `isolated`:站点不可达,例如 WireGuard down、远程 Caddy/CVM 不可访问。
|
||
- `unknown`:缺配置、缺权限、超时或没有足够证据。
|
||
|
||
### 中心拓扑
|
||
|
||
拓扑必须支持两种层级:
|
||
|
||
- 全环境视图:四个站点并列展示关键链路。
|
||
- 单站点视图:展示该站点内部节点和依赖关系。
|
||
|
||
节点分组:
|
||
|
||
```ts
|
||
type EnvironmentNodeGroup =
|
||
| 'entry'
|
||
| 'network'
|
||
| 'frontend'
|
||
| 'service'
|
||
| 'data'
|
||
| 'observability'
|
||
| 'qqbot'
|
||
| 'deploy'
|
||
| 'remote';
|
||
```
|
||
|
||
边关系:
|
||
|
||
```ts
|
||
type EnvironmentEdgeRelation =
|
||
| 'routes-to'
|
||
| 'depends-on'
|
||
| 'observes'
|
||
| 'deploys'
|
||
| 'connects'
|
||
| 'tunnels-to';
|
||
```
|
||
|
||
### 右侧证据与安全动作
|
||
|
||
点击任意站点、节点或边,右侧显示:
|
||
|
||
- 状态。
|
||
- 关键指标。
|
||
- 证据列表。
|
||
- 最后检查时间。
|
||
- 来源种类。
|
||
- 链接。
|
||
- enabled 安全动作。
|
||
- disabled 高风险动作与禁用原因。
|
||
|
||
证据抽屉不是日志正文区,只展示摘要和跳转入口。
|
||
|
||
### 底部事件流
|
||
|
||
事件来源:
|
||
|
||
- 系统日志摘要。
|
||
- Jenkins build 状态。
|
||
- K8s warning event。
|
||
- QQBot/NapCat runtime event。
|
||
- Plugin runtime/task event。
|
||
- 远程站点观测失败。
|
||
|
||
事件流必须支持:
|
||
|
||
- 按站点过滤。
|
||
- 按严重度过滤。
|
||
- 跳转相关页面。
|
||
- 脱敏摘要展示。
|
||
|
||
### MQTT 事件收口
|
||
|
||
环境面板使用平台级 `EnvironmentEventBus` 收口事件。它可以运行在 `local` 或 `mqtt` 模式:`local` 用于本地测试和无 broker 环境,`mqtt` 用于线上和远程探针接入。现有 QQBot MQTT 能力只能作为事件来源之一,通过桥接映射为环境事件;环境面板不得直接依赖 QQBot 专用 topic 常量或 `QqbotBusService`。
|
||
|
||
建议 topic 前缀:
|
||
|
||
```text
|
||
kt/env/{siteKey}/{nodeKey}/{serviceKey}/signal
|
||
kt/env/{siteKey}/{nodeKey}/{serviceKey}/event
|
||
kt/env/{siteKey}/self-check/result
|
||
kt/qqbot/{selfId}/runtime/event
|
||
kt/qqbot/{selfId}/napcat/login
|
||
kt/plugin/{pluginKey}/task/{taskKey}/run
|
||
```
|
||
|
||
MQTT 语义:
|
||
|
||
- `signal` 可以使用 retained message,但 payload 必须包含 `observedAt` 和 `expiresAt`。过期 retained message 只能显示为 `unknown` 或 `unwired`,不能显示绿色健康。
|
||
- 普通运行事件不使用 retained message,只进入 recent event materializer。
|
||
- 状态信号和关键任务结果使用 QoS 1;普通日志摘要事件使用 QoS 0。
|
||
- payload 入库、日志、API 返回前三层都要脱敏。
|
||
- broker 断连是一个 `EnvironmentEvent`,同时使 dashboard cache 失效;不能因为 MQTT 断连把所有服务判定为 down。
|
||
- Admin 不直连 MQTT;前端通过 dashboard HTTP 首屏快照、self-check 主动按钮和 API SSE 长连接读取已经脱敏和聚合后的状态变化。
|
||
|
||
### API SSE 实时出口
|
||
|
||
Admin 页面打开时只做一次 `GET /system/environment/dashboard` 获取首屏快照,然后建立 `GET /system/environment/events/stream` SSE 长连接。后端收到 MQTT 或本地事件后,先经过 `EnvironmentEventBus`、`EnvironmentEventMaterializer` 和脱敏,再向 SSE 订阅者推送增量事件。页面不得设置定时刷新;只有用户点击刷新/只读自检,或后端 SSE 推送 `snapshot-required` 时,才允许再次请求 dashboard 快照。
|
||
|
||
SSE 事件类型:
|
||
|
||
- `environment-event`:追加 recent event,并按事件里的 site/node/service/signal 更新局部状态。
|
||
- `environment-signal`:更新某个 signal 的状态、证据和时间。
|
||
- `snapshot-required`:服务端认为客户端事件缺口过大或 cache generation 已失效,前端执行一次 dashboard 快照请求。
|
||
- `heartbeat`:连接保活,不触发状态刷新。
|
||
- `error`:展示连接或事件解析失败摘要,不能覆盖服务真实状态。
|
||
|
||
SSE 必须支持浏览器 `Last-Event-ID` 或等价 query 参数续接。服务端只保留有限最近事件用于断线恢复;超过 replay window 时推送 `snapshot-required`,避免客户端悄悄停留在旧状态。
|
||
|
||
## API 合同
|
||
|
||
新增受保护接口:
|
||
|
||
```text
|
||
GET /system/environment/dashboard
|
||
POST /system/environment/self-check
|
||
GET /system/environment/events/stream
|
||
```
|
||
|
||
`GET /system/environment/dashboard` 返回当前聚合状态。`POST /system/environment/self-check` 执行同一批只读检查,可以绕过 dashboard TTL,但不得触发任何写入、重启、部署、容器重建或远程配置变更。`GET /system/environment/events/stream` 是受保护 SSE 长连接,只推送脱敏后的环境事件和 snapshot 指令,不暴露 MQTT broker 或内部 topic。
|
||
|
||
建议目录:
|
||
|
||
```text
|
||
src/modules/admin/platform-config/environment-dashboard/
|
||
contract/
|
||
environment-dashboard.controller.ts
|
||
dto/
|
||
environment-dashboard.dto.ts
|
||
application/
|
||
environment-dashboard.service.ts
|
||
environment-event-stream.service.ts
|
||
environment-dashboard-site.mapper.ts
|
||
environment-dashboard-status.mapper.ts
|
||
environment-dashboard-action.catalog.ts
|
||
environment-event.materializer.ts
|
||
domain/
|
||
environment-dashboard.types.ts
|
||
infrastructure/
|
||
environment-dashboard-signal.collector.ts
|
||
event/
|
||
environment-event-bus.service.ts
|
||
environment-mqtt-topic.catalog.ts
|
||
qqbot-environment-event.bridge.ts
|
||
adapters/
|
||
jenkins-readonly.adapter.ts
|
||
kubernetes-readonly.adapter.ts
|
||
tencent-cloud-readonly.adapter.ts
|
||
caddy-readonly.adapter.ts
|
||
mihomo-readonly.adapter.ts
|
||
wireguard-reachability.adapter.ts
|
||
```
|
||
|
||
该模块属于 Admin Platform Config,因为它面向后台运维观测和安全总控,不属于 QQBot、Blog、Asset 任一业务域。
|
||
|
||
### DTO 语义
|
||
|
||
实现时应使用项目现有 class DTO 风格;下列 interface 仅定义合同语义。
|
||
|
||
```ts
|
||
type EnvironmentHealthStatus =
|
||
| 'ok'
|
||
| 'degraded'
|
||
| 'down'
|
||
| 'blocked'
|
||
| 'isolated'
|
||
| 'unknown'
|
||
| 'unwired';
|
||
|
||
interface EnvironmentDashboardDto {
|
||
checkedAt: string;
|
||
checkRunId: string;
|
||
overallStatus: EnvironmentHealthStatus;
|
||
activeSiteKey: string;
|
||
sites: EnvironmentSiteDto[];
|
||
summaryCards: EnvironmentSummaryCardDto[];
|
||
topology: EnvironmentTopologyDto;
|
||
actions: EnvironmentActionDto[];
|
||
recentEvents: EnvironmentEventDto[];
|
||
}
|
||
|
||
interface EnvironmentEventEnvelopeDto {
|
||
eventId: string;
|
||
topic: string;
|
||
siteKey: string;
|
||
nodeKey?: string;
|
||
serviceKey?: string;
|
||
signalKey?: string;
|
||
severity: EnvironmentHealthStatus;
|
||
sourceKind: 'mqtt' | 'local' | EnvironmentSignalSourceKind;
|
||
observedAt: string;
|
||
expiresAt?: string;
|
||
retained?: boolean;
|
||
summary: string;
|
||
evidence?: EnvironmentEvidenceDto[];
|
||
}
|
||
|
||
type EnvironmentStreamEventType =
|
||
| 'environment-event'
|
||
| 'environment-signal'
|
||
| 'snapshot-required'
|
||
| 'heartbeat'
|
||
| 'error';
|
||
|
||
interface EnvironmentStreamEventDto {
|
||
id: string;
|
||
type: EnvironmentStreamEventType;
|
||
data: EnvironmentEventEnvelopeDto | EnvironmentDashboardDto | EnvironmentStreamNoticeDto;
|
||
}
|
||
|
||
interface EnvironmentStreamNoticeDto {
|
||
reason: string;
|
||
summary: string;
|
||
generatedAt: string;
|
||
}
|
||
|
||
interface EnvironmentSiteDto {
|
||
key: string;
|
||
title: string;
|
||
description: string;
|
||
status: EnvironmentSiteStatus;
|
||
region?: string;
|
||
primaryEndpoint?: string;
|
||
sourceKind: EnvironmentSignalSourceKind;
|
||
metrics: EnvironmentMetricDto[];
|
||
evidence: EnvironmentEvidenceDto[];
|
||
lastCheckedAt?: string;
|
||
}
|
||
|
||
type EnvironmentSignalSourceKind =
|
||
| 'live'
|
||
| 'cached'
|
||
| 'derived'
|
||
| 'configured'
|
||
| 'external-link'
|
||
| 'unwired';
|
||
|
||
interface EnvironmentSummaryCardDto {
|
||
key: string;
|
||
siteKey?: string;
|
||
title: string;
|
||
status: EnvironmentHealthStatus;
|
||
primaryMetric: string;
|
||
secondaryMetric?: string;
|
||
source: EnvironmentSignalSourceDto;
|
||
route?: string;
|
||
}
|
||
|
||
interface EnvironmentTopologyDto {
|
||
viewMode: 'global' | 'site';
|
||
activeSiteKey?: string;
|
||
nodes: EnvironmentNodeDto[];
|
||
edges: EnvironmentEdgeDto[];
|
||
}
|
||
|
||
interface EnvironmentNodeDto {
|
||
id: string;
|
||
siteKey: string;
|
||
label: string;
|
||
group: EnvironmentNodeGroup;
|
||
status: EnvironmentHealthStatus;
|
||
sourceKind: EnvironmentSignalSourceKind;
|
||
metrics: EnvironmentMetricDto[];
|
||
evidence: EnvironmentEvidenceDto[];
|
||
links: EnvironmentNodeLinkDto[];
|
||
lastCheckedAt?: string;
|
||
}
|
||
|
||
interface EnvironmentEdgeDto {
|
||
id: string;
|
||
siteKey?: string;
|
||
source: string;
|
||
target: string;
|
||
relation: EnvironmentEdgeRelation;
|
||
status: EnvironmentHealthStatus;
|
||
evidence: EnvironmentEvidenceDto[];
|
||
}
|
||
|
||
interface EnvironmentEvidenceDto {
|
||
key: string;
|
||
title: string;
|
||
status: EnvironmentHealthStatus;
|
||
source: string;
|
||
checkedAt: string;
|
||
summary: string;
|
||
details?: Record<string, string | number | boolean | null>;
|
||
isSensitiveRedacted: boolean;
|
||
ttlMs?: number;
|
||
isStale?: boolean;
|
||
}
|
||
|
||
interface EnvironmentActionDto {
|
||
key: string;
|
||
siteKey?: string;
|
||
nodeId?: string;
|
||
label: string;
|
||
description: string;
|
||
kind: 'refresh' | 'self-check' | 'open-route' | 'open-url' | 'disabled-future';
|
||
riskLevel: 'safe' | 'medium' | 'high';
|
||
enabled: boolean;
|
||
targetRoute?: string;
|
||
targetUrl?: string;
|
||
disabledReason?: string;
|
||
requiredPermission?: string;
|
||
}
|
||
|
||
interface EnvironmentEventDto {
|
||
id: string;
|
||
siteKey?: string;
|
||
source: string;
|
||
level: 'info' | 'warn' | 'error' | 'fatal';
|
||
title: string;
|
||
message: string;
|
||
happenedAt: string;
|
||
route?: string;
|
||
}
|
||
```
|
||
|
||
## 站点模型
|
||
|
||
### `nas-prod`
|
||
|
||
节点:
|
||
|
||
- Caddy / TLS / reverse proxy。
|
||
- Admin static site。
|
||
- API Runtime。
|
||
- Runtime Config。
|
||
- MySQL。
|
||
- Redis / BullMQ。
|
||
- Loki / System Logs。
|
||
- MinIO。
|
||
- WordPress。
|
||
- QQBot Core。
|
||
- OneBot Reverse WS。
|
||
- NapCat Accounts。
|
||
- Plugin Platform。
|
||
- Plugin Scheduled Tasks。
|
||
- Jenkins。
|
||
- K8s Deployment。
|
||
- API Pod。
|
||
|
||
主要证据:
|
||
|
||
- Runtime health service。
|
||
- 系统日志 status/summary。
|
||
- QQBot dashboard/account/runtime summary。
|
||
- NapCat runtime detail。
|
||
- 插件平台与定时任务状态。
|
||
- MinIO connection check。
|
||
- WordPress auth/theme config check。
|
||
- Jenkins Remote Access API。
|
||
- Kubernetes API。
|
||
|
||
### `local-dev`
|
||
|
||
节点:
|
||
|
||
- Local Admin dev server。
|
||
- Local API dev server。
|
||
- Local proxy / API base URL。
|
||
- Local MySQL / Redis if configured。
|
||
|
||
第一版可选读取:
|
||
|
||
- 如果 API 本地运行,则通过当前 Admin 配置的 API base URL 读取自身 runtime。
|
||
- 如果本地服务不可读,显示 `unknown`,并写明缺少本地 API 或 dev server 证据。
|
||
|
||
### `tencent-cloud`
|
||
|
||
节点:
|
||
|
||
- Tencent CVM。
|
||
- Tencent Cloud Monitor。
|
||
- WireGuard endpoint。
|
||
- Caddy service。
|
||
- Public gateway / domain route。
|
||
|
||
主要证据:
|
||
|
||
- Tencent Cloud Monitor / CVM API:实例状态、CPU、网络、磁盘等指标。
|
||
- WireGuard reachability:API 所在网络到腾讯云 WireGuard endpoint 的连通性。
|
||
- Caddy Admin API:只读读取 config/status;仅允许 loopback/WireGuard 地址。
|
||
- Public domain probe:可选 HTTPS HEAD/GET 健康探测。
|
||
|
||
边界:
|
||
|
||
- 不在 Admin/API 里修改 Caddyfile。
|
||
- 不 reload Caddy。
|
||
- 不通过腾讯云 API 执行重启实例、修改安全组、改 DNS。
|
||
|
||
### `r4se`
|
||
|
||
节点:
|
||
|
||
- WireGuard endpoint。
|
||
- OpenClash service。
|
||
- Mihomo/Clash external controller。
|
||
- 关键代理组/连接状态。
|
||
|
||
主要证据:
|
||
|
||
- WireGuard reachability:站点可达性和最近隧道连通性。
|
||
- Mihomo/Clash external controller:`/version`、`/traffic`、`/connections`、`/proxies` 只读摘要。
|
||
|
||
边界:
|
||
|
||
- 不抓取 OpenClash WebUI 私有接口。
|
||
- 不切换代理组。
|
||
- 不更新订阅。
|
||
- 不重启 OpenClash/Mihomo。
|
||
- 如果 external controller 未启用或无只读访问,节点显示 `unknown`,证据写明缺口。
|
||
|
||
## 状态归一规则
|
||
|
||
节点状态:
|
||
|
||
- `ok`:信号源明确可用,关键指标正常。
|
||
- `degraded`:信号源可读,但存在局部异常,例如 Pod restart、部分账号离线、日志查询降级。
|
||
- `down`:信号源明确不可用,例如 Deployment 不可用、MinIO check failed。
|
||
- `blocked`:存在阻断业务闭环的问题,例如 runtime 核心配置缺失、QQBot worker 阻塞。
|
||
- `isolated`:远程站点或网络链路不可达,例如 WireGuard/Caddy endpoint 不通。
|
||
- `unknown`:缺配置、缺权限、超时、API 错误或证据不足。
|
||
- `unwired`:设计中存在该节点,但当前版本未接入读取能力。
|
||
|
||
总体状态按严重度聚合:
|
||
|
||
```text
|
||
blocked > down > isolated > degraded > unknown/unwired > ok
|
||
```
|
||
|
||
站点状态由站点内节点聚合:
|
||
|
||
- 有 `blocked` 或核心入口 `down`:站点 `degraded` 或 `isolated`,由网络证据决定。
|
||
- WireGuard/入口不可达:站点 `isolated`。
|
||
- 核心入口可达但部分节点失败:站点 `degraded`。
|
||
- 只有配置缺失或证据不足:站点 `unknown`。
|
||
- 所有核心节点正常:站点 `online`。
|
||
|
||
`unknown` 和 `unwired` 必须单独计数,不得合并成健康。
|
||
|
||
## 信号源映射
|
||
|
||
### Runtime/API
|
||
|
||
来源:
|
||
|
||
- `RuntimeHealthService.getRuntimeHealth()`
|
||
- `RuntimeConfigService`
|
||
|
||
要求:
|
||
|
||
- 聚合服务直接调用 service,不 HTTP 调本 API。
|
||
- 只返回 configured/missing/ok/down 摘要。
|
||
- 不暴露 env、password、token、secret。
|
||
|
||
### 系统日志/Loki
|
||
|
||
来源:
|
||
|
||
- `SystemLogService`
|
||
- `/system/logs/status`
|
||
- `/system/logs/summary`
|
||
|
||
要求:
|
||
|
||
- 只返回摘要和跳转链接。
|
||
- 不返回大段原始日志。
|
||
- 不把 dashboard 自身请求刷成噪声事件。
|
||
|
||
### QQBot / NapCat
|
||
|
||
来源:
|
||
|
||
- `QqbotDashboardService.summary()`
|
||
- `QqbotAccountService` 或账号列表 runtime append。
|
||
- `QqbotNapcatAccountRuntimeService`
|
||
|
||
要求:
|
||
|
||
- 区分 OneBot reverse WS、容器/WebUI、QQ 登录态。
|
||
- OneBot 心跳不能被当作 QQ 登录成功。
|
||
- 不返回 `replyText`、base64 图片或长日志。
|
||
- NapCat/account runtime 使用短超时和并发限制。
|
||
|
||
### 插件平台与定时任务
|
||
|
||
来源:
|
||
|
||
- Plugin Platform installations/capabilities/operations/runtime events。
|
||
- Plugin Task service。
|
||
|
||
要求:
|
||
|
||
- 展示已安装、启用、operation 数、任务数、最近失败数。
|
||
- 第一版不执行插件 enable/disable、task run、cron 修改。
|
||
- 动作只跳转插件平台或插件任务页。
|
||
|
||
### MinIO / WordPress
|
||
|
||
来源:
|
||
|
||
- `MinioClientService.checkConnection(bucketName?)`
|
||
- `WordpressService.checkAuth()`
|
||
- WordPress/theme config read service。
|
||
|
||
要求:
|
||
|
||
- 不创建 bucket。
|
||
- 不执行 WordPress import/sync/write。
|
||
- 只显示连接、认证、主题配置读取摘要。
|
||
|
||
### Jenkins
|
||
|
||
来源:
|
||
|
||
- Jenkins Remote Access API `GET .../api/json`。
|
||
|
||
读取字段:
|
||
|
||
- jobPath。
|
||
- buildNumber。
|
||
- result。
|
||
- building。
|
||
- branch。
|
||
- commitSha。
|
||
- commitMessageSummary。
|
||
- startedAt。
|
||
- durationMs。
|
||
- buildUrl。
|
||
|
||
禁止:
|
||
|
||
- `/build`
|
||
- `/buildWithParameters`
|
||
- replay。
|
||
- configure。
|
||
- credential。
|
||
- console full log。
|
||
|
||
失败语义:
|
||
|
||
- token 缺失:`unknown`,evidence `jenkins-readonly-auth-missing`。
|
||
- 403:`unknown`,evidence `jenkins-readonly-forbidden`。
|
||
- 最近 build failed:若当前线上镜像不受影响则 `degraded`,若线上目标部署失败则 `down`。
|
||
|
||
### Kubernetes / K3s
|
||
|
||
来源:
|
||
|
||
- Kubernetes API。
|
||
|
||
读取资源:
|
||
|
||
- Deployment `get` / `status`。
|
||
- Pod `list` with labelSelector。
|
||
- ReplicaSet 可选。
|
||
- Event `list`。
|
||
- Metrics API 可选。
|
||
|
||
读取字段:
|
||
|
||
- namespace。
|
||
- deployment name。
|
||
- generation / observedGeneration。
|
||
- replicas / updatedReplicas / readyReplicas / availableReplicas。
|
||
- image。
|
||
- podName。
|
||
- phase。
|
||
- ready。
|
||
- restartCount。
|
||
- startedAt。
|
||
- warning event reason/message 摘要。
|
||
|
||
RBAC:
|
||
|
||
- 只允许 `get/list/watch`。
|
||
- 不允许 `create/update/patch/delete`。
|
||
- 不允许 `pods/exec`。
|
||
- 第一版不读取 `pods/log`,除非后续单独设计脱敏日志入口。
|
||
- 不允许读取 Secret。
|
||
|
||
失败语义:
|
||
|
||
- RBAC 403:`unknown`,evidence `k8s-readonly-rbac-forbidden`。
|
||
- Deployment 未就绪:`down`。
|
||
- Pod restartCount 增长或 warning event:`degraded`。
|
||
|
||
### Tencent Cloud
|
||
|
||
来源:
|
||
|
||
- Tencent Cloud CVM / Cloud Monitor read-only API。
|
||
|
||
读取字段:
|
||
|
||
- instanceId / instanceName。
|
||
- instanceState。
|
||
- region / zone。
|
||
- CPU、内存、磁盘、网络指标摘要。
|
||
- 公网/内网 IP 脱敏展示。
|
||
|
||
禁止:
|
||
|
||
- 重启实例。
|
||
- 修改安全组。
|
||
- 修改公网 IP、EIP、DNS。
|
||
- 修改云产品配置。
|
||
|
||
失败语义:
|
||
|
||
- credential missing:`unknown`。
|
||
- API forbidden:`unknown`。
|
||
- CVM stopped/unreachable:`down` 或站点 `isolated`。
|
||
|
||
### Caddy
|
||
|
||
来源:
|
||
|
||
- Caddy Admin API read-only paths。
|
||
- HTTPS public route probe。
|
||
|
||
要求:
|
||
|
||
- Caddy Admin API 只允许 loopback 或 WireGuard 私网地址。
|
||
- 不调用 config write、load、reload。
|
||
- 只显示当前配置摘要、route 可达性和证书/TLS 证据。
|
||
|
||
### WireGuard
|
||
|
||
来源:
|
||
|
||
- API 所在节点的 WireGuard 本地状态。
|
||
- 私网 endpoint reachability。
|
||
- 未来可选 site probe。
|
||
|
||
要求:
|
||
|
||
- 第一版不假装能读取远端所有 peer。
|
||
- 只展示 API 侧可证明的 tunnel reachability、handshake/endpoint 摘要或缺口。
|
||
- 如果缺少 site probe,远端 peer 细节显示 `unknown`。
|
||
|
||
### OpenClash / Mihomo
|
||
|
||
来源:
|
||
|
||
- Mihomo/Clash external controller read-only endpoints。
|
||
|
||
读取字段:
|
||
|
||
- `/version`
|
||
- `/traffic`
|
||
- `/connections`
|
||
- `/proxies` 摘要。
|
||
|
||
禁止:
|
||
|
||
- 切换代理。
|
||
- 修改配置。
|
||
- 重载配置。
|
||
- 更新订阅。
|
||
- 重启服务。
|
||
|
||
失败语义:
|
||
|
||
- external controller disabled:`unknown`。
|
||
- remote unreachable:站点 `isolated`。
|
||
- API 401/403:`unknown`。
|
||
|
||
## 安全总控 Action Catalog
|
||
|
||
第一版 enabled actions:
|
||
|
||
- `refresh-dashboard`:重新请求 dashboard。
|
||
- `run-readonly-self-check`:执行只读自检。
|
||
- `open-system-logs`:跳转系统日志页。
|
||
- `open-qqbot-dashboard`:跳转 QQBot Dashboard。
|
||
- `open-napcat-runtime`:跳转 NapCat runtime 或账号详情。
|
||
- `open-plugin-platform`:跳转插件平台页。
|
||
- `open-plugin-tasks`:跳转插件任务页。
|
||
- `open-asset-page`:跳转资产/MinIO 页面。
|
||
- `open-wordpress-page`:跳转 Blog/WordPress 管理页。
|
||
- `open-jenkins`:打开 Jenkins 只读入口。
|
||
- `open-k8s-dashboard`:打开 K8s Dashboard 只读入口。
|
||
- `open-tencent-cloud-console`:打开腾讯云控制台链接。
|
||
- `open-caddy-status`:打开 Caddy 只读状态链接。
|
||
- `open-mihomo-dashboard`:打开 r4se Mihomo/OpenClash 只读入口,如果已配置。
|
||
|
||
第一版 disabled actions:
|
||
|
||
- `restart-api-pod`
|
||
- `trigger-jenkins-deploy`
|
||
- `run-db-migration`
|
||
- `recreate-napcat-container`
|
||
- `plugin-enable-disable`
|
||
- `plugin-task-run-once`
|
||
- `minio-create-bucket`
|
||
- `wordpress-import`
|
||
- `reload-caddy`
|
||
- `edit-caddy-config`
|
||
- `switch-openclash-proxy`
|
||
- `restart-openclash`
|
||
- `restart-tencent-cvm`
|
||
- `modify-wireguard-peer`
|
||
|
||
每个 disabled action 必须返回:
|
||
|
||
- `riskLevel: 'high'`
|
||
- `enabled: false`
|
||
- `disabledReason`
|
||
- `requiredPermission`
|
||
- 后续安全条件,例如二次确认、审计日志、回滚路径、只读预检、最小权限凭据。
|
||
|
||
## 前端设计
|
||
|
||
改造位置:
|
||
|
||
```text
|
||
Vue/kt-template-admin/apps/web-antdv-next/src/views/dashboard/analytics/index.vue
|
||
Vue/kt-template-admin/apps/web-antdv-next/src/views/dashboard/analytics/components/
|
||
Vue/kt-template-admin/apps/web-antdv-next/src/api/system/environment.ts
|
||
```
|
||
|
||
路由:
|
||
|
||
- 保留 `/dashboard/analytics`,避免菜单和固定 tab 入口失效。
|
||
- 菜单文案改为“环境总览”或“环境状态”。
|
||
|
||
组件建议:
|
||
|
||
```text
|
||
EnvironmentDashboardPage
|
||
EnvironmentStatusBar
|
||
EnvironmentSiteRail
|
||
EnvironmentTopologyCanvas
|
||
EnvironmentEvidenceDrawer
|
||
EnvironmentActionPanel
|
||
EnvironmentEventStream
|
||
```
|
||
|
||
实现约束:
|
||
|
||
- 页面 root 必须是单一稳定元素,避免 Vben route transition 空白。
|
||
- 拓扑第一版使用 DOM + CSS grid + SVG edge overlay,不引入重量图表库。
|
||
- 节点固定宽高,长文本 ellipsis + Tooltip。
|
||
- 抽屉展示 Descriptions、metrics、evidence、links、actions。
|
||
- 事件流可用 Table 或 Timeline;如果需要排序/过滤,优先 Table。
|
||
- API 请求统一通过 Admin caller/request wrapper。
|
||
|
||
## 后端实现约束
|
||
|
||
- Controller 使用 `JwtAuthGuard`。
|
||
- 返回 `vbenSuccess(data)`。
|
||
- Aggregator 直接调用本地 service 或只读 adapter,不 HTTP 调本 API。
|
||
- 每个信号源独立 timeout、独立 `Promise.allSettled`。
|
||
- 单点失败变成节点 evidence,不让 dashboard 500。
|
||
- 只有鉴权、参数或系统级不可恢复错误才让接口失败。
|
||
- 新增或触碰函数、方法、handler、job 必须补 JSDoc,参数说明写来源和用途。
|
||
- 所有外部证据入库前、日志前、返回前都必须脱敏。
|
||
- Jenkins/K8s/Tencent/Caddy/Mihomo adapter 不得包含写路径。
|
||
|
||
## 缓存与成本
|
||
|
||
建议第一版采用短 TTL:
|
||
|
||
- Dashboard 聚合 TTL:10-30 秒。
|
||
- `self-check` 可绕过 dashboard TTL,但仍对每个信号源设置 timeout。
|
||
- QQBot/NapCat 复用现有 runtime TTL,不强制刷新所有账号。
|
||
- Jenkins/K8s/Tencent/Caddy/Mihomo 每个 adapter 独立短超时。
|
||
- 远程站点失败只降级对应站点,不拖垮整个接口。
|
||
|
||
证据必须标注:
|
||
|
||
- `checkedAt`
|
||
- `sourceKind`
|
||
- `ttlMs`
|
||
- `isStale`
|
||
|
||
## 验收标准
|
||
|
||
### 设计验收
|
||
|
||
- 用户确认本 spec 后,进入 KT workflow KT plan writing。
|
||
- 实施计划必须拆 API 合同、API 聚合、远程只读 adapter、Admin 页面、测试/线上闭环五部分。
|
||
|
||
### API 验收
|
||
|
||
- `GET /system/environment/dashboard` 本地真实请求返回 Vben 包装。
|
||
- `GET /system/environment/events/stream` 本地真实请求返回 SSE 流,能收到 heartbeat 或 synthetic event。
|
||
- 响应包含 `sites`、`summaryCards`、`topology.nodes`、`topology.edges`、`actions`、`recentEvents`。
|
||
- NAS、local-dev、Tencent Cloud、r4se 至少有节点和状态。
|
||
- Jenkins/K8s 节点由只读 adapter 提供真实 build/deployment/pod 证据或失败证据。
|
||
- 腾讯云/Caddy/WireGuard/r4se/OpenClash/Mihomo 节点有只读证据或明确缺口。
|
||
- 任一信号源失败时接口仍 200,并将对应节点标为 `unknown` / `isolated` / `degraded` / `down`。
|
||
- 不返回敏感字段或 QQBot 大字段。
|
||
|
||
### Admin 验收
|
||
|
||
- `/dashboard/analytics` 不再展示 Vben 示例静态图表。
|
||
- 第一屏展示全局状态条、站点导航、拓扑、证据/动作、事件流。
|
||
- 点击站点切换拓扑范围。
|
||
- 点击节点打开证据抽屉。
|
||
- enabled action 可刷新、只读自检或跳转。
|
||
- disabled high-risk action 可见且说明禁用原因。
|
||
- 页面切换无 Vue `non-element root node` / transition 空白问题。
|
||
- 页面不使用轮询或定时刷新 dashboard;状态变化由 SSE 增量事件驱动。
|
||
|
||
### 线上验收
|
||
|
||
- API/Admin 推送部署后,Jenkins/K8s 发布状态按现有 deploy observation 流程验证。
|
||
- 线上 Admin 页面可打开。
|
||
- 线上 dashboard 接口 200。
|
||
- 线上 events stream 可建立 SSE 连接,断线重连后能通过 `Last-Event-ID` 或 `snapshot-required` 恢复一致性。
|
||
- 线上页面展示 NAS、local-dev、Tencent Cloud、r4se 四类站点。
|
||
- Jenkins/K8s 展示真实只读观测证据:Jenkins 最近 build/commit/result,K8s Deployment ready/updated、Pod image/restartCount。
|
||
- 腾讯云/Caddy/WireGuard/r4se/OpenClash/Mihomo 展示真实只读证据或明确缺口,不显示健康假象。
|
||
- 所有高风险动作 disabled,不能触发部署、重启 Pod、修改资源或读取 Secret。
|
||
|
||
## 测试计划方向
|
||
|
||
实施计划阶段应覆盖:
|
||
|
||
- API service 单测:状态严重度聚合、site 聚合、partial failure、isolated/unknown/unwired、action catalog。
|
||
- API adapter 单测:Jenkins/K8s/Tencent/Caddy/Mihomo/WireGuard 的 200、403、timeout、unreachable、failed/degraded mapping。
|
||
- API event bus 单测:MQTT topic mapping、retained message expiry、broker disconnect event、QQBot bridge 脱耦。
|
||
- API SSE 单测:event replay、Last-Event-ID 续接、heartbeat 不触发快照、replay window 缺口推 `snapshot-required`。
|
||
- API controller contract spec:鉴权路由、Vben response shape、self-check 只读行为。
|
||
- Admin API wrapper Vitest:请求路径和响应类型。
|
||
- Admin 页面组件测试:站点导航、拓扑节点、证据抽屉、disabled actions、事件流过滤、EventSource 消费、无 dashboard 轮询。
|
||
- 本地真实接口请求:启动或复用 API 服务调用 dashboard 接口。
|
||
- 本地浏览器 smoke:打开 `/dashboard/analytics`,检查 console、首屏、抽屉和动作。
|
||
- 线上 smoke:部署后调用线上接口和页面。
|
||
|
||
## 后续扩展
|
||
|
||
后续可以在不破坏第一版合同的基础上增加:
|
||
|
||
- 远程 site probe,解决 WireGuard 远端 peer 和 OpenClash 主机本地服务观测盲区。
|
||
- deploy observation evidence 入库。
|
||
- 安全动作审计表。
|
||
- 二次确认和权限码控制的高风险动作。
|
||
- 环境变更历史趋势。
|
||
- 定时自检和告警推送。
|
||
|
||
这些扩展必须继续遵守:读态优先、动作分级、证据脱敏、失败局部降级、高风险动作有审计与回滚路径。
|