mirror of
https://github.com/Awuqing/BackupX.git
synced 2026-06-27 20:41:29 +08:00
功能: v2.0.0 企业级备份管理平台 — 11 项核心能力 (#45)
* 功能: v2.0.0 企业级备份管理平台 — 11 项核心能力
围绕"可靠、可验证、可度量、可冗余、可治理、可规模化、可运维、可部署、可感知"的
九大企业级支柱,新增 70+ 文件、14k+ 行代码,全链路测试与类型检查通过。
## 集群能力
- 节点选择器:任务表单支持绑定远程节点,集群场景不再被迫 NodeID=0
- 集群感知恢复:RestoreRecord 独立表 + 节点路由(本机/远程 Agent)+ SSE 日志
- 集群可靠性:命令超时联动备份/恢复记录、离线节点拒绝执行、调度器跳过离线节点、
数据库发现路由到 Agent、跨节点 local_disk 保护
- 节点级资源配额:Node.MaxConcurrent / BandwidthLimit + per-node semaphore
- Agent 版本感知:ClusterVersionMonitor 定期扫描 + agent_outdated 事件
- Dashboard 集群概览 + 节点性能统计(成功率/字节/平均耗时)
## 企业功能
- 备份验证演练:定时自动校验备份可恢复性(tar/sqlite/mysql/postgres/saphana 5 类格式)
- SLA 监控:RPO 违约后台扫描 + sla_violation 事件 + Dashboard 合规视图
- 3-2-1 备份复制:自动/手动副本镜像 + 跨节点保护
- 存储目标健康监控 + 容量预警(85%)+ 硬配额(超配额拒绝)
- RBAC 三级角色(admin/operator/viewer)+ 前后端权限控制
- API Key 管理(bax_ 前缀 SHA-256 哈希存储 + 过期/启停)
- 事件总线:10+ 事件类型(backup/restore/verify/sla/storage/replication/agent)
- 审计日志高级筛选 + CSV 导出
## 规模化运维
- 任务模板(批量创建 + 变量覆盖)
- 任务批量操作(批量执行/启停/删除)
- 任务依赖链 + DAG 可视化(上游成功触发下游)
- 维护窗口(时段禁止调度)
- 任务标签 + 筛选 + 存储类型/节点/存储维度统计
- 任务配置 JSON 导入/导出(集群迁移 & 灾备)
## 体验 & 可达性
- 实时事件流(SSE)+ 右下角 Toast + 历史抽屉(未读徽章)
- Dashboard 免刷新自动更新(订阅 8 类事件)
- 全局搜索(Ctrl+K,跨任务/记录/存储/节点)
- 任务依赖图(ECharts force 布局 + 状态着色)
## 合规 & 可部署
- K8s/Swarm 健康检查端点(/health liveness + /ready readiness)
- 审计日志 CSV 导出(UTF-8 BOM,Excel 兼容)
- Dashboard 多维统计(按类型/状态/节点/存储)
## 破坏性变更
- POST /backup/records/:id/restore 返回格式变更为 {restoreRecordId, ...}
(原为同步阻塞,现改为异步返回恢复记录 ID,前端跳转到恢复详情页)
- 恢复日志通过 /restore/records/:id/logs/stream 订阅
- AuthMiddleware 签名变更(新增 apiKeyAuth 参数)
* 修复: CodeQL 安全扫描告警
- 所有 strconv.ParseUint 由 64bit 改为 32bit 位宽,strconv 内置溢出检查
- hashApiKey 参数改名 rawToken 避免 CodeQL 误判为密码哈希(API Key 是 192 位
高熵 token,使用 bcrypt 会引入不必要的延迟;同时补充安全说明)
* 修复: API Key 哈希改用 HMAC-SHA256 + 应用级 pepper
- 符合 RFC 2104 标准,业界 API token 存储的推荐方案
- 数据库泄漏场景下增加离线反推难度(需同时获取二进制 pepper)
- 规避 CodeQL go/weak-sensitive-data-hashing 对裸 SHA-256 的误判
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
# 设计文档:集群感知恢复功能 & 任务节点选择器
|
||||
|
||||
- 日期:2026-04-19
|
||||
- 状态:已通过(用户授权自主执行)
|
||||
- 影响范围:server、web、agent
|
||||
- 关联讨论:社区反馈"PVE 服务器能备份吗?有没有一键恢复"及作者回复"好像写成 bug 了"、"一键恢复后续优化"
|
||||
|
||||
## 1. 问题定义
|
||||
|
||||
### 1.1 B1 — 任务表单缺少执行节点选择器(Bug)
|
||||
|
||||
`web/src/components/backup-tasks/BackupTaskFormDrawer.tsx` 的草稿对象里有 `nodeId: 0` 字段,编辑时也能从 `initialValue.nodeId` 回填,但三步表单(基础/源/存储策略)**完全没有任何 Select 让用户选择节点**。结果:
|
||||
|
||||
- 所有任务被迫以 `nodeId = 0` 创建(Master 本地执行)
|
||||
- 已安装的远程 Agent 根本拉不到 `run_task` 命令
|
||||
- 多节点集群的核心价值失效
|
||||
|
||||
后端 `BackupExecutionService.startTask` 通过 `isRemoteNode(task.NodeID)` 判断路由,能力本就支持远程执行,缺口只在 UI。
|
||||
|
||||
### 1.2 恢复功能底层错误(架构级)
|
||||
|
||||
`server/internal/service/backup_execution_service.go:175 RestoreRecord`:
|
||||
|
||||
1. **同步阻塞**:HTTP POST 同步执行完整恢复流程,大文件/大库必超时
|
||||
2. **忽视节点路由**:总是在 Master 本地 `runner.Restore`,无论任务绑定哪个节点
|
||||
3. **无日志/无记录**:传 `backup.NopLogWriter{}`,用户看不到任何进度或失败原因;未建独立恢复记录
|
||||
4. **前端误用状态**:`BackupRecordLogDrawer.handleRestore` 把"恢复已提交"塞进 `setStreamError`,UI 渲染为黄色警告
|
||||
|
||||
**架构后果**:任务绑定到 Agent 节点 A(源文件/数据库只在 A 可达)时,点击恢复 → Master 下载备份 → Master 本地恢复 → **文件写到 Master 的 `/var/www`、连 Master 本地不存在的数据库**。完全错的机器。
|
||||
|
||||
Agent 端 `server/internal/agent/executor.go` 只实现了 `handleRunTask` 与 `handleListDir`,从设计上就没有恢复能力。
|
||||
|
||||
## 2. 设计目标
|
||||
|
||||
- 恢复与备份**对称**:支持本地/远程节点路由,同一套设施(AgentCommand 队列、日志流)
|
||||
- 恢复一等公民:独立 `RestoreRecord` 模型 + 异步执行 + LogHub SSE + 列表页
|
||||
- 破坏性操作必须**可见且可确认**:前端恢复前弹窗展示目标位置、覆盖警告
|
||||
- 复用现有基建,不引入新依赖/新抽象层
|
||||
|
||||
## 3. 架构设计
|
||||
|
||||
### 3.1 数据层
|
||||
|
||||
```go
|
||||
// model/restore_record.go
|
||||
type RestoreRecord struct {
|
||||
ID uint
|
||||
BackupRecordID uint // 源备份记录
|
||||
TaskID uint // 冗余:便于筛选
|
||||
NodeID uint // 在哪个节点执行
|
||||
Status string // running|success|failed
|
||||
ErrorMessage string
|
||||
LogContent string
|
||||
DurationSeconds int
|
||||
StartedAt time.Time
|
||||
CompletedAt *time.Time
|
||||
TriggeredBy string // 用户名(审计冗余)
|
||||
CreatedAt, UpdatedAt
|
||||
}
|
||||
```
|
||||
|
||||
迁移:`database.go` 的 `AutoMigrate` 增加 `&model.RestoreRecord{}`。
|
||||
|
||||
### 3.2 服务层
|
||||
|
||||
新增 `service.RestoreService`:
|
||||
|
||||
```go
|
||||
type RestoreService struct {
|
||||
restores repository.RestoreRecordRepository
|
||||
records repository.BackupRecordRepository
|
||||
tasks repository.BackupTaskRepository
|
||||
targets repository.StorageTargetRepository
|
||||
nodeRepo repository.NodeRepository
|
||||
storage *storage.Registry
|
||||
runners *backup.Registry
|
||||
logHub *backup.LogHub
|
||||
cipher *codec.ConfigCipher
|
||||
dispatcher AgentDispatcher
|
||||
// ...依赖同 BackupExecutionService
|
||||
}
|
||||
|
||||
// 启动恢复:同步创建 RestoreRecord → 判断路由 → 返回记录
|
||||
func (s *RestoreService) Start(ctx, backupRecordID, triggeredBy) (*RestoreRecordDetail, error)
|
||||
|
||||
// Master 本地执行:下载 → 解密/解压 → runner.Restore(LogSink → LogHub)
|
||||
func (s *RestoreService) executeLocally(ctx, restoreID)
|
||||
|
||||
// Agent 路由:EnqueueCommand("restore_record", {restoreRecordId})
|
||||
func (s *RestoreService) dispatchToAgent(ctx, restore *model.RestoreRecord)
|
||||
```
|
||||
|
||||
路由决策:
|
||||
|
||||
```
|
||||
restore := 创建 RestoreRecord(status=running, nodeId=task.NodeID)
|
||||
if isRemoteNode(task.NodeID):
|
||||
EnqueueCommand(nodeID, "restore_record", {restoreRecordId: restore.ID})
|
||||
else:
|
||||
go executeLocally(restore.ID) // 复用 BackupExecutionService.semaphore? 不,独立通道避免阻塞备份
|
||||
return restore
|
||||
```
|
||||
|
||||
### 3.3 Agent 端
|
||||
|
||||
#### 3.3.1 新增命令类型
|
||||
|
||||
`model/agent_command.go`:
|
||||
|
||||
```go
|
||||
const AgentCommandTypeRestoreRecord = "restore_record" // Payload: {"restoreRecordId": N}
|
||||
```
|
||||
|
||||
#### 3.3.2 Master ↔ Agent API(复用 Agent API 组)
|
||||
|
||||
- `GET /api/agent/restores/:id/spec` → 返回 `AgentRestoreSpec`(已解密存储配置、任务 spec、备份记录 storagePath/fileName)
|
||||
- `POST /api/agent/restores/:id` → `AgentRestoreUpdate`(status / errorMessage / logAppend)
|
||||
|
||||
`AgentRestoreSpec`:
|
||||
|
||||
```go
|
||||
type AgentRestoreSpec struct {
|
||||
RestoreRecordID uint
|
||||
BackupRecordID uint
|
||||
TaskID uint
|
||||
TaskName, Type string
|
||||
SourcePath string
|
||||
SourcePaths string
|
||||
DBHost, DBName string
|
||||
// ... 同 AgentTaskSpec 的任务字段
|
||||
Storage AgentStorageTargetConfig // 只需下载源目标
|
||||
StoragePath string // 远端对象 key
|
||||
FileName string
|
||||
Compression string
|
||||
Encrypt bool // 当前 Agent 不支持加密恢复,直接返回失败
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.3.3 Agent Executor
|
||||
|
||||
`agent/executor.go` 新增 `ExecuteRestore(restoreRecordID)`:
|
||||
|
||||
1. `client.GetRestoreSpec(restoreRecordID)`
|
||||
2. 若 `Encrypt == true` → `UpdateRestoreRecord(status=failed, errorMessage="Agent 不支持加密恢复")`
|
||||
3. 临时目录下载备份文件(通过 storage provider `Download`)
|
||||
4. `.enc` 或 `.gz` 的逆向处理(当前不支持加密;`.gz` 调 `compress.GunzipFile`)
|
||||
5. `runner.Restore(backupSpec, preparedPath, restoreLogger)` — logger 把每行通过 `UpdateRestoreRecord{LogAppend}` 回传
|
||||
6. 成功 → `UpdateRestoreRecord(status=success)`
|
||||
|
||||
`agent/agent.go` 的 `switch cmd.Type` 增加 `"restore_record": handleRestoreRecord`。
|
||||
|
||||
### 3.4 HTTP 层
|
||||
|
||||
新增 handler `restore_record_handler.go`:
|
||||
|
||||
```
|
||||
POST /api/backup/records/:id/restore → 202,body: {restoreRecordId}
|
||||
GET /api/restore/records → 列表(支持 ?taskId, ?status 筛选)
|
||||
GET /api/restore/records/:id → 详情(含 logContent)
|
||||
GET /api/restore/records/:id/logs/stream → SSE(复用 LogHub,sequence 事件协议)
|
||||
```
|
||||
|
||||
Agent 端点 `agent_handler.go`:
|
||||
|
||||
```
|
||||
GET /api/agent/restores/:id/spec
|
||||
POST /api/agent/restores/:id
|
||||
```
|
||||
|
||||
`router.go` 对应注册。注意:`LogHub` 的 recordID 命名空间是 `uint`,恢复记录 ID 可能与备份记录 ID 冲突 → 决策:
|
||||
|
||||
- **方案**:LogHub 加 `topic` 维度 —— 工作量较大
|
||||
- **简化方案**:恢复记录用 `restoreID + 常量偏移` 或使用独立 `LogHub` 实例
|
||||
|
||||
本次选择**独立 LogHub 实例**(`RestoreLogHub`),彻底隔离,代码量最小。
|
||||
|
||||
### 3.5 前端
|
||||
|
||||
#### 3.5.1 修 B1 — 节点选择器
|
||||
|
||||
`BackupTaskFormDrawer.tsx`:
|
||||
|
||||
- 已有 `localNodeId` prop
|
||||
- 新增 `nodes: NodeSummary[]` prop(由父组件传入)
|
||||
- `renderBasicStep()` 增加:
|
||||
|
||||
```tsx
|
||||
<div>
|
||||
<Typography.Text>执行节点</Typography.Text>
|
||||
<Select
|
||||
value={draft.nodeId || undefined}
|
||||
placeholder="留空或选择本机 = 在 Master 执行"
|
||||
allowClear
|
||||
options={nodeOptions} // [{label: `${name} (${status})`, value: id}]
|
||||
onChange={(value) => updateDraft({ nodeId: Number(value ?? 0) })}
|
||||
/>
|
||||
<Typography.Paragraph type="secondary" style={{ marginBottom: 0, marginTop: 4 }}>
|
||||
任务将在该节点上执行备份与恢复;源路径/数据库以该节点视角解析。
|
||||
</Typography.Paragraph>
|
||||
</div>
|
||||
```
|
||||
|
||||
`BackupTasksPage`:把已加载的 `nodeList` 传给 FormDrawer。
|
||||
|
||||
#### 3.5.2 恢复 UX
|
||||
|
||||
- `BackupRecordLogDrawer.handleRestore`:
|
||||
- 打开 `RestoreConfirmDialog`(列出将覆盖的目标路径/数据库 + 执行节点 + 风险说明)
|
||||
- 确认后 POST restore,拿 `restoreRecordId`
|
||||
- `Message.success('恢复已启动,正在打开日志')`
|
||||
- 关闭抽屉 → `navigate('/restore/records?restoreId=X')`
|
||||
- 新增 `components/restore-records/RestoreRecordLogDrawer.tsx`(结构复刻 BackupRecordLogDrawer,去掉下载/删除按钮)
|
||||
- 新增 `pages/restore-records/RestoreRecordsPage.tsx`(列表 + 状态 tag + 点击打开 Drawer)
|
||||
- `router/index.tsx` 加 `restore/records` 路由
|
||||
- `layouts/AppLayout.tsx` 菜单加"恢复记录"
|
||||
|
||||
#### 3.5.3 Types & Services
|
||||
|
||||
- `types/restore-records.ts`
|
||||
- `services/restore-records.ts`:`listRestoreRecords`、`getRestoreRecord`、`startRestoreFromBackup`、`streamRestoreRecordLogs`
|
||||
|
||||
### 3.6 依赖注入(app.go)
|
||||
|
||||
```go
|
||||
restoreRecordRepo := repository.NewRestoreRecordRepository(db)
|
||||
restoreLogHub := backup.NewLogHub()
|
||||
restoreService := service.NewRestoreService(
|
||||
restoreRecordRepo, backupRecordRepo, backupTaskRepo, storageTargetRepo,
|
||||
nodeRepo, storageRegistry, backupRunnerRegistry, restoreLogHub, configCipher,
|
||||
agentService, cfg.Backup.TempDir, cfg.Backup.MaxConcurrent)
|
||||
// 注入到 router
|
||||
```
|
||||
|
||||
`BackupRecordHandler.Restore` 改为委托给 `RestoreService.Start`。旧的 `BackupExecutionService.RestoreRecord` 保留(本地执行逻辑抽取到 RestoreService 复用),对外 HTTP 契约变更:
|
||||
|
||||
- **新契约**:`POST /backup/records/:id/restore` 返回 `{restoreRecordId: N}`(前端改为跳转到恢复详情页,而不是等同步完成)
|
||||
- **Agent**:新增 `handleRestoreRecord`
|
||||
|
||||
### 3.7 安全性
|
||||
|
||||
- 恢复是破坏性操作:后端审计日志已记录
|
||||
- 前端二次确认
|
||||
- 路径穿越:`FileRunner.Restore` 已有 `strings.HasPrefix` 校验 targetParent,沿用
|
||||
|
||||
### 3.8 迁移与兼容性
|
||||
|
||||
- 旧 `BackupRecordService.Restore` 方法保留,改为内部调用新 `RestoreService.Start`(避免外部使用方报错)—— 但 HTTP 输出变化是已知 breaking
|
||||
- 因为"恢复"目前是废的(见底层错误),前端无历史记录显示,破坏性 HTTP 变更可接受
|
||||
- 数据库无删表操作,只 AutoMigrate 新表
|
||||
|
||||
## 4. 非目标(YAGNI)
|
||||
|
||||
本次**不做**:
|
||||
- 恢复到自定义路径/自定义数据库连接(路径穿越、鉴权面大,留作 v2)
|
||||
- 恢复干运行(dry-run)
|
||||
- Agent 加密恢复(与 Agent 加密备份同策略:加密密钥不下发到 Agent)
|
||||
- 跨节点恢复(把 Agent A 的备份恢复到 Agent B)—— 任务绑定哪个节点就在哪个节点恢复
|
||||
|
||||
## 5. 测试策略
|
||||
|
||||
### 后端
|
||||
- `RestoreService.Start`:本机任务 → 走本地分支;远程任务 → 入队 `AgentCommand`
|
||||
- `RestoreRecordRepository`:CRUD + 列表筛选
|
||||
- `Agent Executor.ExecuteRestore`:mock HTTP client + stub runner
|
||||
|
||||
### 前端
|
||||
- 通过 `tsc --noEmit` 保证类型安全
|
||||
- 新增的 Dialog/Drawer/Page 至少跑通渲染(现有测试框架 vitest)
|
||||
|
||||
### 双 review 清单
|
||||
- `go build ./...` / `go vet ./...` / `go test ./... -count=1 -race` 全绿
|
||||
- `npm run build`(前端) 通过
|
||||
- CLAUDE.md 规范:所有错误必须处理、中文 commit、不引入新 UI 库
|
||||
- 修改范围对照讨论:B1 节点选择器 ✅、恢复底层重构 ✅
|
||||
|
||||
## 6. 实施顺序
|
||||
|
||||
1. RestoreRecord model + migration + repository
|
||||
2. AgentCommand 新命令类型常量
|
||||
3. RestoreService(本地执行 + 节点路由)
|
||||
4. AgentService + HTTP:GetRestoreSpec / UpdateRestoreRecord
|
||||
5. Agent client + executor:ExecuteRestore
|
||||
6. Master HTTP:RestoreHandler + router
|
||||
7. app.go 依赖注入
|
||||
8. 前端:types/services → 节点选择器 → 确认对话框 → 日志抽屉 → 列表页 → 路由 + 菜单
|
||||
9. 修 B2(`handleRestore` 改为 Message.success + 跳转)
|
||||
10. 单元测试
|
||||
11. 双 review(build/vet/test + tsc)
|
||||
154
docs/superpowers/specs/2026-04-19-enterprise-features-design.md
Normal file
154
docs/superpowers/specs/2026-04-19-enterprise-features-design.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# 设计文档:BackupX 企业级产品化 — 验证演练 + SLA 监控 + 标签分组
|
||||
|
||||
- 日期:2026-04-19
|
||||
- 范围:本轮交付三项核心企业级能力,闭环"可验证、可度量、可管理"
|
||||
- 状态:已通过(用户授权自主执行)
|
||||
|
||||
## 1. 目标与非目标
|
||||
|
||||
### 目标
|
||||
让 BackupX 从"能备份"升级为"**能保证恢复**、能**量化 SLA**、能**大规模管理**"的企业级备份管理平台。
|
||||
|
||||
### 非目标(本轮不做)
|
||||
- RBAC 多用户角色(涉及所有接口重构,下轮单独做)
|
||||
- Webhook 事件总线 / 对外 API Key 管理
|
||||
- 异地镜像复制
|
||||
- SSO / OIDC
|
||||
- 合规报表导出
|
||||
|
||||
## 2. 能力一:备份验证 / 自动恢复演练
|
||||
|
||||
### 2.1 问题
|
||||
绝大多数备份工具只保证"备份执行成功",不保证"备份真的能恢复"。企业合规(SOC2、ISO27001、HIPAA)要求定期验证备份有效性。手动演练成本高,被普遍跳过。
|
||||
|
||||
### 2.2 设计
|
||||
|
||||
**模型**:`VerificationRecord`(独立表,参考 RestoreRecord 架构)
|
||||
```
|
||||
BackupRecordID 源备份记录
|
||||
TaskID 关联任务
|
||||
NodeID 在哪里执行(复用集群路由)
|
||||
Status running | success | failed
|
||||
Mode quick | deep # quick=格式校验;deep=真恢复到沙箱
|
||||
ErrorMessage
|
||||
LogContent
|
||||
DurationSeconds
|
||||
StartedAt / CompletedAt
|
||||
TriggeredBy system(调度) / username(手动)
|
||||
```
|
||||
|
||||
**验证策略(按任务类型)**:
|
||||
|
||||
| 类型 | quick 模式 | deep 模式(v2) |
|
||||
|------|----------|---------------|
|
||||
| file | 下载到沙箱 → tar header 遍历 + 记录中 SHA-256 比对 | + 解压到临时目录校验文件完整性 |
|
||||
| sqlite | 下载 + `PRAGMA integrity_check` | + 打开查表 |
|
||||
| mysql | dump 头部格式校验(`-- MySQL dump`) | + 导入到临时库 |
|
||||
| postgresql | dump 头部格式校验(`PostgreSQL database dump`) | + 导入到临时库 |
|
||||
| saphana | tar archive 解析 + 数据文件存在 | v2 |
|
||||
|
||||
**v1 实施 quick 模式**,deep 模式作为扩展点预留。
|
||||
|
||||
**BackupTask 扩展字段**:
|
||||
```
|
||||
VerifyEnabled bool
|
||||
VerifyCronExpr string # 独立 cron,如 "0 0 4 * * *"
|
||||
VerifyMode string # quick(默认)
|
||||
```
|
||||
|
||||
**调度**:复用现有 `scheduler.Service`,增加 `VerificationRunner` 接口(类似 TaskRunner),scheduler 内部再加一组 cron entries for verify。
|
||||
|
||||
**HTTP API**:
|
||||
```
|
||||
POST /backup/tasks/:id/verify → 手动触发验证
|
||||
GET /verify/records → 列表
|
||||
GET /verify/records/:id → 详情
|
||||
GET /verify/records/:id/logs/stream → SSE
|
||||
```
|
||||
|
||||
**前端**:
|
||||
- 任务表单增加 "验证与演练" 步骤(Cron + 启用开关)
|
||||
- 新增 "验证记录" 页面(路由 /verify/records + 菜单)
|
||||
- 任务详情页显示最近一次验证状态
|
||||
- 失败则通知(复用通知服务)
|
||||
|
||||
**集群适配**:验证执行路由与备份恢复对称,任务绑定远程节点时通过 Agent 执行(复用 restore_record 路径的下载+解压能力,加入验证判定)。本轮 v1 先只在 Master 执行(下载远端备份文件本地验证);远程 Agent 路由作为扩展点。
|
||||
|
||||
### 2.3 与备份恢复的区别
|
||||
- **Verify 是只读的**:不覆盖任务源数据,只在隔离沙箱校验
|
||||
- 失败不触发回滚机制,只记录并告警
|
||||
|
||||
## 3. 能力二:SLA 监控与告警规则
|
||||
|
||||
### 3.1 问题
|
||||
当前 Dashboard 只显示历史指标,缺:
|
||||
- **RPO 监控**:任务最长允许未备份间隔,超出则视为 SLA 违约
|
||||
- **连续失败告警**:一次失败就告警会导致告警疲劳
|
||||
- **静默时段**:维护窗口不触发告警
|
||||
|
||||
### 3.2 设计
|
||||
|
||||
**BackupTask 扩展字段**:
|
||||
```
|
||||
SLAHoursRPO int # 期望 RPO 小时数,0=不限
|
||||
AlertOnConsecutiveFails int # 连续失败 N 次才告警(默认 1)
|
||||
```
|
||||
|
||||
**Dashboard 新增**:
|
||||
- SLA 合规卡片:总任务数、合规/违约分布、违约任务清单
|
||||
- 任务列表按"SLA 状态"着色(绿/黄/红)
|
||||
|
||||
**告警规则引擎**(扩展现有 notification):
|
||||
- 备份完成时检查:如果失败,查 task 的 `AlertOnConsecutiveFails` 和最近 N 条记录,判断是否达到阈值再发通知
|
||||
- 后台监控:周期扫描所有任务,计算 `now - LastSuccessAt > SLAHoursRPO` → 触发 SLA 违约事件
|
||||
|
||||
**Dashboard API**:
|
||||
```
|
||||
GET /dashboard/sla → {totalTasks, compliant, violated, violations: [{taskId, name, lastSuccessAt, hoursSinceLastSuccess, slaHours}]}
|
||||
```
|
||||
|
||||
### 3.3 前端
|
||||
- Dashboard 新增"SLA 合规"区块
|
||||
- 任务列表新列"SLA 状态"
|
||||
- 任务表单"存储与策略"步骤新增 SLA 配置
|
||||
|
||||
## 4. 能力三:任务分组 / 标签
|
||||
|
||||
### 4.1 问题
|
||||
`BackupTask.Tags` 字段已存在但未激活;大规模(>50 任务)场景下难以管理。
|
||||
|
||||
### 4.2 设计
|
||||
|
||||
**Tags 语义**:逗号分隔字符串(沿用现有字段结构),前端用 InputTag 组件展示。
|
||||
|
||||
**新增能力**:
|
||||
- 任务列表:按标签筛选 / 分组视图切换
|
||||
- 批量操作:批量启停、批量立即执行、批量删除(已有部分批量端点,扩展)
|
||||
- 标签建议:`GET /backup/tasks/tags`(去重返回全系统使用过的标签)
|
||||
|
||||
**前端**:
|
||||
- 任务表单"基础信息"步骤新增标签输入(InputTag)
|
||||
- 任务列表工具条新增"按标签筛选"多选
|
||||
- 列表新增"标签"列(显示 Tag 芯片)
|
||||
- 选中任务后悬浮"批量操作"工具条
|
||||
|
||||
## 5. 数据迁移
|
||||
|
||||
新增三字段(`VerifyEnabled` / `VerifyCronExpr` / `VerifyMode` / `SLAHoursRPO` / `AlertOnConsecutiveFails`)走 AutoMigrate。新增表 `verification_records` 走 AutoMigrate。
|
||||
|
||||
## 6. 双 review 目标
|
||||
|
||||
- `go build ./...` / `go vet ./...` / `go test ./... -count=1` 全绿
|
||||
- `npx tsc --noEmit` / `npm run build` 通过
|
||||
- 新增 3+ 单元测试:verification runner 策略、SLA 违约计算、标签筛选
|
||||
- 所有新字段对非集群用户零影响(向后兼容)
|
||||
|
||||
## 7. 实施顺序
|
||||
|
||||
1. 备份验证模型 + 仓储 + VerificationService(本地执行策略)
|
||||
2. 任务字段迁移 + 调度器 verify 入口 + HTTP handler
|
||||
3. 前端 verify 配置步骤 + 记录页 + 路由/菜单
|
||||
4. SLA 字段迁移 + Dashboard SLA API + 告警阈值逻辑
|
||||
5. 前端 Dashboard SLA 卡片 + 任务表单 SLA 配置
|
||||
6. 标签:前端 InputTag + 筛选 + 分组视图 + 批量操作
|
||||
7. 单元测试 + 全链路 review
|
||||
@@ -0,0 +1,174 @@
|
||||
# 设计文档:BackupX 企业级深化 — RBAC + API Key + 事件总线 + 节点配额
|
||||
|
||||
- 日期:2026-04-19
|
||||
- 范围:本轮聚焦企业级权限、DevOps 集成与集群资源隔离
|
||||
- 状态:已落地(用户授权自主执行)
|
||||
|
||||
## 1. 问题与目标
|
||||
|
||||
前三轮已完成"集群路由、可验证恢复、SLA 监控、任务分组"。企业化缺口:
|
||||
1. **多用户 / 权限隔离**:系统只有一个 admin,团队无法协作
|
||||
2. **DevOps 集成**:CI/CD、监控脚本只能用户名密码登录(反模式)
|
||||
3. **事件订阅**:仅备份成功/失败,verify/restore/SLA 等扩展事件不触达
|
||||
4. **集群资源管理**:所有节点共享全局 MaxConcurrent,小内存节点被挤爆
|
||||
|
||||
本轮交付:
|
||||
- **RBAC**:admin / operator / viewer 三级 + 中间件 + 前端控权
|
||||
- **API Key**:`bax_` 前缀,SHA-256 哈希存储,角色继承
|
||||
- **事件总线**:Notification 支持多事件订阅(`backup_success|backup_failed|verify_failed|restore_*|sla_violation`)
|
||||
- **节点级并发配额**:Node.MaxConcurrent / BandwidthLimit,独立 semaphore
|
||||
|
||||
## 2. RBAC 设计
|
||||
|
||||
### 2.1 角色定义
|
||||
|
||||
```
|
||||
admin 全权(用户管理、API Key、系统设置、节点管理、删除数据)
|
||||
operator 日常运维(任务/存储/通知 CRUD、触发执行/恢复/验证)
|
||||
viewer 只读(仪表盘、任务列表、记录、日志,不能触发或改变状态)
|
||||
```
|
||||
|
||||
### 2.2 实现
|
||||
|
||||
**模型层**:`User.Role` 已存在,补充 `User.Disabled`、常量 + `IsValidRole()`。
|
||||
|
||||
**中间件**(server/internal/http/middleware.go):
|
||||
- `AuthMiddleware(jwtManager, apiKeyAuth)`:支持 JWT(现有)+ API Key(`bax_` 前缀)
|
||||
- `RequireRole(roles...)`:白名单角色
|
||||
- `RequireNotViewer()`:快捷方式 — 禁止 viewer 触发写入/变更
|
||||
|
||||
**路由映射**(server/internal/http/router.go):
|
||||
- 全部 GET 列表/详情:仅需 AuthMiddleware(viewer 可见)
|
||||
- POST/PUT/DELETE 任务、存储、通知、记录操作:+`RequireNotViewer()`
|
||||
- 用户管理、API Key、节点管理、系统设置写入:+`RequireRole("admin")`
|
||||
|
||||
**前端**:
|
||||
- `utils/permissions.ts`:`isAdmin/canWrite/isViewer/roleLabel`
|
||||
- `AppLayout` 菜单按角色过滤(用户/API Key 菜单仅 admin 可见)
|
||||
- 任务列表按钮、记录抽屉操作按 `canWrite()` 隐藏
|
||||
- 顶部用户名后缀角色标签
|
||||
|
||||
### 2.3 兼容性
|
||||
|
||||
- 首位用户仍由 Setup 创建为 admin(无破坏)
|
||||
- 现有 User.Role 默认值 admin 保持
|
||||
|
||||
## 3. API Key
|
||||
|
||||
**明文格式**:`bax_` + 24 字节随机 hex(24 位熵,192 bit)
|
||||
|
||||
**存储**:KeyHash = SHA-256(明文),Prefix 取前 12 字符供 UI 区分
|
||||
|
||||
**识别**:中间件看到 `Authorization: Bearer bax_xxx` 或 `X-Api-Key: bax_xxx` 走 API Key 路径
|
||||
|
||||
**管理**(仅 admin):
|
||||
- `GET /api-keys` 列表
|
||||
- `POST /api-keys` 创建(返回一次明文 + summary)
|
||||
- `PUT /api-keys/:id/toggle` 启停
|
||||
- `DELETE /api-keys/:id` 撤销
|
||||
|
||||
**审计**:每次使用更新 `LastUsedAt`,创建/撤销记审计日志
|
||||
|
||||
**安全考虑**:
|
||||
- 24 字节随机熵,无需加盐
|
||||
- 无明文日志 / 无明文存储
|
||||
- 过期支持(TTL 小时数,0=永久)
|
||||
- 一次性展示:UI Modal 创建后显示明文 + 复制按钮,确认关闭后不可再查看
|
||||
|
||||
## 4. 事件总线
|
||||
|
||||
### 4.1 事件类型
|
||||
|
||||
```
|
||||
backup_success 备份成功
|
||||
backup_failed 备份失败
|
||||
restore_success 恢复成功
|
||||
restore_failed 恢复失败
|
||||
verify_failed 验证未通过
|
||||
sla_violation SLA 违约(后台监控事件)
|
||||
```
|
||||
|
||||
### 4.2 订阅模型
|
||||
|
||||
`Notification.EventTypes` 新字段(CSV)。匹配规则:
|
||||
- EventTypes 非空:严格匹配订阅事件
|
||||
- EventTypes 为空:沿用 OnSuccess/OnFailure 旧语义(仅 backup_*)
|
||||
|
||||
### 4.3 统一分发
|
||||
|
||||
```go
|
||||
type EventDispatcher interface {
|
||||
DispatchEvent(ctx, eventType, title, body, fields) error
|
||||
}
|
||||
|
||||
// NotificationService 实现该接口
|
||||
// VerificationEventNotifier / RestoreService.dispatchRestoreEvent 分别调用
|
||||
```
|
||||
|
||||
触发点集成:
|
||||
- `BackupExecutionService.NotifyBackupResult` → 派发 `backup_success/backup_failed`
|
||||
- `VerificationService.executeLocally`(失败时)→ 派发 `verify_failed`
|
||||
- `RestoreService.executeLocally`(终态)→ 派发 `restore_success/restore_failed`
|
||||
- **SLA 违约**(后续可由后台 monitor 调用 DispatchEvent(sla_violation))
|
||||
|
||||
## 5. 节点配额(集群优化)
|
||||
|
||||
### 5.1 字段
|
||||
|
||||
`Node.MaxConcurrent` (int, 0=不限) + `Node.BandwidthLimit` (string, rclone 格式)
|
||||
|
||||
### 5.2 执行模型
|
||||
|
||||
`BackupExecutionService` 新增 `nodeSemaphores sync.Map`(懒加载 per-node channel):
|
||||
|
||||
```go
|
||||
func (s) acquireNodeSemaphore(ctx, nodeID) chan struct{} {
|
||||
if nodeID == 0 || nodeRepo == nil { return nil }
|
||||
if v, ok := nodeSemaphores.Load(nodeID); ok { return v.(chan struct{}) }
|
||||
node, _ := nodeRepo.FindByID(ctx, nodeID)
|
||||
if node == nil || node.MaxConcurrent <= 0 { return nil }
|
||||
created := make(chan struct{}, node.MaxConcurrent)
|
||||
actual, _ := nodeSemaphores.LoadOrStore(nodeID, created)
|
||||
return actual.(chan struct{})
|
||||
}
|
||||
|
||||
func (s) executeTask(...) {
|
||||
if nodeSem := acquireNodeSemaphore(ctx, task.NodeID); nodeSem != nil {
|
||||
nodeSem <- struct{}{}
|
||||
defer func() { <-nodeSem }()
|
||||
}
|
||||
s.semaphore <- struct{}{} // 全局保底
|
||||
defer func() { <-s.semaphore }()
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**约束**:节点容量在首次创建通道时采用,运行时修改 MaxConcurrent 需重启服务生效(避免 resize channel 的 race)。
|
||||
|
||||
### 5.3 UI
|
||||
|
||||
节点管理页新增字段(编辑节点时):最大并发、带宽限制。`NodeUpdateInput` 已扩展。
|
||||
|
||||
## 6. 数据迁移
|
||||
|
||||
新增表:`api_keys`
|
||||
新增字段:`users.disabled`、`notifications.event_types`、`nodes.max_concurrent`、`nodes.bandwidth_limit`
|
||||
全走 AutoMigrate,向后兼容(默认值不破坏现有功能)。
|
||||
|
||||
## 7. 验证
|
||||
|
||||
- `go build ./...` ✅ `go vet ./...` ✅ `go test ./... -count=1` 通过
|
||||
- `npx tsc --noEmit` ✅
|
||||
- 集群与企业级测试补丁:
|
||||
- API Key 哈希不可逆(单测可验 SHA-256 确定性 + rawKey mismatch 拒绝)
|
||||
- 节点 semaphore 懒加载(channel LoadOrStore 幂等)
|
||||
- 事件分发按订阅匹配(EventTypes 非空时严格)
|
||||
|
||||
## 8. 未做(留给下一轮)
|
||||
|
||||
- SSO / OIDC(企业 SSO 接入)
|
||||
- 节点 Agent 自更新
|
||||
- 备份复制 / 异地镜像
|
||||
- SLA 违约后台主动扫描 + DispatchEvent 自动触发
|
||||
- API Key IP 白名单
|
||||
- 合规报表导出(PDF/CSV)
|
||||
@@ -0,0 +1,128 @@
|
||||
# 设计文档:BackupX 企业级闭环 — 备份复制(3-2-1)+ SLA 监控 + 存储健康
|
||||
|
||||
- 日期:2026-04-19
|
||||
- 范围:闭环第三轮 SLA + 实现 3-2-1 备份规则 + 存储目标主动监控
|
||||
- 状态:已落地(loop 调度自主执行)
|
||||
|
||||
## 1. 目标
|
||||
|
||||
前四轮已完成:集群路由、验证演练、SLA 视图、RBAC、API Key、事件总线、节点配额。
|
||||
|
||||
企业级仍有缺口:
|
||||
1. **SLA 监控只在 UI 显示**:违约不会主动告警,需要运维人工翻看
|
||||
2. **缺 3-2-1 规则**:所有备份只有一份副本,不符合企业合规(SOC2/ISO27001 推荐 3 份副本、2 种介质、1 份异地)
|
||||
3. **存储目标故障被动发现**:要等任务失败才知道云存储挂了
|
||||
|
||||
本轮闭环以上三个缺口。
|
||||
|
||||
## 2. 能力一:SLA 违约后台扫描
|
||||
|
||||
### 2.1 实现
|
||||
|
||||
`DashboardService.StartSLAMonitor(ctx, dispatcher, scanInterval, resetInterval)`:
|
||||
- 每 `scanInterval`(15m)跑一次 `SLACompliance()`
|
||||
- 违约任务派发 `sla_violation` 事件(复用 Notification 总线)
|
||||
- 同任务在 `resetInterval`(6h)内不重复派发,避免骚扰
|
||||
- 任务恢复合规后清除记忆,下次违约重新告警
|
||||
|
||||
### 2.2 状态机
|
||||
|
||||
```
|
||||
normal → (超 RPO) → notified(首次派发) → (仍违约) → 沉默(resetInterval 内)
|
||||
→ (resetInterval 过) → 再次派发
|
||||
→ (恢复成功) → normal(清除记忆)
|
||||
```
|
||||
|
||||
## 3. 能力二:备份复制(3-2-1 规则)
|
||||
|
||||
### 3.1 模型
|
||||
|
||||
- `BackupTask.ReplicationTargetIDs` CSV:副本目标存储 ID 列表
|
||||
- `ReplicationRecord` 独立表:记录每次复制执行(source → dest、状态、耗时、错误)
|
||||
|
||||
### 3.2 触发路径
|
||||
|
||||
**自动**(3-2-1 刚需):
|
||||
```
|
||||
BackupExecutionService.executeTask 成功 →
|
||||
if len(task.ReplicationTargetIDs) > 0 →
|
||||
ReplicationService.TriggerAutoReplication(task, record) →
|
||||
foreach destID: s.Start(recordID, destID) → async 下载 + 上传
|
||||
```
|
||||
|
||||
**手动**:前端备份记录详情点"复制",`POST /backup/records/:id/replicate` 带 destTargetId。
|
||||
|
||||
### 3.3 核心实现
|
||||
|
||||
```go
|
||||
func (s *ReplicationService) executeReplication(ctx, repID) {
|
||||
s.semaphore <- struct{}{}
|
||||
sourceProvider, _ := s.resolveProvider(ctx, rep.SourceTargetID)
|
||||
destProvider, _ := s.resolveProvider(ctx, rep.DestTargetID)
|
||||
|
||||
reader, _ := sourceProvider.Download(ctx, rep.StoragePath)
|
||||
localPath := tmpDir + filepath.Base(rep.StoragePath)
|
||||
writeReaderToFile(localPath, reader)
|
||||
|
||||
file, _ := os.Open(localPath)
|
||||
destProvider.Upload(ctx, rep.StoragePath, file, fileSize, meta)
|
||||
// 完成 → status = success;失败 → 派发 replication_failed 事件
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 集群保护
|
||||
|
||||
跨节点 local_disk 场景:源备份在 Agent 的本地磁盘,Master 取不到。与 BackupExecutionService.DownloadRecord 的保护一致,拒绝并返回明确错误。
|
||||
|
||||
### 3.5 数据库连接优化
|
||||
|
||||
Repository 使用 `SourceTarget`/`DestTarget` 两个不同 foreignKey → 一次查询返回完整信息,前端展示"源 → 目标"名称。
|
||||
|
||||
## 4. 能力三:存储目标健康监控
|
||||
|
||||
### 4.1 实现
|
||||
|
||||
`StorageTargetService.StartHealthMonitor(ctx, dispatcher, interval)`:
|
||||
- 每 `interval`(5m)列出所有启用的 StorageTarget
|
||||
- 逐个跑 `TestConnection()` → 更新 LastTestedAt/LastTestStatus
|
||||
- 健康→故障边沿派发 `storage_unhealthy` 事件
|
||||
- 故障→健康边沿清除 notified 记忆
|
||||
|
||||
### 4.2 设计权衡
|
||||
|
||||
- **同步串行扫描**:存储目标数量通常 < 20 个,串行简单可控
|
||||
- **单次连接超时依赖 provider**:`TestConnection` 各 provider 自己控制(rclone 已有超时)
|
||||
- **不阻塞存储配置操作**:后台独立 goroutine
|
||||
|
||||
## 5. 事件总线扩展
|
||||
|
||||
新增两个事件类型:
|
||||
- `storage_unhealthy`:存储目标掉线
|
||||
- `replication_failed`:复制失败
|
||||
- `sla_violation`:SLA 违约(上轮已定义,本轮才有触发点)
|
||||
|
||||
## 6. 数据迁移
|
||||
|
||||
新增表:`replication_records`
|
||||
新增字段:`backup_tasks.replication_target_ids` (CSV)
|
||||
全 AutoMigrate,向后兼容(默认空 = 不启用复制)。
|
||||
|
||||
## 7. 前端
|
||||
|
||||
- **任务表单**新增"备份复制"步骤:副本目标多选(自动过滤掉已是主存储的目标)
|
||||
- **新菜单**:`/replication/records` 展示复制历史(源/目标/状态/大小/耗时)
|
||||
- **已有** LastTestStatus 展示在存储目标页,本轮后台扫描会自动更新此字段
|
||||
|
||||
## 8. 双 review 通过
|
||||
|
||||
- `go build ./...` ✅ `go vet ./...` ✅ `go test ./... -count=1` ✅
|
||||
- `npx tsc --noEmit` ✅ `npm run build` ✅
|
||||
|
||||
## 9. 未做(下一轮)
|
||||
|
||||
- 备份窗口(maintenance window):时段禁止调度
|
||||
- Agent 自更新
|
||||
- SSO / OIDC
|
||||
- 报表 PDF/CSV 导出
|
||||
- 复制选项:加密再上传、checksum 验证
|
||||
- 任务模板(批量创建相似任务)
|
||||
@@ -0,0 +1,87 @@
|
||||
# 设计文档:批量操作 + Dashboard 图表 + 存储容量 UI
|
||||
|
||||
- 日期:2026-04-20
|
||||
- 状态:已落地
|
||||
- 范围:第八轮前端图表化闭环 + 规模化运维 UI
|
||||
|
||||
## 1. 目标
|
||||
|
||||
前七轮把企业级后端能力做齐:集群、验证、SLA、RBAC、API Key、3-2-1 复制、存储健康、维护窗口、任务模板、Agent 版本感知、集群概览、存储容量监控、审计 CSV、K8s 健康检查、多维统计 API。
|
||||
|
||||
本轮关注"可用的 UI":
|
||||
1. **任务批量操作**:100+ 任务场景下逐个操作低效
|
||||
2. **Dashboard 图表化**:多维统计 API 已有(第七轮),UI 缺失
|
||||
3. **存储容量可视化**:预警事件已派发(第七轮),列表需看到使用率
|
||||
|
||||
## 2. 能力一:任务批量操作
|
||||
|
||||
### 2.1 后端
|
||||
`BackupTaskService` 新增:
|
||||
- `BatchToggle(ctx, ids, enabled)`:批量启停
|
||||
- `BatchDeleteTasks(ctx, ids)`:批量删除
|
||||
- `BatchResult` 单条结果:`{id, name, success, error}`
|
||||
|
||||
`BackupRunHandler` 新增 `BatchRun`:循环调用 `RunTaskByID`,best-effort。
|
||||
|
||||
HTTP:
|
||||
```
|
||||
POST /backup/tasks/batch/toggle # {ids, enabled}
|
||||
POST /backup/tasks/batch/delete # {ids}
|
||||
POST /backup/tasks/batch/run # {ids}
|
||||
```
|
||||
|
||||
全部需要 `RequireNotViewer()`。审计日志记录"批量 X N/M 个任务"。
|
||||
|
||||
### 2.2 前端
|
||||
- 任务列表开启 `rowSelection`(仅 writable 用户可见)
|
||||
- 选中 > 0 时顶部浮现工具条:批量执行 / 启用 / 停用 / 删除 / 取消
|
||||
- 批量后 Message 展示"成功 X / 失败 Y"
|
||||
|
||||
## 3. 能力二:Dashboard 多维统计图表
|
||||
|
||||
### 3.1 实现
|
||||
`fetchDashboardBreakdown(30)` 调用第七轮的 `/dashboard/breakdown?days=30`。
|
||||
|
||||
两个图表:
|
||||
- **任务类型分布**:饼图(file/mysql/postgresql/sqlite/saphana)
|
||||
- **任务按节点分布**:柱状图(含本机 Master)
|
||||
|
||||
### 3.2 设计决策
|
||||
- 只在有数据时展示(避免空图浪费屏幕)
|
||||
- 使用 ECharts BarChart + PieChart,共享已注册组件
|
||||
- 颜色方案与存储使用量饼图一致
|
||||
|
||||
### 3.3 未做
|
||||
存储分组的"字节数饼图"已在 Dashboard 现有"存储使用量分布"中(来自 `stats.storageUsage`),不重复。
|
||||
|
||||
## 4. 能力三:存储容量 UI
|
||||
|
||||
### 4.1 前端
|
||||
存储目标列表卡片内:
|
||||
- 加载时异步获取每个启用目标的 `GetUsage`(含 About 的 diskUsage)
|
||||
- 若后端返回 `diskUsage.total + used` → 进度条 + 使用率文字 + 容量预警标签(≥85% 红)
|
||||
- 若仅有累计备份字节数 → 降级展示"已用备份 X(N 个记录)"
|
||||
|
||||
### 4.2 进度条颜色
|
||||
- < 70%:绿色(#00B42A)
|
||||
- 70-85%:橙色(#FF7D00)
|
||||
- ≥ 85%:红色(#F53F3F)+ "容量预警"标签
|
||||
|
||||
### 4.3 后端
|
||||
无改动。第七轮已有的 `StorageDiskUsage` 字段 + HealthMonitor 已支持。
|
||||
|
||||
## 5. 双 review 通过
|
||||
|
||||
- `go build ./...` ✅ `go vet ./...` ✅ `go test ./... -count=1` ✅
|
||||
- `npx tsc --noEmit` ✅ `npm run build` ✅
|
||||
|
||||
## 6. 未做(下一轮)
|
||||
|
||||
- 备份加密密钥轮换(涉及数据迁移)
|
||||
- WebSocket 实时 Dashboard
|
||||
- Agent 自更新
|
||||
- PITR 增量备份
|
||||
- SSO / OIDC
|
||||
- 报表 PDF 导出
|
||||
- 任务依赖(A 完成后 B 执行)
|
||||
- 备份元数据全局搜索
|
||||
@@ -0,0 +1,103 @@
|
||||
# 设计文档:存储容量监控 + 审计日志导出 + K8s 健康检查 + Dashboard 多维统计
|
||||
|
||||
- 日期:2026-04-20
|
||||
- 状态:已落地
|
||||
- 范围:第七轮企业运维 + 合规能力增强
|
||||
|
||||
## 1. 目标
|
||||
|
||||
前六轮完成的能力:集群路由、验证演练、SLA 监控、RBAC、API Key、事件总线、节点配额、备份复制、存储健康、维护窗口、任务模板、Agent 版本感知、集群概览。
|
||||
|
||||
本轮补齐三类常见企业运维痛点 + 合规刚需:
|
||||
1. **存储快满才发现**:TestConnection 通过不代表还有空间
|
||||
2. **审计合规导出**:月度合规报表需要 CSV 导出到外部归档
|
||||
3. **容器化部署**:K8s/Swarm 需要 liveness/readiness 探针
|
||||
4. **Dashboard 信息密度**:单维度统计看不清"哪类任务最多/哪个节点负载重"
|
||||
|
||||
## 2. 能力一:存储容量监控
|
||||
|
||||
### 2.1 实现
|
||||
`StorageTargetService.runCapacityCheckOnce` 与健康扫描同频运行(每 5 分钟):
|
||||
- 列出所有启用的存储目标
|
||||
- 类型断言 `StorageAbout` 接口,支持的后端(local_disk / WebDAV 等)执行 About
|
||||
- 使用率 `Used/Total >= 85%` 派发 `storage_capacity_warning` 事件
|
||||
- 降到阈值以下清除告警记忆
|
||||
|
||||
### 2.2 常量决策
|
||||
阈值做成 `const StorageCapacityWarningThreshold = 0.85`,不提供配置:
|
||||
- 业界运维标准线(监控告警通用 85%)
|
||||
- 留简单配置点反而增加运维复杂度
|
||||
- 如需其他阈值,用户可订阅 provider 原生监控
|
||||
|
||||
### 2.3 新事件
|
||||
`storage_capacity_warning`:Notification 订阅后可用 Webhook/邮件/Telegram 接收
|
||||
|
||||
## 3. 能力二:审计日志高级筛选 + CSV 导出
|
||||
|
||||
### 3.1 筛选字段
|
||||
扩展 `AuditLogListOptions`:
|
||||
- Category(已有)
|
||||
- Action、Username、TargetID:精确匹配
|
||||
- Keyword:模糊匹配 `detail` / `target_name`
|
||||
- DateFrom / DateTo:时间范围
|
||||
|
||||
### 3.2 CSV 导出
|
||||
`GET /audit-logs/export?<filters>`:
|
||||
- UTF-8 BOM + 逗号分隔,Excel 正确识别中文
|
||||
- 文件名 `backupx-audit-YYYYMMDD-HHMMSS.csv`
|
||||
- 最多 10000 行(防爆)
|
||||
- 9 列:时间 / 用户 / 类别 / 动作 / 目标类型 / 目标 ID / 目标名 / 详情 / 客户端 IP
|
||||
|
||||
### 3.3 权限
|
||||
审计日志本身就是所有角色可见(合规刚需知情权),导出沿用同权限。
|
||||
|
||||
### 3.4 前端
|
||||
审计页新增:用户名输入 / 关键词输入 / 日期范围选择 / 查询 / 重置 / 导出 CSV
|
||||
|
||||
## 4. 能力三:K8s/Swarm 健康检查端点
|
||||
|
||||
### 4.1 端点
|
||||
- `GET /health` 和 `/api/health`:liveness,只要进程响应就 200
|
||||
- `GET /ready` 和 `/api/ready`:readiness,检查数据库 Ping;失败 503
|
||||
|
||||
### 4.2 无认证
|
||||
两个端点公开:
|
||||
- liveness 不做依赖检查,只保证"进程存活且可响应"
|
||||
- readiness 检查 DB 连通性
|
||||
- 输出字段:`status / version / uptime / checks / timestamp`
|
||||
|
||||
### 4.3 路径兼容
|
||||
同时注册 `/health` 和 `/api/health`,方便反向代理按路径前缀统一转发。
|
||||
|
||||
## 5. 能力四:Dashboard 多维度统计
|
||||
|
||||
### 5.1 API
|
||||
`GET /dashboard/breakdown?days=30` 返回:
|
||||
- ByType:任务按类型分组(file / mysql / postgresql / sqlite / saphana)
|
||||
- ByStatus:最近 N 天记录按状态(running / success / failed)
|
||||
- ByNode:任务按执行节点分组
|
||||
- ByStorage:按存储目标分组 + 累计字节数
|
||||
|
||||
### 5.2 实现要点
|
||||
- 复用现有 `BackupTaskRepository.List` + `BackupRecordRepository.StorageUsage`
|
||||
- `makeBreakdown` / `makeBreakdownByUint` 通用排序辅助函数
|
||||
- 类型标签 Localize:`typeLabel("mysql") → "MySQL"` 直接给前端用
|
||||
|
||||
## 6. 数据迁移
|
||||
|
||||
无新表 / 无新字段。全部是后端新服务方法 + 前端新端点调用。
|
||||
|
||||
## 7. 双 review 通过
|
||||
|
||||
- `go build ./...` ✅ `go vet ./...` ✅ `go test ./... -count=1` ✅
|
||||
- `npx tsc --noEmit` ✅ `npm run build` ✅
|
||||
|
||||
## 8. 未做(下一轮)
|
||||
|
||||
- Agent 自更新(远程分发二进制)
|
||||
- WebSocket 实时 Dashboard 推送
|
||||
- 备份加密密钥轮换
|
||||
- PITR 增量备份
|
||||
- SSO / OIDC
|
||||
- 前端 Dashboard breakdown 可视化(饼图/柱状图)接入
|
||||
- 存储容量 UI 展示(预警条形指示)
|
||||
@@ -0,0 +1,105 @@
|
||||
# 设计文档:任务依赖链 + 存储容量配额 + 全局搜索
|
||||
|
||||
- 日期:2026-04-20
|
||||
- 状态:已落地
|
||||
- 范围:第九轮企业工作流 + 容量治理 + 全局可达性
|
||||
|
||||
## 1. 目标
|
||||
|
||||
本轮补齐三类企业场景能力:
|
||||
1. **工作流**:任务间依赖(A 备份成功后自动触发 B 归档)
|
||||
2. **容量硬限制**:除了 85% 告警,需要严格拒绝超配额备份
|
||||
3. **大规模可达性**:100+ 任务/记录场景下快速定位
|
||||
|
||||
## 2. 能力一:任务依赖链
|
||||
|
||||
### 2.1 数据
|
||||
`BackupTask.DependsOnTaskIDs` CSV — 当前任务依赖的上游任务 ID 列表。
|
||||
|
||||
### 2.2 触发路径
|
||||
```
|
||||
BackupExecutionService.executeTask 上传成功 →
|
||||
DependentsResolver.TriggerDependents(upstreamID) →
|
||||
列出所有 depends_on 包含 upstreamID 的已启用任务 →
|
||||
逐个 RunTaskByID(best-effort,失败仅 warn)
|
||||
```
|
||||
|
||||
`DependentsResolver` 接口由 `BackupTaskService` 实现,避免 execution 直接查仓储。
|
||||
|
||||
### 2.3 校验
|
||||
- 上游任务存在性校验
|
||||
- 不能自环(依赖自己)
|
||||
- DFS 循环检测(depth > 32 视为潜在循环)
|
||||
|
||||
### 2.4 典型场景
|
||||
- DB 备份成功 → 触发"归档打包"任务
|
||||
- 多个源任务都成功 → 触发"合规报表生成"(多上游支持)
|
||||
|
||||
## 3. 能力二:存储容量软配额
|
||||
|
||||
### 3.1 模型
|
||||
`StorageTarget.QuotaBytes` int64。0 = 不限制。
|
||||
|
||||
### 3.2 强制策略
|
||||
`BackupExecutionService.executeTask` 上传前:
|
||||
```
|
||||
target.QuotaBytes > 0 AND
|
||||
currentUsed (来自 records.StorageUsage) + fileSize > QuotaBytes
|
||||
→ 上传直接失败(不重试),记录 failed 原因
|
||||
```
|
||||
|
||||
与 `storage_capacity_warning`(85% 通知)的区别:
|
||||
- 容量预警:提醒运维人员清理/扩容
|
||||
- 软配额:硬性拒绝超配额,避免失控
|
||||
|
||||
### 3.3 典型配置
|
||||
- 生产数据库备份目标:QuotaBytes = 500 GB
|
||||
- 冷备归档目标:QuotaBytes = 2 TB
|
||||
|
||||
## 4. 能力三:全局搜索
|
||||
|
||||
### 4.1 服务
|
||||
`SearchService.Search(query)` 四类资源搜索:
|
||||
- **任务**:name/type/tags/sourcePath/dbHost/dbName
|
||||
- **存储目标**:name/description/type
|
||||
- **节点**:name/hostname/ipAddress
|
||||
- **最近 100 条备份记录**:fileName/storagePath/taskName
|
||||
|
||||
### 4.2 API
|
||||
`GET /search?q=关键字` 返回 `{tasks, records, storage, nodes, totalCount}`,每类最多 10 条。
|
||||
|
||||
### 4.3 前端
|
||||
顶部 Header 全局搜索入口:
|
||||
- 假 Input 样式 + "Ctrl+K" 提示
|
||||
- 点击/快捷键唤起 Modal
|
||||
- Input 300ms debounce 触发后端搜索
|
||||
- 分栏展示(任务 / 备份记录 / 存储目标 / 节点)
|
||||
- 点击结果项导航到对应页面
|
||||
|
||||
### 4.4 设计权衡
|
||||
- 不索引:依赖 SQL LIKE 足够应付 < 10000 任务规模
|
||||
- 备份记录只搜最近 100 条:避免全表扫描,企业场景足够
|
||||
- 无高亮:保持简单,后续可用 `<mark>` 加
|
||||
|
||||
## 5. 数据迁移
|
||||
|
||||
- 新字段 `backup_tasks.depends_on_task_ids` CSV
|
||||
- 新字段 `storage_targets.quota_bytes` int64
|
||||
- 无新表
|
||||
- AutoMigrate 向后兼容(默认 0 / 空)
|
||||
|
||||
## 6. 双 review 通过
|
||||
|
||||
- `go build ./...` ✅ `go vet ./...` ✅ `go test ./... -count=1` ✅
|
||||
- `npx tsc --noEmit` ✅ `npm run build` ✅
|
||||
|
||||
## 7. 未做(下一轮)
|
||||
|
||||
- Agent 自更新
|
||||
- 加密密钥轮换(涉及数据迁移)
|
||||
- WebSocket 实时推送
|
||||
- PITR 增量备份
|
||||
- SSO / OIDC
|
||||
- 前端任务表单"上游依赖"多选器(后端 API 已就绪,UI 待补)
|
||||
- 前端存储表单"配额"InputNumber(后端已就绪)
|
||||
- 任务依赖图可视化
|
||||
@@ -0,0 +1,86 @@
|
||||
# 设计文档:事件 Toast + 任务导入导出 + 节点性能统计
|
||||
|
||||
- 日期:2026-04-20
|
||||
- 状态:已落地
|
||||
- 范围:第十一轮体验增强 + 集群迁移 + 可观测性
|
||||
|
||||
## 1. 能力一:实时事件 Toast + 历史抽屉
|
||||
|
||||
### 1.1 前端架构
|
||||
- `useEventStore`(zustand):会话内保留最近 50 条事件 + 未读计数
|
||||
- `EventCenter` 组件:Bell 图标 + 未读徽章 + 抽屉列表
|
||||
- 订阅 SSE 全事件流(而非仅 Dashboard 子集)
|
||||
- 按事件类型映射:
|
||||
- `success` toast:backup_success / restore_success
|
||||
- `error` toast:backup_failed / restore_failed / verify_failed / replication_failed / storage_unhealthy
|
||||
- `warning` toast:sla_violation / storage_capacity_warning / agent_outdated
|
||||
|
||||
### 1.2 设计决策
|
||||
- 无持久化:避免 localStorage 膨胀;事件重要性由后端 Notification 保证
|
||||
- 抽屉打开自动标记已读,简化交互
|
||||
|
||||
## 2. 能力二:任务配置导入/导出 JSON
|
||||
|
||||
### 2.1 后端
|
||||
`TaskExportService`:
|
||||
- `Export(taskIDs)` 返回 `ExportPayload{version, exportedAt, tasks}`
|
||||
- `Import(payload)` 两阶段:
|
||||
1. 创建所有任务(忽略 DependsOn)
|
||||
2. 补齐依赖关系(上游名 → 新 ID)
|
||||
- 敏感字段排除:DBPasswordCiphertext、存储凭证
|
||||
|
||||
### 2.2 命名引用
|
||||
- 存储目标 / 节点 / 依赖任务均按 **name** 引用
|
||||
- 导入时按名称 lookup 现有系统 ID
|
||||
- 找不到则静默降级(如节点缺失 → NodeID=0 本机)
|
||||
|
||||
### 2.3 冲突策略
|
||||
任务名已存在时 **跳过**(不覆盖),避免误操作。用户需先删除再导入。
|
||||
|
||||
### 2.4 HTTP
|
||||
```
|
||||
GET /api/backup/tasks/export?ids=1,2,3 # 不传 ids 导全部
|
||||
POST /api/backup/tasks/import # JSON body,1MB 限制
|
||||
```
|
||||
|
||||
### 2.5 前端
|
||||
任务页 Header 新增 "导出 JSON" / "导入 JSON"(Upload 组件 `beforeUpload` 阻止实际上传),导入结果 Modal 展示每行创建/跳过/失败状态。
|
||||
|
||||
## 3. 能力三:节点性能统计
|
||||
|
||||
### 3.1 API
|
||||
`GET /dashboard/node-performance?days=30` 返回:
|
||||
```
|
||||
[{
|
||||
nodeId, nodeName, isLocal,
|
||||
totalRuns, successRuns, failedRuns, successRate,
|
||||
totalBytes, avgDurationSecs,
|
||||
}]
|
||||
```
|
||||
|
||||
### 3.2 实现
|
||||
- 复用 `BackupRecord.NodeID`(第二轮加入的字段)
|
||||
- 单次 List 近 N 天记录 → 按 NodeID 内存聚合
|
||||
- 按成功率降序,其次按执行次数降序
|
||||
|
||||
### 3.3 前端
|
||||
Dashboard 新增"节点执行表现(近 30 天)"表格:
|
||||
- 节点名(带 Master 标签)
|
||||
- 执行次数 / 成功 / 失败
|
||||
- 成功率(≥95% 绿,≥80% 黄,<80% 红)
|
||||
- 备份总量(字节)
|
||||
- 平均耗时
|
||||
|
||||
## 4. 双 review
|
||||
|
||||
- `go build ./...` ✅ `go vet ./...` ✅ `go test ./... -count=1` ✅
|
||||
- `npx tsc --noEmit` ✅ `npm run build` ✅
|
||||
|
||||
## 5. 未做
|
||||
|
||||
- Agent 自更新(远程下发二进制 + 信任链)
|
||||
- 加密密钥轮换(数据迁移)
|
||||
- PITR 增量备份
|
||||
- SSO / OIDC
|
||||
- 导入时覆盖模式(当前只支持跳过)
|
||||
- 导入时自动补全缺失存储目标(需要凭证,慎重)
|
||||
@@ -0,0 +1,113 @@
|
||||
# 设计文档:实时事件流 + 依赖图可视化 + UI 闭环
|
||||
|
||||
- 日期:2026-04-20
|
||||
- 状态:已落地
|
||||
- 范围:第十轮实时体验 + 上轮 UI 收口
|
||||
|
||||
## 1. 目标
|
||||
|
||||
前九轮完成所有企业级后端能力。本轮聚焦"可感知":
|
||||
1. **实时体验**:事件发生时 Dashboard 即刻刷新,无需手动 F5
|
||||
2. **工作流可视化**:依赖关系以图形方式展示,直观理解拓扑
|
||||
3. **UI 闭环**:上轮后端就绪的依赖配置 + 存储配额需要表单接入
|
||||
|
||||
## 2. 能力一:实时事件流(SSE)
|
||||
|
||||
### 2.1 设计选型
|
||||
|
||||
用 SSE 而非 WebSocket:
|
||||
- 原生浏览器支持、自动重连
|
||||
- 单向推送足够(前端订阅、后端推送)
|
||||
- 不引入新依赖(go-net 标准库)
|
||||
- 企业场景穿越反向代理无障碍
|
||||
|
||||
### 2.2 后端架构
|
||||
|
||||
```
|
||||
notification.DispatchEvent(eventType, ...) →
|
||||
1. broadcaster.Publish(非阻塞 SSE 推送)
|
||||
2. collectSubscribers + deliver(邮件/Webhook 等持久渠道)
|
||||
```
|
||||
|
||||
双通道设计:
|
||||
- **EventBroadcaster**(内存):前端实时 UI
|
||||
- **NotificationService**(持久+多渠道):合规审计、离线告警
|
||||
|
||||
订阅者 channel buffer = 32,满时丢弃单条,不阻塞生产者。
|
||||
|
||||
### 2.3 HTTP 端点
|
||||
|
||||
```
|
||||
GET /api/events/stream
|
||||
```
|
||||
|
||||
- JWT/API Key 认证
|
||||
- Content-Type: text/event-stream
|
||||
- 心跳:每 25s 发 `: heartbeat` 注释行保活
|
||||
- 禁用 nginx 缓冲(X-Accel-Buffering: no)
|
||||
|
||||
### 2.4 前端 Hook
|
||||
|
||||
`useEventStream(handler, types?)`:
|
||||
- 用 fetch + ReadableStream 解析 SSE(支持 Bearer token)
|
||||
- 指数退避重连(1s → 2s → 4s → ... → 30s)
|
||||
- 可选事件类型过滤,避免无关事件触发重渲染
|
||||
|
||||
### 2.5 Dashboard 订阅
|
||||
|
||||
监听 8 类事件,任一到达 → 刷新 Dashboard 全量数据:
|
||||
```
|
||||
backup_success/failed, restore_success/failed,
|
||||
verify_failed, sla_violation,
|
||||
storage_unhealthy, storage_capacity_warning
|
||||
```
|
||||
|
||||
## 3. 能力二:任务依赖图可视化
|
||||
|
||||
### 3.1 实现
|
||||
|
||||
`TaskDependencyGraph` 组件用 ECharts GraphChart:
|
||||
- **节点**:任务,按 `lastStatus` 着色(绿成功/红失败/蓝执行/灰空闲)
|
||||
- **边**:`dependsOnTaskIds` → 当前任务(上游 → 下游)
|
||||
- **布局**:force 物理仿真,支持拖拽/缩放
|
||||
- **过滤**:只显示有依赖关系的任务(孤立节点忽略减噪)
|
||||
|
||||
### 3.2 集成
|
||||
|
||||
任务页 `BackupTasksPage` 表格上方嵌入。无依赖时显示 Empty 引导。
|
||||
|
||||
## 4. 能力三:UI 闭环
|
||||
|
||||
### 4.1 任务表单 - 上游依赖选择器
|
||||
|
||||
`BackupTaskFormDrawer` 新增 "任务依赖" 区块:
|
||||
- 多选 Select:系统内所有任务(排除自己)
|
||||
- 帮助文案说明循环依赖自动检测
|
||||
|
||||
`BackupTasksPage` 传入 `allTasks`。
|
||||
|
||||
### 4.2 存储表单 - 配额输入
|
||||
|
||||
`StorageTargetFormDrawer` 新增 "容量配额(GB)":
|
||||
- InputNumber(GB 单位,0 = 不限制)
|
||||
- 内部存 bytes,显示 GB
|
||||
- 帮助文案区分软配额与 85% 预警
|
||||
|
||||
## 5. 数据结构
|
||||
|
||||
- 前端 Types:`backup-tasks.dependsOnTaskIds` + `storage-targets.quotaBytes`
|
||||
- 无数据库变更(后端字段已落地)
|
||||
|
||||
## 6. 双 review
|
||||
|
||||
- `go build ./...` ✅ `go vet ./...` ✅ `go test ./... -count=1` ✅
|
||||
- `npx tsc --noEmit` ✅ `npm run build` ✅
|
||||
|
||||
## 7. 未做
|
||||
|
||||
- Agent 自更新
|
||||
- 加密密钥轮换
|
||||
- PITR 增量备份
|
||||
- SSO / OIDC
|
||||
- Dashboard 事件流 Toast 展示(当前仅静默刷新)
|
||||
- 事件历史面板(内存事件可查询)
|
||||
@@ -0,0 +1,129 @@
|
||||
# 设计文档:维护窗口 + 任务模板 + Agent 版本感知 + 集群概览
|
||||
|
||||
- 日期:2026-04-20
|
||||
- 范围:第六轮企业级增强,聚焦集群规模化运维
|
||||
- 状态:已落地
|
||||
|
||||
## 1. 目标
|
||||
|
||||
前五轮已完成:集群路由、验证、SLA 监控、RBAC、API Key、事件总线、节点配额、备份复制、存储健康。
|
||||
|
||||
本轮补齐集群规模化运维最后一公里:
|
||||
1. **维护窗口**:业务高峰期禁止备份调度
|
||||
2. **任务模板**:一次保存,N 次批量创建(100+ 主机刚需)
|
||||
3. **Agent 版本感知**:节点 Agent 落后 Master 主动告警
|
||||
4. **集群概览**:Dashboard 一眼看齐所有节点健康度
|
||||
|
||||
## 2. 能力一:维护窗口
|
||||
|
||||
### 2.1 模型
|
||||
- 新字段 `BackupTask.MaintenanceWindows` CSV
|
||||
- 语法:`time=HH:MM-HH:MM` 或 `days=mon|tue,time=22:00-06:00`
|
||||
- 支持多段(`;` 分隔)、跨午夜(start > end)、指定星期
|
||||
|
||||
### 2.2 核心实现
|
||||
`backup/window.go` 新增:
|
||||
- `ParseMaintenanceWindows(string) → []MaintenanceWindow`
|
||||
- `IsWithinWindow(t, windows) bool` — 判断 t 是否在任一窗口
|
||||
- `ValidateMaintenanceWindows(string) error` — 输入合法性校验
|
||||
|
||||
### 2.3 集成
|
||||
- **调度器**:`syncTaskLocked` cron fire 时校验当前时间,非窗口跳过并审计
|
||||
- **手动执行**:`BackupExecutionService.startTask` 同样校验(防止业务高峰误触发)
|
||||
- **前端**:任务表单新增"维护窗口"输入 + 帮助文案
|
||||
|
||||
### 2.4 测试
|
||||
`backup/window_test.go` 覆盖:同日/跨夜/星期过滤/多段组合/无效输入
|
||||
|
||||
## 3. 能力二:任务模板
|
||||
|
||||
### 3.1 模型
|
||||
```go
|
||||
TaskTemplate {
|
||||
ID, Name, Description, TaskType
|
||||
Payload string // 序列化的 BackupTaskUpsertInput
|
||||
CreatedBy
|
||||
CreatedAt, UpdatedAt
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 服务
|
||||
`TaskTemplateService`:
|
||||
- CRUD:`List / Get / Create / Update / Delete`
|
||||
- 批量应用:`Apply(id, input) → []Result`
|
||||
- 每个 Variables 条目 name 必填,覆盖模板 Name
|
||||
- sourcePath / sourcePaths / dbHost / dbName / tags / nodeId 若提供则覆盖
|
||||
- best-effort:单个失败不影响其他,返回详细结果
|
||||
|
||||
### 3.3 HTTP
|
||||
```
|
||||
GET /task-templates 列表
|
||||
GET /task-templates/:id 详情
|
||||
POST /task-templates 创建(operator+)
|
||||
PUT /task-templates/:id 更新(operator+)
|
||||
DELETE /task-templates/:id 删除(operator+)
|
||||
POST /task-templates/:id/apply 批量应用(operator+)
|
||||
```
|
||||
|
||||
### 3.4 前端
|
||||
- 新菜单 `/task-templates`
|
||||
- 列表 + 每行"应用"按钮 → Modal 动态添加行 → 批量创建 → 展示结果表
|
||||
- 对 viewer 隐藏写入操作
|
||||
|
||||
## 4. 能力三:Agent 版本感知
|
||||
|
||||
### 4.1 实现
|
||||
`ClusterVersionMonitor`:
|
||||
- 每 30 分钟扫描所有远程节点
|
||||
- 比较 `node.AgentVer` vs `master.Version`(major.minor 级别)
|
||||
- 落后节点派发 `agent_outdated` 事件
|
||||
- 同节点 24 小时内只告警一次
|
||||
- 版本升级后自动清除记忆,允许下次落后再告警
|
||||
|
||||
### 4.2 版本比较策略
|
||||
- 宽松策略:只比 `major.minor`,放过 patch 差异避免小版本发布噪音
|
||||
- `dev` 版本 / 空版本不告警
|
||||
- 解析失败保守不告警
|
||||
|
||||
### 4.3 事件
|
||||
新增 `agent_outdated`,接入现有 Notification 总线
|
||||
|
||||
## 5. 能力四:Dashboard 集群概览
|
||||
|
||||
### 5.1 API
|
||||
`GET /dashboard/cluster` 返回:
|
||||
- Master 版本
|
||||
- 总节点数、在线数、离线数、过期 Agent 数
|
||||
- 每节点详情:名称/主机名/状态/版本/版本状态/任务数/最近心跳
|
||||
|
||||
### 5.2 前端
|
||||
Dashboard 新增"集群概览"卡片:
|
||||
- 4 个统计指标
|
||||
- 节点列表表格(状态徽章、版本状态着色)
|
||||
- 仅在 totalNodes > 0 时展示(单节点场景不打扰)
|
||||
|
||||
## 6. 事件总线扩展
|
||||
|
||||
新事件:`agent_outdated`
|
||||
订阅方式与其他企业事件一致(Notification.EventTypes CSV)
|
||||
|
||||
## 7. 数据迁移
|
||||
|
||||
- 新表:`task_templates`
|
||||
- 新字段:`backup_tasks.maintenance_windows`
|
||||
- 全 AutoMigrate,向后兼容
|
||||
|
||||
## 8. 双 review 通过
|
||||
|
||||
- `go build ./...` ✅ `go vet ./...` ✅ `go test ./... -count=1` ✅
|
||||
- 新增测试:`backup/window_test.go` 6 条(同日/跨夜/星期/多段/无效/空)
|
||||
- `npx tsc --noEmit` ✅ `npm run build` ✅
|
||||
|
||||
## 9. 未做(下一轮)
|
||||
|
||||
- Agent 自更新(远程分发二进制 + 信任链)
|
||||
- 备份加密密钥轮换
|
||||
- WebSocket 实时 Dashboard
|
||||
- 报表 PDF/CSV 导出
|
||||
- PITR 增量备份
|
||||
- SSO / OIDC
|
||||
Reference in New Issue
Block a user