Skip to content

fix(ui): 把已算好但没发出去的数据接到界面上 - #5

Merged
SMNETSTUDIO merged 1 commit into
mainfrom
fix/ux-wiring
Aug 28, 2026
Merged

fix(ui): 把已算好但没发出去的数据接到界面上#5
SMNETSTUDIO merged 1 commit into
mainfrom
fix/ux-wiring

Conversation

@SMNETSTUDIO

Copy link
Copy Markdown
Owner

三批改动,共同点是后端早就算好了,只差最后一段接线 —— 用户看不到的功能等于没有。

1. csrf 错误码没人接(上个 PR 我自己留下的)

api() 只处理 4012fa_required,我上轮加的 code:'csrf' 没有分支,而很多调用点又不判 ok —— 表现是**「点了没反应」**。

正常同源访问走不到这里;走到了基本只有一种情况:面板在反代后面而 Host 头没透传对。而那恰恰是最需要把原因说清楚的时候。

顺带把 MCSP_TRUSTED_ORIGINS 写进 README 和 docker-compose —— 之前这个逃生阀只存在于源码里,遇到问题的人不可能知道有它。

2. 权限分档在 UI 上等于不存在

后端一直算着 req.perm,但从没发给前端:snapshot() 里没有,前端对 .perm 的引用数是 0。于是 viewer 看到的界面和主人一模一样,按钮全在,点下去才 403。

「能看见但一按就报错」比「看不见」更糟 —— 用户分不清是自己没权限还是面板坏了,而 403 的文案是通用的。

几个设计决定:

  • perm 加在路由层(列表 + /status),不进 snapshot() —— 同一份 snapshot 还会经 SSE 广播给权限不同的多个人,塞进去就会发错人
  • 前端用独立的 permMap,不放 instMap —— 否则会被 SSE 的 state 事件覆盖
  • 声明式:HTML 标 data-need="operator|manager|owner",一个函数统一禁用并给出「需要 X 档,你是 Y 档」的 title。以后加按钮只需加个属性
  • 总览卡片的启停按钮按卡片所属实例判权限(不是当前选中实例),只读档直接不渲染 —— 列表里摆一排点了就报错的按钮比没有按钮更让人困惑

3. 五处「数据已在手边,差一行渲染」

之前 现在
计划任务执行历史 存最近 5 次并落盘、API 下发,前端只画 lastResult 画成结果点。「偶尔抽风」和「一直坏」一次结果分不出来
实例 createdAt 三处写入、一直落盘,从没进 snapshot() 设置页显示。用户/邀请/备份都有「创建于」,唯独实例没有
playtime.firstSeen 后端三层都传到前端了,渲染时跳过 显示「初见」
审计按用户筛选 read() 支持 user 且为它写了条免 JSON.parse 的快路径,前端只发 q —— 那条优化没有调用方能触发 点用户名即精确筛选
导入探测置信度 detect.js 算了 high/low/none,前端只用 d.type 低置信度明确提示去核对;notes 从只显示第一条改成全部

notes 里有「未找到已同意的 eula.txt」这类直接影响能否启动的提示,漏掉后面几条等于让用户自己去撞。

测试:85 → 111

新增 perm 下发的 7 条(三档 + 管理员,列表与 /status 各一)。

并修好了一个测试覆盖缺口:collabRoleSuite 原先只在「面板上恰好已有实例」时才跑,空面板(CI 就是空的)会整段跳过 —— 也就是说这套权限用例在 CI 里从来没执行过,而它覆盖的正是最容易写错的部分。现在没有现成实例就自建一个走完 finalize,跑完删掉。上个 PR 里我靠手工验证的那批权限断言,现在 CI 每次都会跑。

前端另在真实浏览器里核对(测试覆盖不到这层):viewer 身份下 9 个控件全部 disabled 且 title 正确、协作者卡片隐藏、createdAt 正常渲染。

顺带发现并对齐了一处样式冲突:代码库已有 .btn:disabled { opacity: 0.35; filter: none },特指度 (0,2,0) 压过我新加的 .perm-denied (0,1,0) —— 我原本写的 grayscale 在按钮上从不生效、在输入框上却生效,两者会灰得不一样。改成对齐既有约定。

我更正的一处说法

上一轮我说「detect.notes 前端不读」。实际上 notes[0] 渲染了的,没渲染的是 confidence 和第 2 条之后的 notes。

🤖 Generated with Claude Code

三批。共同点是后端早就算好了,只差最后一段接线 —— 用户看不到的功能等于没有。

