Skip to content

feat(#10436): serve WebUI settings through the domain app config - #10438

Open
ironAiken2 wants to merge 36 commits into
mainfrom
feat/gh-10436-webui-settings-app-config
Open

ironAiken2 wants to merge 36 commits into
mainfrom
feat/gh-10436-webui-settings-app-config

Conversation

@ironAiken2

@ironAiken2 ironAiken2 commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Resolves #10436

Summary

The layout is recorded as ADR 0018 (docs/adr/0018-webui-settings-in-the-app-config-settings-key.md).

Settings that today come only from the static config.toml can now be changed per domain at runtime through the server-side app config, with the file as the fallback. The layout and precedence follow the decision recorded on the issue:

  • One flat settings document per scope. domainConfig.settings overrides the file for 43 keys after login; publicConfigByDomain.<domain>.settings does so for the ten keys read before sign-in: the six login-page keys, autoLogout, pluginLogin / pluginPage (plugin.login / plugin.page, edited as folder / file rows; the plugin scripts are imported at bootstrap, so PluginLoader now reads the effective login config instead of the raw file), and debug, which the diagnostics surfaces (i18n key markers, error details) read at call time, so a superadmin can turn them on per domain without a webserver restart. Both now have one home each: the per-user Auto logout toggle in user settings is removed (with its i18n keys; the manual is left to the automated docs PR), and the Lit-era globalThis.backendaiwebui.debug copy is gone — its five readers take debug from loginConfigState. Key names are the config.toml names (menu.blocklist → menuBlocklist, menu.inactivelist → menuInactivelist), comma lists become arrays.
  • config.toml is the fallback per key, as theme.json is for appearance: a key no document carries keeps the file value, and nothing is seeded. An untouched deployment behaves exactly as before.
  • The anonymous publicConfigByDomain read is one shared, cached request per page load (react/src/helper/publicAppConfig.ts) that serves the appearance document and the settings slice; when the app joins another webserver it looks the slice up under that server's apiEndpoint. After login connectViaGQL reads domainConfig once through the plain client, merges it, and stores the effective config before applyConfigToClient, so the ~40 _config consumers need no change.
  • A new System Settings page under the admin System group (/admin/system-settings, access: superadmin; domain admins are out of scope) edits the document with the shared SettingList: each row's default is the file value, the "changed" dot and the reset icon work against it, only rows that differ are saved, and Apply confirms, writes both scopes and reloads the page, as Branding does. The toolbar's Reset deletes the saved values, so it opens a BAIDeleteConfirmModal that asks for the Reset word to be typed, then applies at once; a per-row reset only edits the draft. menuBlocklist / menuInactivelist ignore system-settings, so the page cannot be hidden by the lists it edits.

Dropped while reviewing the page row by row: eduAppNamePrefix (the edu app launcher no longer strips a prefix; the key leaves the parser and config.toml.sample, the webserver template follows in BA-8735) and the loginAttemptLimit / loginBlockTime settings (the webserver limiter covers them; removing the browser-side limiter itself is on #10664). The SSO vendors row is a SAML / OpenID checkbox list, number rows show their unit as an input suffix, and the groups read Sign-in, Session, App launcher, Images, Data, Menu, Downloads, Account, Diagnostics, Plugins.

Carried along because the settings table exposed them: the ATOM+ and Hyperaccel LPU caps were never parsed (the webserver renders ATOM+ as maxATOMPlUS…, both spellings are read now); storageWarningThreshold flows through LoginConfigState so the diagnostics page sees an override.

Not in this PR, left as the issue lists: userConfig.settings (personal overrides, the sibling issue under #10207), connectionMode (a webserver forces SESSION whenever apiEndpoint is set, and the Electron app reads the user's stored choice first, so a document value would change nothing), dropping the dead keys from config.toml.sample (#10664) and from the webserver template (BA-8735).

Config: general.apiDomainName = "default"
(The webserver sets apiDomainName itself; without it the pre-login slice is not fetched and the login-page keys keep the file values.)

Verification

  • VERIFY_TESTS=1 bash scripts/verify.sh → === ALL PASS ===
  • New unit tests: react/src/helper/webuiSettings.test.ts (definition table, scope/type-guarded merge, differs-from-file document), react/src/helper/publicAppConfig.test.ts (target resolution, one request per page load, failure paths). customThemeConfig.test.ts and loginSessionAuth.test.ts updated for the shared fetch and the domain read.

Review checklist

  • 1. Sign in as superadmin, open System → System Settings, and confirm every group lists the config.toml value as the default.
  • 2. Change "Hide agents" and a numeric cap, press Apply, confirm the page reloads and the sidebar/session launcher reflect the new values.
  • 3. Reset one changed row, Apply, and confirm that value returns to config.toml; then press Reset in the toolbar, confirm, and check that the page reloads with every row back on config.toml without pressing Apply.
  • 4. In the Login page group change the SSO realm name, Apply, sign out, and confirm the login page shows it before sign-in.

Walkthrough

Not shown in the walkthrough:

  • The merge itself: publicConfigByDomain.<domain>.settings is applied to the login config at bootstrap and domainConfig.settings after login (useWebUIConfig.ts, loginSessionAuth.ts, webuiSettings.ts); only its effect is visible, through the values the page and the app show after Apply.
  • The login page with a saved public value: an ssoRealmName saved in the Login page group shows as the SSO button label before sign-in; this server has no SSO vendor configured, so the label has nothing to attach to.
  • Parsing fixes carried along: eduAppNamePrefix now reaches backendaiclient._config; maxATOMPLUSDevicesPerContainer / maxHyperaccelLPUDevicesPerContainer are parsed (both ATOM+ spellings); storageWarningThreshold flows through LoginConfigState.
  • Unit tests: webuiSettings.test.ts, publicAppConfig.test.ts, and the updated customThemeConfig.test.ts / loginSessionAuth.test.ts.

@ironAiken2

ironAiken2 commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Coverage Report for react-coverage (./react)

Status Category Percentage Covered / Total
🔵 Lines 23.67% 9462 / 39972
🔵 Statements 20.96% 12435 / 59327
🔵 Functions 19.96% 1551 / 7769
🔵 Branches 16.79% 7126 / 42433
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
react/src/routes.tsx 19.04% 2.5% 0% 23.52% 55-56, 58-59, 61, 62, 65-66, 67, 69-70, 71, 72, 74-75, 77-78, 79, 81-82, 84-85, 87-88, 90-91, 94-95, 97-98, 100-101, 102, 104-105, 106, 108, 109, 110, 111, 113-114, 116-117, 118, 120, 122, 123, 125-126, 128, 129, 130, 132-133, 134, 136-137, 139-140, 142-143, 145-146, 148-149, 151-152, 154-155, 157-158, 160-161, 164-165, 170-174, 195-230, 309-1382, 1422-1433, 1437-1439, 1456-1463, 1583-1642
react/src/components/BAIErrorBoundary.tsx 21.05% 0% 7.69% 21.05% 44-67, 95-214, 304-320
react/src/components/DefaultProviders.tsx 25.97% 13.33% 5.26% 27.77% 81, 92-105, 149-167, 181-211, 229-239, 244-266, 272-339
react/src/components/EduAppLauncher.tsx 0% 0% 0% 0% 107-1114
react/src/components/ImportImageModal.tsx 93.82% 84.77% 90.56% 93.36% 163-167, 262, 272-273, 331, 430-431, 508-511, 581, 654-657
react/src/components/LoginView.tsx 0.52% 0% 0% 0.54% 104-111, 124-1491
react/src/components/PluginLoader.tsx 1.25% 0% 0% 1.25% 46-246
react/src/components/PluginPathInput.tsx 0% 0% 0% 0% 10-63
react/src/components/SettingItem.tsx 0% 0% 0% 0% 84-124
react/src/components/UserSettingsGeneralPane.tsx 0% 0% 0% 0% 37-488
react/src/components/WebUISettingLabels.ts 0% 0% 0% 0% 14-20
react/src/components/WebUISettingList.tsx 0% 0% 0% 0% 53-291
react/src/components/ComputeSessionNodeItems/AppLauncherModal.tsx 1.55% 0% 0% 2.43% 31, 56-491
react/src/diagnostics/rules/configRules.ts 100% 100% 100% 100%
react/src/helper/customThemeConfig.ts 83.83% 78.57% 95.45% 85.41% 239, 242, 273-276, 285-286, 289-290, 295-296, 310-316, 340
react/src/helper/loginConfig.ts 93.08% 66.12% 45.45% 94.26% 212, 323, 409-410, 604, 619, 627, 646-647, 666, 695-696, 699-700
react/src/helper/loginSessionAuth.ts 61.97% 52.21% 60% 62.77% 107-118, 155, 157, 169-187, 197-198, 205, 290, 403-506, 523-526, 530, 533, 588-615
react/src/helper/pathBuilder.ts 100% 100% 100% 100%
react/src/helper/pluginPath.ts 100% 100% 100% 100%
react/src/helper/publicAppConfig.ts 97.43% 100% 100% 97.22% 46
react/src/helper/webuiSettings.ts 100% 100% 100% 100%
react/src/hooks/index.tsx 64.4% 51.2% 50.98% 59.01% 65-68, 174-186, 197-198, 206-218, 225-235, 418, 488-491, 506-509, 517-545, 553, 563-578, 598-612, 620-649, 653-681
react/src/hooks/useAppConfig.ts 47.61% 30.23% 43.75% 55.12% 156-162, 173-179, 191-201, 260-266, 302-305, 160-177
react/src/hooks/useBAISetting.tsx 70.17% 45.45% 72.72% 71.42% 110, 119, 126, 134, 136, 140, 149, 178, 190, 199-204, 209-218
react/src/hooks/useBackendAIAppLauncher.tsx 15.22% 18.88% 24.32% 15.1% 34-1063, 1117, 1186, 1210, 1259-1265, 1300, 1306-1309, 1321, 1324-1326
react/src/hooks/usePainKiller.tsx 75% 46.66% 100% 75% 72-80, 84, 87, 91
react/src/hooks/useScanImage.ts 94.28% 70.37% 100% 96.55% 54, 93
react/src/hooks/useStorageProxyDiagnostics.ts 0% 0% 0% 0% 21-129
react/src/hooks/useWebServerConfigDiagnostics.ts 0% 0% 0% 0% 35-242
react/src/hooks/useWebUIConfig.ts 30.7% 22.64% 27.27% 32.71% 212-213, 231-379, 398-447
react/src/hooks/useWebUIMenuItems.tsx 6.25% 4.2% 6.89% 7.27% 196, 249-881
react/src/pages/WebUISettingsPage.tsx 0% 0% 0% 0% 18-41
Generated in workflow #2850 for commit d8ec017 by the Vitest Coverage Report Action

@ironAiken2

Copy link
Copy Markdown
Contributor Author

📍 Walkthrough · 4 stops · 45f05ba — Open the walkthrough
Served by gh-10436-pr10438-serve-webui-settings.

  1. Webui settings › webui-settings-group-login › div "Login pageRead before sign-in, so only a superadmin can change t" — added section
    A new WebUI Settings page was added under the admin System menu. It lists the config.toml settings an admin can now change per domain; the first group, Login page, holds the six keys read before sign-in and is shown to superadmins only.
    check: The page opens with eight groups in the left rail and the "Login page" group first, with a note that only a superadmin can change it.
    code: react/src/components/WebUISettingList.tsx:236
  2. Webui settings › webui-setting-hideAgents › div "Hide agentsHide agentsHide the Agent Summary menu and agent deta" — added checkbox
    Each boolean setting is a checkbox whose default is the value in config.toml. Toggling it marks the row as changed; the reset icon returns it to the file value, which also drops it from the saved document.
    check: The "Hide agents" row shows the config.toml value unchecked; after a click the row shows the "changed from default" dot and a reset icon.
    code: react/src/components/WebUISettingList.tsx:170
  3. Webui settings › webui-setting-maxCPUCoresPerContainer › div "Maximum CPU cores per containerUpper bound of the CPU slider in " — added input
    Number and text settings are inputs that commit on Enter or blur. The resource caps keep their config.toml names so the session launcher's per-accelerator lookup keeps working.
    check: The "Maximum CPU cores per container" row shows a number input holding the config.toml value; typing 128 and pressing Enter marks the row as changed.
    code: react/src/components/WebUISettingList.tsx:179
  4. Webui settings › page-webui-settings › button "Apply" — added button
    Apply saves only the rows that differ from config.toml into the domain app config (and the public one for the Login page group), after a confirmation, then reloads the page so the new values reach every consumer.
    check: Apply stays disabled until a row differs from its default; pressing it shows a confirmation, and after confirming the page reloads with the changed values in effect.
    code: react/src/components/WebUISettingList.tsx:130

@ironAiken2
ironAiken2 force-pushed the feat/gh-10436-webui-settings-app-config branch from c3f127a to d400d89 Compare October 27, 2026 07:55
@ironAiken2
ironAiken2 added this pull request to stack #10681 October 29, 2026 05:29
@ironAiken2
ironAiken2 marked this pull request as ready for review October 29, 2026 07:35
ironAiken2 added a commit that referenced this pull request Oct 30, 2026
…stem Settings page

One sequential spec exercises #10438 end to end against the live cluster:
the page (groups, the 56-row inventory, file-value defaults, changed marker
and reset per row, toolbar reset, Apply with its confirmation and reload,
the public-scope write, the leave guard, the domain-admin view), precedence
(a domain document overrides every domain key after sign-in, wrong-typed
and absent keys fall back to the file, the other scope's keys are ignored,
the public document overrides the login page before sign-in), the visible
effects (hideAgents, menuBlocklist, the CPU cap in the launcher, the
Downloads menu, Models on the Data page, loginAttemptLimit, plugin paths)
and the cleanup contract.

`webui-settings-util.ts` mirrors WEBUI_SETTING_DEFINITIONS, drives the
`settings` sub-key of both scopes through scopedUpsertAppConfigFragments
with snapshot/restore, and wraps the page. test-util gains fetchConfigToml
(extracted from modifyConfigToml) and getSideNavItem (moved from
page-access-control.spec.ts).

The domain admin's Apply is not covered: on the dev cluster that account
lacks app_config_fragment permissions at DOMAIN scope, so the server
refuses the write.
ironAiken2 added a commit that referenced this pull request Oct 30, 2026
#10438 moved the page to /admin/system-settings, dropped loginAttemptLimit,
loginBlockTime and eduAppNamePrefix from the definition table (53 rows:
10 public, 43 domain), moved storageWarningThreshold into the Diagnostics
group and showNonInstalledImages into the Session launcher group, and made
the toolbar Reset save and reload as soon as it is confirmed.

- The row table mirrors the new definitions; the domain-admin test derives
  the hidden groups from the rows (Sign-in and Plugins, no longer
  Diagnostics, which now holds a domain key).
- The toolbar Reset test waits for the reload.
- The loginAttemptLimit effect test is removed: the key is config.toml only.
- The SFTP step closes its error dialog by its footer button so the file
  browser step starts from the explorer.

@agatha197 agatha197 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified the code-review findings against the head; these five block merge.

Comment thread react/src/helper/loginSessionAuth.ts Resolved
Comment thread react/src/helper/webuiSettings.ts Resolved
Comment thread react/src/helper/webuiSettings.ts Resolved
@ironAiken2
ironAiken2 force-pushed the feat/gh-10436-webui-settings-app-config branch from c3d8ba8 to f069db9 Compare October 30, 2026 10:10
@ironAiken2
ironAiken2 requested a review from agatha197 October 30, 2026 10:40
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

PR Analysis Report

📖 Docs Preview

View the docs preview for this PR

GitHub Pages may take up to a minute to hydrate after the deploy.

English (en) — 1 changed page(s) · −2

Page Changes
User Settings −2

日本語 (ja) — 1 changed page(s) · −2

Page Changes
ユーザー設定 −2

한국어 (ko) — 1 changed page(s) · −2

Page Changes
사용자 설정 −2

ภาษาไทย (th) — 1 changed page(s) · −2

Page Changes
ตั้งค่าผู้ใช้ −2

Generated by the PR Preview workflow · Docs · CI run

…ice sign-in

The switch only affects the /interactive-login card an external service opens to sign in with a Backend.AI account, not SSO into Backend.AI itself; the title now says so in every locale.
The SAML / OpenID choice is a MultiSelector instead of a checkbox list, matching how other multi-value settings are picked.
The row is titled Allow SSO sign-in, and its description says the buttons only work when the manager's SSO plugin is installed, since the WebUI cannot tell on its own.
…eckbox rows

SettingItem rendered a custom row's description bare, so it inherited the 16px body size while checkbox and select rows wrapped theirs in Text at 14px. The custom branch now wraps it the same way; the user settings page's custom rows follow.
… with auto sign-out

The realm name row is titled after what it does, the SSO OpenID button label, in every locale; autoLogout moves to the top of the Sign-in group.
SettingList's Reset already asks for confirmation, so confirming it now writes both scopes with an empty document and reloads instead of only clearing the draft. A per-row reset still edits the draft and waits for Apply.
Both plugin rows show the loader's fixed root in a disabled box with the relative path beside it, instead of folder and file rows that hid where the files are served from. The login row takes one path and rejects spaces and commas; the page row takes a comma-separated list. An invalid entry shows the error state and is not committed. PluginPathListInput and its three strings go away.
…eparator between the boxes

The disabled box reads /dist/plugins and a / sits between it and the path box.
The route path and menu key follow the page's name; the component and test ids keep their file names.
…oml parse pure

- loadConfigFromWebServer resolved the public-config target from the merged
  config, whose apiEndpoint is this page's own (the webserver's was
  stripped), so a fresh Electron install never read the public settings and
  a server switch read the old server's slice. It now targets apiEndpoint.
- refreshConfigFromToml no longer writes globalThis.backendaiwebui.debug;
  rendering System Settings undid the public debug override until reload.
  The reload path in useUpdateRawConfig syncs it explicitly instead.
…a typed word

- menuBlocklist / menuInactivelist ignore system-settings, so an admin cannot
  hide the page that edits both lists.
- The toolbar Reset deletes the saved settings documents, so it opens
  BAIDeleteConfirmModal with requireConfirmInput (the localized Reset
  word) instead of SettingList's generic reset prompt; new strings
  webuiSettings.ResetAllSettings / ResetAllSettingsDesc in every locale.
Where the WebUI settings live (the flat `settings` key per scope), which
keys are public, how documents merge over config.toml, which keys stay in
the file, and who may edit them.
…pp config

Auto logout had two homes: the user's own toggle in user settings
(localStorage `backendaiwebui.settings.user.auto_logout`) and the deployment
default, which this PR already serves through the public app config `settings`
with config.toml as the fallback. The user toggle overrode the admin's value,
so an admin change never reached anyone who had once touched the toggle.

The user-settings item, its `auto_logout` setting type, resolveAutoLogout and
the two `userSettings.*AutoLogout` i18n keys are removed; autoLogoutState now
takes `autoLogout` straight from the merged login config. A value left in
localStorage is no longer read. The manual's "Auto logout" section under user
settings is removed in en/ko/ja/th, and the search-palette tests that used the
setting as their sample item now use Automatic update check / My keypair
information.
`debug` already lives on loginConfigState (config.toml, overridden by the
public app config `settings`), but its readers still took the Lit-era copy at
`globalThis.backendaiwebui.debug`, so syncDebugFlag rewrote that copy on every
path that settled the config. The five readers now read the atom:
BAIErrorBoundary, AppLauncherModal, usePainKiller and useBackendAIAppLauncher
through useLoginConfig(), and the i18n key-marker postProcessor, which runs
outside React, through jotaiStore.get(loginConfigState). The global and
syncDebugFlag are removed.

With nothing left to throw on a missing global, `relieve` no longer needs the
try/catch guards ImportImageModal and useScanImage carried for FR-3953; the
regression test keeps asserting that the import button is not stranded. The
uncalled useUpdateRawConfig is removed.
The new maxATOMPLUSDevicesPerContainer / maxHyperaccelLPUDevicesPerContainer
parsing defaulted both to 8, so they always reached backendaiclient._config and
useResourceLimitAndRemaining no longer fell back to maxCUDADevicesPerContainer
for those devices: a deployment with a CUDA cap of 16 and no ATOM+/LPU key was
silently capped at 8. Both are now unset when the key is absent, a placeholder
or not a number, and _config only receives them when set, which is main's
behaviour. The keys stay in the defaults (as undefined) so the settings table
still maps onto them.
The design diagram and the Decision still described syncDebugFlag and the globalThis.backendaiwebui.debug copy that 51cfd67 removed; the merged value now lives only in loginConfigState. The Consequences line also claimed tsc rejects a definition that does not match LoginConfigState, but def() casts the key, so the guard is webuiSettings.test.ts.
Apply writes the domain document, then the public one, with no rollback. A failure only showed the error toast, so after a domain write succeeded and the public one failed, the admin could not tell that part of the change was already live. The failure now opens an error dialog that lists the settings that were not saved (the changed domain and public rows when the first write fails, only the public rows when the second does), says the rest was saved and applies after a reload, and asks to apply again. Four webuiSettings keys added in every locale.
loadConfigFromWebServer merged the server's config.toml onto rawConfigState, which already held the previous endpoint's server config, so signing in to a second endpoint kept every key the first server set and the second did not. It now merges onto the bundled config.toml (fetchAppConfigOnce), and the public settings slice is applied to that result as before.

It also stops setting loginPluginState from the joined server. The Electron login screen is drawn before any endpoint is known (first launch, and right after a logout, which opens the panel before this merge), so a server's login plugin could only ever apply on some launches; the bundled plugin.login stays in charge, as on main.
The plugin diagnostic validated plugin.page with a single-segment name pattern, so a folder path the loader accepts (vendor/dashboard) was reported invalid, and it read the file even when the public settings document overrode the value. It also expected plugin.login to be a name.js filename, which the loader (resolvePluginScriptUrl, and main's sanitiser before it) refuses, since it appends .js itself.

checkPluginConfiguration now takes the effective pluginPages and loginPlugin from loginConfigState and flags whatever isValidPluginPath rejects, page entries and the login plugin alike, under the existing InvalidPluginNames result. The plugin.sidebar check goes: nothing reads that key. The three InvalidPluginFilename keys are removed from every locale.
…lugin error

The free-text and list rows, and both PluginPathInput boxes, rendered UncontrolledInput without a label, so every one took the catalog fallback name "Select"; PluginPathInput's aria-label never reached the input, since UncontrolledInput takes its accessible name from label only. Each now gets its row title as a hidden label and a webui-setting-<key>-input test id.

PluginPathInput kept its error state until the next commit, so a row reset (or any other change to the value) left the red border on a valid value. The error is now tied to the value it was raised against and clears when that value changes.
connectViaGQL read domainConfig.settings with a hand-written query string
through client.query, after loginSessionAuthMyUserQuery had returned:
relay-compiler never checked it against data/schema.graphql, so a schema change
would only show as settings silently falling back, and every sign-in paid one
more sequential round trip.

myAppConfigs(configNames: ["domainConfig"]) now rides in
loginSessionAuthMyUserQuery with @catch(to: NULL), so a failed read still
leaves the base config in place instead of failing the sign-in.
fetchDomainWebUISettings becomes readDomainWebUISettings, which picks the
settings sub-key out of the returned rows.
Domain admins are out of scope for this work. The menu item now shows only to
superadmins (like Branding and Information), the route requires
`access: 'superadmin'`, and the page renders one list that reads and writes
both the public and the domain document. WebUISettingList loses its
canEditPublic switch and the domain-admin variant that hid the public rows.
ADR 0017's "Who sees what" / "Who may write" lines follow.
Restores the four user_settings.md files to the branch point: removing the Auto logout section belongs to the automated docs update, not to this PR.
@ironAiken2
ironAiken2 force-pushed the feat/gh-10436-webui-settings-app-config branch from 867821e to d8ec017 Compare November 3, 2026 06:40
ironAiken2 added a commit that referenced this pull request Nov 3, 2026
…stem Settings page

One sequential spec exercises #10438 end to end against the live cluster:
the page (groups, the 56-row inventory, file-value defaults, changed marker
and reset per row, toolbar reset, Apply with its confirmation and reload,
the public-scope write, the leave guard, the domain-admin view), precedence
(a domain document overrides every domain key after sign-in, wrong-typed
and absent keys fall back to the file, the other scope's keys are ignored,
the public document overrides the login page before sign-in), the visible
effects (hideAgents, menuBlocklist, the CPU cap in the launcher, the
Downloads menu, Models on the Data page, loginAttemptLimit, plugin paths)
and the cleanup contract.

`webui-settings-util.ts` mirrors WEBUI_SETTING_DEFINITIONS, drives the
`settings` sub-key of both scopes through scopedUpsertAppConfigFragments
with snapshot/restore, and wraps the page. test-util gains fetchConfigToml
(extracted from modifyConfigToml) and getSideNavItem (moved from
page-access-control.spec.ts).

The domain admin's Apply is not covered: on the dev cluster that account
lacks app_config_fragment permissions at DOMAIN scope, so the server
refuses the write.
ironAiken2 added a commit that referenced this pull request Nov 3, 2026
loginBlockTime and eduAppNamePrefix from the definition table (53 rows:
10 public, 43 domain), moved storageWarningThreshold into the Diagnostics
group and showNonInstalledImages into the Session launcher group, and made
the toolbar Reset save and reload as soon as it is confirmed.

- The row table mirrors the new definitions; the domain-admin test derives
  the hidden groups from the rows (Sign-in and Plugins, no longer
  Diagnostics, which now holds a domain key).
- The toolbar Reset test waits for the reload.
- The loginAttemptLimit effect test is removed: the key is config.toml only.
- The SFTP step closes its error dialog by its footer button so the file
  browser step starts from the explorer.
ironAiken2 added a commit that referenced this pull request Nov 3, 2026
#10438 now routes System Settings for superadmins only, keeps it out of
menuBlocklist / menuInactivelist, confirms the toolbar Reset with a typed
word in a delete-confirm modal, reads `debug` from loginConfigState only
(no more `globalThis.backendaiwebui.debug`) and names each row's input with a
`webui-setting-<key>-input` test id. main meanwhile scoped a team project's
Data page to that project's folders.

- The domain-admin test asserts the Unauthorized Access screen and no menu
  item instead of a reduced page.
- The toolbar Reset test saves values first, types the confirmation word,
  and checks both documents are deleted.
- `debug` is asserted through the i18n key markers it turns on.
- The blocklist and inactivelist tests include `system-settings` and check
  it stays visible and enabled.
- The row inventory ignores the `-input` test ids.
- The folder explorer test creates a folder in the current project.
@ironAiken2

Copy link
Copy Markdown
Contributor Author

Heads-up on ADR numbering: #10813 (consolidate .specs into ADRs, #10802) adds ADR 0018–0023, and the ADR files on this PR use a number in that range. Git will not flag the duplicate because the file names differ. Whichever PR lands second takes the next free number (0024+), and updates its docs/ARCHITECTURE.md row and any ADR NNNN pointers. #10813 also makes ADRs English-only (ADR 0018), so a Korean-body ADR here would need translating if it lands after #10813.

ironAiken2 added a commit that referenced this pull request Nov 3, 2026
…-settings

#10438 now serves System Settings at /admin/system-settings with the menu
key system-settings, so the Configurations redirect, its help anchor, the
search-index tab override and the e2e expectations point there.

This branch has not been deployed

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Serve WebUI config.toml settings through the domain app config

2 participants