docs: define API-first console charter
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
# Multi SimAdmin 现状审计与迁移清单
|
||||
|
||||
> 审计对象:Phase 0.1 时仓库当前实现<br>
|
||||
> 产品对照基线:API-first Multi SimAdmin V1<br>
|
||||
> 上游证据基线:`3899/SimAdmin@58e220411d6599609f0eeda01eb7016e9212f970`
|
||||
|
||||
> Phase 0.1 文档导航:[返回项目章程](./project-charter.md)|**现状审计**(本文)|[角色与流程](./personas-and-workflows.md)|[信息架构](./information-architecture.md)
|
||||
|
||||
## 1. 审计结论
|
||||
|
||||
当前版本可作为安全与并发行为的 characterization 基线,但产品形态、前端、控制面契约和 API 能力模型均不适合继续增量扩展。迁移策略不是原地美化,而是:
|
||||
|
||||
- **保留迁移** 已验证的安全/一致性思想,并用 TypeScript 契约和测试重建;
|
||||
- **重写** 产品 IA、控制面、状态采集、持久化和全部业务 UI;
|
||||
- **废弃** iframe、Animal Island、通用 API 工作台和分散 endpoint 清单;
|
||||
- 新旧系统并行运行,到 Phase 9 验收后才删除旧实现。
|
||||
|
||||
本审计中的“保留”不等于复制旧文件;除非另有说明,均指保留行为和测试意图,在新架构中重新实现。
|
||||
|
||||
## 2. 当前系统事实
|
||||
|
||||
### 2.1 前端
|
||||
|
||||
| 证据 | 当前行为 | 缺口 |
|
||||
|---|---|---|
|
||||
| `public/index.html` | 单页 DOM 包含首页卡片墙、详情、iframe、API 工作台及多个 dialog | 不是业务路由;页面/模块不可深链;视图与能力无对应关系 |
|
||||
| `public/app.js` | 集中管理全局数组/Map、DOM 拼接、路由、CRUD、状态刷新和代理请求 | 单文件高耦合;业务 schema 缺失;无法按模块独立契约和 E2E |
|
||||
| `public/styles.css`、`public/styles/*` | Animal Island 卡片视觉与响应式样式 | 信息密度低;默认工作面不是异常优先表格 |
|
||||
| `public/router/view-router.js` | `home/detail/rawpage/api` 四视图切换 | 无 `/instances/:id/<module>` 路由,不支持浏览器级导航语义 |
|
||||
| `public/state/request-coordinator.js` | latest-wins/owner 相关防陈旧思想 | 可迁移思想;需要与 TanStack Query key、AbortSignal、instance ownership 结合 |
|
||||
| `public/state/operation-draft.js` | 区分读写并创建代理草稿 | 无业务字段 schema、风险等级、capability 和兼容性 |
|
||||
| `public/infrastructure/api-client.js` | 封装当前本地 API | 将由 OpenAPI 生成客户端和模块 query/mutation 取代 |
|
||||
|
||||
### 2.2 后端
|
||||
|
||||
| 证据 | 当前行为 | 缺口 |
|
||||
|---|---|---|
|
||||
| `server/config/file-config-store.js` | 0600 临时文件、fsync、rename 原子写;串行事务;immutable snapshot;revision;密码 preserve/set/clear | 文件配置不适合历史状态、Job、audit;密码仍在配置模型中,需要 SecretStore 引用 |
|
||||
| `server/clients/client-registry.js` | staged reconcile;创建失败回滚;替换/删除清理 client 和 CookieJar | 需持久实例模型、显式生命周期和可观测错误;行为应保留 |
|
||||
| `server/core.js` | 每实例客户端、CookieJar、认证会话、URL/请求处理 | 需迁入 adapter 并补版本兼容、结构化错误、deadline/取消和 secret redaction |
|
||||
| `server/proxy/policy.js` | 精确读写 allowlist;认证端点拒绝通用代理;未知默认拒绝 | endpoint 清单手写且不完整;风险只有 dangerous 布尔值 |
|
||||
| `server/proxy/confirmations.js` | token 绑定实例、revision、origin、method、规范化 target、body digest;过期且一次消费 | 应扩展 operationId、query/content-type、nonce/actor;R3 与批量 R2 全部进入 Job/audit,R2 默认进入,仅 registry 有理由和测试的 `syncSafe` 单目标例外 |
|
||||
| `server/status/service.js` | health/auth 后并发读取 8 个固定 endpoint,产生 summary | 内层 `catch {}` 丢失证据;无 freshness/support/capability;页面刷新造成多实例扇出 |
|
||||
| `server/app.js` | Fastify、loopback Host/Origin、实例 CRUD、login/logout、status、proxy、静态资源 | API 无 `/api/v1` 统一契约;catalog 与 policy/status 重复;bodyLimit 60MB 不是 OTA 上传方案 |
|
||||
| `test/*.test.js` | 核心、安全、UI 源码契约等测试 | 安全测试可作 characterization;源码正则 UI 测试不能替代组件/E2E/视觉测试 |
|
||||
|
||||
### 2.3 API 覆盖
|
||||
|
||||
- `DEFAULT_READ_PATHS`:36 个路径。
|
||||
- `DEFAULT_WRITE_PATHS`:15 个路径键。
|
||||
- `server/app.js` 的 catalog:7 个分组,并再次硬编码 endpoint。
|
||||
- `status/service.js`:另有 8 个固定采集 endpoint。
|
||||
- `public/app.js`:再次维护模块/快捷 endpoint。
|
||||
- 对照冻结上游研究结果:100 个路径、117 个业务操作(GET 50、POST 62、DELETE 5)。完整 parity 将由 Phase 0.2 的证据矩阵和测试确认。
|
||||
|
||||
结论:当前系统不存在一个能回答“此操作是否存在、支持何版本、请求/响应是什么、风险是什么、该如何展示”的单一事实源。
|
||||
|
||||
## 3. 保留 / 重写 / 废弃决策
|
||||
|
||||
### 3.1 保留迁移
|
||||
|
||||
| 资产/思想 | 新位置 | 必须保留的可验收不变量 |
|
||||
|---|---|---|
|
||||
| 原子写与串行事务 | SQLite transaction、migration、备份/恢复;导入器保留旧文件只读 | 并发变更不丢失;失败不发布新 revision;旧文件不被导入过程修改 |
|
||||
| immutable snapshot/revision | instances/app settings revision、ETag/If-Match | 写确认绑定准备时 revision;配置变化后旧 token 失效 |
|
||||
| URL/SSRF 校验 | `packages/simadmin-adapter` + API schema | 仅连接管理员已登记 canonical origin;loopback/RFC1918 也只限精确 authority;地址/DNS/redirect/proxy/Host/SNI 的完整允许与拒绝政策以[章程 7.5](./project-charter.md#75-已登记实例-origin-与-ssrflan-授权)为准 |
|
||||
| 每实例 CookieJar | adapter client/session registry | Cookie 不跨实例;logout/reconcile/delete 清理会话;Cookie 不发给浏览器 |
|
||||
| 密码登录与临时凭据 | 专用会话 BFF + SecretStore | health/auth-status 严格 R0;verify/login/logout 为 R1-S,限速、无 secret 元数据审计、禁止重放;本地 reference 只有 set/preserve/clear 三态;设备 setup/password 为 R3 专用 Job |
|
||||
| ClientRegistry staged reconcile | adapter client manager | 新 client 全部可创建后才切换;失败清理 staged client 且旧集合继续可用 |
|
||||
| 路径规范化与精确授权 | OperationRegistry 生成执行策略 | 未注册 operation 默认 403;认证端点禁止通用代理;double decode 测试通过 |
|
||||
| 敏感请求头过滤 | adapter transport | Host/Cookie/Authorization 等仅由服务端构造;用户输入不能透传 |
|
||||
| 一次性确认令牌 | `prepare → confirm → execute → audit` | 不在本表维护字段副本;严格实现[章程 7.3 唯一规范](./project-charter.md#73-确认令牌唯一规范),包括 actor/session 绑定与第一次尝试即消费 |
|
||||
| latest-wins/owner 校验 | Query key、AbortSignal、mutation owner | 切换实例后旧响应不能覆盖新实例;dialog 关闭后完成响应不能触发动作 |
|
||||
| 现有安全测试意图 | 新 unit/integration/security suites | Host/Origin、SSRF、代理、确认、secret、并发场景均有等价或更强测试 |
|
||||
|
||||
### 3.2 全面重写
|
||||
|
||||
| 范围 | 目标 | 退出条件 |
|
||||
|---|---|---|
|
||||
| 产品信息架构 | Fleet + 实例模块 + Jobs/Audit/Settings | 所有目标路由可深链;模块归属无冲突;capability 控制导航 |
|
||||
| Web 前端 | React/Vite/Router/Query/Table/Form | 不从旧 HTML/DOM 拼接迁移 UI;组件、E2E、axe 和多宽度验收通过 |
|
||||
| 控制面 API | Fastify `/api/v1` + OpenAPI 3.1 | 统一 error envelope、requestId、分页、filter、revision、partial result |
|
||||
| Operation/capability | 单一 registry | 117/117 parity;每项有 schema、风险、timeout、兼容和 UI 策略 |
|
||||
| 状态采集 | scheduler + snapshot + SSE | 限流/TTL/jitter/backoff/deadline/取消/熔断;无空 catch |
|
||||
| 数据持久化 | SQLite + Drizzle + migration | instances、tags、capabilities、snapshots、jobs/items、audit、settings、secret refs 完整 |
|
||||
| Secret 管理 | Keychain 优先,DB 仅 reference | password/cookie/token 不进入响应、日志、audit、snapshot、fixture |
|
||||
| 业务操作 UI | 按模块结构化读页和写表单 | 不要求手写路径/JSON;unsupported/auth/stale/partial 明确 |
|
||||
| Job 与审计 | 持久任务、逐实例 item、可检索 audit | R3 全部、R2 默认且批量 R2 全部 Job 化;同步安全 R2 例外也可追踪;进程重启恢复;逐实例结果不丢失 |
|
||||
| 测试体系 | unit/contract/integration/component/E2E/security/visual | mock 与真实实例职责分离;每模块至少一读一写 E2E |
|
||||
|
||||
### 3.3 最终废弃
|
||||
|
||||
| 旧资产 | 废弃原因 | 删除门禁 |
|
||||
|---|---|---|
|
||||
| `public/index.html` | 单页静态产品骨架不可扩展 | 新版 Phase 8 全验收,Phase 9 切换与回滚演练完成 |
|
||||
| `public/app.js` | 状态、路由、DOM 和 API 高耦合 | 同上;关键行为已由 E2E/characterization 覆盖 |
|
||||
| `public/styles.css`、`public/styles/*` | Animal Island 视觉不符合高密度运维 | 新设计系统多宽度验收通过 |
|
||||
| Animal Island / “SIM 岛”主视觉 | 装饰优先且占据工作空间 | 不作为新版主题或默认视图保留 |
|
||||
| iframe 一级页面 | 受 CSP/XFO/Cookie 影响,无法保证功能 | 原站仅保留安全新窗口链接 |
|
||||
| 通用 API 工作台 | 要求手写方法/路径/JSON,绕开业务 schema | 所有安全开放操作具备结构化入口;诊断也只能使用 registry operation |
|
||||
| 手写 catalog/read/write/status endpoint 列表 | 多事实源漂移 | OperationRegistry 可生成授权、目录、OpenAPI、类型和 parity 测试 |
|
||||
| 源码正则型 UI 测试 | 验证文本而非用户行为 | 由 component/Playwright/axe/visual test 替代 |
|
||||
| 60MB 普通代理 body 作为 OTA 方案 | 缺少流式上传、进度和资源约束 | OTA 专用上传 API 完成 |
|
||||
|
||||
## 4. 状态模型审计
|
||||
|
||||
### 4.1 当前问题
|
||||
|
||||
当前 Fleet 主要把实例归入 `online/auth/offline/unknown`,但状态采集会吞掉单 endpoint 错误,且 `reachable`、`authenticated`、HTTP status、summary 的含义可能互相冲突。无法判断:
|
||||
|
||||
- 数据是新鲜还是上次成功缓存;
|
||||
- 404 是“不支持”还是路径错误;
|
||||
- 401 是整实例未登录还是单能力需要认证;
|
||||
- 某模块失败时实例应是离线还是退化;
|
||||
- 批量操作中哪些实例未执行。
|
||||
|
||||
### 4.2 目标模型
|
||||
|
||||
每个采集项必须包含:
|
||||
|
||||
```text
|
||||
value | null
|
||||
fetchedAt
|
||||
receivedAt
|
||||
durationMs
|
||||
freshness: fresh | stale | expired | unknown
|
||||
support: supported | unsupported | auth-required | degraded | unknown
|
||||
result: idle | loading | success | error
|
||||
errorCode | null
|
||||
httpStatus | null
|
||||
requestId
|
||||
sourceVersion | null
|
||||
```
|
||||
|
||||
实例汇总状态采用 `offline > auth-required > degraded > online > unknown` 的确定性优先级,但 UI 同时显示 freshness。不得将 `unsupported` 转换成 `null` 后显示 `-`。三个正交维度引用[章程 7.6](./project-charter.md#76-三个正交页面状态);领域/API 枚举统一为 `fresh`(中文“最新”),禁止 `latest`。
|
||||
|
||||
Job 使用以下状态机(Preparation 独立,不是 Job):
|
||||
|
||||
```text
|
||||
queued → running | cancelled
|
||||
running → succeeded | partially-succeeded | failed | unknown-result | cancelling
|
||||
cancelling → cancelled | failed | unknown-result
|
||||
```
|
||||
|
||||
`succeeded/partially-succeeded/failed/cancelled/unknown-result` 是终态;Job 到达终态后,其状态、结果、items、Attempts 与事件历史严格不可变。Preparation 使用 `prepared/consumed/expired/invalidated`。Job item 独立使用 `queued/skipped/running/succeeded/failed/cancelled`;Job 汇总不得覆盖 item 原因。
|
||||
|
||||
用户 retry 总是创建带新 `jobId`、独立状态和独立 Attempt 的新 Job,`retryOfJobId` 指向直接来源,`rootJobId` 指向最初 Job。批量重试另建父 Job,只复制明确选择且可重试的失败 items,每个新 item 记录 `sourceJobItemId`;成功项绝不复制/重放。SSE/audit 关联新 jobId+attemptId(批量另含新 jobItemId),并保留 retry/root/source lineage。Job 尚未终态时,仅幂等且 registry 明确许可的网络级内部重试可在同一 Attempt 内有限执行,并逐次记录 transport try;不得用于非幂等操作。完整不变量以[章程 7.4](./project-charter.md#74-preparationjob-与-attempt-固定模型)为准。
|
||||
|
||||
## 5. 安全与可靠性差距
|
||||
|
||||
| 领域 | 已有基础 | 必补缺口 | 验收 |
|
||||
|---|---|---|---|
|
||||
| SSRF/URL | 当前 core/schema 有校验思想 | 已登记 origin 精确授权;只允许 http(s);规范 IP;loopback/RFC1918 精确端口;拒绝 link-local/metadata/公网/转换地址;全地址 DNS 固定与 peer 复验;redirect 同 origin;忽略代理;Host/SNI 一致 | [章程 7.5](./project-charter.md#75-已登记实例-origin-与-ssrflan-授权)矩阵逐类通过,拒绝 fixture 不产生外连 |
|
||||
| Host/Origin | loopback authority 精确检查 | 新 Fastify 插件化、proxy headers 场景明确 | inject + 真实浏览器跨源测试 |
|
||||
| 代理授权 | 精确 allowlist、auth path 拒绝 | registry 生成,参数 schema,未知 operation 默认拒绝 | 117 operation 与授权快照一致 |
|
||||
| 确认 | 一次性 token 和 body digest | 章程唯一 token 规范;Preparation 与 Job 分离;R3/批量 R2 全部 Job,R2 默认 Job;`syncSafe` 例外须有理由和专项测试 | 首次 mismatch 也消费;replay、换 actor/session/实例/revision/origin/operation/method/path/query/body/content-type 均失败;同步例外无裸执行 |
|
||||
| Secrets | 响应有 redaction | Keychain/reference、日志/audit/snapshot/fixture 全链路 | secret canary 全仓输出扫描为零 |
|
||||
| 请求调度 | 单次 Promise.all | 限流、TTL、退避、熔断、deadline、取消 | 大 fleet 压测不超过配置并发 |
|
||||
| 错误表达 | 部分 status/error 字段 | 统一 envelope 和 item 级错误;禁止空 catch | 每种失败可定位且带 requestId |
|
||||
| OTA | 60MB bodyLimit | 流式上传、大小/类型校验、进度、取消、Job | 50MB 测试不进入 JSON 解析/审计正文 |
|
||||
|
||||
## 6. 迁移顺序与护栏
|
||||
|
||||
1. **Phase 0:** 冻结文档、上游 operation 证据、脱敏 fixtures;生产代码不动,规格与质量复审通过前不提交。
|
||||
2. **Phase 1–4:** 新 monorepo/API/数据/adapter/status 与旧服务并存;禁止直接替换 8788。
|
||||
3. **Phase 5–7:** 新 Web、模块、Jobs、Audit 只调用 `/api/v1`;不从旧通用代理新增功能。
|
||||
4. **Phase 8:** 真实浏览器、真实实例、安全和独立复审;blocker/major 必须修复。
|
||||
5. **Phase 9:** 独立测试端口运行;导入旧配置预览;只读 shadow comparison;备份/回滚演练;最后切换。
|
||||
6. 稳定窗口后才删除旧 `public/`、旧 endpoint lists 和源码正则 UI tests。
|
||||
|
||||
### 禁止事项
|
||||
|
||||
- 在 registry 完成前继续向多个旧 endpoint 数组手工补路径。
|
||||
- 为赶进度把 `unsupported` 当成功空值、把失败 `catch {}`。
|
||||
- 将 Keychain 不可用时自动降级为明文密码而不阻断/告警。
|
||||
- 让前端直连实例、持有 Cookie/密码或拼接任意上游路径。
|
||||
- 以测试假服务替代真实 SimAdmin 最终验收。
|
||||
|
||||
## 附录 A:主要文件覆盖规则与清单(Phase 0.1 snapshot)
|
||||
|
||||
“主要文件”机械定义为 `git ls-files 'public/**' 'server/**' 'test/**'` 的全部跟踪文件;评审用命令输出必须与下列清单逐项相等。新增/删除匹配文件必须在同一变更更新本附录,并在 3.1–3.3 按所属规则获得结论,避免靠人工判断“主要”。
|
||||
|
||||
- **前端全面重写:** `public/app.js`、`public/domain/formatters.js`、`public/index.html`、`public/infrastructure/api-client.js`、`public/router/view-router.js`、`public/state/fleet-view-model.js`、`public/state/operation-draft.js`、`public/state/request-coordinator.js`、`public/styles.css`、`public/styles/base.css`、`public/styles/components.css`、`public/styles/responsive.css`、`public/styles/tokens.css`。其中 request coordinator 的 latest-wins/owner **行为不变量**按 3.1 保留迁移;旧文件仍按 3.3 门禁最终废弃。
|
||||
- **后端保留行为并重写到新架构:** `server/app.js`、`server/clients/client-registry.js`、`server/config/errors.js`、`server/config/file-config-store.js`、`server/config/schema.js`、`server/core.js`、`server/index.js`、`server/proxy/confirmations.js`、`server/proxy/policy.js`、`server/proxy/routes.js`、`server/proxy/service.js`、`server/status/routes.js`、`server/status/service.js`。可保留不变量只限 3.1 明列的原子写/revision、client staged reconcile、Cookie 隔离、路径默认拒绝、header 过滤、确认与安全测试意图;其余控制面、持久化、adapter、scheduler 按 3.2 重写,旧 endpoint/proxy/status 多事实源按 3.3 废弃。
|
||||
- **测试迁移意图、废弃实现形式:** `test/phase-one-architecture.test.js`、`test/phase-one-blockers.test.js`、`test/phase-two-security.test.js`、`test/phase-two-ui.test.js`、`test/server-core.test.js`、`test/ui-contract.test.js`。安全/并发 characterization 意图按 3.1 保留并增强;源码正则 UI 断言按 3.3 删除门禁由 component/E2E/axe/visual 替代。
|
||||
|
||||
## 7. 审计验收清单
|
||||
|
||||
- [ ] 每个当前主要文件均有保留/重写/废弃结论。
|
||||
- [ ] 保留项写明行为不变量,而非笼统“复用代码”。
|
||||
- [ ] 废弃项有删除门禁,不在并行迁移期提前删除。
|
||||
- [ ] 当前 API 覆盖与上游基线差距有明确数字和 Phase 0.2 验证责任。
|
||||
- [ ] 状态、Job、capability 和错误模型足以区分 loading/error/stale/unsupported/auth/partial。
|
||||
- [ ] 安全差距对应可执行测试,不以“注意安全”作为验收。
|
||||
- [ ] 本阶段未修改生产代码、未切换端口、未接触真实写操作。
|
||||
@@ -0,0 +1,235 @@
|
||||
# API-first Multi SimAdmin 信息架构
|
||||
|
||||
> 本文定义 V1 路由、导航、页面职责、上游模块映射、状态呈现与验收矩阵。路由与 OperationRegistry 是实现边界,不允许再建立“任意 endpoint 工作台”。
|
||||
|
||||
> Phase 0.1 文档导航:[返回项目章程](./project-charter.md)|[现状审计](./current-system-audit.md)|[角色与流程](./personas-and-workflows.md)|**信息架构**(本文)
|
||||
|
||||
## 1. 导航模型
|
||||
|
||||
### 1.1 全局导航
|
||||
|
||||
1. **Fleet**:所有实例的异常定位、筛选、比较、多选入口。
|
||||
2. **Jobs**:异步/批量操作的状态、逐项结果、取消和重试。
|
||||
3. **Audit**:操作证据与故障复盘。
|
||||
4. **Settings**:实例、凭据状态和系统采集参数。
|
||||
|
||||
实例被选中后出现二级模块导航:Overview、Cellular、Device Network、Messages、Calls、eSIM、Notifications、Automation、OTA。导航由 capability 驱动,而非固定展示一组空页面。
|
||||
|
||||
### 1.2 URL 规则
|
||||
|
||||
- 实例使用不可变内部 `:id`;名称变化不改变 URL。
|
||||
- 列表 filter/sort/page 可序列化到 query,便于分享和返回。
|
||||
- dialog 不替代主要业务路由;新增实例使用独立路由。
|
||||
- Job/Audit 详情有可深链 URL。
|
||||
- 原站不设 iframe 路由,仅在实例头部提供外链,使用新窗口和 `rel="noopener noreferrer"`。
|
||||
- 不提供 `/api-workbench`、`/rawpage` 或接受任意 path 的 UI。
|
||||
|
||||
## 2. 路由清单
|
||||
|
||||
| 路由 | 页面职责 | 主操作 | 权限/风险边界 |
|
||||
|---|---|---|---|
|
||||
| `/` | 规范重定向 | 重定向 `/fleet` | 无业务内容 |
|
||||
| `/fleet` | 高密度实例表格、搜索、筛选、排序、列、分页、多选、异常入口 | 刷新、批量动作 prepare | R0;批量动作按 registry 风险升级 |
|
||||
| `/instances/new` | 添加实例、连接/认证/capability 初探 | R0 health/auth-status;R1-S credential verify/login;保存 | 仅登记并校验 canonical origin;R1-S 限速、脱敏元数据审计、禁止重放;secret 不回显 |
|
||||
| `/instances/:id/overview` | 设备、SIM、网络、系统、关键异常摘要 | 刷新;服务/系统重启 | 查询 R0;重启 R3 Job |
|
||||
| `/instances/:id/cellular` | 蜂窝、信号、小区、运营商、射频、锁频/锁小区、数据/APN | register、lock、data 等 | R0/R1/R2;影响服务项 Job 化 |
|
||||
| `/instances/:id/device-network` | 接口、地址、DDNS、WLAN | DDNS sync、WLAN connect/forget | R0/R1/R2,断连影响需预检 |
|
||||
| `/instances/:id/messages` | 统计、列表、会话、发送、删除 | send、delete、clear | 查询 R0;发送按 registry;删除/清空 R3 |
|
||||
| `/instances/:id/calls` | 实时通话、拨号、设置、历史、IMS、voicemail | dial/answer/hangup/settings;删除通话记录 | 查询 R0;通话控制/设置按 R1/R2;通话记录删除 R3 Job |
|
||||
| `/instances/:id/esim` | work mode、config、lpac、eUICC、profiles | download/enable/rename/delete | R1/R2;profile delete R3 |
|
||||
| `/instances/:id/notifications` | config、channel test、logs、queue | test/retry/clear/delete | config R1;清理类 R3 |
|
||||
| `/instances/:id/automation` | 配置、task test、logs | save/test/clear | config R1;测试按 operation;clear R3 |
|
||||
| `/instances/:id/ota` | 当前/最新版本、上传、prepare、apply、cancel | upload/prepare/apply/cancel | prepare R2;apply R3 Job;专用流上传 |
|
||||
| `/jobs` | Job 表格与筛选 | 取消/重试入口 | cancel 默认 R1;retry 继承原 operation 风险且最低 R2,均由 registry 可提高 |
|
||||
| `/jobs/:jobId` | 阶段、items、attempt、事件、retry lineage 和关联 audit | cancel/retry items | cancel 默认 R1;retry 创建新 Job 且继承原风险(最低 R2);不复制/重放成功 item,非幂等先核实 |
|
||||
| `/audit` | 审计列表、筛选、导出 | 查询/导出脱敏摘要 | R0;导出遵循 redaction |
|
||||
| `/audit/:eventId` | 单事件详情和关联链路 | 跳转 Job/实例 | 不展示 sensitiveFields |
|
||||
| `/settings/instances` | 实例 CRUD、标签、凭据状态、旧配置导入导出 | create/edit/delete/import | create/update R1;批量 import R2 Job;delete R3 Job;不导出 secret |
|
||||
| `/settings/instances/:id` | 实例编辑、连接测试、会话与本地 secret reference、设备密码专用流程 | health/auth-status R0;credential verify/login/logout R1-S;save;delete | 本地 reference set/preserve/clear 为 R1;设备 setup/password 为 R3 专用 Job;delete R3 Job;revision 并发控制 |
|
||||
| `/settings/system` | 轮询、缓存、并发、诊断、版本和数据目录 | 保存设置、诊断 | 设置 R1;限制安全范围 |
|
||||
| `*` | Not found | 返回 Fleet/上一页 | API 404 与 Web fallback 分离 |
|
||||
|
||||
## 3. 页面内部结构
|
||||
|
||||
### 3.1 AppShell
|
||||
|
||||
- 顶栏:页面标题、全局连接/SSE 状态、全局搜索入口、当前版本。
|
||||
- 全局侧栏/窄屏菜单:Fleet、Jobs、Audit、Settings。
|
||||
- 实例上下文栏:实例名称/ID、origin、总状态、认证、新鲜度、实例切换、原站外链。
|
||||
- 模块导航:由 capability 生成;degraded 有徽标;unsupported 在能力详情可查。
|
||||
- 主内容:页面标题/说明、状态 banner、工具栏、数据区、持久错误区。
|
||||
|
||||
### 3.2 Fleet 表格默认列
|
||||
|
||||
| 列 | 值与行为 |
|
||||
|---|---|
|
||||
| 选择 | 多选;跨页选择必须显示选择范围 |
|
||||
| 实例 | 名称、稳定 ID、标签;点击 overview |
|
||||
| 状态 | online/offline/auth-required/degraded/unknown;点击状态详情 |
|
||||
| 认证 | authenticated/auth-required/unknown |
|
||||
| 运营商/制式 | 最后成功值 + freshness |
|
||||
| 信号 | 规范化等级/原始关键值;可排序,未知不按 0 处理 |
|
||||
| 版本 | 上游版本/commit 与兼容提示 |
|
||||
| 异常 | 关键失败采集项数量及最高优先级 |
|
||||
| 延迟 | health 延迟和 checkedAt |
|
||||
| 最后成功 | 绝对时间 + 相对时间;stale 明显 |
|
||||
| 操作 | 查看、刷新;写动作进入选择/专用流程 |
|
||||
|
||||
默认排序:异常优先级降序 → stale 年龄降序 → 名称升序。列偏好可本地保存,但状态、实例、异常不可全部隐藏。
|
||||
|
||||
### 3.3 实例模块公共布局
|
||||
|
||||
1. **摘要条:** capability、采集时间、freshness、requestId/错误入口。
|
||||
2. **读数据区:** 结构化表格/定义列表/时间序列,不直接 dump raw JSON。
|
||||
3. **动作区:** 只显示 registry 中该 capability 可用的结构化动作。
|
||||
4. **确认抽屉/页:** 目标、diff、风险、预检和确认。
|
||||
5. **关联活动:** 最近 Job 和 audit 摘要。
|
||||
6. **状态覆盖:** loading/empty/stale/error/unsupported/auth-required/degraded 采用统一组件。
|
||||
|
||||
## 4. 上游 API 模块到页面映射
|
||||
|
||||
| 上游模块 | 能力范围 | 主页面 | 次入口/备注 |
|
||||
|---|---|---|---|
|
||||
| 实例与认证 | health、auth status、setup、login、logout、password、settings | `/instances/new`、`/settings/instances/:id` | health/auth-status 严格 R0;verify/login/logout 为 R1-S 专用会话 BFF;setup/password 为 R3 专用 Job;均禁止通用代理 |
|
||||
| 设备与系统 | device、stats、CPU、connectivity、service restart、system reboot | `overview` | restart/reboot 为 R3 Job |
|
||||
| SIM | SIM 信息、详情刷新、缓存更新 | `overview` | 与 cellular 交叉字段只引用同一 resource/query,不重复 endpoint 定义 |
|
||||
| 蜂窝网络 | network、cells、monitor、signal、location、operators、register | `cellular` | overview 仅摘要和深链 |
|
||||
| 射频与锁定 | radio mode、band lock、cell lock、unlock all | `cellular` | lock/unlock 预检并按 R2/R3 registry 执行 |
|
||||
| 数据连接 | data、roaming、airplane、APN、baseband | `cellular` | R1 开关与 R2 restart 分开呈现 |
|
||||
| 设备网络 | interfaces、addresses、DDNS、WLAN 全生命周期 | `device-network` | connect/disconnect 后需验证 connectivity |
|
||||
| 工作模式与 eSIM | work mode、config、lpac、eUICC、profiles | `esim` | profile delete R3;工作模式影响说明 |
|
||||
| 短信 | stats、list、conversation、send、delete、clear | `messages` | 删除/清空 R3;短信正文不进入 audit |
|
||||
| 通话 | calls、dial、answer、hangup、volume、forwarding、history、IMS、voicemail、history delete | `calls` | 实时状态与历史分区;通话记录单条/批量删除 R3 Job,结果进入 Job 并关联脱敏 audit |
|
||||
| 通知 | config、test、logs、queue、retry、clear、delete | `notifications` | 日志/队列删除按 R3 |
|
||||
| 自动化 | config、logs、clear、task test | `automation` | config schema 表单,禁用任意 JSON |
|
||||
| OTA | status、upload、latest、online prepare、apply、cancel | `ota` | upload 专用流;apply R3 Job |
|
||||
|
||||
### 映射约束
|
||||
|
||||
- 每个 operation 只有一个主页面 owner;其他页面只能引用摘要或深链。
|
||||
- 页面不得手写上游 method/path;通过 operationId 调用生成客户端。
|
||||
- 同一 resource 的缓存 key 包含 instanceId、resource/operation 和参数;不可跨实例共享数据。
|
||||
- capability 不等于导航可见性:危险操作还需风险政策、认证和前置状态共同满足。
|
||||
|
||||
## 5. 状态呈现模型
|
||||
|
||||
### 5.1 三个正交维度
|
||||
|
||||
| 维度 | 值 | UI 责任 |
|
||||
|---|---|---|
|
||||
| 请求执行 | idle/loading/success/error | 控制加载、重试和持久错误 |
|
||||
| 数据新鲜度 | fresh/stale/expired/unknown | 决定是否保留旧值、是否要求刷新/确认 |
|
||||
| 能力支持 | supported/unsupported/auth-required/degraded/unknown | 决定模块/动作可用性及解释 |
|
||||
|
||||
不得压缩为单个 `status` 后丢失信息。例如:页面可以同时是 `request=error`、`freshness=stale`、`support=supported`,表示刷新失败但仍展示上次有效值。
|
||||
|
||||
本三维模型受[项目章程 7.6](./project-charter.md#76-三个正交页面状态)约束;领域/API 只允许 `fresh`,其中文显示为“最新”,不得定义 `latest` 枚举(OTA 的“latest release”是资源名称,不是新鲜度枚举)。
|
||||
|
||||
### 5.2 页面状态行为
|
||||
|
||||
| 页面状态 | 数据区 | 动作区 | 反馈 |
|
||||
|---|---|---|---|
|
||||
| loading | skeleton,保留布局 | 禁用依赖当前值的动作 | 超过阈值显示仍在加载与取消/重试 |
|
||||
| empty | 明确“成功查询但为空” | 提供创建/扫描等适用动作 | 显示 fetchedAt |
|
||||
| stale | 保留最后成功值 | R0 可刷新;写操作提示或阻断 | banner 显示最后成功和本次错误 |
|
||||
| error 无缓存 | 错误状态卡 | 只保留安全的重试/登录/诊断 | errorCode、requestId、HTTP status |
|
||||
| unsupported | 不展示伪空表格 | 隐藏写动作 | 说明版本/404 探测证据 |
|
||||
| auth-required | 保留可用旧值并 stale | 禁用写;显示登录 | 登录成功仅自动重试 R0 |
|
||||
| degraded/partial | 成功子区正常,失败子区局部错误 | 仅开放不依赖失败前置项的动作 | 明确失败数量和逐项原因 |
|
||||
|
||||
## 6. 风险操作的信息架构
|
||||
|
||||
| 风险 | 页面形态 | 结果落点 |
|
||||
|---|---|---|
|
||||
| R0 | 页面加载、刷新、筛选 | 原页面数据区;错误持久显示 |
|
||||
| R1 | 页面内结构化表单 → diff 确认 | 原页面刷新 + audit 侧链 |
|
||||
| R2 | 专用表单 → preflight → 确认 | 默认 Job 详情/侧面进度 + audit;`syncSafe` 单目标例外在原页显示结构化结果 + audit |
|
||||
| R3 | 专用危险区 → 强确认/复述目标 → Job | `/jobs/:id` 为主,原页面只显示状态与深链 |
|
||||
|
||||
R3 全部 Job 化。R2 默认 Job 化;仅单目标 operation 可在 OperationRegistry 中以 `syncSafe=true` 明确例外,并必须登记理由及覆盖同步安全、超时、结果落点和误重放的测试;批量 R2 即使单项可同步也仍 Job 化。任何 R2/R3 不得仅用通用 `confirm()`、toast 或“发送 JSON”完成。确认关闭、目标切换或 revision 变化必须清除 token。
|
||||
|
||||
确认令牌不在本文重复定义,以[项目章程 7.3](./project-charter.md#73-确认令牌唯一规范)为唯一规范;Preparation/Job/Attempt/JobItem 与 SSE/audit 关联以[项目章程 7.4](./project-charter.md#74-preparationjob-与-attempt-固定模型)为准。`prepare` 不创建 Job,确认消费 token 后才创建;用户 retry 必须创建新 `jobId`,并用 `retryOfJobId`/`rootJobId` 保留 lineage,来源终态 Job 不变。
|
||||
|
||||
### 6.2 实例删除信息架构
|
||||
|
||||
删除遵循 `active|disabled → deleting → tombstoned → purged`。确认后创建 R3 Job 并进入 `deleting`,立即从默认 Fleet/导航隐藏且停止轮询、会话和新写;删除详情仍可从 Job/audit 深链访问。不可取消点、snapshot/secret ref、失败恢复和“purge 不删除 audit”的完整流程以[WF-13](./personas-and-workflows.md#wf-13-两阶段删除实例)为准。
|
||||
|
||||
### 6.3 出站目标边界
|
||||
|
||||
所有实例页只能使用管理员已登记 canonical origin,不接受动作参数指定 host/port。允许地址、DNS 固定/复验、redirect、代理环境变量及 Host/SNI 的实现与攻击测试以[项目章程 7.5](./project-charter.md#75-已登记实例-origin-与-ssrflan-授权)为唯一政策;特别是 loopback/RFC1918 仅允许已登记精确 authority,不是端口扫描授权。
|
||||
|
||||
### 6.4 复审关注动作的默认风险
|
||||
|
||||
下表默认值必须进入 OperationRegistry;逐项研究可提高风险,不得降低到表中等级以下。若拆分出多个 operation,每项分别登记。
|
||||
|
||||
| 动作 | 默认风险与执行 | 确认/结果落点 |
|
||||
|---|---|---|
|
||||
| Messages send | R2,默认 Job;仅 registry `syncSafe` 理由与测试齐全时允许单目标同步 | 显示实例、号码、内容摘要;Job 详情或同步原页结果 + audit |
|
||||
| Notifications channel test / queue retry | R2,默认 Job;单目标 `syncSafe` 例外规则同上 | 显示 channel/queue item 与外发影响;Job 或同步原页结果 + audit |
|
||||
| Automation task test | R2,默认 Job;单目标 `syncSafe` 例外规则同上 | 显示 task、参数和可能副作用;Job 或同步原页结果 + audit |
|
||||
| Settings instance read | R0 | 设置列表/详情;错误持久显示 |
|
||||
| Settings instance create/update(含凭据 set/preserve/clear) | R1 | diff、revision;原设置页结果 + audit |
|
||||
| Settings health/auth-status | 严格 R0,不提交 secret、不改变会话 | 原设置页分阶段结果 |
|
||||
| Credential verify / login / logout | R1-S(R1 子类,不是新等级) | 专用会话 BFF;按 actor+session+instance 限速;audit 仅无 secret 元数据;禁止自动重放 |
|
||||
| Device setup/password change | R3,全部专用 Job 化 | 强确认、Job+audit;不得通用代理;Phase 0.2 仅可细化上游形态 |
|
||||
| Settings instance import | R2,批量所以全部 Job 化 | 预览 diff/冲突/数量;Job items + audit |
|
||||
| Settings instance delete | R3,全部 Job 化 | 复述实例 ID 与关联清理影响;Job + audit |
|
||||
| Jobs cancel | R1 默认;若取消会触发补偿或中断设备操作则 registry 提高到原 operation 风险 | Job 详情显示可取消阶段;结果仍在该 Job 事件与 audit |
|
||||
| Jobs retry | 继承原 operation 风险且最低 R2;创建具有独立 Attempt 的新 Job | 重显失败 items/参数摘要并重新确认;新 `jobId` + audit,通过 `retryOfJobId`/`rootJobId` 关联不可变来源;批量仅复制明确选择且可重试的失败 items,并记录 `sourceJobItemId`,成功项绝不复制/重放 |
|
||||
| Calls history delete(单条/批量) | R3,全部 Job 化 | 展示记录身份与数量并复述实例 ID(批量再复述数量);`/jobs/:jobId` items + 历史刷新 + 脱敏 audit |
|
||||
|
||||
## 7. 页面 × 模块 × 状态 × 风险验收矩阵
|
||||
|
||||
缩写:`L` loading、`E` empty、`S` stale、`X` error、`U` unsupported、`A` auth-required、`P` degraded/partial。
|
||||
|
||||
| 页面 | API 模块 | 必验收状态 | 写风险/关键动作 | 可验收结果 |
|
||||
|---|---|---|---|---|
|
||||
| Fleet | health/auth/device/SIM/network/stats/OTA 摘要 | L,E,S,X,A,P,unknown | 批量 R1–R3(仅 batchable) | 筛选/排序/多选;逐实例预检和 partial Job |
|
||||
| New instance | 实例与认证 | L,X,A,U/unknown capability | 配置专用保存/secret set | 连接与认证分阶段;失败可存 disabled 草稿;无 secret 回显 |
|
||||
| Overview | 设备系统、SIM、摘要 | L,E,S,X,U,A,P | restart service/system R3 | 摘要深链;重启进入 Job,不在断连时假报成功 |
|
||||
| Cellular | 蜂窝、射频、数据连接 | L,E,S,X,U,A,P | data/roaming R1;register/lock/baseband R2/3 | 当前值→目标值;断连影响;验证后状态 |
|
||||
| Device Network | interfaces/DDNS/WLAN | L,E,S,X,U,A,P | config R1;connect/forget R2 | 扫描空与 unsupported 区分;变更后 connectivity 验证 |
|
||||
| Messages | SMS | L,E,S,X,U,A,P | send R2(默认 Job,`syncSafe` 例外);delete/clear R3 Job | 分页/会话;号码校验;删除数量强确认 |
|
||||
| Calls | Calls/IMS/voicemail | L,E,S,X,U,A,P | dial/answer/hangup/settings R1/2;history delete R3 Job | 实时与历史分离;拨号超时不重放;记录删除复述目标/数量并落 Job、刷新历史及脱敏 audit |
|
||||
| eSIM | work mode/eSIM/lpac/eUICC/profile | L,E,S,X,U,A,P | config R1;download/enable R2;delete R3 | profile 身份可见;删除复述;完成后列表与连接核实 |
|
||||
| Notifications | config/test/logs/queue | L,E,S,X,U,A,P | config R1;test/retry R2(默认 Job,`syncSafe` 例外);clear/delete R3 Job | channel 级反馈;队列逐项;清理确认数量 |
|
||||
| Automation | config/test/logs | L,E,S,X,U,A,P | config R1;test R2(默认 Job,`syncSafe` 例外);clear R3 Job | schema 表单;测试与正式任务明确区分 |
|
||||
| OTA | status/release/upload/prepare/apply/cancel | L,E,S,X,U,A,P | prepare R2;apply R3 | 流式进度/校验;版本重现判完成;可取消性明确 |
|
||||
| Jobs | jobs/items/attempts | L,E,S,X,P | cancel 默认 R1(可提高);retry 继承原风险且最低 R2、创建新 Job | 重启恢复;终态不可变;失败项选择重试;新 jobId+attemptId 与 retry/root/source lineage 关联 |
|
||||
| Job Detail `/jobs/:jobId` | jobs/items/events/audit links | L,E,S,X,P | cancel 默认 R1;retry 最低 R2 | 阶段、逐项、attempt 与关联 audit;不可取消/重试原因可见 |
|
||||
| Audit | audit | L,E,S,X | R0 查询/脱敏导出 | 过滤、详情、requestId/job 链路;敏感 canary 为零 |
|
||||
| Audit Detail `/audit/:eventId` | audit event/job/instance links | L,E,S,X | R0 | 参数脱敏摘要、阶段与上游错误;可往返 Job/实例且不展示 sensitiveFields |
|
||||
| Settings Instances | 实例/认证/导入 | L,E,S,X,A,P | read/health/auth-status R0;R1-S 会话;create/update R1;import R2 Job;delete R3 Job | revision、预览 diff、幂等导入、原文件不改;删除 deleting/tombstone 可追踪 |
|
||||
| Settings Instance Detail `/settings/instances/:id` | 实例/认证/secret reference | L,E,S,X,A,P | health/auth-status R0;verify/login/logout R1-S;save/reference 变更 R1;setup/password/delete R3 Job | preserve/set/clear 明确;revision 冲突;删除不可取消点与 Job/audit 可追踪 |
|
||||
| Settings System | settings/diagnostics | L,E,S,X | R1 | 范围校验;变更 revision;诊断结果可复制且脱敏 |
|
||||
|
||||
## 8. 内容与命名规范
|
||||
|
||||
- 对用户使用“实例”,不用“设备”代替配置实体;硬件信息仍称“设备”。
|
||||
- operation 显示业务名称,技术详情可附 `operationId`,不以 path 作为主标签。
|
||||
- 错误包含可行动说明、`errorCode`、`requestId`;原始上游正文默认不直接展示。
|
||||
- 时间同时提供绝对时间和相对时间;所有新鲜度以服务端时间为准。
|
||||
- destructive button 只用于真正 R3,不以颜色代替风险说明。
|
||||
- 空值显示“未知/未提供”,不使用同一个 `-` 混合 unknown、unsupported 和 empty。
|
||||
|
||||
## 9. 响应式与可访问性验收
|
||||
|
||||
| 宽度 | 主要策略 |
|
||||
|---|---|
|
||||
| 1440 | 完整全局侧栏 + 实例模块栏;Fleet 多列高密度 |
|
||||
| 1024 | 可折叠模块栏;非关键 Fleet 列按优先级隐藏 |
|
||||
| 768 | 全局导航抽屉;表格保留实例/状态/异常/操作,支持列面板 |
|
||||
| 390/320 | 不把完整表格强塞横向页面;采用行详情/关键字段堆叠,页面本身无横向溢出 |
|
||||
|
||||
所有宽度必须满足:焦点可见、dialog 焦点圈定并可返回触发器、错误与字段通过语义关联、图标有名称、状态不只依赖颜色、主要流程可键盘完成。
|
||||
|
||||
## 10. IA 阶段门禁
|
||||
|
||||
进入 Web 实现前必须满足:
|
||||
|
||||
- [ ] 全部上游 operation 在 OperationRegistry 中有且仅有一个主页面 owner。
|
||||
- [ ] 目标路由均有 loading/empty/stale/error/unsupported/auth/partial 设计或明确不适用理由。
|
||||
- [ ] 每个写动作有风险等级、确认形态和结果落点。
|
||||
- [ ] Fleet 默认列、排序、筛选和批量选择规则已形成组件/E2E 验收用例。
|
||||
- [ ] 不存在 iframe、任意 path、任意 JSON 或第二份 endpoint catalog 的产品入口。
|
||||
- [ ] 1440–320 的导航与数据降级策略可执行,而不是仅写“响应式”。
|
||||
@@ -0,0 +1,281 @@
|
||||
# 用户角色、任务与端到端流程
|
||||
|
||||
> 目标:以可执行用户任务约束页面、API、状态和风险交互,而不是从旧页面反推功能。
|
||||
|
||||
> Phase 0.1 文档导航:[返回项目章程](./project-charter.md)|[现状审计](./current-system-audit.md)|**角色与流程**(本文)|[信息架构](./information-architecture.md)
|
||||
|
||||
## 1. 角色定义
|
||||
|
||||
### P1 日常运维员
|
||||
|
||||
- **环境:** 管理 5–100 个 SimAdmin 实例,频繁查看在线、认证、信号、版本和任务结果。
|
||||
- **目标:** 快速发现异常,做低风险恢复或把高风险动作交给 Job。
|
||||
- **痛点:** 卡片难比较;空值无法判断不支持还是失败;切换设备时担心误操作。
|
||||
- **权限假设:** V1 是本机单操作者,不实现 RBAC;仍记录本地 actor/session 标识。
|
||||
- **成功指标:** 从 Fleet 进入异常实例并找到失败采集项不超过 3 次导航。
|
||||
|
||||
### P2 网络与设备工程师
|
||||
|
||||
- **环境:** 调整运营商注册、APN、频段/小区锁定、radio/data/roaming/airplane、WLAN/DDNS、eSIM。
|
||||
- **目标:** 在变更前看清当前值和 capability,变更后保留证据并能诊断失败。
|
||||
- **痛点:** 上游版本差异;网络切换会暂时断连;错误 raw JSON 难解释。
|
||||
- **成功指标:** 所有 R2/R3 变更都有预检、影响说明、明确目标和逐步结果。
|
||||
|
||||
### P3 系统维护者
|
||||
|
||||
- **环境:** 接入/移除实例、管理凭据、轮询参数、服务/系统重启和 OTA。
|
||||
- **目标:** 安全配置控制台,确保升级、数据和回滚可靠。
|
||||
- **痛点:** 密码保存语义含糊;大文件上传与重启无进度;配置失败可能产生半状态。
|
||||
- **成功指标:** 实例 CRUD、导入和凭据操作可预览且原子;OTA/重启可追踪。
|
||||
|
||||
### P4 审计与故障复盘者
|
||||
|
||||
- **环境:** 在故障后按时间、实例、操作和 requestId 查询。
|
||||
- **目标:** 重建操作链路,确定参数摘要、风险、执行结果及部分失败范围。
|
||||
- **痛点:** 临时提示消失;日志可能含敏感信息;批量动作只给总成功/失败。
|
||||
- **成功指标:** 单次操作从 audit 可跳到 Job 和逐实例 item,且不暴露 secret。
|
||||
|
||||
## 2. 共同交互契约
|
||||
|
||||
### 2.1 状态语言
|
||||
|
||||
所有页面使用相同术语:
|
||||
|
||||
- 实例:`在线`、`离线`、`需要认证`、`退化`、`未知`。
|
||||
- 新鲜度:`最新`、`陈旧`、`已过期`、`未知`,并显示最后成功时间。
|
||||
- 能力:`支持`、`不支持`、`需要认证`、`退化`、`未知`。
|
||||
- 请求:`空闲`、`加载中`、`成功`、`失败`。
|
||||
- 批量项:`排队`、`已跳过`、`执行中`、`成功`、`失败`、`已取消`。
|
||||
|
||||
`不支持`不得渲染成空值;`失败`不得清除最后成功数据,而应保留数据并标“陈旧”。领域/API 新鲜度枚举统一为 `fresh`,中文显示“最新”,禁止使用 API 枚举 `latest`;三个正交状态及组合规则以[项目章程 7.6](./project-charter.md#76-三个正交页面状态)为准。
|
||||
|
||||
### 2.2 写操作确认卡
|
||||
|
||||
所有 R1–R3 在提交前至少显示:
|
||||
|
||||
```text
|
||||
目标:实例名称 + 稳定 ID + URL origin
|
||||
操作:业务名称 + operationId
|
||||
当前值:结构化字段
|
||||
目标值:结构化字段
|
||||
变更:逐字段 diff
|
||||
风险:R1 / R2 / R3 + 具体影响
|
||||
支持性:capability 与上次探测时间
|
||||
执行方式:立即 / Job / 批量逐项
|
||||
```
|
||||
|
||||
R2/R3 还必须显示预检结果和失败阻断项;确认 token 的完整绑定、首次尝试即消费规则以[项目章程 7.3](./project-charter.md#73-确认令牌唯一规范)为唯一规范。若用户在确认期间切换实例、修改表单或配置发生变化,旧确认作废并回到预检。
|
||||
|
||||
执行政策统一为:R3 无例外全部创建 Job;R2 默认创建 Job。只有单目标 operation 被 OperationRegistry 显式标记 `syncSafe=true`,并登记同步安全理由及覆盖超时、结果落点和误重放的测试时,R2 才可同步执行;批量 R2 无论单项是否 `syncSafe` 都创建父 Job 和逐实例 item。
|
||||
|
||||
## 3. 关键流程
|
||||
|
||||
### WF-01 添加并验证实例
|
||||
|
||||
**角色:** P3<br>
|
||||
**入口:** `/instances/new` 或 `/settings/instances` 的“添加实例”
|
||||
|
||||
1. 输入稳定 ID、名称、URL、描述、标签和认证模式。
|
||||
2. URL 在客户端做格式提示,服务端执行权威 URL/SSRF 校验。
|
||||
3. 凭据选择 `不保存/设置`;编辑时必须明确 `保留/设置/清除`,不能用空密码猜语义。
|
||||
4. 点击“测试连接”,BFF 先以严格只读 R0 返回 health、延迟、上游版本、auth-status 和 capability 初探;不得在 R0 请求中夹带凭据。
|
||||
5. 若需要密码,可另行使用 R1-S credential verify/login 专用会话流程提交一次性临时密码;按 actor+session+instance 限速并只审计无 secret 元数据,绝不自动重放。除非另行选择本地 SecretStore reference“设置”,临时密码不保存;设备 `setup/password` 改动只能走 R3 专用 Job。
|
||||
6. 测试结果按阶段显示,不把 health 成功等同于认证成功。
|
||||
7. 保存后事务写入实例与 tags,secret 仅保存 reference;client registry reconcile。
|
||||
8. 成功跳转 `/instances/:id/overview`;reconcile pending 时显示持久退化提示并允许重试。
|
||||
|
||||
**分支与恢复:**
|
||||
|
||||
- URL/SSRF 拒绝:字段级错误,禁止发起上游连接。
|
||||
- 不可达/超时:允许保存为 disabled 草稿,默认不进入轮询;不得误标在线。
|
||||
- 401:显示“可达,需要认证”,允许重新输入临时密码。
|
||||
- 不支持 capability endpoint:保存实例,但导航只展示已确认模块和未知待探测模块。
|
||||
- ID 重复/revision 冲突:保留输入,提示选择新 ID 或刷新。
|
||||
|
||||
**验收:** 密码不出现在响应、地址栏、audit 参数、日志;保存成功后 Fleet 只有一个该 ID。
|
||||
|
||||
### WF-02 Fleet 定位异常
|
||||
|
||||
**角色:** P1<br>
|
||||
**入口:** `/fleet`
|
||||
|
||||
1. 默认表格展示名称/ID、总状态、认证、版本、标签、运营商、信号、关键异常、最后成功时间、延迟。
|
||||
2. 用户按 `离线/需要认证/退化/陈旧` 筛选,可叠加标签、版本、运营商、能力和文本搜索。
|
||||
3. 列排序和列显隐保留本地偏好;默认异常优先,卡片仅为可选视图。
|
||||
4. 点击异常单元格进入实例对应模块或状态详情,而不只进入泛化详情。
|
||||
5. 自动状态通过 SSE 更新;手动刷新请求 scheduler,不直接从浏览器扇出上游请求。
|
||||
|
||||
**分支与恢复:**
|
||||
|
||||
- 初始 loading:表格骨架并保留列头,不显示“0 台”。
|
||||
- 整体刷新失败:保留上次数据并显示 stale banner、最后成功时间、requestId 和重试。
|
||||
- 单实例失败:只标该行;其他行仍更新。
|
||||
- 无匹配:区分未配置、搜索无结果和筛选无结果,提供对应动作。
|
||||
|
||||
**验收:** 使用[项目章程 8.4](./project-charter.md#84-可重复验收协议与冻结责任)的确定性 100 实例 fixture、计时与导航口径;筛选后目标位于首个 viewport,或最多一跳分页/行详情可见,不允许逐卡/逐行滚动寻找;部分失败不阻断成功行。
|
||||
|
||||
### WF-03 查看实例与能力导航
|
||||
|
||||
**角色:** P1/P2<br>
|
||||
**入口:** `/instances/:id/overview`
|
||||
|
||||
1. 顶部固定显示实例身份、总状态、认证、新鲜度和安全“原站打开”链接。
|
||||
2. 左侧/窄屏模块导航由 capability 决定:supported 可进入;degraded 带标记;unsupported 默认隐藏但可在“能力详情”查看原因;unknown 显示探测中/重试。
|
||||
3. overview 展示设备、SIM、蜂窝、系统资源和关键异常摘要,各卡链接到对应模块。
|
||||
4. 切换实例后,旧 instanceId 的请求被取消或因 owner/query key 不匹配而丢弃。
|
||||
|
||||
**验收:** 404 capability 不展示为 `-`;刷新页面可恢复相同实例与模块路由。
|
||||
|
||||
### WF-04 执行 R1 可逆设置
|
||||
|
||||
**例:** 开关 data、roaming、WLAN 或修改通知配置。<br>
|
||||
**角色:** P1/P2
|
||||
|
||||
1. 在业务页读取当前值和 `fetchedAt`。
|
||||
2. 编辑结构化表单;字段按 schema 校验。
|
||||
3. 提交前显示目标实例、当前→目标 diff、R1 影响。
|
||||
4. 若当前值 expired,则要求刷新或明确“基于陈旧数据继续”。
|
||||
5. 用户确认后 BFF 重新校验 operation、capability、revision 和请求 schema。
|
||||
6. 执行并写 audit;页面刷新目标资源并显示成功值。
|
||||
|
||||
**失败恢复:** 409/revision 变化重新加载 diff;401 引导登录;超时显示“结果未知”并通过后续读取核实,不直接宣称失败回滚。
|
||||
|
||||
### WF-05 执行 R2/R3 单实例操作
|
||||
|
||||
**例:** operator register、band/cell lock、baseband restart、eSIM profile enable/delete、系统重启、OTA apply。<br>
|
||||
**角色:** P2/P3
|
||||
|
||||
1. 用户在专用业务页选择动作和参数。
|
||||
2. BFF `prepare` 执行 capability、认证、当前状态、参数、冲突 Job 和可恢复性预检。
|
||||
3. UI 展示确认卡。R3 要求复述目标(如输入实例 ID 或 profile 名称)并说明不可逆/中断影响。
|
||||
4. `prepare` 只创建短期 Preparation;确认入口按章程规则消费一次性 token 后才创建 Job。R3 创建 Job,R2 默认创建 Job,返回 202 和稳定 jobId。仅满足 2.2 所述 OperationRegistry `syncSafe` 例外的单目标 R2 可同步返回结构化结果。
|
||||
5. Job 跳转或展开 `/jobs/:jobId` 并由 SSE 更新阶段和日志摘要;同步安全 R2 则在原业务页显示结果并关联 audit。
|
||||
6. Job 完成后刷新受影响资源,audit 关联 jobId/requestId。
|
||||
|
||||
**失败恢复:**
|
||||
|
||||
- capability/revision/实例切换:token 作废,重新 prepare。
|
||||
- 执行中断连:Job 保持 running/unknown-result,按 operation 策略轮询验证,不重复执行非幂等动作。
|
||||
- 可重试失败:只开放 registry 声明的 retry;重新预检/确认且不复用旧 token,随后创建具有新 `jobId` 和独立 Attempt 的新 Job。新 Job 的 `retryOfJobId` 指向直接来源,`rootJobId` 指向最初 Job;来源 Job 及其 Attempt/事件保持终态不可变。Job/Attempt/Item 以[项目章程 7.4](./project-charter.md#74-preparationjob-与-attempt-固定模型)为准。
|
||||
|
||||
### WF-06 批量操作
|
||||
|
||||
**角色:** P1/P3<br>
|
||||
**入口:** `/fleet` 多选;只显示 registry 标记 `batchable` 的动作。
|
||||
|
||||
1. 用户多选实例并选择业务动作。
|
||||
2. 系统逐实例预检 capability、认证、当前值和冲突 Job。
|
||||
3. 确认页分组显示“将执行 N、将跳过 M”,每个跳过项有原因。
|
||||
4. 用户确认后创建父 Job 和逐实例 items;并发受全局/实例上限控制。
|
||||
5. `/jobs/:jobId` 展示逐项 queued/running/succeeded/failed/skipped/cancelled。
|
||||
6. 取消只影响尚未开始或 registry 声明可取消的 item。
|
||||
7. 重试创建新的父 Job,只复制用户明确选择且 registry 判定可重试的失败 items;每个新 item 以 `sourceJobItemId` 指向来源 item。成功项绝不复制或重放,未选择/不可重试/skipped 项不进入新父 Job。新父 Job 使用新 `jobId`、独立状态和 Attempt,并通过 `retryOfJobId`/`rootJobId` 保留 lineage;SSE/audit 关联新 `jobId + attemptId + jobItemId` 并保留来源关联。
|
||||
|
||||
**验收:** 3 成功、1 失败、2 不支持的结果必须为 `partially-succeeded`,不能显示绿色“全部成功”。
|
||||
|
||||
### WF-07 短信与通话
|
||||
|
||||
**角色:** P1<br>
|
||||
**页面:** `/instances/:id/messages`、`/instances/:id/calls`
|
||||
|
||||
- 短信:统计/列表/会话 → 发送(号码与内容校验)→ 结果;删除消息/会话/批量清空按 R3 强确认并列出数量。
|
||||
- 通话:实时通话 → 拨号/接听/挂断 → 状态;音量/转移/设置结构化编辑;历史、IMS、voicemail 独立区域。通话记录支持单条和批量删除,均为 R3:确认页列出实例、记录 ID/对端号码、时间及数量,要求复述实例 ID(批量时同时复述数量),全部创建 Job;结果落到 `/jobs/:jobId` 的逐项结果并刷新通话历史,audit 仅保存脱敏号码摘要、数量、jobId/requestId,不保存敏感通话内容。
|
||||
- 实时状态与历史查询分离;拨号超时不自动重复。
|
||||
|
||||
**验收:** 发送或拨号时目标实例和号码始终可见;短信及通话记录删除不由通用 JSON 完成;通话记录删除必须有 R3 强确认、Job 详情和关联 audit 可追踪。
|
||||
|
||||
### WF-08 eSIM 生命周期
|
||||
|
||||
**角色:** P2<br>
|
||||
**页面:** `/instances/:id/esim`
|
||||
|
||||
1. 查看 work mode、eSIM config、lpac、eUICC 和 profiles capability。
|
||||
2. 下载 profile 时校验激活信息并进入 R2 Job。
|
||||
3. enable/rename 使用专用表单;切换工作模式说明连接影响。
|
||||
4. delete 为 R3:展示 ICCID/名称/状态,要求复述 profile,确认不可恢复。
|
||||
5. 完成后重新获取 profile 和 connectivity;失败保留上次 profile 数据并标 stale。
|
||||
|
||||
### WF-09 OTA
|
||||
|
||||
**角色:** P3<br>
|
||||
**页面:** `/instances/:id/ota`
|
||||
|
||||
1. 查看当前版本、commit、latest release、OTA 状态和 compatibility。
|
||||
2. 选择在线准备或本地包上传;上传使用专用流式接口并显示字节进度、校验和与取消。
|
||||
3. prepare 完成后显示包版本、目标版本、空间/电量/连接等上游可得预检。
|
||||
4. apply 为 R3 Job;确认中说明服务中断和重连窗口。
|
||||
5. 应用后以版本/health 重现作为完成证据;仅 HTTP 断开不能判成功。
|
||||
6. cancel 仅在上游状态允许时出现。
|
||||
|
||||
**验收:** 50MB body 不进入普通 JSON proxy、audit 正文或浏览器内存字符串化流程。
|
||||
|
||||
### WF-10 Job 跟踪、取消与重试
|
||||
|
||||
**角色:** P1/P3<br>
|
||||
**入口:** `/jobs`、`/jobs/:jobId`
|
||||
|
||||
1. 按状态、operation、实例、风险、时间筛选。
|
||||
2. 查看父 Job、items、阶段、尝试次数、requestId、耗时和最近事件。
|
||||
3. 可取消性由 operation/job 当前阶段决定;按钮不可用时解释原因。
|
||||
4. 重试先展示将重试的 items 和参数摘要;非幂等动作必须重新核实目标状态。
|
||||
5. 用户确认重试后创建新 Job,而不是重新打开来源 Job 或向其追加 Attempt;详情同时展示直接来源和根 Job lineage。
|
||||
6. 进程重启后 queued/running 任务按恢复策略标记并继续或进入需人工核实状态;已终态 Job、Attempt 和事件不得改变。
|
||||
7. Job 尚未终态时,仅幂等且 registry 明确允许的网络级内部重试可在同一 Attempt 内按次数/deadline/backoff 上限执行,并记录每次 transport try;非幂等操作及任何用户发起的 retry 均不得使用该机制。
|
||||
|
||||
### WF-11 审计与复盘
|
||||
|
||||
**角色:** P4<br>
|
||||
**入口:** `/audit`、`/audit/:eventId`
|
||||
|
||||
1. 按实例、operation、风险、结果、actor、requestId、jobId 和时间检索。
|
||||
2. 列表显示时间、目标、动作、风险、结果、耗时。
|
||||
3. 详情显示参数脱敏摘要、body digest、准备/确认/执行阶段、上游 HTTP/错误摘要。
|
||||
4. 从 audit 跳到 Job/实例模块;从 Job 返回关联 audit。
|
||||
5. 导出时沿用相同 redaction,不导出 password、cookie、token、短信正文等策略定义的敏感内容。
|
||||
|
||||
### WF-12 认证失效恢复
|
||||
|
||||
**角色:** P1/P3
|
||||
|
||||
1. 任一模块收到 401 时,将相关 capability 标 `auth-required`,但保留上次成功数据并标 stale。
|
||||
2. 顶部显示持久认证提示;用户可用保存凭据重登或输入一次性临时密码。
|
||||
3. 登录成功只重试安全的 R0 查询;不得自动重放写操作或 R1-S 会话操作。
|
||||
4. 返回原模块;原 R1–R3 草稿可保留,但必须重新 prepare/确认。
|
||||
|
||||
### WF-13 两阶段删除实例
|
||||
|
||||
**角色:** P3;**入口:** `/settings/instances/:id`
|
||||
|
||||
1. R3 prepare 展示 stable ID、canonical origin、关联 Job/审计保留范围及将销毁的 secret reference;确认消费 token 后创建删除 Job,并在同一事务把实例从 `active|disabled` 标为 `deleting`。
|
||||
2. `deleting` 后立即停止新轮询、R1-S 会话操作和所有新写操作,导航/Fleet 默认隐藏;Job 保存完成删除所需的不可变配置 snapshot 与 secret reference(不复制 secret)。
|
||||
3. **不可取消点**定义为删除 Attempt 首次开始销毁 adapter/session 或 secret 的原子步骤;此前取消可回到原 `active`/`disabled`,此后取消按钮永久禁用,系统必须继续清理或进入人工处理。
|
||||
4. 不可取消点后先停止并删除 adapter/client/session,再销毁 SecretStore secret,随后清除业务配置并转为脱敏 `tombstoned`;tombstone 仅保留 stable ID/脱敏名称摘要、删除时间/actor、最终结果及 jobId/attemptId/audit 链路,不含 origin、凭据或业务 snapshot。
|
||||
5. `purged` 是后续保留期/管理员策略对 tombstone 业务字段的清理标记;purge 只清业务配置/残余脱敏展示字段,永不删除或改写审计记录。
|
||||
|
||||
**失败恢复:** 不可取消点前失败,事务恢复原 `active`/`disabled` 并恢复 scheduler;不可取消点后失败保持 `deleting`、禁止操作并标人工处理。若 registry 允许用户重试,须先核实 adapter/session/secret 各步骤状态,再创建具备 lineage 的新 Job;来源 Job/Attempt 保持终态不可变,且绝不重建或猜测 secret。
|
||||
|
||||
## 4. 页面状态验收模板
|
||||
|
||||
每个业务页至少验收以下状态:
|
||||
|
||||
| 状态 | 必须行为 |
|
||||
|---|---|
|
||||
| 初始 loading | 保持页面结构和标题,控件禁用,不伪造空数据 |
|
||||
| success/fresh | 显示值、采集时间和可用操作 |
|
||||
| empty | 解释“确实为空”及下一动作,与 unsupported 区分 |
|
||||
| stale | 保留最后数据,显示最后成功时间、失败原因、刷新入口 |
|
||||
| error | 持久错误、requestId、重试;不只 toast |
|
||||
| unsupported | 能力解释、探测证据/版本;不显示可执行控件 |
|
||||
| auth-required | 登录入口;不清空历史数据;不自动重放写请求 |
|
||||
| degraded/partial | 标出失败子项,成功子项仍可用 |
|
||||
| forbidden by risk/policy | 解释政策或前置条件,不提供通用代理绕过 |
|
||||
|
||||
## 5. 跨流程验收场景
|
||||
|
||||
1. **实例切换竞态:** A 页面慢请求未完成时切到 B;A 响应不得写入 B 页面或触发 B 的确认。
|
||||
2. **配置 revision 变化:** prepare 后修改实例 URL;旧 token 执行必须失败。
|
||||
3. **部分失败:** 批量 6 台中 2 台 unsupported、1 台 timeout;结果逐项保留且父 Job 为 partial。
|
||||
4. **认证过期:** R2 prepare 后会话过期;执行不得自动登录后裸重放,必须重新预检/确认。
|
||||
5. **陈旧数据:** 刷新失败时显示上次成功值和时间;用户能区分“当前未知”与“上次值”。
|
||||
6. **重启恢复:** Job running 时重启 BFF;恢复后不重复执行非幂等上游操作,并提示核实状态。
|
||||
7. **敏感数据:** 用 canary password/token/cookie 执行所有流程;响应、日志、audit、snapshot、fixture 和导出均无 canary。
|
||||
@@ -0,0 +1,222 @@
|
||||
# API-first Multi SimAdmin 项目章程
|
||||
|
||||
> 文档状态:Phase 0.1 基线<br>
|
||||
> 冻结日期:2026-07-15<br>
|
||||
> 上游证据基线:[`3899/SimAdmin@58e220411d6599609f0eeda01eb7016e9212f970`](https://github.com/3899/SimAdmin/commit/58e220411d6599609f0eeda01eb7016e9212f970)<br>
|
||||
> 适用范围:Multi SimAdmin V1 的产品、API/BFF、Web 控制台、测试与迁移决策
|
||||
|
||||
> Phase 0.1 文档导航:**项目章程**(本文)|[现状审计](./current-system-audit.md)|[角色与流程](./personas-and-workflows.md)|[信息架构](./information-architecture.md)
|
||||
|
||||
## 1. 立项结论
|
||||
|
||||
现有产品是“状态卡片墙 + 原站 iframe + 手工 API 请求框”,不能支撑多实例日常管理。项目从产品信息架构和控制面契约开始重构,建设一个 **API-first、能力感知、安全可确认、任务可追踪、操作可审计** 的多实例 SimAdmin 控制台。
|
||||
|
||||
“API-first”在本项目中的可验收含义是:
|
||||
|
||||
1. 所有核心管理能力由明确建模的控制面 API 提供,不以 iframe 作为功能依赖。
|
||||
2. 固定上游基线的每个业务操作均进入 OperationRegistry,状态只能是“已支持、版本不支持或有理由暂缓”,不得静默遗漏。
|
||||
3. 用户通过业务页面和结构化表单完成操作,不手写 HTTP 方法、路径和任意 JSON。
|
||||
4. UI、BFF 授权、能力目录、OpenAPI 和契约测试共享同一操作事实源。
|
||||
5. 所有改变设备状态的请求都经过风险分级;R3 全部 Job 化;R2 默认 Job 化,仅允许 OperationRegistry 对经理由与测试证明可同步安全执行的单目标操作显式声明例外;批量 R2 仍全部 Job 化。R2/R3 均经过预检、专用确认和审计。
|
||||
|
||||
## 2. 问题陈述
|
||||
|
||||
当前系统存在以下不可通过局部美化解决的问题:
|
||||
|
||||
- 主要路由、DOM 拼接、状态和异步行为集中于 `public/app.js`,模块边界与可测试性不足。
|
||||
- 默认卡片墙强调装饰而非异常定位、筛选、比较和批量管理。
|
||||
- iframe 会被上游 X-Frame-Options、CSP、跨站 Cookie 阻断,无法作为可靠入口。
|
||||
- API 工作台把方法、路径和 JSON 正确性转嫁给操作者,且无法表达字段约束、影响范围与兼容性。
|
||||
- endpoint 分散在前端、catalog、proxy policy 和 status service,存在漂移。
|
||||
- 当前代理只覆盖 36 个读路径、15 个写路径;上游基线确认有 100 个路径、117 个业务操作(GET 50、POST 62、DELETE 5)。
|
||||
- 缺少 capability、版本差异、部分失败、陈旧数据、Job、审计和历史状态的一致模型。
|
||||
|
||||
## 3. 产品愿景与原则
|
||||
|
||||
### 3.1 愿景
|
||||
|
||||
操作者无需理解 SimAdmin 内部 endpoint,即可从一个高密度控制台发现异常、进入正确模块、安全修改单台或多台实例,并能回答“谁在何时对哪台实例做了什么、结果如何”。
|
||||
|
||||
### 3.2 强制原则
|
||||
|
||||
- **异常优先:** Fleet 默认表格按不可达、认证失败、退化、陈旧等异常可见且可筛选。
|
||||
- **能力驱动:** 不支持的能力不显示为可点击动作;必要时保留解释性占位,不用 `-` 假装正常。
|
||||
- **目标明确:** 每个写操作都显示实例、当前值、目标值、字段 diff、影响和风险。
|
||||
- **默认拒绝:** 未注册 operation、未知方法/路径、认证端点通用代理均拒绝。
|
||||
- **部分结果真实:** 批量动作逐实例给出成功、失败、跳过及原因,不制造“全部成功”。
|
||||
- **凭据服务端化:** 密码、Cookie、token 不进入浏览器响应、日志、审计或 fixture。
|
||||
- **高密度但可访问:** 主要工作流兼容键盘和窄屏;视觉不能牺牲错误、焦点和操作可达性。
|
||||
|
||||
## 4. 目标与验收指标
|
||||
|
||||
| 编号 | V1 目标 | 验收证据 |
|
||||
|---|---|---|
|
||||
| G1 | 多实例可发现、可筛选、可比较 | `/fleet` 支持搜索、状态/认证/版本/标签/运营商/信号/异常筛选,支持排序、列显隐、分页、多选 |
|
||||
| G2 | 完整掌握上游能力 | 固定 commit 的 117 个操作在 registry parity 测试中 117/117 对齐,无重复 operationId |
|
||||
| G3 | 核心操作不依赖 iframe/手写 JSON | 13 个上游模块均有结构化业务入口;原站仅为 `noopener noreferrer` 新窗口链接 |
|
||||
| G4 | 状态可诊断 | UI/API 区分 online、offline、auth-required、degraded、unknown,以及 fresh、stale、loading、error、unsupported、partial |
|
||||
| G5 | 写操作安全 | R1 显示字段 diff;R2/R3 有预检、绑定目标的一次性确认令牌、结果追踪和审计;R3、批量 R2 及无同步安全例外的 R2 有 Job 证据 |
|
||||
| G6 | 多实例请求受控 | scheduler 具备全局/单实例并发、TTL、jitter、backoff、deadline、取消和熔断;页面刷新不扇出 117 个请求 |
|
||||
| G7 | 凭据不泄漏 | secret redaction 契约与安全测试证明 password/cookie/token 不出现在响应、日志、audit、snapshot、fixture |
|
||||
| G8 | 真实可用 | Phase 8 在冻结的真实实例版本/响应基线上逐模块完成读验收,并完成 cellular 至少一个 R1、Notifications channel test、Automation task test,以及专用测试实例上的 service restart(R3)真实写验收;不安全的 OTA apply、删除/通话类动作允许契约+模拟,但必须明确标为模拟,不得计入真实写 |
|
||||
| G9 | 响应式与可访问 | 可见 Chrome 在 1440、1024、768、390、320 宽度无页面横向溢出、遮挡、重叠或不可达动作;关键流程键盘可完成 |
|
||||
| G10 | 可安全切换 | 新旧并行、只读 shadow comparison、数据库备份、独立端口和一键回滚均有演练记录 |
|
||||
|
||||
## 5. 范围
|
||||
|
||||
### 5.1 V1 范围内
|
||||
|
||||
1. 实例添加、编辑、删除、标签、连接测试、认证测试、会话登录/登出。
|
||||
2. Fleet 汇总、状态采集、搜索筛选、异常定位、多选和受控批量动作。
|
||||
3. 实例模块:overview、cellular、device-network、messages、calls、esim、notifications、automation、ota。
|
||||
4. 跨实例模块:jobs、audit、settings/instances、settings/system。
|
||||
5. 117 个上游操作的 registry、schema、风险、超时、兼容性和 UI 策略建模。
|
||||
6. 控制面 `/api/v1`、OpenAPI 3.1、统一错误、分页/排序/filter、revision、partial result。
|
||||
7. Job、逐实例 job item、取消/重试、重启恢复;审计与敏感字段脱敏。
|
||||
8. SQLite 持久化,macOS Keychain 优先保存 secret reference;旧配置预览后幂等导入。
|
||||
9. SSE 推送实例状态、Job 和 audit 摘要。
|
||||
10. 单元、契约、集成、组件、E2E、安全、可访问性和真实浏览器验收。
|
||||
|
||||
### 5.2 V1 非目标
|
||||
|
||||
- 公网 SaaS、租户隔离和多用户 RBAC。
|
||||
- 任意 REST 客户端、任意 URL/路径代理或插件脚本执行器。
|
||||
- iframe 集成、复制上游完整页面、自由拖拽 dashboard。
|
||||
- 复杂告警规则引擎、短信/语音业务运营平台。
|
||||
- 跨地域 HA、集群调度和云端 secret vault。
|
||||
- 未经 registry 声明的“专家模式”绕过风险确认。
|
||||
- 在 Phase 0.1 修改生产代码、删除旧实现、切换 8788 或迁移真实数据。
|
||||
|
||||
## 6. 用户与核心任务
|
||||
|
||||
| 用户 | 首要任务 | 成功判据 |
|
||||
|---|---|---|
|
||||
| 日常运维员 | 找到异常实例并恢复连接/服务 | 2 分钟内从 Fleet 定位异常,进入正确模块,看到结果或 Job |
|
||||
| 网络/设备工程师 | 诊断蜂窝、射频、WLAN、APN、eSIM | 当前值、能力、版本、采集时间和错误证据同时可见;高风险变更可回溯 |
|
||||
| 系统维护者 | 配置实例、控制轮询、升级、排障 | 实例 CRUD 和导入不泄漏凭据;OTA/重启有预检、任务与回滚提示 |
|
||||
| 审计/故障复盘者 | 解释一次变更及部分失败 | 可按时间、实例、operation、风险、结果、requestId 检索完整摘要 |
|
||||
|
||||
详细流程见 [personas-and-workflows.md](./personas-and-workflows.md)。
|
||||
|
||||
## 7. 状态与风险政策
|
||||
|
||||
### 7.1 实例汇总状态
|
||||
|
||||
优先级从高到低:
|
||||
|
||||
1. `offline`:health/network 不可达或 deadline 超时。
|
||||
2. `auth-required`:可达但认证缺失、失效或 401。
|
||||
3. `degraded`:可达且可认证,但一个或多个关键采集项失败/熔断。
|
||||
4. `online`:关键采集项成功,且未超过 freshness TTL。
|
||||
5. `unknown`:尚未采集、版本/响应无法判定或初始化中。
|
||||
|
||||
汇总状态不得覆盖采集项状态;详情必须展示每项的 `fetchedAt`、`duration`、`freshness`、`support`、`errorCode` 和 HTTP status。
|
||||
|
||||
### 7.2 风险等级
|
||||
|
||||
| 等级 | 定义 | 最低交互/执行要求 |
|
||||
|---|---|---|
|
||||
| R0 | 严格只读查询,包括 health 与 auth-status;不得提交凭据或改变服务端会话 | 无确认;受 scheduler、timeout、cache 控制 |
|
||||
| R1 | 可逆设置 | 结构化校验,显示当前值→目标值和目标实例,显式提交,写入 audit |
|
||||
| R1-S | **R1 的会话敏感子类,不是第五个风险等级**:credential verify(提交一次性临时密码)、login、logout;只改变专用认证会话,不改变设备 setup/password | 仅走专用会话 BFF;按 actor+session+instance 限速;审计 actor/session、实例、operation、时间、结果、requestId 等元数据但绝不记录 secret/凭据正文或摘要;超时、401、重连后均禁止自动重放 |
|
||||
| R2 | 服务影响 | capability/认证/当前状态预检,专用确认,一次性 token,默认 Job 化,逐实例结果;仅单目标 operation 可由 OperationRegistry 显式标记 `syncSafe=true` 例外,且必须同时记录理由并有同步执行安全性/超时/结果落点测试;批量 R2 不允许例外,仍全部 Job 化 |
|
||||
| R3 | 破坏性或高危 | R2 全部确认与预检要求 + 强确认文案/目标复述 + 不可逆或中断说明 + 失败恢复建议;无同步例外,全部 Job 化 |
|
||||
|
||||
认证 `setup/password` 等设备凭据改动是 R3 专用流程并全部 Job 化,不进入通用上游代理;待 Phase 0.2 固定上游请求/响应形态后只能细化 schema、前置条件和恢复步骤,不得降级为 R1-S 或通用代理。实例配置中的本地 SecretStore reference `set/preserve/clear` 仍是 R1,必须与设备密码改动明确分开。
|
||||
|
||||
### 7.3 确认令牌唯一规范
|
||||
|
||||
本节是 V1 确认令牌的唯一规范;其余 Phase 0.1 文档只能引用本节,不得维护字段子集。R2/R3 `prepare` 生成的短期一次性 token 必须不可篡改地绑定:`actorId`、控制台 `sessionId`、实例 stable ID、实例 config revision、canonical origin、`operationId`、HTTP method、canonical path/query、request body digest、规范化 content type、expiry 和高熵 nonce。
|
||||
|
||||
- token 只允许提交一次且短期有效;服务端在**第一次执行尝试入口即原子消费**,即使随后发生字段 mismatch、过期、revision 改变或执行失败也不得恢复或复用。
|
||||
- canonical path/query 必须使用单一编码/排序规则;执行端重新计算全部绑定字段并常量时间比较摘要。目标、表单、actor/session 或配置变化均要求重新 prepare。
|
||||
- token 不提供幂等重放语义;网络结果未知时读取目标状态或创建人工核实结果,不自动再次执行。token 本身及 secret 不写日志/audit。
|
||||
|
||||
### 7.4 Preparation、Job 与 Attempt 固定模型
|
||||
|
||||
`prepare` 先创建独立、短期的 **Preparation** 记录(`prepared | consumed | expired | invalidated`),保存脱敏预检结果和确认令牌引用;它不是 Job。确认请求原子消费 token/Preparation 后,才允许为需 Job 化的操作创建 Job,因而 Job 状态机不含 `preflighting` 或 `awaiting-confirmation`。
|
||||
|
||||
- Job 有稳定 `jobId`,状态为 `queued | running | cancelling | succeeded | partially-succeeded | failed | cancelled | unknown-result`。状态机只允许 `queued → running | cancelled`、`running → succeeded | partially-succeeded | failed | unknown-result | cancelling`、`cancelling → cancelled | failed | unknown-result`;`succeeded | partially-succeeded | failed | cancelled | unknown-result` 均为终态。Job 一旦进入终态,状态、结果、JobItems、Attempts 和事件历史严格不可变,不得以 retry、恢复或对账改写。
|
||||
- 每次实际执行是一个有稳定 `attemptId` 的 Attempt;Attempt 的参数摘要、transport tries、事件和结果一经写入即不可变。**用户发起的 retry 永远创建新 Job**:新 Job 使用新 `jobId`、独立状态和独立 Attempt,`retryOfJobId` 指向本次重试的直接来源 Job,`rootJobId` 指向 lineage 中最初 Job;来源 Job 不新增 Attempt、不重新打开终态。
|
||||
- 批量父 Job 包含稳定 JobItems。重试失败 items 时创建新的父 Job,只复制用户明确选择且 OperationRegistry 判定可重试的来源 items;每个新 JobItem 使用新 `jobItemId` 并以 `sourceJobItemId` 关联直接来源。成功项绝不复制、重放或因父 Job 重试而执行;未选择、不可重试和 skipped 项也不进入新父 Job。
|
||||
- 新 Job 的 SSE 事件与 audit 执行记录关联新 `jobId + attemptId`,批量项事件还关联新 `jobItemId`;同时记录 `retryOfJobId`、`rootJobId` 和适用的 `sourceJobItemId`,使 lineage 可双向追踪。Preparation/audit 可用 preparationId 关联,但不能伪装成 Job 阶段。
|
||||
- 仅在 Job 尚未终态时,registry 明确允许的网络级内部重试才可在**同一 Attempt**内有限执行;必须受次数/deadline/backoff 政策约束并逐次记录 transport try。该机制不得用于非幂等操作,也不得被 UI/user retry 调用;网络结果未知的非幂等操作进入 `unknown-result` 或人工核实,不自动重放。
|
||||
|
||||
### 7.5 已登记实例 origin 与 SSRF/LAN 授权
|
||||
|
||||
出站 adapter 只能连接管理员显式创建且通过校验的实例 canonical origin;任何页面参数、redirect、上游正文和 operation 参数都不能改变目标。仅允许 `http`/`https`,authority 必须包含明确端口(省略时规范化为协议默认端口),拒绝 userinfo、fragment、空/歧义 hostname、非规范或歧义 IP 写法(包括整数/八进制/十六进制 IPv4、混合编码与 zone-id)。
|
||||
|
||||
V1 因本机控制台用途,默认允许 loopback 与 RFC1918 LAN 地址,但**仅限已登记 origin 的精确 scheme+host+port authority**,绝不允许借此扫描任意 loopback/LAN 端口。默认拒绝 link-local、unspecified、multicast、broadcast、云 metadata 地址、公网 IP,以及 IPv4-mapped IPv6、NAT64、6to4/Teredo 等转换/绕过类别;公网放行只能是未来显式政策,V1 不实现。IPv6 私网若未来支持也必须显式列入策略,不能由“非公网”推断允许。
|
||||
|
||||
- hostname 每次连接前解析,所有 A/AAAA 结果必须全部属于允许类别;解析结果与实例 revision 一起用于连接,并将实际 socket 目标固定为已验证地址,连接建立时再次验证实际 peer address,禁止 DNS rebinding。
|
||||
- redirect 默认禁用;若某 operation 经 registry 明确需要,逐跳限制次数并重新执行完整校验,且每跳必须与登记 canonical origin 同 origin,不能用 redirect 改 authority。
|
||||
- transport 忽略 `HTTP_PROXY`、`HTTPS_PROXY`、`ALL_PROXY`、`NO_PROXY` 等环境代理;HTTP `Host` 与 TLS SNI 必须由登记 origin 构造并与其一致,不接受调用者覆盖。
|
||||
- 安全测试矩阵至少覆盖允许的已登记 loopback/RFC1918 精确端口,以及未登记端口、协议/userinfo/fragment、各类非规范 IPv4、IPv6 转换、link-local/unspecified/multicast/broadcast/metadata/公网、混合 DNS 结果、DNS rebinding、redirect 跨 origin、代理环境变量和 Host/SNI 覆盖;拒绝用例必须证明未产生外连。
|
||||
|
||||
### 7.6 三个正交页面状态
|
||||
|
||||
页面状态固定为三个不可互相覆盖的维度:请求执行 `idle/loading/success/error`、数据新鲜度 `fresh/stale/expired/unknown`、能力支持 `supported/unsupported/auth-required/degraded/unknown`。实例汇总状态与这三维并存;详细呈现和组合规则见[信息架构 5.1](./information-architecture.md#51-三个正交维度)。领域/API 枚举只能使用 `fresh`,中文显示为“最新”;禁止 API/数据库枚举使用 `latest`。
|
||||
|
||||
## 8. 交付物与阶段门禁
|
||||
|
||||
### 8.1 Phase 0.1 交付物
|
||||
|
||||
- 本项目章程。
|
||||
- [现状审计](./current-system-audit.md)。
|
||||
- [角色与用户流程](./personas-and-workflows.md)。
|
||||
- [信息架构](./information-architecture.md)。
|
||||
|
||||
### 8.2 Phase 0.1 验收清单
|
||||
|
||||
- [ ] 四份文档均为中文且互相链接,包含目标、非目标、状态、风险、路由和模块边界。
|
||||
- [ ] 审计清单逐项标为保留迁移、重写或废弃,并有理由与退出条件。
|
||||
- [ ] 用户流程覆盖实例接入、Fleet 定位、模块读写、批量动作、Job、audit 和错误恢复。
|
||||
- [ ] IA 包含全部目标路由及页面 × API 模块 × 状态 × 风险验收矩阵。
|
||||
- [ ] 上游基线 commit 和“100 路径/117 操作”只作为 Phase 0.2 待 parity 验证的冻结事实,不宣称已完成 registry。
|
||||
- [ ] `git diff --check` 通过;本任务不修改生产代码,规格与质量复审通过前不提交。
|
||||
|
||||
### 8.3 后续阶段门禁
|
||||
|
||||
| 门禁 | 进入条件 | 禁止带入下一阶段的问题 |
|
||||
|---|---|---|
|
||||
| Phase 0 → 1 | 章程/审计/流程/IA 评审;117 操作证据矩阵与脱敏 fixture 策略完成 | endpoint 不明、风险未分级、页面归属冲突 |
|
||||
| Phase 1 → 2 | workspace、strict typecheck、OpenAPI、统一错误与 Fastify 骨架通过契约测试 | 无 schema API、无 requestId、控制面路径漂移 |
|
||||
| Phase 2 → 3 | migration 可重放;旧配置预览/幂等导入;SecretStore 无泄漏;实例 CRUD/连接测试通过 | 明文 secret、不可回滚迁移、含糊 password 更新 |
|
||||
| Phase 3 → 4 | registry 117/117;capability 五态;安全执行管线 replay/mismatch/expiry 测试通过 | 任意代理、unknown 默认允许、R3 同步执行、批量 R2 同步执行、或 R2 无 `syncSafe` 理由/测试的同步裸执行 |
|
||||
| Phase 4 → 5 | scheduler 限流/退避/熔断;结构化状态;SSE 重连续传测试通过 | 页面触发请求风暴、空 catch、陈旧数据无标识 |
|
||||
| Phase 5 → 6 | AppShell、Fleet、实例 CRUD、capability 导航完成多宽度/键盘验收 | Animal Island/iframe/通用 API 工作台残留为主流程 |
|
||||
| Phase 6 → 7 | 每模块至少一读一写契约与 E2E;真实实例读验收;风险表单符合政策 | 模块仅展示 raw JSON、unsupported 冒充空值 |
|
||||
| Phase 7 → 8 | Job 可恢复、批量逐项结果、audit 可检索且脱敏 | 部分失败被折叠、任务重启丢失、敏感字段入库 |
|
||||
| Phase 8 → 9 | 全测试、安全审计、真实 Chrome、独立规格/安全复审无 blocker/major | 用 mock 替代真实验收、可访问性或安全缺口 |
|
||||
| Phase 9 正式切换 | shadow comparison、备份恢复、回滚演练,新版独立端口稳定 | 直接覆盖 8788、先删旧版再验证 |
|
||||
|
||||
### 8.4 可重复验收协议与冻结责任
|
||||
|
||||
- **100 实例 fixture:** Phase 0.2 由测试负责人冻结版本化 seed、生成器版本和期望结果;固定包含 online/offline/auth-required/degraded、三维页面状态、能力差异和批量部分失败。相同 seed 必须生成相同 stable ID、origin、数据和排序。
|
||||
- **Fleet 计时:** Phase 8 在指定浏览器/硬件、冷启动 fixture 后开始;从 `/fleet` 首个可交互帧且筛选控件 enabled 时为 `T0`,到目标实例行可见且目标异常单元格可操作为 `T1`。记录 `T1-T0`、浏览器/硬件、fixture hash;阈值由 Phase 0.2 性能负责人冻结,Phase 0.1 不伪造数值。
|
||||
- **导航计数:** 从 Fleet `T0` 后第一次用户 click/keyboard activation 起计;筛选/搜索提交算一次,分页或展开算一次,进入目标模块算一次,滚动不算导航但受下一条约束。100 实例 fixture 下应用目标筛选后,目标必须出现在首个 viewport,或仅需一次明确分页/行详情跳转后可见;禁止以逐卡/逐行滚动寻找目标作为通过条件。进入失败采集项详情总计不超过 3 次导航。
|
||||
- **真实写清单:** Phase 8 必测 cellular 一个 R1、Notifications channel test、Automation task test,以及专用可恢复测试实例的 service restart(R3);均保存上游前后读取、版本、响应摘要、jobId/attemptId、audit 和恢复证据。OTA apply、eSIM/消息/通话删除等若环境不安全,只做契约+模拟并显著标记 `SIMULATED`,不得冒充真实。
|
||||
- **版本/响应冻结:** Phase 0.2 的上游取证负责人固定 SimAdmin commit/版本、每个操作的脱敏真实响应 fixture、schema 和真实/模拟分类,测试负责人固定环境与指标阈值;Phase 8 QA 按冻结物执行并报告 fixture hash、环境、原始结果和偏差。版本变化必须重新取证和审批,不得静默更新 golden。
|
||||
|
||||
## 9. 依赖、约束与风险
|
||||
|
||||
- 上游无正式 OpenAPI:以固定 commit 的路由、handler、model、Bruno 和上游前端交叉取证。
|
||||
- 上游版本差异:capability discovery + compatibility adapter + fixture,不用空值吞差异。
|
||||
- 多实例扇出:并发限额、TTL、jitter、backoff、deadline、取消、熔断和 SSE。
|
||||
- 网络盘路径:代码可留在现路径;运行期 SQLite、缓存和 secrets 应落本地数据目录。
|
||||
- OTA 大文件:专用上传流和进度,不放入普通 JSON proxy;普通 body limit 不作为产品方案。
|
||||
- UI 偏离运维需求:先验收 IA、表格与完整工作流,再验收视觉。
|
||||
|
||||
## 10. 决策与变更控制
|
||||
|
||||
以下变更必须更新本章程、IA 或 registry 并经过规格与安全复审:
|
||||
|
||||
- 新增/移除一级路由或上游操作;
|
||||
- 改变风险等级、确认策略或 batchable 属性;
|
||||
- 改变 secret 存储和日志/audit 字段;
|
||||
- 将 `unsupported`、`auth-required`、`degraded` 合并为模糊错误;
|
||||
- 引入通用代理、iframe、绕过 Job 的 R3/批量 R2 快捷入口,或未由 OperationRegistry 以理由和测试明确证明 `syncSafe` 的同步 R2 入口。
|
||||
|
||||
## 11. 完成定义
|
||||
|
||||
V1 只有在 G1–G10 全部有可重复证据、117 个操作没有静默遗漏、真实实例和真实浏览器验收通过、且 8788 切换具备回滚时才称为“可使用”。单纯页面完成、接口返回 200 或单元测试通过均不构成项目完成。
|
||||
Reference in New Issue
Block a user