Repository navigation
Commit 115a43a
feat(console): clear the value saved beneath an env pin in Setup (#31039)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} -->
_Requested by **Tesla** · [project
thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoVUxqTWaM6rFCGZo1WDXBGb)_
Fixes #30665
Clause-②: yes (widening)
Metadata-protocol: none
## 维护者速读
- 改了什么:Setup
设置页里被环境变量锁定的字段,若锁下面还存着一个保存值(在本页写入的那一层),该行会说明"环境变量的值仍然生效",并提供"清除保存的值"按钮;确认后只对这一个键写
`null`。
- 为什么改:#30385 裁决 A 第 5 项:管理员用环境变量锁定(如
`OS_AUTH_ALLOWED_IP_RANGES`)来自救后,以前在 Setup
里删不掉锁下面的旧值,一旦移除变量旧值就回来;服务端的清除(#30448)和"锁下是否有存值"的读数(#30823)都已落地,这里补上界面。
- 风险与回滚:只动 Console 设置页与语言包四个新键;按钮只在"锁定 + 本层确有存值"时出现,确认后才写,页面草稿不受影响;回滚即
revert 本 PR。
-
先看:`ui/apps/console/src/pages/settings/SettingsField.tsx`(`storedBeneathPin`
与
`ClearSavedBeneathPin`)、`SettingsView.tsx`(`onClearSaved`)、`__tests__/SettingsView.clearBeneathPin-30665.test.tsx`。
- 两分钟验证:`pnpm --dir ui exec vitest run
apps/console/src/pages/settings/__tests__/SettingsView.clearBeneathPin-30665.test.tsx`,预期
7/7 通过。
- 需要你做的:无,席位落地。
## What changes
- **`SettingsField.tsx`:** `storedBeneathPin(resolved, writeScope)`
reads the server's `cascadeChain` alone: true when the row is env-pinned
(`locked`, `source: 'env'`) and an entry beneath the `env` entry at the
scope this page writes carries a non-null value (a secret's entry is the
read mask, which counts). On such a row, and only when the host passes
`onClearSaved`, the row draws one line ("A value is also saved here. The
environment value stays in force; ...") and a "Clear the saved value"
button. The button opens a confirm (title and action "Clear the saved
value", body saying the saved value will not come back when the variable
is removed and the environment value stays in force; Cancel is the
shared `actionConfirm.cancel`). Drawn under the control for every input
row and under the help text for a toggle row.
- **`SettingsView.tsx`:** `onClearSaved(key)` writes `PUT
/api/settings/:namespace` with `{ [key]: null }` for that key alone,
merges the read-back, toasts "Saved value cleared" and reads the
namespace again. The page draft is not touched: the row is read-only, so
the clear is not one of the page's edits. A refusal toasts the server's
sentence (the pin never refuses a clear, so `lockedByEnv` would misname
a row lock or a 403).
- **`@object-ui/i18n`:** four `console.settingsView` keys
(`savedBeneathPin`, `clearSaved`, `clearSavedConfirm`, `savedCleared`)
in all eleven packs (since #30902 merged: `zh-Hans`, `zh-Hant` and
`pt-BR` replace `zh` and `pt`). Wording clears the saved value and never
says delete.
- **`.changeset/30665-clear-saved-beneath-env-pin.md`:**
`@object-ui/console` and `@object-ui/i18n` patch.
What the server serves, measured on `origin/main` `41dc302496` before
the first edit:
- The namespace read (`getNamespace` → `resolveMany(..., beneathPins =
true)`) answers a pinned key `{ source: 'env', locked: true,
cascadeChain: [env entry (effective), ...stored cascade beneath] }` via
`withStoredBeneath`; the entries beneath lose `effective` and end with
the `default` entry. A pinned key whose env value is null keeps its one
`env` entry. A secret beneath the pin is `storedSecretPresence` on the
service and masked entry by entry by `redactSecretValues` on the route,
so the console sees the mask or `null`.
- The clear (`setMany`, the `[#30385]` block): `null` / `undefined` for
a key an in-force override pins passes the `SettingsLockedError` check
and is judged by the row locks and write predicates like any clear; it
writes at the key's registered scope (`spec.scope ?? manifest.scope ??
'tenant'`), the same derivation the page passes as `writeScope`. So the
control is gated on an entry at that scope: a value stored only at
another rung could not be removed by this clear, and no control is drawn
for it.
No new server field is needed; no file under `packages/**` changes.
## Tests
`ui/apps/console/src/pages/settings/__tests__/SettingsView.clearBeneathPin-30665.test.tsx`
(7 tests, whole page over a stubbed `fetch` and the real `../api`):
- the control appears on exactly the three pinned rows with a value
stored at the write scope (text, masked secret, toggle) and not on a
pinned row with nothing stored, a pinned row stored only above the write
scope, or an unpinned row;
- the line and the confirm say the environment value stays in force and
never say delete;
- confirm sends exactly `{"allowed_ip_ranges":null}`, toasts, and the
row stays locked and read-only with no control left after the server's
answer;
- a masked secret's clear sends `{"webhook_secret":null}`;
- Cancel sends nothing;
- an unsaved edit elsewhere stays in the draft and is not sent;
- a refused clear toasts the server's sentence and keeps the control.
## Verification
<details><summary>Local runs on head (see the report comment for the
final sha)</summary>
- `pnpm --dir ui exec vitest run apps/console/src/pages/settings/` →
Test Files 38 passed (38), Tests 304 passed (304).
- `pnpm --dir ui exec vitest run packages/i18n/` → Test Files 97 passed
(97), Tests 1521 passed, 13 skipped (key parity across all ten packs
included; on head fb666f8 after merging main: settings + i18n 136/136
files, 1949 passed, 13 skipped, eleven-pack parity included).
- Ablation (`node scripts/ablation-replace.mjs`, source is what the
console project resolves): the write-scope condition in
`storedBeneathPin` widened to any stored rung → 2 failed / 5 passed (the
offer test and the clear test, both seeing the extra control on the
tenant row stored only at global); restored, blob equal to HEAD, `git
diff HEAD` empty.
- Derived gates (`dispatch-gates.mjs --commands`): every line run with
stdin closed; exit codes in the report comment, reconciled with `--ran`.
</details>
## Acceptance notes
- The clear writes at once after its own confirm rather than through the
page's Save bar. A pinned row is read-only, so a pending clear would
have to draw a value the row never shows; the confirm is the row's own
question, like an `action_button`'s `confirmText`.
- The `i18n-locale-en` chunk gains four strings; its per-chunk ceiling
is unchanged. The eager-closure budget is weighed by CI's `Build & E2E
UI` job (`pnpm --dir ui run check:eager-closure`), not re-run locally.
- The first-run checklist renders `SettingsField` without
`onClearSaved`, so it shows no control.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L9qibzzesuj1TsQQD4hQjM)_
<!-- signed: domain:ui#3 · os-tesla · session_01L9qibxW6qraU2Z3mwupUGp
-->
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent d9d3eb7 commit 115a43a
15 files changed
Lines changed: 447 additions & 3 deletions
File tree
- .changeset
- ui
- apps/console/src/pages/settings
- __tests__
- packages/i18n/src/locales
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
128 | 128 | | |
129 | 129 | | |
130 | 130 | | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
131 | 188 | | |
132 | 189 | | |
133 | 190 | | |
| |||
519 | 576 | | |
520 | 577 | | |
521 | 578 | | |
522 | | - | |
523 | | - | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
524 | 593 | | |
525 | 594 | | |
526 | 595 | | |
| |||
691 | 760 | | |
692 | 761 | | |
693 | 762 | | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
694 | 768 | | |
695 | 769 | | |
696 | 770 | | |
| |||
723 | 797 | | |
724 | 798 | | |
725 | 799 | | |
| 800 | + | |
726 | 801 | | |
727 | 802 | | |
728 | 803 | | |
| |||
832 | 907 | | |
833 | 908 | | |
834 | 909 | | |
| 910 | + | |
835 | 911 | | |
836 | 912 | | |
837 | 913 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
| 8 | + | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
| |||
596 | 597 | | |
597 | 598 | | |
598 | 599 | | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
599 | 624 | | |
600 | 625 | | |
601 | 626 | | |
| |||
734 | 759 | | |
735 | 760 | | |
736 | 761 | | |
| 762 | + | |
737 | 763 | | |
738 | 764 | | |
739 | 765 | | |
| |||
0 commit comments