Repository navigation
Commit b6f4c0e
feat(console): Allowed IP ranges shows the address the server judges before a save (#30540)
<!-- claude-projects-attribution: {"github_login":"os-sales"} -->
_Requested by **Sales** · [project
thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4Uu2w1nngmTV9xFM22hjNeNh5)_
domain:ui#4 · os-sales · session_01L6bhgVFhcnGrB1Wp8fLTwB
Fixes #30313
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:设置页「认证」里的「允许的 IP 段」字段下方,新增一行说明服务器判定的当前地址(读 `GET
/api/v1/auth/admin/client-address`),并按 `trustProxy`
说明这个地址是取自受信任代理的转发头,还是连接本身(不信任代理时,在代理之后看到的是代理地址);服务器无法确定地址时,说明此处无法保存非空列表。保存被拒时这行仍在,与拒绝信息并列。
- 为什么改:保存一个不含自己地址的列表会被拒绝,但以前管理员只有先被拒一次,才知道服务器认的是哪个地址。
- 风险与回滚:只加不改,只读一个已落地的接口;接口拒绝(非平台管理员
401/403)、老服务器没有该接口或网络失败时,什么都不显示,页面其余不变。新增 3 个文案键,10 种语言都已翻译。回滚即撤本 PR。
- 先看:`ui/apps/console/src/pages/settings/SettingsView.tsx`(读取与
`clientAddressNote`);`SettingsField.tsx` 新的 `note` 槽与
`aria-describedby`;`api.ts` 的 `getJudgedClientAddress` 形状检查。
- 两分钟验证:`pnpm --dir ui exec vitest run
apps/console/src/pages/settings/__tests__/SettingsView.clientAddress-30313.test.tsx`,预期
1 个文件、9 条全绿。
- 需要你做的:无,席位落地。
## What changes
Item 1 of the card. The Authentication settings page reads the caller's
address as the server judges it, `GET /api/v1/auth/admin/client-address`
(PR #30483: `{ clientAddress, trustProxy }` from the save check's own
`judgedClientAddress` call, platform-admin gated, `no-store`), and draws
one line under the Allowed IP ranges row:
- `trustProxy: false`: "Your address, as the server judges it: ADDRESS.
A list that leaves it out cannot be saved from here. This server does
not trust proxy headers, so behind a proxy this is the proxy's address."
- `trustProxy: true`: the address, "read from the forwarded headers of
the proxy this server trusts".
- `clientAddress: null`: "The server cannot establish your address, so a
non-empty list cannot be saved from here." (the route's documented
consequence of null).
The trustProxy half is spelled out because it changes what the admin
should do with the address: with trust off, behind a proxy every caller
is judged by the proxy's address.
- `api.ts`: `getJudgedClientAddress()`, through the module's existing
envelope unwrap; a body whose `clientAddress` is not a non-empty string
or null, or whose `trustProxy` is not a boolean, throws by name.
- `SettingsView.tsx`: reads it once per visit, only on the `auth`
namespace, and passes the sentence as `note` to the `allowed_ip_ranges`
row. Any failure (401, 403, a server without the route, a malformed
body, a network error) draws no note: no guessed address, and the page
is otherwise unchanged.
- `SettingsField.tsx`: an optional `note` drawn under the control after
the help text or the error, never in place of either; the control's
`aria-describedby` names the error and the note.
- `@object-ui/i18n`: `console.settingsView.clientAddressDirect`,
`clientAddressViaProxy`, `clientAddressUnknown` in all ten shipped packs
(en, zh, ja, ko, de, fr, es, pt, ru, ar).
Item 2 re-measured, not rebuilt: on this branch a refused save still
shows the server's sentence naming `constraint.clientAddress` in red
under the row (the generic field-error path), and the new line stays
beside it. Both items hold, so `Fixes #30313`.
Before / after, real stack (showcase `objectstack dev --seed-admin`, the
branch's console under vite, Playwright 1440 px; "before" is the same
page with the note's one line ablated live, held and restored with
`ablation-replace.mjs`):
| | zh-CN |
|:--|:--|
| before, nothing under the field says which address is judged |

|
| after, the judged address under the field |

|
| after, a refused save: the server's sentence, and the line beside it |

