04-config-and-credentials.md 6.4 KB

04: 配置与凭据注入

What to build: 通过部署环境或后端运行时配置注入后端地址、天地图、Cesium、WebSocket 等配置与凭据。前端不硬编码账号、密钥或环境地址。

Blocked by: 01

Status: resolved Labels: ready-for-agent

请勿据标签重复接单:本票已完成并有关闭记录,Labels 保留只是为了让面板不把它退化成 needs-triage。

  • 配置统一由配置服务读取,页面不直接拼接环境地址。
  • 后端接口、天地图、Cesium、WebSocket 均可由配置覆盖。
  • 真实接口的 Authorization: Bearer <JWT> 由部署层注入(生产网关补头,开发本地反代或部署配置提供只读 token);适配层统一附加,页面与 store 不触碰凭据。
  • 凭据缺失时不进入登录页,而是显示数据不可用状态。
  • 数据源默认 mock;真实接口由部署配置显式开启,两者走同一适配层。
  • 配置加载成功、失败和缺失三种状态可演示。
  • 有配置加载纯逻辑单测。

进度:100%

已完成并关闭(2026-09-17)。 后端地址、天地图、Cesium、WebSocket 与凭据都收进配置服务,前端零硬编码;默认 mock,真接口要部署配置显式打开;凭据只在适配层出现一次。

与维护者确认的两条前置决策

  • 注入方式:运行时分层(后者覆盖前者,深合并):内置默认 → 构建期 VITE_COCKPIT_* → <base>cockpit/config.json → 宿主注入 globalThis.__COCKPIT_CONFIG__。
  • 联调凭据:仓库只放 public/cockpit/config.example.json;真实 config.json 进 .gitignore;本机只读令牌写进它或 .env.local(也已 gitignore),生产一律由网关补头。

改动

  • 新增 domain/configStatus.js:配置/数据源/凭据词汇 + 「状态 → 色键/文案」纯函数映射;另给票 05 留了 isCredentialFailure() 与统一文案 DATA_UNAVAILABLE_LABEL。
  • 新增 services/cockpitConfig.js:四层解析、宽松校验(错字段退回默认并记 issue,不整份作废)、三态 ready/missing/failed、同步读 getCockpitConfig();没有任何配置来源时一个请求都不发(原因见下方「一个坑」)。
  • 新增 services/cockpitApi.js:适配层接缝。planCockpitRequest() 是全仓唯一拼地址、唯一附加 Authorization 的地方(且只在 real 分支加);requestCockpitJson() 是 mock/real 唯一出口,失败一律收敛成 {ok:false, reason},不抛异常、不跳登录页;mock 处理器注册表留给票 05 挂领域样例。
  • 新增 public/cockpit/config.example.json(部署模板);.gitignore 忽略 public/cockpit/config.json;public/cockpit/README.md 补「部署配置」一节:四层、三态、凭据策略、部署后怎么改无需重新构建。
  • 外壳顶栏加配置标签(复用票 06 的 StatusTag):data-config-state / data-data-source / data-source 供走查断言;界面只显示状态,不显示任何令牌。
  • 配置真被消费,不是摆设:map.cesium.enabled=false → 地图服务不启动引擎并如实标注 disabled(中央区域留临时影像兜底);map.cesium.baseUrl → 覆盖 CESIUM_BASE_URL(cesiumAssets.ensureCesiumBaseUrl(override))。
  • 未动 MapCanvas、面板与 store:它们连配置服务都不直接 import,页面只读外壳给的视图模型。

验证证据

  • 纯逻辑:node .scratch/cockpit-shell-panel-layout/verify/t04-config.mjs → 通过。覆盖三态、四层来源与覆盖顺序、宽松校验、载荷判定、请求计划、mock/real 同一出口、假 401 归类;含两条静态守卫:8 个页面/store 文件零 Authorization/凭据字样、15 个模块文件零登录跳转,且 VITE_COCKPIT_*/__COCKPIT_CONFIG__ 只在配置服务里读。
  • 真机走查:node .scratch/cockpit-shell-panel-layout/verify/t04-config-walkthrough.mjs → 9/9:默认缺失态(内置 mock、无页面报错)、宿主注入成功态(real)、运行时文件成功态(source=file,真走 HTTP)、坏文件失败态(红色「数据不可用」、URL 不变、页面里没有登录入口)、cesium.enabled=false 生效;并断言注入的令牌不出现在 DOM/顶栏。
  • 回归:t01-browser-walkthrough 20/20(A11「无页面级报错」保持绿)、t01-panel-catalog / t03-camera-contract / t06-assets / t28-fill-container 全通过、yarn build exit 0(55s)。
  • 归因(对抗式):t03-camera-walkthrough 有 2 项红(A5/A8:中心点投影)。把本票三个改动文件 git stash 回 HEAD 复跑,同样两条、同样数字(投影 827,417 vs 画布中心 840,520)→ 属票 03 在途工作(claimed / 90%),与本票无关;随后已 stash pop 还原。

一个坑(值得记住)

dev 下 Vite 的 HTML 回退只对 Accept: text/html 生效;配置服务发的是 Accept: application/json,所以「没有配置文件」拿到的是真 404(Chrome 记一条 console error),不是 200 HTML。第一版按「默认总去探测 cockpit/config.json」实现,实测把 t01 的 A11 打红;改为没有显式配置 URL 就不发请求(此时 missing 表示「没有任何配置来源」),文件层由部署侧显式开启,三种等价写法:宿主页一行 window.__COCKPIT_CONFIG_URL__ = 'auto'、构建期 VITE_COCKPIT_CONFIG_URL=auto、或直接给绝对 URL 从后端拉(auto = 按应用基址取约定路径,qiankun / 子目录部署下也对)。README 已写清。

边界与遗留

  • 天地图与 WebSocket 目前只有配置位与文档化的消费点(map.tianditu.token → 票 21;ws.url/ws.token → 票 15);本票保证它们能被配置覆盖,真正使用落在那两票。
  • 「401/凭据类失败只把分区标成数据不可用」的渲染归票 05(其验收原句);本票已提供 REQUEST_REASON.UNAUTHORIZED、isCredentialFailure()、统一文案,并用假 401 在逻辑脚本里断言过。
  • 为放部署模板,票 06 的 t06-assets.mjs E1「public/cockpit 下每个资源都有引用点」需把 *.example.json 与 README 一视同仁(模板由运维复制,代码永不引用):已按此最小修改,aerial.webp 的引用清单照旧。
  • 本票改动未提交(工作区里另有票 03 会话的提交 71c55bc 与在途改动)。