## Purpose

定义基于 Vite + Bun 的全栈应用运行时，包括 HTTP server、API 命名空间、健康检查、生产静态资源服务和 SPA fallback 行为。

## Requirements

### Requirement: Bun HTTP 运行时
系统 SHALL 运行一个 Bun HTTP server，使用 `routes` 对象声明式注册 HTML 页面路由和 API 端点，由单个进程提供后端 API、健康检查和前端服务。

#### Scenario: 启动运行时服务器
- **WHEN** server 进程成功启动
- **THEN** 它 SHALL 监听配置文件中指定的 host 和 port，通过 routes 对象注册所有路由，并记录实际 server URL

#### Scenario: 通过 YAML 配置提供运行时参数
- **WHEN** 通过 YAML 配置文件提供 host、port、数据目录等参数
- **THEN** server SHALL 使用该值，且不需要重新构建

#### Scenario: CLI 只接受配置文件路径
- **WHEN** 用户通过命令行启动程序
- **THEN** 系统 SHALL 只接受一个命令行参数作为 YAML 配置文件路径

#### Scenario: 提供拨测相关 API
- **WHEN** server 启动完成
- **THEN** 系统 SHALL 通过 routes 对象提供 `/api/summary`、`/api/targets`、`/api/targets/:id/history`、`/api/targets/:id/trend` 端点

### Requirement: HTTP method 语义
系统 SHALL 只为运行时端点声明实际支持的 GET handler；不支持的 API method SHALL 按未匹配 API 路由处理，不再保留自定义 405 和 Allow header 语义。

#### Scenario: GET 请求访问运行时端点
- **WHEN** 客户端使用 `GET` 请求 `/health` 或 `/api/*` 端点
- **THEN** Bun server SHALL 返回对应端点的成功响应

#### Scenario: 不支持的 API method 请求
- **WHEN** 客户端使用不支持的 method 请求已存在的 `/api/*` 端点
- **THEN** `/api/*` 通配符 SHALL 返回包含 `error` 和 `status` 字段的 JSON 404 响应

### Requirement: API 路由命名空间
系统 MUST 将 `/api/*` 保留给后端 API 路由。

#### Scenario: API 路由匹配
- **WHEN** 请求匹配已注册的 `/api/*` 路由
- **THEN** Bun server SHALL 返回 API handler 的响应

#### Scenario: API 路由未命中
- **WHEN** 请求访问未注册的 `/api/*` 路由
- **THEN** Bun server MUST 返回 JSON 404 响应，而不是前端 HTML 文档

### Requirement: API 错误响应一致性
系统 SHALL 为 API 命名空间内的未匹配路由和未匹配 method 返回机器可读 JSON 404 响应。

#### Scenario: 未知 API 路由
- **WHEN** 客户端请求未知的 `/api/*` 路由
- **THEN** Bun server MUST 返回包含 `error` 和 `status` 字段的 JSON 404 响应，而不是前端 HTML 文档

#### Scenario: API method 不匹配
- **WHEN** 客户端使用不支持的 method 请求已存在的 API 路由
- **THEN** Bun server MUST 返回包含 `error` 和 `status` 字段的 JSON 404 响应

### Requirement: 健康检查端点
系统 SHALL 在前端 SPA fallback 之外暴露健康检查端点。

#### Scenario: 健康检查成功
- **WHEN** 客户端请求 `/health`
- **THEN** Bun server SHALL 返回成功的、机器可读的健康检查响应

### Requirement: 生产静态资源服务
系统 SHALL 在生产模式下通过自定义 `serveStaticAsset` 函数服务嵌入的 Vite 前端产出。

#### Scenario: 请求构建后的资源
- **WHEN** 客户端请求 `/assets/*` 路径下的前端资源
- **THEN** 系统 SHALL 从 StaticAssets 的 files map 中查找并返回对应资源，Content-Type 根据扩展名推断

#### Scenario: 请求前端根路径
- **WHEN** 客户端请求 `/`
- **THEN** 系统 SHALL 返回 StaticAssets 中的 indexHtml，Content-Type 为 `text/html; charset=utf-8`

### Requirement: 生产缓存策略
系统 SHALL 为生产静态资源提供基于文件名 content hash 的缓存策略。

#### Scenario: 请求前端入口 HTML
- **WHEN** 生产 server 返回前端入口 HTML 文档
- **THEN** 响应 SHALL 包含 `Cache-Control: no-cache` header

#### Scenario: 请求构建后的静态资源
- **WHEN** 生产 server 返回 `/assets/*` 路径下的静态资源
- **THEN** 响应 SHALL 包含 `Cache-Control: public, max-age=31536000, immutable` header

### Requirement: 低风险安全响应头
系统 SHALL 在生产运行时的 JSON API 响应中附加低风险安全响应头；HTML 和静态资源响应由 Bun HTML import manifest 返回其内置 headers。

#### Scenario: 生产 JSON 响应包含安全头
- **WHEN** 生产 Bun server 返回 `/health` 或 `/api/*` JSON 响应
- **THEN** 响应 SHALL 包含 `X-Content-Type-Options: nosniff` 和 `Referrer-Policy` headers

#### Scenario: 生产静态资源响应
- **WHEN** 生产 server 返回前端 HTML 文档或构建后的静态资源
- **THEN** 响应 SHALL 不要求附加自定义安全 headers（仅需 Content-Type 和 Cache-Control）

### Requirement: SPA fallback 行为
系统 SHALL 通过 fetch fallback 为非 API、非静态资源路径返回前端入口 HTML 文档。

#### Scenario: 刷新前端路由
- **WHEN** 客户端请求不包含文件扩展名的非 API 路径（如 `/dashboard`）
- **THEN** fetch fallback SHALL 返回前端入口 HTML 文档

#### Scenario: 保留 API 错误语义
- **WHEN** 客户端请求未知的 `/api/*` 路由
- **THEN** routes 中的 `/api/*` 通配符 MUST 返回 JSON 404 响应，而不是前端入口 HTML 文档

### Requirement: 优雅关机
系统 SHALL 在收到终止信号时正确清理资源。

#### Scenario: SIGINT/SIGTERM 处理
- **WHEN** 开发模式进程收到 SIGINT 或 SIGTERM 信号
- **THEN** 系统 SHALL 调用 engine.stop() 停止调度、调用 store.close() 关闭数据库连接后退出进程