|
en-US after: 
## Tests
`ui/apps/console/src/pages/settings/__tests__/SettingsView.clientAddress-30313.test.tsx`,
the whole page over a stubbed `fetch` and the real `api.ts`, sentences
read from the shipped `en` pack: the note under the list and only there,
help text kept, `aria-describedby`; the trust-proxy and null sentences;
no note on 403, 401, a malformed body and a network failure; no read on
another namespace with a same-named key; the note kept beside a
`SETTINGS_VALIDATION` refusal carrying `constraint.clientAddress`, both
named by `aria-describedby`, one read per visit.
## Acceptance notes
- The note is hard-wired to `auth` / `allowed_ip_ranges` in the console,
as the route ledger entry describes ("the console's Authentication
settings page reads it beside the Allowed IP ranges field"); the
settings manifest has no key to declare it.
- `@object-ui/i18n` gains three keys and no export, so `Clause-②: no`
stands.
- The en pack is in the eager closure; three sentences are added to it.
The eager-closure budget is measured by CI's Bundle Analysis, not
locally.
## Verification
<details><summary>Unit tests (head 7dbb986, code identical at
da21d37)</summary>
`pnpm exec vitest run --maxWorkers=2 apps/console/src/pages/settings`
(in `ui/`, under `os-verify-lock`): `Test Files 35 passed (35)`, `Tests
297 passed (297)`, `VERDICT command-exit 0`.
</details>
<details><summary>Ablation</summary>
`ablation-replace.mjs` on `SettingsView.tsx`, anchor `?
clientAddressNote(judgedAddress, t)` to `? undefined` (anchor 1 to 0,
blob b5ed73e to d292d590): the new file goes `4 failed | 5 passed (9)`,
the four presence cases red, the five absence cases green as expected.
Restored: blob equals HEAD, `git diff HEAD` empty.
</details>
<details><summary>Live readings (real stack)</summary>
- `GET /api/v1/auth/admin/client-address` as the seeded admin: 200,
`Cache-Control: no-store`, `{ success: true, data: { clientAddress:
"127.0.0.1", trustProxy: false } }`.
- Row text before a save (en-US): label, help text, then "Your address,
as the server judges it: 127.0.0.1. A list that leaves it out cannot be
saved from here. This server does not trust proxy headers, so behind a
proxy this is the proxy's address." zh-CN:
"服务器判定的你的地址:127.0.0.1。不包含该地址的列表无法在此保存。此服务器不信任代理转发头,若部署在代理之后,这里显示的是代理的地址。"
- Save `203.0.113.0/24`: `PUT /api/settings/auth` 400
`SETTINGS_VALIDATION`, `details.fields[0]` field `allowed_ip_ranges`,
code `invalid_value`, `constraint.clientAddress` `127.0.0.1`; the row
shows that message in red, then the note; toast "1 setting needs
attention: Allowed IP ranges". Same in zh-CN with the server's zh
sentence.
- Before (note ablated): the row carries label and help text only, in
both locales; the refusal reads the same.
</details>
<details><summary>Derived gates (dispatch-gates --commands at 7dbb986,
15 paths; code identical at da21d37)</summary>
35 derived, 35 run, all exit 0; `--ran` reconciliation: 35 run, 0
NOT-MEASURED, 0 UNRUN.
```text
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-sdui-manifest.mjs :: exit 0
node scripts/check-sdui-manifest.mjs --self-test :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --dir ui exec vitest run scripts/ :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0
```
Also: `turbo run build --filter='@object-ui/console^...'` then
`@object-ui/console` `type-check` and `@object-ui/i18n` `type-check`:
`VERDICT command-exit 0`. `eslint` on the changed console files and the
en/zh packs: 0 errors (5 warnings, all on lines this PR does not touch).
Repo-wide lint and the full UI suites are CI's.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L6bhgVFhcnGrB1Wp8fLTwB)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent bbd6072 commit b6f4c0e
15 files changed
Lines changed: 410 additions & 4 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
114 | 114 | | |
115 | 115 | | |
116 | 116 | | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
117 | 125 | | |
118 | 126 | | |
119 | 127 | | |
| |||
395 | 403 | | |
396 | 404 | | |
397 | 405 | | |
398 | | - | |
| 406 | + | |
399 | 407 | | |
400 | 408 | | |
401 | 409 | | |
| |||
557 | 565 | | |
558 | 566 | | |
559 | 567 | | |
| 568 | + | |
560 | 569 | | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
561 | 573 | | |
562 | 574 | | |
563 | 575 | | |
| |||
579 | 591 | | |
580 | 592 | | |
581 | 593 | | |
582 | | - | |
| 594 | + | |
583 | 595 | | |
584 | | - | |
585 | | - | |
| 596 | + | |
| 597 | + | |
586 | 598 | | |
587 | 599 | | |
588 | 600 | | |
589 | 601 | | |
590 | 602 | | |
591 | 603 | | |
592 | 604 | | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
593 | 610 | | |
594 | 611 | | |
595 | 612 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
| 26 | + | |
25 | 27 | | |
26 | 28 | | |
27 | 29 | | |
| |||
48 | 50 | | |
49 | 51 | | |
50 | 52 | | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
51 | 79 | | |
52 | 80 | | |
53 | 81 | | |
| |||
331 | 359 | | |
332 | 360 | | |
333 | 361 | | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
334 | 393 | | |
335 | 394 | | |
336 | 395 | | |
| |||
661 | 720 | | |
662 | 721 | | |
663 | 722 | | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
664 | 730 | | |
665 | 731 | | |
666 | 732 | | |
| |||
0 commit comments