diff --git a/docs/README.md b/docs/README.md index 408be2e..f4a8c0e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,6 +5,7 @@ | 文档 | 查什么 | | --- | --- | | [真实云管接入](cloud-live-integration.md) | 正式环境启动、现场 PVE/盒子、凭据与回退 | +| [云管功能核对](cloud-feature-progress-20261110.md) | 对照原需求的已实现/剩余项、模板与桌面池使用及验收条件 | | [当前状态](current-status.md) | 当前架构、三种桌面配置、已完成与未验收事项 | | [终端说明](terminal-guide.md) | 开机直达选择页、进入/退出交接、快捷键、显示恢复、构建入口 | | [云桌面说明](desktop-guide.md) | Windows/Linux 编码与画质、原生光标、电源按钮、U 盘 | @@ -21,7 +22,7 @@ `D:\DAIMA\vdi-monitor` 负责日常监控页面、指标采集与日志解读,具体维护方法见该项目的 [README](../../vdi-monitor/README.md) 和 [运维说明与已知限制](../../vdi-monitor/docs/运维说明与已知限制.md)。 -监控当前约每 30 秒采集一次盒子数据,保存最新原始快照和状态,并保留约 7 天指标历史。这不等于完整历史日志备份,也不覆盖开发决策、测试场景、失败实验和回退配置。`vdi-cloud` 保留这些资料,监控页面的使用说明以后在 `vdi-monitor` 维护。 +监控当前约每 30 秒采集一次盒子数据,保存最新原始快照和状态,并保留约 7 天指标历史。这不等于完整历史日志备份,也不覆盖开发决策、测试场景、失败实验和回退配置。`vdi-cloud` 保留这些资料;11-10 另在云管控制台新增 PVE 节点曲线和平台告警,见[范围说明](cloud-operations-20261110.md),不替代独立监控的诊断采集。 ## 历史记录与原始资料 @@ -35,3 +36,19 @@ ## 后续维护方式 日常变更更新对应主题文档,注明日期、实际部署状态、证据位置和限制;计划统一写入 `roadmap.md`。测试原始输出放在 `evidence/<专题-日期>/`,一般不再新增一份顶层日报文档。发生配置冲突时,以较新的部署证据核对现场,不把历史测试成功直接视为当前状态。 + +- [终端生命周期与用户维护](cloud-lifecycle-20261110.md):设备改名/分组、停用/重新激活、用户资料/启停与批量桌面授权。 + +- [终端运维策略:配置模板、下发和应用回执](cloud-terminal-policies-20261110.md) + +- [CSV 导入、会话历史与监控告警](cloud-operations-20261110.md):使用入口、格式、数据留存、迁移与验收边界。 + +- [PVE 接入配置、桌面规格与登录限流](cloud-configuration-20261110.md):真实配置保存、规格任务保护、可信代理及现场验收边界。 + +- [关机快照与手动还原](cloud-snapshots-20261110.md):列表、创建/删除/还原任务、未知结果保护及现场权限与磁盘范围限制。 + +- [桌面批量电源操作](cloud-batch-operations-20261110.md):跨页选择、逐台预检、批次任务、跳过原因与未确认结果核对;已部署,现场仅做只读预检和取消提交验收。 + +- [GPU 清单与配置引用](cloud-gpu-inventory-20261110.md):真实 PCI 清单、IOMMU 分组、当前/待应用直通引用及不完整检查提示;仅资源查看,绑定与健康采集尚未接入。 + +- [终端系统指标与历史](cloud-terminal-metrics-20261110.md):真实 CPU、内存、SoC 温度、运行时长和 30 天历史;串流 FPS/RTT 仍未采集。 diff --git a/docs/cloud-batch-operations-20261110.md b/docs/cloud-batch-operations-20261110.md new file mode 100644 index 0000000..0c3b4b5 --- /dev/null +++ b/docs/cloud-batch-operations-20261110.md @@ -0,0 +1,44 @@ +# 桌面批量电源操作(2026-11-10) + +已部署本机真实控制台 [5182](http://127.0.0.1:5182/desktops),入口为「桌面管理」。勾选卡片或列表中的桌面,选择「批量开机 / 批量关机 / 批量重启」,先逐台预检,再确认提交;「批量记录」查看最近 100 个批次及逐台结果。跨页保留选择,每批最多 50 台。超级管理员和运维可操作,只读角色可查看历史。 + +## 执行与保护 + +- 预检读取真实 PVE 配置、电源与 `VM.PowerMgmt` 权限;不存在、模板、锁定、状态不适用、活动会话、维护任务或原结果未确认的桌面逐台跳过。批量关机使用正常 shutdown,重启使用 reboot。 +- 预检结果签名并绑定当前账号、登录会话、动作和所选桌面,有效 5 分钟。提交时再次检查,只允许预检批准且标识/电源状态一致的桌面进入队列;原先不可用的目标不会后来自动加入。 +- 提交必须携带 `Idempotency-Key`。重复相同请求返回原批次,参数变化返回冲突;确认取消不创建批次或任务。所有跳过项及原因也保留在批次中。 +- 执行前再次读取状态和权限,成功需确认原 PVE UPID 完成且实际电源状态符合预期;开机完成不表示 Sunshine 已就绪。页面区分已提交、执行中、成功、失败、未确认和跳过。 +- 明确的 PVE 4xx 拒绝记为失败;发送后的通信异常、任务失败或缺少 UPID 保守记为未确认。重启恢复只查询已记录的原任务,不自动重发。未确认阻止后续桌面修改和新连接。 +- 对有原 UPID 且最终任务成功的未确认结果,可在批次或任务页「核对电源结果」。该操作只读取 PVE 并更新平台记录,不重新下发;缺少 UPID、PVE 失败或实际状态不符仍须管理员核查,当前无强制解除隔离入口。 +- 电源、规格、快照共用每桌面活动任务约束。批量与克隆/规格/快照共用一次一个的长任务执行通道,普通命令继续处理;排队超过 2 小时且尚未发送的任务失败,执行中超过恢复期限按原阶段保守处理。 + +外部直接在 PVE 操作 VM 不受平台行锁约束,预检与发送之间仍可能发生变化;平台会核对任务和最终状态,不承诺跨系统原子操作。当前只支持单个队列运行者,没有批量发放、批量规格修改或定时电源策略。 + +## 接口与持久化 + +路径均位于 `/api/v1`: + +| 方法 | 路径 | 用途 | +| --- | --- | --- | +| POST | `/desktop-batches/check` | 只读预检,返回逐台原因及签名凭证 | +| POST | `/desktop-batches` | 确认后持久化批次与子任务,返回 202 | +| GET | `/desktop-batches` | 最近 100 批分页列表 | +| GET | `/desktop-batches/{id}` | 批次汇总、每台任务或跳过原因 | +| POST | `/tasks/{id}/batch-power-reconcile` | 核对原 PVE 任务及实际状态 | + +新增 `power_batches`、`power_batch_items` 和覆盖 `batch-desktop` 的活动任务唯一索引;子任务与审计同事务写入,启动幂等迁移,不增加原业务表字段。API 契约见 [OpenAPI](openapi/platform.v1.json)。 + +## 验证与现场边界 + +- 独立临时 PostgreSQL 与本地 PVE HTTP 测试服务验证三种电源动作、逐台跳过、角色与签名绑定、幂等并发、提交/执行复查、活动会话与维护互斥、网络未知、重启不重发、过期及单连接池核对。Go 全量测试和 `go vet` 通过。 +- 前端 ESLint、TypeScript、3 项单元测试和生产构建通过;8 项新增浏览器用例及 37 项已有回归全部通过,共 45/45,覆盖深浅主题、390px 窄屏、跨页选择、取消、重复提交和只读权限。接口全部拦截,不操作现场 VM。 +- API/控制台镜像已更新,业务 PostgreSQL 容器和卷保留。现场三台运行中的桌面:批量开机预检全部跳过,关机/重启预检各 3 台可执行。真实浏览器打开确认后取消,空批次历史验证通过;没有提交批次或执行 VM 电源操作。 +- 核查时 VM100/101/102 均运行,盒子心跳正常;原管理员及密码哈希指纹、桌面连接配置、用户授权与终端身份和更新前一致。批次数与批量任务数均为 0。真实批量电源执行尚未做现场验收。 + +证据:[45 项页面回归](evidence/cloud-batch-operations-20261110/playwright-report.json)、[真实接口核查](evidence/cloud-batch-operations-20261110/live-verification.json)、[真实页面核查](evidence/cloud-batch-operations-20261110/browser-verification.json)、[现场批量预检](evidence/cloud-batch-operations-20261110/live-batch-preview-dark.png)、[现场窄屏](evidence/cloud-batch-operations-20261110/live-batch-preview-mobile.png)。`fixture-` 图片为拦截接口的测试数据,不代表现场已有对应批次或执行结果。 + +## 备份与后续 + +更新前备份为私有 `.local/live/backups/before-batch-operations-20261110-163850.dump`,已检查备份目录可读;API/控制台旧镜像标签均为 `before-batch-operations-20261110-163850`。尚未做恢复演练。回退前须评估新增批量任务;旧后端不识别新任务与隔离状态,不可带着活动或未确认批次直接回退,不删除业务卷。 + +用户已明确暂缓专用测试 VM、Windows 模板副本、存储准备及依赖它们的真实快照内容还原验收;本批没有创建 VM、格式化或挂载额外磁盘、修改 PVE 权限。公共池自动领取/退出还原仍未实施,后续顺序见 [计划](roadmap.md)。 diff --git a/docs/cloud-configuration-20261110.md b/docs/cloud-configuration-20261110.md new file mode 100644 index 0000000..7e8edad --- /dev/null +++ b/docs/cloud-configuration-20261110.md @@ -0,0 +1,54 @@ +# PVE 接入、桌面规格与登录限流(2026-11-10) + +本批已部署到本机真实服务,入口仍为 `http://127.0.0.1:5182`,使用原管理员账号。 + +| 功能 | 使用入口 | 当前行为 | +| --- | --- | --- | +| PVE 接入 | 系统设置 → 平台接入 → 配置 Proxmox 接入 | 检查 HTTPS、Token 与资源读取;保存后热生效,重启保留;密钥不回显,留空保留同地址同 Token ID 的原密钥 | +| CPU / 内存 | 桌面管理 → 查看详情 → 规格管理 | 读取真实规格;正常关机、无活动会话、无锁和待应用配置时,先预检再确认提交任务 | +| 虚拟磁盘扩容 | 规格管理 → 磁盘扩容 | 选择现有磁盘,填写扩容后总容量;禁止缩容,客体分区与文件系统需另行扩展 | +| 登录限流 | 登录时自动执行 | 5 分钟窗口内同账号最多 10 次、同来源 IP 最多 30 次,包含成功与失败;超限返回 429 和剩余等待秒数 | + +## 接入配置 + +仅超级管理员可读取、检查和保存接入配置。没有数据库配置时读取原服务端环境;保存后以数据库配置为准。始终验证 TLS,可使用系统信任或输入 PEM CA 公证书,不提供忽略证书选项。保存前再次检查 `/cluster/resources` 至少能读取一个节点,检查失败保留原配置。 + +首次接入后,本期只支持维护相同地址的凭据和证书;不提供在线更换集群地址或多集群管理。这样可以避免同步中的旧集群资源混入新集群。计算任务排队或执行时,配置保存会被拒绝。并发编辑通过版本冲突提示重新加载,不覆盖他人的配置。 + +配置以 AES-GCM 加密保存在 `pve_connection`,随机 nonce,响应、错误和操作审计不记录 Token 密钥。加密密钥由服务端 `VDI_JWT_SECRET` 派生,备份数据库时必须一并安全保管对应服务端密钥。**本期没有在线密钥迁移工具,不可单独更换 JWT Secret 后继续使用原密文。** 失配会明确拒绝启动或返回配置不可用,需在维护窗口恢复匹配密钥或迁移密文。当前启动仍保留原有效环境配置与 CA 文件作为未保存时的基线。 + +## 规格任务 + +规格操作仅超级管理员可提交。CPU/内存和单块磁盘扩容分开执行。只写目标规格字段,保留 CPU 型号、GPU 直通和其他设备配置。CPU 每插槽 1~256 核,1~8 插槽,总核不超过 1024;内存 512~1048576 MiB,按 128 MiB 递增且不低于已有 balloon 最小值。带自定义 NUMA、PVE 锁或待应用配置的 VM 需先在 PVE 处理。 + +扩容仅支持可识别的 scsi/sata/virtio/ide 虚拟存储磁盘,不接受物理磁盘、ISO、cloud-init、EFI 或 TPM 设备。目标容量最大 65536 GiB,要求存储启用、在线且有扩容量加 1 GiB 余量。CPU、内存和磁盘分别预检 `VM.Config.CPU`、`VM.Config.Memory`、`VM.Config.Disk`;扩容另需对应存储的 `Datastore.AllocateSpace`。PVE 仍执行最终权限和资源校验。 + +提交和执行前都重新读取真实电源状态、配置摘要、权限和余量。相同请求标识返回原任务,参数或桌面变化不得复用标识。CPU/内存使用 PVE 配置异步任务,磁盘发送**绝对目标容量**,不发送增量扩容。收到 UPID 后等待结束,再读取实际规格确认成功。接口依据 [PVE Qemu 官方实现](https://github.com/proxmox/qemu-server/blob/master/src/PVE/API2/Qemu.pm)。 + +任务状态与原请求保存在 `hardware_jobs`。修改发送前先持久化提交阶段;发送后网络异常、服务中断或最终规格无法确认时显示「结果未确认」。恢复进程只查询原任务,不自动重发修改。此时后续规格、电源操作和新连接票据均被阻止;「任务与审计 → 目标任务 → 核对规格结果」只读取原 UPID 与目标规格,确认后解除保护。缺失 UPID 或实际规格未达到目标时保留未知状态,需在 PVE 人工核查并安排离线恢复,本期没有一键清除未知结果的入口。 + +排队过期且尚未发送的任务明确失败;已经进入提交阶段的过期任务保持未知结果。规格与电源任务共享同桌面的活动任务限制,长规格任务与克隆共用执行通道,不阻塞普通终端命令。 + +## 登录限流 + +计数保存在 PostgreSQL,多实例和重启共享。账号按去空格、小写后的值计数,账号/IP 存为 HMAC 摘要,不保存明文计数键或密码。旧计数按保留策略清理。 + +默认不信任转发头;`VDI_TRUSTED_PROXIES` 仅配置实际可信代理的 IP/CIDR。现场 Nginx 覆盖客户端提交的 `X-Forwarded-For`,API 信任现场容器网络。更换网关拓扑时同步调整可信代理配置,否则来源 IP 可能聚合在网关地址。该限流只作用于管理员登录,设备注册、心跳与票据鉴权继续使用原机制。 + +## 验收与限制 + +- Go 全量测试与 `go vet` 通过,数据库仅使用独立临时 `vdi_configuration_test`。本地 PVE 测试服务覆盖 CPU/内存、绝对磁盘扩容、权限/余量、运行/锁/待应用配置/摘要冲突、幂等、任务互斥、未知结果、重启不重发、只读核对和连接保护。 +- 前端 lint、类型检查、3 项单元测试和生产构建通过;23 项既有管理页面回归及新增 6 项接入/规格流程通过。深浅主题、窄屏、取消确认及只读角色入口检查通过。 +- 真实 PVE 连接检查、同地址原凭据加密保存、热加载、错误保存保留原配置、API 重启后继续读取均通过。临时只读账号验证第 11 次登录返回 429 与 `Retry-After`,账号和对应限流摘要已定向清理,审计保留。 +- 三台现场 VM 的真实规格读取和运行中修改拒绝通过;现场 Token **仍缺少三台 VM 的 CPU/内存/磁盘配置权限**。实际规格写入成功仅在隔离测试服务验证,未在现场执行关机、重启、扩容、克隆或其他 VM 修改。 +- 重启后原业务快照一致:管理员密码指纹、桌面参数、用户授权及终端身份未改变;三台 VM 运行,`qt-platform/0.3.0` 盒子心跳正常。 + +证据:[真实接口验收](evidence/cloud-configuration-20261110/live-verification.json)、[重启与业务快照](evidence/cloud-configuration-20261110/restart-verification.json)、[浏览器验收](evidence/cloud-configuration-20261110/browser-verification.json)、[测试汇总](evidence/cloud-configuration-20261110/test-summary.json)。页面见[真实规格](evidence/cloud-configuration-20261110/live-hardware-dark.png)与[真实接入配置](evidence/cloud-configuration-20261110/live-pve-light.png)。 + +## 接口与迁移 + +接口契约见 `docs/openapi/platform.v1.json`:`GET/PUT /settings/pve`、`POST /settings/pve/check`、`GET/POST /desktops/{id}/hardware`、`POST /desktops/{id}/hardware/check`、`POST /tasks/{id}/hardware-reconcile`。均以 `/api/v1` 为前缀。 + +启动新增 `pve_connection`、`hardware_jobs`、`login_limits` 与规格/电源活动任务唯一索引,不删除既有业务表或列。部署前数据库备份为私有 `.local/live/backups/before-configuration-20261110-151859.dump`,对应 API/控制台旧镜像标签为 `before-configuration-20261110-151859`。本次只重建 API/控制台容器,未重建 PostgreSQL 或删除业务卷。 + +回退前需再次备份并核查新增任务、接入配置与凭据变化。旧 API 使用环境中的 PVE 配置且不识别规格任务,存在排队或未知规格任务时不可直接切回旧执行器;恢复数据库快照会丢弃快照后的业务记录,当前未进行恢复演练。 diff --git a/docs/cloud-feature-progress-20261110.md b/docs/cloud-feature-progress-20261110.md new file mode 100644 index 0000000..9cc287e --- /dev/null +++ b/docs/cloud-feature-progress-20261110.md @@ -0,0 +1,138 @@ +# 云管功能核对与本轮实现(2026-11-10) + +依据《VDI云桌面软件开发方案(1)》第 4、5 章、《VDI云桌面完整方案-云管端》第 3、4 章,以及 [控制台功能设计](cloud-console-functional-design-20261109.md)。以下区分代码实现、测试环境验证和真实现场验收。 + +## 功能核对 + +| 模块 | 当前实现 | 仍缺少的内容 | +| --- | --- | --- | +| 登录与授权 | 管理员登录、退出、三角色后端校验;账号新增、改名、角色、启停、他人密码重置、本人改密与会话撤销;用户与终端、多桌面绑定;账号/IP 登录限流 | AD/LDAP、自助找回、密码到期、多因素认证;无永久删除账号入口 | +| 资源与已有桌面 | PVE 节点/VM 同步、纳管、电源任务;最多 50 台批量开机/正常关机/重启、逐台预检与批次结果;网页接入配置、关机规格变更与磁盘扩容任务;现场三台桌面已接通 | 多集群、备份/迁移与完整生命周期;现场规格写入缺权限,批量电源真实执行未验收 | +| 快照与手动还原 | 真实列表、关机创建/删除/还原、预检/确认、异步任务、幂等与只读核对;还原撤销票据并要求重新确认初始化 | 公共池自动退出还原;现场缺快照权限,VM100 不支持快照;真实写入和磁盘内容恢复待专用 VM 验收 | +| 镜像模板(本轮) | 发现、登记、版本、系统标记、配置验证、启停;拒绝普通 VM、锁定和直通模板 | 制作模板、镜像发布流水线、连接服务自动探测 | +| 克隆发放(本轮) | 同节点完整/链接克隆、存储与权限预检、异步 UPID、幂等提交、失败与未知结果区分、已知 UPID 重新核对 | 跨节点调度、创建向导直接指定规格、自动初始化与配对、批量发放;现场真实克隆未验收 | +| 桌面连接(本轮) | 管理员修改端点和分辨率/FPS;新克隆默认禁止连接,确认初始化后放行;活动会话时禁止更改 | VM 内自动初始化、自动探测 Sunshine、自动配对 | +| 桌面池(本轮) | 专属/公共类型、容量、默认模板、启停、成员、已授权和活动数量、创建预留 | 自动领取、用户与池的授权规则、退出还原、自动回收、扩容调度、池删除 | +| 终端与会话 | 设备登记、注册、心跳、名称/分组修改、停用/重新激活与旧令牌撤销;授权列表、票据、串流状态、重启/会话重置/诊断日志回执;运维策略模板、按设备/分组下发与应用回执;90 天会话历史与名称快照 | Wi-Fi、USB、应用与显示策略;重新激活需管理员更新设备身份配置 | +| 桌面使用者 | 新增、姓名编辑、启停、更换/解除关联终端、多桌面授权、批量追加/撤销、CSV 用户与授权导入、未消费票据作废 | 用户目录集成、用户自主登录模式 | +| GPU | 已部署真实节点 PCI 显卡清单、IOMMU 分组、当前/待应用配置引用与纳管关系;直通配置由现场手工维护 | 实际驱动/运行占用、独占锁与绑定/解绑、健康指标和 GPU broker;配置引用不表示可分配 | +| 监控告警 | 节点历史曲线、CPU/内存/存储与节点异常/终端离线规则、控制台告警确认及自动恢复;终端 CPU/内存/SoC 温度/运行时长、历史曲线和 30 天留存;缺失值保持空 | VictoriaMetrics、外部通知、终端系统指标告警、完整串流 FPS/RTT 采集 | +| OTA | 尚无发布闭环 | 包签名、适配机型、灰度批次、暂停/恢复、Agent 安装、A/B 回滚 | +| 批量与交付 | 本机 Docker/真实 PostgreSQL、用户/终端/授权 CSV 预检及整批导入、操作审计与任务记录 | 独立服务器、MQTT/Rust Agent、七服务独立部署、备份恢复演练、离线交付 | + +画质/延迟、上网代理、软件白名单和品牌兜底画面继续按原有计划单独处理。 + +## 使用路径 + +控制台地址仍为 `http://127.0.0.1:5182`,使用原管理员账号。新增「镜像模板」「桌面池」,桌面管理新增「从模板创建」及详情中的「配置连接」。 + +用户已确认复用现有 Windows 镜像作为母盘。11-10 再次只读核实:VM100 `win10-test` 正在运行,PVE 标记 `template=0`;系统盘为 `local-lvm:base-100-disk-0,size=60G`,配置包含 GTX 965M 的 `hostpci0` 及相关 `args`。磁盘名称带 `base-` 不等于该 VM 已是平台可登记的专用模板。无需重新安装 Windows,后续以该母盘准备独立模板副本,核查副本的系统身份、账号和 Sunshine 配对,移除或重新安排物理显卡绑定;原 VM100 不在本次核查中转换或修改。现有平台 Token 的克隆权限和存储预检限制仍需解决。 + +1. 在 PVE 准备专用模板,不要将正在使用的 VM100/101/102 直接转换为模板。 +2. 在「镜像模板」发现并登记名称、版本和系统。登记检查模板标志、锁、磁盘容量、直通设备和宿主脚本;模板变动后重新验证。 +3. 在「桌面池」设置容量及可选默认模板,通过「成员」加入已有桌面。成员变更不自动增删用户授权。 +4. 创建向导填写名称、VMID、完整/链接克隆和存储,可选加入池。先预检,再提交任务。硬件继承模板,目标固定为源节点。 +5. 到「任务与审计」查看最终结果。成功只表示 VM 克隆并纳管,不表示 Sunshine 或串流可用。 +6. 完成操作系统身份、网络、Sunshine 和终端配对,再保存连接地址及显示参数,确认初始化;随后单独授权用户。 + +公共池目前采用管理员分配/回收,没有自动领取或退出还原。停用池阻止新建,不会中断池内桌面或撤销已有授权。 + +## 执行边界 + +- 池成员、创建预留、容量编辑使用同一个事务锁,成员加预留不得超额;非空池不能切换类型。 +- 完整克隆按虚拟磁盘容量(含 EFI/TPM)加 1 GiB 余量检查目标存储,较稀疏占用更保守;链接克隆沿用模板存储并检查余量。PVE 最终校验权限和资源。 +- 源 VM 需 `VM.Clone`,目标需 `VM.Allocate`,所用存储需 `Datastore.AllocateSpace`;网络还需现场对应桥/SDN 权限。平台不会自行提权。 +- 网络异常、进程中断或 PVE 任务失败可能留下目标资源,平台不重发克隆、不自动删除 VM;未确认结果继续占预留。 +- 有 UPID 的未确认任务可点「核对克隆结果」,仅查询原任务并确认目标 VM;没有 UPID、明确失败或残留资源须管理员在 PVE 核对,当前无自动清理/释放预留接口。 +- VMID 创建记录保留,失败后也不直接复用。相同请求标识重发不创建第二台 VM,参数变化必须换标识。 +- 只允许一个队列运行者恢复任务。长克隆单独执行,普通管理命令继续处理;当前一次一个克隆,尚无分布式调度。 + +## 现场条件与验收 + +11-10 只读检查:`starhe` 仅有运行中的 VM100/101/102,未发现模板;平台 Token 具有审计及三台 VM 电源权限,尚无克隆、创建及存储分配权限。10:07 查询 `local`、`local-lvm` 可用空间约 10.5 GiB、7.2 GiB,随现场变化。 + +真实接入可验证模板发现、节点存储查询、迁移和页面;不能声称已现场发放新桌面。克隆成功、失败、网络未知及恢复流程由本地 PVE HTTP 测试服务和独立 PostgreSQL 验证。现有业务 VM 未做克隆、关机、重启或模板转换。 + +## 迁移与回退 + +新增 `image_templates`、`desktop_pools`、`provisioning_jobs`,`desktops` 增加可空 `pool_id` 和默认 false 的 `setup_required`;启动时幂等迁移,原桌面保持连接行为。未删除原字段。 + +更新前备份:私有 `.local/live/backups/before-catalog-20261110-095713.dump`;旧 API/控制台镜像标签 `before-catalog-20261110`。不提交含业务数据的备份。 + +旧后端严格映射 `SELECT *`,不能直接读取新增列。回退需停服务、另行备份并评估新增业务数据,再恢复配套数据库快照和旧镜像。恢复会覆盖快照后的数据,当前未做恢复演练,不直接删库或回退列。 + +## 验证记录 + +- 专用临时 PostgreSQL 的 Go 全量测试、`go vet` 通过;覆盖 RBAC、克隆最终确认、重复提交、模板变更、权限/空间不足、容量预留、失败/未知结果、已知 UPID 核对及初始化门禁。 +- 队列修改后另验长克隆期间普通电源任务继续完成;测试仅操作演示数据和本地 PVE HTTP 测试服务。 +- ESLint、TypeScript、3 项前端单元测试和生产构建通过;9 项浏览器用例通过,包含新表单和既有会话、主题与窄屏回归。浏览器用例拦截接口,不操作现场资源。 +- API/控制台 Docker 镜像构建并更新到 5182,完成新增表迁移。更新时 Docker Desktop/WSL 引擎发生故障,重启 Docker Desktop 后恢复并继续迁移,没有删卷或恢复数据库快照。 +- 真实 API 验证登录、模板发现、存储查询、空池创建/扩容/成员保存/停用;只创建的临时空池已定向清理,审计保留。当前有 3 台桌面、0 模板、0 池、0 克隆任务。 +- 更新前后桌面连接配置、用户授权和终端身份映射一致,三台桌面运行、终端在线。真实 API 的 8 个深浅主题页面及 2 个窄屏页面无脚本错误或失败响应。 + +证据:[真实 API 核验](evidence/cloud-catalog-20261110/live-verification.json)、[真实页面核验](evidence/cloud-catalog-20261110/browser-verification.json)、[现场模板页](evidence/cloud-catalog-20261110/live-templates-dark.png)、[现场桌面页](evidence/cloud-catalog-20261110/live-desktops-light.png)、[测试数据池页面](evidence/cloud-catalog-20261110/fixture-pools-light.png)。`fixture-` 图片使用隔离测试数据,不代表现场已有对应模板或桌面池。 + + +## 后续功能:管理员账号维护(11-10) + +已实现真实 API、PostgreSQL 持久化及控制台页面,入口为「系统设置 → 管理员账号」。超级管理员可新增账号、修改显示名称/角色、启停账号和重置其他管理员密码;登录名创建后固定。运维和只读角色不能维护管理员。所有角色通过右上角钥匙按钮修改本人密码,必须验证原密码。 + +- 新建默认只读角色;新设密码至少 12 个字符且 UTF-8 不超过 72 字节。既有 root 账号密码保留,启动时不覆盖已有账号密码或启用状态。 +- 角色或启用状态变更、重置密码、本人改密立即撤销该账号全部会话;仅修改显示名称保留登录。原密码输错提示错误且不退出当前登录。 +- 禁止停用自己或修改自己的角色;事务锁与最新权限复核防止并发停用导致没有启用的超级管理员。登录插入会话与重置/停用串行,避免旧凭据在撤销后新建会话。 +- 停用代替永久删除,保留审计;重新启用后可使用当前密码登录。账号修改与审计同事务,响应和审计不包含明文密码或密码哈希。 +- 新增 `administrators.enabled boolean NOT NULL DEFAULT true`,既有账号默认启用。更新前备份 `.local/live/backups/before-admin-20261110-102640.dump`,旧镜像标签 `before-admin-20261110`。旧 API 的严格 `SELECT *` 映射不能直接读取新增列,回退仍需评估数据并配套数据库快照;本轮未执行回退或删除业务卷。 + +Go 全量测试与 `go vet` 通过,使用独立临时 PostgreSQL,覆盖账号生命周期、权限/密码校验、会话撤销及并发超级管理员保护。前端 ESLint、类型检查、3 项单元测试、生产构建和 13 项隔离接口浏览器回归通过。全目录浏览器命令曾包含只适用于 demo 的旧用例,被 production 模式守卫阻止;没有执行其电源操作。随后显式选择管理员、模板池及 UI 三组用例,13/13 通过。 + +已更新本机 5182 API/控制台,用临时账号完成真实新增、角色修改、密码重置、停用/恢复、本人改密、旧密码拒绝、全部旧会话失效与权限拒绝验证。临时账号及其会话已定向清理,审计保留;原 root 账号字段和密码哈希指纹、桌面配置及用户绑定与更新前完全一致。查询时 VM100/101/102 均运行,1 台终端在线,没有发送 VM 电源、克隆或 GPU 操作。 + +证据:[真实账号接口核验](evidence/cloud-administrators-20261110/live-verification.json)、[隔离测试深色页面](evidence/cloud-administrators-20261110/fixture-accounts-dark.png)、[隔离测试窄屏页面](evidence/cloud-administrators-20261110/fixture-accounts-mobile.png)。`fixture-` 图片中的运维/财务账号为测试数据。 + +真实浏览器补充:深浅主题、390px 窄屏及新增/本人改密弹窗均通过;弹窗取消未发送修改请求,无脚本错误或失败响应。见[页面核验](evidence/cloud-administrators-20261110/browser-verification.json)、[真实账号页](evidence/cloud-administrators-20261110/live-accounts-light.png)、[真实窄屏新增弹窗](evidence/cloud-administrators-20261110/live-create-mobile.png)。 + +## 后续功能:终端与用户维护(11-10) + +新增终端资料编辑、停用/重新激活,用户姓名编辑/启停和批量追加/撤销桌面授权;原有换绑和授权流程增加确认及旧票据作废。18 项隔离浏览器回归、独立数据库 Go 全量测试及静态检查通过。使用路径、重新激活设备配置步骤、执行边界与回退见[本轮说明](cloud-lifecycle-20261110.md)。 + +已更新 5182 并以 2 台临时设备、2 位临时用户完成真实流程验收;临时数据清理后原设备身份、管理员密码、桌面配置、用户与授权均一致,原盒子心跳继续推进、3 台 VM 运行。见[真实核验](evidence/cloud-lifecycle-20261110/live-verification.json)。 + +## 后续功能:终端运维策略(11-10) + +「终端策略」已部署 5182:模板版本固定、复制新版本、按设备/分组下发、离线等待、失败原因、重新下发与旧版本恢复。第一批支持心跳、命令轮询、桌面列表刷新三项管理间隔;网络、USB、白名单、显示策略仍待对应客户端能力。旧客户端明确提示更新,不会伪装应用成功。 + +真实盒子已更新为 `qt-platform/0.3.0`,在配置版本 2 应用命令 5 秒/列表 20 秒,再在版本 3 恢复默认 30/3/15 秒,均收到设备成功回执并核对设备 0600 策略文件。原身份、root 密码、桌面参数、用户和授权快照保持一致,三台 VM 运行,无活动串流。独立 PostgreSQL Go 全量测试/vet、前端检查/构建、22 项浏览器流程及盒子 5 项 CTest 已通过。见[功能说明](cloud-terminal-policies-20261110.md)与[真实验收](evidence/cloud-policies-20261110/live-verification.json)。 + +策略页面深浅主题、390px 窄屏及弹窗已完成真实只读检查,无脚本错误、失败响应或修改请求。见[页面验收](evidence/cloud-policies-20261110/browser-verification.json)。 + +## 后续功能:CSV、会话历史与监控告警(11-10) + +用户/终端/授权 CSV 预检与整批幂等提交、90 天会话历史、节点历史采样、告警规则/确认/自动恢复已补齐。具体入口、CSV 表头、留存与验收边界见[本轮说明](cloud-operations-20261110.md)。VictoriaMetrics、外部通知和完整串流指标采集仍未接入;终端系统指标已在后续批次接通。 + +已部署本机 5182,并完成真实 CSV 临时身份、会话事件及告警触发/确认/恢复验收;会话使用临时设备合成事件,未开启真实视频串流。临时数据清理后原业务快照一致,原盒子心跳正常、三台 VM 运行。27 项浏览器回归及真实页面深浅/窄屏检查通过,见[真实接口证据](evidence/cloud-operations-20261110/live-verification.json)。 + +## 接入配置与规格管理更新 + +已部署网页 PVE 接入、桌面 CPU/内存与单磁盘扩容任务、账号/IP 登录限流。真实接入保存、热加载、重启保留、临时账号限流、三台 VM 规格读取与运行拦截已验收。现场 Token 缺规格写权限,未修改任何真实 VM;写入流程仅在隔离测试验证。入口、权限、未知结果和密钥备份边界见[本批说明](cloud-configuration-20261110.md)。 + + +## 后续功能:快照与手动还原(11-10) + +已部署「桌面管理 → 查看详情 → 快照与还原」,接口与页面、权限、磁盘范围、维护互斥和未知结果保护见[说明](cloud-snapshots-20261110.md)。关机无内存快照可创建、删除叶快照和手动还原;本期不支持直通或宿主自定义配置等复杂布局。还原包括快照内的数据盘和配置,不保证只恢复系统盘。后台任务成功需原 PVE UPID 成功及实际状态确认,超时不重发。 + +37 项浏览器回归及 Go 全量测试通过;现场仅只读检查,三台 VM 运行、原业务映射一致。当前 Token 缺快照权限,VM100 的 PVE 快照能力检查为不支持,未执行真实 VM 快照写入。公共池自动领取/退出还原仍在计划中。 + + +## 桌面批量电源操作(2026-11-10) + +5182 的「桌面管理」新增跨页选择、批量开机/正常关机/重启和批次历史,每批最多 50 台。逐台真实预检、5 分钟签名凭证、确认后幂等提交;执行前复查,未确认结果不重发并阻止后续修改和新连接。Go 全量与静态检查、前端检查和 45 项隔离接口页面回归通过;真实环境只做预检与取消提交,三台桌面继续运行。新增表、任务状态、备份与现场边界见[功能说明](cloud-batch-operations-20261110.md)。专用测试 VM、Windows 副本和存储准备按用户要求暂缓。 + + +## GPU 资源查看(2026-11-10) + +5182 侧栏新增「GPU 资源」,三角色均可读取节点显卡清单、PCI 地址、IOMMU 分组与当前/待应用的虚拟机配置引用,支持搜索、筛选与详情。现场确认 Intel 核显、GTX 965M 及 Windows VM100 引用;自定义配置导致不完整时明确提示,不将未发现引用视为可分配。只新增 GET 接口,无数据库迁移或 GPU 写入;Go 全量与静态检查、前端检查和 51 项页面回归通过。接口、权限、备份和现场证据见[功能说明](cloud-gpu-inventory-20261110.md)。 + + +## 终端系统指标与历史(2026-11-10) + +5182 的「终端管理 → 详情 → 查看运行指标」已接通真实 CPU、内存、SoC 温度和运行时长,支持 1 小时/24 小时/7 天趋势、30 天留存、缺失断线和过期隐藏。盒子升级为 `qt-platform/0.4.0`,真实连续采样已入库,旧 Agent 兼容通过;没有将目标 FPS 或请求耗时当作串流实测指标。Go 全量及静态检查、前端检查/构建、57 项页面回归和盒子 6 项 CTest 通过,真实深浅主题及 390px 页面检查无异常。原设备配置、账号密码哈希指纹、桌面参数和授权一致,三台 VM 的配置 digest 与运行状态保持。详见[采集范围与验收](cloud-terminal-metrics-20261110.md)。串流 FPS/RTT、GPU 健康指标及系统指标告警仍待接入。 diff --git a/docs/cloud-gpu-inventory-20261110.md b/docs/cloud-gpu-inventory-20261110.md new file mode 100644 index 0000000..083eb5b --- /dev/null +++ b/docs/cloud-gpu-inventory-20261110.md @@ -0,0 +1,49 @@ +# GPU 清单与配置引用(2026-11-10) + +已部署真实控制台 [GPU 资源](http://127.0.0.1:5182/gpus),侧栏入口为「GPU 资源」。沿用现有深浅主题和图标,三个管理员角色均可查看。 + +## 当前能力 + +- 选择计算节点,读取真实显示控制器类 PCI 设备的厂商、型号、PCI 地址、厂商/设备 ID 和 IOMMU 分组;分组 0 正常显示,无分组报告显示「未报告」。 +- 搜索显卡、PCI 地址、虚拟机名称/VMID 和纳管桌面名称,按配置引用状态筛选。 +- 检查该节点所有当前 PVE 凭据可见的 QEMU VM,包括未纳管 VM、关机 VM 和模板;详情区分当前配置、待应用配置与待移除项。 +- 解析 `hostpciN` 的短地址、完整域地址、整槽所有功能、分号多地址、PCI 映射和介导设备标记;显示引用 VM、配置槽位、平台纳管关系和映射来源。 +- 展示已检查/可见 VM 数、读取时间与检查失败原因,超过 90 秒提醒刷新。手动刷新重新读取,页脚明确说明按需读取,无后台高频 GPU 采集。 + +状态含义: + +| 状态 | 含义 | +| --- | --- | +| 有配置引用 | 查到一台 VM 的当前或待应用配置引用 | +| 多 VM 引用 | 查到多台 VM 的配置引用,需进一步核查;不直接判定运行冲突,介导设备也可能有合法共享 | +| 未发现配置引用 | 本次 QEMU 配置检查无异常且没有查到引用,不表示设备可分配 | +| 待核查 | 没有查到引用,但检查范围不完整,例如部分 VM 不可读、映射不可见或有自定义参数 | + +## 接口和边界 + +新增管理员鉴权后的 `GET /api/v1/resources/gpus?node=<节点>`,返回节点、读取时间、检查完整性、可见/已检查 VM 数、显卡及逐条引用、检查问题;没有 GPU 写接口。请求节点必须在实时 PVE 资源清单中可见。 + +读取 PVE `/cluster/resources`、`/nodes/{node}/hardware/pci?verbose=1`、`/nodes/{node}/qemu/{vmid}/pending` 和根路径权限;按需读取 `/cluster/mapping/pci`。PCI 清单需要根路径 `Sys.Audit` 等 PVE 允许的审计权限,判断全局 VM 可见范围检查根路径 `VM.Audit`;映射仍受 `Mapping.Audit/Use/Modify` 可见性约束。平台不自动增加权限。 + +每节点每次最多检查 200 台 VM,以 4 个并发读取任务、12 秒总预算控制请求;超出范围、读取失败或映射候选不唯一时标明不完整。自定义 `args` / `hookscript` 的额外引用不解析,不将其原文输出至接口。前端请求失败显示明确错误,旧清单清除,避免把旧记录当作本次结果。 + +本轮不查询宿主驱动实际绑定、运行中 QEMU 的实际设备、LXC 设备挂载、GPU 温度/利用率/显存,也不确认硬件健康、IOMMU 隔离可用性或硬编能力。多次 PVE 读取不是原子状态快照。配置引用、运行状态和可分配性是不同信息;绑定/解绑、独占锁、GPU broker 和健康采集仍待实现。 + +此功能无数据库表或字段迁移,不创建任务、审计或显卡分配记录。API 契约见 [OpenAPI](openapi/platform.v1.json)。 + +接口语义参考 Proxmox 官方 [PCI API 源码](https://github.com/proxmox/pve-manager/blob/master/PVE/API2/Hardware/PCI.pm)、[映射 API 源码](https://github.com/proxmox/pve-manager/blob/master/PVE/API2/Cluster/Mapping/PCI.pm) 和 [PCI 直通文档](https://github.com/proxmox/pve-docs/blob/master/qm-pci-passthrough.adoc)。 + +## 验证与部署 + +- 本地 PVE HTTP 测试服务和独立临时 PostgreSQL 覆盖只读请求、管理员鉴权及只读角色访问、输入校验、PCI 功能/域地址、映射解析、待应用与待移除、关机 VM 引用、未纳管 VM、多引用、权限缺失、配置失败及原始参数不泄露。Go 全量测试和 `go vet` 通过。 +- ESLint、TypeScript、3 项前端单元测试和生产构建通过;新增 6 项 GPU 浏览器测试,连同已有用例共 51/51 通过。覆盖错误重试、过期、空态、只读角色、搜索、详情与 390px 浅色窄屏;页脚刷新说明修正后,GPU 6 项用例再次通过。 +- API/控制台镜像已更新到 5182,保留原 PostgreSQL 容器和业务卷。真实浏览器深浅主题与 390px 页面检查通过,无脚本错误或失败响应。 +- 现场 `starhe` 读取 2 块显卡、检查 3 台 VM:Intel `0000:00:02.0` / IOMMU 0;NVIDIA GTX 965M `0000:01:00.0` / IOMMU 2,当前配置引用为 VM100 `win10-test` / 已纳管 Windows 10。 +- VM100 有自定义参数,因此页面说明检查未完整,Intel 显示「待核查」,没有标成可分配。没有改动显卡直通或启动任何 VM 电源/快照/磁盘操作。 +- 三台 VM 仍运行,配置摘要和原管理员密码哈希指纹、桌面配置、授权、终端身份与更新前一致;盒子心跳正常,任务及快照数量未增加。 + +更新前数据库备份:私有 `.local/live/backups/before-gpu-inventory-20261110-170206.dump`;已验证备份目录可读。旧 API/控制台镜像标签均为 `before-gpu-inventory-20261110-170206`。本轮无数据库结构变更,可在核实版本配套后回退这两个应用镜像;无须因本功能删除或重建数据库。备份恢复演练尚未执行。 + +证据:[51 项页面回归](evidence/cloud-gpu-inventory-20261110/playwright-report.json)、[真实接口与业务基线核查](evidence/cloud-gpu-inventory-20261110/live-verification.json)、[真实页面核查](evidence/cloud-gpu-inventory-20261110/browser-verification.json)、[现场清单](evidence/cloud-gpu-inventory-20261110/live-gpu-inventory-dark.png)、[现场详情](evidence/cloud-gpu-inventory-20261110/live-gpu-detail-light.png)、[现场窄屏](evidence/cloud-gpu-inventory-20261110/live-gpu-detail-mobile.png)。`fixture-` 图片使用测试数据,不代表现场存在测试显卡或映射。 + +专用 VM、Windows 副本、存储准备和真实快照内容恢复仍按用户要求暂缓,后续统一见 [计划](roadmap.md)。 diff --git a/docs/cloud-lifecycle-20261110.md b/docs/cloud-lifecycle-20261110.md new file mode 100644 index 0000000..37990f2 --- /dev/null +++ b/docs/cloud-lifecycle-20261110.md @@ -0,0 +1,53 @@ +# 终端生命周期与用户维护(2026-11-10) + +本轮补齐现有控制台的设备与人员日常维护,沿用 `ui/` 的深浅主题和表格、抽屉、确认弹窗。管理员账号和桌面使用者仍分开管理。 + +## 使用入口 + +| 入口 | 功能 | +| --- | --- | +| 终端管理 → 编辑资料 | 修改设备名称、分组;SN 保持固定 | +| 终端管理 → 详情 → 设备管理 | 停用设备、生成新的激活信息;只允许超级管理员 | +| 用户与授权 → 编辑资料 | 修改姓名、启用/停用用户;登录名固定,原终端和桌面授权保留 | +| 用户与授权 → 编辑授权 | 更换/解除关联终端、启停、设置完整桌面列表;已有流程增加确认和旧票据作废 | +| 用户与授权 → 勾选用户 → 批量授权 | 追加分配或撤销指定桌面,保留其他授权;一次最多 100 位用户、100 台桌面 | + +运维仍可执行原有重启、重置会话和日志命令;只读人员只能查看。停用设备不再提供远程命令入口,后端也拒绝新命令。批量操作对所选用户和桌面先整体校验,再一次提交;目标不存在则整体回滚。 + +## 停用与重新激活 + +- 停用立即撤销设备令牌和旧激活密钥,清理该设备连接票据;设备的 SN、业务 ID、分组和用户关联保留。 +- 重新激活会撤销旧身份,切换为待激活,生成只显示一次的新密钥。再次生成会作废上一次尚未使用的密钥;注册成功后密钥不能再次使用。 +- 活动会话/连接预约(有桌面关联且最近 90 秒有心跳)或排队/执行中的设备命令会阻止停用和重新激活,返回明确原因。先结束/重置会话,等待命令回执或核对超时状态。旧心跳超过在线窗口时按现有离线判定处理,平台身份撤销不等于网络层强制结束既有 Moonlight 串流。 +- 身份变更重置当前会话、FPS/RTT 和注册元信息;完整历史会话与指标查询尚未实现。永久删除设备/用户未增加,使用停用保留业务关系与审计。 + +重新激活不会通过已撤销的通道自动安装新身份。设备管理员需在终端空闲时备份并更新原 `platform.json`:保留原 `url` 和 `caFile`,移除旧 `deviceToken`,写入新 `activationSecret`,然后重启客户端使其重新注册。Qt 客户端优先尝试已有令牌,所以只添加激活密钥而保留旧令牌不能完成恢复。配置按凭据管理,权限保持 0600,不将密钥写入仓库或审计。 + +用户停用、授权撤销和更换终端会阻止后续连接;已建立串流仍通过独立的“重置会话”处理。授权修改和用户资料保存作废关联设备未消费票据,避免恢复启用或换绑后继续使用旧票据。桌面批量分配不会自动开启停用用户或自动关联终端。 + +## 接口与一致性 + +新增 `PUT /terminals/{id}`、`POST /terminals/{id}/disable`、`POST /terminals/{id}/reactivate`、`PUT /users/{id}`、`POST /users/batch-bindings`,均限超级管理员,已同步 OpenAPI 和前端类型。已有 `PUT /users/{id}/bindings` 保持兼容。 + +设备认证与该次心跳、命令领取/回执或票据操作共用事务和设备行锁;停用、重新激活、命令提交按同一设备行串行,避免旧请求在撤销后恢复身份或领取新命令。票据创建/消费、授权写入、设备身份重置和桌面连接配置共用授权事务锁,避免校验后权限变更或票据删除与消费的锁顺序冲突。普通设备心跳和命令处理不占用全局授权锁。新管理操作及其审计同事务,凭据不进入公开列表或审计。 + +数据库新增 `terminals.enabled boolean NOT NULL DEFAULT true`;原有设备默认启用,既有设备令牌、SN 和授权不重建,启动不重新激活已停用设备。新增数据库字段要求配套后端模型,回退不能直接将旧 API 接到新结构。 + +## 验证 + +独立临时 PostgreSQL 的 Go 全量测试与 `go vet` 通过;覆盖身份撤销、旧票据失效、重复注册、激活密钥轮换、重启保留停用、会话/任务保护、RBAC、批量幂等与回滚。前端类型检查、ESLint、3 项单元测试、生产构建和 18 项隔离接口浏览器回归通过。测试中的设备、用户和桌面为隔离数据,没有向现场设备或 PVE 发命令。 + +界面证据:[终端详情](evidence/cloud-lifecycle-20261110/fixture-terminal-detail-light.png)、[窄屏批量授权](evidence/cloud-lifecycle-20261110/fixture-batch-users-mobile.png)、[测试记录](evidence/cloud-lifecycle-20261110/test-verification.json)。`fixture-` 截图不代表现场实际设备和人员。 + +更新前备份 `.local/live/backups/before-lifecycle-20261110-105516.dump`,旧镜像标签 `before-lifecycle-20261110`。备份、原凭据指纹及数据快照只保存在忽略提交的私有目录。回退需评估快照之后的数据,恢复配套数据库与镜像;本轮不执行破坏性回退,不删除正式数据卷。 + +终端配置模板/热更新、自动领取与还原、GPU 调度、历史监控告警、OTA 和独立服务器交付仍按[功能核对清单](cloud-feature-progress-20261110.md)继续推进。 + + +## 本机真实验收 + +11-10 已构建并更新 5182 API/控制台,完成设备启用字段迁移。使用 2 个临时设备身份、2 位临时用户验证资料保存、用户启停、换绑/占用冲突、批量追加/撤销、票据作废、设备停用、重复生成激活密钥、一次性注册和旧令牌拒绝;没有向业务 VM 或真实盒子发送命令,没有实际创建 VM 连接预约。临时设备、用户、绑定与票据已定向清理,审计保留。 + +更新前后管理员及密码指纹、原设备身份凭据/元信息、桌面连接配置、用户和绑定逐项一致。原盒子心跳在更新后继续推进,查询时 1 台终端在线,VM100/101/102 均运行。见[真实接口核验](evidence/cloud-lifecycle-20261110/live-verification.json)。活动会话和待执行命令保护在隔离环境验证,未为测试打断现场业务连接。 + +部署后的真实页面已检查深浅主题、390px 窄屏、终端/用户资料弹窗及批量授权弹窗,取消未发送修改请求,无脚本错误或失败响应。证据:[页面核验](evidence/cloud-lifecycle-20261110/browser-verification.json)、[真实终端详情](evidence/cloud-lifecycle-20261110/live-terminal-detail-light.png)、[真实窄屏批量授权](evidence/cloud-lifecycle-20261110/live-batch-users-mobile.png)。 diff --git a/docs/cloud-live-integration.md b/docs/cloud-live-integration.md index f0188d4..e2a73bc 100644 --- a/docs/cloud-live-integration.md +++ b/docs/cloud-live-integration.md @@ -1,6 +1,10 @@ # 现场真实接入 -更新:2026-11-09。与原演示环境分开,当前 API 使用 `production` 模式和独立 PostgreSQL,不生成演示节点、桌面或用户。服务运行在开发机 Docker Desktop;这是接入现场设备的联调环境,尚未迁到独立云服务器。 +更新:2026-11-10。与原演示环境分开,当前 API 使用 `production` 模式和独立 PostgreSQL,不生成演示节点、桌面或用户。服务运行在开发机 Docker Desktop;这是接入现场设备的联调环境,尚未迁到独立云服务器。 + +11-10 模板、桌面池与克隆接口及页面扩展见[功能核对与验收边界](cloud-feature-progress-20261110.md)。现场暂无 PVE 模板和创建权限,克隆实际发放仍待接入条件满足后验收。 + +Windows 模板来源已由用户确认为现有 Windows 镜像:复用 VM100 的系统作为母盘,不要求重装。VM100 当前仍在运行,PVE `template=0`,60 GiB 系统盘且带物理 GPU 直通;尚未制作独立模板副本。这里只读核查,没有转换现有桌面或修改显卡配置。 ## 日常启动与入口 @@ -32,6 +36,8 @@ PVE Token 继承专用用户权限:根路径 `PVEAuditor` 负责只读发现 1. 管理员登记 SN,生成一次性激活密钥。盒子读取 `~/.config/vdi-terminal/platform.json`,通过注册接口取得设备 Token,以 0600 原子保存并删除激活密钥。 2. 每 30 秒发送真实心跳;每 15 秒在选择页刷新授权桌面;每 3 秒领取远程命令。平台配置存在时不回退到本地授权或本地票据,列表与连接参数由平台维护。 + + 11-10 修复冷启动认证失败的页面回退:启动始终显示选择页,平台未就绪时按 2~30 秒退避自动重试,成功后加载授权桌面;不会退回旧账号密码页。此前首次失败的底层原因缺少足够日志,新增 HTTP 状态/网络错误码记录供复查,详见[终端说明](terminal-guide.md#session)。 3. 点击连接后申请、消费单次票据。消费只登记“连接中”,Moonlight 实际画面就绪后才上报 connected,计入活动连接。 4. 自然退出、连接失败、管理员重置都会按 sessionId 释放预约。结束事件和回执写入待发队列,网络恢复后重试;已执行的命令不自动重放。旧会话事件或迟到的重置回执不能清理新会话。 5. “重置会话”等待 Moonlight 进程真正退出后回执。“收集日志”上传受限诊断信息与脱敏日志尾部;管理员/运维可在成功任务详情下载,只读角色不可读取。 @@ -67,7 +73,7 @@ PVE SSH 密码通过交互提示或 `VDI_PVE_SSH_PASSWORD` 读取;工具不在 本轮初始备份路径在盒子 `~/vdi-terminal/backups/platform-live-current-backup` 指向的目录。备份包括接入前 kiosk-shell、client.conf、desktops.json。维护时先结束串流,再原子恢复该 kiosk-shell 并将 platform.json 移至受限备份目录,由原有监控进程重启 kiosk。恢复后是原本的本地 POC 模式,不再从平台授权。无需回退 Moonlight 二进制或重启 VM。 -只撤销 PVE 接入时,可禁用 `vdi-cloud@pve` 用户或移除 `console` Token;不要删除 root 或其他账号。独立部署、用户管理与密码轮换、登录限流、备份恢复演练、设备撤销/重新激活、OTA 和多租户仍属于后续产品化范围。 +只撤销 PVE 接入时,可禁用 `vdi-cloud@pve` 用户或移除 `console` Token;不要删除 root 或其他账号。独立部署、用户目录集成、登录限流、备份恢复演练、终端策略、OTA 和多租户仍属于后续产品化范围。设备停用/重新激活及用户维护见[11-10 功能说明](cloud-lifecycle-20261110.md)。管理员账号维护与手工密码修改已于 11-10 接通。 ## 本轮现场验收 @@ -79,6 +85,8 @@ PVE SSH 密码通过交互提示或 `VDI_PVE_SSH_PASSWORD` 读取;工具不在 - 现场 Playwright 页面流程通过:正式登录、3 台真实桌面、真实设备在线、下发日志采集、收到成功回执并下载实际 boot ID/uptime/日志内容。截图见 [真实总览](evidence/cloud-live-20261109/overview.png)、[真实桌面](evidence/cloud-live-20261109/desktops.png)。 - 现场 kiosk-shell SHA256:`2cca9d2d183c2f34f0f6172db207649decb2799414f9443043d175bbb19d33a1`。Moonlight 二进制未更换。 +以上为 11-09 接入验收。11-10 启动认证重试修复已更新 kiosk-shell 为 SHA256 `9dba9c66092aa9ccf67477b305aaefe09f52eabb8c6b30dbbcbb1ce43dc7a6a6`,CTest 4/4 与部署后真实选择页核验通过;原接入身份、票据、三桌面参数、Moonlight 均保留,未重启云桌面或宿主,未再次断电验收。备份与证据见[终端说明](terminal-guide.md#session)。 + 浏览器现场用例独立执行,避免运行演示库用例操作真实 VM: ```powershell @@ -87,3 +95,43 @@ npx playwright test --config=playwright.live.config.ts ``` 该用例读取本机私有初始账号文件,验证正式登录、真实桌面/设备以及日志收集下载,不发 VM 电源操作。完整演示 E2E 仍要求 demo 模式;数据库集成测试仍只指向专用 vdi_test。 + +## 管理员维护更新(2026-11-10) + +5182 已部署「系统设置 → 管理员账号」和右上角本人改密入口,真实数据库验证新增/角色/启停/重置/改密及全部旧登录失效。临时账号已清理,原 root 密码、桌面连接参数、用户授权保持一致,3 台 VM 运行且终端在线。新增 `administrators.enabled` 默认 true;启动不重置既有密码或停用状态。更新前保留数据库备份及 `before-admin-20261110` 镜像,回退不能直接将旧 API 接到新增列数据库。完整说明见[功能核对清单](cloud-feature-progress-20261110.md)。 + +## 终端与用户维护更新(2026-11-10) + +5182 已部署终端资料编辑、设备停用/重新激活、用户资料/启停及批量桌面授权。真实临时设备与用户验收后定向清理,原 root 密码、设备凭据、桌面配置、用户和授权保持一致,原盒子心跳继续推进。设备重新激活需管理员更新设备身份配置,不会通过旧令牌自动恢复。新增字段、回退边界和证据见[本轮说明](cloud-lifecycle-20261110.md)。 + +## 终端策略更新(2026-11-10) + +5182 新增「终端策略」。真实设备客户端为 `qt-platform/0.3.0`,kiosk-shell SHA256 `690a74ee5b077ff96e385f2ba45ca756f1879223f7433e63ec1833eb0f3a628b`。盒子原二进制备份 `~/vdi-terminal/backups/policy-20261110/kiosk-shell`;只替换空闲 kiosk 子进程,由既有看门狗接管,未重启 LightDM、Moonlight、PVE 或 VM。真实配置变更与恢复均已回执,最终为 30/3/15 秒管理间隔。本段记录策略批次的 0.3.0 版本,前文 SHA 为历史接入记录;最新 0.4.0 指标批次见后文。范围、接口与备份见[终端策略说明](cloud-terminal-policies-20261110.md)。 + +## 11-10 批量导入、会话历史与告警 + +新增 CSV 用户/终端/授权导入、90 天会话历史、PostgreSQL 节点采样及控制台告警,入口、接口、留存、迁移与验收范围见[功能说明](cloud-operations-20261110.md)。VictoriaMetrics 和外部通知仍待接入。 + +## 11-10 接入配置、规格与登录限流 + +PVE 连接可在「系统设置 → 配置 Proxmox 接入」检查和保存,当前原地址/Token/CA 已入加密存储并通过重启核对,密钥不回显。JWT Secret 与数据库密文必须配套保管。桌面详情新增「规格管理」,三台真实 VM 读取和运行中拒绝修改已验证;现场缺 VM.Config.CPU/Memory/Disk 权限,未进行真实规格写入。管理员登录新增 5 分钟账号 10 次/来源 IP 30 次限制;设备心跳不受登录预算影响。详见[入口、测试与恢复边界](cloud-configuration-20261110.md)。 + + +## 快照与手动还原(2026-11-10) + +新增 snapshot_jobs 表及电源/规格/快照任务的共同活动索引,启动幂等迁移。控制台入口为「桌面管理 → 详情 → 快照与还原」;仅支持关机后的普通虚拟磁盘与无内存还原,含数据盘的快照也会回退其数据。原任务未知不重发,成功还原后须重新确认连接初始化。接口、测试、备份和现场权限/VM100 限制见[本批说明](cloud-snapshots-20261110.md)。本批未进行真实 VM 快照写入,公共池自动领取/还原尚未实施。 + + +## 桌面批量电源操作(2026-11-10) + +5182 的「桌面管理」新增跨页选择、批量开机/正常关机/重启和批次历史,每批最多 50 台。逐台真实预检、5 分钟签名凭证、确认后幂等提交;执行前复查,未确认结果不重发并阻止后续修改和新连接。Go 全量与静态检查、前端检查和 45 项隔离接口页面回归通过;真实环境只做预检与取消提交,三台桌面继续运行。新增表、任务状态、备份与现场边界见[功能说明](cloud-batch-operations-20261110.md)。专用测试 VM、Windows 副本和存储准备按用户要求暂缓。 + + +## GPU 资源查看(2026-11-10) + +5182 侧栏新增「GPU 资源」,三角色均可读取节点显卡清单、PCI 地址、IOMMU 分组与当前/待应用的虚拟机配置引用,支持搜索、筛选与详情。现场确认 Intel 核显、GTX 965M 及 Windows VM100 引用;自定义配置导致不完整时明确提示,不将未发现引用视为可分配。只新增 GET 接口,无数据库迁移或 GPU 写入;Go 全量与静态检查、前端检查和 51 项页面回归通过。接口、权限、备份和现场证据见[功能说明](cloud-gpu-inventory-20261110.md)。 + + +## 11-10 终端系统指标 + +当前真实盒子为 `qt-platform/0.4.0`,kiosk SHA256 `6ff0697e9c5efb7428053d54af6d52f360e726eea5962e944f592c802f7463e1`。旧二进制保留在 `~/vdi-terminal/bin/kiosk-shell.before-terminal-metrics-20261110`。CPU、内存、SoC 温度和运行时长已连续上报入库,控制台详情可查历史。只在空闲时替换 kiosk,未重启 LightDM 或宿主;身份、策略文件、桌面参数、账号授权及三台 VM 配置核对一致。数据库备份、API、留存与采集限制见[系统指标说明](cloud-terminal-metrics-20261110.md),现场结果见[验收数据](evidence/cloud-terminal-metrics-20261110/live-verification.json)。 diff --git a/docs/cloud-operations-20261110.md b/docs/cloud-operations-20261110.md new file mode 100644 index 0000000..149c5cb --- /dev/null +++ b/docs/cloud-operations-20261110.md @@ -0,0 +1,83 @@ +# 批量导入、会话历史与监控告警(2026-11-10) + +本轮按原需求补齐三项真实功能,使用 Go API、PostgreSQL 与现有 Vue 控制台。入口保持 http://127.0.0.1:5182,沿用原管理员账号。没有修改盒子串流或画质参数。 + +## 使用入口 + +| 功能 | 入口 | 权限 | +| --- | --- | --- | +| 用户 CSV | 用户与授权 → 导入用户 | 超级管理员 | +| 终端 CSV | 终端管理 → 批量导入 | 超级管理员 | +| 授权 CSV | 用户与授权 → 导入授权 | 超级管理员 | +| 会话历史 | 远程会话 → 历史记录 | 所有管理员角色只读 | +| 资源曲线、告警 | 侧栏 → 监控告警 | 所有角色查看,超管/运维确认,超管编辑规则 | + +## CSV 格式与提交规则 + +UTF-8 CSV,支持 BOM 和标准双引号字段;每批最多 200 行、512 KiB。页面可下载空白模板、上传文件或直接粘贴。先预检,逐行显示错误并可下载错误 CSV;所有行通过后才能整批提交。 + +```csv +username,displayName +worker01,研发人员 +``` + +```csv +sn,name,group +BOX-NEW-001,研发终端,研发组 +``` + +```csv +username,terminalSn,desktopIds +worker01,BOX-NEW-001,desktop-id-1;desktop-id-2 +``` + +表头名称固定,列顺序可调整。用户和终端只新增,已存在或文件内重复的账号/SN会阻止整批导入。用户无需在 CSV 填密码,仍是用户与设备绑定模式。 + +授权导入仅追加桌面,不撤销原授权;空 terminalSn 保持原终端。不能抢占他人终端、给已有其他终端的用户换绑或绑定停用终端。预检后原授权发生变化时,提交返回冲突,整批不写入,需重新预检。已停用用户不会因导入授权被重新启用;既有串流不因此强制结束。 + +提交使用幂等请求标识,同一管理员重发相同内容不重复创建。新终端激活密钥仅在首次成功响应中返回,页面可下载并在关闭时清除;数据库和审计不保存密钥明文或源 CSV。重复请求不再返回密钥,丢失后到终端管理重新激活。 + +接口:`POST /api/v1/imports/preview`、`POST /api/v1/imports/commit`,后者要求 `Idempotency-Key` 和预检返回的 `previewToken`。 + +## 会话记录 + +`GET /api/v1/sessions/history` 支持关键词、状态、起止时间和分页,默认最近 24 小时、最多查询 90 天。记录保留终端、用户及桌面名称快照,后续改名不影响旧记录。 + +- 消费票据生成“连接中”预约,不能记作已建立视频串流。 +- 客户端画面就绪事件写入首次连接时间,重复事件不重置时间;结束事件记录结束原因。 +- 新连接替代旧预约、身份重置、90 秒无心跳分别记录中断原因,旧设备或旧会话事件不能结束新会话。 +- 超时只结束历史片段,不自动关闭 VM、不重置终端串流。之后合法事件或已连接状态的心跳恢复时,另记“首次观测”片段。 +- 升级前的历史不补造。升级中已有连接从首次观测开始,不能将观测时间当成原始连接开始时间。 +- 时长从画面就绪计算;活动会话只算到最近上报,尚未就绪显示“未就绪”。结束记录保留 90 天,活动记录不删。 + +这仍依赖终端对实际串流状态的上报;本轮真实 API 验证使用临时设备发送合成事件,没有开启现场视频串流。 + +## 监控与告警 + +PVE 节点同步约每 30 秒保存 CPU、内存、存储使用率,使用 PostgreSQL 保留 30 天;控制台每 15 秒刷新,单次可看最近 1 小时、6 小时、24 小时或 7 天,按时间范围分桶平均。存储值来自 PVE 节点 disk/maxdisk,不表示每台客体磁盘使用率。缺失分母或离线节点保存空值;无有效采样显示空态,采样间隔过大时曲线断开,不补零。 + +启动幂等创建五条默认规则,已有管理员修改不被覆盖:CPU/内存/存储 90% 持续 120 秒,节点离线或采集异常 120 秒,已激活启用终端离线 180 秒。水位阈值可设 1~100%、持续 0~3600 秒;离线阈值 90~86400 秒。 + +评估器每 15 秒运行,多 API 实例通过事务锁避免重复事件。指标持续越线才触发,同一规则/目标只保留一条活动告警;缺少新采样不会假称恢复,采集异常由独立规则检测。节点离线状态持续计时,在线且采集正常后恢复;终端真实心跳恢复后自动解除离线告警。目标移除或停用会结束对应告警。 + +确认仅表示已知悉,不将告警改为恢复;保留首次确认人和时间。修改/停用规则会结束旧告警并重新计时。已恢复事件保留 90 天。 + +接口包括 `/api/v1/monitor/series`、`/monitor/rules`、`/monitor/alerts` 及 `/monitor/alerts/:id/acknowledge`,完整契约见 [OpenAPI](openapi/platform.v1.json)。 + +本轮只实现控制台内告警。VictoriaMetrics、外部邮件/Webhook 通知、终端温度及完整 FPS/RTT 历史采集仍未接入;现有独立 vdi-monitor 的诊断采集继续保留。 + +## 迁移与回退 + +新增 `import_batches`、`session_history`、`resource_samples`、`alert_rules`、`alerts`、`alert_states` 六张表及索引;默认规则只在不存在时新增,没有向原业务表添加字段。原设备令牌、root 账号、桌面参数和用户授权保留。 + +更新前备份 `.local/live/backups/before-operations-20261110-133019.dump`;旧 API/控制台镜像标签 `before-operations-20261110-133019`。备份含业务数据,只放私有目录。旧版本不记录新增会话/采样;如需回退,先另行备份并评估新增数据,不能直接删除业务卷。本轮未做数据库恢复演练。 + +验证期间 Docker/本机进程中断,恢复后 PostgreSQL 正常完成启动恢复;原业务快照核对一致,随后继续测试和更新。镜像构建首次遇到 Docker Hub 鉴权网络 EOF,重试成功。 + +## 验证证据 + +独立临时 PostgreSQL 的 Go 全量测试与 go vet、前端 ESLint/类型检查、3 项单元测试、生产构建及 27 项指定浏览器回归通过。数据库测试没有连接真实业务库;浏览器回归使用接口拦截数据,截图以 `fixture-` 标记。 + +真实服务验证及页面检查见 [接口核验](evidence/cloud-operations-20261110/live-verification.json)、[页面核验](evidence/cloud-operations-20261110/browser-verification.json)、[自动检查范围](evidence/cloud-operations-20261110/automated-verification.json)。临时用户/终端/授权/导入批次和测试规则/告警定向清理,操作审计保留。告警验证只调整临时终端的测试心跳时间,并通过真实设备心跳 API 验证恢复;没有修改默认规则或原设备。 + +真实验收结果:三类 CSV 均成功且重放未重复写入;会话预约、就绪和结束落库;真实 PVE 采样已返回有效点,历史从本次部署开始积累。临时终端离线告警触发、确认后由心跳自动恢复。清理后原业务快照一致,原盒子心跳正常,VM100/101/102 均运行。真实页面 9 张截图覆盖深浅主题与 390px 弹窗,无脚本错误、失败接口或页面检查产生的修改请求。 diff --git a/docs/cloud-platform-development.md b/docs/cloud-platform-development.md index 1085535..51e76ec 100644 --- a/docs/cloud-platform-development.md +++ b/docs/cloud-platform-development.md @@ -2,6 +2,8 @@ 更新:2026-11-09。当前交付是第一阶段本地可运行版本。Vue 控制台通过真实 HTTP 调用 Go API,账号、授权、设备、任务和审计保存在 PostgreSQL;演示 VM 的电源操作由明确标记的演示计算适配执行。本文件主要描述演示开发环境;随后已接通[独立真实环境](cloud-live-integration.md),控制台端口 5182、设备 HTTPS 8443。 +11-10 新增模板登记、PVE 克隆任务、桌面池、初始化连接配置,以及真实管理员账号维护和本人改密。最新实现范围、操作路径、现场前提和剩余功能见[功能核对清单](cloud-feature-progress-20261110.md)。 + ## 启动 ### Windows 本机开发 @@ -56,7 +58,7 @@ MSW 在浏览器层模拟 HTTP,只有开发构建允许启用,不需要 Go 当前连接票据仅授权并返回 Moonlight 端点与参数;终端仍需既有 Sunshine 配对。消费票据登记连接预约,不证明串流已建立;现场 Qt 已上报实际画面就绪与进程结束事件,详情见真实接入说明。不要把 `credentialMode=existing-pairing` 当作免配对认证。 -本轮将 API 入口与基础业务模块放在一个可运行的 Go 网关进程中,数据库和 Proxmox client 独立封装。原方案的七个服务职责继续作为后续拆分边界;当前尚未交付七个独立部署的服务。MQTT 总线、Rust Agent、VictoriaMetrics 指标接入、模板发放、桌面池、GPU 绑定、OTA、AD/LDAP 尚未完成;Qt 适配现已实际部署。控制台仅显示已接通的第一阶段功能。 +本轮将 API 入口与基础业务模块放在一个可运行的 Go 网关进程中,数据库和 Proxmox client 独立封装。原方案的七个服务职责继续作为后续拆分边界;当前尚未交付七个独立部署的服务。MQTT 总线、Rust Agent、VictoriaMetrics 指标接入、GPU 绑定、OTA、AD/LDAP 尚未完成;模板登记、克隆编排与桌面池已在后续批次实现,现场发放仍待模板和权限验收;Qt 适配现已实际部署。控制台仅显示已接通的第一阶段功能。 ## 真实 Proxmox 接入 @@ -74,11 +76,11 @@ PVE_TOKEN_SECRET=... PVE_CA_FILE=/run/secrets/pve-ca.pem ``` -PVE 凭据由后端持有,前端和终端不会接收;证书校验始终开启。PVE_URL 只填 HTTPS origin,不附 `/api2/json`。由运维给 API Token 配置实际需要的节点/VM/存储查询和 VM 电源操作权限。本轮不自动修改 PVE 账号、ACL、VM 或主机配置。 +PVE API Token 密钥由后端保管,网页只输入不回显,终端不接收;证书校验始终开启。PVE_URL 只填 HTTPS origin,不附 `/api2/json`。由运维给 API Token 配置实际需要的节点/VM/存储查询和 VM 电源操作权限。本轮不自动修改 PVE 账号、ACL、VM 或主机配置。 启动后到“计算资源”手动同步;再到“桌面管理”选择“纳管已有 VM”。纳管仅登记已存在的 VM,不改 VM 配置。连接地址可在纳管表单填写。首次真实接入应使用专门测试 VM 核验 API 版本、证书、任务结果和关机行为,再对业务 VM 开放操作。 -正式环境的 HTTPS 入口、运维凭据轮换、账号维护、登录限流、独立数据库迁移流程与备份恢复仍属于部署产品化工作;当前 compose 不作生产就绪声明。 +正式环境的 HTTPS 入口、运维凭据轮换、独立数据库迁移流程与备份恢复仍属于部署产品化工作;账号维护与登录限流已在后续批次接入;当前 compose 不作生产就绪声明。 ## 设备接口联调 @@ -142,3 +144,43 @@ go test ./... - 桌面“重启”只用 PVE reboot;未将硬重启 reset 冒充“还原镜像”。 - 当前任务和审计列表展示最近 1000 条,完整记录仍存 PostgreSQL;长期检索和归档后续扩展。 - 凭据不写入审计;设备上报的自由文本不直接放入公开任务结果。 + +## 管理员维护(2026-11-10) + +真实环境入口为「系统设置 → 管理员账号」,仅超级管理员可新增、编辑角色/显示名称、启停及重置他人密码。所有角色通过右上角钥匙修改本人密码;角色/状态/密码变化撤销该账号所有会话。新增密码至少 12 字符且 UTF-8 不超过 72 字节,原账号不强制改密;无永久删除入口。迁移、测试和验收证据见[功能清单](cloud-feature-progress-20261110.md)。 + +## 终端与桌面用户维护(2026-11-10) + +终端列表支持编辑名称/分组,详情增加停用/重新激活;用户列表支持资料编辑、启停和批量追加/撤销指定桌面。设备停用撤销旧身份,重新激活显示一次性密钥并需管理员更新设备配置。活动会话和未完成命令会阻止身份重置;用户权限变更阻止后续连接,既有串流单独重置。完整接口、迁移和验收见[本轮说明](cloud-lifecycle-20261110.md)。 + +## 11-10 终端策略 + +新增策略模板、下发记录、设备结果和设备应用回执。配置版本沿用终端版本递增;四张独立策略表保存能力、模板与历史,不向既有业务表新增列。仅超级管理员可创建/下发,设备回执沿用鉴权事务与行锁。Qt 客户端在安全持久化后热更新实际定时器,失败保留原运行配置;不改变串流参数。已部署本机真实服务与 RK3528 盒子并验证配置变化/恢复,详见[实现与验收](cloud-terminal-policies-20261110.md)。 + +## 11-10 批量导入、会话历史与告警 + +新增 CSV 用户/终端/授权导入、90 天会话历史、PostgreSQL 节点采样及控制台告警,入口、接口、留存、迁移与验收范围见[功能说明](cloud-operations-20261110.md)。VictoriaMetrics 和外部通知仍待接入。 + +## 接入配置与规格任务(2026-11-10) + +新增网页 PVE 配置及热加载、真实规格读取/预检/变更任务与只读核对、共享数据库的登录限流。完整接口、可信代理、加密密钥保存要求、独立测试和现场权限限制见[本批说明](cloud-configuration-20261110.md)。 + + +## 快照与手动还原(2026-11-10) + +新增 snapshot_jobs 表及电源/规格/快照任务的共同活动索引,启动幂等迁移。控制台入口为「桌面管理 → 详情 → 快照与还原」;仅支持关机后的普通虚拟磁盘与无内存还原,含数据盘的快照也会回退其数据。原任务未知不重发,成功还原后须重新确认连接初始化。接口、测试、备份和现场权限/VM100 限制见[本批说明](cloud-snapshots-20261110.md)。本批未进行真实 VM 快照写入,公共池自动领取/还原尚未实施。 + + +## 桌面批量电源操作(2026-11-10) + +5182 的「桌面管理」新增跨页选择、批量开机/正常关机/重启和批次历史,每批最多 50 台。逐台真实预检、5 分钟签名凭证、确认后幂等提交;执行前复查,未确认结果不重发并阻止后续修改和新连接。Go 全量与静态检查、前端检查和 45 项隔离接口页面回归通过;真实环境只做预检与取消提交,三台桌面继续运行。新增表、任务状态、备份与现场边界见[功能说明](cloud-batch-operations-20261110.md)。专用测试 VM、Windows 副本和存储准备按用户要求暂缓。 + + +## GPU 资源查看(2026-11-10) + +5182 侧栏新增「GPU 资源」,三角色均可读取节点显卡清单、PCI 地址、IOMMU 分组与当前/待应用的虚拟机配置引用,支持搜索、筛选与详情。现场确认 Intel 核显、GTX 965M 及 Windows VM100 引用;自定义配置导致不完整时明确提示,不将未发现引用视为可分配。只新增 GET 接口,无数据库迁移或 GPU 写入;Go 全量与静态检查、前端检查和 51 项页面回归通过。接口、权限、备份和现场证据见[功能说明](cloud-gpu-inventory-20261110.md)。 + + +## 终端系统指标与历史(2026-11-10) + +5182 的「终端管理 → 详情 → 查看运行指标」已接通真实 CPU、内存、SoC 温度和运行时长,支持 1 小时/24 小时/7 天趋势、30 天留存、缺失断线和过期隐藏。盒子升级为 `qt-platform/0.4.0`,真实连续采样已入库,旧 Agent 兼容通过;没有将目标 FPS 或请求耗时当作串流实测指标。Go 全量及静态检查、前端检查/构建、57 项页面回归和盒子 6 项 CTest 通过,真实深浅主题及 390px 页面检查无异常。原设备配置、账号密码哈希指纹、桌面参数和授权一致,三台 VM 的配置 digest 与运行状态保持。详见[采集范围与验收](cloud-terminal-metrics-20261110.md)。串流 FPS/RTT、GPU 健康指标及系统指标告警仍待接入。 diff --git a/docs/cloud-snapshots-20261110.md b/docs/cloud-snapshots-20261110.md new file mode 100644 index 0000000..26c5485 --- /dev/null +++ b/docs/cloud-snapshots-20261110.md @@ -0,0 +1,55 @@ +# 关机快照与手动还原(2026-11-10) + +本批接入真实 PVE API,补充公共池自动还原所需的基础能力。入口为「桌面管理 → 查看详情 → 快照与还原」,异步结果在「任务与审计」查看。本批不包含公共池自动领取、退出还原、自动回收、备份或迁移。 + +## 功能与范围 + +- 全部管理员角色可查看快照名称、描述、时间、父快照及内存标记;仅超级管理员可预检、创建、删除、还原和核对未知结果。 +- 创建保存关机后的磁盘与配置,不保存内存;名称由字母开头,1~40 位字母、数字、下划线或短横线,不使用 current/pending。 +- 删除只支持已完成且没有子快照的恢复点,不使用 PVE force 删除。 +- 还原前输入当前 VMID,明确确认回退快照内磁盘和配置。数据盘若在快照内也会回退,不保证只还原系统盘。拒绝内存快照,完成后保持关机。 +- 还原提交前撤销旧连接票据并将桌面标记为待初始化。管理员需核对系统身份、IP、Sunshine 服务和终端配对,再在「配置连接」确认初始化。PVE 明确拒绝还原时也保留此标记,核查后重新确认即可。 +- 页面沿用现有深浅主题,窄屏保留弹窗标题和底部操作,内容区内部滚动。参数变更会使预检失效;取消二次确认不提交写入。 + +## 预检与执行保护 + +所有快照写操作都要求正常关机、无 PVE 锁、无待应用配置、无活动终端会话,且没有其他电源/规格/快照任务或未确认的维护结果。创建时检查存储快照能力;删除/还原校验选定快照版本;还原另检查磁盘布局、容量和配置目标。 + +本期仅支持普通虚拟磁盘(包含 EFI/TPM),跳过 CD-ROM/cloud-init。包含直通设备、宿主 args/hookscript、未使用磁盘、物理盘、共享盘或排除快照的磁盘时不允许平台快照写入;列表仍可查看。自定义 NUMA 等既有配置限制也会拦截。磁盘配置字符串的严格比较可能拒绝等价但格式不同的配置,需要在 PVE 人工核查。 + +读取需 VM.Audit;创建/删除需 VM.Snapshot;还原允许 VM.Snapshot 或 VM.Snapshot.Rollback。平台不修改 PVE 权限。PVE 的 start=0 无法保证含内存快照不启动,所以本期拒绝这类还原。依据 [PVE QEMU API 源码](https://raw.githubusercontent.com/proxmox/qemu-server/master/src/PVE/API2/Qemu.pm) 与 [QemuConfig 实现](https://raw.githubusercontent.com/proxmox/qemu-server/master/src/PVE/QemuConfig.pm)。 + +任务使用持久化请求、幂等键、桌面行锁和活动任务唯一索引。排队与执行前分别核对配置摘要及快照指纹;进入 submitting 后只发送一次,服务重启仅查询原 UPID。普通任务不等待长快照完成,当前长维护任务仍单个串行执行。 + +成功需要原 PVE 任务 stopped/OK 和实际状态一致;还原不能只凭配置相同就断言磁盘数据已经恢复。PVE 最终失败、网络异常或缺少 UPID 时,发送后的结果保持未确认,阻止后续电源/规格变更、新连接及重新开放初始化。任务详情的「核对快照结果」仅查原任务和实际状态,不重发修改;缺 UPID、PVE 任务失败或残留状态需在 PVE 人工核查,当前无一键清除未知保护入口。 + +预检无法防止管理员绕过平台直接修改 PVE:这些快照写接口没有配置 digest 条件参数,执行前检查与 PVE 接收之间仍有竞态。请避免从其他入口同时维护同一 VM。 + +## API 与数据 + +| 方法 | 路径 | 用途 | +| --- | --- | --- | +| GET | /desktops/{id}/snapshots | 列表、配置摘要、磁盘范围、限制及操作权限 | +| POST | /desktops/{id}/snapshots/check | 只读预检 | +| POST | /desktops/{id}/snapshots/actions | 幂等提交 create/delete/rollback,返回 202 任务 | +| POST | /tasks/{id}/snapshot-reconcile | 只读核对原任务 | + +提交字段为 action/name/digest,创建可带 description,删除/还原需 revision,还原需 confirmVmid。拒绝未知字段和不适用参数。接口契约见 [OpenAPI](openapi/platform.v1.json)。 + +新增 snapshot_jobs 表,保存请求、请求散列、目标配置散列和阶段;不保存完整 PVE 配置。新增 tasks_vm_maintenance_target 唯一索引,与既有电源/规格队列互斥;启动幂等迁移,无业务表删列。 + +## 验证与现场限制 + +已部署本机控制台 http://127.0.0.1:5182。现场只读结果:三台 VM 均在运行且没有快照;Token 缺 VM.Snapshot 和 VM.Snapshot.Rollback。PVE 报告 VM101/102 支持快照,VM100 不支持;VM100 还含显卡直通和宿主 args,本期写入范围也会拒绝。此次未对真实 VM 执行关机、快照创建/删除/还原、规格修改或克隆。 + +Go 全量测试与 vet、ESLint、TypeScript、3 项前端单元测试及生产构建通过。37 项浏览器回归通过,其中快照 8 项;随后修复窄屏弹窗滚动,8 项快照流程重新通过。新增后端覆盖权限二选一、关机/锁/待应用/活动会话限制、磁盘与内存限制、幂等、执行前变更、未知状态不重发、只读核对、过期、维护互斥与还原票据撤销。完整测试摘要见 [记录](evidence/cloud-snapshots-20261110/test-verification.json)。 + +真实深浅主题和 390px 页面检查无脚本错误、失败响应或写请求;原 root 密码指纹、桌面连接、用户授权和终端身份与更新前一致,三台桌面运行、盒子心跳正常。 + +验证记录在 [证据目录](evidence/cloud-snapshots-20261110/)。写入链路用独立临时 PostgreSQL 和本机 PVE HTTP 测试服务验证;不能将其视为真实磁盘恢复验收。 + +部署和现场只读检查结果见 [真实 API 核验](evidence/cloud-snapshots-20261110/live-verification.json) 与 [页面核验](evidence/cloud-snapshots-20261110/browser-verification.json)。快照真实写入与磁盘内容还原需要后续补充 Token 权限,在无直通的专用测试 VM 验收。 + +页面证据:[现场深色](evidence/cloud-snapshots-20261110/live-snapshots-dark.png)、[现场窄屏](evidence/cloud-snapshots-20261110/live-snapshots-mobile.png)、[隔离数据创建表单](evidence/cloud-snapshots-20261110/fixture-snapshot-create-dark.png)、[隔离数据还原确认](evidence/cloud-snapshots-20261110/fixture-snapshot-restore-light-mobile.png)。fixture 图片不代表现场已有快照。 + +更新前私有备份为 .local/live/backups/before-snapshots-20261110-155834.dump,API/控制台旧镜像标签为 before-snapshots-20261110-155834。数据库备份包含业务数据,不提交仓库。本批不改终端程序。回退前需停写并评估新增任务记录及 schema 兼容性,不能只切旧镜像后丢弃维护保护;备份恢复尚未演练。 diff --git a/docs/cloud-terminal-metrics-20261110.md b/docs/cloud-terminal-metrics-20261110.md new file mode 100644 index 0000000..9747a66 --- /dev/null +++ b/docs/cloud-terminal-metrics-20261110.md @@ -0,0 +1,41 @@ +# 终端系统指标与历史(2026-11-10) + +本轮补齐 Qt 终端的真实系统指标采集、设备鉴权上传、PostgreSQL 历史保存,以及控制台详情中的数值和趋势。串流 FPS、RTT 和 GPU 健康数据仍未采集;目标帧率、HTTP 耗时或 ping 不会被当作实际串流指标。 + +## 页面与采集 + +入口:`http://127.0.0.1:5182/terminals` → 终端详情 → **查看运行指标**。三个管理角色均可只读查看;面板打开后每 30 秒读取,关闭后停止;支持手动刷新和最近 1 小时 / 24 小时 / 7 天。失败时显示错误并允许重试,切换设备或时间范围时旧响应不会覆盖新响应。 + +| 指标 | 数据来源与含义 | +| --- | --- | +| CPU 使用率 | `/proc/stat` 聚合 CPU 相邻心跳差分,idle 与 iowait 计为空闲;不重复累加 guest。第一次采样、读失败、计数倒退或无差分返回空值 | +| 内存使用率 | `/proc/meminfo` 的 `100 × (MemTotal − MemAvailable) / MemTotal`。缺少 MemAvailable 不回退为把缓存全部计入已用 | +| SoC 温度 | sysfs 中类型为 `soc-thermal` 的 thermal zone,毫摄氏度转摄氏度。不将任意 thermal zone 命名为 CPU/GPU 温度 | +| 运行时长 | `/proc/uptime` 第一项。未采到有效值显示“未采集” | + +Agent 版本为 `qt-platform/0.4.0`。采集只读取系统文件,不启动额外后台守护、不改变 Moonlight 参数和性能叠图。采样频率沿用已下发的心跳策略,现场为 30 秒。 + +## 服务与数据 + +- `POST /api/v1/terminal/heartbeat` 新增可选 `systemMetrics`,包含可空的 `cpuPercent`、`memoryPercent`、`socTemperatureC`、`uptimeSeconds`。 +- 校验范围依次为 0~100%、0~100%、−40~150°C、0~3155760000 秒;不接受非数值或超限值。 +- 使用设备令牌对应的终端身份,在已有设备行锁事务内写心跳和指标,客户端不能指定另一个终端 ID。旧 Agent 未带系统指标时兼容接收,并清空当前指标,显示升级提示。 +- 新表 `terminal_metrics_current` 保存最新状态,`terminal_metric_samples` 保存历史;单终端历史至多每 10 秒一条,重复心跳仍更新当前状态。历史与当前表均外键关联设备;不修改 `terminals` 的原有字段结构。 +- `GET /api/v1/terminals/{id}/metrics?from=...&to=...` 返回当前值、在线/新鲜度、最近采样时间和聚合历史。未知终端 404,无管理登录 401。单次查询最长 7 天,6 小时以内按 60 秒、24 小时以内按 300 秒、更长按 1800 秒聚合。 +- CPU/内存/温度历史为桶内均值,运行时长为桶内最大值。显式返回空桶,页面在缺失处断线;粗粒度聚合可能合并同一桶内的短暂中断,不表示整桶内持续在线。 +- 设备停用、心跳或指标超过 90 秒,当前数值返回空值;历史仍可查。设备重新激活会清除旧身份的当前指标,保留历史。 +- 现有观测循环定期清理 30 天以前的历史。当前使用 PostgreSQL;VictoriaMetrics 和指标告警规则尚未接入。 + +API 契约由 `tools/generate-platform-openapi.mjs` 生成,并同步到控制台类型。 + +## 验证与部署 + +Go 全量测试、`go vet`、前端 lint / 类型检查 / 单元测试 / 生产构建、57 项页面回归和盒子 6 项 CTest 均通过。真实深浅主题、390px 与 7 天聚合检查通过,无脚本异常或失败响应。 + +现场验收中连续采样真实入库,CPU 约 2.5%、内存约 10.2%、SoC 温度约 45.5°C(数值会变化)。独立读取 `/proc` 和 thermal 核对内存、温度、时长;CPU 因取样间隔不同只核对合法范围,差分算法由固定计数器测试验证。原业务身份、授权、密码哈希指纹、设备配置文件和三台 VM 的配置 digest 均一致,三台 VM 保持运行。 + +测试覆盖 Qt 文件读取、差分/重置/缺失、传感器来源和单位;后端的身份隔离、数值校验、旧 Agent、在线过期、身份重置、采样限速、聚合空桶、查询范围和留存;页面的按需加载、零值、缺失、离线、重试、窄屏和响应竞态。 + +最终现场验收及截图见 [证据目录](evidence/cloud-terminal-metrics-20261110/)。更新时备份数据库和旧镜像,盒子单独备份旧二进制,仅在无活动串流时替换 kiosk 并由原看门狗恢复;不重启 LightDM、系统或虚拟机,不更改设备令牌、密码、桌面参数或授权。 + +数据库备份:`.local/live/backups/before-terminal-metrics-20261110-173343.dump`;API 与 console 旧镜像标签:`before-terminal-metrics-20261110-173343`。回退可恢复旧镜像和旧 kiosk 二进制,新指标表可保留,旧版本不依赖它们。 diff --git a/docs/cloud-terminal-policies-20261110.md b/docs/cloud-terminal-policies-20261110.md new file mode 100644 index 0000000..acb1990 --- /dev/null +++ b/docs/cloud-terminal-policies-20261110.md @@ -0,0 +1,60 @@ +# 终端运维策略(2026-11-10) + +入口为控制台「终端策略」。这批实现的是管理轮询策略,不调整分辨率、FPS、编解码或现有桌面连接参数。 + +## 功能与使用 + +- 配置模板:名称、版本;心跳间隔 15~60 秒、命令检查间隔 3~30 秒、桌面列表刷新间隔 5~120 秒。默认分别为 30/3/15 秒。列表仅在桌面选择页刷新。 +- 保存的模板版本固定。修改时复制为新版本;恢复旧配置时下发旧模板,生成新的设备配置版本。 +- 超级管理员可创建和下发。运维、只读角色可查看模板、记录和结果。 +- 支持指定设备或一个非空分组,每次最多 200 台。按提交时的成员创建固定记录,后加入分组的设备不会自动继承。 +- 下发前整体验证设备已启用、已激活且上报策略协议 1。任一设备不满足条件时整体拒绝。旧客户端显示「需更新客户端」。支持协议的离线设备允许下发,保持等待联网。 +- 「下发记录」保留每次提交的目标、策略、操作者和设备结果;「设备结果」显示当前期望配置、应用状态、回执时间和失败原因。 +- 同一请求标识重试不会重复下发;换参数须换标识。重新下发会生成更大的配置版本;尚未应用或失败的旧版本标记为已替代,历史成功结果仍保留。 + +## 设备应用语义 + +Qt 平台客户端版本为 `qt-platform/0.3.0`。登录后立即获取配置,每 15 秒检查更新;该周期独立于可配置的心跳与命令轮询。管理操作期间不会停止正在运行的串流。 + +设备先验证协议、整数范围、配置版本与下发标识,再通过 `QSaveFile` 原子保存到 `~/.config/vdi-terminal/policy.json`,权限 0600,然后更新三个运行时定时器。该文件只包含策略字段,不包含设备令牌、桌面凭据或串流参数。成功后才回传 `applied`。 + +校验失败、无法持久化、运行组件未就绪分别回传固定错误代码,不上传自由文本或敏感配置。失败设备后续拉取会尝试恢复;相同失败回执不会持续增加审计。网络中断时保留待确认回执并重试,进程重启后重新从平台读取并应用,再确认结果。 + +当前设备身份被撤销后,旧令牌不能继续回执。停用或重新激活会清除协议能力,并将当前策略重新置为待应用;新客户端完成激活、获取配置后重新回执。新版本已下发时,旧版本迟到的回执返回 409,不能覆盖新结果。设备应用成功后迟到的失败回执也不能倒退成功状态。 + +“已应用”表示客户端确认配置持久化并完成运行时更新,不表示该设备当前在线。离线或停用状态单独展示。设备未回执时不会根据 HTTP 202、配置拉取或心跳推测成功。设备断网期间不能获得新策略;没有离线授权回退。 + +## 接口与存储 + +- `GET/POST /api/v1/terminal-policy-templates` +- `GET /api/v1/terminal-policy-states` +- `GET/POST /api/v1/terminal-policy-deployments` +- `GET /api/v1/terminal-policy-deployments/{id}/results` +- `GET /api/v1/terminal/config` 返回期望版本、下发标识与完整策略。 +- `POST /api/v1/terminal/config/ack` 接收设备应用结果。 +- `POST /api/v1/terminal/heartbeat` 增加可选 `policyProtocol`;旧客户端省略即按不支持处理。 + +新增四张表:`terminal_policy_templates`、`terminal_policy_capabilities`、`terminal_policy_deployments`、`terminal_policy_deliveries`。沿用 `terminals.config_version`,每次下发递增;没有向现有业务表新增列。模板、下发与回执均持久化;创建、下发和应用结果写入审计。下发按终端 ID 排序锁定设备行,设备回执沿用设备鉴权事务,身份重置与下发不会交错修改同一终端。 + +本批没有新增网络/Wi-Fi 配置、USB/剪贴板开关、应用白名单、自动登录桌面、OTA 或画质策略。这些能力需要对应终端实现后才能提供可用开关。 + +## 部署与回退 + +更新前数据库备份:私有 `.local/live/backups/before-policy-20261110-114707.dump`;旧 API、控制台镜像标签 `before-policy-20261110`。备份包含业务数据,不提交仓库。盒子二进制更新前另存备份,只替换 kiosk-shell,不改 Moonlight、LightDM 或 VM。 + +恢复配置优先重新下发旧策略版本。回退软件前停止新增下发,确认当前策略、身份与会话状态,另行备份新增数据,再评估配套 API、控制台和客户端版本;不要直接删除业务卷。数据库快照恢复会覆盖快照后的业务数据,本批不执行恢复。 + +验证与真实部署结果见本文件后续记录和 `docs/evidence/cloud-policies-20261110/`。 + +## 验证与现场结果 + +- 独立临时 PostgreSQL:`go test ./...`、`go vet ./...` 通过。覆盖旧设备能力拒绝、离线等待、真实回执状态、失败恢复、重复请求、旧回执隔离、历史保留、身份重置、权限、整数范围、分组快照及整批校验。没有对 live 数据库运行后端测试。 +- ESLint、TypeScript、3 项单元测试、本地与 Docker 生产构建通过。浏览器共验证 22 个流程,其中新增策略 4 项;修正测试对 Element Plus 选择器的定位后新增用例全部通过。所有浏览器功能回归均拦截 API,与真实资源隔离。已有 Element Plus 大包提示仍保留。 +- 真实 ARM64 盒子编译完成,5 项 CTest 全部通过,包括新增安全持久化/配置范围/失败不更新运行时,以及既有启动、会话、选择页和显示守护测试。 +- API/控制台已更新到本机 5182;盒子仅替换空闲的 kiosk-shell,由看门狗拉起,未重启 LightDM 或 VM。新版二进制 SHA256 `690a74ee5b077ff96e385f2ba45ca756f1879223f7433e63ec1833eb0f3a628b`,旧版本备份 `~/vdi-terminal/backups/policy-20261110/kiosk-shell`。 +- 11:55 真实下发「标准运维 1.1」:心跳 30 秒、命令 5 秒、桌面列表 20 秒,配置版本 2 收到设备应用成功;随后下发 1.0 恢复 30/3/15 秒,配置版本 3 再次收到成功,现场保留两个模板版本及完整下发记录。核对实际设备策略文件与权限,重复提交没有产生第二次下发。 +- 设备身份、root 账号密码、三台桌面连接参数、用户和授权快照保持一致;心跳继续推进,三台 VM 运行,验收时活动会话为 0。没有执行 VM 电源、GPU、克隆或模板转换。未声称已完成断电恢复、长时间离线、活动串流中下发或多型号设备验收。 + +证据:[测试](evidence/cloud-policies-20261110/test-verification.json)、[真实下发](evidence/cloud-policies-20261110/live-verification.json)。`fixture-` 截图使用隔离测试数据,`live-` 截图展示真实服务。 + +现场页面补验:深浅主题、1440px 与 390px、模板/设备结果/下发详情及新增/下发弹窗均可用,8 张截图,无脚本错误和失败接口;检查期间未发送修改请求。见[页面验收](evidence/cloud-policies-20261110/browser-verification.json)及[真实设备结果](evidence/cloud-policies-20261110/live-states-dark.png)。临时测试数据库与本轮 Vite 开发服务已停止,真实 5182 服务保留。 diff --git a/docs/current-status.md b/docs/current-status.md index f6714c6..1ceeff9 100644 --- a/docs/current-status.md +++ b/docs/current-status.md @@ -1,6 +1,6 @@ # 当前状态 -截至 2026-11-09,依据仓库中的实施记录整理。本次文档整理没有重新连接现场设备,下面的“已验证”指相应日期的历史验收,并非实时健康报告。 +截至 2026-11-10,依据仓库中的实施记录整理。“已验证”以对应日期和范围为准,并非持续实时健康报告;11-10 启动认证修复已重新连接盒子核验。 ## 产品与架构 @@ -32,6 +32,15 @@ | 事项 | 记录与验证边界 | | --- | --- | +| 终端系统指标 | 11-10 已部署真实 CPU、内存、SoC 温度、运行时长与历史趋势;盒子 0.4.0 连续入库,57 项页面回归和 6 项 Qt 测试通过。串流 FPS/RTT 仍未采集。见[采集与验收](cloud-terminal-metrics-20261110.md) | +| GPU 资源查看 | 11-10 已部署真实显卡清单、PCI/IOMMU、当前/待应用配置引用和纳管关系;现场识别 Intel 核显、GTX 965M 及 VM100 引用,51 项页面回归通过。未修改直通配置,独占分配和健康指标尚未接入。见[功能与验收](cloud-gpu-inventory-20261110.md) | +| 桌面批量电源 | 11-10 已部署最多 50 台的开机/正常关机/重启、逐台预检、批次历史、幂等任务和原结果核对;45 项页面回归通过,现场仅做预检及取消,三台 VM 保持运行。专用 VM/Windows 副本和存储准备按用户要求暂缓。见[功能与边界](cloud-batch-operations-20261110.md) | +| CSV、会话历史与监控告警 | 11-10 增加用户/终端/授权 CSV 预检与整批提交、90 天会话历史、节点曲线与规则/确认/恢复;完整范围见[功能说明](cloud-operations-20261110.md) | +| 终端运维策略 | 11-10 已部署模板版本、设备/分组下发、真实应用回执及失败原因;真实盒子完成修改/恢复,最终管理间隔 30/3/15 秒。见[策略与验收](cloud-terminal-policies-20261110.md) | +| 终端与用户维护 | 11-10 设备名称/分组、停用/重新激活、用户资料/启停及批量授权已部署 5182;真实临时身份验收后清理,原盒子心跳和业务映射保留。见[功能说明](cloud-lifecycle-20261110.md) | +| 管理员账号维护 | 11-10 真实账号新增、显示名称/角色编辑、启停、他人密码重置、本人改密与全部会话撤销已部署 5182;临时账号验收后清理,原 root 保留。见[功能清单](cloud-feature-progress-20261110.md) | +| 云管模板与桌面池 | 11-10 增加真实 PVE 克隆编排、模板/池页面及连接初始化确认;独立数据库与 PVE 测试服务通过,现场缺模板与创建权限,未实际克隆 VM。见[功能清单](cloud-feature-progress-20261110.md) | +| 冷启动失败不再退旧登录页 | 11-10 设备认证失败保持选择页,2~30 秒退避重试;5 个启动场景通过,盒子部署后设备认证与三桌面列表正常,尚未再次断电验收 | | 开机直接显示桌面列表 | 11-09 独占 LightDM 会话;保留[冷启动截图](evidence/boot-direct-list-cold-boot.png) | | 退出短暂露出绿色桌面 | 11-09 列表全屏常驻串流窗口下方;三系统两轮共 3121 个 X11 采样未见底层窗口 | | 进入左上角小窗口 | 11-09 Moonlight 0007 隐藏 CLI Qt 窗口,SDL 创建时铺满屏幕;三系统进入 2695 次、返回 1562 次采样均无违规 | @@ -56,3 +65,12 @@ - Rust Agent、MQTT、OTA、多租户授权及通用 USB 会话隔离仍待产品化。已有 POC 模块不等于完整功能交付。 当前构建、策略和排障入口分别见[终端说明](terminal-guide.md)、[云桌面说明](desktop-guide.md)、[运维说明](operations.md);待办统一见[未来计划](roadmap.md)。历史精确配置和回退路径可从[归档索引](archive/README.md)查原文。 + +## 11-10 接入配置与规格管理 + +本机 5182 已部署 PVE 网页接入维护、关机后 CPU/内存及虚拟磁盘扩容任务、管理员登录限流。原接入配置已加密保存并通过 API 重启验证;真实三台 VM 只读规格及运行中拦截通过,规格写入仍缺现场 Token 权限,未做真实 VM 修改。原 root 密码、桌面连接、授权与终端身份快照一致,盒子心跳正常。细节与证据见[接入、规格和限流](cloud-configuration-20261110.md)。 + + +## 11-10 关机快照与手动还原 + +本机 5182 已部署真实快照列表、创建/删除/手动还原任务及只读核对,维护期间和未知结果阻止新连接与桌面修改。还原撤销旧票据并要求重新确认初始化。Go 全量、37 项浏览器回归和窄屏复验通过;现场只读检查保持原业务配置和三台 VM 运行,盒子心跳正常。当前 Token 无快照权限,PVE 报告 VM100 不支持快照,真实写入尚未验收;公共池退出自动还原未实现。见[快照与还原说明](cloud-snapshots-20261110.md)。 diff --git a/docs/evidence/boot-auth-20261110/ctest.txt b/docs/evidence/boot-auth-20261110/ctest.txt new file mode 100644 index 0000000..7e2ea61 --- /dev/null +++ b/docs/evidence/boot-auth-20261110/ctest.txt @@ -0,0 +1,144 @@ +Start testing: Nov 10 09:28 CST +---------------------------------------------------------- +1/4 Testing: display-guard +1/4 Test: display-guard +Command: "/usr/bin/python3" "/home/armbian/vdi-build/boot-device-auth-20261110/tests/test_display_guard.py" +Directory: /home/armbian/vdi-build/boot-device-auth-20261110/build +"display-guard" start time: Nov 10 09:28 CST +Output: +---------------------------------------------------------- +......... +---------------------------------------------------------------------- +Ran 9 tests in 0.110s + +OK + +Test time = 0.83 sec +---------------------------------------------------------- +Test Passed. +"display-guard" end time: Nov 10 09:28 CST +"display-guard" time elapsed: 00:00:00 +---------------------------------------------------------- + +2/4 Testing: moonlight-session +2/4 Test: moonlight-session +Command: "/home/armbian/vdi-build/boot-device-auth-20261110/build/test-moonlight-session" +Directory: /home/armbian/vdi-build/boot-device-auth-20261110/build +"moonlight-session" start time: Nov 10 09:28 CST +Output: +---------------------------------------------------------- +********* Start testing of MoonlightSessionTest ********* +Config: Using QtTest library 5.15.13, Qt 5.15.13 (arm64-little_endian-lp64 shared (dynamic) release build; by GCC 13.2.0), ubuntu 24.04 +PASS : MoonlightSessionTest::initTestCase() +PASS : MoonlightSessionTest::parsesSplitReadyMarkers() +PASS : MoonlightSessionTest::requiresBothReadyMarkers() +PASS : MoonlightSessionTest::acceptsSeparateHardwareTestDecoder() +PASS : MoonlightSessionTest::rejectsSecondStart() +PASS : MoonlightSessionTest::reportsKioskLaunchFailure() +PASS : MoonlightSessionTest::buildsConsistentStreamArguments() +PASS : MoonlightSessionTest::selectsProfileWithoutLosingStats() +PASS : MoonlightSessionTest::rejectsInvalidDecoderOverride() +PASS : MoonlightSessionTest::cleanupTestCase() +Totals: 10 passed, 0 failed, 0 skipped, 0 blacklisted, 917ms +********* Finished testing of MoonlightSessionTest ********* + +Test time = 0.94 sec +---------------------------------------------------------- +Test Passed. +"moonlight-session" end time: Nov 10 09:28 CST +"moonlight-session" time elapsed: 00:00:00 +---------------------------------------------------------- + +3/4 Testing: desktop-list-page +3/4 Test: desktop-list-page +Command: "/home/armbian/vdi-build/boot-device-auth-20261110/build/test-desktop-list-page" +Directory: /home/armbian/vdi-build/boot-device-auth-20261110/build +"desktop-list-page" start time: Nov 10 09:28 CST +Output: +---------------------------------------------------------- +********* Start testing of DesktopListPageTest ********* +Config: Using QtTest library 5.15.13, Qt 5.15.13 (arm64-little_endian-lp64 shared (dynamic) release build; by GCC 13.2.0), ubuntu 24.04 +PASS : DesktopListPageTest::initTestCase() +PASS : DesktopListPageTest::firstClickDisablesActionsAndBlocksDuplicate() +PASS : DesktopListPageTest::cleanupTestCase() +Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted, 60ms +********* Finished testing of DesktopListPageTest ********* + +Test time = 0.11 sec +---------------------------------------------------------- +Test Passed. +"desktop-list-page" end time: Nov 10 09:28 CST +"desktop-list-page" time elapsed: 00:00:00 +---------------------------------------------------------- + +4/4 Testing: main-window-boot +4/4 Test: main-window-boot +Command: "/home/armbian/vdi-build/boot-device-auth-20261110/build/test-main-window" +Directory: /home/armbian/vdi-build/boot-device-auth-20261110/build +"main-window-boot" start time: Nov 10 09:28 CST +Output: +---------------------------------------------------------- +********* Start testing of vdi::MainWindowTest ********* +Config: Using QtTest library 5.15.13, Qt 5.15.13 (arm64-little_endian-lp64 shared (dynamic) release build; by GCC 13.2.0), ubuntu 24.04 +PASS : vdi::MainWindowTest::initTestCase() +QWARN : vdi::MainWindowTest::offlineBootRecoversWithoutRestart() This plugin does not support propagateSizeHints() +QWARN : vdi::MainWindowTest::offlineBootRecoversWithoutRestart() This plugin does not support raise() +QWARN : vdi::MainWindowTest::offlineBootRecoversWithoutRestart() This plugin does not support raise() +QWARN : vdi::MainWindowTest::offlineBootRecoversWithoutRestart() This plugin does not support raise() +QWARN : vdi::MainWindowTest::offlineBootRecoversWithoutRestart() This plugin does not support raise() +QWARN : vdi::MainWindowTest::offlineBootRecoversWithoutRestart() vdi_device_auth_request http_status= 0 network_error= 1 +QWARN : vdi::MainWindowTest::offlineBootRecoversWithoutRestart() vdi_device_auth result=waiting retry_ms= 30 +QWARN : vdi::MainWindowTest::offlineBootRecoversWithoutRestart() This plugin does not support propagateSizeHints() +QWARN : vdi::MainWindowTest::offlineBootRecoversWithoutRestart() vdi_device_auth_request http_status= 0 network_error= 1 +QWARN : vdi::MainWindowTest::offlineBootRecoversWithoutRestart() vdi_device_auth result=waiting retry_ms= 60 +QINFO : vdi::MainWindowTest::offlineBootRecoversWithoutRestart() vdi_device_auth result=ready +PASS : vdi::MainWindowTest::offlineBootRecoversWithoutRestart() +QWARN : vdi::MainWindowTest::rejectedIdentityStaysOnSelectionAndRetries() This plugin does not support propagateSizeHints() +QWARN : vdi::MainWindowTest::rejectedIdentityStaysOnSelectionAndRetries() This plugin does not support raise() +QWARN : vdi::MainWindowTest::rejectedIdentityStaysOnSelectionAndRetries() This plugin does not support raise() +QWARN : vdi::MainWindowTest::rejectedIdentityStaysOnSelectionAndRetries() This plugin does not support raise() +QWARN : vdi::MainWindowTest::rejectedIdentityStaysOnSelectionAndRetries() This plugin does not support raise() +QWARN : vdi::MainWindowTest::rejectedIdentityStaysOnSelectionAndRetries() vdi_device_auth_request http_status= 403 network_error= 201 +QWARN : vdi::MainWindowTest::rejectedIdentityStaysOnSelectionAndRetries() vdi_device_auth result=waiting retry_ms= 30 +QWARN : vdi::MainWindowTest::rejectedIdentityStaysOnSelectionAndRetries() This plugin does not support propagateSizeHints() +QWARN : vdi::MainWindowTest::rejectedIdentityStaysOnSelectionAndRetries() vdi_device_auth_request http_status= 403 network_error= 201 +QWARN : vdi::MainWindowTest::rejectedIdentityStaysOnSelectionAndRetries() vdi_device_auth result=waiting retry_ms= 60 +QWARN : vdi::MainWindowTest::rejectedIdentityStaysOnSelectionAndRetries() vdi_device_auth_request http_status= 403 network_error= 201 +QWARN : vdi::MainWindowTest::rejectedIdentityStaysOnSelectionAndRetries() vdi_device_auth result=waiting retry_ms= 120 +QINFO : vdi::MainWindowTest::rejectedIdentityStaysOnSelectionAndRetries() vdi_device_auth result=ready +QWARN : vdi::MainWindowTest::rejectedIdentityStaysOnSelectionAndRetries() This plugin does not support propagateSizeHints() +PASS : vdi::MainWindowTest::rejectedIdentityStaysOnSelectionAndRetries() +QWARN : vdi::MainWindowTest::slowAuthenticationDoesNotOverlap() This plugin does not support propagateSizeHints() +QWARN : vdi::MainWindowTest::slowAuthenticationDoesNotOverlap() This plugin does not support raise() +QWARN : vdi::MainWindowTest::slowAuthenticationDoesNotOverlap() This plugin does not support raise() +QWARN : vdi::MainWindowTest::slowAuthenticationDoesNotOverlap() This plugin does not support raise() +QWARN : vdi::MainWindowTest::slowAuthenticationDoesNotOverlap() This plugin does not support raise() +QINFO : vdi::MainWindowTest::slowAuthenticationDoesNotOverlap() vdi_device_auth result=ready +QWARN : vdi::MainWindowTest::slowAuthenticationDoesNotOverlap() This plugin does not support propagateSizeHints() +PASS : vdi::MainWindowTest::slowAuthenticationDoesNotOverlap() +QWARN : vdi::MainWindowTest::invalidConfigurationNeverShowsLogin() This plugin does not support propagateSizeHints() +QWARN : vdi::MainWindowTest::invalidConfigurationNeverShowsLogin() This plugin does not support raise() +QWARN : vdi::MainWindowTest::invalidConfigurationNeverShowsLogin() This plugin does not support raise() +QWARN : vdi::MainWindowTest::invalidConfigurationNeverShowsLogin() This plugin does not support raise() +QWARN : vdi::MainWindowTest::invalidConfigurationNeverShowsLogin() This plugin does not support raise() +QWARN : vdi::MainWindowTest::invalidConfigurationNeverShowsLogin() vdi_device_auth result=waiting retry_ms= 30 +PASS : vdi::MainWindowTest::invalidConfigurationNeverShowsLogin() +QWARN : vdi::MainWindowTest::localModeStillLoadsDesktops() This plugin does not support propagateSizeHints() +QWARN : vdi::MainWindowTest::localModeStillLoadsDesktops() This plugin does not support raise() +QWARN : vdi::MainWindowTest::localModeStillLoadsDesktops() This plugin does not support raise() +QWARN : vdi::MainWindowTest::localModeStillLoadsDesktops() This plugin does not support raise() +QWARN : vdi::MainWindowTest::localModeStillLoadsDesktops() This plugin does not support raise() +QINFO : vdi::MainWindowTest::localModeStillLoadsDesktops() vdi_device_auth result=ready +PASS : vdi::MainWindowTest::localModeStillLoadsDesktops() +PASS : vdi::MainWindowTest::cleanupTestCase() +Totals: 7 passed, 0 failed, 0 skipped, 0 blacklisted, 4104ms +********* Finished testing of vdi::MainWindowTest ********* + +Test time = 4.19 sec +---------------------------------------------------------- +Test Passed. +"main-window-boot" end time: Nov 10 09:28 CST +"main-window-boot" time elapsed: 00:00:04 +---------------------------------------------------------- + +End testing: Nov 10 09:28 CST diff --git a/docs/evidence/boot-auth-20261110/deployment.txt b/docs/evidence/boot-auth-20261110/deployment.txt new file mode 100644 index 0000000..3b9158c --- /dev/null +++ b/docs/evidence/boot-auth-20261110/deployment.txt @@ -0,0 +1,4 @@ +[1/6] Automatic MOC for target kiosk-shell +[2/4] Automatic MOC for target test-main-window +9dba9c66092aa9ccf67477b305aaefe09f52eabb8c6b30dbbcbb1ce43dc7a6a6 /home/armbian/vdi-terminal/bin/kiosk-shell +old_kiosk_pid=1789 diff --git a/docs/evidence/boot-auth-20261110/live-check.txt b/docs/evidence/boot-auth-20261110/live-check.txt new file mode 100644 index 0000000..02f461f --- /dev/null +++ b/docs/evidence/boot-auth-20261110/live-check.txt @@ -0,0 +1,12 @@ +--- Current kiosk --- + PID PPID COMMAND + 12913 12912 /home/armbian/vdi-terminal/bin/kiosk-shell +DESKTOP_SESSION=vdi-kiosk +DISPLAY=:0 +9dba9c66092aa9ccf67477b305aaefe09f52eabb8c6b30dbbcbb1ce43dc7a6a6 /home/armbian/vdi-terminal/bin/kiosk-shell +--- Authentication --- +vdi_device_auth result=ready +--- No host desktop processes --- +--- Boot ID unchanged --- +7ca3f9d2-9316-45cd-a490-7fc5c3db8ec4 +{"http_status": 200, "authorized_desktops": [{"name": "Ubuntu", "fps": 45, "width": 2560, "height": 1440}, {"name": "Windows 10", "fps": 45, "width": 2560, "height": 1440}, {"name": "麒麟", "fps": 30, "width": 2560, "height": 1440}], "identity_file_mode": "0o600"} diff --git a/docs/evidence/boot-auth-20261110/result.json b/docs/evidence/boot-auth-20261110/result.json new file mode 100644 index 0000000..b39a22b --- /dev/null +++ b/docs/evidence/boot-auth-20261110/result.json @@ -0,0 +1,32 @@ +{ + "date": "2026-11-10", + "cause": "A failed one-shot device authentication transitioned DESKTOP_LIST to CONFIG and had no authentication retry; the first underlying network/TLS/HTTP failure was not logged.", + "changes": [ + "Stay on desktop selection for authentication failure", + "Retry after completion with 2/4/8/16/30 second backoff", + "Do not load local authorization or request tickets before authentication", + "Log authentication HTTP status and network error code without credentials" + ], + "ctest_groups_passed": 4, + "new_regression_scenarios": [ + "offline startup and recovery without restart", + "HTTP 403 followed by authorization recovery", + "slow authentication does not overlap", + "synchronous invalid CA configuration stays on selection", + "local POC mode still loads desktops" + ], + "deployed_kiosk_sha256": "9dba9c66092aa9ccf67477b305aaefe09f52eabb8c6b30dbbcbb1ce43dc7a6a6", + "previous_kiosk_sha256": "2cca9d2d183c2f34f0f6172db207649decb2799414f9443043d175bbb19d33a1", + "backup": "/home/armbian/vdi-terminal/backups/boot-device-auth-20261110/", + "live_checks": { + "device_auth": "ready", + "authorized_desktops_http": 200, + "authorized_desktops_count": 3, + "session": "vdi-kiosk", + "boot_id": "7ca3f9d2-9316-45cd-a490-7fc5c3db8ec4", + "kernel_rebooted": false, + "host_desktop_processes": false, + "selection_screenshot": "selection-page.png" + }, + "power_cycle_validation": "Not performed; recovery scenarios used isolated HTTPS test platform on the box." +} diff --git a/docs/evidence/boot-auth-20261110/selection-page.png b/docs/evidence/boot-auth-20261110/selection-page.png new file mode 100644 index 0000000..372af29 Binary files /dev/null and b/docs/evidence/boot-auth-20261110/selection-page.png differ diff --git a/docs/evidence/cloud-administrators-20261110/browser-verification.json b/docs/evidence/cloud-administrators-20261110/browser-verification.json new file mode 100644 index 0000000..936b148 --- /dev/null +++ b/docs/evidence/cloud-administrators-20261110/browser-verification.json @@ -0,0 +1,13 @@ +{ + "checkedAt": "2026-11-10T02:36:12.594Z", + "realAPI": true, + "themes": [ + "dark", + "light" + ], + "mobileWidth": 390, + "createAndOwnPasswordDialogsCancelled": true, + "pageErrors": [], + "failedResponses": [], + "mutations": [] +} diff --git a/docs/evidence/cloud-administrators-20261110/fixture-accounts-dark.png b/docs/evidence/cloud-administrators-20261110/fixture-accounts-dark.png new file mode 100644 index 0000000..9b96d29 Binary files /dev/null and b/docs/evidence/cloud-administrators-20261110/fixture-accounts-dark.png differ diff --git a/docs/evidence/cloud-administrators-20261110/fixture-accounts-light.png b/docs/evidence/cloud-administrators-20261110/fixture-accounts-light.png new file mode 100644 index 0000000..5cc21ac Binary files /dev/null and b/docs/evidence/cloud-administrators-20261110/fixture-accounts-light.png differ diff --git a/docs/evidence/cloud-administrators-20261110/fixture-accounts-mobile.png b/docs/evidence/cloud-administrators-20261110/fixture-accounts-mobile.png new file mode 100644 index 0000000..95f8ebb Binary files /dev/null and b/docs/evidence/cloud-administrators-20261110/fixture-accounts-mobile.png differ diff --git a/docs/evidence/cloud-administrators-20261110/live-accounts-dark.png b/docs/evidence/cloud-administrators-20261110/live-accounts-dark.png new file mode 100644 index 0000000..b5d3ae3 Binary files /dev/null and b/docs/evidence/cloud-administrators-20261110/live-accounts-dark.png differ diff --git a/docs/evidence/cloud-administrators-20261110/live-accounts-light.png b/docs/evidence/cloud-administrators-20261110/live-accounts-light.png new file mode 100644 index 0000000..c82d296 Binary files /dev/null and b/docs/evidence/cloud-administrators-20261110/live-accounts-light.png differ diff --git a/docs/evidence/cloud-administrators-20261110/live-accounts-mobile.png b/docs/evidence/cloud-administrators-20261110/live-accounts-mobile.png new file mode 100644 index 0000000..1a051a3 Binary files /dev/null and b/docs/evidence/cloud-administrators-20261110/live-accounts-mobile.png differ diff --git a/docs/evidence/cloud-administrators-20261110/live-create-dialog.png b/docs/evidence/cloud-administrators-20261110/live-create-dialog.png new file mode 100644 index 0000000..f51a8e7 Binary files /dev/null and b/docs/evidence/cloud-administrators-20261110/live-create-dialog.png differ diff --git a/docs/evidence/cloud-administrators-20261110/live-create-mobile.png b/docs/evidence/cloud-administrators-20261110/live-create-mobile.png new file mode 100644 index 0000000..092856b Binary files /dev/null and b/docs/evidence/cloud-administrators-20261110/live-create-mobile.png differ diff --git a/docs/evidence/cloud-administrators-20261110/live-verification.json b/docs/evidence/cloud-administrators-20261110/live-verification.json new file mode 100644 index 0000000..193c324 --- /dev/null +++ b/docs/evidence/cloud-administrators-20261110/live-verification.json @@ -0,0 +1,149 @@ +{ + "checkedAt": "2026-11-10T10:35:19.033192+08:00", + "realAPI": true, + "checks": [ + { + "path": "/healthz", + "status": 200 + }, + { + "path": "/api/v1/auth/login", + "status": 200 + }, + { + "path": "/api/v1/administrators", + "status": 200 + }, + { + "path": "/api/v1/administrators", + "status": 201 + }, + { + "path": "/api/v1/auth/login", + "status": 200 + }, + { + "path": "/api/v1/administrators", + "status": 403 + }, + { + "path": "/api/v1/administrators/adm_069fc9f84da08f3593dccdfb4e6b2eab", + "status": 200 + }, + { + "path": "/api/v1/auth/me", + "status": 401 + }, + { + "path": "/api/v1/auth/login", + "status": 200 + }, + { + "path": "/api/v1/administrators/adm_069fc9f84da08f3593dccdfb4e6b2eab/password", + "status": 204 + }, + { + "path": "/api/v1/auth/me", + "status": 401 + }, + { + "path": "/api/v1/auth/login", + "status": 401 + }, + { + "path": "/api/v1/auth/login", + "status": 200 + }, + { + "path": "/api/v1/administrators/adm_069fc9f84da08f3593dccdfb4e6b2eab", + "status": 200 + }, + { + "path": "/api/v1/auth/me", + "status": 401 + }, + { + "path": "/api/v1/auth/login", + "status": 401 + }, + { + "path": "/api/v1/administrators/adm_069fc9f84da08f3593dccdfb4e6b2eab", + "status": 200 + }, + { + "path": "/api/v1/auth/login", + "status": 200 + }, + { + "path": "/api/v1/auth/login", + "status": 200 + }, + { + "path": "/api/v1/auth/password", + "status": 400 + }, + { + "path": "/api/v1/auth/me", + "status": 200 + }, + { + "path": "/api/v1/auth/password", + "status": 204 + }, + { + "path": "/api/v1/auth/me", + "status": 401 + }, + { + "path": "/api/v1/auth/me", + "status": 401 + }, + { + "path": "/api/v1/auth/login", + "status": 401 + }, + { + "path": "/api/v1/auth/login", + "status": 200 + }, + { + "path": "/api/v1/auth/logout", + "status": 204 + }, + { + "path": "/api/v1/administrators/adm_069fc9f84da08f3593dccdfb4e6b2eab", + "status": 200 + }, + { + "path": "/api/v1/auth/logout", + "status": 204 + }, + { + "path": "/api/v1/auth/login", + "status": 200 + }, + { + "path": "/api/v1/desktops", + "status": 200 + }, + { + "path": "/api/v1/terminals", + "status": 200 + }, + { + "path": "/api/v1/auth/logout", + "status": 204 + } + ], + "temporaryAccountRemoved": true, + "originalAdministratorAndPasswordUnchanged": true, + "desktopConfigurationAndBindingsUnchanged": true, + "managedVMIDs": [ + 100, + 101, + 102 + ], + "allVMsRunning": true, + "onlineTerminals": 1, + "vmMutationsPerformed": false +} diff --git a/docs/evidence/cloud-batch-operations-20261110/browser-verification.json b/docs/evidence/cloud-batch-operations-20261110/browser-verification.json new file mode 100644 index 0000000..7108634 --- /dev/null +++ b/docs/evidence/cloud-batch-operations-20261110/browser-verification.json @@ -0,0 +1,20 @@ +{ + "time": "2026-11-10T08:42:10.235Z", + "themes": [ + "dark", + "light" + ], + "viewportWidths": [ + 1440, + 390 + ], + "scriptErrors": [], + "failedResponses": [], + "blockedWrites": [], + "readOnlyPreflights": 2, + "batchSubmissions": 0, + "powerActions": 0, + "confirmationCancelled": true, + "emptyHistoryVerified": true, + "screenshots": 7 +} \ No newline at end of file diff --git a/docs/evidence/cloud-batch-operations-20261110/checks.json b/docs/evidence/cloud-batch-operations-20261110/checks.json new file mode 100644 index 0000000..bfead79 --- /dev/null +++ b/docs/evidence/cloud-batch-operations-20261110/checks.json @@ -0,0 +1,19 @@ +{ + "date": "2026-11-10", + "goFullSuite": "passed; dedicated isolated PostgreSQL, local HTTP PVE fixture", + "goVet": "passed", + "eslint": "passed", + "typecheck": "passed", + "vitestPassed": 3, + "productionBuild": "passed", + "dockerBuildAndDeploy": "passed", + "playwrightPassed": 45, + "playwrightFailed": 0, + "livePowerWrites": 0, + "livePreflightActions": [ + "start", + "shutdown", + "reboot" + ], + "liveBusinessSnapshotUnchanged": true +} \ No newline at end of file diff --git a/docs/evidence/cloud-batch-operations-20261110/fixture-batch-preview-dark.png b/docs/evidence/cloud-batch-operations-20261110/fixture-batch-preview-dark.png new file mode 100644 index 0000000..e0e58c6 Binary files /dev/null and b/docs/evidence/cloud-batch-operations-20261110/fixture-batch-preview-dark.png differ diff --git a/docs/evidence/cloud-batch-operations-20261110/fixture-batch-preview-light-mobile.png b/docs/evidence/cloud-batch-operations-20261110/fixture-batch-preview-light-mobile.png new file mode 100644 index 0000000..cf7dfd4 Binary files /dev/null and b/docs/evidence/cloud-batch-operations-20261110/fixture-batch-preview-light-mobile.png differ diff --git a/docs/evidence/cloud-batch-operations-20261110/fixture-batch-submitted-dark.png b/docs/evidence/cloud-batch-operations-20261110/fixture-batch-submitted-dark.png new file mode 100644 index 0000000..5beb37d Binary files /dev/null and b/docs/evidence/cloud-batch-operations-20261110/fixture-batch-submitted-dark.png differ diff --git a/docs/evidence/cloud-batch-operations-20261110/live-batch-history-dark.png b/docs/evidence/cloud-batch-operations-20261110/live-batch-history-dark.png new file mode 100644 index 0000000..5921281 Binary files /dev/null and b/docs/evidence/cloud-batch-operations-20261110/live-batch-history-dark.png differ diff --git a/docs/evidence/cloud-batch-operations-20261110/live-batch-history-light.png b/docs/evidence/cloud-batch-operations-20261110/live-batch-history-light.png new file mode 100644 index 0000000..0e08781 Binary files /dev/null and b/docs/evidence/cloud-batch-operations-20261110/live-batch-history-light.png differ diff --git a/docs/evidence/cloud-batch-operations-20261110/live-batch-preview-dark.png b/docs/evidence/cloud-batch-operations-20261110/live-batch-preview-dark.png new file mode 100644 index 0000000..a2418be Binary files /dev/null and b/docs/evidence/cloud-batch-operations-20261110/live-batch-preview-dark.png differ diff --git a/docs/evidence/cloud-batch-operations-20261110/live-batch-preview-light.png b/docs/evidence/cloud-batch-operations-20261110/live-batch-preview-light.png new file mode 100644 index 0000000..7c026e4 Binary files /dev/null and b/docs/evidence/cloud-batch-operations-20261110/live-batch-preview-light.png differ diff --git a/docs/evidence/cloud-batch-operations-20261110/live-batch-preview-mobile.png b/docs/evidence/cloud-batch-operations-20261110/live-batch-preview-mobile.png new file mode 100644 index 0000000..10fe39f Binary files /dev/null and b/docs/evidence/cloud-batch-operations-20261110/live-batch-preview-mobile.png differ diff --git a/docs/evidence/cloud-batch-operations-20261110/live-desktops-dark.png b/docs/evidence/cloud-batch-operations-20261110/live-desktops-dark.png new file mode 100644 index 0000000..20e7792 Binary files /dev/null and b/docs/evidence/cloud-batch-operations-20261110/live-desktops-dark.png differ diff --git a/docs/evidence/cloud-batch-operations-20261110/live-desktops-light.png b/docs/evidence/cloud-batch-operations-20261110/live-desktops-light.png new file mode 100644 index 0000000..de6230a Binary files /dev/null and b/docs/evidence/cloud-batch-operations-20261110/live-desktops-light.png differ diff --git a/docs/evidence/cloud-batch-operations-20261110/live-verification.json b/docs/evidence/cloud-batch-operations-20261110/live-verification.json new file mode 100644 index 0000000..bf746b5 --- /dev/null +++ b/docs/evidence/cloud-batch-operations-20261110/live-verification.json @@ -0,0 +1,159 @@ +{ + "time": "2026-11-10T08:41:51.709569+00:00", + "scope": "live read-only preflight; power writes tested only with local PVE HTTP fixture and isolated PostgreSQL", + "powerWrites": 0, + "snapshotWrites": 0, + "hardwareWrites": 0, + "cloneRequests": 0, + "checks": [ + { + "action": "start", + "eligibleCount": 0, + "skippedCount": 3, + "expiresAt": "2026-11-10T08:46:52.12093709Z", + "items": [ + { + "desktopId": "desk_29cf3c79d9fc1b4a25e1bfe55c5c943f", + "name": "Ubuntu", + "vmid": 102, + "powerState": "running", + "available": false, + "code": "power_mismatch", + "message": "当前电源状态不适用此操作" + }, + { + "desktopId": "desk_38010c040399376117ea116029767c25", + "name": "Windows 10", + "vmid": 100, + "powerState": "running", + "available": false, + "code": "power_mismatch", + "message": "当前电源状态不适用此操作" + }, + { + "desktopId": "desk_bbec6e55af36b91f042f66df80902007", + "name": "麒麟", + "vmid": 101, + "powerState": "running", + "available": false, + "code": "power_mismatch", + "message": "当前电源状态不适用此操作" + } + ] + }, + { + "action": "shutdown", + "eligibleCount": 3, + "skippedCount": 0, + "expiresAt": "2026-11-10T08:47:05.604010139Z", + "items": [ + { + "desktopId": "desk_29cf3c79d9fc1b4a25e1bfe55c5c943f", + "name": "Ubuntu", + "vmid": 102, + "powerState": "running", + "available": true, + "code": "ready", + "message": "可提交,执行前将再次核对" + }, + { + "desktopId": "desk_38010c040399376117ea116029767c25", + "name": "Windows 10", + "vmid": 100, + "powerState": "running", + "available": true, + "code": "ready", + "message": "可提交,执行前将再次核对" + }, + { + "desktopId": "desk_bbec6e55af36b91f042f66df80902007", + "name": "麒麟", + "vmid": 101, + "powerState": "running", + "available": true, + "code": "ready", + "message": "可提交,执行前将再次核对" + } + ] + }, + { + "action": "reboot", + "eligibleCount": 3, + "skippedCount": 0, + "expiresAt": "2026-11-10T08:47:05.741115326Z", + "items": [ + { + "desktopId": "desk_29cf3c79d9fc1b4a25e1bfe55c5c943f", + "name": "Ubuntu", + "vmid": 102, + "powerState": "running", + "available": true, + "code": "ready", + "message": "可提交,执行前将再次核对" + }, + { + "desktopId": "desk_38010c040399376117ea116029767c25", + "name": "Windows 10", + "vmid": 100, + "powerState": "running", + "available": true, + "code": "ready", + "message": "可提交,执行前将再次核对" + }, + { + "desktopId": "desk_bbec6e55af36b91f042f66df80902007", + "name": "麒麟", + "vmid": 101, + "powerState": "running", + "available": true, + "code": "ready", + "message": "可提交,执行前将再次核对" + } + ] + } + ], + "site": { + "terminals": [ + { + "sn": "ee621cae7199a2e9", + "agentVersion": "qt-platform/0.3.0", + "heartbeatAgeSeconds": 6.90757, + "session": null + } + ], + "desktops": [ + { + "vmid": 100, + "powerState": "running" + }, + { + "vmid": 101, + "powerState": "running" + }, + { + "vmid": 102, + "powerState": "running" + } + ], + "batchCount": 0, + "batchTaskCount": 0, + "tasks": 7, + "snapshots": 0 + }, + "businessSnapshotUnchanged": true, + "deployment": { + "time": "2026-11-10T08:38:53.386241+00:00", + "backup": ".local/live/backups/before-batch-operations-20261110-163850.dump", + "backupBytes": 78720, + "oldImageTag": "before-batch-operations-20261110-163850", + "oldImages": { + "api": "sha256:094d38e803437d3a34041e1af36530e7f14445cd37959b6d7316dfadc3f8b062", + "console": "sha256:3b3af4559890cbe7629abd0d029f83c59f6afc23e274b5db07ba64175ae15df0" + }, + "deployedAt": "2026-11-10T08:44:32.256998+00:00", + "newImages": { + "api": "sha256:326813ba972357535d34597e3b2aa794c6d5448b48f79549fe6e0792843076e6", + "console": "sha256:93a232b112ff021d8796e3b18780a5c6ece9e8f932a2203488c2329c13505d28" + } + } +} \ No newline at end of file diff --git a/docs/evidence/cloud-batch-operations-20261110/playwright-report.json b/docs/evidence/cloud-batch-operations-20261110/playwright-report.json new file mode 100644 index 0000000..0d89888 --- /dev/null +++ b/docs/evidence/cloud-batch-operations-20261110/playwright-report.json @@ -0,0 +1,1718 @@ +{ + "config": { + "argv": [ + "D:\\Nodejs\\node.exe", + "D:\\DAIMA\\vdi-cloud\\console\\node_modules\\@playwright\\test\\cli.js", + "test", + "e2e/administrators.spec.ts", + "e2e/catalog.spec.ts", + "e2e/configuration.spec.ts", + "e2e/lifecycle.spec.ts", + "e2e/operations.spec.ts", + "e2e/policies.spec.ts", + "e2e/snapshots.spec.ts", + "e2e/batch-operations.spec.ts", + "--reporter=list,json" + ], + "configFile": "D:\\DAIMA\\vdi-cloud\\console\\playwright.config.ts", + "rootDir": "D:/DAIMA/vdi-cloud/console/e2e", + "failOnFlakyTests": false, + "forbidOnly": false, + "fullyParallel": false, + "globalSetup": null, + "globalTeardown": null, + "globalTimeout": 0, + "grep": {}, + "grepInvert": null, + "maxFailures": 0, + "metadata": { + "actualWorkers": 1 + }, + "preserveOutput": "always", + "projects": [ + { + "outputDir": "D:/DAIMA/vdi-cloud/console/test-results", + "repeatEach": 1, + "retries": 0, + "metadata": { + "actualWorkers": 1 + }, + "id": "chromium", + "name": "chromium", + "testDir": "D:/DAIMA/vdi-cloud/console/e2e", + "testIgnore": [ + "**/live.spec.ts" + ], + "testMatch": [ + "**/*.@(spec|test).?(c|m)[jt]s?(x)" + ], + "timeout": 30000 + } + ], + "filteredProjects": [ + { + "grep": {}, + "grepInvert": null, + "outputDir": "D:\\DAIMA\\vdi-cloud\\console\\test-results", + "repeatEach": 1, + "retries": 0, + "metadata": { + "actualWorkers": 1 + }, + "name": "chromium", + "testDir": "D:\\DAIMA\\vdi-cloud\\console\\e2e", + "snapshotDir": "D:\\DAIMA\\vdi-cloud\\console\\e2e", + "testIgnore": "**/live.spec.ts", + "testMatch": "**/*.@(spec|test).?(c|m)[jt]s?(x)", + "timeout": 30000, + "use": { + "baseURL": "http://127.0.0.1:5183", + "trace": "retain-on-failure", + "screenshot": "only-on-failure", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/158.0.8127.18 Safari/537.36", + "viewport": { + "width": 1280, + "height": 720 + }, + "screen": { + "width": 1920, + "height": 1080 + }, + "deviceScaleFactor": 1, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium", + "channel": "msedge" + }, + "dependencies": [], + "ignoreSnapshots": false, + "__projectId": "chromium" + } + ], + "quiet": false, + "reporter": [ + [ + "list" + ], + [ + "json" + ] + ], + "reportSlowTests": { + "max": 5, + "threshold": 300000 + }, + "shard": null, + "tags": [], + "updateSnapshots": "default", + "updateSourceMethod": "patch", + "version": "1.65.0", + "workers": 1, + "webServer": null + }, + "suites": [ + { + "title": "administrators.spec.ts", + "file": "administrators.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "新增管理员校验密码确认,账号持久化字段正确", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3159, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:38:15.480Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "f106411db4d53e6f54fc-9ff20e11d6ba36d13fb4", + "file": "administrators.spec.ts", + "line": 37, + "column": 1 + }, + { + "title": "编辑本人禁止角色和停用,取消其他账号停用不提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4019, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:38:19.332Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "f106411db4d53e6f54fc-8f9e5dc1882673404c2c", + "file": "administrators.spec.ts", + "line": 60, + "column": 1 + }, + { + "title": "重置其他账号密码需要确认,取消不提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3172, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:38:23.372Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "f106411db4d53e6f54fc-62ed47448461158980c7", + "file": "administrators.spec.ts", + "line": 80, + "column": 1 + }, + { + "title": "只读人员可修改本人密码,错误原密码不退出,成功后清理登录", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2257, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:38:26.564Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "f106411db4d53e6f54fc-c111f5c502d645287604", + "file": "administrators.spec.ts", + "line": 96, + "column": 1 + } + ] + }, + { + "title": "batch-operations.spec.ts", + "file": "batch-operations.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "批量关机逐台预检,取消不提交,确认后仅显示已提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3810, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:38:28.858Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4670852b237ecdd90860-ff79dbd7596146b563c7", + "file": "batch-operations.spec.ts", + "line": 47, + "column": 1 + }, + { + "title": "卡片与列表共享选择,跨页保留选择并可清空", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2773, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:38:32.688Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4670852b237ecdd90860-b947bc7a8d75849b533f", + "file": "batch-operations.spec.ts", + "line": 58, + "column": 1 + }, + { + "title": "浅色窄屏预检与结果操作区域保持可见", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2225, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:38:35.480Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4670852b237ecdd90860-7a816c67ca4af9d813cd", + "file": "batch-operations.spec.ts", + "line": 68, + "column": 1 + }, + { + "title": "全部跳过或预检过期不能提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3703, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:38:37.732Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4670852b237ecdd90860-61d7c85b2d31ec138f30", + "file": "batch-operations.spec.ts", + "line": 74, + "column": 1 + }, + { + "title": "提交网络失败保留相同幂等标识重试", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3385, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:38:41.458Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4670852b237ecdd90860-d5c195cec979161e5088", + "file": "batch-operations.spec.ts", + "line": 80, + "column": 1 + }, + { + "title": "批量记录保留失败和跳过原因,运维可只读核对原任务", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2204, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:38:44.867Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4670852b237ecdd90860-f731b3741f4f29667611", + "file": "batch-operations.spec.ts", + "line": 85, + "column": 1 + }, + { + "title": "只读人员无批量选择与提交入口,可查看历史", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1921, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:38:47.100Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4670852b237ecdd90860-6c99db90e3c18cebb753", + "file": "batch-operations.spec.ts", + "line": 91, + "column": 1 + }, + { + "title": "任务页支持运维只读核对批量电源结果", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1850, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:38:49.045Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4670852b237ecdd90860-b85559f50ce3dae792bc", + "file": "batch-operations.spec.ts", + "line": 96, + "column": 1 + } + ] + }, + { + "title": "catalog.spec.ts", + "file": "catalog.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "模板克隆向导先预检,再提交带去重标识的任务", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2865, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:38:50.930Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "30aa42b97d8966d0c2dc-6dc25297809099af4961", + "file": "catalog.spec.ts", + "line": 37, + "column": 1 + }, + { + "title": "桌面池创建保存业务字段,窄屏与浅色主题可用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3186, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:38:53.821Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "30aa42b97d8966d0c2dc-cd42c1325adf729ee74e", + "file": "catalog.spec.ts", + "line": 58, + "column": 1 + }, + { + "title": "待初始化桌面必须勾选确认才能保存连接", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2871, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:38:57.033Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "30aa42b97d8966d0c2dc-b2369e6c29902b57e3c0", + "file": "catalog.spec.ts", + "line": 77, + "column": 1 + }, + { + "title": "空模板和接口错误显示明确原因,只读角色没有管理按钮", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2024, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:38:59.923Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "30aa42b97d8966d0c2dc-65bb9db30a98e42e5eb1", + "file": "catalog.spec.ts", + "line": 92, + "column": 1 + }, + { + "title": "模板读取失败提供错误反馈", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1378, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:01.970Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "30aa42b97d8966d0c2dc-7880f7837d1c1e12b68e", + "file": "catalog.spec.ts", + "line": 102, + "column": 1 + } + ] + }, + { + "title": "configuration.spec.ts", + "file": "configuration.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "接入配置不回显密钥,取消不写入,检查后保留原密钥保存", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3286, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:03.406Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-b03247368498a6e385e3", + "file": "configuration.spec.ts", + "line": 31, + "column": 1 + }, + { + "title": "接入检查失败保留输入并支持窄屏", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1804, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:06.711Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-4de3f8cdf96f8d9cfa06", + "file": "configuration.spec.ts", + "line": 49, + "column": 1 + }, + { + "title": "CPU 内存调整先预检,修改参数使预检失效,确认后提交一次", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3782, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:08.538Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-f720d3c1636dc5c09d61", + "file": "configuration.spec.ts", + "line": 63, + "column": 1 + }, + { + "title": "磁盘输入总容量并提示客体扩展,取消确认不提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3235, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:12.343Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-236d59933f17057a16f6", + "file": "configuration.spec.ts", + "line": 86, + "column": 1 + }, + { + "title": "运行中的桌面阻止修改,结果未知任务只读核对", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3745, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:15.614Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-3b49f62d60f5298ab0f9", + "file": "configuration.spec.ts", + "line": 104, + "column": 1 + }, + { + "title": "只读角色不显示接入与规格管理入口", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2005, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:19.379Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-075ac56a91937fe0a694", + "file": "configuration.spec.ts", + "line": 120, + "column": 1 + } + ] + }, + { + "title": "lifecycle.spec.ts", + "file": "lifecycle.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "终端资料保存名称和分组,深浅主题与窄屏可用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2813, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:21.417Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "1f1785dee40219527314-c0762e5dd5157bd1883b", + "file": "lifecycle.spec.ts", + "line": 41, + "column": 1 + }, + { + "title": "停用与重新激活需要确认,旧凭据撤销提示和一次性密钥可见", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4742, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:24.251Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "1f1785dee40219527314-59fd31cc1e9e633dfe77", + "file": "lifecycle.spec.ts", + "line": 62, + "column": 1 + }, + { + "title": "活动会话保护设备身份,运维和只读人员无设备维护入口", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3059, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:29.019Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "1f1785dee40219527314-a67e3aae0049fbe18ea5", + "file": "lifecycle.spec.ts", + "line": 83, + "column": 1 + }, + { + "title": "用户改名与停用保持授权,取消状态变更不提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3402, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:32.100Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "1f1785dee40219527314-4a42cb023d4d7c8fa280", + "file": "lifecycle.spec.ts", + "line": 97, + "column": 1 + }, + { + "title": "批量授权明确目标和追加撤销语义,取消不提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 6186, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:35.528Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "1f1785dee40219527314-1c3b752922c992ae7414", + "file": "lifecycle.spec.ts", + "line": 112, + "column": 1 + } + ] + }, + { + "title": "operations.spec.ts", + "file": "operations.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "CSV 预检有错误不能提交,修改后重新预检并整批导入", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4354, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:41.797Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4f8fd72df7d8ab107a64-29a62ceefda80d2f008a", + "file": "operations.spec.ts", + "line": 36, + "column": 1 + }, + { + "title": "批量终端激活信息支持下载,未下载关闭需要确认且密钥不残留", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4801, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:46.186Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4f8fd72df7d8ab107a64-e935b01f4412d0cef3b4", + "file": "operations.spec.ts", + "line": 54, + "column": 1 + }, + { + "title": "会话历史按需查询,展示心跳超时和实际连接时长", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2510, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:51.026Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4f8fd72df7d8ab107a64-49e23988c2dda0e07a6c", + "file": "operations.spec.ts", + "line": 69, + "column": 1 + }, + { + "title": "告警确认保留正在告警状态,规则编辑和深浅窄屏可用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3968, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:53.564Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4f8fd72df7d8ab107a64-5b1f20f2475609c5661c", + "file": "operations.spec.ts", + "line": 77, + "column": 1 + }, + { + "title": "只读人员可查监控及历史,无导入、确认或规则编辑入口", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2326, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:57.554Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4f8fd72df7d8ab107a64-2e666d3f00d39df4f6ee", + "file": "operations.spec.ts", + "line": 95, + "column": 1 + } + ] + }, + { + "title": "policies.spec.ts", + "file": "policies.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "策略版本可保存和复制,深浅主题及手机表单可用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2802, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:39:59.934Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "b074270cade4da53ff13-98ecff8013fc8188157d", + "file": "policies.spec.ts", + "line": 39, + "column": 1 + }, + { + "title": "按分组下发先确认,提交后等待设备回执,不显示已应用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4509, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:40:02.756Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "b074270cade4da53ff13-21c971eda20a592e7370", + "file": "policies.spec.ts", + "line": 60, + "column": 1 + }, + { + "title": "旧客户端分组禁止下发,失败原因和重试入口可查看", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3513, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:40:07.293Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "b074270cade4da53ff13-64b439cb8d39f3f53217", + "file": "policies.spec.ts", + "line": 78, + "column": 1 + }, + { + "title": "只读和运维角色只能查看策略与结果", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3486, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:40:10.843Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "b074270cade4da53ff13-b28e3758b602c09af5c2", + "file": "policies.spec.ts", + "line": 90, + "column": 1 + } + ] + }, + { + "title": "snapshots.spec.ts", + "file": "snapshots.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "创建快照先预检,参数改变失效,取消不写,确认提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4006, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:40:14.374Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-a03887b92dbaa2e36b31", + "file": "snapshots.spec.ts", + "line": 36, + "column": 1 + }, + { + "title": "还原需输入当前 VMID,保留磁盘范围提示,支持浅色窄屏", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3331, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:40:18.398Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-3354c2348ac9287db9af", + "file": "snapshots.spec.ts", + "line": 55, + "column": 1 + }, + { + "title": "删除叶快照先确认,内存还原和含子快照的删除禁用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3137, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:40:21.747Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-8a9e31987ab4df26a860", + "file": "snapshots.spec.ts", + "line": 75, + "column": 1 + }, + { + "title": "运行中或权限不足阻止操作", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2320, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:40:24.910Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-2413751efdb9e97a4851", + "file": "snapshots.spec.ts", + "line": 84, + "column": 1 + }, + { + "title": "只读角色仅可查看列表", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2035, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:40:27.250Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-b913b949af08f8e0d78e", + "file": "snapshots.spec.ts", + "line": 90, + "column": 1 + }, + { + "title": "PVE 权限不足与读取失败明确显示", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2377, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:40:29.303Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-6397b2bbe7c944781389", + "file": "snapshots.spec.ts", + "line": 95, + "column": 1 + }, + { + "title": "网络失败重试保留相同幂等标识", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3701, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:40:31.697Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-cb01d8a027460462177e", + "file": "snapshots.spec.ts", + "line": 102, + "column": 1 + }, + { + "title": "任务页只读核对快照结果", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1829, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:40:35.415Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-8f76ce7fa3598e801550", + "file": "snapshots.spec.ts", + "line": 111, + "column": 1 + } + ] + } + ], + "errors": [], + "stats": { + "startTime": "2026-11-10T08:38:14.383Z", + "duration": 145575.261, + "expected": 45, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + } +} \ No newline at end of file diff --git a/docs/evidence/cloud-catalog-20261110/browser-verification.json b/docs/evidence/cloud-catalog-20261110/browser-verification.json new file mode 100644 index 0000000..966dece --- /dev/null +++ b/docs/evidence/cloud-catalog-20261110/browser-verification.json @@ -0,0 +1,48 @@ +{ + "checkedAt": "2026-11-10T02:07:43.283Z", + "realAPI": true, + "results": [ + { + "route": "templates", + "theme": "dark", + "heading": "镜像模板" + }, + { + "route": "pools", + "theme": "dark", + "heading": "桌面池" + }, + { + "route": "desktops", + "theme": "dark", + "heading": "桌面管理" + }, + { + "route": "tasks", + "theme": "dark", + "heading": "任务与审计" + }, + { + "route": "templates", + "theme": "light", + "heading": "镜像模板" + }, + { + "route": "pools", + "theme": "light", + "heading": "桌面池" + }, + { + "route": "desktops", + "theme": "light", + "heading": "桌面管理" + }, + { + "route": "tasks", + "theme": "light", + "heading": "任务与审计" + } + ], + "pageErrors": [], + "failedResponses": [] +} diff --git a/docs/evidence/cloud-catalog-20261110/fixture-pools-light.png b/docs/evidence/cloud-catalog-20261110/fixture-pools-light.png new file mode 100644 index 0000000..b9b0cf4 Binary files /dev/null and b/docs/evidence/cloud-catalog-20261110/fixture-pools-light.png differ diff --git a/docs/evidence/cloud-catalog-20261110/fixture-pools-mobile.png b/docs/evidence/cloud-catalog-20261110/fixture-pools-mobile.png new file mode 100644 index 0000000..3112e96 Binary files /dev/null and b/docs/evidence/cloud-catalog-20261110/fixture-pools-mobile.png differ diff --git a/docs/evidence/cloud-catalog-20261110/fixture-templates-dark.png b/docs/evidence/cloud-catalog-20261110/fixture-templates-dark.png new file mode 100644 index 0000000..44ec463 Binary files /dev/null and b/docs/evidence/cloud-catalog-20261110/fixture-templates-dark.png differ diff --git a/docs/evidence/cloud-catalog-20261110/live-desktops-dark.png b/docs/evidence/cloud-catalog-20261110/live-desktops-dark.png new file mode 100644 index 0000000..8613822 Binary files /dev/null and b/docs/evidence/cloud-catalog-20261110/live-desktops-dark.png differ diff --git a/docs/evidence/cloud-catalog-20261110/live-desktops-light.png b/docs/evidence/cloud-catalog-20261110/live-desktops-light.png new file mode 100644 index 0000000..8a700bd Binary files /dev/null and b/docs/evidence/cloud-catalog-20261110/live-desktops-light.png differ diff --git a/docs/evidence/cloud-catalog-20261110/live-pools-dark.png b/docs/evidence/cloud-catalog-20261110/live-pools-dark.png new file mode 100644 index 0000000..0eeb288 Binary files /dev/null and b/docs/evidence/cloud-catalog-20261110/live-pools-dark.png differ diff --git a/docs/evidence/cloud-catalog-20261110/live-pools-light.png b/docs/evidence/cloud-catalog-20261110/live-pools-light.png new file mode 100644 index 0000000..9a91277 Binary files /dev/null and b/docs/evidence/cloud-catalog-20261110/live-pools-light.png differ diff --git a/docs/evidence/cloud-catalog-20261110/live-pools-mobile.png b/docs/evidence/cloud-catalog-20261110/live-pools-mobile.png new file mode 100644 index 0000000..710b163 Binary files /dev/null and b/docs/evidence/cloud-catalog-20261110/live-pools-mobile.png differ diff --git a/docs/evidence/cloud-catalog-20261110/live-tasks-dark.png b/docs/evidence/cloud-catalog-20261110/live-tasks-dark.png new file mode 100644 index 0000000..0fe63fc Binary files /dev/null and b/docs/evidence/cloud-catalog-20261110/live-tasks-dark.png differ diff --git a/docs/evidence/cloud-catalog-20261110/live-tasks-light.png b/docs/evidence/cloud-catalog-20261110/live-tasks-light.png new file mode 100644 index 0000000..a2bbfb4 Binary files /dev/null and b/docs/evidence/cloud-catalog-20261110/live-tasks-light.png differ diff --git a/docs/evidence/cloud-catalog-20261110/live-templates-dark.png b/docs/evidence/cloud-catalog-20261110/live-templates-dark.png new file mode 100644 index 0000000..5e1b0de Binary files /dev/null and b/docs/evidence/cloud-catalog-20261110/live-templates-dark.png differ diff --git a/docs/evidence/cloud-catalog-20261110/live-templates-light.png b/docs/evidence/cloud-catalog-20261110/live-templates-light.png new file mode 100644 index 0000000..b97df53 Binary files /dev/null and b/docs/evidence/cloud-catalog-20261110/live-templates-light.png differ diff --git a/docs/evidence/cloud-catalog-20261110/live-templates-mobile.png b/docs/evidence/cloud-catalog-20261110/live-templates-mobile.png new file mode 100644 index 0000000..5091c5c Binary files /dev/null and b/docs/evidence/cloud-catalog-20261110/live-templates-mobile.png differ diff --git a/docs/evidence/cloud-catalog-20261110/live-verification.json b/docs/evidence/cloud-catalog-20261110/live-verification.json new file mode 100644 index 0000000..cacf021 --- /dev/null +++ b/docs/evidence/cloud-catalog-20261110/live-verification.json @@ -0,0 +1,27 @@ +{ + "checkedAt": "2026-11-10T10:07:32.848271+08:00", + "mode": "production", + "health": "ok", + "existingMappingsUnchanged": true, + "managedVMIDs": [ + 100, + 101, + 102 + ], + "allVMsRunning": true, + "onlineTerminals": 1, + "registeredTemplates": 0, + "discoveredPVETemplates": 0, + "storage": [ + { + "name": "local", + "availableGiB": 10.47 + }, + { + "name": "local-lvm", + "availableGiB": 7.22 + } + ], + "poolPersistenceCheck": "create/update/empty-members/disable passed; own temporary pool removed", + "liveVMClonePerformed": false +} diff --git a/docs/evidence/cloud-configuration-20261110/browser-verification.json b/docs/evidence/cloud-configuration-20261110/browser-verification.json new file mode 100644 index 0000000..26ac7d0 --- /dev/null +++ b/docs/evidence/cloud-configuration-20261110/browser-verification.json @@ -0,0 +1,16 @@ +{ + "time": "2026-11-10T07:28:23.238Z", + "themes": [ + "dark", + "light" + ], + "viewportWidths": [ + 1440, + 390 + ], + "scriptErrors": [], + "failedResponses": [], + "mutations": [], + "readOnlyInspection": true, + "screenshots": 8 +} \ No newline at end of file diff --git a/docs/evidence/cloud-configuration-20261110/hardware-compute-dark.png b/docs/evidence/cloud-configuration-20261110/hardware-compute-dark.png new file mode 100644 index 0000000..c63b081 Binary files /dev/null and b/docs/evidence/cloud-configuration-20261110/hardware-compute-dark.png differ diff --git a/docs/evidence/cloud-configuration-20261110/hardware-disk-light.png b/docs/evidence/cloud-configuration-20261110/hardware-disk-light.png new file mode 100644 index 0000000..f42c507 Binary files /dev/null and b/docs/evidence/cloud-configuration-20261110/hardware-disk-light.png differ diff --git a/docs/evidence/cloud-configuration-20261110/live-disk-dark.png b/docs/evidence/cloud-configuration-20261110/live-disk-dark.png new file mode 100644 index 0000000..865e583 Binary files /dev/null and b/docs/evidence/cloud-configuration-20261110/live-disk-dark.png differ diff --git a/docs/evidence/cloud-configuration-20261110/live-disk-light.png b/docs/evidence/cloud-configuration-20261110/live-disk-light.png new file mode 100644 index 0000000..0e25165 Binary files /dev/null and b/docs/evidence/cloud-configuration-20261110/live-disk-light.png differ diff --git a/docs/evidence/cloud-configuration-20261110/live-hardware-dark.png b/docs/evidence/cloud-configuration-20261110/live-hardware-dark.png new file mode 100644 index 0000000..00263f9 Binary files /dev/null and b/docs/evidence/cloud-configuration-20261110/live-hardware-dark.png differ diff --git a/docs/evidence/cloud-configuration-20261110/live-hardware-light.png b/docs/evidence/cloud-configuration-20261110/live-hardware-light.png new file mode 100644 index 0000000..c0df294 Binary files /dev/null and b/docs/evidence/cloud-configuration-20261110/live-hardware-light.png differ diff --git a/docs/evidence/cloud-configuration-20261110/live-hardware-mobile.png b/docs/evidence/cloud-configuration-20261110/live-hardware-mobile.png new file mode 100644 index 0000000..da73998 Binary files /dev/null and b/docs/evidence/cloud-configuration-20261110/live-hardware-mobile.png differ diff --git a/docs/evidence/cloud-configuration-20261110/live-pve-dark.png b/docs/evidence/cloud-configuration-20261110/live-pve-dark.png new file mode 100644 index 0000000..e527090 Binary files /dev/null and b/docs/evidence/cloud-configuration-20261110/live-pve-dark.png differ diff --git a/docs/evidence/cloud-configuration-20261110/live-pve-light.png b/docs/evidence/cloud-configuration-20261110/live-pve-light.png new file mode 100644 index 0000000..ea3f31a Binary files /dev/null and b/docs/evidence/cloud-configuration-20261110/live-pve-light.png differ diff --git a/docs/evidence/cloud-configuration-20261110/live-pve-mobile.png b/docs/evidence/cloud-configuration-20261110/live-pve-mobile.png new file mode 100644 index 0000000..0c81af2 Binary files /dev/null and b/docs/evidence/cloud-configuration-20261110/live-pve-mobile.png differ diff --git a/docs/evidence/cloud-configuration-20261110/live-verification.json b/docs/evidence/cloud-configuration-20261110/live-verification.json new file mode 100644 index 0000000..9ff0a1d --- /dev/null +++ b/docs/evidence/cloud-configuration-20261110/live-verification.json @@ -0,0 +1,90 @@ +{ + "time": "2026-11-10T07:27:26.168155+00:00", + "vmPowerActions": 0, + "vmHardwareWrites": 0, + "cloneRequests": 0, + "connection": { + "check": true, + "blankSecretPreserved": true, + "encryptedAtRest": true, + "secretOmittedFromResponse": true, + "failedSaveKeptPriorConfig": true, + "hotReload": true, + "nodes": 1, + "vms": 3 + }, + "hardwareReadOnly": [ + { + "vmid": 102, + "cores": 8, + "sockets": 1, + "memoryMiB": 6144, + "disks": [ + { + "key": "scsi0", + "storage": "local", + "sizeGiB": 50 + } + ], + "runningModificationRejected": true + }, + { + "vmid": 100, + "cores": 4, + "sockets": 1, + "memoryMiB": 8192, + "disks": [ + { + "key": "sata0", + "storage": "local-lvm", + "sizeGiB": 60 + } + ], + "runningModificationRejected": true + }, + { + "vmid": 101, + "cores": 2, + "sockets": 1, + "memoryMiB": 4096, + "disks": [ + { + "key": "scsi0", + "storage": "local-lvm", + "sizeGiB": 50 + } + ], + "runningModificationRejected": true + } + ], + "siteHardwarePermissions": [ + { + "vmid": 102, + "cpu": false, + "memory": false, + "disk": false + }, + { + "vmid": 100, + "cpu": false, + "memory": false, + "disk": false + }, + { + "vmid": 101, + "cpu": false, + "memory": false, + "disk": false + } + ], + "loginLimit": { + "temporaryAccount": true, + "attemptsAllowed": 10, + "attempt11Rejected": true, + "retryAfterProvided": true, + "forgedForwardingDidNotResetAccountBudget": true, + "viewerCannotReadConnection": true + }, + "temporaryAccountCleaned": true, + "businessSnapshotUnchanged": true +} \ No newline at end of file diff --git a/docs/evidence/cloud-configuration-20261110/pve-connection-dark.png b/docs/evidence/cloud-configuration-20261110/pve-connection-dark.png new file mode 100644 index 0000000..afcfbcb Binary files /dev/null and b/docs/evidence/cloud-configuration-20261110/pve-connection-dark.png differ diff --git a/docs/evidence/cloud-configuration-20261110/pve-connection-mobile.png b/docs/evidence/cloud-configuration-20261110/pve-connection-mobile.png new file mode 100644 index 0000000..d7727f4 Binary files /dev/null and b/docs/evidence/cloud-configuration-20261110/pve-connection-mobile.png differ diff --git a/docs/evidence/cloud-configuration-20261110/restart-verification.json b/docs/evidence/cloud-configuration-20261110/restart-verification.json new file mode 100644 index 0000000..c9a259d --- /dev/null +++ b/docs/evidence/cloud-configuration-20261110/restart-verification.json @@ -0,0 +1,33 @@ +{ + "time": "2026-11-10T07:33:51.209149+00:00", + "vmPowerActions": 0, + "vmHardwareWrites": 0, + "cloneRequests": 0, + "encryptedConfigurationSurvivedRestart": true, + "businessSnapshotUnchanged": true, + "site": { + "terminals": [ + { + "sn": "ee621cae7199a2e9", + "agentVersion": "qt-platform/0.3.0", + "lastSeen": "2026-11-10T07:33:29.003564+00:00", + "heartbeatAgeSeconds": 22.56868, + "session": null + } + ], + "desktops": [ + { + "vmid": 100, + "powerState": "running" + }, + { + "vmid": 101, + "powerState": "running" + }, + { + "vmid": 102, + "powerState": "running" + } + ] + } +} \ No newline at end of file diff --git a/docs/evidence/cloud-configuration-20261110/test-summary.json b/docs/evidence/cloud-configuration-20261110/test-summary.json new file mode 100644 index 0000000..48d5746 --- /dev/null +++ b/docs/evidence/cloud-configuration-20261110/test-summary.json @@ -0,0 +1,11 @@ +{ + "goFullSuite": "passed (dedicated vdi_configuration_test only)", + "goVet": "passed", + "frontendLint": "passed", + "frontendTypes": "passed", + "frontendUnitTests": 3, + "browserRegressions": 23, + "newBrowserFlows": 6, + "dockerBuild": "passed", + "liveVMWritesInTests": 0 +} \ No newline at end of file diff --git a/docs/evidence/cloud-gpu-inventory-20261110/browser-verification.json b/docs/evidence/cloud-gpu-inventory-20261110/browser-verification.json new file mode 100644 index 0000000..cfa31e6 --- /dev/null +++ b/docs/evidence/cloud-gpu-inventory-20261110/browser-verification.json @@ -0,0 +1,20 @@ +{ + "time": "2026-11-10T09:10:36.285Z", + "themes": [ + "dark", + "light" + ], + "viewportWidths": [ + 1440, + 390 + ], + "scriptErrors": [], + "failedResponses": [], + "blockedWrites": [], + "gpuWrites": 0, + "powerWrites": 0, + "verifiedGPUCount": 2, + "windowsVMReference": 100, + "partialCoverageShown": true, + "screenshots": 6 +} \ No newline at end of file diff --git a/docs/evidence/cloud-gpu-inventory-20261110/checks.json b/docs/evidence/cloud-gpu-inventory-20261110/checks.json new file mode 100644 index 0000000..efa9bc3 --- /dev/null +++ b/docs/evidence/cloud-gpu-inventory-20261110/checks.json @@ -0,0 +1,17 @@ +{ + "date": "2026-11-10", + "goFullSuite": "passed; dedicated isolated PostgreSQL, local HTTP PVE fixture", + "goVet": "passed", + "eslint": "passed", + "typecheck": "passed", + "vitestPassed": 3, + "productionBuild": "passed", + "dockerBuildAndDeploy": "passed", + "playwrightPassed": 51, + "playwrightFailed": 0, + "finalGPURecheckPassed": 6, + "liveGPUWrites": 0, + "livePowerWrites": 0, + "liveBusinessSnapshotUnchanged": true, + "liveVMConfigDigestsUnchanged": true +} \ No newline at end of file diff --git a/docs/evidence/cloud-gpu-inventory-20261110/fixture-gpu-detail-light-mobile.png b/docs/evidence/cloud-gpu-inventory-20261110/fixture-gpu-detail-light-mobile.png new file mode 100644 index 0000000..e1f63d6 Binary files /dev/null and b/docs/evidence/cloud-gpu-inventory-20261110/fixture-gpu-detail-light-mobile.png differ diff --git a/docs/evidence/cloud-gpu-inventory-20261110/fixture-gpu-inventory-dark.png b/docs/evidence/cloud-gpu-inventory-20261110/fixture-gpu-inventory-dark.png new file mode 100644 index 0000000..1268ca1 Binary files /dev/null and b/docs/evidence/cloud-gpu-inventory-20261110/fixture-gpu-inventory-dark.png differ diff --git a/docs/evidence/cloud-gpu-inventory-20261110/fixture-gpu-inventory-light-mobile.png b/docs/evidence/cloud-gpu-inventory-20261110/fixture-gpu-inventory-light-mobile.png new file mode 100644 index 0000000..7bb9fe9 Binary files /dev/null and b/docs/evidence/cloud-gpu-inventory-20261110/fixture-gpu-inventory-light-mobile.png differ diff --git a/docs/evidence/cloud-gpu-inventory-20261110/fixture-gpu-reference-dark.png b/docs/evidence/cloud-gpu-inventory-20261110/fixture-gpu-reference-dark.png new file mode 100644 index 0000000..0d3cef7 Binary files /dev/null and b/docs/evidence/cloud-gpu-inventory-20261110/fixture-gpu-reference-dark.png differ diff --git a/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-detail-dark.png b/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-detail-dark.png new file mode 100644 index 0000000..d38e79a Binary files /dev/null and b/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-detail-dark.png differ diff --git a/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-detail-light.png b/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-detail-light.png new file mode 100644 index 0000000..c23a6ca Binary files /dev/null and b/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-detail-light.png differ diff --git a/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-detail-mobile.png b/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-detail-mobile.png new file mode 100644 index 0000000..fdf85d0 Binary files /dev/null and b/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-detail-mobile.png differ diff --git a/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-inventory-dark.png b/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-inventory-dark.png new file mode 100644 index 0000000..9bd11d6 Binary files /dev/null and b/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-inventory-dark.png differ diff --git a/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-inventory-light.png b/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-inventory-light.png new file mode 100644 index 0000000..217882d Binary files /dev/null and b/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-inventory-light.png differ diff --git a/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-inventory-mobile.png b/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-inventory-mobile.png new file mode 100644 index 0000000..58dfec6 Binary files /dev/null and b/docs/evidence/cloud-gpu-inventory-20261110/live-gpu-inventory-mobile.png differ diff --git a/docs/evidence/cloud-gpu-inventory-20261110/live-verification.json b/docs/evidence/cloud-gpu-inventory-20261110/live-verification.json new file mode 100644 index 0000000..e6ead41 --- /dev/null +++ b/docs/evidence/cloud-gpu-inventory-20261110/live-verification.json @@ -0,0 +1,106 @@ +{ + "time": "2026-11-10T09:10:32.890463+00:00", + "scope": "read-only GPU and QEMU configuration inspection", + "inventory": { + "complete": false, + "issues": [ + { + "code": "custom_vm_config", + "vmid": 100, + "message": "虚拟机包含自定义参数或宿主脚本,额外设备引用未解析" + } + ], + "items": [ + { + "id": "starhe:0000:00:02.0", + "address": "0000:00:02.0", + "name": "4th Gen Core Processor Integrated Graphics Controller", + "vendor": "Intel Corporation", + "vendorId": "0x8086", + "deviceId": "0x0416", + "class": "0x030000", + "iommuGroup": 0, + "state": "unknown", + "references": [] + }, + { + "id": "starhe:0000:01:00.0", + "address": "0000:01:00.0", + "name": "GM204M [GeForce GTX 965M]", + "vendor": "NVIDIA Corporation", + "vendorId": "0x10de", + "deviceId": "0x13d9", + "class": "0x030200", + "iommuGroup": 2, + "state": "referenced", + "references": [ + { + "vmid": 100, + "vmName": "win10-test", + "vmStatus": "running", + "template": false, + "desktopId": "desk_38010c040399376117ea116029767c25", + "desktopName": "Windows 10", + "slot": "hostpci0", + "phase": "current", + "source": "direct", + "mapping": "", + "mediated": false, + "pendingRemoval": false + } + ] + } + ], + "node": "starhe", + "observedAt": "2026-11-10T09:10:32.294280849Z", + "scannedVmCount": 3, + "visibleVmCount": 3 + }, + "site": { + "terminals": [ + { + "sn": "ee621cae7199a2e9", + "agentVersion": "qt-platform/0.3.0", + "heartbeatAgeSeconds": 3.650522, + "session": null + } + ], + "desktops": [ + { + "vmid": 100, + "powerState": "running" + }, + { + "vmid": 101, + "powerState": "running" + }, + { + "vmid": 102, + "powerState": "running" + } + ], + "tasks": 7, + "snapshots": 0 + }, + "businessSnapshotUnchanged": true, + "vmConfigurationDigestsUnchanged": true, + "gpuBindingWrites": 0, + "powerWrites": 0, + "snapshotWrites": 0, + "diskWrites": 0, + "deployment": { + "time": "2026-11-10T09:02:09.266736+00:00", + "backup": ".local/live/backups/before-gpu-inventory-20261110-170206.dump", + "backupBytes": 84736, + "oldImageTag": "before-gpu-inventory-20261110-170206", + "oldImages": { + "api": "sha256:326813ba972357535d34597e3b2aa794c6d5448b48f79549fe6e0792843076e6", + "console": "sha256:93a232b112ff021d8796e3b18780a5c6ece9e8f932a2203488c2329c13505d28" + }, + "deploymentVerifiedAt": "2026-11-10T09:10:50.372617+00:00", + "newImages": { + "api": "sha256:0c33d1618dc5f6ed5dd0afa0d5876f2fc22eaf94508815b90029461b77e20259", + "console": "sha256:71d16b9761d49a91a117d8a65b756a1089fec15dfbfc3f2607a0274cc7afd73c" + } + } +} \ No newline at end of file diff --git a/docs/evidence/cloud-gpu-inventory-20261110/playwright-gpu-final.json b/docs/evidence/cloud-gpu-inventory-20261110/playwright-gpu-final.json new file mode 100644 index 0000000..efba4e3 --- /dev/null +++ b/docs/evidence/cloud-gpu-inventory-20261110/playwright-gpu-final.json @@ -0,0 +1,329 @@ +{ + "config": { + "argv": [ + "D:\\Nodejs\\node.exe", + "D:\\DAIMA\\vdi-cloud\\console\\node_modules\\@playwright\\test\\cli.js", + "test", + "e2e/gpus.spec.ts", + "--reporter=list,json" + ], + "configFile": "D:\\DAIMA\\vdi-cloud\\console\\playwright.config.ts", + "rootDir": "D:/DAIMA/vdi-cloud/console/e2e", + "failOnFlakyTests": false, + "forbidOnly": false, + "fullyParallel": false, + "globalSetup": null, + "globalTeardown": null, + "globalTimeout": 0, + "grep": {}, + "grepInvert": null, + "maxFailures": 0, + "metadata": { + "actualWorkers": 1 + }, + "preserveOutput": "always", + "projects": [ + { + "outputDir": "D:/DAIMA/vdi-cloud/console/test-results", + "repeatEach": 1, + "retries": 0, + "metadata": { + "actualWorkers": 1 + }, + "id": "chromium", + "name": "chromium", + "testDir": "D:/DAIMA/vdi-cloud/console/e2e", + "testIgnore": [ + "**/live.spec.ts" + ], + "testMatch": [ + "**/*.@(spec|test).?(c|m)[jt]s?(x)" + ], + "timeout": 30000 + } + ], + "filteredProjects": [ + { + "grep": {}, + "grepInvert": null, + "outputDir": "D:\\DAIMA\\vdi-cloud\\console\\test-results", + "repeatEach": 1, + "retries": 0, + "metadata": { + "actualWorkers": 1 + }, + "name": "chromium", + "testDir": "D:\\DAIMA\\vdi-cloud\\console\\e2e", + "snapshotDir": "D:\\DAIMA\\vdi-cloud\\console\\e2e", + "testIgnore": "**/live.spec.ts", + "testMatch": "**/*.@(spec|test).?(c|m)[jt]s?(x)", + "timeout": 30000, + "use": { + "baseURL": "http://127.0.0.1:5183", + "trace": "retain-on-failure", + "screenshot": "only-on-failure", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/158.0.8127.18 Safari/537.36", + "viewport": { + "width": 1280, + "height": 720 + }, + "screen": { + "width": 1920, + "height": 1080 + }, + "deviceScaleFactor": 1, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium", + "channel": "msedge" + }, + "dependencies": [], + "ignoreSnapshots": false, + "__projectId": "chromium" + } + ], + "quiet": false, + "reporter": [ + [ + "list" + ], + [ + "json" + ] + ], + "reportSlowTests": { + "max": 5, + "threshold": 300000 + }, + "shard": null, + "tags": [], + "updateSnapshots": "default", + "updateSourceMethod": "patch", + "version": "1.65.0", + "workers": 1, + "webServer": null + }, + "suites": [ + { + "title": "gpus.spec.ts", + "file": "gpus.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "只读角色可查显卡并筛选配置引用,IOMMU 0 保留", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2221, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:09:05.718Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "339e1869555d4df5a70f-908a22891ab6162efd96", + "file": "gpus.spec.ts", + "line": 31, + "column": 1 + }, + { + "title": "详情区分当前与待应用、待移除、模板及未纳管的映射引用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1815, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:09:08.689Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "339e1869555d4df5a70f-033b538c63d47607bcb8", + "file": "gpus.spec.ts", + "line": 44, + "column": 1 + }, + { + "title": "配置读取不完整显示待核查,不宣称显卡可用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1589, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:09:10.527Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "339e1869555d4df5a70f-d14ca93dfc0e7b05ba08", + "file": "gpus.spec.ts", + "line": 54, + "column": 1 + }, + { + "title": "PCI 权限错误提供重试,数据过期有明确提示", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1480, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:09:12.140Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "339e1869555d4df5a70f-52cbecb3930b42eec753", + "file": "gpus.spec.ts", + "line": 62, + "column": 1 + }, + { + "title": "浅色窄屏清单和详情可读且页面不横向溢出", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2009, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:09:13.640Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "339e1869555d4df5a70f-f676b76941e652a2209b", + "file": "gpus.spec.ts", + "line": 69, + "column": 1 + }, + { + "title": "空节点清单与尚未接入显示真实空态", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2113, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:09:15.677Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "339e1869555d4df5a70f-1db16c0f30c2de9beb2d", + "file": "gpus.spec.ts", + "line": 80, + "column": 1 + } + ] + } + ], + "errors": [], + "stats": { + "startTime": "2026-11-10T09:09:04.608Z", + "duration": 14081.699, + "expected": 6, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + } +} \ No newline at end of file diff --git a/docs/evidence/cloud-gpu-inventory-20261110/playwright-report.json b/docs/evidence/cloud-gpu-inventory-20261110/playwright-report.json new file mode 100644 index 0000000..1d736ac --- /dev/null +++ b/docs/evidence/cloud-gpu-inventory-20261110/playwright-report.json @@ -0,0 +1,1931 @@ +{ + "config": { + "argv": [ + "D:\\Nodejs\\node.exe", + "D:\\DAIMA\\vdi-cloud\\console\\node_modules\\@playwright\\test\\cli.js", + "test", + "e2e/administrators.spec.ts", + "e2e/catalog.spec.ts", + "e2e/configuration.spec.ts", + "e2e/lifecycle.spec.ts", + "e2e/operations.spec.ts", + "e2e/policies.spec.ts", + "e2e/snapshots.spec.ts", + "e2e/batch-operations.spec.ts", + "e2e/gpus.spec.ts", + "--reporter=list,json" + ], + "configFile": "D:\\DAIMA\\vdi-cloud\\console\\playwright.config.ts", + "rootDir": "D:/DAIMA/vdi-cloud/console/e2e", + "failOnFlakyTests": false, + "forbidOnly": false, + "fullyParallel": false, + "globalSetup": null, + "globalTeardown": null, + "globalTimeout": 0, + "grep": {}, + "grepInvert": null, + "maxFailures": 0, + "metadata": { + "actualWorkers": 1 + }, + "preserveOutput": "always", + "projects": [ + { + "outputDir": "D:/DAIMA/vdi-cloud/console/test-results", + "repeatEach": 1, + "retries": 0, + "metadata": { + "actualWorkers": 1 + }, + "id": "chromium", + "name": "chromium", + "testDir": "D:/DAIMA/vdi-cloud/console/e2e", + "testIgnore": [ + "**/live.spec.ts" + ], + "testMatch": [ + "**/*.@(spec|test).?(c|m)[jt]s?(x)" + ], + "timeout": 30000 + } + ], + "filteredProjects": [ + { + "grep": {}, + "grepInvert": null, + "outputDir": "D:\\DAIMA\\vdi-cloud\\console\\test-results", + "repeatEach": 1, + "retries": 0, + "metadata": { + "actualWorkers": 1 + }, + "name": "chromium", + "testDir": "D:\\DAIMA\\vdi-cloud\\console\\e2e", + "snapshotDir": "D:\\DAIMA\\vdi-cloud\\console\\e2e", + "testIgnore": "**/live.spec.ts", + "testMatch": "**/*.@(spec|test).?(c|m)[jt]s?(x)", + "timeout": 30000, + "use": { + "baseURL": "http://127.0.0.1:5183", + "trace": "retain-on-failure", + "screenshot": "only-on-failure", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/158.0.8127.18 Safari/537.36", + "viewport": { + "width": 1280, + "height": 720 + }, + "screen": { + "width": 1920, + "height": 1080 + }, + "deviceScaleFactor": 1, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium", + "channel": "msedge" + }, + "dependencies": [], + "ignoreSnapshots": false, + "__projectId": "chromium" + } + ], + "quiet": false, + "reporter": [ + [ + "list" + ], + [ + "json" + ] + ], + "reportSlowTests": { + "max": 5, + "threshold": 300000 + }, + "shard": null, + "tags": [], + "updateSnapshots": "default", + "updateSourceMethod": "patch", + "version": "1.65.0", + "workers": 1, + "webServer": null + }, + "suites": [ + { + "title": "administrators.spec.ts", + "file": "administrators.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "新增管理员校验密码确认,账号持久化字段正确", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3217, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:02:10.858Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "f106411db4d53e6f54fc-9ff20e11d6ba36d13fb4", + "file": "administrators.spec.ts", + "line": 37, + "column": 1 + }, + { + "title": "编辑本人禁止角色和停用,取消其他账号停用不提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4068, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:02:14.772Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "f106411db4d53e6f54fc-8f9e5dc1882673404c2c", + "file": "administrators.spec.ts", + "line": 60, + "column": 1 + }, + { + "title": "重置其他账号密码需要确认,取消不提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3102, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:02:18.860Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "f106411db4d53e6f54fc-62ed47448461158980c7", + "file": "administrators.spec.ts", + "line": 80, + "column": 1 + }, + { + "title": "只读人员可修改本人密码,错误原密码不退出,成功后清理登录", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2153, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:02:21.992Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "f106411db4d53e6f54fc-c111f5c502d645287604", + "file": "administrators.spec.ts", + "line": 96, + "column": 1 + } + ] + }, + { + "title": "batch-operations.spec.ts", + "file": "batch-operations.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "批量关机逐台预检,取消不提交,确认后仅显示已提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4348, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:02:24.181Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4670852b237ecdd90860-ff79dbd7596146b563c7", + "file": "batch-operations.spec.ts", + "line": 47, + "column": 1 + }, + { + "title": "卡片与列表共享选择,跨页保留选择并可清空", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2955, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:02:28.549Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4670852b237ecdd90860-b947bc7a8d75849b533f", + "file": "batch-operations.spec.ts", + "line": 58, + "column": 1 + }, + { + "title": "浅色窄屏预检与结果操作区域保持可见", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2307, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:02:31.524Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4670852b237ecdd90860-7a816c67ca4af9d813cd", + "file": "batch-operations.spec.ts", + "line": 68, + "column": 1 + }, + { + "title": "全部跳过或预检过期不能提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3684, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:02:33.850Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4670852b237ecdd90860-61d7c85b2d31ec138f30", + "file": "batch-operations.spec.ts", + "line": 74, + "column": 1 + }, + { + "title": "提交网络失败保留相同幂等标识重试", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3591, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:02:37.554Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4670852b237ecdd90860-d5c195cec979161e5088", + "file": "batch-operations.spec.ts", + "line": 80, + "column": 1 + }, + { + "title": "批量记录保留失败和跳过原因,运维可只读核对原任务", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2377, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:02:41.162Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4670852b237ecdd90860-f731b3741f4f29667611", + "file": "batch-operations.spec.ts", + "line": 85, + "column": 1 + }, + { + "title": "只读人员无批量选择与提交入口,可查看历史", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2716, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:02:43.558Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4670852b237ecdd90860-6c99db90e3c18cebb753", + "file": "batch-operations.spec.ts", + "line": 91, + "column": 1 + }, + { + "title": "任务页支持运维只读核对批量电源结果", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2805, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:02:46.294Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4670852b237ecdd90860-b85559f50ce3dae792bc", + "file": "batch-operations.spec.ts", + "line": 96, + "column": 1 + } + ] + }, + { + "title": "catalog.spec.ts", + "file": "catalog.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "模板克隆向导先预检,再提交带去重标识的任务", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4459, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:02:49.167Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "30aa42b97d8966d0c2dc-6dc25297809099af4961", + "file": "catalog.spec.ts", + "line": 37, + "column": 1 + }, + { + "title": "桌面池创建保存业务字段,窄屏与浅色主题可用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4218, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:02:53.665Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "30aa42b97d8966d0c2dc-cd42c1325adf729ee74e", + "file": "catalog.spec.ts", + "line": 58, + "column": 1 + }, + { + "title": "待初始化桌面必须勾选确认才能保存连接", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3888, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:02:57.919Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "30aa42b97d8966d0c2dc-b2369e6c29902b57e3c0", + "file": "catalog.spec.ts", + "line": 77, + "column": 1 + }, + { + "title": "空模板和接口错误显示明确原因,只读角色没有管理按钮", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2382, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:01.847Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "30aa42b97d8966d0c2dc-65bb9db30a98e42e5eb1", + "file": "catalog.spec.ts", + "line": 92, + "column": 1 + }, + { + "title": "模板读取失败提供错误反馈", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1473, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:04.251Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "30aa42b97d8966d0c2dc-7880f7837d1c1e12b68e", + "file": "catalog.spec.ts", + "line": 102, + "column": 1 + } + ] + }, + { + "title": "configuration.spec.ts", + "file": "configuration.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "接入配置不回显密钥,取消不写入,检查后保留原密钥保存", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4462, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:05.758Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-b03247368498a6e385e3", + "file": "configuration.spec.ts", + "line": 31, + "column": 1 + }, + { + "title": "接入检查失败保留输入并支持窄屏", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1949, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:10.249Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-4de3f8cdf96f8d9cfa06", + "file": "configuration.spec.ts", + "line": 49, + "column": 1 + }, + { + "title": "CPU 内存调整先预检,修改参数使预检失效,确认后提交一次", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3741, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:12.220Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-f720d3c1636dc5c09d61", + "file": "configuration.spec.ts", + "line": 63, + "column": 1 + }, + { + "title": "磁盘输入总容量并提示客体扩展,取消确认不提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4464, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:15.985Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-236d59933f17057a16f6", + "file": "configuration.spec.ts", + "line": 86, + "column": 1 + }, + { + "title": "运行中的桌面阻止修改,结果未知任务只读核对", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3733, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:20.486Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-3b49f62d60f5298ab0f9", + "file": "configuration.spec.ts", + "line": 104, + "column": 1 + }, + { + "title": "只读角色不显示接入与规格管理入口", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2139, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:24.237Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-075ac56a91937fe0a694", + "file": "configuration.spec.ts", + "line": 120, + "column": 1 + } + ] + }, + { + "title": "gpus.spec.ts", + "file": "gpus.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "只读角色可查显卡并筛选配置引用,IOMMU 0 保留", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1618, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:26.412Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "339e1869555d4df5a70f-908a22891ab6162efd96", + "file": "gpus.spec.ts", + "line": 31, + "column": 1 + }, + { + "title": "详情区分当前与待应用、待移除、模板及未纳管的映射引用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1802, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:28.053Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "339e1869555d4df5a70f-033b538c63d47607bcb8", + "file": "gpus.spec.ts", + "line": 43, + "column": 1 + }, + { + "title": "配置读取不完整显示待核查,不宣称显卡可用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1642, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:29.876Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "339e1869555d4df5a70f-d14ca93dfc0e7b05ba08", + "file": "gpus.spec.ts", + "line": 53, + "column": 1 + }, + { + "title": "PCI 权限错误提供重试,数据过期有明确提示", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1558, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:31.538Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "339e1869555d4df5a70f-52cbecb3930b42eec753", + "file": "gpus.spec.ts", + "line": 61, + "column": 1 + }, + { + "title": "浅色窄屏清单和详情可读且页面不横向溢出", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2251, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:33.116Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "339e1869555d4df5a70f-f676b76941e652a2209b", + "file": "gpus.spec.ts", + "line": 68, + "column": 1 + }, + { + "title": "空节点清单与尚未接入显示真实空态", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3386, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:35.399Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "339e1869555d4df5a70f-1db16c0f30c2de9beb2d", + "file": "gpus.spec.ts", + "line": 79, + "column": 1 + } + ] + }, + { + "title": "lifecycle.spec.ts", + "file": "lifecycle.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "终端资料保存名称和分组,深浅主题与窄屏可用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2950, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:38.838Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "1f1785dee40219527314-c0762e5dd5157bd1883b", + "file": "lifecycle.spec.ts", + "line": 41, + "column": 1 + }, + { + "title": "停用与重新激活需要确认,旧凭据撤销提示和一次性密钥可见", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4788, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:41.807Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "1f1785dee40219527314-59fd31cc1e9e633dfe77", + "file": "lifecycle.spec.ts", + "line": 62, + "column": 1 + }, + { + "title": "活动会话保护设备身份,运维和只读人员无设备维护入口", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3377, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:46.614Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "1f1785dee40219527314-a67e3aae0049fbe18ea5", + "file": "lifecycle.spec.ts", + "line": 83, + "column": 1 + }, + { + "title": "用户改名与停用保持授权,取消状态变更不提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3067, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:50.010Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "1f1785dee40219527314-4a42cb023d4d7c8fa280", + "file": "lifecycle.spec.ts", + "line": 97, + "column": 1 + }, + { + "title": "批量授权明确目标和追加撤销语义,取消不提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 5394, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:53.096Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "1f1785dee40219527314-1c3b752922c992ae7414", + "file": "lifecycle.spec.ts", + "line": 112, + "column": 1 + } + ] + }, + { + "title": "operations.spec.ts", + "file": "operations.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "CSV 预检有错误不能提交,修改后重新预检并整批导入", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2627, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:03:58.537Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4f8fd72df7d8ab107a64-29a62ceefda80d2f008a", + "file": "operations.spec.ts", + "line": 36, + "column": 1 + }, + { + "title": "批量终端激活信息支持下载,未下载关闭需要确认且密钥不残留", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3775, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:04:01.181Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4f8fd72df7d8ab107a64-e935b01f4412d0cef3b4", + "file": "operations.spec.ts", + "line": 54, + "column": 1 + }, + { + "title": "会话历史按需查询,展示心跳超时和实际连接时长", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1805, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:04:04.973Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4f8fd72df7d8ab107a64-49e23988c2dda0e07a6c", + "file": "operations.spec.ts", + "line": 69, + "column": 1 + }, + { + "title": "告警确认保留正在告警状态,规则编辑和深浅窄屏可用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3611, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:04:06.799Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4f8fd72df7d8ab107a64-5b1f20f2475609c5661c", + "file": "operations.spec.ts", + "line": 77, + "column": 1 + }, + { + "title": "只读人员可查监控及历史,无导入、确认或规则编辑入口", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2258, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:04:10.430Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4f8fd72df7d8ab107a64-2e666d3f00d39df4f6ee", + "file": "operations.spec.ts", + "line": 95, + "column": 1 + } + ] + }, + { + "title": "policies.spec.ts", + "file": "policies.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "策略版本可保存和复制,深浅主题及手机表单可用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2755, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:04:12.727Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "b074270cade4da53ff13-98ecff8013fc8188157d", + "file": "policies.spec.ts", + "line": 39, + "column": 1 + }, + { + "title": "按分组下发先确认,提交后等待设备回执,不显示已应用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3847, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:04:15.499Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "b074270cade4da53ff13-21c971eda20a592e7370", + "file": "policies.spec.ts", + "line": 60, + "column": 1 + }, + { + "title": "旧客户端分组禁止下发,失败原因和重试入口可查看", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2550, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:04:19.368Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "b074270cade4da53ff13-64b439cb8d39f3f53217", + "file": "policies.spec.ts", + "line": 78, + "column": 1 + }, + { + "title": "只读和运维角色只能查看策略与结果", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3445, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:04:21.939Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "b074270cade4da53ff13-b28e3758b602c09af5c2", + "file": "policies.spec.ts", + "line": 90, + "column": 1 + } + ] + }, + { + "title": "snapshots.spec.ts", + "file": "snapshots.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "创建快照先预检,参数改变失效,取消不写,确认提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4078, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:04:25.417Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-a03887b92dbaa2e36b31", + "file": "snapshots.spec.ts", + "line": 36, + "column": 1 + }, + { + "title": "还原需输入当前 VMID,保留磁盘范围提示,支持浅色窄屏", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3350, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:04:29.513Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-3354c2348ac9287db9af", + "file": "snapshots.spec.ts", + "line": 55, + "column": 1 + }, + { + "title": "删除叶快照先确认,内存还原和含子快照的删除禁用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3247, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:04:32.891Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-8a9e31987ab4df26a860", + "file": "snapshots.spec.ts", + "line": 75, + "column": 1 + }, + { + "title": "运行中或权限不足阻止操作", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2342, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:04:36.157Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-2413751efdb9e97a4851", + "file": "snapshots.spec.ts", + "line": 84, + "column": 1 + }, + { + "title": "只读角色仅可查看列表", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2073, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:04:38.517Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-b913b949af08f8e0d78e", + "file": "snapshots.spec.ts", + "line": 90, + "column": 1 + }, + { + "title": "PVE 权限不足与读取失败明确显示", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2330, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:04:40.608Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-6397b2bbe7c944781389", + "file": "snapshots.spec.ts", + "line": 95, + "column": 1 + }, + { + "title": "网络失败重试保留相同幂等标识", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3822, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:04:42.956Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-cb01d8a027460462177e", + "file": "snapshots.spec.ts", + "line": 102, + "column": 1 + }, + { + "title": "任务页只读核对快照结果", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2020, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T09:04:46.803Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-8f76ce7fa3598e801550", + "file": "snapshots.spec.ts", + "line": 111, + "column": 1 + } + ] + } + ], + "errors": [], + "stats": { + "startTime": "2026-11-10T09:02:09.444Z", + "duration": 160955.34, + "expected": 51, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + } +} \ No newline at end of file diff --git a/docs/evidence/cloud-lifecycle-20261110/browser-verification.json b/docs/evidence/cloud-lifecycle-20261110/browser-verification.json new file mode 100644 index 0000000..69bae3d --- /dev/null +++ b/docs/evidence/cloud-lifecycle-20261110/browser-verification.json @@ -0,0 +1,14 @@ +{ + "checkedAt": "2026-11-10T03:09:46.802Z", + "realAPI": true, + "themes": [ + "dark", + "light" + ], + "mobileWidth": 390, + "terminalAndUserEditDialogsCancelled": true, + "batchDialogCancelled": true, + "pageErrors": [], + "failedResponses": [], + "mutations": [] +} diff --git a/docs/evidence/cloud-lifecycle-20261110/fixture-batch-users-mobile.png b/docs/evidence/cloud-lifecycle-20261110/fixture-batch-users-mobile.png new file mode 100644 index 0000000..822e386 Binary files /dev/null and b/docs/evidence/cloud-lifecycle-20261110/fixture-batch-users-mobile.png differ diff --git a/docs/evidence/cloud-lifecycle-20261110/fixture-terminal-detail-light.png b/docs/evidence/cloud-lifecycle-20261110/fixture-terminal-detail-light.png new file mode 100644 index 0000000..fab6c87 Binary files /dev/null and b/docs/evidence/cloud-lifecycle-20261110/fixture-terminal-detail-light.png differ diff --git a/docs/evidence/cloud-lifecycle-20261110/fixture-terminal-detail-mobile.png b/docs/evidence/cloud-lifecycle-20261110/fixture-terminal-detail-mobile.png new file mode 100644 index 0000000..58f7d90 Binary files /dev/null and b/docs/evidence/cloud-lifecycle-20261110/fixture-terminal-detail-mobile.png differ diff --git a/docs/evidence/cloud-lifecycle-20261110/fixture-terminals-dark.png b/docs/evidence/cloud-lifecycle-20261110/fixture-terminals-dark.png new file mode 100644 index 0000000..f386465 Binary files /dev/null and b/docs/evidence/cloud-lifecycle-20261110/fixture-terminals-dark.png differ diff --git a/docs/evidence/cloud-lifecycle-20261110/fixture-terminals-light.png b/docs/evidence/cloud-lifecycle-20261110/fixture-terminals-light.png new file mode 100644 index 0000000..7631ef1 Binary files /dev/null and b/docs/evidence/cloud-lifecycle-20261110/fixture-terminals-light.png differ diff --git a/docs/evidence/cloud-lifecycle-20261110/fixture-users-dark.png b/docs/evidence/cloud-lifecycle-20261110/fixture-users-dark.png new file mode 100644 index 0000000..7bf35a7 Binary files /dev/null and b/docs/evidence/cloud-lifecycle-20261110/fixture-users-dark.png differ diff --git a/docs/evidence/cloud-lifecycle-20261110/fixture-users-light.png b/docs/evidence/cloud-lifecycle-20261110/fixture-users-light.png new file mode 100644 index 0000000..d8d1d80 Binary files /dev/null and b/docs/evidence/cloud-lifecycle-20261110/fixture-users-light.png differ diff --git a/docs/evidence/cloud-lifecycle-20261110/live-batch-users-light.png b/docs/evidence/cloud-lifecycle-20261110/live-batch-users-light.png new file mode 100644 index 0000000..fe99c27 Binary files /dev/null and b/docs/evidence/cloud-lifecycle-20261110/live-batch-users-light.png differ diff --git a/docs/evidence/cloud-lifecycle-20261110/live-batch-users-mobile.png b/docs/evidence/cloud-lifecycle-20261110/live-batch-users-mobile.png new file mode 100644 index 0000000..7c37e71 Binary files /dev/null and b/docs/evidence/cloud-lifecycle-20261110/live-batch-users-mobile.png differ diff --git a/docs/evidence/cloud-lifecycle-20261110/live-terminal-detail-light.png b/docs/evidence/cloud-lifecycle-20261110/live-terminal-detail-light.png new file mode 100644 index 0000000..aba3828 Binary files /dev/null and b/docs/evidence/cloud-lifecycle-20261110/live-terminal-detail-light.png differ diff --git a/docs/evidence/cloud-lifecycle-20261110/live-terminal-detail-mobile.png b/docs/evidence/cloud-lifecycle-20261110/live-terminal-detail-mobile.png new file mode 100644 index 0000000..c0a24b0 Binary files /dev/null and b/docs/evidence/cloud-lifecycle-20261110/live-terminal-detail-mobile.png differ diff --git a/docs/evidence/cloud-lifecycle-20261110/live-terminals-dark.png b/docs/evidence/cloud-lifecycle-20261110/live-terminals-dark.png new file mode 100644 index 0000000..dfbe0b3 Binary files /dev/null and b/docs/evidence/cloud-lifecycle-20261110/live-terminals-dark.png differ diff --git a/docs/evidence/cloud-lifecycle-20261110/live-terminals-light.png b/docs/evidence/cloud-lifecycle-20261110/live-terminals-light.png new file mode 100644 index 0000000..f1915aa Binary files /dev/null and b/docs/evidence/cloud-lifecycle-20261110/live-terminals-light.png differ diff --git a/docs/evidence/cloud-lifecycle-20261110/live-user-profile-light.png b/docs/evidence/cloud-lifecycle-20261110/live-user-profile-light.png new file mode 100644 index 0000000..30e0bb4 Binary files /dev/null and b/docs/evidence/cloud-lifecycle-20261110/live-user-profile-light.png differ diff --git a/docs/evidence/cloud-lifecycle-20261110/live-users-dark.png b/docs/evidence/cloud-lifecycle-20261110/live-users-dark.png new file mode 100644 index 0000000..42d7e06 Binary files /dev/null and b/docs/evidence/cloud-lifecycle-20261110/live-users-dark.png differ diff --git a/docs/evidence/cloud-lifecycle-20261110/live-users-light.png b/docs/evidence/cloud-lifecycle-20261110/live-users-light.png new file mode 100644 index 0000000..ce5fbba Binary files /dev/null and b/docs/evidence/cloud-lifecycle-20261110/live-users-light.png differ diff --git a/docs/evidence/cloud-lifecycle-20261110/live-verification.json b/docs/evidence/cloud-lifecycle-20261110/live-verification.json new file mode 100644 index 0000000..01293e5 --- /dev/null +++ b/docs/evidence/cloud-lifecycle-20261110/live-verification.json @@ -0,0 +1,208 @@ +{ + "checkedAt": "2026-11-10T11:09:02.738808+08:00", + "realAPI": true, + "checks": [ + { + "path": "/healthz", + "status": 200 + }, + { + "path": "/api/v1/desktops", + "status": 200 + }, + { + "path": "/api/v1/terminals", + "status": 201 + }, + { + "path": "/api/v1/terminal/register", + "status": 200 + }, + { + "path": "/api/v1/terminals", + "status": 201 + }, + { + "path": "/api/v1/terminal/register", + "status": 200 + }, + { + "path": "/api/v1/users", + "status": 201 + }, + { + "path": "/api/v1/users", + "status": 201 + }, + { + "path": "/api/v1/terminals/term_18ea2ec00829ebe8876a29e204383145", + "status": 200 + }, + { + "path": "/api/v1/terminals", + "status": 200 + }, + { + "path": "/api/v1/users/user_1ad489582c47eb1ce87e4c03fe761dc5/bindings", + "status": 200 + }, + { + "path": "/api/v1/terminal/connect-ticket", + "status": 201 + }, + { + "path": "/api/v1/users/user_1ad489582c47eb1ce87e4c03fe761dc5", + "status": 200 + }, + { + "path": "/api/v1/terminal/desktops", + "status": 200 + }, + { + "path": "/api/v1/terminal/connect-ticket", + "status": 403 + }, + { + "path": "/api/v1/users/user_1ad489582c47eb1ce87e4c03fe761dc5", + "status": 200 + }, + { + "path": "/api/v1/terminal/connect-ticket/consume", + "status": 401 + }, + { + "path": "/api/v1/users/batch-bindings", + "status": 200 + }, + { + "path": "/api/v1/terminal/desktops", + "status": 200 + }, + { + "path": "/api/v1/users/batch-bindings", + "status": 200 + }, + { + "path": "/api/v1/terminal/desktops", + "status": 200 + }, + { + "path": "/api/v1/users/user_1ad489582c47eb1ce87e4c03fe761dc5/bindings", + "status": 200 + }, + { + "path": "/api/v1/terminal/desktops", + "status": 200 + }, + { + "path": "/api/v1/terminal/desktops", + "status": 200 + }, + { + "path": "/api/v1/users/user_4761a0e23465c4c83b712d80577af2c9/bindings", + "status": 409 + }, + { + "path": "/api/v1/terminals/term_18ea2ec00829ebe8876a29e204383145/disable", + "status": 200 + }, + { + "path": "/api/v1/terminal/heartbeat", + "status": 401 + }, + { + "path": "/api/v1/terminal/config", + "status": 401 + }, + { + "path": "/api/v1/terminal/commands", + "status": 401 + }, + { + "path": "/api/v1/terminals/term_18ea2ec00829ebe8876a29e204383145/reactivate", + "status": 200 + }, + { + "path": "/api/v1/terminals/term_18ea2ec00829ebe8876a29e204383145/reactivate", + "status": 200 + }, + { + "path": "/api/v1/terminal/register", + "status": 401 + }, + { + "path": "/api/v1/terminal/register", + "status": 200 + }, + { + "path": "/api/v1/terminal/register", + "status": 401 + }, + { + "path": "/api/v1/terminal/config", + "status": 200 + }, + { + "path": "/api/v1/terminal/config", + "status": 401 + }, + { + "path": "/api/v1/terminals/term_18ea2ec00829ebe8876a29e204383145/disable", + "status": 200 + }, + { + "path": "/api/v1/terminals/term_e6d8b1bff21760de08021d8ae9b42c99/disable", + "status": 200 + }, + { + "path": "/api/v1/auth/logout", + "status": 204 + }, + { + "path": "/api/v1/terminals", + "status": 200 + }, + { + "path": "/api/v1/terminals", + "status": 200 + }, + { + "path": "/api/v1/terminals", + "status": 200 + }, + { + "path": "/api/v1/terminals", + "status": 200 + }, + { + "path": "/api/v1/terminals", + "status": 200 + }, + { + "path": "/api/v1/terminals", + "status": 200 + }, + { + "path": "/api/v1/desktops", + "status": 200 + }, + { + "path": "/api/v1/auth/logout", + "status": 204 + } + ], + "temporaryDevicesAndUsersRemoved": true, + "originalAdministratorAndPasswordUnchanged": true, + "originalDeviceCredentialsAndMetadataUnchanged": true, + "desktopSettingsUsersAndBindingsUnchanged": true, + "originalHeartbeatAdvanced": true, + "onlineTerminals": 1, + "managedVMIDs": [ + 100, + 101, + 102 + ], + "allVMsRunning": true, + "realVMReservationPerformed": false, + "realVMOrDeviceCommandsSent": false +} diff --git a/docs/evidence/cloud-lifecycle-20261110/test-verification.json b/docs/evidence/cloud-lifecycle-20261110/test-verification.json new file mode 100644 index 0000000..dd44a6c --- /dev/null +++ b/docs/evidence/cloud-lifecycle-20261110/test-verification.json @@ -0,0 +1,7 @@ +{ + "checkedAt": "2026-11-10T10:58:17.203679+08:00", + "backend": "go test ./... and go vet ./... passed with dedicated disposable PostgreSQL", + "frontend": "ESLint, TypeScript, 3 unit tests and production build passed", + "browser": "18 fixture tests passed (lifecycle, administrators, catalog, ui)", + "fixtureTestsSendLiveMutations": false +} diff --git a/docs/evidence/cloud-operations-20261110/automated-verification.json b/docs/evidence/cloud-operations-20261110/automated-verification.json new file mode 100644 index 0000000..c79d5ac --- /dev/null +++ b/docs/evidence/cloud-operations-20261110/automated-verification.json @@ -0,0 +1,7 @@ +{ + "backend": "Go全量集成测试、go vet;仅使用独立临时PostgreSQL", + "frontend": "ESLint、TypeScript、3项单元测试、生产构建", + "browserTests": 27, + "browserData": "API拦截测试数据,不代表现场业务资源", + "noRealVMPowerActions": true +} \ No newline at end of file diff --git a/docs/evidence/cloud-operations-20261110/browser-verification.json b/docs/evidence/cloud-operations-20261110/browser-verification.json new file mode 100644 index 0000000..9fbe014 --- /dev/null +++ b/docs/evidence/cloud-operations-20261110/browser-verification.json @@ -0,0 +1,16 @@ +{ + "time": "2026-11-10T06:33:01.774Z", + "themes": [ + "dark", + "light" + ], + "viewportWidths": [ + 1440, + 390 + ], + "scriptErrors": [], + "failedResponses": [], + "mutations": [], + "readOnlyInspection": true, + "screenshots": 9 +} \ No newline at end of file diff --git a/docs/evidence/cloud-operations-20261110/fixture-monitor-dark.png b/docs/evidence/cloud-operations-20261110/fixture-monitor-dark.png new file mode 100644 index 0000000..07105af Binary files /dev/null and b/docs/evidence/cloud-operations-20261110/fixture-monitor-dark.png differ diff --git a/docs/evidence/cloud-operations-20261110/fixture-monitor-light.png b/docs/evidence/cloud-operations-20261110/fixture-monitor-light.png new file mode 100644 index 0000000..73c0647 Binary files /dev/null and b/docs/evidence/cloud-operations-20261110/fixture-monitor-light.png differ diff --git a/docs/evidence/cloud-operations-20261110/fixture-monitor-mobile.png b/docs/evidence/cloud-operations-20261110/fixture-monitor-mobile.png new file mode 100644 index 0000000..605df68 Binary files /dev/null and b/docs/evidence/cloud-operations-20261110/fixture-monitor-mobile.png differ diff --git a/docs/evidence/cloud-operations-20261110/fixture-session-history.png b/docs/evidence/cloud-operations-20261110/fixture-session-history.png new file mode 100644 index 0000000..e3537c6 Binary files /dev/null and b/docs/evidence/cloud-operations-20261110/fixture-session-history.png differ diff --git a/docs/evidence/cloud-operations-20261110/live-history-dark.png b/docs/evidence/cloud-operations-20261110/live-history-dark.png new file mode 100644 index 0000000..84c006d Binary files /dev/null and b/docs/evidence/cloud-operations-20261110/live-history-dark.png differ diff --git a/docs/evidence/cloud-operations-20261110/live-history-light.png b/docs/evidence/cloud-operations-20261110/live-history-light.png new file mode 100644 index 0000000..2e6471f Binary files /dev/null and b/docs/evidence/cloud-operations-20261110/live-history-light.png differ diff --git a/docs/evidence/cloud-operations-20261110/live-import-users-light.png b/docs/evidence/cloud-operations-20261110/live-import-users-light.png new file mode 100644 index 0000000..b3e9262 Binary files /dev/null and b/docs/evidence/cloud-operations-20261110/live-import-users-light.png differ diff --git a/docs/evidence/cloud-operations-20261110/live-import-users-mobile.png b/docs/evidence/cloud-operations-20261110/live-import-users-mobile.png new file mode 100644 index 0000000..133c9e2 Binary files /dev/null and b/docs/evidence/cloud-operations-20261110/live-import-users-mobile.png differ diff --git a/docs/evidence/cloud-operations-20261110/live-monitor-dark.png b/docs/evidence/cloud-operations-20261110/live-monitor-dark.png new file mode 100644 index 0000000..a13c833 Binary files /dev/null and b/docs/evidence/cloud-operations-20261110/live-monitor-dark.png differ diff --git a/docs/evidence/cloud-operations-20261110/live-monitor-light.png b/docs/evidence/cloud-operations-20261110/live-monitor-light.png new file mode 100644 index 0000000..90795a3 Binary files /dev/null and b/docs/evidence/cloud-operations-20261110/live-monitor-light.png differ diff --git a/docs/evidence/cloud-operations-20261110/live-monitor-mobile.png b/docs/evidence/cloud-operations-20261110/live-monitor-mobile.png new file mode 100644 index 0000000..3f46428 Binary files /dev/null and b/docs/evidence/cloud-operations-20261110/live-monitor-mobile.png differ diff --git a/docs/evidence/cloud-operations-20261110/live-rules-dark.png b/docs/evidence/cloud-operations-20261110/live-rules-dark.png new file mode 100644 index 0000000..b8c5969 Binary files /dev/null and b/docs/evidence/cloud-operations-20261110/live-rules-dark.png differ diff --git a/docs/evidence/cloud-operations-20261110/live-rules-light.png b/docs/evidence/cloud-operations-20261110/live-rules-light.png new file mode 100644 index 0000000..2fd1398 Binary files /dev/null and b/docs/evidence/cloud-operations-20261110/live-rules-light.png differ diff --git a/docs/evidence/cloud-operations-20261110/live-verification.json b/docs/evidence/cloud-operations-20261110/live-verification.json new file mode 100644 index 0000000..30e9457 --- /dev/null +++ b/docs/evidence/cloud-operations-20261110/live-verification.json @@ -0,0 +1,56 @@ +{ + "time": "2026-11-10T06:30:41.520567+00:00", + "mode": "production", + "streamingTest": false, + "vmPowerOrCloneActions": 0, + "csv": { + "userCreated": true, + "terminalCreated": true, + "bindingCreated": true, + "idempotencyVerified": true, + "secretsStoredInBatch": false + }, + "sessionHistory": { + "realApiAndDatabase": true, + "syntheticDeviceEvents": true, + "reservationAndReadySeparated": true, + "readyTimeStable": true, + "endRecorded": true, + "realVideoStreamTested": false + }, + "resourceSeries": { + "points": 2, + "retentionDays": 30, + "realPVESamples": true + }, + "alerts": { + "temporaryTerminalLastSeenAdjustedForTest": true, + "automaticTrigger": true, + "acknowledged": true, + "automaticRecoveryFromHeartbeat": true, + "defaultRulesUnchanged": true + }, + "temporaryDataCleaned": true, + "originalBusinessSnapshotUnchanged": true, + "vmPowerStates": [ + { + "vmid": 100, + "powerState": "running" + }, + { + "vmid": 101, + "powerState": "running" + }, + { + "vmid": 102, + "powerState": "running" + } + ], + "originalTerminalHeartbeat": [ + { + "name": "RK3528 联调终端", + "lastSeen": "2026-11-10T06:30:57.986658+00:00", + "recent": true + } + ] +} \ No newline at end of file diff --git a/docs/evidence/cloud-policies-20261110/browser-verification.json b/docs/evidence/cloud-policies-20261110/browser-verification.json new file mode 100644 index 0000000..3f60c88 --- /dev/null +++ b/docs/evidence/cloud-policies-20261110/browser-verification.json @@ -0,0 +1,16 @@ +{ + "time": "2026-11-10T03:58:20.165Z", + "themes": [ + "dark", + "light" + ], + "viewportWidths": [ + 1440, + 390 + ], + "scriptErrors": [], + "failedResponses": [], + "mutations": [], + "readOnlyInspection": true, + "screenshots": 8 +} \ No newline at end of file diff --git a/docs/evidence/cloud-policies-20261110/fixture-policies-dark.png b/docs/evidence/cloud-policies-20261110/fixture-policies-dark.png new file mode 100644 index 0000000..45e36c8 Binary files /dev/null and b/docs/evidence/cloud-policies-20261110/fixture-policies-dark.png differ diff --git a/docs/evidence/cloud-policies-20261110/fixture-policies-light.png b/docs/evidence/cloud-policies-20261110/fixture-policies-light.png new file mode 100644 index 0000000..47e09d2 Binary files /dev/null and b/docs/evidence/cloud-policies-20261110/fixture-policies-light.png differ diff --git a/docs/evidence/cloud-policies-20261110/fixture-policy-create-mobile.png b/docs/evidence/cloud-policies-20261110/fixture-policy-create-mobile.png new file mode 100644 index 0000000..ccf220a Binary files /dev/null and b/docs/evidence/cloud-policies-20261110/fixture-policy-create-mobile.png differ diff --git a/docs/evidence/cloud-policies-20261110/fixture-policy-results-dark.png b/docs/evidence/cloud-policies-20261110/fixture-policy-results-dark.png new file mode 100644 index 0000000..e6c6773 Binary files /dev/null and b/docs/evidence/cloud-policies-20261110/fixture-policy-results-dark.png differ diff --git a/docs/evidence/cloud-policies-20261110/live-create-mobile.png b/docs/evidence/cloud-policies-20261110/live-create-mobile.png new file mode 100644 index 0000000..033fab3 Binary files /dev/null and b/docs/evidence/cloud-policies-20261110/live-create-mobile.png differ diff --git a/docs/evidence/cloud-policies-20261110/live-dispatch-light.png b/docs/evidence/cloud-policies-20261110/live-dispatch-light.png new file mode 100644 index 0000000..50bca52 Binary files /dev/null and b/docs/evidence/cloud-policies-20261110/live-dispatch-light.png differ diff --git a/docs/evidence/cloud-policies-20261110/live-result-dark.png b/docs/evidence/cloud-policies-20261110/live-result-dark.png new file mode 100644 index 0000000..fadc760 Binary files /dev/null and b/docs/evidence/cloud-policies-20261110/live-result-dark.png differ diff --git a/docs/evidence/cloud-policies-20261110/live-result-light.png b/docs/evidence/cloud-policies-20261110/live-result-light.png new file mode 100644 index 0000000..f277b13 Binary files /dev/null and b/docs/evidence/cloud-policies-20261110/live-result-light.png differ diff --git a/docs/evidence/cloud-policies-20261110/live-states-dark.png b/docs/evidence/cloud-policies-20261110/live-states-dark.png new file mode 100644 index 0000000..e750c28 Binary files /dev/null and b/docs/evidence/cloud-policies-20261110/live-states-dark.png differ diff --git a/docs/evidence/cloud-policies-20261110/live-states-light.png b/docs/evidence/cloud-policies-20261110/live-states-light.png new file mode 100644 index 0000000..fd2eeb3 Binary files /dev/null and b/docs/evidence/cloud-policies-20261110/live-states-light.png differ diff --git a/docs/evidence/cloud-policies-20261110/live-templates-dark.png b/docs/evidence/cloud-policies-20261110/live-templates-dark.png new file mode 100644 index 0000000..f87bc6f Binary files /dev/null and b/docs/evidence/cloud-policies-20261110/live-templates-dark.png differ diff --git a/docs/evidence/cloud-policies-20261110/live-templates-light.png b/docs/evidence/cloud-policies-20261110/live-templates-light.png new file mode 100644 index 0000000..7f33ed2 Binary files /dev/null and b/docs/evidence/cloud-policies-20261110/live-templates-light.png differ diff --git a/docs/evidence/cloud-policies-20261110/live-verification.json b/docs/evidence/cloud-policies-20261110/live-verification.json new file mode 100644 index 0000000..f6f7d71 --- /dev/null +++ b/docs/evidence/cloud-policies-20261110/live-verification.json @@ -0,0 +1,35 @@ +{ + "time": "2026-11-10T03:56:28.928545+00:00", + "mode": "production", + "agentVersion": "qt-platform/0.3.0", + "realDeviceAppliedChangedPolicy": true, + "realDeviceRestoredDefaultIntervals": true, + "policyFilePermissions": "0600", + "idempotencyVerified": true, + "historicalSuccessPreserved": true, + "businessSnapshotUnchanged": true, + "heartbeatAdvanced": true, + "runningDesktopCount": 3, + "activeSessions": 0, + "dispatches": [ + { + "id": "dispatch_247e8e7efc3173fc679eadfce888e1a6", + "templateVersion": "1.1", + "configVersion": 2, + "status": "applied", + "acknowledgedAt": "2026-11-10T03:55:41.32034Z" + }, + { + "id": "dispatch_048a20603f3aaf026319e34bc8e68a0d", + "templateVersion": "1.0", + "configVersion": 3, + "status": "applied", + "acknowledgedAt": "2026-11-10T03:55:56.3569Z" + } + ], + "finalIntervals": { + "heartbeat": 30, + "commands": 3, + "desktopList": 15 + } +} \ No newline at end of file diff --git a/docs/evidence/cloud-policies-20261110/test-verification.json b/docs/evidence/cloud-policies-20261110/test-verification.json new file mode 100644 index 0000000..7fd0d74 --- /dev/null +++ b/docs/evidence/cloud-policies-20261110/test-verification.json @@ -0,0 +1,27 @@ +{ + "time": "2026-11-10T03:55:02.010477+00:00", + "database": "isolated disposable PostgreSQL 16 on 127.0.0.1:55439, not live", + "goTestAll": "passed", + "goVetAll": "passed", + "eslint": "passed", + "typecheck": "passed as part of production build", + "frontendUnitTests": 3, + "productionBuild": "passed locally and in Docker", + "uniqueBrowserTestsPassed": 22, + "browserCoverage": [ + "administrators", + "catalog", + "lifecycle", + "policies", + "ui" + ], + "browserNote": "18 existing tests passed in the combined run; policy tests were repaired to target the Element Plus select wrapper and all 4 policy tests then passed. No fixture request reached the live API.", + "arm64CTestPassed": 5, + "arm64CTest": [ + "terminal-policy", + "display-guard", + "moonlight-session", + "desktop-list-page", + "main-window-boot" + ] +} \ No newline at end of file diff --git a/docs/evidence/cloud-snapshots-20261110/browser-verification.json b/docs/evidence/cloud-snapshots-20261110/browser-verification.json new file mode 100644 index 0000000..085132f --- /dev/null +++ b/docs/evidence/cloud-snapshots-20261110/browser-verification.json @@ -0,0 +1,16 @@ +{ + "time": "2026-11-10T08:04:28.609Z", + "themes": [ + "dark", + "light" + ], + "viewportWidths": [ + 1440, + 390 + ], + "scriptErrors": [], + "failedResponses": [], + "mutations": [], + "readOnlyInspection": true, + "screenshots": 3 +} \ No newline at end of file diff --git a/docs/evidence/cloud-snapshots-20261110/final-check.json b/docs/evidence/cloud-snapshots-20261110/final-check.json new file mode 100644 index 0000000..93f4f1b --- /dev/null +++ b/docs/evidence/cloud-snapshots-20261110/final-check.json @@ -0,0 +1,11 @@ +{ + "time": "2026-11-10T08:06:47.614035+00:00", + "isolatedTestDatabaseRemoved": true, + "site": { + "vmsRunning": 3, + "terminalHeartbeatAge": 18.280398, + "snapshotJobs": 0 + }, + "businessSnapshotUnchanged": true, + "liveContainersPreserved": true +} \ No newline at end of file diff --git a/docs/evidence/cloud-snapshots-20261110/fixture-snapshot-create-dark.png b/docs/evidence/cloud-snapshots-20261110/fixture-snapshot-create-dark.png new file mode 100644 index 0000000..e6b19c2 Binary files /dev/null and b/docs/evidence/cloud-snapshots-20261110/fixture-snapshot-create-dark.png differ diff --git a/docs/evidence/cloud-snapshots-20261110/fixture-snapshot-restore-light-mobile.png b/docs/evidence/cloud-snapshots-20261110/fixture-snapshot-restore-light-mobile.png new file mode 100644 index 0000000..b25026c Binary files /dev/null and b/docs/evidence/cloud-snapshots-20261110/fixture-snapshot-restore-light-mobile.png differ diff --git a/docs/evidence/cloud-snapshots-20261110/live-snapshots-dark.png b/docs/evidence/cloud-snapshots-20261110/live-snapshots-dark.png new file mode 100644 index 0000000..cebdaaf Binary files /dev/null and b/docs/evidence/cloud-snapshots-20261110/live-snapshots-dark.png differ diff --git a/docs/evidence/cloud-snapshots-20261110/live-snapshots-light.png b/docs/evidence/cloud-snapshots-20261110/live-snapshots-light.png new file mode 100644 index 0000000..2c8c012 Binary files /dev/null and b/docs/evidence/cloud-snapshots-20261110/live-snapshots-light.png differ diff --git a/docs/evidence/cloud-snapshots-20261110/live-snapshots-mobile.png b/docs/evidence/cloud-snapshots-20261110/live-snapshots-mobile.png new file mode 100644 index 0000000..8686a73 Binary files /dev/null and b/docs/evidence/cloud-snapshots-20261110/live-snapshots-mobile.png differ diff --git a/docs/evidence/cloud-snapshots-20261110/live-verification.json b/docs/evidence/cloud-snapshots-20261110/live-verification.json new file mode 100644 index 0000000..6deec0f --- /dev/null +++ b/docs/evidence/cloud-snapshots-20261110/live-verification.json @@ -0,0 +1,110 @@ +{ + "time": "2026-11-10T08:03:59.582300+00:00", + "vmPowerActions": 0, + "vmSnapshotWrites": 0, + "vmHardwareWrites": 0, + "cloneRequests": 0, + "validationScope": "read-only live inspection; writes tested against local HTTP fixture and isolated PostgreSQL", + "businessSnapshotUnchanged": true, + "snapshots": [ + { + "vmid": 102, + "powerState": "running", + "snapshotCount": 0, + "disks": [ + "scsi0" + ], + "blockers": [ + "请先正常关闭桌面,再管理快照" + ], + "canCreate": false, + "canDelete": false, + "canRestore": false, + "runningPreflightRejected": true + }, + { + "vmid": 100, + "powerState": "running", + "snapshotCount": 0, + "disks": [], + "blockers": [ + "请先正常关闭桌面,再管理快照", + "包含直通设备、未使用磁盘或宿主自定义配置,请在 PVE 人工管理快照" + ], + "canCreate": false, + "canDelete": false, + "canRestore": false, + "runningPreflightRejected": true + }, + { + "vmid": 101, + "powerState": "running", + "snapshotCount": 0, + "disks": [ + "scsi0" + ], + "blockers": [ + "请先正常关闭桌面,再管理快照" + ], + "canCreate": false, + "canDelete": false, + "canRestore": false, + "runningPreflightRejected": true + } + ], + "permissionsAndStorageFeature": [ + { + "vmid": 102, + "VM.Snapshot": false, + "VM.Snapshot.Rollback": false, + "pveSnapshotFeature": true + }, + { + "vmid": 100, + "VM.Snapshot": false, + "VM.Snapshot.Rollback": false, + "pveSnapshotFeature": false + }, + { + "vmid": 101, + "VM.Snapshot": false, + "VM.Snapshot.Rollback": false, + "pveSnapshotFeature": true + } + ], + "site": { + "terminals": [ + { + "sn": "ee621cae7199a2e9", + "agentVersion": "qt-platform/0.3.0", + "heartbeatAgeSeconds": 1.568618, + "session": null + } + ], + "desktops": [ + { + "vmid": 100, + "powerState": "running" + }, + { + "vmid": 101, + "powerState": "running" + }, + { + "vmid": 102, + "powerState": "running" + } + ], + "snapshotJobs": 0 + }, + "deployment": { + "tag": "before-snapshots-20261110-155834", + "backup": ".local\\live\\backups\\before-snapshots-20261110-155834.dump", + "bytes": 74201, + "baselineTime": "2026-11-10T07:58:35.990995+00:00", + "backupArchiveReadable": true, + "apiImage": "sha256:094d38e803437d3a34041e1af36530e7f14445cd37959b6d7316dfadc3f8b062", + "consoleImage": "sha256:3b3af4559890cbe7629abd0d029f83c59f6afc23e274b5db07ba64175ae15df0", + "containersUpdated": true + } +} \ No newline at end of file diff --git a/docs/evidence/cloud-snapshots-20261110/playwright-report.json b/docs/evidence/cloud-snapshots-20261110/playwright-report.json new file mode 100644 index 0000000..e5b5693 --- /dev/null +++ b/docs/evidence/cloud-snapshots-20261110/playwright-report.json @@ -0,0 +1,1437 @@ +{ + "config": { + "argv": [ + "D:\\Nodejs\\node.exe", + "D:\\DAIMA\\vdi-cloud\\console\\node_modules\\@playwright\\test\\cli.js", + "test", + "e2e/administrators.spec.ts", + "e2e/catalog.spec.ts", + "e2e/configuration.spec.ts", + "e2e/lifecycle.spec.ts", + "e2e/operations.spec.ts", + "e2e/policies.spec.ts", + "e2e/snapshots.spec.ts", + "--reporter=list,json" + ], + "configFile": "D:\\DAIMA\\vdi-cloud\\console\\playwright.config.ts", + "rootDir": "D:/DAIMA/vdi-cloud/console/e2e", + "failOnFlakyTests": false, + "forbidOnly": false, + "fullyParallel": false, + "globalSetup": null, + "globalTeardown": null, + "globalTimeout": 0, + "grep": {}, + "grepInvert": null, + "maxFailures": 0, + "metadata": { + "actualWorkers": 1 + }, + "preserveOutput": "always", + "projects": [ + { + "outputDir": "D:/DAIMA/vdi-cloud/console/test-results", + "repeatEach": 1, + "retries": 0, + "metadata": { + "actualWorkers": 1 + }, + "id": "chromium", + "name": "chromium", + "testDir": "D:/DAIMA/vdi-cloud/console/e2e", + "testIgnore": [ + "**/live.spec.ts" + ], + "testMatch": [ + "**/*.@(spec|test).?(c|m)[jt]s?(x)" + ], + "timeout": 30000 + } + ], + "filteredProjects": [ + { + "grep": {}, + "grepInvert": null, + "outputDir": "D:\\DAIMA\\vdi-cloud\\console\\test-results", + "repeatEach": 1, + "retries": 0, + "metadata": { + "actualWorkers": 1 + }, + "name": "chromium", + "testDir": "D:\\DAIMA\\vdi-cloud\\console\\e2e", + "snapshotDir": "D:\\DAIMA\\vdi-cloud\\console\\e2e", + "testIgnore": "**/live.spec.ts", + "testMatch": "**/*.@(spec|test).?(c|m)[jt]s?(x)", + "timeout": 30000, + "use": { + "baseURL": "http://127.0.0.1:5183", + "trace": "retain-on-failure", + "screenshot": "only-on-failure", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/158.0.8127.18 Safari/537.36", + "viewport": { + "width": 1280, + "height": 720 + }, + "screen": { + "width": 1920, + "height": 1080 + }, + "deviceScaleFactor": 1, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium", + "channel": "msedge" + }, + "dependencies": [], + "ignoreSnapshots": false, + "__projectId": "chromium" + } + ], + "quiet": false, + "reporter": [ + [ + "list" + ], + [ + "json" + ] + ], + "reportSlowTests": { + "max": 5, + "threshold": 300000 + }, + "shard": null, + "tags": [], + "updateSnapshots": "default", + "updateSourceMethod": "patch", + "version": "1.65.0", + "workers": 1, + "webServer": null + }, + "suites": [ + { + "title": "administrators.spec.ts", + "file": "administrators.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "新增管理员校验密码确认,账号持久化字段正确", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3224, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:58:28.097Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "f106411db4d53e6f54fc-9ff20e11d6ba36d13fb4", + "file": "administrators.spec.ts", + "line": 37, + "column": 1 + }, + { + "title": "编辑本人禁止角色和停用,取消其他账号停用不提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4125, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:58:32.029Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "f106411db4d53e6f54fc-8f9e5dc1882673404c2c", + "file": "administrators.spec.ts", + "line": 60, + "column": 1 + }, + { + "title": "重置其他账号密码需要确认,取消不提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3045, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:58:36.177Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "f106411db4d53e6f54fc-62ed47448461158980c7", + "file": "administrators.spec.ts", + "line": 80, + "column": 1 + }, + { + "title": "只读人员可修改本人密码,错误原密码不退出,成功后清理登录", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2007, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:58:39.246Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "f106411db4d53e6f54fc-c111f5c502d645287604", + "file": "administrators.spec.ts", + "line": 96, + "column": 1 + } + ] + }, + { + "title": "catalog.spec.ts", + "file": "catalog.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "模板克隆向导先预检,再提交带去重标识的任务", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2812, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:58:41.290Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "30aa42b97d8966d0c2dc-6dc25297809099af4961", + "file": "catalog.spec.ts", + "line": 37, + "column": 1 + }, + { + "title": "桌面池创建保存业务字段,窄屏与浅色主题可用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3270, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:58:44.121Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "30aa42b97d8966d0c2dc-cd42c1325adf729ee74e", + "file": "catalog.spec.ts", + "line": 58, + "column": 1 + }, + { + "title": "待初始化桌面必须勾选确认才能保存连接", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3121, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:58:47.411Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "30aa42b97d8966d0c2dc-b2369e6c29902b57e3c0", + "file": "catalog.spec.ts", + "line": 77, + "column": 1 + }, + { + "title": "空模板和接口错误显示明确原因,只读角色没有管理按钮", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2009, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:58:50.553Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "30aa42b97d8966d0c2dc-65bb9db30a98e42e5eb1", + "file": "catalog.spec.ts", + "line": 92, + "column": 1 + }, + { + "title": "模板读取失败提供错误反馈", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1396, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:58:52.582Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "30aa42b97d8966d0c2dc-7880f7837d1c1e12b68e", + "file": "catalog.spec.ts", + "line": 102, + "column": 1 + } + ] + }, + { + "title": "configuration.spec.ts", + "file": "configuration.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "接入配置不回显密钥,取消不写入,检查后保留原密钥保存", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3068, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:58:54.025Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-b03247368498a6e385e3", + "file": "configuration.spec.ts", + "line": 31, + "column": 1 + }, + { + "title": "接入检查失败保留输入并支持窄屏", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1746, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:58:57.112Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-4de3f8cdf96f8d9cfa06", + "file": "configuration.spec.ts", + "line": 49, + "column": 1 + }, + { + "title": "CPU 内存调整先预检,修改参数使预检失效,确认后提交一次", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3326, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:58:58.879Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-f720d3c1636dc5c09d61", + "file": "configuration.spec.ts", + "line": 63, + "column": 1 + }, + { + "title": "磁盘输入总容量并提示客体扩展,取消确认不提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3200, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:02.223Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-236d59933f17057a16f6", + "file": "configuration.spec.ts", + "line": 86, + "column": 1 + }, + { + "title": "运行中的桌面阻止修改,结果未知任务只读核对", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3535, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:05.440Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-3b49f62d60f5298ab0f9", + "file": "configuration.spec.ts", + "line": 104, + "column": 1 + }, + { + "title": "只读角色不显示接入与规格管理入口", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2099, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:08.993Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "c9271d8ec6470e516d84-075ac56a91937fe0a694", + "file": "configuration.spec.ts", + "line": 120, + "column": 1 + } + ] + }, + { + "title": "lifecycle.spec.ts", + "file": "lifecycle.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "终端资料保存名称和分组,深浅主题与窄屏可用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2848, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:11.140Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "1f1785dee40219527314-c0762e5dd5157bd1883b", + "file": "lifecycle.spec.ts", + "line": 41, + "column": 1 + }, + { + "title": "停用与重新激活需要确认,旧凭据撤销提示和一次性密钥可见", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4836, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:14.010Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "1f1785dee40219527314-59fd31cc1e9e633dfe77", + "file": "lifecycle.spec.ts", + "line": 62, + "column": 1 + }, + { + "title": "活动会话保护设备身份,运维和只读人员无设备维护入口", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2848, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:18.870Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "1f1785dee40219527314-a67e3aae0049fbe18ea5", + "file": "lifecycle.spec.ts", + "line": 83, + "column": 1 + }, + { + "title": "用户改名与停用保持授权,取消状态变更不提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3044, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:21.737Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "1f1785dee40219527314-4a42cb023d4d7c8fa280", + "file": "lifecycle.spec.ts", + "line": 97, + "column": 1 + }, + { + "title": "批量授权明确目标和追加撤销语义,取消不提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 5437, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:24.803Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "1f1785dee40219527314-1c3b752922c992ae7414", + "file": "lifecycle.spec.ts", + "line": 112, + "column": 1 + } + ] + }, + { + "title": "operations.spec.ts", + "file": "operations.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "CSV 预检有错误不能提交,修改后重新预检并整批导入", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2842, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:30.282Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4f8fd72df7d8ab107a64-29a62ceefda80d2f008a", + "file": "operations.spec.ts", + "line": 36, + "column": 1 + }, + { + "title": "批量终端激活信息支持下载,未下载关闭需要确认且密钥不残留", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3876, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:33.145Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4f8fd72df7d8ab107a64-e935b01f4412d0cef3b4", + "file": "operations.spec.ts", + "line": 54, + "column": 1 + }, + { + "title": "会话历史按需查询,展示心跳超时和实际连接时长", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2219, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:37.045Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4f8fd72df7d8ab107a64-49e23988c2dda0e07a6c", + "file": "operations.spec.ts", + "line": 69, + "column": 1 + }, + { + "title": "告警确认保留正在告警状态,规则编辑和深浅窄屏可用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3305, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:39.284Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4f8fd72df7d8ab107a64-5b1f20f2475609c5661c", + "file": "operations.spec.ts", + "line": 77, + "column": 1 + }, + { + "title": "只读人员可查监控及历史,无导入、确认或规则编辑入口", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2308, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:42.607Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "4f8fd72df7d8ab107a64-2e666d3f00d39df4f6ee", + "file": "operations.spec.ts", + "line": 95, + "column": 1 + } + ] + }, + { + "title": "policies.spec.ts", + "file": "policies.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "策略版本可保存和复制,深浅主题及手机表单可用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2867, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:44.958Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "b074270cade4da53ff13-98ecff8013fc8188157d", + "file": "policies.spec.ts", + "line": 39, + "column": 1 + }, + { + "title": "按分组下发先确认,提交后等待设备回执,不显示已应用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3719, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:47.844Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "b074270cade4da53ff13-21c971eda20a592e7370", + "file": "policies.spec.ts", + "line": 60, + "column": 1 + }, + { + "title": "旧客户端分组禁止下发,失败原因和重试入口可查看", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2711, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:51.586Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "b074270cade4da53ff13-64b439cb8d39f3f53217", + "file": "policies.spec.ts", + "line": 78, + "column": 1 + }, + { + "title": "只读和运维角色只能查看策略与结果", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3363, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:54.314Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "b074270cade4da53ff13-b28e3758b602c09af5c2", + "file": "policies.spec.ts", + "line": 90, + "column": 1 + } + ] + }, + { + "title": "snapshots.spec.ts", + "file": "snapshots.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "创建快照先预检,参数改变失效,取消不写,确认提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4068, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T07:59:57.712Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-a03887b92dbaa2e36b31", + "file": "snapshots.spec.ts", + "line": 36, + "column": 1 + }, + { + "title": "还原需输入当前 VMID,保留磁盘范围提示,支持浅色窄屏", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4469, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:00:01.798Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-3354c2348ac9287db9af", + "file": "snapshots.spec.ts", + "line": 55, + "column": 1 + }, + { + "title": "删除叶快照先确认,内存还原和含子快照的删除禁用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4290, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:00:06.292Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-8a9e31987ab4df26a860", + "file": "snapshots.spec.ts", + "line": 73, + "column": 1 + }, + { + "title": "运行中或权限不足阻止操作", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2585, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:00:10.615Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-2413751efdb9e97a4851", + "file": "snapshots.spec.ts", + "line": 82, + "column": 1 + }, + { + "title": "只读角色仅可查看列表", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2621, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:00:13.233Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-b913b949af08f8e0d78e", + "file": "snapshots.spec.ts", + "line": 88, + "column": 1 + }, + { + "title": "PVE 权限不足与读取失败明确显示", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2609, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:00:15.888Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-6397b2bbe7c944781389", + "file": "snapshots.spec.ts", + "line": 93, + "column": 1 + }, + { + "title": "网络失败重试保留相同幂等标识", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3722, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:00:18.522Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-cb01d8a027460462177e", + "file": "snapshots.spec.ts", + "line": 100, + "column": 1 + }, + { + "title": "任务页只读核对快照结果", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1877, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:00:22.262Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-8f76ce7fa3598e801550", + "file": "snapshots.spec.ts", + "line": 109, + "column": 1 + } + ] + } + ], + "errors": [], + "stats": { + "startTime": "2026-11-10T07:58:26.780Z", + "duration": 119115.903, + "expected": 37, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + } +} \ No newline at end of file diff --git a/docs/evidence/cloud-snapshots-20261110/snapshot-layout-report.json b/docs/evidence/cloud-snapshots-20261110/snapshot-layout-report.json new file mode 100644 index 0000000..ffeb007 --- /dev/null +++ b/docs/evidence/cloud-snapshots-20261110/snapshot-layout-report.json @@ -0,0 +1,397 @@ +{ + "config": { + "argv": [ + "D:\\Nodejs\\node.exe", + "D:\\DAIMA\\vdi-cloud\\console\\node_modules\\@playwright\\test\\cli.js", + "test", + "e2e/snapshots.spec.ts", + "--reporter=list,json" + ], + "configFile": "D:\\DAIMA\\vdi-cloud\\console\\playwright.config.ts", + "rootDir": "D:/DAIMA/vdi-cloud/console/e2e", + "failOnFlakyTests": false, + "forbidOnly": false, + "fullyParallel": false, + "globalSetup": null, + "globalTeardown": null, + "globalTimeout": 0, + "grep": {}, + "grepInvert": null, + "maxFailures": 0, + "metadata": { + "actualWorkers": 1 + }, + "preserveOutput": "always", + "projects": [ + { + "outputDir": "D:/DAIMA/vdi-cloud/console/test-results", + "repeatEach": 1, + "retries": 0, + "metadata": { + "actualWorkers": 1 + }, + "id": "chromium", + "name": "chromium", + "testDir": "D:/DAIMA/vdi-cloud/console/e2e", + "testIgnore": [ + "**/live.spec.ts" + ], + "testMatch": [ + "**/*.@(spec|test).?(c|m)[jt]s?(x)" + ], + "timeout": 30000 + } + ], + "filteredProjects": [ + { + "grep": {}, + "grepInvert": null, + "outputDir": "D:\\DAIMA\\vdi-cloud\\console\\test-results", + "repeatEach": 1, + "retries": 0, + "metadata": { + "actualWorkers": 1 + }, + "name": "chromium", + "testDir": "D:\\DAIMA\\vdi-cloud\\console\\e2e", + "snapshotDir": "D:\\DAIMA\\vdi-cloud\\console\\e2e", + "testIgnore": "**/live.spec.ts", + "testMatch": "**/*.@(spec|test).?(c|m)[jt]s?(x)", + "timeout": 30000, + "use": { + "baseURL": "http://127.0.0.1:5183", + "trace": "retain-on-failure", + "screenshot": "only-on-failure", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/158.0.8127.18 Safari/537.36", + "viewport": { + "width": 1280, + "height": 720 + }, + "screen": { + "width": 1920, + "height": 1080 + }, + "deviceScaleFactor": 1, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium", + "channel": "msedge" + }, + "dependencies": [], + "ignoreSnapshots": false, + "__projectId": "chromium" + } + ], + "quiet": false, + "reporter": [ + [ + "list" + ], + [ + "json" + ] + ], + "reportSlowTests": { + "max": 5, + "threshold": 300000 + }, + "shard": null, + "tags": [], + "updateSnapshots": "default", + "updateSourceMethod": "patch", + "version": "1.65.0", + "workers": 1, + "webServer": null + }, + "suites": [ + { + "title": "snapshots.spec.ts", + "file": "snapshots.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "创建快照先预检,参数改变失效,取消不写,确认提交", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4773, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:02:08.204Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-a03887b92dbaa2e36b31", + "file": "snapshots.spec.ts", + "line": 36, + "column": 1 + }, + { + "title": "还原需输入当前 VMID,保留磁盘范围提示,支持浅色窄屏", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3674, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:02:13.668Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-3354c2348ac9287db9af", + "file": "snapshots.spec.ts", + "line": 55, + "column": 1 + }, + { + "title": "删除叶快照先确认,内存还原和含子快照的删除禁用", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3213, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:02:17.378Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-8a9e31987ab4df26a860", + "file": "snapshots.spec.ts", + "line": 75, + "column": 1 + }, + { + "title": "运行中或权限不足阻止操作", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2295, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:02:20.610Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-2413751efdb9e97a4851", + "file": "snapshots.spec.ts", + "line": 84, + "column": 1 + }, + { + "title": "只读角色仅可查看列表", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2104, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:02:22.925Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-b913b949af08f8e0d78e", + "file": "snapshots.spec.ts", + "line": 90, + "column": 1 + }, + { + "title": "PVE 权限不足与读取失败明确显示", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2303, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:02:25.046Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-6397b2bbe7c944781389", + "file": "snapshots.spec.ts", + "line": 95, + "column": 1 + }, + { + "title": "网络失败重试保留相同幂等标识", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3721, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:02:27.367Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-cb01d8a027460462177e", + "file": "snapshots.spec.ts", + "line": 102, + "column": 1 + }, + { + "title": "任务页只读核对快照结果", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1828, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2026-11-10T08:02:31.108Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "95206587925d7902ff45-8f76ce7fa3598e801550", + "file": "snapshots.spec.ts", + "line": 111, + "column": 1 + } + ] + } + ], + "errors": [], + "stats": { + "startTime": "2026-11-10T08:02:06.881Z", + "duration": 26849.727, + "expected": 8, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + } +} \ No newline at end of file diff --git a/docs/evidence/cloud-snapshots-20261110/test-verification.json b/docs/evidence/cloud-snapshots-20261110/test-verification.json new file mode 100644 index 0000000..80c4746 --- /dev/null +++ b/docs/evidence/cloud-snapshots-20261110/test-verification.json @@ -0,0 +1,53 @@ +{ + "time": "2026-11-10T08:04:30.555417+00:00", + "goFullSuite": { + "passed": true, + "command": "go test ./... -count=1", + "database": "isolated vdi_snapshot_test on 127.0.0.1:55439", + "serverSeconds": 49.246 + }, + "goVet": { + "passed": true, + "command": "go vet ./..." + }, + "frontend": { + "eslint": true, + "typescript": true, + "vitestPassed": 3, + "productionBuild": true + }, + "playwright": { + "passed": 37, + "files": [ + "administrators", + "catalog", + "configuration", + "lifecycle", + "operations", + "policies", + "snapshots" + ], + "allAPIRequestsIntercepted": true, + "liveSpecExcluded": true + }, + "layoutFollowup": { + "passed": 8, + "snapshotOnly": true, + "headerAndFooterVisibleOn390px": true + }, + "pveFixtureCoverage": [ + "create/delete/rollback UPID and final state", + "strict inputs and permission alternatives", + "VM power/pending/lock/session blockers", + "disk layout and memory state restrictions", + "snapshot revision and config digest", + "idempotency and pre-execution recheck", + "missing UPID and unknown result without retry", + "original task failure cannot pass by config hash alone", + "read-only reconciliation with one SQL connection", + "expiry and maintenance exclusivity", + "rollback initialization and ticket revocation", + "long snapshot does not block ordinary tasks" + ], + "siteSnapshotWritesTested": false +} \ No newline at end of file diff --git a/docs/evidence/cloud-terminal-metrics-20261110/browser-verification.json b/docs/evidence/cloud-terminal-metrics-20261110/browser-verification.json new file mode 100644 index 0000000..40124e8 --- /dev/null +++ b/docs/evidence/cloud-terminal-metrics-20261110/browser-verification.json @@ -0,0 +1,17 @@ +{ + "time": "2026-11-10T09:46:48.378Z", + "themes": [ + "dark", + "light" + ], + "viewportWidths": [ + 1440, + 390 + ], + "scriptErrors": [], + "failedResponses": [], + "blockedWrites": [], + "systemMetricCharts": 3, + "sevenDayAggregationVerified": true, + "screenshots": 6 +} \ No newline at end of file diff --git a/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-metrics-dark.png b/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-metrics-dark.png new file mode 100644 index 0000000..84c63b4 Binary files /dev/null and b/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-metrics-dark.png differ diff --git a/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-metrics-light.png b/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-metrics-light.png new file mode 100644 index 0000000..4b0a4ee Binary files /dev/null and b/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-metrics-light.png differ diff --git a/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-metrics-mobile-7days.png b/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-metrics-mobile-7days.png new file mode 100644 index 0000000..300dd19 Binary files /dev/null and b/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-metrics-mobile-7days.png differ diff --git a/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-metrics-mobile.png b/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-metrics-mobile.png new file mode 100644 index 0000000..500a5eb Binary files /dev/null and b/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-metrics-mobile.png differ diff --git a/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-trends-dark.png b/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-trends-dark.png new file mode 100644 index 0000000..7c049e9 Binary files /dev/null and b/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-trends-dark.png differ diff --git a/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-trends-light.png b/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-trends-light.png new file mode 100644 index 0000000..e7d6443 Binary files /dev/null and b/docs/evidence/cloud-terminal-metrics-20261110/live-terminal-trends-light.png differ diff --git a/docs/evidence/cloud-terminal-metrics-20261110/live-verification.json b/docs/evidence/cloud-terminal-metrics-20261110/live-verification.json new file mode 100644 index 0000000..09b8393 --- /dev/null +++ b/docs/evidence/cloud-terminal-metrics-20261110/live-verification.json @@ -0,0 +1,175 @@ +{ + "time": "2026-11-10T09:46:38.270458+00:00", + "agentVersion": "qt-platform/0.4.0", + "latest": { + "cpuPercent": 2.598813403526364, + "memoryPercent": 10.229769274225609, + "socTemperatureC": 44.37, + "uptimeSeconds": 30575, + "sampledAt": "2026-11-10T09:46:26.030724Z", + "supported": true, + "online": true, + "fresh": true + }, + "sourceCrossCheck": { + "time": "2026-11-10T09:46:33.562078+00:00", + "cpuPercent": 0.25125628140703515, + "cpuIntervalSeconds": 1, + "memoryPercent": 10.435217539024709, + "socTemperatureC": 44.926, + "uptimeSeconds": 30585.7, + "kioskPIDs": [ + 317057 + ], + "binarySHA256": "6ff0697e9c5efb7428053d54af6d52f360e726eea5962e944f592c802f7463e1" + }, + "cpuComparisonNote": "Heartbeat CPU averages the interval between heartbeats; the independent proc sample averages 1 second. Only range is compared, not exact equality.", + "historySamples": [ + { + "sampled_at": "2026-11-10T09:38:59.015711+00:00", + "cpu_percent": null, + "memory_percent": null, + "soc_temperature_c": null, + "uptime_seconds": null + }, + { + "sampled_at": "2026-11-10T09:39:29.026968+00:00", + "cpu_percent": null, + "memory_percent": null, + "soc_temperature_c": null, + "uptime_seconds": null + }, + { + "sampled_at": "2026-11-10T09:39:56.345374+00:00", + "cpu_percent": null, + "memory_percent": 10.093169094284015, + "soc_temperature_c": 47.704, + "uptime_seconds": 30185.31 + }, + { + "sampled_at": "2026-11-10T09:40:26.026575+00:00", + "cpu_percent": 2.5424444632147987, + "memory_percent": 10.225883492301667, + "soc_temperature_c": 46.037, + "uptime_seconds": 30215 + }, + { + "sampled_at": "2026-11-10T09:40:56.024322+00:00", + "cpu_percent": 2.6115342763873777, + "memory_percent": 10.246508027128735, + "soc_temperature_c": 45.481, + "uptime_seconds": 30245 + }, + { + "sampled_at": "2026-11-10T09:41:26.022178+00:00", + "cpu_percent": 2.5163016218023744, + "memory_percent": 10.23255906637613, + "soc_temperature_c": 45.481, + "uptime_seconds": 30275 + }, + { + "sampled_at": "2026-11-10T09:41:56.027487+00:00", + "cpu_percent": 4.4864819619988285, + "memory_percent": 10.244913860185582, + "soc_temperature_c": 45.481, + "uptime_seconds": 30305 + }, + { + "sampled_at": "2026-11-10T09:42:26.026358+00:00", + "cpu_percent": 2.508151492350138, + "memory_percent": 10.319441164778077, + "soc_temperature_c": 44.926, + "uptime_seconds": 30335 + }, + { + "sampled_at": "2026-11-10T09:42:56.026416+00:00", + "cpu_percent": 2.643908969210174, + "memory_percent": 10.249397454713204, + "soc_temperature_c": 44.926, + "uptime_seconds": 30365 + }, + { + "sampled_at": "2026-11-10T09:43:26.026552+00:00", + "cpu_percent": 2.482862397592376, + "memory_percent": 10.236544483734017, + "soc_temperature_c": 44.926, + "uptime_seconds": 30395 + }, + { + "sampled_at": "2026-11-10T09:43:56.020921+00:00", + "cpu_percent": 1.4039779374895538, + "memory_percent": 10.30519329772363, + "soc_temperature_c": 46.592, + "uptime_seconds": 30425 + }, + { + "sampled_at": "2026-11-10T09:44:26.033131+00:00", + "cpu_percent": 2.1219715956558063, + "memory_percent": 10.214724323699583, + "soc_temperature_c": 44.37, + "uptime_seconds": 30455 + }, + { + "sampled_at": "2026-11-10T09:44:56.034962+00:00", + "cpu_percent": 2.600117047069643, + "memory_percent": 10.196989216456984, + "soc_temperature_c": 44.37, + "uptime_seconds": 30485 + }, + { + "sampled_at": "2026-11-10T09:45:56.037685+00:00", + "cpu_percent": 2.693208430913349, + "memory_percent": 10.245910214525052, + "soc_temperature_c": 44.37, + "uptime_seconds": 30545 + }, + { + "sampled_at": "2026-11-10T09:46:26.030724+00:00", + "cpu_percent": 2.598813403526364, + "memory_percent": 10.229769274225609, + "soc_temperature_c": 44.37, + "uptime_seconds": 30575 + } + ], + "bucketSeconds": 300, + "retentionDays": 30, + "emptyBuckets": 286, + "oldAgentCompatibility": { + "agentVersion": "qt-platform/0.3.0", + "latest": { + "cpuPercent": null, + "memoryPercent": null, + "socTemperatureC": null, + "uptimeSeconds": null, + "sampledAt": "2026-11-10T09:38:59.015711Z", + "supported": false, + "online": true, + "fresh": true + } + }, + "deviceConfigurationUnchanged": true, + "businessSnapshotUnchanged": true, + "vmConfigurationDigestsUnchanged": true, + "allVMsRunning": true, + "taskCountsUnchanged": true, + "lightDMRestarted": false, + "hostRebooted": false, + "powerWrites": 0, + "snapshotWrites": 0, + "diskWrites": 0, + "images": { + "api": "sha256:525a99894a7e1efade08087082e356888192a452b90e537fb6a278c59b1e62fe", + "console": "sha256:17af96c4ba2ff10b9ea4c1380179d54e1e325edf5fda7d63225aca1d5e37e78e" + }, + "deployment": { + "time": "2026-11-10T09:33:46.348508+00:00", + "backup": ".local/live/backups/before-terminal-metrics-20261110-173343.dump", + "backupBytes": 87058, + "oldImageTag": "before-terminal-metrics-20261110-173343", + "oldImages": { + "api": "sha256:0c33d1618dc5f6ed5dd0afa0d5876f2fc22eaf94508815b90029461b77e20259", + "console": "sha256:71d16b9761d49a91a117d8a65b756a1089fec15dfbfc3f2607a0274cc7afd73c" + } + }, + "kioskBackup": "/home/armbian/vdi-terminal/bin/kiosk-shell.before-terminal-metrics-20261110" +} \ No newline at end of file diff --git a/docs/evidence/cloud-terminal-metrics-20261110/terminal-metrics-dark.png b/docs/evidence/cloud-terminal-metrics-20261110/terminal-metrics-dark.png new file mode 100644 index 0000000..c4447ee Binary files /dev/null and b/docs/evidence/cloud-terminal-metrics-20261110/terminal-metrics-dark.png differ diff --git a/docs/evidence/cloud-terminal-metrics-20261110/terminal-metrics-mobile-light.png b/docs/evidence/cloud-terminal-metrics-20261110/terminal-metrics-mobile-light.png new file mode 100644 index 0000000..d6dacd8 Binary files /dev/null and b/docs/evidence/cloud-terminal-metrics-20261110/terminal-metrics-mobile-light.png differ diff --git a/docs/evidence/cloud-terminal-metrics-20261110/validation.json b/docs/evidence/cloud-terminal-metrics-20261110/validation.json new file mode 100644 index 0000000..cdc4ad6 --- /dev/null +++ b/docs/evidence/cloud-terminal-metrics-20261110/validation.json @@ -0,0 +1,35 @@ +{ + "time": "2026-11-10T09:45:13.437882+00:00", + "goAll": "passed", + "goFinalTargeted": "passed", + "goVet": "passed", + "frontendLint": "passed", + "frontendTypecheck": "passed in production build", + "frontendUnitTests": 3, + "frontendProductionBuild": "passed", + "browserFixtureTests": 57, + "qtCTestTargets": 6, + "isolatedDatabases": [ + "vdi_metrics_test", + "vdi_metrics_final" + ], + "liveDatabaseUsedForTests": false, + "sourceChecks": [ + "first CPU sample is null", + "counter reset and missing files are null", + "guest counters not counted twice", + "soc-thermal source required" + ], + "liveEvidence": "live-verification.json", + "browserEvidence": "browser-verification.json", + "limitations": [ + "No actual streaming FPS/RTT collector", + "No GPU health collector", + "No terminal system metric alarm rules", + "PostgreSQL history, no VictoriaMetrics integration", + "Coarse aggregate buckets can merge short gaps" + ], + "temporaryDatabaseRemoved": true, + "temporaryViteStopped": true, + "finalDeploymentVerified": true +} \ No newline at end of file diff --git a/docs/evidence/terminal-selection-style-20261110/busy-1280.png b/docs/evidence/terminal-selection-style-20261110/busy-1280.png new file mode 100644 index 0000000..86215d3 Binary files /dev/null and b/docs/evidence/terminal-selection-style-20261110/busy-1280.png differ diff --git a/docs/evidence/terminal-selection-style-20261110/compact-800.png b/docs/evidence/terminal-selection-style-20261110/compact-800.png new file mode 100644 index 0000000..393af5e Binary files /dev/null and b/docs/evidence/terminal-selection-style-20261110/compact-800.png differ diff --git a/docs/evidence/terminal-selection-style-20261110/dark-1280.png b/docs/evidence/terminal-selection-style-20261110/dark-1280.png new file mode 100644 index 0000000..073cf9b Binary files /dev/null and b/docs/evidence/terminal-selection-style-20261110/dark-1280.png differ diff --git a/docs/evidence/terminal-selection-style-20261110/dark-1366.png b/docs/evidence/terminal-selection-style-20261110/dark-1366.png new file mode 100644 index 0000000..5a59d58 Binary files /dev/null and b/docs/evidence/terminal-selection-style-20261110/dark-1366.png differ diff --git a/docs/evidence/terminal-selection-style-20261110/dark-1920.png b/docs/evidence/terminal-selection-style-20261110/dark-1920.png new file mode 100644 index 0000000..d1c9d79 Binary files /dev/null and b/docs/evidence/terminal-selection-style-20261110/dark-1920.png differ diff --git a/docs/evidence/terminal-selection-style-20261110/dark-2560.png b/docs/evidence/terminal-selection-style-20261110/dark-2560.png new file mode 100644 index 0000000..c2ee461 Binary files /dev/null and b/docs/evidence/terminal-selection-style-20261110/dark-2560.png differ diff --git a/docs/evidence/terminal-selection-style-20261110/error-empty-1280.png b/docs/evidence/terminal-selection-style-20261110/error-empty-1280.png new file mode 100644 index 0000000..464214c Binary files /dev/null and b/docs/evidence/terminal-selection-style-20261110/error-empty-1280.png differ diff --git a/docs/evidence/terminal-selection-style-20261110/light-1280.png b/docs/evidence/terminal-selection-style-20261110/light-1280.png new file mode 100644 index 0000000..8c1341a Binary files /dev/null and b/docs/evidence/terminal-selection-style-20261110/light-1280.png differ diff --git a/docs/evidence/terminal-selection-style-20261110/light-1366.png b/docs/evidence/terminal-selection-style-20261110/light-1366.png new file mode 100644 index 0000000..0c97a70 Binary files /dev/null and b/docs/evidence/terminal-selection-style-20261110/light-1366.png differ diff --git a/docs/evidence/terminal-selection-style-20261110/light-1920.png b/docs/evidence/terminal-selection-style-20261110/light-1920.png new file mode 100644 index 0000000..f016050 Binary files /dev/null and b/docs/evidence/terminal-selection-style-20261110/light-1920.png differ diff --git a/docs/evidence/terminal-selection-style-20261110/light-2560.png b/docs/evidence/terminal-selection-style-20261110/light-2560.png new file mode 100644 index 0000000..d8011be Binary files /dev/null and b/docs/evidence/terminal-selection-style-20261110/light-2560.png differ diff --git a/docs/evidence/terminal-selection-style-20261110/live-dark.png b/docs/evidence/terminal-selection-style-20261110/live-dark.png new file mode 100644 index 0000000..288ad68 Binary files /dev/null and b/docs/evidence/terminal-selection-style-20261110/live-dark.png differ diff --git a/docs/evidence/terminal-selection-style-20261110/live-light.png b/docs/evidence/terminal-selection-style-20261110/live-light.png new file mode 100644 index 0000000..f2abb2a Binary files /dev/null and b/docs/evidence/terminal-selection-style-20261110/live-light.png differ diff --git a/docs/evidence/terminal-selection-style-20261110/live-light.xwd b/docs/evidence/terminal-selection-style-20261110/live-light.xwd new file mode 100644 index 0000000..daf79eb Binary files /dev/null and b/docs/evidence/terminal-selection-style-20261110/live-light.xwd differ diff --git a/docs/evidence/terminal-selection-style-20261110/states-light-1366.png b/docs/evidence/terminal-selection-style-20261110/states-light-1366.png new file mode 100644 index 0000000..a8018b3 Binary files /dev/null and b/docs/evidence/terminal-selection-style-20261110/states-light-1366.png differ diff --git a/docs/operations.md b/docs/operations.md index f9665d8..88442c0 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -38,6 +38,8 @@ ## 常见问题的排查顺序 +**开机显示旧的用户名/密码页**:11-10 已定位到原启动认证回调在任一失败后切到 CONFIG,且没有认证重试。新版保持选择页并退避重试;查询 `.xsession-errors` 中 `vdi_device_auth` 和 `vdi_device_auth_request`,分别核对重试、HTTP 状态及网络错误码。旧日志没有记录首次失败细节时,不直接认定 Wi-Fi、证书或平台停机中的某一个原因。当前平台仍在开发机 Docker,开发机/容器不可达时需要恢复服务,页面保持选择页不代表离线仍能授权连接。 + 1. **开机/退出出现盒子桌面或系统 greeter**:核对真实运行会话 `DESKTOP_SESSION=vdi-kiosk`、LightDM 配置、是否残留 `mate-session/mate-panel/marco/caja`,检查是否从 SSH 脱离 LightDM 拉起了 kiosk。不要仅检查磁盘默认会话或一张开机截图。 2. **进入时左上角小窗**:核对 Moonlight 0007 构建、`VDI_KIOSK_STREAM=1`、SDL 首次映射几何、连接遮罩,使用进入探针;只测退出不能覆盖进入阶段。 3. **黑边/画面变矮**:同时核对盒子 XRandR、Moonlight 窗口、客体捕获尺寸与显示守护;原 1920×1440 回退会让 16:9 视频上下留边。 diff --git a/docs/roadmap.md b/docs/roadmap.md index f06de89..fd5c496 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,17 +1,22 @@ # 未来计划 -更新日期:2026-11-09。本文是后续工作的统一清单;列入计划不表示本次已实施。 +更新日期:2026-11-10。本文是后续工作的统一清单;列入计划不表示本次已实施。 + +云管功能逐项状态见[11-10 核对清单](cloud-feature-progress-20261110.md)。模板登记、同节点克隆编排、桌面池容量/成员和连接初始化确认已补齐;实际发放仍缺现场模板/权限,管理员账号维护、密码重置/修改及会话撤销也已部署并完成真实临时账号验收。终端改名/分组、停用/重新激活及用户资料/批量授权已补齐,见[设备与用户维护](cloud-lifecycle-20261110.md)。运维策略模板、设备/分组下发及真实应用回执已补齐,见[终端策略](cloud-terminal-policies-20261110.md)。PVE 网页接入、关机规格管理和登录限流也已部署,现场规格写入待补充权限与验收,见[说明](cloud-configuration-20261110.md)。关机快照与手动还原基础已部署,现场快照写入仍待专用 VM 验收,见[说明](cloud-snapshots-20261110.md)。后续重点为公共池自动领取/还原、GPU、VictoriaMetrics/外部告警通知、完整终端指标采集、扩展终端策略、OTA 与认证增强(目录集成、找回/到期和多因素认证)。 + +CSV 导入、90 天会话历史及节点历史曲线/告警规则已补齐,见[本轮实现与边界](cloud-operations-20261110.md)。 ## 用户明确暂不实施的事项 | 事项 | 后续范围 | 当前状态 | | --- | --- | --- | +| 专用 VM、Windows 模板副本与存储准备 | 为快照内容恢复、真实克隆和公共池闭环准备独立环境 | 11-10 用户明确要求先不做,改做其他功能;仅做过只读空间检查,未创建 VM、未修改业务 VM、未挂载/格式化额外磁盘或补 PVE 权限。相关现场写入验收暂缓,不默认恢复此工作 | | 云桌面内上网代理 / VPN | 登录首页提供 Clash/Mihomo 订阅导入/更新、节点选择、启停与连通性测试;平台向 Windows、Ubuntu、麒麟下发策略,让桌面内浏览器和应用访问 GPT 等外部服务;串流、管理与必要内网流量保留直连/专用路由 | 仅规划,未实现、未部署;不是单纯把 Moonlight 服务器地址换成 VPN 地址 | | 自有软件白名单与商业合规 | 按客户/桌面管理允许的软件和版本、策略下发、审计与回滚;Windows/麒麟拟使用签名插件或代理组件,Ubuntu 拟在系统层实施;定版前核对系统接口、授权、权限模型、更新与例外机制 | 仅规划,未实现、未部署;不能直接把某种底层修改一概认定为合法或违法 | | 盒子品牌安全画面 | 公司 Logo 全屏兜底、隐藏桌面内容,覆盖开机、断流、异常退出及重启场景 | 品牌替换尚未实施。11-09 已另行修复独占会话、常驻列表和进入小窗,不能继续把这些已做的修复写为“全部未实现” | -以上三项保持后续计划,不因文档合并启动开发或部署。 +以上事项保持后续计划,不因文档合并启动开发或部署。 ## 当前体验的剩余验收 @@ -53,3 +58,10 @@ ## 已完成事项不再重复排期 三系统正常接入、Windows NVENC、RKMPP 硬解、2K 配置、基础 USB 真实透传、图形电源策略及 11-09 进入/退出交接已经有实施记录。后续计划应写剩余验收或产品化边界,不再笼统写“未接入 Linux”“未打通硬解”“尚未开始 USB”。详情见[当前状态](current-status.md)。 + + +## 云管下一步顺序(11-10 更新) + +1. 桌面批量开机/正常关机/重启已部署,逐台预检、跨页选择、幂等提交、批次结果和只读核对已完成;现场仅预检与取消,未对现有 VM 执行电源操作。见[实现与验收](cloud-batch-operations-20261110.md)。 +2. GPU 只读清单与虚拟机配置引用已部署,含待应用变更和不完整检查提示;实际驱动占用、独占绑定/解绑和健康指标仍待实施,见[范围与验收](cloud-gpu-inventory-20261110.md)。终端 CPU/内存/SoC 温度/运行时长及历史已接通,见[采集与验收](cloud-terminal-metrics-20261110.md);串流 FPS/RTT 与 GPU 健康指标仍待接入。下一步优先扩展终端策略,再推进外部通知、签名 OTA 和认证增强。接入外部通知前需确定接收渠道及配置,不能将控制台告警视为已发通知。 +3. 专用测试 VM、Windows 副本、存储及真实快照内容还原验收按用户要求暂缓。公共池自动领取/退出还原仍依赖独立环境和恢复验收,当前不启用自动还原。恢复该方向后再补池基线、池授权、原子领取与占用租约,区分退出/断线/离线,失败或未知结果隔离。 diff --git a/docs/terminal-guide.md b/docs/terminal-guide.md index 9c0b771..9576008 100644 --- a/docs/terminal-guide.md +++ b/docs/terminal-guide.md @@ -9,6 +9,12 @@ 产品入口由 LightDM 管理 `vdi-kiosk` 独占会话,再运行 `vdi-kiosk-start → vdi-kiosk-monitor → kiosk-shell`。开机直接进入“选择云桌面”,不再通过普通 MATE 桌面或旧登录页中转。Moonlight 只在选择桌面后启动,不能与 kiosk 分别设置开机自启。 +11-10 补充:真实平台接入后的启动认证原先只尝试一次,任一失败都会跳回旧的用户名/密码页,并且不再认证。已改为先绘制选择页,再异步进行设备认证;失败留在该页显示自动重试提示,按 2/4/8/16/30 秒退避(请求完成后再计时),恢复后自动加载授权桌面。认证完成前不读取本地桌面作为授权、不发连接票据,不并发重复认证。设备撤销、配置错误同样不会回到旧登录页,但仍需管理员处理实际授权/配置问题。 + +用户首次启动失败的具体网络/TLS/HTTP 原因没有足够旧日志,不能仅凭第二次开机正常确定是哪一项。新增诊断只记录认证请求 HTTP 状态和网络错误码、重试间隔,不记录身份凭据或请求正文。回归结果见 [boot-auth-20261110](evidence/boot-auth-20261110/)。 + +部署/验证:11-10 盒子 ARM64 构建 CTest 4/4 通过;新增真实 HTTPS 隔离测试覆盖首次离线后恢复、403 后恢复、慢认证不重叠、同步配置错误、本地模式五个场景。只原子替换 kiosk-shell,由原看门狗重启子进程;当前进程认证 ready、平台授权列表 200/三桌面、独占会话与选择页截图核对通过,boot ID 未变,未再次断电验证。kiosk-shell SHA256 为 `9dba9c66092aa9ccf67477b305aaefe09f52eabb8c6b30dbbcbb1ce43dc7a6a6`;备份在 `~/vdi-terminal/backups/boot-device-auth-20261110/`。Moonlight 与光标校验配置未变。 + 主要配置/实现入口: - [LightDM 配置](../terminal/kiosk-shell/etc/92-vdi-kiosk.conf)、[会话定义](../terminal/kiosk-shell/vdi-kiosk-session.desktop)、[启动脚本](../terminal/kiosk-shell/scripts/vdi-kiosk-start)。 @@ -75,3 +81,14 @@ QT_QPA_PLATFORM=offscreen ctest --test-dir build --output-on-failure 旧二进制和 Linux 光标配置备份在盒子 `~/vdi-terminal/backups/return-surface-20261109/`。回退必须同时匹配 Moonlight 二进制与两台 Linux 原生光标 profile 中的客户端 SHA,不能只换二进制。显示守护旧文件备份为 `~/vdi-terminal/backups/display-guard-20261103-095920/`,恢复旧布局也会恢复原先的显示回退风险。 当日 CTest 3/3、原生光标边界测试 4/4 通过;完整实验过程及较早版本校验值见[归档](archive/README.md)。 + +## 运维策略(2026-11-10) + +客户端 `qt-platform/0.4.0`(从 0.3.0 起)支持平台配置的心跳、命令检查和桌面列表刷新间隔。每 15 秒检查新版本,安全写入 `~/.config/vdi-terminal/policy.json`(0600)后更新运行定时器并回执;身份仍保存在原 `platform.json`,不要混用。新下发失败时保留原运行配置,原因在控制台「终端策略 → 设备结果」查看。 + +当前 kiosk-shell SHA256 `690a74ee5b077ff96e385f2ba45ca756f1879223f7433e63ec1833eb0f3a628b`,更新前备份 `~/vdi-terminal/backups/policy-20261110/kiosk-shell`;CTest 已增至 5 项并全部通过。真实盒子完成改变管理间隔及恢复默认的设备回执验证,保留之前启动认证重试与全屏交接修复。该次未测试断电、长时间离线或活动串流中下发。见[策略说明](cloud-terminal-policies-20261110.md)。 + + +## 平台系统指标 + +`qt-platform/0.4.0` 在心跳中上报真实 CPU 使用率、内存使用率、SoC 温度和运行时长,沿用心跳策略(现场 30 秒)。控制台入口为「终端管理 → 详情 → 查看运行指标」,可查最近 1 小时/24 小时/7 天,历史保留 30 天。CPU 第一次采样为空;缺失传感器和离线过期数据不填零。不会更改串流参数,实际 FPS/RTT 仍未采集。定义与验收见[终端系统指标](cloud-terminal-metrics-20261110.md)。 diff --git a/docs/terminal-selection-style-20261110.md b/docs/terminal-selection-style-20261110.md new file mode 100644 index 0000000..a260dcd --- /dev/null +++ b/docs/terminal-selection-style-20261110.md @@ -0,0 +1,34 @@ +# 云桌面选择页样式调整(2026-11-10) + +## 范围与设计来源 + +按用户确认,调整开机直接显示的“选择云桌面”页,参考 +`ui/terminal/index.html` 的“登录工作台”设计;保留现有设备认证与平台授权流程。 + +- 左侧采用百工万象标识、品牌文案、金色树形图案与深色/暖白背景。 +- 右侧展示平台返回的桌面卡片,使用铜橙色连接按钮、系统图标与状态文字。 +- 深浅色外观可切换并记忆;空列表、平台错误、任务进行中、待分配及连接中均保留真实状态。 +- Qt 原生布局,短屏可滚动;背景与品牌资源静态绘制,没有持续动画、WebView 或额外后台进程。 +- 未引入设计原型中的模拟账号、SSO 或离线认证入口。 + +## 文件 + +- `terminal/kiosk-shell/src/desktop_list_page.cpp/.h`:页面布局、主题、响应式尺寸和桌面操作。 +- `terminal/kiosk-shell/src/main_window.cpp`:移除旧选择页的局部样式覆盖。 +- `terminal/kiosk-shell/resources/`:内嵌品牌与麒麟图标,来源见该目录 README。 +- `terminal/kiosk-shell/tests/preview_desktop_list.cpp`:Qt 离屏截图工具。 +- `tools/deploy_kiosk.sh`:同步新增资源目录。 + +验证截图位于 `docs/evidence/terminal-selection-style-20261110/`。 + +## 验证与部署结果 + +- ARM 原生 Release 编译通过。 +- CTest 5/5:terminal-policy、display-guard、moonlight-session、desktop-list-page、main-window-boot。 +- 离屏检查:1280×720、1366×768、1920×1080、2560×1440 深浅两色;800×600 滚动布局;连接中、平台错误、空列表、任务进行中与待分配状态。 +- 实机:2560×1440,平台返回的 Ubuntu、Windows 10、麒麟三张卡片正常显示;外观按钮可切换并保存。 +- 已更新 `armbian@192.168.1.188` 的 `~/vdi-terminal/bin/kiosk-shell`,由 LightDM 重新启动;已移除底部内部状态名称的显示。 +- 原二进制备份:`~/vdi-terminal/bin/kiosk-shell.before-selection-style-20261110`。 +- 仅更新界面程序,未重新配置虚拟机、Sunshine、Moonlight 或串流参数;本次没有发起新的远程桌面连接。 + +后续按用户反馈放大树形图案约 24%,保持宽高比并上移;移除“当前身份”与设备 SN 的可见文本。后台设备认证与当前身份的数据接口保留。