文档:同步云管功能进度与现场验收记录

This commit is contained in:
宋一鸣 2026-11-10 17:53:20 +08:00
parent 6134d49c16
commit 5ac692724d
179 changed files with 8311 additions and 9 deletions

View File

@ -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 仍未采集。

View File

@ -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)。

View File

@ -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 配置且不识别规格任务,存在排队或未知规格任务时不可直接切回旧执行器;恢复数据库快照会丢弃快照后的业务记录,当前未进行恢复演练。

View File

@ -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 健康指标及系统指标告警仍待接入。

View File

@ -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)。

View File

@ -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)。

View File

@ -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)。

View File

@ -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 弹窗,无脚本错误、失败接口或页面检查产生的修改请求。

View File

@ -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 健康指标及系统指标告警仍待接入。

View File

@ -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 兼容性,不能只切旧镜像后丢弃维护保护;备份恢复尚未演练。

View File

@ -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 二进制,新指标表可保留,旧版本不依赖它们。

View File

@ -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 服务保留。

View File

@ -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)。

View File

@ -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
<end of output>
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 *********
<end of output>
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 *********
<end of output>
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 *********
<end of output>
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

View File

@ -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

View File

@ -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"}

View File

@ -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."
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -0,0 +1,13 @@
{
"checkedAt": "2026-11-10T02:36:12.594Z",
"realAPI": true,
"themes": [
"dark",
"light"
],
"mobileWidth": 390,
"createAndOwnPasswordDialogsCancelled": true,
"pageErrors": [],
"failedResponses": [],
"mutations": []
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

View File

@ -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"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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": []
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -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
}

View File

@ -0,0 +1,16 @@
{
"time": "2026-11-10T07:28:23.238Z",
"themes": [
"dark",
"light"
],
"viewportWidths": [
1440,
390
],
"scriptErrors": [],
"failedResponses": [],
"mutations": [],
"readOnlyInspection": true,
"screenshots": 8
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

@ -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
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -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"
}
]
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@ -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"
}
}
}

View File

@ -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
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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": []
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Some files were not shown because too many files have changed in this diff Show More