## 1. csrf 错误码没人接(上个 PR 我自己留下的)

api() 只处理 401 和 2fa_required,而我上轮加的 code:'csrf' 没有分支,很多调用点
又不判 ok —— 表现是"点了没反应"。正常同源访问走不到这里,走到了基本只有一种
情况:面板在反代后面而 Host 头没透传对,而那恰恰是最需要把原因说清楚的时候。
顺带把 MCSP_TRUSTED_ORIGINS 这个逃生阀写进 README 和 docker-compose ——
之前它只存在于源码里,遇到问题的人不可能知道有这个东西。

## 2. 权限分档在 UI 上等于不存在

后端一直算着 req.perm(viewer/operator/manager/owner),但从没发给前端:
snapshot() 里没有,前端对 .perm 的引用数是 0。于是 viewer 看到的界面和主人
一模一样,启动、保存配置、清空统计按钮全在,点下去才 403。
"能看见但一按就报错"比"看不见"更糟 —— 用户分不清是自己没权限还是面板坏了。

- perm 加在**路由层**(实例列表 + /status),不进 snapshot():同一份 snapshot
  还会经 SSE 广播给权限不同的多个用户,塞进去就会发错人。
- 前端用独立的 permMap 存,不放 instMap —— 否则会被 SSE 的 state 事件覆盖掉。
- 做法是声明式的:HTML 标 data-need="operator|manager|owner",一个函数统一
  禁用并给出"需要 X 档,你是 Y 档"的 title。以后加按钮只需加个属性。
- 总览卡片的启停按钮按**卡片所属实例**判权限(不是当前选中实例),只读档直接
  不渲染 —— 列表里摆一排点了就报错的按钮比没有按钮更让人困惑。
- .perm-denied 的透明度对齐已有的 .btn:disabled(0.35)。那条规则特指度 (0,2,0)
  本来就压过 (0,1,0),按钮走的一直是它;这里主要给非 .btn 控件兜底,数值写一样
  免得输入框和按钮灰得深浅不同,也不加 filter(否则只在输入框上生效,更不一致)。

## 3. 五处"数据已在手边,差一行渲染"

- **计划任务执行历史**:后端存最近 5 次并落盘、API 原样下发,前端只画了
  lastResult。而"偶尔抽风"和"一直坏"是两种处置,一次结果分不出来。
  画成结果点,失败用方块+琥珀色(形状+颜色双编码,不只靠颜色)。
- **实例 createdAt**:三处写入、一直落盘,却从没进过 snapshot。用户/邀请/备份
  都有"创建于",唯独实例没有。
- **playtime firstSeen**:后端三层都传到前端了,渲染时跳过了。
- **审计按用户精确筛选**:read() 早就支持 user 参数,还为它写了条免 JSON.parse
  的快路径(直接在原始行里找 "user":"xxx"),但前端只发 q —— 那条优化没有调用方
  能触发。改成点用户名即筛他一个人;全文搜索会把路径里含同名子串的行也带进来,
  精确匹配不会。
- **导入探测置信度**:detect.js 算了 high/low/none 三级(三条线索都对上才 high),
  前端只用了 d.type,于是"三样都对上"和"只猜出个类型"反馈一模一样,而后者
  需要用户去核对。notes 也从只显示第一条改成全部显示 —— 里面有"未找到已同意的
  eula.txt"这类直接影响能否启动的提示,漏掉后面几条等于让用户自己去撞。

## 测试

85 → 111 项。新增 perm 下发的 7 条(三档 + 管理员,列表与 /status 各一)。

**并修好了一个测试覆盖缺口**:collabRoleSuite 原先只在"面板上恰好已有实例"时才跑,
空面板(CI 就是空的)会整段跳过 —— 也就是说这套权限用例在 CI 里从来没执行过,
而它覆盖的正是最容易写错的部分。现在没有现成实例就自建一个空壳走完 finalize,
跑完删掉。上个 PR 里靠手工验证的那批权限断言,现在 CI 每次都会跑。

前端部分另在真实浏览器里核对:viewer 身份下 9 个控件全部 disabled 且 title 正确、
协作者卡片隐藏、createdAt 正常渲染、三类控件透明度一致(0.35)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@SMNETSTUDIO
SMNETSTUDIO merged commit b418114 into main Aug 28, 2026
2 checks passed
@SMNETSTUDIO
SMNETSTUDIO deleted the fix/ux-wiring branch August 28, 2026 12:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant