Skip to content

feat(console): Allowed IP ranges shows the address the server judges before a save - #30540

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-7phf7w
Nov 5, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-7phf7w

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Sales · project thread

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 before
after, the judged address under the field after
after, a refused save: the server's sentence, and the line beside it refused

en-US after: after en

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

Unit tests (head 7dbb986, code identical at da21d37)

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.

Ablation

ablation-replace.mjs on SettingsView.tsx, anchor ? clientAddressNote(judgedAddress, t) to ? undefined (anchor 1 to 0, blob b5ed73e9 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.

Live readings (real stack)
  • 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.
Derived gates (dispatch-gates --commands at 7dbb986, 15 paths; code identical at da21d37)

35 derived, 35 run, all exit 0; --ran reconciliation: 35 run, 0 NOT-MEASURED, 0 UNRUN.

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.


Generated by Claude Code

@github-actions github-actions Bot added the size/m label Nov 5, 2026
@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 5, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#4 · os-sales · session_01L6bhgVfPeFZkN1FP93imKU

维护者速读

  • 改了什么:设置页「认证」的「允许的 IP 段」字段下方多一行,写出服务器判定的当前地址(读已落地的 GET /api/v1/auth/admin/client-address),并按 trustProxy 说明这个地址取自受信任代理的转发头,还是连接本身(不信任代理时,在代理后面看到的是代理的地址);服务器无法确定地址时,说明此处无法保存非空列表。保存被拒时这行仍在,与服务器的拒绝信息并列。
  • 为什么改:保存一个不含自己地址的列表会被拒绝,但以前管理员要先被拒一次,才知道服务器认的是哪个地址(Allowed IP ranges console field: show the caller's current address before saving, and show the refusal's address when a save would lock the admin out #30313 第 1 项)。第 2 项(拒绝时在行内显示地址)main 上已成立,本 PR 复测仍成立,所以用 Fixes #30313。
  • 风险与回滚:只加不改,只读一个已落地的接口;非平台管理员被拒、老服务器没有这个接口、返回形状不对或网络失败时,什么都不显示,页面其余不变。新增 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,预期 9 passed。
  • 需要你做的:无,席位落地。
  • 席位意见:可合。

席位核对 — ACCEPT · head da21d37

  • 形态:15 文件 +410/−4(源码 SettingsView.tsx +66、SettingsField.tsx +21/−4、api.ts +44;10 个语言包各 3 键,en 带注释 +6;1 份新测试 +235;changeset @object-ui/console 与 @object-ui/i18n 各 patch)。check-governed-merges --pr 30540:NOT governed(0/15),414 行 ≤ 3000。Fixes #30313,draft,目标 main,标题无 !。证据截图提交 e1c74c22 已由 da21d372 移除,代码 head 7dbb9868 与 da21d372 之间 diff 为空。提交尾行为无模型名的一对。
  • 契约:Clause-② no,Metadata-protocol none,与认领 6660433935 一致:没有新导出;@object-ui/i18n 只加 3 个键;只读一条已落地的路由,没有碰 packages/**。
  • 席位逐行读:读取只在 auth 命名空间、每次进页一次,结果带上读取时的命名空间,切到别的命名空间不会画出来;任何失败(401、403、无此路由、形状不对、网络错)都不画,不猜地址。getJudgedClientAddress 走模块现有的信封解包,clientAddress 只接受非空字符串或 null、trustProxy 只接受布尔,否则按名抛错。note 画在帮助文字或错误之后、从不取代二者;aria-describedby 同时指向错误与说明,aria-invalid 仍只在有错误时设。说明行按 auth / allowed_ip_ranges 写死在控制台,与 Allowed IP ranges: no route serves the caller's address as the server resolves it, so the console cannot show an admin which address the lock-out check will judge before they save #30398 路由账本条目的描述一致,manifest 没有可声明它的键。
  • 前提实测:dev 报告 6661148449。真栈(showcase objectstack dev --seed-admin + 本分支 console vite + Playwright 1440):接口 200、no-store、{ clientAddress: "127.0.0.1", trustProxy: false };改前行内只有标签与帮助文字,改后多出判定地址那行(en-US、zh-CN);保存 203.0.113.0/24 得 400 SETTINGS_VALIDATION、constraint.clientAddress 127.0.0.1,红字拒绝信息与说明行并列。消融(说明换成 undefined)新文件 4 红 5 绿,还原后 blob 与 HEAD 相同。settings 目录 35 文件 297 例绿;派生门禁 35 条全部 exit 0,0 NOT-MEASURED;console 与 i18n type-check exit 0。
  • 门禁 check-runs:head 上 55 个,44 success,11 skipped(ui-only diff 的预期跳过),0 红,Build & E2E UI、Live E2E UI、Test UI 16 片与各 Type Check 均绿。mergeable_state clean。
  • 接受的偏差:en 包在首屏闭包内,多 3 句:PR 正文说体积由 CI 的 Bundle Analysis 量,但本 head 的 check-runs 里没有这个名字的检查,dev 本地也没量,所以体积变化没有读数;三句文案量级很小,本席接受。ui/packages/i18n/src/locales/*.ts 与 i18n phase 2 (Console UI): ui/packages/i18n reads PLATFORM_LOCALES, adds zh-Hant, and moves pack keys to table tags #30511(domain:ui#5)同目录:本 PR 只加键、不动 i18n.ts 与包结构,后落地者合 main 即可。
  • 范围外去向:无。Allowed IP ranges: no route serves the caller's address as the server resolves it, so the console cannot show an admin which address the lock-out check will judge before they save #30398 落地评论里的跟进(authentication.mdx 在 ui 显示地址后补一行)归 domain:server#2 席位,本席落地后知会该席位。
  • 落地条款:中继 pr_ready + automerge_enable。合并后由 Fixes 关卡,席位同一笔摘掉 pm:dispatched 并留落地评论。

Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allowed IP ranges console field: show the caller's current address before saving, and show the refusal's address when a save would lock the admin out

1 participant