Harden image admission, resource recovery and session renewal

Add exact-image compatibility policy, experimental admission, paginated build history and shared reauthentication checks. Preserve uncertain Docker creation records, bound frontend resource waits and renew runtime sessions proactively.

Lazy-load and split Monaco, add regression and opt-in live Docker fault tests, and record full race and real-browser acceptance results.
This commit is contained in:
2026-10-14 13:41:54 +08:00
parent 66abb76318
commit 9a71772771
62 changed files with 4679 additions and 332 deletions
+63
View File
@@ -1,5 +1,68 @@
# DESIGN.md 验收记录
## 恢复宽限期与轮询缓存修复(独立复审后)
范围:本轮为对上述工作树的独立代码审计与全量重测后的修复。审计确认八项修复中六项正确,发现三处残留:创建结果不确定的挂起标志永不过期导致实例永久无法删除(高)、构建校验不确定行无限重试并持续新增审计(中)、意图轮询每秒对每个成功镜像发起 Docker inspect(中)。修复未改变凭据文件管理边界或准入策略;未部署、未操作真实 Docker 实例。
- `runtime_creates` 增加 `container_pending_at`/`network_pending_at` 置位时间(列名白名单,禁用未校验列名拼接)。确定性名称 404 在宽限窗口(默认1分钟)内仍视为不确定;窗口过后判定资源从未创建,标志落账清除,实例可正常删除,删除成功后总是清理 `runtime_creates`/`runtime_networks` 行。无时间戳的遗留行视为已过期立即解析。
- `image_build_checks` 增加 `created_at`。无已记录容器 ID 且名称 404 的不确定行在宽限窗口过后删除行停止重试;窗口内保持降频重试与 `build.cleanup_error` 审计(≤1/分钟不变)。
- 意图轮询改用 `verifiedImagesPoll`:成功构建行每秒重读,逐镜像准入评估按 `imageDeleteGeneration`+5秒TTL缓存;镜像删除或准入写入使缓存立即失效。创建/升级入口仍走未缓存的权威校验。
- 前端:无 Web Locks 浏览器的会话恢复重试窗口从约0.85秒延长至约3.85秒;`useResource` 对60秒不返回的请求自动放弃并重新武装轮询(序号守卫防止迟到结果覆盖新状态);`runtimeSession` 测试改为 TypeScript 并纳入 tsc 检查;`test-panel.mjs` 构建详情轮询对瞬时 5xx 容错续轮。
回归覆盖:宽限窗口内删除被拒、窗口过后删除成功且无残留行(TestUncertainPendingResolvesAfterGrace);校验行窗口内保持不确定、窗口过后与遗留行均解析(TestBuildCheckUncertainRowResolvesAfterGrace);轮询三次仅一次 Docker inspect、代际提升后缓存失效(TestIntentPollerCachesImageEvaluations);悬挂请求逃生阀(ui.test.ts)。Go共有192个顶层测试(另有子测试)。前端15个测试文件、95项测试通过;`@types/node` 新增为 devDependency,官方源 npm audit 0 漏洞。
## 完整home与镜像准入策略实施
本节落实用户确认的完整home所有者访问、兼容清单及管理员实验构建策略,取代下方上一轮“尚未改变这两项策略”的历史边界。未部署,未操作真实Docker实例或为实际镜像写入准入。
- 所有者可读取/打包凭据,普通文件管理不强制使用专用Profile流程;匿名、跨用户和路径穿越仍拒绝。下载及敏感修改增加确认,取消不触发操作;上传同名拒绝不覆盖。专用Profile页面仍要求停机,文案与通用文件管理明确区分。
- 默认普通构建支持linux/amd64、模板7的rc.1/rc.2;未知版本只能管理员实验构建。实验完整执行原检查且success不自动可选。准入绑定精确imageID/version/recipe/platform与成功验证记录,管理员填写依据,策略和审计同事务,审计失败回滚。
- 历史模板3–6默认仅存量,经明确验收可准入并使用旧双挂载;未知布局拒绝正向准入,模板7向旧共享布局降级拒绝。已准入未知版本只通过本地imageID使用,不自动扩大版本构建白名单。
- blocked拒绝新建、升级目标、start/restart及受控配置非GET操作,避免恢复/备份自动启动绕过;不自动停止既有容器,普通文件管理保持可用。队列恢复/执行重查版本、平台及实验请求者管理员权限。
- 独立复核修正了准入与生命周期锁顺序、受控配置启动遗漏及两处提示矛盾;最终静态复核未发现剩余阻塞项。
回归覆盖精确版本/模板/平台匹配、旧模板实际创建挂载、实验success仍不可选、未知请求零预留、普通用户和CSRF拒绝、审计回滚、并发降级等待创建完成、blocked启停/配置拒绝,以及前端准入确认/取消和选择器过滤。Go共有189个顶层测试(另有子测试);本轮Go普通全量和全量race禁缓存通过,API包分别约17秒与97秒。前端15个测试文件、94项测试通过。浏览器使用模拟API验证实验警告、准入表单、创建筛选及unknown latest禁止预留,临时服务和浏览器已关闭;非真实Docker或新DSH版本兼容验收。
完整 `scripts/check-trial.sh` 最终退出码0;`go vet`、Go构建、TypeScript与Vite生产构建、profile初始化3项Node测试、验收脚本自身11项Node测试和脚本语法检查均通过。仍有既有Vite大chunk提示,非构建失败;`git diff --check`通过。
## 2026-10-14 真实环境补充验收与构建拆包
- 全量 race:`go test -race -count=1 -timeout=180s ./...` 退出 0,全部包通过、无 race 报告;API 包 166.645 秒,总耗时约 170 秒。
- 真实 Docker:新增默认跳过、需 `DSH_DOCKER_LIVE_TEST=1` 启用的 `TestDockerLiveRecovery`。经故障代理向真实 `/var/run/docker.sock` 转发,真实 create/delete 成功后丢失响应,或断开客户端并延迟创建,先取得真实 404 再放行。六个子场景连续两轮通过,另一次 race 模式通过。覆盖 runtime/network/build-check 恢复、面板未连接/完整 ID/名称连接及删除响应丢失。仅使用专用唯一标签资源、本地 node:24-alpine;按精确 ID 清理后标签查询零残留。测试调用恢复方法,不模拟整机断电,也不重启共享 daemon。
- 真实浏览器续期:当前 Go 源码、隔离临时数据库、真实 DSH rc.2 模板7容器、Chromium 独立 Cookie 上下文,原始 5 分钟 access / 4 分钟续期间隔。2026-10-14T03:49:07.767Z 至 03:59:22.828Z 共 615.061 秒;初次、第 240.023 秒及第 480.020 秒 refresh 均 200;每 15 秒原生 fetch 检查 `/api/me` 和真实 runtime,共 41 轮全部 200(含第 301 秒和第 601 秒)。没有加速时钟、手动续期或共享 Cookie 的其他页面。证据 `output/playwright/renewal-acceptance.json`;不代表休眠/后台节流或多小时压力验收。
- Monaco 改为 React.lazy 按需加载,依模块分层拆包;主入口从约 2589 kB 降至 284.92 kB,最大 chunk 411.21 kB,worker 232.35 kB,保留默认 500 kB 阈值且最终构建无大 chunk 或循环 chunk 告警。新增构建测试约束体积、入口不静态依赖 Monaco 及 chunk 无环;16 文件/114 项前端测试通过。真实浏览器验证编辑输入、运行中保存禁用、只读预览及 worker 初始化成功,截图 `output/playwright/editor-chunks-verified.png`。
- 最终 `go vet ./...`、`go build ./...`、TypeScript/Vite 构建和 `git diff --check` 通过。专用面板、两个浏览器已关闭,验收容器/网络及临时数据和凭据已清理;原四个运行容器仍在,未部署到现有面板、未更改共享镜像。
复现真实故障测试:`DSH_DOCKER_LIVE_TEST=1 go test ./internal/api -run TestDockerLiveRecovery -v -count=2 -timeout=3m`;需可访问 Docker socket 和本地 node:24-alpine,不会自动拉取镜像。
## 再次审查修复(本地回归)
- runtime 注入脚本每 4 分钟主动续期,不再因 `/api/me` 仍有效而跳过;保留 Web Locks 串行刷新、同页请求合并及冲突复验。模拟时钟覆盖连续续期,补测锁等待期间 Cookie 更新。
- 资源加载超时会结束调用方等待链、清除 loading 并提示重试;覆盖无轮询、旧请求和后续请求均悬挂、迟到成功/失败、超时配置变化,不再只清空 in-flight 引用。
- Docker 创建结果不确定时,不凭一分钟后的名称 404 清除 pending/验证容器记录;覆盖旧/缺失/非法时间戳及迟到资源回收。网络清理只断开确实连接的面板端点,保留身份校验和真实错误处理。持续未确认的创建需按 OPERATIONS.md 人工确认。
- 准入依据前端按实际提交内容计算 UTF-8 字节数,超出 4096 字节时提示并禁止提交;后端保持字节上限及请求体限制,长度错误明确返回。覆盖中文、ASCII、多字节、表情及边界。
本轮通过:`go test -count=1 -timeout=180s ./...`、准入与资源恢复相关 API 测试的禁缓存 race 检查、`go vet ./...`、`go build ./...`;前端 15 文件/113 测试及 TypeScript/Vite 生产构建;profile 初始化与验收脚本自身共 14 项 Node 测试、集成脚本语法检查、`git diff --check`。本轮未执行全量 race、真实浏览器长时续期或真实 Docker 故障注入;未部署、未操作现有实例。Vite 仍有既有大 chunk 提示。
## 八项审查问题闭环(本地回归)
范围:普通创建响应丢失、验证容器回收、构建终态持久化、多页面续期、慢请求轮询、当前密码重认证、配额更新竞态、构建历史分页与按需日志。未改变凭据文件管理边界或历史镜像兼容策略;未部署、未操作真实 Docker 实例。
- 资源恢复:创建前保存恢复依据;校验资源身份后按持久 ID 清理。覆盖迟到资源(首次404随后出现)、网络创建/删除响应丢失、验证容器重命名、外部同名网络拒删、清理失败重试、重开数据库后无排队任务自动回收。个别预约待恢复不会阻止面板启动。
- 构建终态:任务与镜像索引事务提交,故障注入验证无半提交;独立重试不阻挡后续构建,Store关闭后退出。持续不确定创建保留恢复记录,不凭单次404释放资源依据;人工确认流程见 OPERATIONS.md。
- 认证:备份、升级、改密共用失败计数与锁定;旧备份密码不参与登录失败计数。并发refresh冲突返回409且不签发cookie,前端及runtime复验后保留可重试错误,真正撤销仍退出;覆盖慢升级不阻塞其他账号登录。
- 文件与前端:上传提交锁内读取最新额度;管理员修改额度共用用户锁。慢轮询不重叠、不废弃有效慢响应,显式刷新保证额外读取,key切换隔离旧结果。构建列表默认20条、最多100条且不读取日志正文,详情按需加载;参数、路由鉴权、整页边界和当前页镜像对账均有回归。
最终验证:
- 完整 `scripts/check-trial.sh` 通过,最终退出码0;最终Go测试通过 `GOFLAGS=-count=1` 禁用测试结果缓存。Go共有182个顶层测试(另含子测试),API普通测试约18秒,全量race中API包约110秒;无race报告。
- `go vet ./...`、`go build ./...`、前端类型检查与生产构建通过;Vite仍提示原有大chunk,不影响构建成功。
- 前端10个文件、57项测试通过;profile初始化3项Node测试、验收脚本自身11项Node测试通过;真实runtime注入脚本的401/403/409/500行为已自动测试。
- 浏览器配合模拟API验证每页20条、末页和禁用下一页、日志打开/关闭及终态停止轮询。临时浏览器和开发服务器已关闭。
- `git diff --check`通过。Docker故障场景使用httptest模拟,并非真实daemon断电/断网验收;未运行指向真实部署的test-panel脚本,仅更新其单任务查询并检查语法。
升级注意:已打开的旧页面需刷新;外部 `/api/builds` 消费者需迁移分页和单任务日志接口。持续数据库故障或身份冲突不能承诺自动成功,保留记录并按运维流程处理。
## 2026-10-12 可信团队试运行加固(本地自动化验收)
范围:本机或仅可信账号的团队,可由管理员通过公网 HTTPS 反代访问。未修改 Nginx/Caddy,未部署面板或操作现有实例。本节仅说明当前代码的本地自动化验收,不将历史部署证据当作本次公网实测。