feat: 基础设施加固 — 修复构建、数据保留、错误边界、bundle 拆分
- 修复 build script 引用已删除的 registerCheckers,恢复生产构建 - 生产入口添加 SIGINT/SIGTERM 优雅关闭(与 dev.ts 一致) - 新增 runtime.retention 配置(默认 7d),ProbeStore.prune() 定时清理过期数据 - parseDuration 扩展支持 h/d 单位 - 新增前端 ErrorBoundary 组件,防止渲染错误白屏 - Vite codeSplitting.groups 拆分 vendor chunks(业务代码 1180KB → 47KB) - 同步 delta specs 到主规范
This commit is contained in:
43
openspec/specs/data-retention/spec.md
Normal file
43
openspec/specs/data-retention/spec.md
Normal file
@@ -0,0 +1,43 @@
|
||||
## Purpose
|
||||
|
||||
定义历史拨测数据的自动清理机制:可配置的保留时长和定时清理调度。
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: 数据保留配置
|
||||
系统 SHALL 支持通过 `runtime.retention` 配置项指定历史数据保留时长,格式为持续时间字符串(`<数字><单位>`,单位支持 `d`/`h`/`m`)。
|
||||
|
||||
#### Scenario: 配置 7 天保留
|
||||
- **WHEN** 配置文件中 `runtime.retention` 设置为 `"7d"`
|
||||
- **THEN** 系统 SHALL 保留最近 7 天的检查结果,清理更早的数据
|
||||
|
||||
#### Scenario: 配置小时级保留
|
||||
- **WHEN** 配置文件中 `runtime.retention` 设置为 `"24h"`
|
||||
- **THEN** 系统 SHALL 保留最近 24 小时的检查结果
|
||||
|
||||
#### Scenario: 未配置 retention
|
||||
- **WHEN** 配置文件中未指定 `runtime.retention`
|
||||
- **THEN** 系统 SHALL 使用默认值 `"7d"`
|
||||
|
||||
#### Scenario: 无效 retention 格式
|
||||
- **WHEN** 配置文件中 `runtime.retention` 格式不合法(如 `"abc"`、`"7x"`)
|
||||
- **THEN** 系统 SHALL 在配置校验阶段报错,拒绝启动
|
||||
|
||||
### Requirement: 定时清理调度
|
||||
系统 SHALL 以固定间隔(1 小时)定期执行数据清理,删除超过保留时长的历史检查结果。
|
||||
|
||||
#### Scenario: 引擎启动后首次清理
|
||||
- **WHEN** ProbeEngine 启动
|
||||
- **THEN** 系统 SHALL 立即执行一次清理,然后每隔 1 小时再次执行
|
||||
|
||||
#### Scenario: 清理执行
|
||||
- **WHEN** 清理定时器触发
|
||||
- **THEN** 系统 SHALL 删除 `check_results` 表中 `timestamp` 早于 `now - retentionMs` 的所有记录
|
||||
|
||||
#### Scenario: 引擎停止时清除定时器
|
||||
- **WHEN** ProbeEngine.stop() 被调用
|
||||
- **THEN** 系统 SHALL 清除清理定时器,不再执行后续清理
|
||||
|
||||
#### Scenario: retention 为 0 时不清理
|
||||
- **WHEN** 配置的 retention 解析为 0 毫秒
|
||||
- **THEN** 系统 SHALL 不注册清理定时器,数据永久保留
|
||||
31
openspec/specs/frontend-error-boundary/spec.md
Normal file
31
openspec/specs/frontend-error-boundary/spec.md
Normal file
@@ -0,0 +1,31 @@
|
||||
## Purpose
|
||||
|
||||
定义前端全局错误边界:捕获渲染错误防止白屏,展示友好的错误兜底 UI。
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: 全局渲染错误捕获
|
||||
前端应用 SHALL 在最外层包裹 ErrorBoundary 组件,捕获所有子组件树的渲染错误,防止白屏。
|
||||
|
||||
#### Scenario: 子组件渲染抛出异常
|
||||
- **WHEN** 任意子组件在渲染过程中抛出 JavaScript 异常
|
||||
- **THEN** ErrorBoundary SHALL 捕获该异常,展示错误兜底 UI,而非白屏
|
||||
|
||||
#### Scenario: 错误兜底 UI 内容
|
||||
- **WHEN** ErrorBoundary 捕获到渲染错误
|
||||
- **THEN** 系统 SHALL 使用 TDesign Result 组件(type="500")展示错误提示,并提供"刷新页面"按钮
|
||||
|
||||
#### Scenario: 刷新页面恢复
|
||||
- **WHEN** 用户点击错误兜底 UI 中的"刷新页面"按钮
|
||||
- **THEN** 系统 SHALL 调用 `window.location.reload()` 重新加载页面
|
||||
|
||||
#### Scenario: 错误信息记录
|
||||
- **WHEN** ErrorBoundary 捕获到渲染错误
|
||||
- **THEN** 系统 SHALL 通过 `console.error` 输出错误信息和组件堆栈
|
||||
|
||||
### Requirement: ErrorBoundary 包裹位置
|
||||
ErrorBoundary SHALL 包裹在 QueryClientProvider 外层,确保 React Query 相关的渲染错误也能被捕获。
|
||||
|
||||
#### Scenario: 包裹层级
|
||||
- **WHEN** 应用渲染树构建
|
||||
- **THEN** 层级 SHALL 为 StrictMode > ErrorBoundary > QueryClientProvider > App
|
||||
@@ -276,3 +276,18 @@
|
||||
#### Scenario: 不配置 expect
|
||||
- **WHEN** target 未配置任何 expect 规则
|
||||
- **THEN** 系统 SHALL 正常处理,expect 字段为 undefined
|
||||
|
||||
### Requirement: 数据保留配置字段
|
||||
配置 schema 的 `runtime` 段 SHALL 支持 `retention` 字段,类型为字符串,格式为 `<数字><单位>`(单位:`d` 天、`h` 小时、`m` 分钟),用于指定历史数据保留时长。
|
||||
|
||||
#### Scenario: retention 字段校验通过
|
||||
- **WHEN** 配置文件中 `runtime.retention` 为合法格式(如 `"7d"`、`"24h"`、`"30m"`)
|
||||
- **THEN** 配置校验 SHALL 通过
|
||||
|
||||
#### Scenario: retention 字段格式非法
|
||||
- **WHEN** 配置文件中 `runtime.retention` 为非法格式(如 `"abc"`、`"7x"`、`""`)
|
||||
- **THEN** 配置校验 SHALL 失败并报告格式错误
|
||||
|
||||
#### Scenario: retention 字段缺省
|
||||
- **WHEN** 配置文件中未指定 `runtime.retention`
|
||||
- **THEN** 系统 SHALL 使用默认值 `"7d"`
|
||||
|
||||
@@ -32,3 +32,14 @@ Dashboard SHALL 使用 TDesign 组件正确处理加载状态和 API 错误。
|
||||
#### Scenario: API 请求失败
|
||||
- **WHEN** 前端 API 请求失败
|
||||
- **THEN** 页面 SHALL 使用 TDesign Alert 组件(theme=error)显示错误提示
|
||||
|
||||
### Requirement: 前端构建产物拆分
|
||||
前端生产构建 SHALL 将 vendor 依赖拆分为独立 chunk,利用浏览器并行加载和长期缓存。
|
||||
|
||||
#### Scenario: vendor chunk 拆分
|
||||
- **WHEN** 执行前端生产构建
|
||||
- **THEN** 构建产物 SHALL 包含独立的 vendor chunk(react、tdesign、recharts 各自独立),而非单个 bundle
|
||||
|
||||
#### Scenario: 业务代码变更不影响 vendor 缓存
|
||||
- **WHEN** 仅修改业务代码(src/web/ 下非 node_modules 文件)并重新构建
|
||||
- **THEN** vendor chunk 的文件名(含 hash)SHALL 保持不变,浏览器缓存 SHALL 继续有效
|
||||
|
||||
@@ -126,3 +126,18 @@
|
||||
#### Scenario: command target config 序列化
|
||||
- **WHEN** 同步 command target
|
||||
- **THEN** targets.config SHALL 存储 JSON,包含 exec、args、cwd、env、maxOutputBytes
|
||||
|
||||
### Requirement: 数据清理方法
|
||||
ProbeStore SHALL 提供 `prune(retentionMs: number)` 方法,删除超过保留时长的历史检查结果并返回删除行数。
|
||||
|
||||
#### Scenario: 清理过期数据
|
||||
- **WHEN** 调用 `prune(604800000)`(7 天毫秒数)
|
||||
- **THEN** 系统 SHALL 删除 `check_results` 表中 `timestamp` 早于当前时间减去 604800000 毫秒的所有记录,并返回实际删除的行数
|
||||
|
||||
#### Scenario: 无过期数据
|
||||
- **WHEN** 调用 `prune()` 但所有记录都在保留期内
|
||||
- **THEN** 系统 SHALL 返回 0,不删除任何记录
|
||||
|
||||
#### Scenario: 清理不影响保留期内数据
|
||||
- **WHEN** 调用 `prune()` 且存在保留期内和保留期外的记录
|
||||
- **THEN** 系统 SHALL 仅删除保留期外的记录,保留期内的记录 SHALL 不受影响
|
||||
|
||||
@@ -230,3 +230,18 @@ HTTP checker SHALL 将运行期失败归属到实际失败阶段。请求、网
|
||||
#### Scenario: 选择 command runner
|
||||
- **WHEN** target.type 为 `command`
|
||||
- **THEN** 系统 SHALL 使用 command runner 执行该目标
|
||||
|
||||
### Requirement: 定期数据清理
|
||||
ProbeEngine SHALL 在启动时注册数据清理定时器,定期调用 ProbeStore.prune() 清理过期数据。
|
||||
|
||||
#### Scenario: 引擎启动注册清理
|
||||
- **WHEN** ProbeEngine.start() 被调用且 retentionMs > 0
|
||||
- **THEN** 系统 SHALL 立即执行一次 prune,然后每隔 1 小时再次执行
|
||||
|
||||
#### Scenario: 引擎停止清除定时器
|
||||
- **WHEN** ProbeEngine.stop() 被调用
|
||||
- **THEN** 系统 SHALL 清除清理定时器,不再执行后续清理
|
||||
|
||||
#### Scenario: retentionMs 为 0 不注册清理
|
||||
- **WHEN** ProbeEngine 构造时 retentionMs 为 0
|
||||
- **THEN** 系统 SHALL 不注册清理定时器
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
- **THEN** 生成的嵌入资源模块 SHALL 保持语义一致且不依赖文件系统遍历顺序
|
||||
|
||||
### Requirement: 单 executable 输出
|
||||
生产构建 SHALL 输出一个 standalone executable,其中包含 Bun 后端、必要 server 依赖和构建后的前端资源。构建成功后 SHALL 自动清理中间产物目录(`.build/`),构建失败时 SHALL 保留中间产物以便排查。
|
||||
生产构建 SHALL 输出一个 standalone executable,其中包含 Bun 后端、必要 server 依赖和构建后的前端资源。构建成功后 SHALL 自动清理中间产物目录(`.build/`),构建失败时 SHALL 保留中间产物以便排查。生成的入口代码 SHALL 通过 import config-loader 模块隐式触发 checker 注册,而非显式调用注册函数。生成的入口 SHALL 注册 SIGINT 和 SIGTERM 信号处理器,在收到信号时依次调用 engine.stop() 和 store.close() 后退出进程。
|
||||
|
||||
#### Scenario: 在目标机器运行 executable
|
||||
- **WHEN** 生成的 executable 在兼容目标平台上运行
|
||||
@@ -49,6 +49,14 @@
|
||||
- **WHEN** 生产构建在任意步骤失败(前端构建、中间产物生成、Bun 编译)
|
||||
- **THEN** `.build/` 目录 SHALL 保留在磁盘上以供排查
|
||||
|
||||
#### Scenario: checker 注册通过 import 链触发
|
||||
- **WHEN** 生成的入口代码 import config-loader 模块
|
||||
- **THEN** checkerRegistry 单例 SHALL 通过模块依赖链自动完成注册,入口代码 SHALL NOT 显式调用任何注册函数
|
||||
|
||||
#### Scenario: 生产入口优雅关闭
|
||||
- **WHEN** executable 进程收到 SIGINT 或 SIGTERM 信号
|
||||
- **THEN** 系统 SHALL 调用 engine.stop() 停止所有定时器,调用 store.close() 关闭数据库连接,然后以退出码 0 退出进程
|
||||
|
||||
### Requirement: 外部运行时配置
|
||||
executable MUST 将环境相关运行时配置保留在嵌入的前端和 server bundle 之外。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user