fix(online-update): stop polling update progress without permission - #904
Open
kittors wants to merge 1 commit into
Open
fix(online-update): stop polling update progress without permission#904kittors wants to merge 1 commit into
kittors wants to merge 1 commit into
Conversation
An idle panel tab issued ~33 requests a minute, forever, against endpoints it often had no right to call. Measured on a live instance: GET /update/progress every ~2s and an /update/events reconnect every ~3s, from every open tab, on a deployment with no updater sidecar where each of those fails. The backend audited each refusal, which is how the governance page ended up holding 28,435 rows of one operator being told no. Three causes, three fixes: - The provider subscribed every authenticated operator. It is now gated on system.status.read, the permission the update endpoints actually require and the one the system route already declares. - The transport treated "no run in flight" the same as "mid-update", so the aggressive cadence the container restart needs ran around the clock. Cadence now follows what is happening: unchanged while a run is in flight (2s polls, sub-second reconnects), 30s polls backing off to 5m and 3-30s reconnects when idle. - A 403 is not transient. The transport now stops instead of retrying, and resets when the last subscriber leaves so a new session is not stuck with it. The audit page also rendered "denied" and "failed" as the same red badge, hiding the distinction that matters most there: refused is not the same as errored. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
线上抓包(CDP network + 服务端
journalctl)显示,一个空闲面板标签页每分钟发约 33 个请求,永不停止:GET /v0/management/update/progress每 ~2s(本部署没有 updater sidecar,后端 502,前端 5s 超时中断)GET /v0/management/update/events每 ~3s 重连(返回 2xx 但不投递事件,link永远停在reconnecting,于是轮询永远不停)对没有
system.status.read的租户管理员,每次 progress 请求都是 403。后端逐条写审计日志,这就是audit-logs页面里 28435 条update · progress的来源。改动
权限门控:
OnlineUpdateProvider从"所有已登录用户"改为额外要求can("system.status.read")—— 与后端/update/*只读所需权限、以及systemRoute.requiredPermission完全一致。用不了这个功能的人不再去轮询它。自适应节奏:以"是否有更新在执行中"(
isRunningProgress(latest),或 apply 之后 5 分钟的 active hint)为准。更新过程中的行为与原来完全一致 —— 容器重启期间的快速重连正是它存在的理由;空闲标签页从 ~33 次/分钟降到 ~4 次/分钟。
403 直接停机:权限被拒不是暂时故障,重试改变不了结果,只会继续灌审计日志。订阅者全部离开时重置,重新登录不会继承这个状态。
审计结果三态:
denied与failed此前渲染成同一个红色"失败"徽章,掩盖了这个页面上最该区分的东西。现在success绿 /denied琥珀 /failed红,新增identity_admin.result_denied(zh-CN / en / ru)。后端配套 PR:kittors/CliRelay#863(审计写入策略 + 保留策略 + 历史清理),建议后端先合并部署。
验证
本地完整
./scripts/ci-pr.sh通过:lint、design:check、全量 vitest、build、bundle:diff、12 条 @critical e2e。测试改动:
reconnects quickly after the stream drops during a run:原用例保留原意(更新中必须快速重连),补上"先确立有 run 在执行"的前提。backs off instead of hammering while nothing is in flight:空闲时 1 秒内最多 1 次重连、轮询只有订阅那一次。stops entirely once the server refuses the operator:403 之后不再有任何请求。distinguishes a refused result from a failed one:审计页 denied 徽章。🤖 Generated with Claude Code