You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ji...@apache.org on 2026/08/13 08:21:46 UTC

(ambari) branch frontend-refactor updated: AMBARI-26623: Complete React authentication and application shell parity (#4165)

This is an automated email from the ASF dual-hosted git repository.

JiaLiangC pushed a commit to branch frontend-refactor
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/frontend-refactor by this push:
     new 2c1f3e6ce6 AMBARI-26623: Complete React authentication and application shell parity (#4165)
2c1f3e6ce6 is described below

commit 2c1f3e6ce621b92d89dd7e24a6ac7ca1cee4f144
Author: jialiang <ji...@gmail.com>
AuthorDate: Thu Aug 13 16:21:40 2026 +0800

    AMBARI-26623: Complete React authentication and application shell parity (#4165)
    
    * AMBARI-26623: Complete React authentication and application shell parity
    
    * AMBARI-26623: Remove trailing blank line
---
 ambari-web/latest/package-lock.json                | 1579 +++++++++++++++++---
 ambari-web/latest/package.json                     |    6 +-
 ambari-web/latest/src/AmbariAboutModal.tsx         |   88 +-
 ambari-web/latest/src/App.tsx                      |   67 +-
 ambari-web/latest/src/AppLoader.tsx                |  244 +--
 ambari-web/latest/src/InactivityTimeout.test.ts    |   48 +
 ambari-web/latest/src/InactivityTimeout.tsx        |  183 ++-
 ambari-web/latest/src/Utils/adminViewRedirect.ts   |    5 +-
 ambari-web/latest/src/Utils/authNavigation.test.ts |   77 +
 ambari-web/latest/src/Utils/authNavigation.ts      |  152 ++
 ambari-web/latest/src/Utils/authPolicy.test.ts     |   46 +
 ambari-web/latest/src/Utils/authPolicy.ts          |   52 +
 ambari-web/latest/src/Utils/db.ts                  |   72 +-
 .../{vite.config.ts => src/Utils/httpError.ts}     |   13 +-
 .../latest/src/Utils/persistedSettings.test.ts     |   39 +
 .../persistedSettings.ts}                          |   30 +-
 ambari-web/latest/src/api/clusterApi.ts            |    6 +-
 ambari-web/latest/src/api/config/axiosConfig.ts    |   23 +-
 ambari-web/latest/src/api/loginApi.test.ts         |   52 +
 ambari-web/latest/src/api/loginApi.ts              |   77 +-
 .../AdminViewRouteGuard.test.ts}                   |   38 +-
 .../AdminViewRouteGuard.tsx}                       |   41 +-
 ambari-web/latest/src/components/AuthGuard.tsx     |   21 +-
 .../latest/src/components/DocumentTitleUpdater.tsx |   28 +-
 .../components/FeatureRouteGuard.tsx}              |   21 +-
 ambari-web/latest/src/components/Modal.tsx         |   48 +-
 ambari-web/latest/src/components/Navbar.tsx        |  136 +-
 .../latest/src/components/Sidebar/SideItemList.tsx |   14 +-
 .../latest/src/components/Sidebar/Sidebar.tsx      |   15 +-
 .../latest/src/components/UserSettingsModal.tsx    |  168 ++-
 ambari-web/latest/src/constants.ts                 |   29 +
 ambari-web/latest/src/hooks/useAuth.ts             |    6 +-
 .../latest/src/layout/InstallerVersionGuard.tsx    |   40 +
 .../src/{InactivityTimeout.tsx => layout/Main.tsx} |   32 +-
 ambari-web/latest/src/layout/Views.tsx             |   80 +
 ambari-web/latest/src/router/RoutesList.tsx        |  400 +++--
 .../screens/Authentication/AdminViewRedirect.tsx   |   64 +
 .../latest/src/screens/Authentication/Login.tsx    |  107 +-
 .../Authentication/LoginMessageModal.tsx}          |   32 +-
 .../latest/src/screens/Experimental/index.tsx      |  139 ++
 ambari-web/latest/src/store/UserContext.test.tsx   |  125 ++
 ambari-web/latest/src/store/UserContext.tsx        |  480 +++---
 ambari-web/latest/src/store/context.tsx            |  241 ++-
 .../UserSettingsModal.tsx => test/setup.ts}        |   58 +-
 ambari-web/latest/src/types/auth.ts                |    7 +
 ambari-web/latest/vite.config.ts                   |    8 +-
 ambari-web/pom.xml                                 |    3 +
 .../ember-baseline/01-auth-shell.md                |   50 +
 .../ember-baseline/13-permissions-flags.md         |  158 ++
 .../ember-baseline/14-react-gap-matrix.md          |  101 ++
 .../ember-baseline/generated/ajax-calls.md         |  440 ++++++
 .../ember-baseline/generated/ajax-endpoints.md     |  632 ++++++++
 .../generated/api-by-module/auth-shell.md          |   21 +
 .../generated/browser-network-entrypoints.md       |   66 +
 .../ember-baseline/generated/direct-http-calls.md  |   29 +
 .../ember-baseline/generated/realtime-channels.md  |  360 +++++
 .../react-current/01-auth-shell-gap.md             |  113 ++
 .../react-current/issues/01-auth-shell.md          |   85 ++
 58 files changed, 5777 insertions(+), 1518 deletions(-)

diff --git a/ambari-web/latest/package-lock.json b/ambari-web/latest/package-lock.json
index 81a3077f6b..7e9dd3f7a3 100644
--- a/ambari-web/latest/package-lock.json
+++ b/ambari-web/latest/package-lock.json
@@ -11,6 +11,8 @@
         "@fortawesome/free-solid-svg-icons": "^6.7.2",
         "@fortawesome/react-fontawesome": "^0.2.2",
         "@stomp/stompjs": "^7.1.1",
+        "@tanstack/react-table": "^8.21.3",
+        "@types/file-saver": "^2.0.7",
         "@types/lodash": "^4.17.16",
         "@types/react-select": "^5.0.0",
         "axios": "^1.11.0",
@@ -18,27 +20,34 @@
         "chart.js": "^4.5.1",
         "classnames": "^2.5.1",
         "dayjs": "^1.11.13",
+        "deep-object-diff": "^1.1.9",
+        "file-saver": "^2.0.5",
         "html-react-parser": "^5.2.6",
         "i18next": "^25.1.2",
         "i18next-browser-languagedetector": "^8.1.0",
         "isomorphic-dompurify": "^2.26.0",
         "js-cookie": "^3.0.5",
         "lodash": "^4.17.21",
+        "rc-slider": "^11.1.9",
         "react": "^19.0.0",
         "react-bootstrap": "^2.10.10",
         "react-bootstrap-icons": "^1.11.6",
         "react-chartjs-2": "^5.3.1",
         "react-dom": "^19.0.0",
+        "react-helmet-async": "^3.0.0",
         "react-hook-form": "^7.62.0",
         "react-hot-toast": "^2.5.2",
         "react-i18next": "^15.5.1",
         "react-router-dom": "^7.6.0",
         "react-select": "^5.10.2",
+        "react-use-websocket": "^4.13.0",
         "sass": "^1.88.0",
-        "timeago.js": "^4.0.2"
+        "timeago.js": "^4.0.2",
+        "universal-cookie": "^8.1.2"
       },
       "devDependencies": {
         "@eslint/js": "^9.21.0",
+        "@testing-library/react": "^16.3.0",
         "@types/js-cookie": "^3.0.6",
         "@types/node": "^24.2.1",
         "@types/react": "^19.0.10",
@@ -48,9 +57,11 @@
         "eslint-plugin-react-hooks": "^5.1.0",
         "eslint-plugin-react-refresh": "^0.4.19",
         "globals": "^15.15.0",
+        "jsdom": "^26.1.0",
         "typescript": "~5.7.2",
         "typescript-eslint": "^8.24.1",
-        "vite": "^6.2.0"
+        "vite": "^6.2.0",
+        "vitest": "^3.2.4"
       }
     },
     "node_modules/@acemir/cssom": {
@@ -74,49 +85,57 @@
       }
     },
     "node_modules/@asamuzakjp/css-color": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.2.tgz",
-      "integrity": "sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==",
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz",
+      "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@csstools/css-calc": "^3.0.0",
-        "@csstools/css-color-parser": "^4.0.1",
-        "@csstools/css-parser-algorithms": "^4.0.0",
-        "@csstools/css-tokenizer": "^4.0.0",
-        "lru-cache": "^11.2.5"
+        "@csstools/css-calc": "^2.1.3",
+        "@csstools/css-color-parser": "^3.0.9",
+        "@csstools/css-parser-algorithms": "^3.0.4",
+        "@csstools/css-tokenizer": "^3.0.3",
+        "lru-cache": "^10.4.3"
       }
     },
     "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": {
-      "version": "11.2.5",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz",
-      "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==",
-      "license": "BlueOak-1.0.0",
-      "engines": {
-        "node": "20 || >=22"
-      }
+      "version": "10.4.3",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "dev": true,
+      "license": "ISC"
     },
     "node_modules/@asamuzakjp/dom-selector": {
-      "version": "6.7.8",
-      "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.8.tgz",
-      "integrity": "sha512-stisC1nULNc9oH5lakAj8MH88ZxeGxzyWNDfbdCxvJSJIvDsHNZqYvscGTgy/ysgXWLJPt6K/4t0/GjvtKcFJQ==",
+      "version": "6.8.1",
+      "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz",
+      "integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==",
       "license": "MIT",
       "dependencies": {
         "@asamuzakjp/nwsapi": "^2.3.9",
         "bidi-js": "^1.0.3",
         "css-tree": "^3.1.0",
         "is-potential-custom-element-name": "^1.0.1",
-        "lru-cache": "^11.2.5"
+        "lru-cache": "^11.2.6"
       }
     },
     "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": {
-      "version": "11.2.5",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz",
-      "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==",
+      "version": "11.5.2",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz",
+      "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==",
       "license": "BlueOak-1.0.0",
       "engines": {
         "node": "20 || >=22"
       }
     },
+    "node_modules/@asamuzakjp/generational-cache": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz",
+      "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==",
+      "license": "MIT",
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      }
+    },
     "node_modules/@asamuzakjp/nwsapi": {
       "version": "2.3.9",
       "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz",
@@ -153,7 +172,6 @@
       "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==",
       "dev": true,
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "@ampproject/remapping": "^2.2.0",
         "@babel/code-frame": "^7.27.1",
@@ -405,10 +423,23 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@bramus/specificity": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz",
+      "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==",
+      "license": "MIT",
+      "dependencies": {
+        "css-tree": "^3.0.0"
+      },
+      "bin": {
+        "specificity": "bin/cli.js"
+      }
+    },
     "node_modules/@csstools/color-helpers": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.1.tgz",
-      "integrity": "sha512-NmXRccUJMk2AWA5A7e5a//3bCIMyOu2hAtdRYrhPPHjDxINuCwX1w6rnIZ4xjLcp0ayv6h8Pc3X0eJUGiAAXHQ==",
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
+      "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
+      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -421,13 +452,14 @@
       ],
       "license": "MIT-0",
       "engines": {
-        "node": ">=20.19.0"
+        "node": ">=18"
       }
     },
     "node_modules/@csstools/css-calc": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.0.0.tgz",
-      "integrity": "sha512-q4d82GTl8BIlh/dTnVsWmxnbWJeb3kiU8eUH71UxlxnS+WIaALmtzTL8gR15PkYOexMQYVk0CO4qIG93C1IvPA==",
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz",
+      "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
+      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -440,17 +472,18 @@
       ],
       "license": "MIT",
       "engines": {
-        "node": ">=20.19.0"
+        "node": ">=18"
       },
       "peerDependencies": {
-        "@csstools/css-parser-algorithms": "^4.0.0",
-        "@csstools/css-tokenizer": "^4.0.0"
+        "@csstools/css-parser-algorithms": "^3.0.5",
+        "@csstools/css-tokenizer": "^3.0.4"
       }
     },
     "node_modules/@csstools/css-color-parser": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.1.tgz",
-      "integrity": "sha512-vYwO15eRBEkeF6xjAno/KQ61HacNhfQuuU/eGwH67DplL0zD5ZixUa563phQvUelA07yDczIXdtmYojCphKJcw==",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
+      "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
+      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -463,21 +496,22 @@
       ],
       "license": "MIT",
       "dependencies": {
-        "@csstools/color-helpers": "^6.0.1",
-        "@csstools/css-calc": "^3.0.0"
+        "@csstools/color-helpers": "^5.1.0",
+        "@csstools/css-calc": "^2.1.4"
       },
       "engines": {
-        "node": ">=20.19.0"
+        "node": ">=18"
       },
       "peerDependencies": {
-        "@csstools/css-parser-algorithms": "^4.0.0",
-        "@csstools/css-tokenizer": "^4.0.0"
+        "@csstools/css-parser-algorithms": "^3.0.5",
+        "@csstools/css-tokenizer": "^3.0.4"
       }
     },
     "node_modules/@csstools/css-parser-algorithms": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz",
-      "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==",
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
+      "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
+      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -489,18 +523,17 @@
         }
       ],
       "license": "MIT",
-      "peer": true,
       "engines": {
-        "node": ">=20.19.0"
+        "node": ">=18"
       },
       "peerDependencies": {
-        "@csstools/css-tokenizer": "^4.0.0"
+        "@csstools/css-tokenizer": "^3.0.4"
       }
     },
     "node_modules/@csstools/css-syntax-patches-for-csstree": {
-      "version": "1.0.26",
-      "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.26.tgz",
-      "integrity": "sha512-6boXK0KkzT5u5xOgF6TKB+CLq9SOpEGmkZw0g5n9/7yg85wab3UzSxB8TxhLJ31L4SGJ6BCFRw/iftTha1CJXA==",
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.7.tgz",
+      "integrity": "sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==",
       "funding": [
         {
           "type": "github",
@@ -511,12 +544,21 @@
           "url": "https://opencollective.com/csstools"
         }
       ],
-      "license": "MIT-0"
+      "license": "MIT-0",
+      "peerDependencies": {
+        "css-tree": "^3.2.1"
+      },
+      "peerDependenciesMeta": {
+        "css-tree": {
+          "optional": true
+        }
+      }
     },
     "node_modules/@csstools/css-tokenizer": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz",
-      "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==",
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
+      "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
+      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -528,9 +570,8 @@
         }
       ],
       "license": "MIT",
-      "peer": true,
       "engines": {
-        "node": ">=20.19.0"
+        "node": ">=18"
       }
     },
     "node_modules/@emotion/babel-plugin": {
@@ -1233,9 +1274,9 @@
       }
     },
     "node_modules/@exodus/bytes": {
-      "version": "1.12.0",
-      "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.12.0.tgz",
-      "integrity": "sha512-BuCOHA/EJdPN0qQ5MdgAiJSt9fYDHbghlgrj33gRdy/Yp1/FMCDhU6vJfcKrLC0TPWGSrfH3vYXBQWmFHxlddw==",
+      "version": "1.15.1",
+      "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz",
+      "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==",
       "license": "MIT",
       "engines": {
         "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
@@ -1420,9 +1461,9 @@
       }
     },
     "node_modules/@jridgewell/sourcemap-codec": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
-      "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+      "version": "1.5.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+      "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
       "license": "MIT"
     },
     "node_modules/@jridgewell/trace-mapping": {
@@ -1780,7 +1821,6 @@
       "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
       "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
       "license": "MIT",
-      "peer": true,
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/popperjs"
@@ -2149,6 +2189,96 @@
         "tslib": "^2.8.0"
       }
     },
+    "node_modules/@tanstack/react-table": {
+      "version": "8.21.3",
+      "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz",
+      "integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==",
+      "license": "MIT",
+      "dependencies": {
+        "@tanstack/table-core": "8.21.3"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/tannerlinsley"
+      },
+      "peerDependencies": {
+        "react": ">=16.8",
+        "react-dom": ">=16.8"
+      }
+    },
+    "node_modules/@tanstack/table-core": {
+      "version": "8.21.3",
+      "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz",
+      "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/tannerlinsley"
+      }
+    },
+    "node_modules/@testing-library/dom": {
+      "version": "10.4.1",
+      "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
+      "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.10.4",
+        "@babel/runtime": "^7.12.5",
+        "@types/aria-query": "^5.0.1",
+        "aria-query": "5.3.0",
+        "dom-accessibility-api": "^0.5.9",
+        "lz-string": "^1.5.0",
+        "picocolors": "1.1.1",
+        "pretty-format": "^27.0.2"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@testing-library/react": {
+      "version": "16.3.2",
+      "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz",
+      "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.12.5"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@testing-library/dom": "^10.0.0",
+        "@types/react": "^18.0.0 || ^19.0.0",
+        "@types/react-dom": "^18.0.0 || ^19.0.0",
+        "react": "^18.0.0 || ^19.0.0",
+        "react-dom": "^18.0.0 || ^19.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@types/aria-query": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
+      "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
     "node_modules/@types/babel__core": {
       "version": "7.20.5",
       "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@@ -2194,6 +2324,24 @@
         "@babel/types": "^7.20.7"
       }
     },
+    "node_modules/@types/chai": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
+      "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/deep-eql": "*",
+        "assertion-error": "^2.0.1"
+      }
+    },
+    "node_modules/@types/deep-eql": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+      "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/estree": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
@@ -2201,6 +2349,12 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@types/file-saver": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.7.tgz",
+      "integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==",
+      "license": "MIT"
+    },
     "node_modules/@types/js-cookie": {
       "version": "3.0.6",
       "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz",
@@ -2225,7 +2379,6 @@
       "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz",
       "integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
       "dev": true,
-      "peer": true,
       "dependencies": {
         "undici-types": "~7.10.0"
       }
@@ -2247,7 +2400,6 @@
       "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.5.tgz",
       "integrity": "sha512-piErsCVVbpMMT2r7wbawdZsq4xMvIAhQuac2gedQHysu1TZYEigE6pnFfgZT+/jQnrRuF5r+SHzuehFjfRjr4g==",
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "csstype": "^3.0.2"
       }
@@ -2339,7 +2491,6 @@
       "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==",
       "dev": true,
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "@typescript-eslint/scope-manager": "8.32.1",
         "@typescript-eslint/types": "8.32.1",
@@ -2543,13 +2694,127 @@
         "vite": "^4.2.0 || ^5.0.0 || ^6.0.0"
       }
     },
+    "node_modules/@vitest/expect": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.7.tgz",
+      "integrity": "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/chai": "^5.2.2",
+        "@vitest/spy": "3.2.7",
+        "@vitest/utils": "3.2.7",
+        "chai": "^5.2.0",
+        "tinyrainbow": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/mocker": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz",
+      "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/spy": "3.2.7",
+        "estree-walker": "^3.0.3",
+        "magic-string": "^0.30.17"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      },
+      "peerDependencies": {
+        "msw": "^2.4.9",
+        "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
+      },
+      "peerDependenciesMeta": {
+        "msw": {
+          "optional": true
+        },
+        "vite": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vitest/pretty-format": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.7.tgz",
+      "integrity": "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tinyrainbow": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/runner": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.7.tgz",
+      "integrity": "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/utils": "3.2.7",
+        "pathe": "^2.0.3",
+        "strip-literal": "^3.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/snapshot": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.7.tgz",
+      "integrity": "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/pretty-format": "3.2.7",
+        "magic-string": "^0.30.17",
+        "pathe": "^2.0.3"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/spy": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.7.tgz",
+      "integrity": "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tinyspy": "^4.0.3"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/utils": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.7.tgz",
+      "integrity": "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/pretty-format": "3.2.7",
+        "loupe": "^3.1.4",
+        "tinyrainbow": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
     "node_modules/acorn": {
       "version": "8.14.1",
       "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
       "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
       "dev": true,
       "license": "MIT",
-      "peer": true,
       "bin": {
         "acorn": "bin/acorn"
       },
@@ -2593,6 +2858,17 @@
         "url": "https://github.com/sponsors/epoberezkin"
       }
     },
+    "node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/ansi-styles": {
       "version": "4.3.0",
       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -2616,6 +2892,27 @@
       "dev": true,
       "license": "Python-2.0"
     },
+    "node_modules/aria-query": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
+      "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "peer": true,
+      "dependencies": {
+        "dequal": "^2.0.3"
+      }
+    },
+    "node_modules/assertion-error": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+      "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -2725,7 +3022,6 @@
         }
       ],
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "caniuse-lite": "^1.0.30001716",
         "electron-to-chromium": "^1.5.149",
@@ -2739,6 +3035,16 @@
         "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
       }
     },
+    "node_modules/cac": {
+      "version": "6.7.14",
+      "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
+      "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/call-bind-apply-helpers": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
@@ -2781,6 +3087,23 @@
       ],
       "license": "CC-BY-4.0"
     },
+    "node_modules/chai": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
+      "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "assertion-error": "^2.0.1",
+        "check-error": "^2.1.1",
+        "deep-eql": "^5.0.1",
+        "loupe": "^3.1.0",
+        "pathval": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/chalk": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -2803,7 +3126,6 @@
       "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
       "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "@kurkle/color": "^0.3.0"
       },
@@ -2811,6 +3133,16 @@
         "pnpm": ">=8"
       }
     },
+    "node_modules/check-error": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz",
+      "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      }
+    },
     "node_modules/chokidar": {
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
@@ -2877,12 +3209,16 @@
       "license": "MIT"
     },
     "node_modules/cookie": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
-      "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+      "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
       "license": "MIT",
       "engines": {
         "node": ">=18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
       }
     },
     "node_modules/cosmiconfig": {
@@ -2926,60 +3262,50 @@
       }
     },
     "node_modules/css-tree": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
-      "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==",
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
+      "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==",
       "license": "MIT",
       "dependencies": {
-        "mdn-data": "2.12.2",
-        "source-map-js": "^1.0.1"
+        "mdn-data": "2.27.1",
+        "source-map-js": "^1.2.1"
       },
       "engines": {
         "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
       }
     },
     "node_modules/cssstyle": {
-      "version": "5.3.7",
-      "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.7.tgz",
-      "integrity": "sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==",
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",
+      "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@asamuzakjp/css-color": "^4.1.1",
-        "@csstools/css-syntax-patches-for-csstree": "^1.0.21",
-        "css-tree": "^3.1.0",
-        "lru-cache": "^11.2.4"
+        "@asamuzakjp/css-color": "^3.2.0",
+        "rrweb-cssom": "^0.8.0"
       },
       "engines": {
-        "node": ">=20"
-      }
-    },
-    "node_modules/cssstyle/node_modules/lru-cache": {
-      "version": "11.2.5",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz",
-      "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==",
-      "license": "BlueOak-1.0.0",
-      "engines": {
-        "node": "20 || >=22"
+        "node": ">=18"
       }
     },
     "node_modules/csstype": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
       "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
-      "license": "MIT",
-      "peer": true
+      "license": "MIT"
     },
     "node_modules/data-urls": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz",
-      "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
+      "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
-        "whatwg-mimetype": "^5.0.0",
-        "whatwg-url": "^16.0.0"
+        "whatwg-mimetype": "^4.0.0",
+        "whatwg-url": "^14.0.0"
       },
       "engines": {
-        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+        "node": ">=18"
       }
     },
     "node_modules/dayjs": {
@@ -3010,6 +3336,16 @@
       "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
       "license": "MIT"
     },
+    "node_modules/deep-eql": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
+      "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/deep-is": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -3017,6 +3353,12 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/deep-object-diff": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.9.tgz",
+      "integrity": "sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==",
+      "license": "MIT"
+    },
     "node_modules/delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -3047,6 +3389,14 @@
         "node": ">=0.10"
       }
     },
+    "node_modules/dom-accessibility-api": {
+      "version": "0.5.16",
+      "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
+      "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
     "node_modules/dom-helpers": {
       "version": "5.2.1",
       "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
@@ -3190,6 +3540,13 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/es-module-lexer": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+      "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/es-object-atoms": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
@@ -3284,7 +3641,6 @@
       "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==",
       "dev": true,
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.2.0",
         "@eslint-community/regexpp": "^4.12.1",
@@ -3447,6 +3803,16 @@
         "node": ">=4.0"
       }
     },
+    "node_modules/estree-walker": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+      "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "^1.0.0"
+      }
+    },
     "node_modules/esutils": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -3457,6 +3823,16 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/expect-type": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz",
+      "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
     "node_modules/fast-deep-equal": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -3531,6 +3907,12 @@
         "node": ">=16.0.0"
       }
     },
+    "node_modules/file-saver": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
+      "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==",
+      "license": "MIT"
+    },
     "node_modules/fill-range": {
       "version": "7.1.1",
       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -3814,15 +4196,16 @@
       }
     },
     "node_modules/html-encoding-sniffer": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz",
-      "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
+      "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@exodus/bytes": "^1.6.0"
+        "whatwg-encoding": "^3.1.1"
       },
       "engines": {
-        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+        "node": ">=18"
       }
     },
     "node_modules/html-parse-stringify": {
@@ -3929,7 +4312,6 @@
         }
       ],
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "@babel/runtime": "^7.27.1"
       },
@@ -3951,6 +4333,19 @@
         "@babel/runtime": "^7.23.2"
       }
     },
+    "node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/ignore": {
       "version": "5.3.2",
       "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -4088,43 +4483,195 @@
         "node": ">=20.19.5"
       }
     },
-    "node_modules/js-cookie": {
-      "version": "3.0.5",
-      "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
-      "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
+    "node_modules/isomorphic-dompurify/node_modules/@asamuzakjp/css-color": {
+      "version": "5.1.11",
+      "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz",
+      "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==",
+      "license": "MIT",
+      "dependencies": {
+        "@asamuzakjp/generational-cache": "^1.0.1",
+        "@csstools/css-calc": "^3.2.0",
+        "@csstools/css-color-parser": "^4.1.0",
+        "@csstools/css-parser-algorithms": "^4.0.0",
+        "@csstools/css-tokenizer": "^4.0.0"
+      },
       "engines": {
-        "node": ">=14"
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
       }
     },
-    "node_modules/js-tokens": {
+    "node_modules/isomorphic-dompurify/node_modules/@csstools/color-helpers": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.0.tgz",
+      "integrity": "sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT-0",
+      "engines": {
+        "node": ">=20.19.0"
+      }
+    },
+    "node_modules/isomorphic-dompurify/node_modules/@csstools/css-calc": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz",
+      "integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=20.19.0"
+      },
+      "peerDependencies": {
+        "@csstools/css-parser-algorithms": "^4.0.0",
+        "@csstools/css-tokenizer": "^4.0.0"
+      }
+    },
+    "node_modules/isomorphic-dompurify/node_modules/@csstools/css-color-parser": {
+      "version": "4.1.10",
+      "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.10.tgz",
+      "integrity": "sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@csstools/color-helpers": "^6.1.0",
+        "@csstools/css-calc": "^3.3.0"
+      },
+      "engines": {
+        "node": ">=20.19.0"
+      },
+      "peerDependencies": {
+        "@csstools/css-parser-algorithms": "^4.0.0",
+        "@csstools/css-tokenizer": "^4.0.0"
+      }
+    },
+    "node_modules/isomorphic-dompurify/node_modules/@csstools/css-parser-algorithms": {
       "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
-      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
-      "license": "MIT"
+      "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz",
+      "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=20.19.0"
+      },
+      "peerDependencies": {
+        "@csstools/css-tokenizer": "^4.0.0"
+      }
     },
-    "node_modules/js-yaml": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
-      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
-      "dev": true,
+    "node_modules/isomorphic-dompurify/node_modules/@csstools/css-tokenizer": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz",
+      "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=20.19.0"
+      }
+    },
+    "node_modules/isomorphic-dompurify/node_modules/cssstyle": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-6.2.0.tgz",
+      "integrity": "sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==",
       "license": "MIT",
       "dependencies": {
-        "argparse": "^2.0.1"
+        "@asamuzakjp/css-color": "^5.0.1",
+        "@csstools/css-syntax-patches-for-csstree": "^1.0.28",
+        "css-tree": "^3.1.0",
+        "lru-cache": "^11.2.6"
       },
-      "bin": {
-        "js-yaml": "bin/js-yaml.js"
+      "engines": {
+        "node": ">=20"
       }
     },
-    "node_modules/jsdom": {
-      "version": "28.0.0",
-      "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-28.0.0.tgz",
-      "integrity": "sha512-KDYJgZ6T2TKdU8yBfYueq5EPG/EylMsBvCaenWMJb2OXmjgczzwveRCoJ+Hgj1lXPDyasvrgneSn4GBuR1hYyA==",
+    "node_modules/isomorphic-dompurify/node_modules/data-urls": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz",
+      "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==",
+      "license": "MIT",
+      "dependencies": {
+        "whatwg-mimetype": "^5.0.0",
+        "whatwg-url": "^16.0.0"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      }
+    },
+    "node_modules/isomorphic-dompurify/node_modules/entities": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz",
+      "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=20.19.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/isomorphic-dompurify/node_modules/html-encoding-sniffer": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz",
+      "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==",
+      "license": "MIT",
+      "dependencies": {
+        "@exodus/bytes": "^1.6.0"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      }
+    },
+    "node_modules/isomorphic-dompurify/node_modules/jsdom": {
+      "version": "28.1.0",
+      "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-28.1.0.tgz",
+      "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==",
       "license": "MIT",
       "dependencies": {
         "@acemir/cssom": "^0.9.31",
-        "@asamuzakjp/dom-selector": "^6.7.6",
+        "@asamuzakjp/dom-selector": "^6.8.1",
+        "@bramus/specificity": "^2.4.2",
         "@exodus/bytes": "^1.11.0",
-        "cssstyle": "^5.3.7",
+        "cssstyle": "^6.0.1",
         "data-urls": "^7.0.0",
         "decimal.js": "^10.6.0",
         "html-encoding-sniffer": "^6.0.0",
@@ -4135,7 +4682,7 @@
         "saxes": "^6.0.0",
         "symbol-tree": "^3.2.4",
         "tough-cookie": "^6.0.0",
-        "undici": "^7.20.0",
+        "undici": "^7.21.0",
         "w3c-xmlserializer": "^5.0.0",
         "webidl-conversions": "^8.0.1",
         "whatwg-mimetype": "^5.0.0",
@@ -4154,40 +4701,202 @@
         }
       }
     },
-    "node_modules/jsesc": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
-      "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+    "node_modules/isomorphic-dompurify/node_modules/lru-cache": {
+      "version": "11.5.2",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz",
+      "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==",
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/isomorphic-dompurify/node_modules/parse5": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
+      "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==",
       "license": "MIT",
-      "bin": {
-        "jsesc": "bin/jsesc"
+      "dependencies": {
+        "entities": "^8.0.0"
       },
-      "engines": {
-        "node": ">=6"
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
       }
     },
-    "node_modules/json-buffer": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
-      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
-      "dev": true,
-      "license": "MIT"
+    "node_modules/isomorphic-dompurify/node_modules/tldts": {
+      "version": "7.4.10",
+      "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.10.tgz",
+      "integrity": "sha512-GgouD1B+sWwvkaEq8vXC15DjQitxbvs12oIXELpconwm+Tg3zfcEv4jgzq3vtKverDXsg3VI8aRgNL2Nra0Iog==",
+      "license": "MIT",
+      "dependencies": {
+        "tldts-core": "^7.4.10"
+      },
+      "bin": {
+        "tldts": "bin/cli.js"
+      }
     },
-    "node_modules/json-parse-even-better-errors": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
-      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+    "node_modules/isomorphic-dompurify/node_modules/tldts-core": {
+      "version": "7.4.10",
+      "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.10.tgz",
+      "integrity": "sha512-KnQjp53ZekKgm/r3l+u8kJGGzYgrWdP8+Mql7a4vijh2WE0IrZWspQj/TpTxDho/YxO+AnOZnIjQcCD+q6iJsw==",
       "license": "MIT"
     },
-    "node_modules/json-schema-traverse": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
-      "dev": true,
-      "license": "MIT"
+    "node_modules/isomorphic-dompurify/node_modules/tough-cookie": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz",
+      "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "tldts": "^7.0.5"
+      },
+      "engines": {
+        "node": ">=16"
+      }
     },
-    "node_modules/json-stable-stringify-without-jsonify": {
-      "version": "1.0.1",
+    "node_modules/isomorphic-dompurify/node_modules/tr46": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz",
+      "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==",
+      "license": "MIT",
+      "dependencies": {
+        "punycode": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/isomorphic-dompurify/node_modules/webidl-conversions": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz",
+      "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/isomorphic-dompurify/node_modules/whatwg-mimetype": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz",
+      "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/isomorphic-dompurify/node_modules/whatwg-url": {
+      "version": "16.0.1",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz",
+      "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==",
+      "license": "MIT",
+      "dependencies": {
+        "@exodus/bytes": "^1.11.0",
+        "tr46": "^6.0.0",
+        "webidl-conversions": "^8.0.1"
+      },
+      "engines": {
+        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+      }
+    },
+    "node_modules/js-cookie": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
+      "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "license": "MIT"
+    },
+    "node_modules/js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/jsdom": {
+      "version": "26.1.0",
+      "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz",
+      "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cssstyle": "^4.2.1",
+        "data-urls": "^5.0.0",
+        "decimal.js": "^10.5.0",
+        "html-encoding-sniffer": "^4.0.0",
+        "http-proxy-agent": "^7.0.2",
+        "https-proxy-agent": "^7.0.6",
+        "is-potential-custom-element-name": "^1.0.1",
+        "nwsapi": "^2.2.16",
+        "parse5": "^7.2.1",
+        "rrweb-cssom": "^0.8.0",
+        "saxes": "^6.0.0",
+        "symbol-tree": "^3.2.4",
+        "tough-cookie": "^5.1.1",
+        "w3c-xmlserializer": "^5.0.0",
+        "webidl-conversions": "^7.0.0",
+        "whatwg-encoding": "^3.1.1",
+        "whatwg-mimetype": "^4.0.0",
+        "whatwg-url": "^14.1.1",
+        "ws": "^8.18.0",
+        "xml-name-validator": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "canvas": "^3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "canvas": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/jsesc": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+      "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+      "license": "MIT",
+      "bin": {
+        "jsesc": "bin/jsesc"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/json-buffer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+      "license": "MIT"
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
       "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
       "dev": true,
@@ -4276,6 +4985,13 @@
         "loose-envify": "cli.js"
       }
     },
+    "node_modules/loupe": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
+      "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/lru-cache": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -4286,6 +5002,27 @@
         "yallist": "^3.0.2"
       }
     },
+    "node_modules/lz-string": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
+      "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "bin": {
+        "lz-string": "bin/bin.js"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.21",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+      "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.5"
+      }
+    },
     "node_modules/math-intrinsics": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -4295,9 +5032,9 @@
       }
     },
     "node_modules/mdn-data": {
-      "version": "2.12.2",
-      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
-      "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
+      "version": "2.27.1",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz",
+      "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==",
       "license": "CC0-1.0"
     },
     "node_modules/memoize-one": {
@@ -4408,6 +5145,13 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/nwsapi": {
+      "version": "2.2.24",
+      "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.24.tgz",
+      "integrity": "sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/object-assign": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -4498,9 +5242,10 @@
       }
     },
     "node_modules/parse5": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz",
-      "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==",
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+      "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "entities": "^6.0.0"
@@ -4513,6 +5258,7 @@
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
       "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+      "dev": true,
       "license": "BSD-2-Clause",
       "engines": {
         "node": ">=0.12"
@@ -4556,6 +5302,23 @@
         "node": ">=8"
       }
     },
+    "node_modules/pathe": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+      "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/pathval": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
+      "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.16"
+      }
+    },
     "node_modules/picocolors": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -4614,6 +5377,44 @@
         "node": ">= 0.8.0"
       }
     },
+    "node_modules/pretty-format": {
+      "version": "27.5.1",
+      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+      "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ansi-regex": "^5.0.1",
+        "ansi-styles": "^5.0.0",
+        "react-is": "^17.0.1"
+      },
+      "engines": {
+        "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+      }
+    },
+    "node_modules/pretty-format/node_modules/ansi-styles": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+      "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/pretty-format/node_modules/react-is": {
+      "version": "17.0.2",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+      "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
     "node_modules/prop-types": {
       "version": "15.8.1",
       "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -4673,12 +5474,49 @@
       ],
       "license": "MIT"
     },
+    "node_modules/rc-slider": {
+      "version": "11.1.9",
+      "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.9.tgz",
+      "integrity": "sha512-h8IknhzSh3FEM9u8ivkskh+Ef4Yo4JRIY2nj7MrH6GQmrwV6mcpJf5/4KgH5JaVI1H3E52yCdpOlVyGZIeph5A==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.10.1",
+        "classnames": "^2.2.5",
+        "rc-util": "^5.36.0"
+      },
+      "engines": {
+        "node": ">=8.x"
+      },
+      "peerDependencies": {
+        "react": ">=16.9.0",
+        "react-dom": ">=16.9.0"
+      }
+    },
+    "node_modules/rc-util": {
+      "version": "5.44.4",
+      "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz",
+      "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.18.3",
+        "react-is": "^18.2.0"
+      },
+      "peerDependencies": {
+        "react": ">=16.9.0",
+        "react-dom": ">=16.9.0"
+      }
+    },
+    "node_modules/rc-util/node_modules/react-is": {
+      "version": "18.3.1",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+      "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+      "license": "MIT"
+    },
     "node_modules/react": {
       "version": "19.1.0",
       "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
       "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
       "license": "MIT",
-      "peer": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -4740,7 +5578,6 @@
       "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
       "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "scheduler": "^0.26.0"
       },
@@ -4748,6 +5585,26 @@
         "react": "^19.1.0"
       }
     },
+    "node_modules/react-fast-compare": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+      "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
+      "license": "MIT"
+    },
+    "node_modules/react-helmet-async": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-3.0.0.tgz",
+      "integrity": "sha512-nA3IEZfXiclgrz4KLxAhqJqIfFDuvzQwlKwpdmzZIuC1KNSghDEIXmyU0TKtbM+NafnkICcwx8CECFrZ/sL/1w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "invariant": "^2.2.4",
+        "react-fast-compare": "^3.2.2",
+        "shallowequal": "^1.1.0"
+      },
+      "peerDependencies": {
+        "react": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+      }
+    },
     "node_modules/react-hook-form": {
       "version": "7.71.1",
       "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.71.1.tgz",
@@ -4909,6 +5766,12 @@
         "react-dom": ">=16.6.0"
       }
     },
+    "node_modules/react-use-websocket": {
+      "version": "4.13.0",
+      "resolved": "https://registry.npmjs.org/react-use-websocket/-/react-use-websocket-4.13.0.tgz",
+      "integrity": "sha512-anMuVoV//g2N76Wxqvqjjo1X48r9Np3y1/gMl7arX84tAPXdy5R7sB5lO5hvCzQRYjqXwV8XMAiEBOUbyrZFrw==",
+      "license": "MIT"
+    },
     "node_modules/readdirp": {
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
@@ -5011,6 +5874,13 @@
         "fsevents": "~2.3.2"
       }
     },
+    "node_modules/rrweb-cssom": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
+      "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/run-parallel": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -5035,12 +5905,18 @@
         "queue-microtask": "^1.2.2"
       }
     },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/sass": {
       "version": "1.89.0",
       "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.0.tgz",
       "integrity": "sha512-ld+kQU8YTdGNjOLfRWBzewJpU5cwEv/h5yyqlSeJcj6Yh8U4TDA9UA5FPicqDz/xgRPWRSYIQNiFks21TbA9KQ==",
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "chokidar": "^4.0.0",
         "immutable": "^5.0.2",
@@ -5090,6 +5966,12 @@
       "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
       "license": "MIT"
     },
+    "node_modules/shallowequal": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
+      "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==",
+      "license": "MIT"
+    },
     "node_modules/shebang-command": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -5113,6 +5995,13 @@
         "node": ">=8"
       }
     },
+    "node_modules/siginfo": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+      "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+      "dev": true,
+      "license": "ISC"
+    },
     "node_modules/source-map": {
       "version": "0.5.7",
       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@@ -5131,6 +6020,20 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/stackback": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+      "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/std-env": {
+      "version": "3.10.0",
+      "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
+      "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/strip-json-comments": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -5144,6 +6047,26 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/strip-literal": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz",
+      "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "js-tokens": "^9.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/strip-literal/node_modules/js-tokens": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
+      "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/style-to-js": {
       "version": "1.1.21",
       "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz",
@@ -5205,15 +6128,29 @@
       "integrity": "sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w==",
       "license": "MIT"
     },
+    "node_modules/tinybench": {
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+      "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tinyexec": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
+      "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/tinyglobby": {
-      "version": "0.2.13",
-      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
-      "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
+      "version": "0.2.17",
+      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+      "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
-        "fdir": "^6.4.4",
-        "picomatch": "^4.0.2"
+        "fdir": "^6.5.0",
+        "picomatch": "^4.0.4"
       },
       "engines": {
         "node": ">=12.0.0"
@@ -5223,11 +6160,14 @@
       }
     },
     "node_modules/tinyglobby/node_modules/fdir": {
-      "version": "6.4.4",
-      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
-      "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+      "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
       "dev": true,
       "license": "MIT",
+      "engines": {
+        "node": ">=12.0.0"
+      },
       "peerDependencies": {
         "picomatch": "^3 || ^4"
       },
@@ -5238,12 +6178,11 @@
       }
     },
     "node_modules/tinyglobby/node_modules/picomatch": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
-      "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+      "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
       "dev": true,
       "license": "MIT",
-      "peer": true,
       "engines": {
         "node": ">=12"
       },
@@ -5251,22 +6190,54 @@
         "url": "https://github.com/sponsors/jonschlinkert"
       }
     },
+    "node_modules/tinypool": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
+      "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      }
+    },
+    "node_modules/tinyrainbow": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz",
+      "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/tinyspy": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz",
+      "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
     "node_modules/tldts": {
-      "version": "7.0.23",
-      "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.23.tgz",
-      "integrity": "sha512-ASdhgQIBSay0R/eXggAkQ53G4nTJqTXqC2kbaBbdDwM7SkjyZyO0OaaN1/FH7U/yCeqOHDwFO5j8+Os/IS1dXw==",
+      "version": "6.1.86",
+      "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
+      "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
-        "tldts-core": "^7.0.23"
+        "tldts-core": "^6.1.86"
       },
       "bin": {
         "tldts": "bin/cli.js"
       }
     },
     "node_modules/tldts-core": {
-      "version": "7.0.23",
-      "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.23.tgz",
-      "integrity": "sha512-0g9vrtDQLrNIiCj22HSe9d4mLVG3g5ph5DZ8zCKBr4OtrspmNB6ss7hVyzArAeE88ceZocIEGkyW1Ime7fxPtQ==",
+      "version": "6.1.86",
+      "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
+      "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",
+      "dev": true,
       "license": "MIT"
     },
     "node_modules/to-regex-range": {
@@ -5283,27 +6254,29 @@
       }
     },
     "node_modules/tough-cookie": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz",
-      "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==",
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
+      "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
+      "dev": true,
       "license": "BSD-3-Clause",
       "dependencies": {
-        "tldts": "^7.0.5"
+        "tldts": "^6.1.32"
       },
       "engines": {
         "node": ">=16"
       }
     },
     "node_modules/tr46": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz",
-      "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==",
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
+      "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "punycode": "^2.3.1"
       },
       "engines": {
-        "node": ">=20"
+        "node": ">=18"
       }
     },
     "node_modules/ts-api-utils": {
@@ -5344,7 +6317,6 @@
       "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
       "devOptional": true,
       "license": "Apache-2.0",
-      "peer": true,
       "bin": {
         "tsc": "bin/tsc",
         "tsserver": "bin/tsserver"
@@ -5392,9 +6364,9 @@
       }
     },
     "node_modules/undici": {
-      "version": "7.21.0",
-      "resolved": "https://registry.npmjs.org/undici/-/undici-7.21.0.tgz",
-      "integrity": "sha512-Hn2tCQpoDt1wv23a68Ctc8Cr/BHpUSfaPYrkajTXOS9IKpxVRx/X5m1K2YkbK2ipgZgxXSgsUinl3x+2YdSSfg==",
+      "version": "7.29.0",
+      "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz",
+      "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==",
       "license": "MIT",
       "engines": {
         "node": ">=20.18.1"
@@ -5406,6 +6378,15 @@
       "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
       "dev": true
     },
+    "node_modules/universal-cookie": {
+      "version": "8.1.2",
+      "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-8.1.2.tgz",
+      "integrity": "sha512-kcKzTGNsxVytujrYOvQbvh//QyFrA53HrzCGyzh6i9ujCww5gfPrLK0tG+jJD40SIIldiEjBNPPSR8fBMS21GA==",
+      "license": "MIT",
+      "dependencies": {
+        "cookie": "^1.1.1"
+      }
+    },
     "node_modules/update-browserslist-db": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
@@ -5467,7 +6448,6 @@
       "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
       "dev": true,
       "license": "MIT",
-      "peer": true,
       "dependencies": {
         "esbuild": "^0.25.0",
         "fdir": "^6.4.4",
@@ -5537,6 +6517,29 @@
         }
       }
     },
+    "node_modules/vite-node": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz",
+      "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cac": "^6.7.14",
+        "debug": "^4.4.1",
+        "es-module-lexer": "^1.7.0",
+        "pathe": "^2.0.3",
+        "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
+      },
+      "bin": {
+        "vite-node": "vite-node.mjs"
+      },
+      "engines": {
+        "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
     "node_modules/vite/node_modules/fdir": {
       "version": "6.4.4",
       "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
@@ -5558,7 +6561,92 @@
       "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
       "dev": true,
       "license": "MIT",
-      "peer": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/vitest": {
+      "version": "3.2.7",
+      "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz",
+      "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/chai": "^5.2.2",
+        "@vitest/expect": "3.2.7",
+        "@vitest/mocker": "3.2.7",
+        "@vitest/pretty-format": "^3.2.7",
+        "@vitest/runner": "3.2.7",
+        "@vitest/snapshot": "3.2.7",
+        "@vitest/spy": "3.2.7",
+        "@vitest/utils": "3.2.7",
+        "chai": "^5.2.0",
+        "debug": "^4.4.1",
+        "expect-type": "^1.2.1",
+        "magic-string": "^0.30.17",
+        "pathe": "^2.0.3",
+        "picomatch": "^4.0.2",
+        "std-env": "^3.9.0",
+        "tinybench": "^2.9.0",
+        "tinyexec": "^0.3.2",
+        "tinyglobby": "^0.2.14",
+        "tinypool": "^1.1.1",
+        "tinyrainbow": "^2.0.0",
+        "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0",
+        "vite-node": "3.2.4",
+        "why-is-node-running": "^2.3.0"
+      },
+      "bin": {
+        "vitest": "vitest.mjs"
+      },
+      "engines": {
+        "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      },
+      "peerDependencies": {
+        "@edge-runtime/vm": "*",
+        "@types/debug": "^4.1.12",
+        "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+        "@vitest/browser": "3.2.7",
+        "@vitest/ui": "3.2.7",
+        "happy-dom": "*",
+        "jsdom": "*"
+      },
+      "peerDependenciesMeta": {
+        "@edge-runtime/vm": {
+          "optional": true
+        },
+        "@types/debug": {
+          "optional": true
+        },
+        "@types/node": {
+          "optional": true
+        },
+        "@vitest/browser": {
+          "optional": true
+        },
+        "@vitest/ui": {
+          "optional": true
+        },
+        "happy-dom": {
+          "optional": true
+        },
+        "jsdom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vitest/node_modules/picomatch": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+      "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
+      "dev": true,
+      "license": "MIT",
       "engines": {
         "node": ">=12"
       },
@@ -5597,35 +6685,51 @@
       }
     },
     "node_modules/webidl-conversions": {
-      "version": "8.0.1",
-      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz",
-      "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==",
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+      "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+      "dev": true,
       "license": "BSD-2-Clause",
       "engines": {
-        "node": ">=20"
+        "node": ">=12"
+      }
+    },
+    "node_modules/whatwg-encoding": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
+      "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
+      "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "iconv-lite": "0.6.3"
+      },
+      "engines": {
+        "node": ">=18"
       }
     },
     "node_modules/whatwg-mimetype": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz",
-      "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+      "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+      "dev": true,
       "license": "MIT",
       "engines": {
-        "node": ">=20"
+        "node": ">=18"
       }
     },
     "node_modules/whatwg-url": {
-      "version": "16.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.0.tgz",
-      "integrity": "sha512-9CcxtEKsf53UFwkSUZjG+9vydAsFO4lFHBpJUtjBcoJOCJpKnSJNwCw813zrYJHpCJ7sgfbtOe0V5Ku7Pa1XMQ==",
+      "version": "14.2.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
+      "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
-        "@exodus/bytes": "^1.11.0",
-        "tr46": "^6.0.0",
-        "webidl-conversions": "^8.0.1"
+        "tr46": "^5.1.0",
+        "webidl-conversions": "^7.0.0"
       },
       "engines": {
-        "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+        "node": ">=18"
       }
     },
     "node_modules/which": {
@@ -5644,6 +6748,23 @@
         "node": ">= 8"
       }
     },
+    "node_modules/why-is-node-running": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+      "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "siginfo": "^2.0.0",
+        "stackback": "0.0.2"
+      },
+      "bin": {
+        "why-is-node-running": "cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/word-wrap": {
       "version": "1.2.5",
       "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
@@ -5654,6 +6775,28 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/ws": {
+      "version": "8.21.3",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz",
+      "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/xml-name-validator": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
diff --git a/ambari-web/latest/package.json b/ambari-web/latest/package.json
index 07f0521e11..fa40876d12 100755
--- a/ambari-web/latest/package.json
+++ b/ambari-web/latest/package.json
@@ -7,6 +7,7 @@
     "dev": "vite",
     "build": "tsc -b && vite build",
     "lint": "eslint .",
+    "test": "vitest run",
     "preview": "vite preview"
   },
   "dependencies": {
@@ -54,12 +55,15 @@
     "@types/react": "^19.0.10",
     "@types/react-dom": "^19.0.4",
     "@vitejs/plugin-react": "^4.3.4",
+    "@testing-library/react": "^16.3.0",
     "eslint": "^9.21.0",
     "eslint-plugin-react-hooks": "^5.1.0",
     "eslint-plugin-react-refresh": "^0.4.19",
     "globals": "^15.15.0",
+    "jsdom": "^26.1.0",
     "typescript": "~5.7.2",
     "typescript-eslint": "^8.24.1",
-    "vite": "^6.2.0"
+    "vite": "^6.2.0",
+    "vitest": "^3.2.4"
   }
 }
diff --git a/ambari-web/latest/src/AmbariAboutModal.tsx b/ambari-web/latest/src/AmbariAboutModal.tsx
index d68895bd85..5db8d0c78b 100644
--- a/ambari-web/latest/src/AmbariAboutModal.tsx
+++ b/ambari-web/latest/src/AmbariAboutModal.tsx
@@ -15,84 +15,48 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { useEffect, useState } from "react";
+
+import { useContext } from "react";
 import { Image } from "react-bootstrap";
-import { ServiceApi } from "./api/serviceApi";
-import { get } from "lodash";
-import Spinner from "./components/Spinner";
 import Modal from "./components/Modal";
-import AmbariLogo from "./assets/img/ambari-logo.png"
-
-type AmbariAboutModalProps = {
-  isOpen: boolean;
-  onClose: () => void;
-};
+import AmbariLogo from "./assets/img/ambari-logo.png";
+import { AppContext } from "./store/context";
 
 export default function AmbariAboutModal({
   isOpen,
   onClose,
-}: AmbariAboutModalProps) {
-  
-  const [loading, setLoading] = useState(false);
-  const [ ambariVersion, setAmbariVersion ] = useState(false);
-  useEffect(() => {
-    async function getAmbariAboutInfo() {
-      setLoading(true);
-      const data: any = await ServiceApi.adminAboutInfo(
-        "RootServiceComponents/component_version,RootServiceComponents/properties/server.os_family&minimal_response=true"
-      );
-      console.log("version", get(data, "RootServiceComponents.component_version"))
-      setAmbariVersion(get(data, "RootServiceComponents.component_version"));
-      setLoading(false);
-    }
-    if (!ambariVersion) {
-      getAmbariAboutInfo();
-    }
-  }, []);
-
-  const modalBody = loading ? (
-    <Spinner />
-  ) : (
-    <div className="d-flex">
-      <Image
-        src={AmbariLogo}
-        height={75}
-        width={75}
-        className="me-5"
-      />
-      <div>
-        <h2>Apache Ambari</h2>
-        <div className="mb-3">Version {ambariVersion}</div>
-        <div>
-          <a href="http://ambari.apache.org/" className="custom-link">
-            Get involved!
-          </a>
-        </div>
-        <div>
-          <a
-            href="http://www.apache.org/licenses/LICENSE-2.0"
-            className="custom-link"
-          >
-            Licensed under the Apache License, Version 2.0
-          </a>
-        </div>
-      </div>
-    </div>
-  );
-
+}: {
+  isOpen: boolean;
+  onClose: () => void;
+}) {
+  const { ambariServerVersion } = useContext(AppContext);
   return (
     <Modal
       isOpen={isOpen}
       onClose={onClose}
       modalTitle="About"
-      modalBody={modalBody}
+      modalBody={(
+        <div className="d-flex">
+          <Image src={AmbariLogo} height={75} width={75} className="me-5" />
+          <div>
+            <h2>Apache Ambari</h2>
+            <div className="mb-3">Version {ambariServerVersion || "N/A"}</div>
+            <div><a href="https://ambari.apache.org/" className="custom-link">Get involved!</a></div>
+            <div>
+              <a href="https://www.apache.org/licenses/LICENSE-2.0" className="custom-link">
+                Licensed under the Apache License, Version 2.0
+              </a>
+            </div>
+          </div>
+        </div>
+      )}
       successCallback={onClose}
       options={{
         modalSize: "modal-lg",
         okButtonText: "OK",
         cancelableViaBtn: false,
-        okButtonVariant: "success"
+        okButtonVariant: "success",
       }}
     />
   );
-}
\ No newline at end of file
+}
diff --git a/ambari-web/latest/src/App.tsx b/ambari-web/latest/src/App.tsx
index a1f150e3de..d3a7d19fc9 100644
--- a/ambari-web/latest/src/App.tsx
+++ b/ambari-web/latest/src/App.tsx
@@ -16,73 +16,24 @@
  * limitations under the License.
  */
 
-import { Suspense} from "react";
+import { Suspense } from "react";
+import { createHashRouter, RouterProvider } from "react-router-dom";
+import { HelmetProvider } from "react-helmet-async";
 import { Toaster } from "react-hot-toast";
-import "./styles/app.scss";
-import {AppProvider} from "./store/context";
+import RoutesList from "./router/RoutesList";
 import { UserProvider } from "./store/UserContext";
-import useWebSocket from "react-use-websocket";
 import Spinner from "./components/Spinner";
-import AppLoader from "./AppLoader";
-import { ModalProvider } from "./store/ModalContext";
-import CustomModal from "./store/CustomModal";
-import clusterApi from "./api/clusterApi";
-import { Login } from "./screens/Authentication/Login";
-import { useEffect, useState } from "react";
-import InactivityTimeout from "./InactivityTimeout";
-import { HelmetProvider } from "react-helmet-async";
-import DocumentTitleUpdater from "./components/DocumentTitleUpdater";
+import "./styles/app.scss";
 
-function App() {
-  const [userSessionTimeout, setUserSessionTimeout] = useState<number>(0);
-  const socketUrl = "/api/stomp/v1/websocket";
-  const { readyState: _readyState } = useWebSocket(socketUrl, {
-    onOpen: () => console.log("opened"),
-    //Will attempt to reconnect on all close events, such as server shutting down
-    shouldReconnect: () => false,
-  });
+const router = createHashRouter(RoutesList);
 
-  // Fetch timeout value once when app loads
-  useEffect(() => {
-    const fetchTimeout = async () => {
-      try {
-        const response = await clusterApi.getUserTimeout();
-        if (response.status === 200) {
-          const timeoutInSeconds = response.data.RootServiceComponents.properties["server.http.session.inactive_timeout"];
-          setUserSessionTimeout(timeoutInSeconds * 1000);
-        }
-      } catch (error) {
-        console.error("Error fetching timeout:", error);
-        setUserSessionTimeout(90000); // Default 90 seconds
-      }
-    };
-    fetchTimeout();
-  }, []);
-  
-  const isLoginPage = window.location.hash === '#/login';
-  
-  console.log("app")
+function App() {
   return (
     <HelmetProvider>
       <Suspense fallback={<Spinner />}>
         <UserProvider>
-          {isLoginPage ? (
-            <>
-              <Login />
-            </>
-          ) : (
-            <AppProvider>
-              <ModalProvider>
-                <DocumentTitleUpdater />
-                <Toaster />
-                {userSessionTimeout > 0 && (
-                   <InactivityTimeout timeout={userSessionTimeout} />
-                 )}
-                <CustomModal />
-                <AppLoader />
-              </ModalProvider>
-            </AppProvider>
-          )}
+          <Toaster />
+          <RouterProvider router={router} />
         </UserProvider>
       </Suspense>
     </HelmetProvider>
diff --git a/ambari-web/latest/src/AppLoader.tsx b/ambari-web/latest/src/AppLoader.tsx
index b5c0105827..bc4c904fde 100644
--- a/ambari-web/latest/src/AppLoader.tsx
+++ b/ambari-web/latest/src/AppLoader.tsx
@@ -17,151 +17,187 @@
  */
 
 import { useContext, useEffect, useState } from "react";
-import { AppContext } from "./store/context";
-import { createHashRouter, RouterProvider } from "react-router-dom";
-import RoutesList from "./router/RoutesList";
-import { LocalStorageOps } from "./Utils/LocalStorageOps";
-import clusterApi from "./api/clusterApi";
-import { useLocation } from "react-router-dom";
-import { useNavigate } from "react-router-dom";
-import { toBePreservedPaths } from "./constants";
-import ClusterApi from "./api/clusterApi";
-import { ProgressBar } from "react-bootstrap";
+import { Navigate, Outlet, useLocation, useNavigate } from "react-router-dom";
+import { Alert, Button, ProgressBar } from "react-bootstrap";
+import { AppContext, AppProvider } from "./store/context";
+import { ModalProvider } from "./store/ModalContext";
+import { useAuth } from "./hooks/useAuth";
 import usePolling from "./hooks/usePolling";
-
-// Separate component for route tracking that will be used inside router
-export function RouteTracker() {
-  const [lastVisitedURL] = useState(
-    () => LocalStorageOps.getItem("lastVisitedURL") || "/#/installer/step0"
-  );
-  const [hasCluster, setHasCluster] = useState<boolean | null>(null);
-  const [isClusterChecked, setIsClusterChecked] = useState(false);
-  const unpreservedPaths = ["/", "/login"];
-
+import ClusterApi from "./api/clusterApi";
+import { toBePreservedPaths } from "./constants";
+import {
+  consumePreferredPath,
+  normalizeInternalPath,
+  savePreferredPath,
+} from "./Utils/authNavigation";
+import CustomModal from "./store/CustomModal";
+import DocumentTitleUpdater from "./components/DocumentTitleUpdater";
+import InactivityTimeout from "./InactivityTimeout";
+import LoginMessageModal from "./screens/Authentication/LoginMessageModal";
+import { isViewOnlyUser, selectLandingPath } from "./Utils/authPolicy";
+
+export function AuthenticatedApplication() {
+  const {
+    isAuthenticated,
+    isLoading,
+    loginMessage,
+    retrySession,
+    sessionError,
+  } = useAuth();
   const location = useLocation();
-  const navigate = useNavigate();
-
-  async function saveUserUrl() {
-    const currentURL = location.pathname;
-    await ClusterApi.postPersistData(
-      JSON.stringify({
-        USER_REDIRECTION_URL: currentURL,
-      })
-    );
-  }
 
-  // URL tracking effect
   useEffect(() => {
-    const currentURL = location.pathname;
-    console.log("currentURL", currentURL);
-    if (!unpreservedPaths.includes(currentURL)) {
-      LocalStorageOps.setItem("lastVisitedURL", "/#" + currentURL);
-    }
-    //Check if toBePreservedPaths has any key with location.pathname substring
-    if (
-      Object.keys(toBePreservedPaths).some((path) => currentURL.includes(path))
-    ) {
-      saveUserUrl();
+    if (!isLoading && !isAuthenticated && location.pathname !== "/") {
+      savePreferredPath(`${location.pathname}${location.search}`);
     }
-  }, [location]);
+  }, [isAuthenticated, isLoading, location.pathname, location.search]);
 
-  // Check if cluster exists when component mounts
   useEffect(() => {
-    async function checkCluster() {
+    if (!isAuthenticated) {
+      return;
+    }
+
+    let stopped = false;
+    let timeoutId: ReturnType<typeof setTimeout> | undefined;
+    const keepAlive = async () => {
       try {
-        const response = await clusterApi.getClusterData();
-        const firstCluster = response?.items?.[0]?.Clusters;
-        if (firstCluster && firstCluster?.provisioning_state === "INSTALLED")
-          setHasCluster(true);
-      } catch (error) {
-        console.error("Error checking cluster existence:", error);
-        setHasCluster(false);
+        await ClusterApi.noopPolling();
+      } catch {
+        // The global response handler owns authentication failures.
       } finally {
-        setIsClusterChecked(true);
+        if (!stopped) {
+          timeoutId = setTimeout(keepAlive, 60_000);
+        }
       }
-    }
-    checkCluster();
-  }, []); // Run only once on mount
+    };
+    timeoutId = setTimeout(keepAlive, 60_000);
+    return () => {
+      stopped = true;
+      if (timeoutId) clearTimeout(timeoutId);
+    };
+  }, [isAuthenticated]);
+
+  if (isLoading) {
+    return <div className="p-5"><h2>Loading...</h2></div>;
+  }
+  if (sessionError) {
+    return (
+      <div className="container py-5">
+        <Alert variant="danger">
+          <Alert.Heading>Unable to validate the Ambari session</Alert.Heading>
+          <p>{sessionError}</p>
+          <Button variant="outline-danger" onClick={() => void retrySession()}>
+            Retry
+          </Button>
+        </Alert>
+      </div>
+    );
+  }
+  if (!isAuthenticated) {
+    return <Navigate to="/login" replace />;
+  }
+  if (loginMessage) {
+    return <LoginMessageModal />;
+  }
 
-  // Handle routing after cluster check
-  useEffect(() => {
-    if (!isClusterChecked) return;
+  return (
+    <AppProvider>
+      <ModalProvider>
+        <ApplicationLoader />
+      </ModalProvider>
+    </AppProvider>
+  );
+}
 
-    const currentURL = location.pathname;
-    const hashExclusiveUrl = LocalStorageOps.getItem("lastVisitedURL")?.replace(
-      "/#",
-      ""
-    );
-    let relocationPath = currentURL;
-    if (hasCluster && currentURL.includes("/installer")) {
-      relocationPath = "/main/dashboard/metrics";
-    } else if (!hasCluster && currentURL.includes("/main")) {
-      relocationPath = "/installer/step0";
-    } else {
-      if (currentURL === "/") {
-        if (hashExclusiveUrl&&hasCluster) {
-          relocationPath = hashExclusiveUrl;
-        } else {
-          if (hasCluster) {
-            relocationPath = "/main/dashboard/metrics";
-          } else {
-            relocationPath = "/installer/step0";
-          }
-        }
-      } else {
-        relocationPath = currentURL;
-      }
-    }
+export function LandingRoute() {
+  const { cluster, isClusterInstalled } = useContext(AppContext);
+  const { authorizations } = useAuth();
+  const preferredPath = consumePreferredPath();
+  return <Navigate to={selectLandingPath({
+    clusterInstalled: Boolean(isClusterInstalled),
+    clusterName: cluster?.cluster_name,
+    preferredPath,
+    viewOnly: isViewOnlyUser(authorizations),
+  })} replace />;
+}
 
-    navigate(relocationPath);
-  }, [isClusterChecked, hasCluster, lastVisitedURL]);
+export function RouteTracker() {
+  const { cluster, isClusterInstalled } = useContext(AppContext);
+  const location = useLocation();
+  const navigate = useNavigate();
 
   useEffect(() => {
-    if (hasCluster === null) {
+    const currentPath = normalizeInternalPath(`${location.pathname}${location.search}`);
+    if (!currentPath || location.pathname === "/") {
       return;
     }
-    const currentURL = location.pathname;
-    if (hasCluster && currentURL.startsWith("/installer")) {
-      navigate("/main/dashboard/metrics");
-    } else if (!hasCluster && currentURL.startsWith("/main")) {
-      navigate("/installer/step0");
+
+    savePreferredPath(currentPath);
+    if (Object.keys(toBePreservedPaths).some((path) => location.pathname.includes(path))) {
+      void ClusterApi.postPersistData({ USER_REDIRECTION_URL: currentPath });
     }
-  }, [location, hasCluster]);
+  }, [location.pathname, location.search]);
+
+  useEffect(() => {
+    if (isClusterInstalled && location.pathname.startsWith("/installer")) {
+      navigate("/main/dashboard/metrics", { replace: true });
+    } else if (
+      cluster?.cluster_name
+      && !isClusterInstalled
+      && location.pathname.startsWith("/main")
+    ) {
+      navigate("/installer/step0", { replace: true });
+    }
+  }, [cluster?.cluster_name, isClusterInstalled, location.pathname, navigate]);
 
   return null;
 }
 
-function AppLoader() {
-  const { isAppLoaded } = useContext(AppContext);
+function ApplicationLoader() {
+  const { initializationError, isAppLoaded, retryInitialization } = useContext(AppContext);
   const [progress, setProgress] = useState(0);
+  const { stopPolling } = usePolling(
+    () => setProgress((value) => Math.min(value + 10, 90)),
+    500,
+  );
 
   useEffect(() => {
     if (isAppLoaded) {
       stopPolling();
     }
-  }, [isAppLoaded]);
-  function incrementProgress() {
-    setProgress((p) => p + 20);
-  }
+  }, [isAppLoaded, stopPolling]);
 
-  const { stopPolling } = usePolling(incrementProgress,500);
-
-  const router = createHashRouter([...(RoutesList as any)]);
+  if (initializationError) {
+    return (
+      <div className="container py-5">
+        <Alert variant="danger">
+          <Alert.Heading>Unable to initialize Ambari</Alert.Heading>
+          <p>{initializationError}</p>
+          <Button variant="outline-danger" onClick={retryInitialization}>Retry</Button>
+        </Alert>
+      </div>
+    );
+  }
   if (!isAppLoaded) {
     return (
       <div className="vh-100 mt-0">
         <div className="p-5">
           <h2>Loading...</h2>
-          <ProgressBar  variant="blue" now={progress} className="mt-3" />
+          <ProgressBar variant="blue" now={progress} className="mt-3" />
         </div>
       </div>
     );
   }
+
   return (
     <>
-      <RouterProvider router={router} />
+      <RouteTracker />
+      <DocumentTitleUpdater />
+      <InactivityTimeout />
+      <CustomModal />
+      <Outlet />
     </>
   );
 }
 
-export default AppLoader;
+export default ApplicationLoader;
diff --git a/ambari-web/latest/src/InactivityTimeout.test.ts b/ambari-web/latest/src/InactivityTimeout.test.ts
new file mode 100644
index 0000000000..9c0984142c
--- /dev/null
+++ b/ambari-web/latest/src/InactivityTimeout.test.ts
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { describe, expect, it } from "vitest";
+import {
+  inactivityTimeoutSeconds,
+  isInactivityRouteExcluded,
+  remainingInactivitySeconds,
+} from "./InactivityTimeout";
+
+describe("inactivity policy", () => {
+  const properties = {
+    "user.inactivity.timeout.default": "900",
+    "user.inactivity.timeout.role.readonly.default": "300",
+  };
+
+  it("selects the administrator and readonly server properties", () => {
+    expect(inactivityTimeoutSeconds(properties, true)).toBe(900);
+    expect(inactivityTimeoutSeconds(properties, false)).toBe(300);
+    expect(inactivityTimeoutSeconds({}, true)).toBe(0);
+  });
+
+  it("excludes wizard steps and stack upgrade routes", () => {
+    expect(isInactivityRouteExcluded("/installer/step3")).toBe(true);
+    expect(isInactivityRouteExcluded("/main/admin/stack/upgrade")).toBe(true);
+    expect(isInactivityRouteExcluded("/main/hosts")).toBe(false);
+  });
+
+  it("computes the warning countdown from the last activity", () => {
+    expect(remainingInactivitySeconds(300, 1_000, 241_000)).toBe(60);
+    expect(remainingInactivitySeconds(300, 1_000, 301_000)).toBe(0);
+  });
+});
diff --git a/ambari-web/latest/src/InactivityTimeout.tsx b/ambari-web/latest/src/InactivityTimeout.tsx
index 90b943c229..3f86d9934a 100644
--- a/ambari-web/latest/src/InactivityTimeout.tsx
+++ b/ambari-web/latest/src/InactivityTimeout.tsx
@@ -16,20 +16,179 @@
  * limitations under the License.
  */
 
-interface InactivityTimeoutProps {
-  timeout?: number; // Make optional since we'll get it from server properties
+import { useCallback, useContext, useEffect, useRef, useState } from "react";
+import { useLocation, useNavigate } from "react-router-dom";
+import Modal from "./components/Modal";
+import { AppContext } from "./store/context";
+import { useAuth } from "./hooks/useAuth";
+
+const WARNING_SECONDS = 60;
+const ACTIVITY_EVENTS: (keyof WindowEventMap)[] = ["mousemove", "keypress", "click"];
+
+export function inactivityTimeoutSeconds(
+  properties: Record<string, unknown> | undefined,
+  admin: boolean,
+): number {
+  const value = Number(properties?.[
+    admin
+      ? "user.inactivity.timeout.default"
+      : "user.inactivity.timeout.role.readonly.default"
+  ] || 0);
+  return Number.isFinite(value) && value > 0 ? value : 0;
 }
 
-const InactivityTimeout: React.FC<InactivityTimeoutProps> = ({ timeout: propTimeout }) => {
+export function isInactivityRouteExcluded(pathname: string): boolean {
+  return pathname.includes("/step") || pathname.includes("/stack/upgrade");
+}
+
+export function remainingInactivitySeconds(
+  timeoutSeconds: number,
+  lastActivityTime: number,
+  currentTime: number,
+): number {
+  return timeoutSeconds - Math.floor((currentTime - lastActivityTime) / 1000);
+}
+
+export default function InactivityTimeout() {
+  const { ambariProperties } = useContext(AppContext);
+  const { isAdmin, logout } = useAuth();
+  const location = useLocation();
+  const navigate = useNavigate();
+  const [remainingSeconds, setRemainingSeconds] = useState<number | null>(null);
+  const lastActivity = useRef(Date.now());
+  const warningVisible = useRef(false);
+  const signingOut = useRef(false);
+
+  const timeoutSeconds = inactivityTimeoutSeconds(ambariProperties, isAdmin());
+  const isExcludedRoute = isInactivityRouteExcluded(location.pathname);
+
+  const updateRemainingSeconds = useCallback((value: number | null) => {
+    warningVisible.current = value !== null;
+    setRemainingSeconds(value);
+  }, []);
+
+  const signOut = useCallback(async () => {
+    if (signingOut.current) {
+      return;
+    }
+    signingOut.current = true;
+    await logout();
+    navigate("/login", { replace: true });
+  }, [logout, navigate]);
+
+  useEffect(() => {
+    if (timeoutSeconds <= 0) {
+      return;
+    }
+
+    const markActive = () => {
+      if (!warningVisible.current) {
+        lastActivity.current = Date.now();
+      }
+    };
+    const bindFrame = (frame: HTMLIFrameElement, shouldBind: boolean) => {
+      try {
+        ACTIVITY_EVENTS.forEach((eventName) => {
+          if (shouldBind) {
+            frame.contentWindow?.addEventListener(eventName, markActive);
+          } else {
+            frame.contentWindow?.removeEventListener(eventName, markActive);
+          }
+        });
+      } catch {
+        // Cross-origin View iframes cannot be observed by the parent application.
+      }
+    };
+    const trackedFrames = new Map<HTMLIFrameElement, () => void>();
+    const trackFrame = (frame: HTMLIFrameElement) => {
+      if (trackedFrames.has(frame)) {
+        return;
+      }
+      const bindLoadedFrame = () => bindFrame(frame, true);
+      trackedFrames.set(frame, bindLoadedFrame);
+      frame.addEventListener("load", bindLoadedFrame);
+      bindLoadedFrame();
+    };
+    const untrackFrame = (frame: HTMLIFrameElement) => {
+      const bindLoadedFrame = trackedFrames.get(frame);
+      if (!bindLoadedFrame) {
+        return;
+      }
+      bindFrame(frame, false);
+      frame.removeEventListener("load", bindLoadedFrame);
+      trackedFrames.delete(frame);
+    };
+    const visitFrames = (node: Node, callback: (frame: HTMLIFrameElement) => void) => {
+      if (!(node instanceof Element)) {
+        return;
+      }
+      if (node instanceof HTMLIFrameElement) {
+        callback(node);
+      }
+      node.querySelectorAll("iframe").forEach(callback);
+    };
+
+    ACTIVITY_EVENTS.forEach((eventName) => window.addEventListener(eventName, markActive));
+    document.querySelectorAll("iframe").forEach(trackFrame);
+    const frameObserver = new MutationObserver((mutations) => {
+      mutations.forEach((mutation) => {
+        mutation.addedNodes.forEach((node) => visitFrames(node, trackFrame));
+        mutation.removedNodes.forEach((node) => visitFrames(node, untrackFrame));
+      });
+    });
+    frameObserver.observe(document.body, { childList: true, subtree: true });
+    const intervalId = window.setInterval(() => {
+      if (isExcludedRoute) {
+        lastActivity.current = Date.now();
+        updateRemainingSeconds(null);
+        return;
+      }
+
+      const remaining = remainingInactivitySeconds(
+        timeoutSeconds,
+        lastActivity.current,
+        Date.now(),
+      );
+      if (remaining <= 0) {
+        void signOut();
+      } else if (remaining <= WARNING_SECONDS) {
+        updateRemainingSeconds(remaining);
+      }
+    }, 1000);
+
+    return () => {
+      window.clearInterval(intervalId);
+      frameObserver.disconnect();
+      ACTIVITY_EVENTS.forEach((eventName) => window.removeEventListener(eventName, markActive));
+      Array.from(trackedFrames.keys()).forEach(untrackFrame);
+    };
+  }, [isExcludedRoute, signOut, timeoutSeconds, updateRemainingSeconds]);
+
+  if (remainingSeconds === null) {
+    return null;
+  }
+
   return (
-    <div className="inactivity-timeout">
-      {propTimeout ? (
-        <p>Your session will expire after {propTimeout} minutes of inactivity.</p>
-      ) : (
-        <p>Loading inactivity timeout...</p>
+    <Modal
+      isOpen
+      onClose={() => undefined}
+      modalTitle="Session Expiration"
+      modalBody={(
+        <p>
+          Your session will expire in <strong>{remainingSeconds}</strong> seconds due to inactivity.
+        </p>
       )}
-    </div>
+      successCallback={() => {
+        lastActivity.current = Date.now();
+        updateRemainingSeconds(null);
+      }}
+      options={{
+        cancelableViaIcon: false,
+        okButtonText: "Continue Session",
+        cancelButtonText: "Sign Out",
+        extraButtons: [{ text: "Sign Out", onClick: () => void signOut(), variant: "secondary" }],
+        cancelableViaBtn: false,
+      }}
+    />
   );
-};
-
-export default InactivityTimeout;
+}
diff --git a/ambari-web/latest/src/Utils/adminViewRedirect.ts b/ambari-web/latest/src/Utils/adminViewRedirect.ts
index cde96b3bf5..78e9113e8f 100644
--- a/ambari-web/latest/src/Utils/adminViewRedirect.ts
+++ b/ambari-web/latest/src/Utils/adminViewRedirect.ts
@@ -17,5 +17,6 @@
  */
 
 export async function redirectToAdminView(adminPage = "") {
-  console.log("Redirecting to admin view:", adminPage);
-}
\ No newline at end of file
+  const suffix = adminPage ? `?page=${encodeURIComponent(adminPage)}` : "";
+  window.location.hash = `/adminView${suffix}`;
+}
diff --git a/ambari-web/latest/src/Utils/authNavigation.test.ts b/ambari-web/latest/src/Utils/authNavigation.test.ts
new file mode 100644
index 0000000000..2226ebaa4f
--- /dev/null
+++ b/ambari-web/latest/src/Utils/authNavigation.test.ts
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { beforeEach, describe, expect, it, vi } from "vitest";
+import {
+  getExternalLoginUrl,
+  handleAuthenticationError,
+  normalizeInternalPath,
+  SESSION_EXPIRED_EVENT,
+} from "./authNavigation";
+
+describe("authentication navigation", () => {
+  beforeEach(() => {
+    localStorage.clear();
+    sessionStorage.clear();
+    window.location.hash = "#/login";
+  });
+
+  it.each([
+    ["/main/hosts", "/main/hosts"],
+    ["#/main/hosts?tab=summary", "/main/hosts?tab=summary"],
+    ["/#/installer/step0", "/installer/step0"],
+    ["//attacker.example/path", null],
+    ["https://attacker.example/path", null],
+    ["/login", null],
+    ["/login/local", null],
+    ["/", null],
+  ])("normalizes %s", (input, expected) => {
+    expect(normalizeInternalPath(input)).toBe(expected);
+  });
+
+  it("supports external login while preserving the return URL", () => {
+    expect(getExternalLoginUrl(
+      "https://knox.example/sso?originalUrl=",
+      "https://ambari.example/#/main/hosts",
+      false,
+    )).toBe(
+      "https://knox.example/sso?originalUrl="
+      + encodeURIComponent("https://ambari.example/#/main/hosts"),
+    );
+  });
+
+  it("does not redirect local login and caps external redirect loops", () => {
+    expect(getExternalLoginUrl("https://knox.example/", "return", true)).toBeNull();
+    expect(getExternalLoginUrl("https://knox.example/", "return", false)).not.toBeNull();
+    expect(getExternalLoginUrl("https://knox.example/", "return", false)).not.toBeNull();
+    expect(getExternalLoginUrl("https://knox.example/", "return", false)).not.toBeNull();
+    expect(getExternalLoginUrl("https://knox.example/", "return", false)).toBeNull();
+  });
+
+  it("expires the client session for 401 but not a plain authorization 403", () => {
+    const listener = vi.fn();
+    window.addEventListener(SESSION_EXPIRED_EVENT, listener);
+
+    expect(handleAuthenticationError({ status: 403, data: {} })).toBe(false);
+    expect(listener).not.toHaveBeenCalled();
+
+    expect(handleAuthenticationError({ status: 401, data: {} })).toBe(false);
+    expect(listener).toHaveBeenCalledOnce();
+    window.removeEventListener(SESSION_EXPIRED_EVENT, listener);
+  });
+});
diff --git a/ambari-web/latest/src/Utils/authNavigation.ts b/ambari-web/latest/src/Utils/authNavigation.ts
new file mode 100644
index 0000000000..dbbc1e95a7
--- /dev/null
+++ b/ambari-web/latest/src/Utils/authNavigation.ts
@@ -0,0 +1,152 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { LocalStorageOps } from "./LocalStorageOps";
+
+const PREFERRED_PATH_KEY = "lastVisitedURL";
+const REDIRECT_COUNT_KEY = "ambari.jwtRedirectCount";
+const MAX_EXTERNAL_REDIRECTS = 3;
+export const SESSION_EXPIRED_EVENT = "ambari:session-expired";
+
+export const LOGIN_PATH = "/login";
+export const LOCAL_LOGIN_PATH = "/login/local";
+
+export function currentHashPath(location: Pick<Location, "hash"> = window.location): string {
+  const value = location.hash.startsWith("#") ? location.hash.slice(1) : location.hash;
+  return value || "/";
+}
+
+export function normalizeInternalPath(value?: string | null): string | null {
+  if (!value) {
+    return null;
+  }
+
+  let candidate = value.trim();
+  if (candidate.startsWith("/#")) {
+    candidate = candidate.slice(2);
+  } else if (candidate.startsWith("#")) {
+    candidate = candidate.slice(1);
+  }
+
+  if (!candidate.startsWith("/") || candidate.startsWith("//")) {
+    return null;
+  }
+
+  const pathOnly = candidate.split("?")[0].split("#")[0];
+  if (pathOnly === "/" || pathOnly === LOGIN_PATH || pathOnly === LOCAL_LOGIN_PATH) {
+    return null;
+  }
+
+  return candidate;
+}
+
+export function savePreferredPath(path: string): void {
+  const normalized = normalizeInternalPath(path);
+  if (normalized) {
+    LocalStorageOps.setItem(PREFERRED_PATH_KEY, normalized);
+  }
+}
+
+export function consumePreferredPath(): string | null {
+  const normalized = normalizeInternalPath(LocalStorageOps.getItem(PREFERRED_PATH_KEY));
+  localStorage.removeItem(PREFERRED_PATH_KEY);
+  return normalized;
+}
+
+export function peekPreferredPath(): string | null {
+  return normalizeInternalPath(LocalStorageOps.getItem(PREFERRED_PATH_KEY));
+}
+
+export function resetExternalRedirectCount(): void {
+  sessionStorage.removeItem(REDIRECT_COUNT_KEY);
+}
+
+export function getExternalLoginUrl(
+  jwtProviderUrl: unknown,
+  returnUrl: string,
+  isLocalLogin: boolean,
+): string | null {
+  if (isLocalLogin || typeof jwtProviderUrl !== "string" || !jwtProviderUrl) {
+    return null;
+  }
+
+  const redirectCount = Number(sessionStorage.getItem(REDIRECT_COUNT_KEY) || "0");
+  if (!Number.isFinite(redirectCount) || redirectCount >= MAX_EXTERNAL_REDIRECTS) {
+    return null;
+  }
+
+  sessionStorage.setItem(REDIRECT_COUNT_KEY, String(redirectCount + 1));
+  return jwtProviderUrl + encodeURIComponent(returnUrl);
+}
+
+export function hasExceededExternalRedirectLimit(): boolean {
+  return Number(sessionStorage.getItem(REDIRECT_COUNT_KEY) || "0") >= MAX_EXTERNAL_REDIRECTS;
+}
+
+export function redirectToLocalLogin(): void {
+  const currentPath = currentHashPath();
+  if (currentPath !== LOGIN_PATH && currentPath !== LOCAL_LOGIN_PATH) {
+    savePreferredPath(currentPath);
+  }
+  window.location.hash = LOGIN_PATH;
+}
+
+function expireClientSession(): void {
+  window.dispatchEvent(new Event(SESSION_EXPIRED_EVENT));
+}
+
+export function handleAuthenticationError(response: {
+  status?: number;
+  data?: { jwtProviderUrl?: string };
+}): boolean {
+  if (response.status !== 401 && response.status !== 403) {
+    return false;
+  }
+
+  // A plain 403 is an authorization failure, not evidence that the session expired.
+  if (response.status === 403 && !response.data?.jwtProviderUrl) {
+    return false;
+  }
+
+  expireClientSession();
+
+  const currentPath = currentHashPath();
+  const isLocalLogin = currentPath.split("?")[0] === LOCAL_LOGIN_PATH;
+  const externalUrl = getExternalLoginUrl(
+    response.data?.jwtProviderUrl,
+    window.location.href,
+    isLocalLogin,
+  );
+
+  if (externalUrl) {
+    window.location.assign(externalUrl);
+    return true;
+  }
+
+  if (hasExceededExternalRedirectLimit() && !isLocalLogin) {
+    window.location.hash = `${LOCAL_LOGIN_PATH}?redirectError=true`;
+    return true;
+  }
+
+  if (currentPath !== LOGIN_PATH && !isLocalLogin) {
+    redirectToLocalLogin();
+    return true;
+  }
+
+  return false;
+}
diff --git a/ambari-web/latest/src/Utils/authPolicy.test.ts b/ambari-web/latest/src/Utils/authPolicy.test.ts
new file mode 100644
index 0000000000..cf63694150
--- /dev/null
+++ b/ambari-web/latest/src/Utils/authPolicy.test.ts
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { describe, expect, it } from "vitest";
+import { hasVersionConflict, selectLandingPath } from "./authPolicy";
+
+describe("application landing policy", () => {
+  it.each([
+    [{ clusterInstalled: true, clusterName: "c1", viewOnly: false }, "/main/dashboard/metrics"],
+    [{
+      clusterInstalled: true,
+      clusterName: "c1",
+      preferredPath: "/main/hosts",
+      viewOnly: false,
+    }, "/main/hosts"],
+    [{ clusterInstalled: false, clusterName: "c1", viewOnly: false }, "/installer/step0"],
+    [{ clusterInstalled: false, clusterName: "", viewOnly: false }, "/adminView"],
+    [{ clusterInstalled: true, clusterName: "c1", viewOnly: true }, "/main/view"],
+  ])("selects the expected landing path", (input, expected) => {
+    expect(selectLandingPath(input)).toBe(expected);
+  });
+
+  it.each([
+    ["", "2.0.0", false],
+    ["2.0.0", "", false],
+    ["2.0.0", "2.0.0", false],
+    ["2.0.0", "2.0.1", true],
+  ])("compares packaged and server versions", (client, server, expected) => {
+    expect(hasVersionConflict(client, server)).toBe(expected);
+  });
+});
diff --git a/ambari-web/latest/src/Utils/authPolicy.ts b/ambari-web/latest/src/Utils/authPolicy.ts
new file mode 100644
index 0000000000..acd3da9551
--- /dev/null
+++ b/ambari-web/latest/src/Utils/authPolicy.ts
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Authorization } from "../types/auth";
+
+export function isViewOnlyUser(authorizations: Authorization[]): boolean {
+  return authorizations.length === 0
+    || (authorizations.length === 1
+      && authorizations[0].authorization_id === "VIEW.USE");
+}
+
+export function selectLandingPath({
+  clusterInstalled,
+  clusterName,
+  preferredPath,
+  viewOnly,
+}: {
+  clusterInstalled: boolean;
+  clusterName?: string;
+  preferredPath?: string | null;
+  viewOnly: boolean;
+}): string {
+  if (viewOnly) {
+    return "/main/view";
+  }
+  if (clusterInstalled) {
+    return preferredPath || "/main/dashboard/metrics";
+  }
+  if (clusterName) {
+    return "/installer/step0";
+  }
+  return "/adminView";
+}
+
+export function hasVersionConflict(clientVersion: string, serverVersion: string): boolean {
+  return Boolean(clientVersion && serverVersion && clientVersion !== serverVersion);
+}
diff --git a/ambari-web/latest/src/Utils/db.ts b/ambari-web/latest/src/Utils/db.ts
index e2352c6189..85934fc0d6 100644
--- a/ambari-web/latest/src/Utils/db.ts
+++ b/ambari-web/latest/src/Utils/db.ts
@@ -79,10 +79,10 @@ interface DbData {
       this.data = this.getDb() || InitialData;
     }
 
-    private checkNamespace(namespace: string): boolean {
+    private checkNamespace(data: DbData, namespace: string): boolean {
       if (!namespace) return false;
-      if (!this.data[namespace]) {
-        this.data[namespace] = {};
+      if (!data[namespace]) {
+        data[namespace] = {};
       }
       return true;
     }
@@ -90,7 +90,12 @@ interface DbData {
     getDb(): DbData | null {
       try {
         const stored = localStorage.getItem('ambari');
-        return stored ? JSON.parse(stored) : null;
+        if (!stored) return null;
+        try {
+          return JSON.parse(Utility.decryptData(stored));
+        } catch {
+          return JSON.parse(stored);
+        }
       } catch (e) {
         console.error('Error reading from localStorage:', e);
         return null;
@@ -99,7 +104,7 @@ interface DbData {
 
     private setDb(data: DbData): void {
       try {
-        localStorage.setItem('ambari', JSON.stringify(data));
+        localStorage.setItem('ambari', Utility.encryptData(JSON.stringify(data)));
       } catch (e) {
         console.error('Error writing to localStorage:', e);
       }
@@ -108,8 +113,14 @@ interface DbData {
     getItem(key: string): string | null {
       const value = localStorage.getItem(key);
       if (value === null) return null;
-      return Utility.decryptData(value);
-}
+      const decrypted = Utility.decryptData(value);
+      try {
+        JSON.parse(decrypted);
+        return decrypted;
+      } catch {
+        return value;
+      }
+    }
     setItem(key: string, value: string): void {
       try {
         // Always encrypt before storing to ensure consistency
@@ -124,34 +135,63 @@ interface DbData {
     }
 
     cleanUp(): void {
-      this.data = InitialData;
+      this.data = JSON.parse(JSON.stringify(InitialData));
       this.setDb(this.data);
     }
 
+    clearSession(): void {
+      const data = this.getDb() || this.getInitialData();
+      data.app = {
+        ...data.app,
+        loginName: '',
+        authenticated: false,
+      };
+      delete data.app.user;
+      delete data.app.auth;
+      this.data = data;
+      this.setDb(data);
+    }
+
     createNameSpace(namespace: string): void {
-      if (!this.data[namespace]) {
-        this.data[namespace] = {};
-        this.setDb(this.data);
+      const data = this.getDb() || this.getInitialData();
+      if (!data[namespace]) {
+        data[namespace] = {};
+        this.data = data;
+        this.setDb(data);
       }
     }
 
     // Core get/set methods
     get(namespace: string, key: string): any {
       const data = this.getDb();
-      if (!data || !this.checkNamespace(namespace)) return null;
+      if (!data || !this.checkNamespace(data, namespace)) return null;
       return key.includes('user-pref') ? 
         data[namespace][key] : 
         this.getNestedValue(data[namespace], key);
     }
 
     set(namespace: string, key: string, value: any): void {
-      const data = this.getDb() || InitialData;
-      if (!this.checkNamespace(namespace)) return;
+      const data = this.getDb() || this.getInitialData();
+      if (!this.checkNamespace(data, namespace)) return;
       if (key.includes('user-pref')) {
         data[namespace][key] = value;
       } else {
         this.setNestedValue(data[namespace], key, value);
       }
+      this.data = data;
+      this.setDb(data);
+    }
+
+    setSession(loginName: string, user: unknown, authorizations: string[]): void {
+      const data = this.getDb() || this.getInitialData();
+      data.app = {
+        ...data.app,
+        loginName: encodeURIComponent(loginName),
+        authenticated: true,
+        user,
+        auth: authorizations,
+      };
+      this.data = data;
       this.setDb(data);
     }
 
@@ -169,10 +209,10 @@ interface DbData {
       target[last] = value;
     }
 
-    getInitialData(): string {
+    getInitialData(): DbData {
       return JSON.parse(JSON.stringify(InitialData));
     }
 
   }
 
-  export const db = new Database()
\ No newline at end of file
+  export const db = new Database()
diff --git a/ambari-web/latest/vite.config.ts b/ambari-web/latest/src/Utils/httpError.ts
old mode 100755
new mode 100644
similarity index 76%
copy from ambari-web/latest/vite.config.ts
copy to ambari-web/latest/src/Utils/httpError.ts
index 929c08ff59..5655220040
--- a/ambari-web/latest/vite.config.ts
+++ b/ambari-web/latest/src/Utils/httpError.ts
@@ -16,10 +16,11 @@
  * limitations under the License.
  */
 
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
+import axios from "axios";
 
-export default defineConfig({
-  plugins: [react()],
-  base:"./"
-})
+export function responseErrorMessage(error: unknown, fallback: string): string {
+  if (axios.isAxiosError<{ message?: string }>(error)) {
+    return error.response?.data?.message || fallback;
+  }
+  return fallback;
+}
diff --git a/ambari-web/latest/src/Utils/persistedSettings.test.ts b/ambari-web/latest/src/Utils/persistedSettings.test.ts
new file mode 100644
index 0000000000..27cfd97445
--- /dev/null
+++ b/ambari-web/latest/src/Utils/persistedSettings.test.ts
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { describe, expect, it } from "vitest";
+import { parsePersistedValue, persistedPayload } from "./persistedSettings";
+
+describe("persisted settings", () => {
+  it("round trips booleans, strings, and objects as JSON values", () => {
+    const payload = persistedPayload({ enabled: false, timezone: "UTC", wizard: {} });
+    expect(payload).toEqual({
+      enabled: "false",
+      timezone: '"UTC"',
+      wizard: "{}",
+    });
+    expect(parsePersistedValue(payload.enabled, true)).toBe(false);
+    expect(parsePersistedValue(payload.timezone, "Browser")).toBe("UTC");
+    expect(parsePersistedValue(payload.wizard, { stale: true })).toEqual({});
+  });
+
+  it("uses the fallback for empty or invalid persisted data", () => {
+    expect(parsePersistedValue(undefined, true)).toBe(true);
+    expect(parsePersistedValue("not-json", "Browser")).toBe("Browser");
+  });
+});
diff --git a/ambari-web/latest/src/InactivityTimeout.tsx b/ambari-web/latest/src/Utils/persistedSettings.ts
similarity index 61%
copy from ambari-web/latest/src/InactivityTimeout.tsx
copy to ambari-web/latest/src/Utils/persistedSettings.ts
index 90b943c229..88bfcc9925 100644
--- a/ambari-web/latest/src/InactivityTimeout.tsx
+++ b/ambari-web/latest/src/Utils/persistedSettings.ts
@@ -16,20 +16,22 @@
  * limitations under the License.
  */
 
-interface InactivityTimeoutProps {
-  timeout?: number; // Make optional since we'll get it from server properties
+export function parsePersistedValue<T>(value: unknown, fallback: T): T {
+  if (value === undefined || value === null || value === "") {
+    return fallback;
+  }
+  if (typeof value !== "string") {
+    return value as T;
+  }
+  try {
+    return JSON.parse(value) as T;
+  } catch {
+    return fallback;
+  }
 }
 
-const InactivityTimeout: React.FC<InactivityTimeoutProps> = ({ timeout: propTimeout }) => {
-  return (
-    <div className="inactivity-timeout">
-      {propTimeout ? (
-        <p>Your session will expire after {propTimeout} minutes of inactivity.</p>
-      ) : (
-        <p>Loading inactivity timeout...</p>
-      )}
-    </div>
+export function persistedPayload(values: Record<string, unknown>): Record<string, string> {
+  return Object.fromEntries(
+    Object.entries(values).map(([key, value]) => [key, JSON.stringify(value)]),
   );
-};
-
-export default InactivityTimeout;
+}
diff --git a/ambari-web/latest/src/api/clusterApi.ts b/ambari-web/latest/src/api/clusterApi.ts
index dca1291b8e..344e7a9a0f 100644
--- a/ambari-web/latest/src/api/clusterApi.ts
+++ b/ambari-web/latest/src/api/clusterApi.ts
@@ -122,7 +122,7 @@ const ClusterApi = {
     return clusterName;
   },
   getClusterData: async function () {
-    const url= `/clusters?fields=Clusters/provisioning_state,Clusters/security_type,Clusters/version,Clusters/cluster_id`;
+    const url= `/clusters?fields=Clusters/cluster_name,Clusters/provisioning_state,Clusters/security_type,Clusters/version,Clusters/cluster_id`;
     const response = await ambariApi.request({
       url: url,
       method: "GET",
@@ -176,7 +176,7 @@ const ClusterApi = {
       url: '/services/AMBARI/components/AMBARI_SERVER',
       method: 'GET',
       params: {
-        fields: 'RootServiceComponents/properties/user.inactivity.timeout.default',
+        fields: 'RootServiceComponents/properties/user.inactivity.timeout.default,RootServiceComponents/properties/user.inactivity.timeout.role.readonly.default',
         _: Date.now() // Cache buster
       }
     });
@@ -212,4 +212,4 @@ const ClusterApi = {
   }
 }
 
-export default ClusterApi;
\ No newline at end of file
+export default ClusterApi;
diff --git a/ambari-web/latest/src/api/config/axiosConfig.ts b/ambari-web/latest/src/api/config/axiosConfig.ts
index 8f98a7cab2..ecc6e42b0f 100644
--- a/ambari-web/latest/src/api/config/axiosConfig.ts
+++ b/ambari-web/latest/src/api/config/axiosConfig.ts
@@ -19,6 +19,13 @@
 import axios from "axios";
 import { toast } from "react-hot-toast";
 import { get } from "lodash";
+import { handleAuthenticationError } from "../../Utils/authNavigation";
+
+declare module "axios" {
+  export interface AxiosRequestConfig {
+    skipAuthRedirect?: boolean;
+  }
+}
 
 const config = {
   development: {
@@ -63,13 +70,10 @@ const createAxiosInstance = (baseURL: string, headers = {}) => {
 
   instance.interceptors.response.use(undefined, (error) => {
     const responseMessage = get(error, "response.data.message", undefined);
-    // Check for 403 Forbidden status
-    if (error.response && error.response.status === 403) {
-      // Redirect to login page
-      window.location.href = "/#/login";
-      return Promise.reject(error);
+    if (error.response && !error.config?.skipAuthRedirect) {
+      handleAuthenticationError(error.response);
     }
-    if (responseMessage && error.response.status !== 400) {
+    if (responseMessage && error.response?.status !== 400 && !error.config?.skipAuthRedirect) {
       toast.error(responseMessage);
     }
     return Promise.reject(error);
@@ -93,11 +97,8 @@ const createSupressErrorAxiosInstance = (baseURL: string, headers = {}) => {
   });
 
   instance.interceptors.response.use(undefined, (error) => {
-    // Check for 403 Forbidden status
-    if (error.response && error.response.status === 403) {
-      // Redirect to login page
-      window.location.href = "/#/login";
-      return Promise.reject(error);
+    if (error.response && !error.config?.skipAuthRedirect) {
+      handleAuthenticationError(error.response);
     }
     return Promise.reject(error);
   });
diff --git a/ambari-web/latest/src/api/loginApi.test.ts b/ambari-web/latest/src/api/loginApi.test.ts
new file mode 100644
index 0000000000..ce965a1c2f
--- /dev/null
+++ b/ambari-web/latest/src/api/loginApi.test.ts
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { beforeEach, describe, expect, it, vi } from "vitest";
+
+const mocks = vi.hoisted(() => ({ request: vi.fn() }));
+vi.mock("./config/axiosConfig", () => ({
+  ambariApi: { request: mocks.request },
+}));
+
+import LoginApi from "./loginApi";
+
+describe("login API", () => {
+  beforeEach(() => mocks.request.mockReset());
+
+  it("sends UTF-8 Basic credentials without exposing them in the body", async () => {
+    mocks.request.mockResolvedValue({});
+    await LoginApi.authenticate("用户", "päss");
+
+    expect(mocks.request).toHaveBeenCalledWith(expect.objectContaining({
+      url: "/auth",
+      method: "POST",
+      headers: expect.objectContaining({
+        Authorization: `Basic ${Buffer.from("用户:päss", "utf8").toString("base64")}`,
+      }),
+      skipAuthRedirect: true,
+    }));
+  });
+
+  it("URL-encodes the username exactly once at the API boundary", async () => {
+    mocks.request.mockResolvedValue({});
+    await LoginApi.handleSuccessfulLogin({ usr: "", loginName: "user/name %" });
+    expect(mocks.request).toHaveBeenCalledWith(expect.objectContaining({
+      url: "/users/user%2Fname%20%25?fields=*,privileges/PrivilegeInfo/*",
+    }));
+  });
+});
diff --git a/ambari-web/latest/src/api/loginApi.ts b/ambari-web/latest/src/api/loginApi.ts
index 65dc9a93bc..9296827257 100644
--- a/ambari-web/latest/src/api/loginApi.ts
+++ b/ambari-web/latest/src/api/loginApi.ts
@@ -18,21 +18,17 @@
 
 import {ambariApi} from "./config/axiosConfig";
 import {misc} from "../Utils/misc";
-import { db } from "../Utils/db";
 import Cookies from 'js-cookie';
 
 interface LoginParams {
   usr: string;
   loginName: string;
 }
-interface dataLoginType {
-  Users: any;
-  loginData: any;
-}
-interface LoginDataParamsType {
-  loginName: string;
-  loginData: any;
+export interface LoginMessage {
+  text: string;
+  buttonText: string;
 }
+
 const LoginApi = {
 
   authenticate: async function (username: string, password: string) {
@@ -44,11 +40,12 @@ const LoginApi = {
         'Content-Type': 'text/plain',
         Authorization: "Basic " + hashForUserNamePassword,
       },
+      skipAuthRedirect: true,
     });
     return response;
   },
   handleSuccessfulLogin: async function (params: LoginParams) {
-    const url = `/users/${encodeURIComponent(params.loginName)}?fields=*,privileges/PrivilegeInfo/cluster_name,privileges/PrivilegeInfo/permission_name`;
+    const url = `/users/${encodeURIComponent(params.loginName)}?fields=*,privileges/PrivilegeInfo/*`;
     const response = await ambariApi.request({
       url: url,
       method: "GET",
@@ -68,31 +65,42 @@ const LoginApi = {
     });
     return response;
   },
-  afterLoginSuccessCallback: async function(data: dataLoginType) {
-    const response = await ambariApi.request({
-      url: "/settings/motd",
+  loadPrivileges: async function(loginName: string) {
+    return ambariApi.request({
+      url: `/users/${encodeURIComponent(loginName)}/privileges?fields=*`,
       method: "GET",
-      data: {
-        loginName: data.Users.user_name,
-        loginData: data
-      }
     });
-    return response.data;
   },
-  setClusterDataCallback: async function(params: LoginDataParamsType) {
-    const requestData = {
-      loginName: params.loginName,
-      loginData: params.loginData
-    };
-    const response = await ambariApi.request({
+  probeSession: async function() {
+    return ambariApi.request({
       url: "/clusters?fields=Clusters/provisioning_state,Clusters/security_type,Clusters/version,Clusters/cluster_id",
-      data: requestData,
-    })
-    return response;
+      method: "GET",
+    });
+  },
+  loadLoginMessage: async function(): Promise<LoginMessage | null> {
+    try {
+      const response = await ambariApi.request({
+        url: "/settings/motd",
+        method: "GET",
+      });
+      const content = response.data?.Settings?.content;
+      if (typeof content !== "string") {
+        return null;
+      }
+      const parsed = JSON.parse(content.replace(/\n/g, "\\n"));
+      if (parsed?.status !== "true" || !parsed?.text) {
+        return null;
+      }
+      return {
+        text: String(parsed.text).replace(/(\r\n|\n|\r)/g, "\n"),
+        buttonText: parsed.button ? String(parsed.button) : "OK",
+      };
+    } catch {
+      return null;
+    }
   },
   logout: async () => {
-    Cookies.remove('AMBARISESSIONID', { path: '/' ,domain: 'localhost', secure: true })    
-    console.log('After logout cookies:', document.cookie);
+    Cookies.remove('AMBARISESSIONID', { path: '/' });
     const timestamp = Date.now();
     try {
       const response = await ambariApi.request({
@@ -108,23 +116,12 @@ const LoginApi = {
         }
       });
 
-      // Only clean up and redirect on successful logout
-      if (response.status === 200) {
-        db.cleanUp();
-            // Remove AMBARI-SESSION-ID cookie
-        // // Clear auth header
-        // delete ambariApi.defaults.headers.common['Authorization'];
-        // // Clear JWT cookie
-        // document.cookie = "jwt=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
-        window.location.replace('/#/login');
-      }
       return response;
     } catch (error) {
       console.error('Logout failed:', error);
-      throw error; // Let the UI handle the error
+      throw error;
     }
   },
   
 }
 export default LoginApi;
-
diff --git a/ambari-web/latest/src/InactivityTimeout.tsx b/ambari-web/latest/src/components/AdminViewRouteGuard.test.ts
similarity index 53%
copy from ambari-web/latest/src/InactivityTimeout.tsx
copy to ambari-web/latest/src/components/AdminViewRouteGuard.test.ts
index 90b943c229..10bd036597 100644
--- a/ambari-web/latest/src/InactivityTimeout.tsx
+++ b/ambari-web/latest/src/components/AdminViewRouteGuard.test.ts
@@ -16,20 +16,28 @@
  * limitations under the License.
  */
 
-interface InactivityTimeoutProps {
-  timeout?: number; // Make optional since we'll get it from server properties
-}
+import { describe, expect, it } from "vitest";
+import { canEnterAdminView } from "./AdminViewRouteGuard";
 
-const InactivityTimeout: React.FC<InactivityTimeoutProps> = ({ timeout: propTimeout }) => {
-  return (
-    <div className="inactivity-timeout">
-      {propTimeout ? (
-        <p>Your session will expire after {propTimeout} minutes of inactivity.</p>
-      ) : (
-        <p>Loading inactivity timeout...</p>
-      )}
-    </div>
-  );
-};
+describe("Admin View route policy", () => {
+  it("keeps the classic stack-upgrade guard for an existing cluster", () => {
+    expect(canEnterAdminView({
+      ambariAdmin: true,
+      canUpgradeStack: false,
+      clusterName: "c1",
+    })).toBe(false);
+    expect(canEnterAdminView({
+      ambariAdmin: false,
+      canUpgradeStack: true,
+      clusterName: "c1",
+    })).toBe(true);
+  });
 
-export default InactivityTimeout;
+  it("allows the no-cluster Ambari administrator landing", () => {
+    expect(canEnterAdminView({
+      ambariAdmin: true,
+      canUpgradeStack: false,
+      clusterName: "",
+    })).toBe(true);
+  });
+});
diff --git a/ambari-web/latest/src/InactivityTimeout.tsx b/ambari-web/latest/src/components/AdminViewRouteGuard.tsx
similarity index 50%
copy from ambari-web/latest/src/InactivityTimeout.tsx
copy to ambari-web/latest/src/components/AdminViewRouteGuard.tsx
index 90b943c229..2787fec0de 100644
--- a/ambari-web/latest/src/InactivityTimeout.tsx
+++ b/ambari-web/latest/src/components/AdminViewRouteGuard.tsx
@@ -16,20 +16,31 @@
  * limitations under the License.
  */
 
-interface InactivityTimeoutProps {
-  timeout?: number; // Make optional since we'll get it from server properties
-}
+import { ReactNode, useContext } from "react";
+import { Navigate } from "react-router-dom";
+import { useAuth } from "../hooks/useAuth";
+import { AppContext } from "../store/context";
 
-const InactivityTimeout: React.FC<InactivityTimeoutProps> = ({ timeout: propTimeout }) => {
-  return (
-    <div className="inactivity-timeout">
-      {propTimeout ? (
-        <p>Your session will expire after {propTimeout} minutes of inactivity.</p>
-      ) : (
-        <p>Loading inactivity timeout...</p>
-      )}
-    </div>
-  );
-};
+export function canEnterAdminView({
+  ambariAdmin,
+  canUpgradeStack,
+  clusterName,
+}: {
+  ambariAdmin: boolean;
+  canUpgradeStack: boolean;
+  clusterName: string;
+}): boolean {
+  return canUpgradeStack || (!clusterName && ambariAdmin);
+}
 
-export default InactivityTimeout;
+export default function AdminViewRouteGuard({ children }: { children: ReactNode }) {
+  const { clusterName } = useContext(AppContext);
+  const { hasAuthorization, isAdmin } = useAuth();
+  return canEnterAdminView({
+    ambariAdmin: isAdmin(),
+    canUpgradeStack: hasAuthorization("CLUSTER.UPGRADE_DOWNGRADE_STACK"),
+    clusterName,
+  })
+    ? children
+    : <Navigate to="/" replace />;
+}
diff --git a/ambari-web/latest/src/components/AuthGuard.tsx b/ambari-web/latest/src/components/AuthGuard.tsx
index 9505ebd411..d85919b657 100644
--- a/ambari-web/latest/src/components/AuthGuard.tsx
+++ b/ambari-web/latest/src/components/AuthGuard.tsx
@@ -18,7 +18,7 @@
 
 import React from "react";
 import { useAuth } from "../hooks/useAuth";
-import { Alert } from "react-bootstrap";
+import { Navigate } from "react-router-dom";
 
 interface AuthGuardProps {
   children: React.ReactNode;
@@ -116,12 +116,16 @@ export const ProtectedRoute: React.FC<ProtectedRouteProps> = ({
   requireAuthorization,
   requirePrivilege,
   clusterName,
-  //@ts-ignore
   redirectTo = "/login",
 }) => {
-  const handleUnauthorized = () => {
-    
-  };
+  const { isAuthenticated, isLoading } = useAuth();
+  if (isLoading) {
+    return <div>Loading...</div>;
+  }
+
+  if (requireAuth && !isAuthenticated) {
+    return <Navigate to="/login" replace />;
+  }
 
   return (
     <AuthGuard
@@ -131,12 +135,7 @@ export const ProtectedRoute: React.FC<ProtectedRouteProps> = ({
       requireAuthorization={requireAuthorization}
       requirePrivilege={requirePrivilege}
       clusterName={clusterName}
-      onUnauthorized={handleUnauthorized}
-      fallback={
-        <Alert variant="danger" className="m-3">
-          You do not have permission to view this content.
-        </Alert>
-      }
+      fallback={<Navigate to={redirectTo} replace />}
     >
       {children}
     </AuthGuard>
diff --git a/ambari-web/latest/src/components/DocumentTitleUpdater.tsx b/ambari-web/latest/src/components/DocumentTitleUpdater.tsx
index c9993d6d05..bb3bbc44de 100644
--- a/ambari-web/latest/src/components/DocumentTitleUpdater.tsx
+++ b/ambari-web/latest/src/components/DocumentTitleUpdater.tsx
@@ -16,10 +16,26 @@
  * limitations under the License.
  */
 
-const DocumentTitleUpdater = () => {
-  return (
-    <div>In Progress</div>
-  );
-};
+import { useContext, useEffect } from "react";
+import { useLocation } from "react-router-dom";
+import { AppContext } from "../store/context";
 
-export default DocumentTitleUpdater;
\ No newline at end of file
+export default function DocumentTitleUpdater() {
+  const { clusterName } = useContext(AppContext);
+  const location = useLocation();
+
+  useEffect(() => {
+    if (location.pathname.startsWith("/experimental")) {
+      document.title = "Ambari - Experimental";
+    } else if (clusterName) {
+      const displayName = clusterName.length > 13
+        ? `${clusterName.slice(0, 10)}...`
+        : clusterName;
+      document.title = `Ambari - ${displayName}`;
+    } else {
+      document.title = "Ambari";
+    }
+  }, [clusterName, location.pathname]);
+
+  return null;
+}
diff --git a/ambari-web/latest/vite.config.ts b/ambari-web/latest/src/components/FeatureRouteGuard.tsx
old mode 100755
new mode 100644
similarity index 66%
copy from ambari-web/latest/vite.config.ts
copy to ambari-web/latest/src/components/FeatureRouteGuard.tsx
index 929c08ff59..d9d1af5b3d
--- a/ambari-web/latest/vite.config.ts
+++ b/ambari-web/latest/src/components/FeatureRouteGuard.tsx
@@ -16,10 +16,19 @@
  * limitations under the License.
  */
 
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
+import { ReactNode, useContext } from "react";
+import { Navigate } from "react-router-dom";
+import { AppContext } from "../store/context";
 
-export default defineConfig({
-  plugins: [react()],
-  base:"./"
-})
+export default function FeatureRouteGuard({
+  children,
+  feature,
+}: {
+  children: ReactNode;
+  feature: string;
+}) {
+  const { supports } = useContext(AppContext);
+  return supports[feature]
+    ? children
+    : <Navigate to="/main/dashboard/metrics" replace />;
+}
diff --git a/ambari-web/latest/src/components/Modal.tsx b/ambari-web/latest/src/components/Modal.tsx
index 4d238be994..9326513c8a 100644
--- a/ambari-web/latest/src/components/Modal.tsx
+++ b/ambari-web/latest/src/components/Modal.tsx
@@ -74,7 +74,7 @@ export default function Modal({
   } = options;
 
   const sortedExtraButtons = Array.isArray(extraButtons)
-    ? extraButtons.sort((a: any, b: any) => (a.order || 0) - (b.order || 0))
+    ? [...extraButtons].sort((a: any, b: any) => (a.order || 0) - (b.order || 0))
     : [];
 
   return (
@@ -110,24 +110,34 @@ export default function Modal({
               {cancelButtonText}
             </DefaultButton>
           ) : null}
-          {sortedExtraButtons.map((button: any) => (
-            <Button
-              key={button.text}
-              className={classNames(
-                "ps-3",
-                "pe-3",
-                "text-white",
-                "custom-btn",
-                button.className
-              )}
-              variant={button.variant}
-              onClick={button.onClick}
-              size={buttonSize}
-              {...button}
-            >
-              {button.text}
-            </Button>
-          ))}
+          {sortedExtraButtons.map((button: any) => {
+            const {
+              text,
+              onClick,
+              order: _order,
+              className: buttonClassName,
+              variant,
+              ...buttonProps
+            } = button;
+            return (
+              <Button
+                {...buttonProps}
+                key={text}
+                className={classNames(
+                  "ps-3",
+                  "pe-3",
+                  "text-white",
+                  "custom-btn",
+                  buttonClassName
+                )}
+                variant={variant}
+                onClick={onClick}
+                size={buttonSize}
+              >
+                {text}
+              </Button>
+            );
+          })}
           {cancelableViaSuccessBtn ? (
             <Button
               className={classNames(
diff --git a/ambari-web/latest/src/components/Navbar.tsx b/ambari-web/latest/src/components/Navbar.tsx
index f37fb9c21d..afe2b417ae 100644
--- a/ambari-web/latest/src/components/Navbar.tsx
+++ b/ambari-web/latest/src/components/Navbar.tsx
@@ -45,10 +45,7 @@ import { AlertsApi } from "../api/alertsApi.ts";
 import { redirectToAdminView } from "../Utils/adminViewRedirect";
 import modalManager from "../store/ModalManager.ts";
 import BackgroundOperations from "../screens/BackgroundOperations/index.tsx";
-import { db } from "../Utils/db";
-import Cookies from "universal-cookie";
 import { useCallback } from "react";
-import { isString } from "lodash";
 import { useAuth } from "../hooks/useAuth";
 
 type NavbarOption = {
@@ -63,6 +60,8 @@ type NavbarOption = {
 type NavBarProps = {
   subPath: string;
   viewsList: any[];
+  clusterControls?: boolean;
+  homePath?: string;
   runningRequestsCount?: number;
   hostMaintenanceState?: string;
   hostname?: string;
@@ -71,6 +70,8 @@ type NavBarProps = {
 export default function NavBar({
   subPath,
   viewsList,
+  clusterControls = true,
+  homePath = "/main/dashboard/metrics",
   runningRequestsCount,
   hostMaintenanceState,
   hostname,
@@ -86,34 +87,12 @@ export default function NavBar({
     warning: 0,
   });
   const location = useLocation();
-  const localStorageData = db.getItem("ambari");
-  let login = "";
-  if (localStorageData) {
-    let parsedData = {};
-    try {
-      parsedData = JSON.parse(localStorageData || "{}");
-      if (isString(parsedData)) {
-        parsedData = JSON.parse(parsedData);
-      }
-    } catch (err) {
-      console.log("Error parsing ambari data", err);
-      parsedData = {};
-    }
-
-    let ambari: any = parsedData;
-    if (ambari?.app?.loginName) {
-      login = ambari.app.loginName;
-      // If we already have a username, we can consider the user authenticated
-    }
-  }
-
   const [selectedFilter, setSelectedFilter] = useState("all");
   const { clusterName, cluster } = useContext(AppContext);
   const [showAmbariAboutModal, setShowAmbariAboutModal] = useState(false);
   
   const isClusterInstalled = cluster?.provisioning_state === "INSTALLED";
   const navigate = useNavigate();
-  const cookies = new Cookies();
 
   const isInstaller = () => {
     const path = location.pathname;
@@ -200,73 +179,50 @@ export default function NavBar({
     return viewsList?.length;
   };
 
-  const logOffBeforeSend = useCallback((headers: Record<string, string>) => {
-    headers["Authorization"] = "";
-    return headers;
-  }, []);
+  // Authorization hooks - implementing Ember.js showSettingsPopup authorization pattern
+  const { user, hasAuthorization, logout } = useAuth();
+  const { isNonWizardUser, upgradeIsRunning, upgradeSuspended } = useContext(AppContext);
 
   const handleSignOut = useCallback(async () => {
-    try {
-      stopPolling();
-      db.cleanUp();
-      db.setItem("ambari", JSON.stringify(db.getInitialData()));
-
-      localStorage.removeItem("ambari");
-
-      sessionStorage.clear();
-
-      cookies.remove("AMBARISESSIONID", {
-        path: "/", // Cookie is set with path '/'
-        domain: "localhost", // Cookie is set for localhost
-        secure: true, // Cookie has secure flag
-        sameSite: "lax", // Default SameSite value
-      });
-
-      const response = await fetch("/logout", {
-        method: "GET",
-        headers: logOffBeforeSend({
-          "Content-Type": "text/plain",
-        }),
-      });
-
-      if (response.ok) {
-        window.location.href = "/#/login";
-      } else {
-        window.location.href = "/#/login";
-      }
-    } catch (error) {
-      window.location.href = "/#/login";
-    }
-  }, [navigate, stopPolling]);
-
-  // Authorization hooks - implementing Ember.js showSettingsPopup authorization pattern
-  const { isAdmin } = useAuth();
-  const { upgradeIsRunning, upgradeSuspended } = useContext(AppContext);
+    stopPolling();
+    await logout();
+    navigate("/login", { replace: true });
+  }, [logout, navigate, stopPolling]);
 
   // Check if upgrade is blocking operations (running but not suspended)
   // FIXED: Add additional check for upgrade suspended state to prevent flaky behavior
   // When upgrade is suspended/paused, admin options should be available
   const isUpgradeBlocking = upgradeIsRunning && !upgradeSuspended;
 
+  const canUseRestrictedNavigation = !isUpgradeBlocking && !isNonWizardUser;
+  const canManageAmbari = canUseRestrictedNavigation && hasAuthorization(
+    "AMBARI.ADD_DELETE_CLUSTERS, AMBARI.ASSIGN_ROLES, AMBARI.EDIT_STACK_REPOS, AMBARI.MANAGE_GROUPS, AMBARI.MANAGE_STACK_VERSIONS, AMBARI.MANAGE_USERS, AMBARI.MANAGE_VIEWS, AMBARI.RENAME_CLUSTER"
+  );
+  const canSeeSettings = canUseRestrictedNavigation
+    && hasAuthorization("AMBARI.MANAGE_SETTINGS");
+  const canOpenSettings = !isNonWizardUser
+    && hasAuthorization("CLUSTER.UPGRADE_DOWNGRADE_STACK");
+
   const navbarOptions: NavbarOption[] = [
     {
       label: "About",
       callback: () => setShowAmbariAboutModal(true),
     },
-    // FIXED: Consolidate admin checks to eliminate code duplication (AI code review suggestion)
-    // Admin options should be available when upgrade is suspended/paused
-    ...(isAdmin()
+    ...(canManageAmbari
       ? [
           {
             label: "Manage Ambari",
-            callback: () => {
-              // First navigate to adminView route
-              redirectToAdminView();
-            },
+            callback: () => void redirectToAdminView(),
           },
+        ]
+      : []),
+    ...(canSeeSettings
+      ? [
           {
             label: "Settings",
-            callback: () => setShowUserSettingsModal(true),
+            callback: () => {
+              if (canOpenSettings) setShowUserSettingsModal(true);
+            },
           },
         ]
       : []),
@@ -276,26 +232,6 @@ export default function NavBar({
     },
   ];
 
-  // FIXED: Apply upgrade blocking logic only to the callback functions, not to the dropdown items visibility
-  // This prevents flaky behavior where dropdown items disappear during upgrade pause
-  const getNavbarOptionsWithUpgradeCheck = () => {
-    return navbarOptions.map(option => {
-      // Block admin actions during active upgrades (not suspended)
-      if ((option.label === "Manage Ambari" || option.label === "Settings") && isUpgradeBlocking) {
-        return {
-          ...option,
-          callback: () => {
-            // Could show a message that admin functions are disabled during upgrade
-            console.log("Admin functions are disabled during active upgrade");
-          }
-        };
-      }
-      return option;
-    });
-  };
-
-  const finalNavbarOptions = getNavbarOptionsWithUpgradeCheck();
-
   return (
     <div>
       {showAmbariAboutModal ? (
@@ -321,7 +257,7 @@ export default function NavBar({
                 className="me-1"
                 icon={faHome}
                 style={{ fontSize: 24, cursor: "pointer" }}
-                onClick={() => navigate("/main/dashboard/metrics")}
+                onClick={() => navigate(homePath)}
               />
               {hostname && hostMaintenanceState === "ON" ? (
                 <div className="d-flex align-items-center">
@@ -353,7 +289,7 @@ export default function NavBar({
               {clusterName}
             </Nav.Link>
             <div style={{ width: "20px" }}></div>
-            {isInstaller() ? null : (
+            {isInstaller() || !clusterControls ? null : (
               <div
                 onClick={() => {
                   modalManager.show(
@@ -382,7 +318,7 @@ export default function NavBar({
               </div>
             )}
             <div style={{ width: "20px" }}></div>
-            {isInstaller() ? null : (
+            {isInstaller() || !clusterControls ? null : (
               <NotificationDropdown
                 notifications={filteredNotifications}
                 onFilterChange={setSelectedFilter}
@@ -440,19 +376,19 @@ export default function NavBar({
                 data-bs-toggle="dropdown"
               >
                 <FontAwesomeIcon icon={faUser} className="me-2" />
-                <div className="navbar-text navbar-size">{login}</div>
+                <div className="navbar-text navbar-size">{user?.user_name || ""}</div>
               </Dropdown.Toggle>
               <Dropdown.Menu className="rounded-0">
-                {finalNavbarOptions.slice(0, -1).map((option) => (
+                {navbarOptions.slice(0, -1).map((option) => (
                   <Dropdown.Item key={option.label} onClick={option.callback}>
                     {option.label}
                   </Dropdown.Item>
                 ))}
                 <DropdownDivider />
                 <Dropdown.Item
-                  onClick={finalNavbarOptions[finalNavbarOptions.length - 1].callback}
+                  onClick={navbarOptions[navbarOptions.length - 1].callback}
                 >
-                  {finalNavbarOptions[finalNavbarOptions.length - 1].label}
+                  {navbarOptions[navbarOptions.length - 1].label}
                 </Dropdown.Item>
               </Dropdown.Menu>
             </Dropdown>
diff --git a/ambari-web/latest/src/components/Sidebar/SideItemList.tsx b/ambari-web/latest/src/components/Sidebar/SideItemList.tsx
index e98b5b3cb0..9041d24103 100644
--- a/ambari-web/latest/src/components/Sidebar/SideItemList.tsx
+++ b/ambari-web/latest/src/components/Sidebar/SideItemList.tsx
@@ -43,7 +43,12 @@ enum SideItemLabels {
  * Generate sidebar items with authorization checks
  * Based on Ember.js ui/app/views/main/admin.js authorization patterns
  */
-const getSideItemList = (hasAuthorization: (auth: string) => boolean, upgradeInProgress: boolean = false, upgradeHolding: boolean = false): SideItem[] => {
+const getSideItemList = (
+  hasAuthorization: (auth: string) => boolean,
+  supports: Record<string, boolean>,
+  upgradeInProgress: boolean = false,
+  upgradeHolding: boolean = false,
+): SideItem[] => {
   const adminChildren: SideItem[] = [];
   
   // Stack and Versions - Requires CLUSTER.VIEW_STACK_DETAILS OR CLUSTER.UPGRADE_DOWNGRADE_STACK (matches Ember.js)
@@ -71,7 +76,8 @@ const getSideItemList = (hasAuthorization: (auth: string) => boolean, upgradeInP
   }
   
   // Kerberos - Requires CLUSTER.TOGGLE_KERBEROS
-  if (hasAuthorization('CLUSTER.TOGGLE_KERBEROS') || upgradeInProgress || upgradeHolding) {
+  if (supports.enableToggleKerberos
+    && (hasAuthorization('CLUSTER.TOGGLE_KERBEROS') || upgradeInProgress || upgradeHolding)) {
     adminChildren.push({
       id: SideItemLabels.KERBEROS,
       icon: <></>,
@@ -83,7 +89,9 @@ const getSideItemList = (hasAuthorization: (auth: string) => boolean, upgradeInP
   }
   
   // Service Auto Start - Requires SERVICE.START_STOP authorization (matches ServiceAutoStart component)
-  if (hasAuthorization('SERVICE.START_STOP') || upgradeInProgress || upgradeHolding) {
+  const canSeeAutoStart = hasAuthorization('SERVICE.START_STOP, CLUSTER.MODIFY_CONFIGS')
+    && hasAuthorization('SERVICE.MANAGE_AUTO_START, CLUSTER.MANAGE_AUTO_START');
+  if (supports.serviceAutoStart && (canSeeAutoStart || upgradeInProgress || upgradeHolding)) {
     adminChildren.push({
       id: SideItemLabels.SERVICE_AUTO_START,
       icon: <></>,
diff --git a/ambari-web/latest/src/components/Sidebar/Sidebar.tsx b/ambari-web/latest/src/components/Sidebar/Sidebar.tsx
index 718fcdb701..5bf7c3f550 100644
--- a/ambari-web/latest/src/components/Sidebar/Sidebar.tsx
+++ b/ambari-web/latest/src/components/Sidebar/Sidebar.tsx
@@ -50,7 +50,13 @@ const SideBar = ({
   isSidebarCollapsed,
   setIsSidebarCollapsed,
 }: SideBarProps) => {
-  const { clusterName, services: contextServices, upgradeInProgress, upgradeHolding } = useContext(AppContext);
+  const {
+    clusterName,
+    services: contextServices,
+    supports,
+    upgradeHolding,
+    upgradeInProgress,
+  } = useContext(AppContext);
   const [openOptions, setOpenOptions] = useState<string[]>([SideItemLabels.SERVICES]);
   const [selectedOption, setSelectedOption] = useState<string>("");
   const { allServiceModels  } = useContext(ServiceContext);
@@ -60,7 +66,12 @@ const SideBar = ({
   const { hasAuthorization } = useAuth();
   
   // Get authorization-aware sidebar items using computed upgrade properties
-  const authorizedSideItemList = getSideItemList(hasAuthorization, upgradeInProgress, upgradeHolding);
+  const authorizedSideItemList = getSideItemList(
+    hasAuthorization,
+    supports,
+    upgradeInProgress,
+    upgradeHolding,
+  );
   const [services, setServices] = useState<
     {
       name: string;
diff --git a/ambari-web/latest/src/components/UserSettingsModal.tsx b/ambari-web/latest/src/components/UserSettingsModal.tsx
index 4d574303a2..cba308294c 100644
--- a/ambari-web/latest/src/components/UserSettingsModal.tsx
+++ b/ambari-web/latest/src/components/UserSettingsModal.tsx
@@ -16,29 +16,181 @@
  * limitations under the License.
  */
 
+import { useContext, useEffect, useState } from "react";
+import { Alert, Form, Table } from "react-bootstrap";
+import ClusterApi from "../api/clusterApi";
+import LoginApi from "../api/loginApi";
+import { useAuth } from "../hooks/useAuth";
+import { detectUserTimezone, parseTimezones } from "../Utils/timezone";
+import { parsePersistedValue, persistedPayload } from "../Utils/persistedSettings";
 import Modal from "./Modal";
+import { AppContext } from "../store/context";
 
 type UserSettingsModalProps = {
   isOpen: boolean;
   onClose: () => void;
 };
 
-const UserSettingsModal = ({ isOpen, onClose }: UserSettingsModalProps) => {
+type PrivilegeGroup = {
+  name: string;
+  privileges: string[];
+};
+
+export default function UserSettingsModal({ isOpen, onClose }: UserSettingsModalProps) {
+  const { user, isAdmin, hasAuthorization } = useAuth();
+  const { syncUserBgPreferences } = useContext(AppContext);
+  const [showBackgroundOperations, setShowBackgroundOperations] = useState(true);
+  const [timezone, setTimezone] = useState(detectUserTimezone());
+  const [initialTimezone, setInitialTimezone] = useState("");
+  const [clusterPrivileges, setClusterPrivileges] = useState<PrivilegeGroup[]>([]);
+  const [viewPrivileges, setViewPrivileges] = useState<PrivilegeGroup[]>([]);
+  const [loading, setLoading] = useState(true);
+  const [saving, setSaving] = useState(false);
+  const [error, setError] = useState("");
+  const loginName = user?.user_name || "";
+  const showBackgroundKey = `admin-settings-show-bg-${loginName}`;
+  const timezoneKey = `admin-settings-timezone-${loginName}`;
+  const canPersist = hasAuthorization("CLUSTER.MANAGE_USER_PERSISTED_DATA");
+
+  useEffect(() => {
+    if (!isOpen || !loginName) {
+      return;
+    }
+    async function load() {
+      setLoading(true);
+      setError("");
+      try {
+        const [settings, privilegesResponse] = await Promise.all([
+          ClusterApi.getPersistData(),
+          LoginApi.loadPrivileges(loginName),
+        ]);
+        const detectedTimezone = detectUserTimezone();
+        const savedTimezone = parsePersistedValue(settings?.[timezoneKey], detectedTimezone);
+        setTimezone(savedTimezone);
+        setInitialTimezone(savedTimezone);
+        setShowBackgroundOperations(parsePersistedValue(settings?.[showBackgroundKey], true));
+        if (canPersist) {
+          const missingDefaults: Record<string, unknown> = {};
+          if (settings?.[showBackgroundKey] === undefined) {
+            missingDefaults[showBackgroundKey] = true;
+          }
+          if (settings?.[timezoneKey] === undefined) {
+            missingDefaults[timezoneKey] = detectedTimezone;
+          }
+          if (Object.keys(missingDefaults).length) {
+            void ClusterApi.postPersistData(persistedPayload(missingDefaults)).catch(() => undefined);
+          }
+        }
+
+        const clusters = new Map<string, string[]>();
+        const views = new Map<string, string[]>();
+        (privilegesResponse.data.items || []).forEach((item: any) => {
+          const privilege = item.PrivilegeInfo;
+          const target = privilege.type === "CLUSTER" ? clusters : privilege.type === "VIEW" ? views : null;
+          const name = privilege.type === "CLUSTER" ? privilege.cluster_name : privilege.instance_name;
+          if (target && name) {
+            target.set(name, [...(target.get(name) || []), privilege.permission_label]);
+          }
+        });
+        setClusterPrivileges(Array.from(clusters, ([name, privileges]) => ({ name, privileges })));
+        setViewPrivileges(Array.from(views, ([name, privileges]) => ({ name, privileges })));
+      } catch (loadError: any) {
+        setError(loadError?.response?.data?.message || "Unable to load user settings.");
+      } finally {
+        setLoading(false);
+      }
+    }
+    void load();
+  }, [canPersist, isOpen, loginName, showBackgroundKey, timezoneKey]);
+
+  const save = async () => {
+    if (!canPersist) return;
+    setSaving(true);
+    setError("");
+    try {
+      await ClusterApi.postPersistData(persistedPayload({
+        [showBackgroundKey]: showBackgroundOperations,
+        [timezoneKey]: timezone,
+      }));
+      syncUserBgPreferences(showBackgroundOperations);
+      onClose();
+      if (timezone !== initialTimezone) {
+        window.location.reload();
+      }
+    } catch (saveError: any) {
+      setError(saveError?.response?.data?.message || "Unable to save user settings.");
+    } finally {
+      setSaving(false);
+    }
+  };
+
+  const privilegeRows = (groups: PrivilegeGroup[]) => groups.map((group) => (
+    <tr key={group.name}>
+      <td>{group.name}</td>
+      <td>{group.privileges.join(", ")}</td>
+    </tr>
+  ));
+
   return (
     <Modal
       isOpen={isOpen}
       onClose={onClose}
       modalTitle="User Settings"
-      modalBody={<div/>}
-      successCallback={() => {}}
+      modalBody={loading ? <p>Loading...</p> : (
+        <div>
+          {error ? <Alert variant="danger">{error}</Alert> : null}
+          <h4>General</h4>
+          <Form.Check
+            className="mb-4"
+            type="checkbox"
+            label="Do not show the Background Operations dialog automatically"
+            checked={!showBackgroundOperations}
+            onChange={(event) => setShowBackgroundOperations(!event.target.checked)}
+          />
+          <h4>Locale</h4>
+          <Form.Select
+            className="mb-4"
+            value={timezone}
+            onChange={(event) => setTimezone(event.target.value)}
+            aria-label="Timezone"
+          >
+            {parseTimezones().map((item) => (
+              <option key={item.value} value={item.value}>{item.label}</option>
+            ))}
+          </Form.Select>
+          {isAdmin() ? (
+            <Alert variant="info">Ambari administrators have all privileges.</Alert>
+          ) : (
+            <Table hover responsive>
+              <thead><tr><th>Cluster</th><th>Cluster Role</th></tr></thead>
+              <tbody>
+                {clusterPrivileges.length ? privilegeRows(clusterPrivileges) : (
+                  <tr><td colSpan={2}>No cluster privileges</td></tr>
+                )}
+              </tbody>
+              <thead><tr><th>View</th><th>View Permissions</th></tr></thead>
+              <tbody>
+                {viewPrivileges.length ? privilegeRows(viewPrivileges) : (
+                  <tr><td colSpan={2}>No View privileges</td></tr>
+                )}
+              </tbody>
+            </Table>
+          )}
+          {!canPersist ? (
+            <Alert variant="warning">
+              You do not have permission to persist user settings.
+            </Alert>
+          ) : null}
+        </div>
+      )}
+      successCallback={() => void save()}
       options={{
         modalSize: "modal-lg",
-        okButtonText: "SAVE",
+        okButtonText: saving ? "SAVING..." : "SAVE",
         cancelButtonText: "CANCEL",
-        okButtonVariant: "success"
+        okButtonVariant: "success",
+        okButtonDisabled: loading || saving || !canPersist,
       }}
     />
-  )
+  );
 }
-
-export default UserSettingsModal;
\ No newline at end of file
diff --git a/ambari-web/latest/src/constants.ts b/ambari-web/latest/src/constants.ts
index 7f41f05cbf..83d82a8593 100644
--- a/ambari-web/latest/src/constants.ts
+++ b/ambari-web/latest/src/constants.ts
@@ -147,6 +147,35 @@ export const toBePreservedPaths = {
   kerberos: "KERBEROS_WIZARD_LAST_PATH",
 };
 
+export const DEFAULT_SUPPORTS: Record<string, boolean> = {
+  preUpgradeCheck: true,
+  displayOlderVersions: false,
+  autoRollbackHA: false,
+  alwaysEnableManagedMySQLForHive: false,
+  preKerberizeCheck: false,
+  customizeAgentUserAccount: false,
+  installGanglia: false,
+  opsDuringRollingUpgrade: false,
+  customizedWidgetLayout: false,
+  showPageLoadTime: false,
+  skipComponentStartAfterInstall: false,
+  preInstallChecks: false,
+  serviceAutoStart: true,
+  logSearch: true,
+  redhatSatellite: false,
+  addingNewRepository: false,
+  kerberosStackAdvisor: true,
+  logCountVizualization: false,
+  createAlerts: false,
+  enabledWizardForHostOrderedUpgrade: true,
+  manageJournalNode: true,
+  enableToggleKerberos: true,
+  enableAddDeleteServices: true,
+  regenerateKeytabsOnSingleHost: false,
+  disableCredentialsAutocompleteForRepoUrls: true,
+  enableNewServiceRestartOptions: false,
+};
+
 export const selectMasterComponentsForService = {
   HIVE: ["HIVE_METASTORE", "HIVE_SERVER"],
   ZOOKEEPER: ["ZOOKEEPER_SERVER"],
diff --git a/ambari-web/latest/src/hooks/useAuth.ts b/ambari-web/latest/src/hooks/useAuth.ts
index 1866b95163..b66e3fb872 100644
--- a/ambari-web/latest/src/hooks/useAuth.ts
+++ b/ambari-web/latest/src/hooks/useAuth.ts
@@ -29,6 +29,7 @@ export const useAuth = () => {
     user: context.user,
     isAuthenticated: context.isAuthenticated,
     isLoading: context.isLoading,
+    sessionError: context.sessionError,
     
     // Authorization data
     authorizations: context.authorizations,
@@ -47,7 +48,10 @@ export const useAuth = () => {
     // Actions
     login: context.login,
     logout: context.logout,
-    refreshUserData: context.refreshUserData
+    refreshUserData: context.refreshUserData,
+    retrySession: context.retrySession,
+    loginMessage: context.loginMessage,
+    acknowledgeLoginMessage: context.acknowledgeLoginMessage,
   };
 };
 
diff --git a/ambari-web/latest/src/layout/InstallerVersionGuard.tsx b/ambari-web/latest/src/layout/InstallerVersionGuard.tsx
new file mode 100644
index 0000000000..a362c8eaaf
--- /dev/null
+++ b/ambari-web/latest/src/layout/InstallerVersionGuard.tsx
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { ReactNode, useContext } from "react";
+import { Alert } from "react-bootstrap";
+import { AppContext } from "../store/context";
+import { hasVersionConflict } from "../Utils/authPolicy";
+
+export default function InstallerVersionGuard({ children }: { children: ReactNode }) {
+  const { ambariServerVersion } = useContext(AppContext);
+  const clientVersion = import.meta.env.VITE_AMBARI_VERSION || "";
+  if (!hasVersionConflict(clientVersion, ambariServerVersion || "")) {
+    return children;
+  }
+
+  return (
+    <main className="container py-5">
+      <h2>Ambari Server / Web Client Version Mismatch</h2>
+      <Alert variant="danger">
+        Ambari Server version <strong>{ambariServerVersion}</strong> does not match
+        Web Client version <strong>{clientVersion}</strong>. Cluster installation is blocked.
+      </Alert>
+    </main>
+  );
+}
diff --git a/ambari-web/latest/src/InactivityTimeout.tsx b/ambari-web/latest/src/layout/Main.tsx
similarity index 59%
copy from ambari-web/latest/src/InactivityTimeout.tsx
copy to ambari-web/latest/src/layout/Main.tsx
index 90b943c229..264cb638b7 100644
--- a/ambari-web/latest/src/InactivityTimeout.tsx
+++ b/ambari-web/latest/src/layout/Main.tsx
@@ -16,20 +16,20 @@
  * limitations under the License.
  */
 
-interface InactivityTimeoutProps {
-  timeout?: number; // Make optional since we'll get it from server properties
-}
-
-const InactivityTimeout: React.FC<InactivityTimeoutProps> = ({ timeout: propTimeout }) => {
-  return (
-    <div className="inactivity-timeout">
-      {propTimeout ? (
-        <p>Your session will expire after {propTimeout} minutes of inactivity.</p>
-      ) : (
-        <p>Loading inactivity timeout...</p>
-      )}
-    </div>
-  );
-};
+import { Navigate, useLocation } from "react-router-dom";
+import { isViewOnlyUser } from "../Utils/authPolicy";
+import { useAuth } from "../hooks/useAuth";
+import DashboardLayout from "./Dashboard";
+import ViewsLayout from "./Views";
 
-export default InactivityTimeout;
+export default function MainLayout() {
+  const { authorizations } = useAuth();
+  const location = useLocation();
+  if (!isViewOnlyUser(authorizations)) {
+    return <DashboardLayout />;
+  }
+  if (!location.pathname.startsWith("/main/view")) {
+    return <Navigate to="/main/view" replace />;
+  }
+  return <ViewsLayout />;
+}
diff --git a/ambari-web/latest/src/layout/Views.tsx b/ambari-web/latest/src/layout/Views.tsx
new file mode 100644
index 0000000000..36f44fba1d
--- /dev/null
+++ b/ambari-web/latest/src/layout/Views.tsx
@@ -0,0 +1,80 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useEffect, useState } from "react";
+import { Outlet } from "react-router-dom";
+import ViewApi from "../api/viewApi";
+import LicenseFooter from "../components/LicenseFooter";
+import NavBar from "../components/Navbar";
+
+type ViewLink = {
+  instance_name: string;
+  label?: string;
+  version: string;
+  view_name: string;
+};
+
+type ViewInstanceResponse = { ViewInstanceInfo?: Omit<ViewLink, "version" | "view_name"> };
+type ViewVersionResponse = {
+  ViewVersionInfo?: { version?: string };
+  instances?: ViewInstanceResponse[];
+};
+type ViewResponse = {
+  ViewInfo?: { view_name?: string };
+  versions?: ViewVersionResponse[];
+};
+
+function viewLinks(data: { items?: ViewResponse[] }): ViewLink[] {
+  return (data?.items || []).flatMap((item) => {
+    const viewName = item?.ViewInfo?.view_name;
+    return (item?.versions || []).flatMap((version) => (
+      (version?.instances || []).flatMap((instance) => {
+        const instanceInfo = instance.ViewInstanceInfo;
+        const versionNumber = version.ViewVersionInfo?.version;
+        return instanceInfo?.instance_name && versionNumber && viewName
+          ? [{ ...instanceInfo, version: versionNumber, view_name: viewName }]
+          : [];
+      })
+    ));
+  });
+}
+
+export default function ViewsLayout() {
+  const [views, setViews] = useState<ViewLink[]>([]);
+
+  useEffect(() => {
+    ViewApi.getInstances()
+      .then((data) => setViews(viewLinks(data)))
+      .catch(() => setViews([]));
+  }, []);
+
+  return (
+    <div className="d-flex flex-column h-100">
+      <NavBar
+        clusterControls={false}
+        homePath="/main/view"
+        viewsList={views}
+        subPath="Views"
+      />
+      <div className="h-100" style={{ paddingBottom: "80px", overflowY: "auto" }}>
+        <Outlet />
+      </div>
+      <LicenseFooter hasSidebar={false} />
+    </div>
+  );
+}
diff --git a/ambari-web/latest/src/router/RoutesList.tsx b/ambari-web/latest/src/router/RoutesList.tsx
index 0f7ddfb923..652ea41294 100644
--- a/ambari-web/latest/src/router/RoutesList.tsx
+++ b/ambari-web/latest/src/router/RoutesList.tsx
@@ -16,284 +16,218 @@
  * limitations under the License.
  */
 
-import { RouteObject, Navigate, Outlet } from "react-router-dom";
-import ClusterCreationWizard from "../screens/ClusterWizard";
-import { Login } from "../screens/Authentication/Login.tsx";
+import { Navigate, Outlet, RouteObject } from "react-router-dom";
+import { AuthenticatedApplication, LandingRoute } from "../AppLoader";
+import { ProtectedRoute } from "../components/AuthGuard";
+import AdminRouteGuard from "../components/AdminRouteGuard";
 import InstallerLayout from "../layout/Installer";
-import HostsList from "../screens/Hosts/HostsList.tsx";
-import Alerts from "../screens/Alerts/Alerts.tsx";
-import DashboardLayout from "../layout/Dashboard";
-import ServiceAutoStart from "../screens/ServiceAutoStart/index.tsx";
-import ServiceAccounts from "../screens/ServiceAccounts/index.tsx";
-import EnableKerberos from "../screens/KerberosWizard/EnableKerberos.tsx";
-import { Actions } from "../screens/Services/Actions.tsx";
-import AlertDefinitionDetails from "../screens/Alerts/AlertDefinitionDetails.tsx";
-import ServiceDashboard from "../screens/Services/ServiceDashboard.tsx";
-import { redirectToAdminView } from "../Utils/adminViewRedirect";
-import { Hosts } from "../screens/Hosts/index.tsx";
-import Spinner from "../components/Spinner";
-import ViewDetails from "../screens/Views/ViewDetails.tsx";
-import ViewsListPage from "../screens/Views/ViewsListPage";
-import { RouteTracker } from "../AppLoader.tsx";
-import StackAndVersions from "../screens/ClusterAdmin/StackAndVersions/StackAndVersions.tsx";
+import MainLayout from "../layout/Main";
+import InstallerVersionGuard from "../layout/InstallerVersionGuard";
+import ClusterCreationWizard from "../screens/ClusterWizard";
 import {
   ClusterCreationContext,
   ClusterCreationProvider,
-} from "../screens/ClusterWizard/clusterStore/context.tsx";
-import wizardSteps from "../screens/ClusterWizard/wizardSteps.tsx";
-import ServiceLoader from "../screens/Services/ServiceLoader.tsx";
-import Dashboard from "../screens/Dashboard/Index.tsx";
-import AddWizardUrlMapping from "../screens/Services/AddWizardUrlMapping.tsx";
-import { ProtectedRoute } from "../components/AuthGuard";
-import AdminRouteGuard from "../components/AdminRouteGuard";
+} from "../screens/ClusterWizard/clusterStore/context";
+import wizardSteps from "../screens/ClusterWizard/wizardSteps";
+import { Login } from "../screens/Authentication/Login";
+import HostsList from "../screens/Hosts/HostsList";
+import { Hosts } from "../screens/Hosts";
+import Alerts from "../screens/Alerts/Alerts";
+import AlertDefinitionDetails from "../screens/Alerts/AlertDefinitionDetails";
+import ServiceAutoStart from "../screens/ServiceAutoStart";
+import ServiceAccounts from "../screens/ServiceAccounts";
+import EnableKerberos from "../screens/KerberosWizard/EnableKerberos";
+import { Actions } from "../screens/Services/Actions";
+import ServiceDashboard from "../screens/Services/ServiceDashboard";
+import ServiceLoader from "../screens/Services/ServiceLoader";
+import AddWizardUrlMapping from "../screens/Services/AddWizardUrlMapping";
+import ViewDetails from "../screens/Views/ViewDetails";
+import ViewsListPage from "../screens/Views/ViewsListPage";
+import StackAndVersions from "../screens/ClusterAdmin/StackAndVersions/StackAndVersions";
+import Dashboard from "../screens/Dashboard/Index";
+import AdminViewRedirect from "../screens/Authentication/AdminViewRedirect";
+import Experimental from "../screens/Experimental";
+import FeatureRouteGuard from "../components/FeatureRouteGuard";
+import AdminViewRouteGuard from "../components/AdminViewRouteGuard";
 
 const RoutesList: RouteObject[] = [
   {
     path: "/",
-    element: (
-      <>
-        <RouteTracker />
-        <Outlet />
-      </>
-    ),
+    element: <Outlet />,
     children: [
+      { path: "login", element: <Login /> },
+      { path: "login/local", element: <Login isLocalLogin /> },
       {
-        path: "installer",
-        element: <InstallerLayout />,
+        element: <AuthenticatedApplication />,
         children: [
+          { index: true, element: <LandingRoute /> },
           {
-            path: ":stepNumber",
+            path: "adminView",
             element: (
-              <ClusterCreationWizard
-                Context={ClusterCreationContext}
-                Provider={ClusterCreationProvider}
-                wizardSteps={wizardSteps as any}
-              />
+              <AdminViewRouteGuard>
+                <AdminViewRedirect />
+              </AdminViewRouteGuard>
             ),
           },
-        ],
-      },
-      {
-        path: "/login",
-        element: <Login />,
-      },
-
-      {
-        path: "/main",
-        element: (
-          <>
-            <DashboardLayout />
-          </>
-        ),
-        children: [
           {
-            path: "service/add/:stepNumber",
-            element: <AddWizardUrlMapping />,
+            path: "experimental",
+            element: (
+              <ProtectedRoute
+                requireAuthorization="AMBARI.MANAGE_SETTINGS"
+                redirectTo="/"
+              >
+                <Experimental />
+              </ProtectedRoute>
+            ),
           },
           {
-            path: "dashboard",
-            element: <Outlet />,
+            path: "installer",
+            element: <InstallerVersionGuard><InstallerLayout /></InstallerVersionGuard>,
             children: [
               {
-                index: true,
-                element: <Navigate to="metrics" replace />,
-              },
-              {
-                path: ":tabName",
-                element: <Dashboard />,
+                path: ":stepNumber",
+                element: (
+                  <ClusterCreationWizard
+                    Context={ClusterCreationContext}
+                    Provider={ClusterCreationProvider}
+                    wizardSteps={wizardSteps as any}
+                  />
+                ),
               },
             ],
           },
           {
-            path: "actions",
-            element: <Actions serviceName="HDFS" />,
-          },
-
-          {
-            path: "services",
-            element: <Outlet />,
+            path: "main",
+            element: <MainLayout />,
             children: [
+              { path: "service/add/:stepNumber", element: <AddWizardUrlMapping /> },
               {
-                path: ":serviceName",
+                path: "dashboard",
                 element: <Outlet />,
                 children: [
-                  {
-                    path: ":tabName",
-                    element: <ServiceDashboard />,
-                  },
+                  { index: true, element: <Navigate to="metrics" replace /> },
+                  { path: ":tabName", element: <Dashboard /> },
                 ],
               },
+              { path: "actions", element: <Actions serviceName="HDFS" /> },
               {
-                path: "highAvailability/:componentName/enable/:stepNumber",
-                element: <ServiceLoader />,
-              },
-              {
-                path:":componentName/federation/:stepNumber",
-                element: <ServiceLoader />,
+                path: "services",
+                element: <Outlet />,
+                children: [
+                  {
+                    path: ":serviceName",
+                    element: <Outlet />,
+                    children: [
+                      { path: ":tabName", element: <ServiceDashboard /> },
+                    ],
+                  },
+                  {
+                    path: "highAvailability/:componentName/enable/:stepNumber",
+                    element: <ServiceLoader />,
+                  },
+                  {
+                    path: ":componentName/federation/:stepNumber",
+                    element: <ServiceLoader />,
+                  },
+                  {
+                    path: "highAvailability/:componentName/manage/:stepNumber",
+                    element: <ServiceLoader />,
+                  },
+                ],
               },
               {
-                path: "highAvailability/:componentName/manage/:stepNumber",
+                path: "service/reassign/:componentName/:stepNumber",
                 element: <ServiceLoader />,
               },
-              
-            ],
-          },
-          {
-            path:"service",
-            element:<Outlet/>,
-            children:[{
-              path:"reassign/:componentName/:stepNumber",
-              element:<ServiceLoader />
-            }]
-          },
-          {
-            path: "hosts",
-            element: <HostsList />,
-          },
-          {
-            path: "hosts/component/:componentName",
-            element: <HostsList />,
-          },
-          {
-            path: "hosts/version/:versionName/:versionStatus",
-            element: <HostsList />,
-          },
-          {
-            path: "hosts/:hostname/:tab",
-            element: <Hosts />,
-          },
-          {
-            path: "host/add/:stepNumber",
-            element: (
-              <AddWizardUrlMapping />
-            ),
-          },
-          {
-            path: "alerts",
-            element: <Alerts />,
-          },
-          {
-            path: "alerts/:alertId",
-            element: <AlertDefinitionDetails />,
-          },
-          {
-            path: "admin",
-            element: (
-              <AdminRouteGuard>
-                <Outlet />
-              </AdminRouteGuard>
-            ),
-            children: [
-              {
-                index: true,
-                element: <Navigate to="stack/services" replace />,
-              },
+              { path: "hosts", element: <HostsList /> },
+              { path: "hosts/component/:componentName", element: <HostsList /> },
+              { path: "hosts/version/:versionName/:versionStatus", element: <HostsList /> },
+              { path: "hosts/:hostname/:tab", element: <Hosts /> },
+              { path: "host/add/:stepNumber", element: <AddWizardUrlMapping /> },
+              { path: "alerts", element: <Alerts /> },
+              { path: "alerts/:alertId", element: <AlertDefinitionDetails /> },
               {
-                path: "stack/:tabName",
+                path: "admin",
                 element: (
-                  <ProtectedRoute 
-                    requireAuthorization="CLUSTER.VIEW_STACK_DETAILS, CLUSTER.UPGRADE_DOWNGRADE_STACK"
-                    redirectTo="/main/dashboard/metrics"
-                  >
-                    <StackAndVersions />
-                  </ProtectedRoute>
+                  <AdminRouteGuard>
+                    <Outlet />
+                  </AdminRouteGuard>
                 ),
+                children: [
+                  { index: true, element: <Navigate to="stack/services" replace /> },
+                  {
+                    path: "stack/:tabName",
+                    element: (
+                      <ProtectedRoute
+                        requireAuthorization="CLUSTER.VIEW_STACK_DETAILS, CLUSTER.UPGRADE_DOWNGRADE_STACK"
+                        redirectTo="/main/dashboard/metrics"
+                      >
+                        <StackAndVersions />
+                      </ProtectedRoute>
+                    ),
+                  },
+                  {
+                    path: "serviceAutoStart",
+                    element: (
+                      <FeatureRouteGuard feature="serviceAutoStart">
+                        <ProtectedRoute
+                          requireAuthorization="SERVICE.MANAGE_AUTO_START, CLUSTER.MANAGE_AUTO_START"
+                          redirectTo="/main/dashboard/metrics"
+                        >
+                          <ServiceAutoStart />
+                        </ProtectedRoute>
+                      </FeatureRouteGuard>
+                    ),
+                  },
+                  {
+                    path: "serviceAccounts",
+                    element: (
+                      <ProtectedRoute
+                        requireAuthorization="SERVICE.SET_SERVICE_USERS_GROUPS"
+                        redirectTo="/main/dashboard/metrics"
+                      >
+                        <ServiceAccounts />
+                      </ProtectedRoute>
+                    ),
+                  },
+                  {
+                    path: "kerberos",
+                    element: (
+                      <FeatureRouteGuard feature="enableToggleKerberos">
+                        <ProtectedRoute
+                          requireAuthorization="CLUSTER.TOGGLE_KERBEROS"
+                          redirectTo="/main/dashboard/metrics"
+                        >
+                          <EnableKerberos />
+                        </ProtectedRoute>
+                      </FeatureRouteGuard>
+                    ),
+                  },
+                  {
+                    path: "kerberos/enable/:stepNumber",
+                    element: (
+                      <FeatureRouteGuard feature="enableToggleKerberos">
+                        <ProtectedRoute
+                          requireAuthorization="CLUSTER.TOGGLE_KERBEROS"
+                          redirectTo="/main/dashboard/metrics"
+                        >
+                          <EnableKerberos />
+                        </ProtectedRoute>
+                      </FeatureRouteGuard>
+                    ),
+                  },
+                ],
               },
-              {
-                path: "serviceAutoStart",
-                element: (
-                  <ProtectedRoute 
-                    requireAuthorization="SERVICE.START_STOP"
-                    redirectTo="/main/dashboard/metrics"
-                  >
-                    <ServiceAutoStart />
-                  </ProtectedRoute>
-                ),
-              },
-              {
-                path: "serviceAccounts",
-                element: (
-                  <ProtectedRoute 
-                    requireAuthorization="SERVICE.SET_SERVICE_USERS_GROUPS"
-                    redirectTo="/main/dashboard/metrics"
-                  >
-                    <ServiceAccounts />
-                  </ProtectedRoute>
-                ),
-              },
-              {
-                path: "kerberos",
-                element: (
-                  <ProtectedRoute 
-                    requireAuthorization="CLUSTER.TOGGLE_KERBEROS"
-                    redirectTo="/main/dashboard/metrics"
-                  >
-                    <EnableKerberos />
-                  </ProtectedRoute>
-                ),
-              },
-              {
-                path: "kerberos/enable/:stepNumber",
-                element: (
-                  <ProtectedRoute 
-                    requireAuthorization="CLUSTER.TOGGLE_KERBEROS"
-                    redirectTo="/main/dashboard/metrics"
-                  >
-                    <EnableKerberos />
-                  </ProtectedRoute>
-                ),
-              }
-            ],
-          },
-          {
-            path: "views/:viewName/:viewVersion/:instanceName/*",
-            element: <ViewDetails />,
-          },
-          {
-            path: "view",
-            element: <ViewsListPage />,
-          },
-          {
-            path: "view/:viewName",
-            element: <Navigate to="/main/view" replace />,
-          },
-        ],
-      },
-      {
-        path: "main",
-        element: (
-          <>
-            {/* <DashboardWrapper /> */}
-            <Outlet />
-          </>
-        ),
-        children: [
-          {
-            path: "admin",
-            element: <Outlet />,
-            children: [
-              {
-                path: "serviceAccounts",
-                element: <ServiceAccounts />,
-              },
+              { path: "views/:viewName/:viewVersion/:instanceName/*", element: <ViewDetails /> },
+              { path: "view", element: <ViewsListPage /> },
+              { path: "view/:viewName", element: <Navigate to="/main/view" replace /> },
             ],
           },
         ],
       },
+      { path: "*", element: <Navigate to="/" replace /> },
     ],
   },
-  {
-    path: "adminView",
-    loader: () => {
-      redirectToAdminView();
-      return null;
-    },
-    element: <Spinner />, // This won't actually render as we'll redirect immediately
-  },
-  {
-    path: "/login",
-    element: <Login />,
-  },
 ];
 
 export default RoutesList;
diff --git a/ambari-web/latest/src/screens/Authentication/AdminViewRedirect.tsx b/ambari-web/latest/src/screens/Authentication/AdminViewRedirect.tsx
new file mode 100644
index 0000000000..be912d4867
--- /dev/null
+++ b/ambari-web/latest/src/screens/Authentication/AdminViewRedirect.tsx
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useEffect, useState } from "react";
+import { Navigate, useLocation } from "react-router-dom";
+import Spinner from "../../components/Spinner";
+import { ServiceApi } from "../../api/serviceApi";
+
+type ServerVersionResponse = {
+  components?: Array<{
+    RootServiceComponents?: { component_version?: string };
+  }>;
+};
+
+function latestServerVersion(data: ServerVersionResponse): string | null {
+  const versions = (data?.components || [])
+    .map((item) => item.RootServiceComponents?.component_version)
+    .filter((value): value is string => Boolean(value))
+    .map((value) => value.replace(/[^\d.-]/g, ""))
+    .sort();
+  return versions.at(-1) || null;
+}
+
+export default function AdminViewRedirect() {
+  const [failed, setFailed] = useState(false);
+  const location = useLocation();
+
+  useEffect(() => {
+    async function redirect() {
+      try {
+        const data = await ServiceApi.getAmbariServerVersion();
+        const version = latestServerVersion(data);
+        if (!version) {
+          throw new Error("Ambari Server version is unavailable");
+        }
+        const page = new URLSearchParams(location.search).get("page");
+        const adminHash = page ? `#/${encodeURIComponent(page)}` : "#/";
+        window.location.assign(
+          `/views/ADMIN_VIEW/${encodeURIComponent(version)}/INSTANCE/${adminHash}`,
+        );
+      } catch {
+        setFailed(true);
+      }
+    }
+    void redirect();
+  }, [location.search]);
+
+  return failed ? <Navigate to="/main/view" replace /> : <Spinner />;
+}
diff --git a/ambari-web/latest/src/screens/Authentication/Login.tsx b/ambari-web/latest/src/screens/Authentication/Login.tsx
index ef161f83b4..12ea549058 100644
--- a/ambari-web/latest/src/screens/Authentication/Login.tsx
+++ b/ambari-web/latest/src/screens/Authentication/Login.tsx
@@ -27,24 +27,44 @@ import {
   Image,
 } from "react-bootstrap";
 import React, { useEffect, useState } from "react";
+import { useLocation, useNavigate } from "react-router-dom";
 import AmbariLogo from "../../assets/img/ambari-logo.png";
 import "../../../custom.scss";
-import { LocalStorageOps } from "../../Utils/LocalStorageOps";
 import { useUserContext } from "../../store/UserContext";
+import { LOCAL_LOGIN_PATH } from "../../Utils/authNavigation";
+import LoginMessageModal from "./LoginMessageModal";
 
-export const Login = () => {
+export const Login = ({ isLocalLogin = false }: { isLocalLogin?: boolean }) => {
   const [username, setUsername] = useState("");
   const [password, setPassword] = useState("");
   const [errorMessage, setErrorMessage] = useState("");
 
   // Use the UserContext for authentication
-  const { login, isLoading, loginError } = useUserContext();
+  const navigate = useNavigate();
+  const location = useLocation();
+  const {
+    login,
+    isAuthenticated,
+    isLoading,
+    loginError,
+    loginMessage,
+  } = useUserContext();
 
   useEffect(()=>{
     if (loginError) {
       setErrorMessage(loginError);
     }
-  },[loginError])
+  },[loginError]);
+
+  useEffect(() => {
+    document.title = "Ambari";
+  }, []);
+
+  useEffect(() => {
+    if (isAuthenticated && !loginMessage) {
+      navigate("/", { replace: true });
+    }
+  }, [isAuthenticated, loginMessage, navigate]);
 
   const handleSignIn = async (event: React.FormEvent) => {
     event.preventDefault();
@@ -53,79 +73,16 @@ export const Login = () => {
     try {
       // Use the UserContext login method
       const success = await login(username, password);
-      console.log("Login success:", success);
-      if (success) {
-        // Redirect after successful login
-        const hashExclusiveUrl = LocalStorageOps.getItem(
-          "lastVisitedURL"
-        )?.replace("/#", "");
-
-        if (hashExclusiveUrl) {
-          window?.location?.replace("/#" + hashExclusiveUrl);
-        } else {
-          window.location.replace("/#");
-        }
-        window.location.reload();
-      } else {
+      if (!success) {
         setErrorMessage(loginError || "Login failed. Please check your credentials.");
       }
-    } catch (error: any) {
+    } catch {
       setErrorMessage(loginError || "An unexpected error occurred during login.");
     }
   };
-  // const handleLoginSuccess = async (params: LoginParams) => {
-  //   try {
-  //     const response = await LoginApi.handleSuccessfulLogin(params);
-  //     await afterLoginSuccess(response.data, params as any);
-  //     setErrorMessage("");
-  //   } catch (error) {
-  //     handleLoginError(error);
-  //   }
-  // };
-  // const afterLoginSuccess = async (data: dataLoginType, params: LoginDataParamsType) => {
-  //   try {
-  //     await LoginApi.afterLoginSuccessCallback(data);
-  //     // if (afterLoginSuccessCallbackResponse.status !== 200) {
-  //     //   throw new Error("after login success callback failed");
-  //     // }
-  //     await setClusterData(params);
-  //   } catch (error) {
-  //     await setClusterData(params);
-  //     handleLoginError(error);
-  //   }
-  // }
-  // const setClusterData = async (params: LoginDataParamsType) => {
-  //   try {
-  //     const response = await LoginApi.setClusterDataCallback(params)
-  //     if (!response.data.items) {
-  //       throw new Error("Failed to set cluster data");
-  //     }
-  //     await loadAuthorizations(params as any);
-  //     setErrorMessage("");
-  //   } catch (error) {
-  //     handleLoginError(error);
-  //   }
-  // }
-  // const loadAuthorizations = async (params: LoginParams) => {
-  //   try {
-  //     const response = await LoginApi.loadAuthorizationsCallback(params);
-  //     const authorizationIds = response.data.items.map(
-  //       (item: any) => item.AuthorizationInfo.authorization_id
-  //     );
-
-  //     let ambariData = JSON.parse(Utility.decryptData(db.getItem("ambari") || "") || "");
-  //     console.log("ambariData", ambariData)
-  //     ambariData.app.auth = authorizationIds;
-  //     db.setItem("ambari", Utility.encryptData(JSON.stringify(ambariData)));
-
-  //     // After successful login and authorization, redirect to metrics dashboard
-  //     window.location.href = '/#/main/dashboard/metrics';
-  //   } catch (error) {
-  //     handleLoginError(error);
-  //   }
-  // }
   return (
     <>
+      <LoginMessageModal />
       <div
         className="w-100 d-flex align-items-center py-2 px-4"
         style={{ background: "#313d54" }}
@@ -143,6 +100,11 @@ export const Login = () => {
                 <Card.Title className="text-start">
                   <h2>Sign in</h2>
                 </Card.Title>
+                {isLocalLogin && new URLSearchParams(location.search).has("redirectError") ? (
+                  <Alert variant="warning" className="my-3">
+                    External authentication could not be completed. Use local Ambari credentials.
+                  </Alert>
+                ) : null}
                 {errorMessage ? (
                   <Alert variant="danger" className="my-3">
                     {errorMessage}
@@ -173,6 +135,11 @@ export const Login = () => {
                   >
                     {isLoading ? "Signing In..." : "SIGN IN"}
                   </Button>
+                  {!isLocalLogin ? (
+                    <a className="d-block mt-3" href={`#${LOCAL_LOGIN_PATH}`}>
+                      Sign in with local credentials
+                    </a>
+                  ) : null}
                 </Form.Group>
               </Form>
             </Card>
diff --git a/ambari-web/latest/src/components/UserSettingsModal.tsx b/ambari-web/latest/src/screens/Authentication/LoginMessageModal.tsx
similarity index 62%
copy from ambari-web/latest/src/components/UserSettingsModal.tsx
copy to ambari-web/latest/src/screens/Authentication/LoginMessageModal.tsx
index 4d574303a2..97df32ed10 100644
--- a/ambari-web/latest/src/components/UserSettingsModal.tsx
+++ b/ambari-web/latest/src/screens/Authentication/LoginMessageModal.tsx
@@ -16,29 +16,27 @@
  * limitations under the License.
  */
 
-import Modal from "./Modal";
+import Modal from "../../components/Modal";
+import { useAuth } from "../../hooks/useAuth";
 
-type UserSettingsModalProps = {
-  isOpen: boolean;
-  onClose: () => void;
-};
+export default function LoginMessageModal() {
+  const { loginMessage, acknowledgeLoginMessage } = useAuth();
+  if (!loginMessage) {
+    return null;
+  }
 
-const UserSettingsModal = ({ isOpen, onClose }: UserSettingsModalProps) => {
   return (
     <Modal
-      isOpen={isOpen}
-      onClose={onClose}
-      modalTitle="User Settings"
-      modalBody={<div/>}
-      successCallback={() => {}}
+      isOpen
+      onClose={acknowledgeLoginMessage}
+      modalTitle="Login Message"
+      modalBody={<div style={{ whiteSpace: "pre-wrap" }}>{loginMessage.text}</div>}
+      successCallback={acknowledgeLoginMessage}
       options={{
         modalSize: "modal-lg",
-        okButtonText: "SAVE",
-        cancelButtonText: "CANCEL",
-        okButtonVariant: "success"
+        cancelableViaBtn: false,
+        okButtonText: loginMessage.buttonText,
       }}
     />
-  )
+  );
 }
-
-export default UserSettingsModal;
\ No newline at end of file
diff --git a/ambari-web/latest/src/screens/Experimental/index.tsx b/ambari-web/latest/src/screens/Experimental/index.tsx
new file mode 100644
index 0000000000..f554f65a9b
--- /dev/null
+++ b/ambari-web/latest/src/screens/Experimental/index.tsx
@@ -0,0 +1,139 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useContext, useState } from "react";
+import { Alert, Button, Form, Table } from "react-bootstrap";
+import { useNavigate } from "react-router-dom";
+import ClusterApi from "../../api/clusterApi";
+import Modal from "../../components/Modal";
+import { useAuth } from "../../hooks/useAuth";
+import { db } from "../../Utils/db";
+import { persistedPayload } from "../../Utils/persistedSettings";
+import { AppContext } from "../../store/context";
+import { responseErrorMessage } from "../../Utils/httpError";
+
+export default function Experimental() {
+  const { user, hasAuthorization } = useAuth();
+  const {
+    isNonWizardUser,
+    setSupports: setSharedSupports,
+    supports: sharedSupports,
+  } = useContext(AppContext);
+  const navigate = useNavigate();
+  const [supports, setSupports] = useState(sharedSupports);
+  const [saving, setSaving] = useState(false);
+  const [error, setError] = useState("");
+  const [showReset, setShowReset] = useState(false);
+  const key = `user-pref-${user?.user_name || ""}-supports`;
+  const canReset = hasAuthorization("CLUSTER.MANAGE_USER_PERSISTED_DATA");
+
+  const save = async () => {
+    setSaving(true);
+    setError("");
+    try {
+      await ClusterApi.postPersistData(persistedPayload({ [key]: supports }));
+      setSharedSupports(supports);
+      navigate("/", { replace: true });
+    } catch (saveError) {
+      setError(responseErrorMessage(saveError, "Unable to save experimental settings."));
+    } finally {
+      setSaving(false);
+    }
+  };
+
+  const reset = async () => {
+    setSaving(true);
+    setError("");
+    try {
+      await ClusterApi.postPersistData(persistedPayload({ "wizard-data": {} }));
+      db.cleanUp();
+      localStorage.removeItem("lastVisitedURL");
+      window.location.hash = "/";
+      window.location.reload();
+    } catch (resetError) {
+      setError(responseErrorMessage(resetError, "Unable to reset UI state."));
+      setShowReset(false);
+    } finally {
+      setSaving(false);
+    }
+  };
+
+  return (
+    <main className="container py-4">
+      <Alert variant="danger">
+        <h2>Ambari's Experimental Functionality</h2>
+        <strong>Changes made here are neither tested nor supported by Ambari.</strong>
+        <p className="mb-0 mt-2">
+          Experimental functionality can change or be removed without notice.
+        </p>
+      </Alert>
+      {error ? <Alert variant="danger">{error}</Alert> : null}
+      <Table hover responsive>
+          <thead>
+            <tr><th>Experimental Functionality</th><th>Enabled?</th></tr>
+          </thead>
+          <tbody>
+            {Object.entries(supports).map(([name, enabled]) => (
+              <tr key={name} className={enabled ? "table-active" : ""}>
+                <td>{name}</td>
+                <td>
+                  <Form.Check
+                    type="checkbox"
+                    checked={enabled}
+                    onChange={(event) => setSupports((current) => ({
+                      ...current,
+                      [name]: event.target.checked,
+                    }))}
+                    aria-label={`Enable ${name}`}
+                  />
+                </td>
+              </tr>
+            ))}
+          </tbody>
+      </Table>
+      <div className="d-flex justify-content-between">
+        <div>
+          {canReset ? (
+            <Button
+              variant="danger"
+              onClick={() => setShowReset(true)}
+              disabled={saving || isNonWizardUser}
+              title={isNonWizardUser ? "Another user owns the active wizard." : undefined}
+            >
+              Reset UI States
+            </Button>
+          ) : null}
+        </div>
+        <div className="d-flex gap-2">
+          <Button variant="secondary" onClick={() => navigate("/", { replace: true })}>Cancel</Button>
+          <Button variant="primary" onClick={() => void save()} disabled={saving}>Save</Button>
+        </div>
+      </div>
+      {showReset ? (
+        <Modal
+          isOpen
+          onClose={() => setShowReset(false)}
+          modalTitle="Reset UI States"
+          modalBody="Reset UI state locally and on the server?"
+          successCallback={() => void reset()}
+          options={{ okButtonText: "Yes", okButtonVariant: "danger" }}
+        />
+      ) : null}
+    </main>
+  );
+}
diff --git a/ambari-web/latest/src/store/UserContext.test.tsx b/ambari-web/latest/src/store/UserContext.test.tsx
new file mode 100644
index 0000000000..419a0cbf74
--- /dev/null
+++ b/ambari-web/latest/src/store/UserContext.test.tsx
@@ -0,0 +1,125 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { act, render, waitFor } from "@testing-library/react";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+import { UserContextType } from "../types/auth";
+
+const mocks = vi.hoisted(() => ({
+  authenticate: vi.fn(),
+  handleSuccessfulLogin: vi.fn(),
+  loadAuthorizationsCallback: vi.fn(),
+  loadLoginMessage: vi.fn(),
+  logout: vi.fn(),
+  probeSession: vi.fn(),
+}));
+vi.mock("../api/loginApi", () => ({ default: mocks }));
+
+import { UserProvider, useUserContext } from "./UserContext";
+import { db } from "../Utils/db";
+
+let currentContext: UserContextType;
+
+function ContextReader() {
+  currentContext = useUserContext();
+  return null;
+}
+
+const user = {
+  user_name: "operator/name",
+  user_id: 1,
+  user_type: "LOCAL",
+  admin: false,
+  operator: true,
+  cluster_user: false,
+  active: true,
+  ldap_user: false,
+  principal_type: "USER",
+};
+
+describe("user session lifecycle", () => {
+  beforeEach(() => {
+    vi.clearAllMocks();
+    localStorage.clear();
+    sessionStorage.clear();
+    mocks.probeSession.mockRejectedValue({ response: { status: 401 } });
+    mocks.loadLoginMessage.mockResolvedValue(null);
+  });
+
+  async function renderProvider() {
+    render(<UserProvider><ContextReader /></UserProvider>);
+    await waitFor(() => expect(currentContext.isLoading).toBe(false));
+  }
+
+  it("does not establish a session when authorization loading fails", async () => {
+    db.set("Installer", "currentStep", 4);
+    mocks.authenticate.mockResolvedValue({});
+    mocks.handleSuccessfulLogin.mockResolvedValue({
+      data: { Users: user, privileges: [] },
+    });
+    mocks.loadAuthorizationsCallback.mockRejectedValue(new Error("unavailable"));
+    await renderProvider();
+
+    let result = true;
+    await act(async () => {
+      result = await currentContext.login("operator/name", "secret");
+    });
+
+    expect(result).toBe(false);
+    expect(currentContext.isAuthenticated).toBe(false);
+    expect(db.get("Installer", "currentStep")).toBe(4);
+  });
+
+  it("recovers a server-authenticated session from the User header", async () => {
+    mocks.probeSession.mockResolvedValue({ headers: { user: "operator/name" } });
+    mocks.handleSuccessfulLogin.mockResolvedValue({
+      data: { Users: user, privileges: [] },
+    });
+    mocks.loadAuthorizationsCallback.mockResolvedValue({ data: { items: [] } });
+    await renderProvider();
+
+    expect(currentContext.isAuthenticated).toBe(true);
+    expect(currentContext.user?.user_name).toBe("operator/name");
+    expect(mocks.handleSuccessfulLogin).toHaveBeenCalledWith({
+      usr: "",
+      loginName: "operator/name",
+    });
+  });
+
+  it("completes client cleanup when server logout fails", async () => {
+    mocks.authenticate.mockResolvedValue({});
+    mocks.handleSuccessfulLogin.mockResolvedValue({
+      data: { Users: user, privileges: [] },
+    });
+    mocks.loadAuthorizationsCallback.mockResolvedValue({ data: { items: [] } });
+    mocks.logout.mockRejectedValue(new Error("server unavailable"));
+    await renderProvider();
+
+    await act(async () => {
+      expect(await currentContext.login("operator/name", "secret")).toBe(true);
+    });
+    db.set("Installer", "currentStep", 4);
+    await act(async () => {
+      await currentContext.logout();
+    });
+
+    expect(currentContext.isAuthenticated).toBe(false);
+    expect(currentContext.user).toBeNull();
+    expect(db.get("Installer", "currentStep")).toBeUndefined();
+  });
+});
diff --git a/ambari-web/latest/src/store/UserContext.tsx b/ambari-web/latest/src/store/UserContext.tsx
index dbff61a613..06ba15d5c1 100644
--- a/ambari-web/latest/src/store/UserContext.tsx
+++ b/ambari-web/latest/src/store/UserContext.tsx
@@ -16,58 +16,73 @@
  * limitations under the License.
  */
 
-import React, { createContext, useContext, useEffect, useState, useCallback } from 'react';
-import { User, UserContextType, Authorization, Privilege} from '../types/auth';
-import LoginApi from '../api/loginApi';
-import { db } from '../Utils/db';
-import { isString } from 'lodash';
-import { AppContext } from './context';
+import React, { createContext, useCallback, useContext, useEffect, useState } from "react";
+import { isString } from "lodash";
+import LoginApi, { LoginMessage } from "../api/loginApi";
+import { db } from "../Utils/db";
+import {
+  resetExternalRedirectCount,
+  SESSION_EXPIRED_EVENT,
+} from "../Utils/authNavigation";
+import { Authorization, Privilege, User, UserContextType } from "../types/auth";
 
 const UserContext = createContext<UserContextType | undefined>(undefined);
 
 export const useUserContext = () => {
   const context = useContext(UserContext);
   if (!context) {
-    throw new Error('useUserContext must be used within a UserProvider');
+    throw new Error("useUserContext must be used within a UserProvider");
   }
   return context;
 };
 
-interface UserProviderProps {
-  children: React.ReactNode;
+function storedLoginName(): string | null {
+  const value = db.getItem("ambari");
+  if (!value) {
+    return null;
+  }
+
+  try {
+    let parsed: any = JSON.parse(value);
+    if (isString(parsed)) {
+      parsed = JSON.parse(parsed);
+    }
+    return parsed?.app?.loginName ? decodeURIComponent(parsed.app.loginName) : null;
+  } catch {
+    return null;
+  }
 }
 
-export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
+export const UserProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
   const [user, setUser] = useState<User | null>(null);
   const [isAuthenticated, setIsAuthenticated] = useState(false);
   const [isLoading, setIsLoading] = useState(true);
   const [authorizations, setAuthorizations] = useState<Authorization[]>([]);
   const [privileges, setPrivileges] = useState<Privilege[]>([]);
   const [clusterPrivileges, setClusterPrivileges] = useState<Record<string, string[]>>({});
-  const [viewPrivileges, setViewPrivileges] = useState<Record<string, { privileges: string[]; version: string; view_name: string }>>({});
+  const [viewPrivileges, setViewPrivileges] = useState<Record<string, {
+    privileges: string[];
+    version: string;
+    view_name: string;
+  }>>({});
   const [loginError, setLoginError] = useState<string | null>(null);
-  
-  // Access AppContext for upgrade state - this will be available after AppProvider wraps UserProvider
-  const appContext = useContext(AppContext);
-  // Parse privileges into cluster and view privileges
-  const parsePrivileges = useCallback((privilegesList: Privilege[]) => {
+  const [loginMessage, setLoginMessage] = useState<LoginMessage | null>(null);
+  const [sessionError, setSessionError] = useState<string | null>(null);
+
+  const parsePrivileges = useCallback((items: Privilege[]) => {
     const clusters: Record<string, string[]> = {};
     const views: Record<string, { privileges: string[]; version: string; view_name: string }> = {};
 
-    privilegesList.forEach((privilege) => {
-      if (privilege.type === 'CLUSTER' && privilege.cluster_name) {
-        if (!clusters[privilege.cluster_name]) {
-          clusters[privilege.cluster_name] = [];
-        }
+    items.forEach((privilege) => {
+      if (privilege.type === "CLUSTER" && privilege.cluster_name) {
+        clusters[privilege.cluster_name] ??= [];
         clusters[privilege.cluster_name].push(privilege.permission_label);
-      } else if (privilege.type === 'VIEW' && privilege.instance_name) {
-        if (!views[privilege.instance_name]) {
-          views[privilege.instance_name] = {
-            privileges: [],
-            version: privilege.version || '',
-            view_name: privilege.view_name || ''
-          };
-        }
+      } else if (privilege.type === "VIEW" && privilege.instance_name) {
+        views[privilege.instance_name] ??= {
+          privileges: [],
+          version: privilege.version || "",
+          view_name: privilege.view_name || "",
+        };
         views[privilege.instance_name].privileges.push(privilege.permission_label);
       }
     });
@@ -76,299 +91,186 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
     setViewPrivileges(views);
   }, []);
 
-  // Helper methods - implementing Ember.js App.havePermissions and App.isAuthorized logic
-  const havePermissions = useCallback((authRoles: string): boolean => {
-    if (!authorizations.length) {
-      return false;
-    }
-
-    const authRolesList = authRoles.split(',').map(role => role.trim());
-    
-    // When Upgrade running(not suspended) only operations related to upgrade should be allowed
-    // This matches the Ember.js logic in ui/app/app.js
-    const upgradeState = appContext?.upgradeState || 'NOT_REQUIRED';
-    const upgradeSuspended = appContext?.upgradeSuspend || false;
-    
-    // Check if upgrade is blocking operations
-    if ((!upgradeSuspended &&
-         !authRolesList.includes('CLUSTER.UPGRADE_DOWNGRADE_STACK') &&
-         !authRolesList.includes('CLUSTER.MANAGE_USER_PERSISTED_DATA')) &&
-        // TODO: Add supports.opsDuringRollingUpgrade check when available
-        !['NOT_REQUIRED', 'COMPLETED'].includes(upgradeState)) {
-      return false;
-    }
-    
-    return authRolesList.some(auth => 
-      authorizations.some(authorization => authorization.authorization_id === auth)
-    );
-  }, [authorizations, appContext?.upgradeState, appContext?.upgradeSuspend]);
+  const clearSession = useCallback(() => {
+    setUser(null);
+    setIsAuthenticated(false);
+    setAuthorizations([]);
+    setPrivileges([]);
+    setClusterPrivileges({});
+    setViewPrivileges({});
+    setLoginMessage(null);
+    db.clearSession();
+  }, []);
 
-  const hasAuthorization = useCallback((authId: string): boolean => {
-    // This implements App.isAuthorized logic: havePermissions + wizard check
-    // For now, we'll use havePermissions. Wizard check can be added later when needed
-    
-    // Special case: Cluster operators should have HOST.ADD_DELETE_COMPONENTS permission
-    // This matches Ember.js behavior where cluster operators can manage components
-    if (authId === 'HOST.ADD_DELETE_COMPONENTS') {
-      // Check if user has explicit authorization OR is a cluster operator
-      return havePermissions(authId) || havePermissions('CLUSTER.ADMINISTRATOR');
+  const loadSession = useCallback(async (loginName: string): Promise<User> => {
+    const [userResponse, authorizationResponse, message] = await Promise.all([
+      LoginApi.handleSuccessfulLogin({ usr: "", loginName }),
+      LoginApi.loadAuthorizationsCallback({ usr: "", loginName }),
+      LoginApi.loadLoginMessage(),
+    ]);
+    const userData = userResponse.data.Users as User | undefined;
+    if (!userData) {
+      throw new Error("Ambari did not return the authenticated user");
     }
-    
-    return havePermissions(authId);
-  }, [havePermissions]);
 
-  const hasPrivilege = useCallback((permissionName: string, clusterName?: string): boolean => {
-    if (clusterName) {
-      return clusterPrivileges[clusterName]?.includes(permissionName) || false;
-    }
-    // Check across all clusters if no specific cluster is provided
-    return Object.values(clusterPrivileges).some(privs => privs.includes(permissionName));
-  }, [clusterPrivileges]);
-
-  const isAdmin = useCallback((): boolean => {
-    const hasAmbariAdmin = privileges.some(privilege => 
-      privilege.permission_name === 'AMBARI.ADMINISTRATOR'
+    const mappedPrivileges = (userResponse.data.privileges || []).map(
+      (item: any) => item.PrivilegeInfo,
     );
-    
-    if (hasAmbariAdmin) {
-      return true;
-    }
-    
-    // Check if user has CLUSTER.ADMINISTRATOR permission for any cluster
-    return hasAuthorization('CLUSTER.ADMINISTRATOR');
-  }, [privileges, hasAuthorization]);
-
-  const isOperator = useCallback((): boolean => {
-    // Based on Ember.js router.js loginGetClustersSuccessCallback logic:
-    // isOperator is set to true when user has CLUSTER.ADMINISTRATOR permission
-    // This matches the exact logic in router.js lines 670-675
-    return hasAuthorization('CLUSTER.ADMINISTRATOR');
-  }, [hasAuthorization]);
-
-  const isClusterUser = useCallback((): boolean => {
-    // Based on Ember.js router.js loginGetClustersSuccessCallback logic:
-    // isClusterUser is set to false when user has CLUSTER.ADMINISTRATOR permission
-    // This matches the exact logic in router.js lines 670-675
-    return !hasAuthorization('CLUSTER.ADMINISTRATOR');
-  }, [hasAuthorization]);
-
-  // Additional role helper - isClusterOperator (same as isOperator but more explicit naming)
-  const isClusterOperator = useCallback((): boolean => {
-    // This follows the users_mapper.js logic: user has CLUSTER.ADMINISTRATOR but not AMBARI.ADMINISTRATOR
-    const hasAmbariAdmin = privileges.some(privilege => 
-      privilege.permission_name === 'AMBARI.ADMINISTRATOR'
+    const mappedAuthorizations = (authorizationResponse.data.items || []).map(
+      (item: any) => item.AuthorizationInfo,
     );
-    return hasAuthorization('CLUSTER.ADMINISTRATOR') && !hasAmbariAdmin;
-  }, [hasAuthorization, privileges]);
 
-  // Load user authorizations
-  const loadAuthorizations = useCallback(async (loginName: string): Promise<Authorization[]> => {
-    try {
-      const response = await LoginApi.loadAuthorizationsCallback({
-        usr: '',
-        loginName: encodeURIComponent(loginName)
-      });
-      
-      const authList = response.data.items.map((item: any) => item.AuthorizationInfo);
-      setAuthorizations(authList);
-      
-      // Store in database similar to Ember implementation
-      const authIds = authList.map((auth: Authorization) => auth.authorization_id);
-      db.set('app', 'auth', authIds);
-      
-      return authList;
-    } catch (error) {
-      console.error('Failed to load authorizations:', error);
-      return [];
-    }
-  }, []);
-
-  // Load user data and privileges
-  const loadUserData = useCallback(async (loginName: string): Promise<User | null> => {
-    try {
-      const response = await LoginApi.handleSuccessfulLogin({
-        usr: '',
-        loginName: encodeURIComponent(loginName)
-      });
-
-      const userData = response.data.Users;
-      setUser(userData);
-
-      // Load privileges if they exist in the response
-      if (response.data.privileges) {
-        // Map the privileges from the API response structure
-        const mappedPrivileges = response.data.privileges.map((item: any) => item.PrivilegeInfo);
-        setPrivileges(mappedPrivileges);
-        parsePrivileges(mappedPrivileges);
-      }
-
-      return userData;
-    } catch (error) {
-      console.error('Failed to load user data:', error);
-      return null;
-    }
+    setUser(userData);
+    setPrivileges(mappedPrivileges);
+    setAuthorizations(mappedAuthorizations);
+    parsePrivileges(mappedPrivileges);
+    const authorizationIds = mappedAuthorizations.map(
+      (authorization: Authorization) => authorization.authorization_id,
+    );
+    db.setSession(userData.user_name, userData, authorizationIds);
+    setLoginMessage(message);
+    setIsAuthenticated(true);
+    resetExternalRedirectCount();
+    return userData;
   }, [parsePrivileges]);
 
-  // Initialize user from stored data
-  const initializeUser = useCallback(async (): Promise<boolean> => {
+  const initializeUser = useCallback(async () => {
+    setIsLoading(true);
+    setSessionError(null);
     try {
-      setIsLoading(true);
-      
-      const ambariLocalData = db.getItem('ambari');
-      if (!ambariLocalData) {
-        setIsLoading(false);
-        return false;
-      }
-
-      let parsedData: any = {};
-      try {
-        parsedData = JSON.parse(ambariLocalData);
-        if (isString(parsedData)) {
-          parsedData = JSON.parse(parsedData);
-        }
-      } catch (err) {
-        console.error('Error parsing ambari data:', err);
-        setIsLoading(false);
-        return false;
-      }
-
-      const loginName = parsedData?.app?.loginName;
+      const response = await LoginApi.probeSession();
+      const responseUser = response.headers?.user as string | undefined;
+      const loginName = responseUser || storedLoginName();
       if (!loginName) {
-        setIsLoading(false);
-        return false;
+        clearSession();
+        return;
       }
 
-      // Load user data and authorizations
-      const [userData] = await Promise.all([
-        loadUserData(decodeURIComponent(loginName)),
-        loadAuthorizations(decodeURIComponent(loginName))
-      ]);
-
-      if (userData) {
-        setIsAuthenticated(true);
-        setIsLoading(false);
-        return true;
+      await loadSession(loginName);
+    } catch (error: any) {
+      clearSession();
+      const status = error?.response?.status;
+      if (status !== 401 && status !== 403) {
+        setSessionError(
+          error?.response?.data?.message || "Ambari Server could not validate the current session.",
+        );
       }
-
-      setIsLoading(false);
-      return false;
-    } catch (error) {
-      console.error('Failed to initialize user:', error);
+    } finally {
       setIsLoading(false);
-      return false;
     }
-  }, [loadUserData, loadAuthorizations]);
+  }, [clearSession, loadSession]);
 
-  // Login method
   const login = useCallback(async (username: string, password: string): Promise<boolean> => {
+    setIsLoading(true);
+    setLoginError(null);
     try {
-      setIsLoading(true);
-      
-      // Authenticate user
       await LoginApi.authenticate(username, password);
-      
-      // Store initial data
-      const initialAmbariLsData = {
-        app: {
-          loginName: encodeURIComponent(username),
-          authenticated: true
-        }
-      };
-      
-      db.setItem('ambari', JSON.stringify(initialAmbariLsData));
-      
-      // Load user data and authorizations
-      const [userData] = await Promise.all([
-        loadUserData(username),
-        loadAuthorizations(username)
-      ]);
-
-      if (userData) {
-        // Update stored data with user info
-        const updatedData = {
-          app: {
-            loginName: encodeURIComponent(username),
-            authenticated: true,
-            user: userData
-          }
-        };
-        db.setItem('ambari', JSON.stringify(updatedData));
-        
-        setIsAuthenticated(true);
-        setIsLoading(false);
-        setLoginError(null);
-        return true;
+      await loadSession(username);
+      return true;
+    } catch (error: any) {
+      clearSession();
+      const status = error?.response?.status;
+      if (status === 403) {
+        setLoginError(error?.response?.data?.message || "Invalid username or password.");
+      } else if (status === 500) {
+        setLoginError(error?.response?.data?.message || "Ambari Server could not complete the login request.");
+      } else {
+        setLoginError(error?.response?.data?.message || "Unable to sign in to Ambari.");
       }
-      setIsLoading(false);
       return false;
-    } catch (error:any) {
-      setLoginError(error?.response?.data?.message || 'Login failed');
+    } finally {
       setIsLoading(false);
-      return false;
     }
-  }, [loadUserData, loadAuthorizations]);
+  }, [clearSession, loadSession]);
+
+  const logout = useCallback(async () => {
+    clearSession();
+    db.cleanUp();
+    sessionStorage.clear();
+    localStorage.removeItem("lastVisitedURL");
+    void LoginApi.logout().catch(() => {
+      // Client cleanup and navigation must not depend on server logoff success.
+    });
+  }, [clearSession]);
 
-  // Logout method
-  const logout = useCallback(async (): Promise<void> => {
-    try {
-      await LoginApi.logout();
-    } catch (error) {
-      console.error('Logout error:', error);
-    } finally {
-      // Clear all user data
-      setUser(null);
-      setIsAuthenticated(false);
-      setAuthorizations([]);
-      setPrivileges([]);
-      setClusterPrivileges({});
-      setViewPrivileges({});
-      db.cleanUp();
+  const refreshUserData = useCallback(async () => {
+    if (user?.user_name) {
+      await loadSession(user.user_name);
     }
-  }, []);
+  }, [loadSession, user]);
+
+  const havePermissions = useCallback((authorizationIds: string): boolean => {
+    const requested = authorizationIds.split(",").map((value) => value.trim());
+    return requested.some((id) => authorizations.some(
+      (authorization) => authorization.authorization_id === id,
+    ));
+  }, [authorizations]);
+
+  const hasAuthorization = useCallback((authorizationId: string): boolean => {
+    if (authorizationId === "HOST.ADD_DELETE_COMPONENTS") {
+      return havePermissions(authorizationId) || havePermissions("CLUSTER.ADMINISTRATOR");
+    }
+    return havePermissions(authorizationId);
+  }, [havePermissions]);
 
-  // Refresh user data
-  const refreshUserData = useCallback(async (): Promise<void> => {
-    if (user?.user_name) {
-      await Promise.all([
-        loadUserData(user.user_name),
-        loadAuthorizations(user.user_name)
-      ]);
+  const hasPrivilege = useCallback((permissionName: string, clusterName?: string): boolean => {
+    if (clusterName) {
+      return clusterPrivileges[clusterName]?.includes(permissionName) || false;
     }
-  }, [user, loadUserData, loadAuthorizations]);
+    return Object.values(clusterPrivileges).some((items) => items.includes(permissionName));
+  }, [clusterPrivileges]);
 
-  // Initialize on mount
-  useEffect(() => {
-    initializeUser();
-  }, [initializeUser]);
+  const isAdmin = useCallback(() => privileges.some(
+    (privilege) => privilege.permission_name === "AMBARI.ADMINISTRATOR",
+  ) || hasAuthorization("CLUSTER.ADMINISTRATOR"), [hasAuthorization, privileges]);
+  const isOperator = useCallback(
+    () => hasAuthorization("CLUSTER.ADMINISTRATOR"),
+    [hasAuthorization],
+  );
+  const isClusterUser = useCallback(() => !isOperator(), [isOperator]);
+  const isClusterOperator = useCallback(() => isOperator() && !privileges.some(
+    (privilege) => privilege.permission_name === "AMBARI.ADMINISTRATOR",
+  ), [isOperator, privileges]);
 
-  // Update parsed privileges when privileges change
   useEffect(() => {
-    parsePrivileges(privileges);
-  }, [privileges, parsePrivileges]);
+    const expireSession = () => {
+      clearSession();
+      setSessionError(null);
+      setIsLoading(false);
+    };
+    window.addEventListener(SESSION_EXPIRED_EVENT, expireSession);
+    return () => window.removeEventListener(SESSION_EXPIRED_EVENT, expireSession);
+  }, [clearSession]);
 
-  const contextValue: UserContextType = {
-    user,
-    isAuthenticated,
-    isLoading,
-    authorizations,
-    privileges,
-    clusterPrivileges,
-    viewPrivileges,
-    havePermissions,
-    hasAuthorization,
-    hasPrivilege,
-    isAdmin,
-    isOperator,
-    isClusterUser,
-    isClusterOperator,
-    login,
-    logout,
-    refreshUserData,
-    loginError
-  };
+  useEffect(() => {
+    void initializeUser();
+  }, [initializeUser]);
 
   return (
-    <UserContext.Provider value={contextValue}>
+    <UserContext.Provider value={{
+      user,
+      isAuthenticated,
+      isLoading,
+      sessionError,
+      authorizations,
+      privileges,
+      clusterPrivileges,
+      viewPrivileges,
+      havePermissions,
+      hasAuthorization,
+      hasPrivilege,
+      isAdmin,
+      isOperator,
+      isClusterUser,
+      isClusterOperator,
+      login,
+      logout,
+      refreshUserData,
+      retrySession: initializeUser,
+      loginError,
+      loginMessage,
+      acknowledgeLoginMessage: () => setLoginMessage(null),
+    }}>
       {children}
     </UserContext.Provider>
   );
diff --git a/ambari-web/latest/src/store/context.tsx b/ambari-web/latest/src/store/context.tsx
index 12a88acd9b..e3349678b8 100644
--- a/ambari-web/latest/src/store/context.tsx
+++ b/ambari-web/latest/src/store/context.tsx
@@ -29,12 +29,12 @@ import { Client } from "@stomp/stompjs";
 import ClusterApi from "../api/clusterApi";
 import { ChooseServicesApi } from "../api/chooseServicesApi";
 import { ServicesApi } from "../api/servicesApi";
-import { get, isEmpty, isString, isUndefined, map, set } from "lodash";
+import { get, isEmpty, isUndefined, map, set } from "lodash";
 import ConfigsApi from "../api/configsApi";
-import { mapStackConfigProperties, redirectToLogin } from "../Utils/Utility";
-import LoginApi from "../api/loginApi";
-import { db } from "../Utils/db";
+import { mapStackConfigProperties } from "../Utils/Utility";
 import useAuth from "../hooks/useAuth";
+import { parsePersistedValue, persistedPayload } from "../Utils/persistedSettings";
+import { DEFAULT_SUPPORTS } from "../constants";
 // import {LocalStorageOps} from "../Utils/LocalStorageOps";
 
 interface AppContextProps {
@@ -47,11 +47,16 @@ interface AppContextProps {
   services: any[];
   cluster: any;
   isAppLoaded: boolean;
+  initializationError: string | null;
+  retryInitialization: () => void;
   serviceComponentInfo: any;
   isKerberosEnabled: boolean;
   stackConfigurations: any;
   allHostNames: string[];
   ambariProperties: any;
+  ambariServerVersion?: string;
+  supports: Record<string, boolean>;
+  setSupports: (supports: Record<string, boolean>) => void;
   upgradeState: string;
   setUpgradeState: (state: string) => void;
   upgradeDirection: string;
@@ -73,6 +78,7 @@ interface AppContextProps {
   clusterState: any;
   userBgPreferences: boolean;
   setUserBgPreferences: (value: boolean) => void;
+  syncUserBgPreferences: (value: boolean) => void;
   // Background Operations - persistent cache like Ember.js singleton
   backgroundOperations: any[];
   setBackgroundOperations: (operations: any[]) => void;
@@ -87,6 +93,7 @@ interface AppContextProps {
   upgradeSuspended: boolean;
   upgradeIsRunning: boolean;
   wizardIsNotFinished: boolean;
+  isNonWizardUser: boolean;
   isClusterInstalled?: boolean;
 }
 
@@ -100,11 +107,16 @@ export const AppContext = createContext<AppContextProps>({
   services: [],
   cluster: {},
   isAppLoaded: false,
+  initializationError: null,
+  retryInitialization: () => {},
   serviceComponentInfo: {},
   isKerberosEnabled: false,
   stackConfigurations: [],
   allHostNames: [],
   ambariProperties: {},
+  ambariServerVersion: "",
+  supports: DEFAULT_SUPPORTS,
+  setSupports: () => {},
   upgradeState: "",
   setUpgradeState: () => {},
   upgradeDirection: "",
@@ -123,6 +135,7 @@ export const AppContext = createContext<AppContextProps>({
   clusterState: {},
   userBgPreferences: false,
   setUserBgPreferences: () => {},
+  syncUserBgPreferences: () => {},
   // Background Operations defaults
   backgroundOperations: [],
   setBackgroundOperations: () => {},
@@ -139,6 +152,7 @@ export const AppContext = createContext<AppContextProps>({
   upgradeIsFinalizeItem: false,
   setUpgradeIsFinalizeItem: () => {},
   wizardIsNotFinished: false,
+  isNonWizardUser: false,
   isClusterInstalled: false,
 });
 
@@ -149,6 +163,8 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
   const [isSocketConnected, setIsSocketConnected] = useState(false);
   const [socketClient, setSocketClient] = useState(null);
   const [isAppLoaded, setAppLoaded] = useState(false);
+  const [initializationError, setInitializationError] = useState<string | null>(null);
+  const [initializationAttempt, setInitializationAttempt] = useState(0);
   const [parsedSocketMessages, setParsedSocketMessages] = useState<any[]>([]);
   const [clusterName, setClusterName] = useState<string>("");
   const [isKerberosEnabled, setIsKerberosEnabled] = useState(false);
@@ -166,21 +182,25 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
     useState<string>("");
   const [currentStackVersion, setCurrentStackVersion] = useState<string>("");
   const [ambariProperties, setAmbariProperties] = useState({});
-  const [sessionsValidated, setSessionsValidated] = useState(false);
-  const [sessionExists, setSessionExists] = useState(false);
+  const [ambariServerVersion, setAmbariServerVersion] = useState("");
+  const [supports, setSupports] = useState(DEFAULT_SUPPORTS);
+  const [wizardUser, setWizardUser] = useState("");
   const [clusterState, setClusterState] = useState({});
   const [userUrl, setUserUrl] = useState("");
-  const { user } = useAuth();
+  const { authorizations, user } = useAuth();
   const loginName = user?.user_name;
-  const client = new Client({
-    brokerURL: "/api/stomp/v1/websocket", // 'ws://localhost:15674/ws'
-    debug: function (str) {
-      console.log(str);
-    },
-    reconnectDelay: 1000,
-    heartbeatIncoming: 1000,
-    heartbeatOutgoing: 1000,
-  });
+  const isOnlyViewUser = authorizations.length === 0
+    || (authorizations.length === 1
+      && authorizations[0].authorization_id === "VIEW.USE");
+  const [client] = useState(() => new Client({
+      brokerURL: "/api/stomp/v1/websocket", // 'ws://localhost:15674/ws'
+      debug: function (str) {
+        console.log(str);
+      },
+      reconnectDelay: 1000,
+      heartbeatIncoming: 1000,
+      heartbeatOutgoing: 1000,
+    }));
   const [services, setServices] = useState([]);
   const [stackConfigurations, setStackConfigurations] = useState([]);
   const [userBgPreferences, setUserBgPreferences] = useState(false);
@@ -257,6 +277,7 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
       setAppLoaded(true);
     } catch (err) {
       setServices([]);
+      setInitializationError("Ambari could not load the installed cluster services.");
     }
   };
 
@@ -270,10 +291,16 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
   };
 
   useEffect(() => {
-    fetchClusterState();
-  }, []);
+    if (!isOnlyViewUser) {
+      void fetchClusterState();
+    }
+  }, [isOnlyViewUser]);
 
   useEffect(() => {
+    if (isOnlyViewUser) {
+      setAppLoaded(true);
+      return;
+    }
     if (clusterName && isClusterInstalled) {
       fetchClusterServices();
       fetchAllHostNames();
@@ -282,7 +309,7 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
     } else if (!isUndefined(isClusterInstalled) && !isClusterInstalled) {
       setAppLoaded(true);
     }
-  }, [clusterName, isClusterInstalled]);
+  }, [clusterName, isClusterInstalled, isOnlyViewUser, initializationAttempt]);
 
   useEffect(() => {
     if (!clusterName || !isClusterInstalled) return;
@@ -334,16 +361,7 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
     fetchStackConfigs();
   }, [services, cluster]);
 
-  const fetchClusterName = async () => {
-    try {
-      const name = await ClusterApi.getClusterName();
-      setClusterName(name);
-    } catch (error) {
-      console.error("Failed to fetch cluster name:", error);
-    }
-  };
   const fetchClusterData = async () => {
-    try {
       const clusterData = await ClusterApi.getClusterData();
       set(
         clusterData,
@@ -356,16 +374,14 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
         get(clusterData, "items.[0].Clusters.version", "")?.split("-")[1]
       );
       const clusterInfo = clusterData?.items[0]?.Clusters;
-      setCluster(clusterInfo);
+      setCluster(clusterInfo || {});
+      setClusterName(clusterInfo?.cluster_name || "");
       setIsKerberosEnabled(
         clusterData?.items?.[0]?.Clusters?.security_type === "KERBEROS"
       );
 
       const isInstalled = clusterInfo?.provisioning_state === "INSTALLED";
       setIsClusterInstalled(isInstalled);
-    } catch (error) {
-      setIsClusterInstalled(false);
-    }
   };
 
   const fetchServiceComponentInfo = async () => {
@@ -392,65 +408,8 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
   };
   const getAmbariProperties = async () => {
     const response = await ClusterApi.loadAmbariProperties();
-    setAmbariProperties(response);
-  };
-  const fetchUserInfo = async () => {
-    try {
-      try {
-        let username = "";
-        const ambariLocalData = db.getItem("ambari");
-        if (ambariLocalData) {
-          let parsedData = {};
-          try {
-            parsedData = JSON.parse(db.getItem("ambari") || "{}");
-            if (isString(parsedData)) {
-              parsedData = JSON.parse(parsedData);
-            }
-          } catch (err) {
-            console.log("Error parsing ambari data", err);
-            parsedData = {};
-          }
-
-          let ambari: any = parsedData;
-          if (ambari?.app?.loginName) {
-            username = ambari.app.loginName;
-            // If we already have a username, we can consider the user authenticated
-            if (!username) {
-              window.location.href = "/#/login";
-            }
-            setSessionsValidated(true);
-            setSessionExists(true);
-          }
-        } else {
-          redirectToLogin();
-        }
-        // If we don't have user info in localStorage
-
-        //fetch initial LS value for key ambari with empty fields
-        let initialAmbariLsData: any = { app: {} };
-
-        //set login name in the app object within the ambari object
-        initialAmbariLsData.app.loginName = encodeURIComponent(username);
-        initialAmbariLsData.app.authenticated = true;
-        const params = { usr: "", loginName: encodeURIComponent(username) };
-        const response = await LoginApi.handleSuccessfulLogin(params);
-        initialAmbariLsData.app.user = response.data.Users;
-        //convert JS object to JSON String and then encrypt the JSON String
-        initialAmbariLsData = JSON.stringify(initialAmbariLsData);
-        //encrypt the data and store it in Local Storage
-        db.setItem("ambari", initialAmbariLsData);
-        return true;
-      } catch (error) {
-        setSessionsValidated(true);
-        setSessionExists(false);
-        return false;
-      }
-    } catch (err) {
-      console.log("Error in fetching user info", err);
-      setSessionsValidated(true);
-      setSessionExists(false);
-      return false;
-    }
+    setAmbariProperties(response?.RootServiceComponents?.properties || {});
+    setAmbariServerVersion(response?.RootServiceComponents?.component_version || "");
   };
 
   const fetchUpgradeStates = async () => {
@@ -516,11 +475,7 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
       const persistedData = await ClusterApi.getPersistData(
         "admin-settings-show-bg-" + loginName
       );
-      if (!!persistedData) {
-        setUserBgPreferences(true);
-      } else {
-        setUserBgPreferences(false);
-      }
+      setUserBgPreferences(parsePersistedValue(persistedData, true));
     } catch (err) {
       console.error("Could not fetch user bg preferences", err);
     }
@@ -528,11 +483,9 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
   async function setUserBgPreferencesData(value: boolean) {
     try {
       setUserBgPreferences(value);
-      await ClusterApi.postPersistData(
-        JSON.stringify({
-          ["admin-settings-show-bg-" + loginName]: `${value}`,
-        })
-      );
+      await ClusterApi.postPersistData(persistedPayload({
+        ["admin-settings-show-bg-" + loginName]: value,
+      }));
     } catch (err) {
       console.error("Could not fetch user bg preferences", err);
     }
@@ -540,21 +493,36 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
 
   useEffect(() => {
     async function moveAppToReadyState() {
-      const isAuthenticated = await fetchUserInfo();
-      setSessionsValidated(true);
-      setSessionExists(true);
-      if (isAuthenticated) {
-        await fetchClusterName();
-        await fetchClusterData();
+      setAppLoaded(false);
+      setInitializationError(null);
+      const supportsKey = `user-pref-${loginName || ""}-supports`;
+      try {
+        const [savedSupports, wizardData] = await Promise.all([
+          ClusterApi.getPersistData(supportsKey).catch(() => null),
+          ClusterApi.getPersistData("wizard-data").catch(() => null),
+        ]);
+        setSupports({
+          ...DEFAULT_SUPPORTS,
+          ...parsePersistedValue(savedSupports, {}),
+        });
+        setWizardUser(parsePersistedValue<{ userName?: string }>(wizardData, {}).userName || "");
+
         await getAmbariProperties();
-        try {
-          await getUserUrl();
-        } catch (err) {}
-      } else {
+        if (isOnlyViewUser) {
+          setAppLoaded(true);
+          return;
+        }
+
+        await fetchClusterData();
+        await getUserUrl().catch(() => undefined);
+      } catch (error: any) {
+        setInitializationError(
+          error?.response?.data?.message || "Ambari could not initialize the application shell.",
+        );
       }
     }
-    moveAppToReadyState();
-  }, []);
+    void moveAppToReadyState();
+  }, [initializationAttempt, isOnlyViewUser, loginName]);
 
   useEffect(() => {
     if (!isEmpty(cluster) && cluster?.versionNum && cluster?.stack) {
@@ -637,22 +605,6 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
   client.onConnect = function () {
     setSocketClient(client as any);
     setIsSocketConnected(true);
-    client.subscribe("/events/requests", (message: any) => {
-      // called when the client receives a STOMP message from the server
-      if (message.body) {
-        try {
-          const parsedMessage = JSON.parse(message.body);
-          //TODO: parsedSocketMessages can exceed to very long list, need to limit it to some constant
-          setParsedSocketMessages((prevMessages) => [
-            parsedMessage,
-            ...prevMessages,
-          ]);
-        } catch {
-          console.log("Error in parsing socket message");
-        }
-      } else {
-      }
-    });
     client.subscribe("/events/services", (message: any) => {
       // called when the client receives a STOMP message from the server
       if (message.body) {
@@ -735,10 +687,24 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
     console.error("Broker reported error: " + frame.headers["message"]);
     console.error("Additional details: " + frame.body);
   };
+  client.onDisconnect = function () {
+    setSocketClient(null);
+    setIsSocketConnected(false);
+  };
+  client.onWebSocketClose = function () {
+    setSocketClient(null);
+    setIsSocketConnected(false);
+  };
 
   useEffect(() => {
+    if (isOnlyViewUser) {
+      return;
+    }
     client.activate();
-  }, []);
+    return () => {
+      void client.deactivate();
+    };
+  }, [client, isOnlyViewUser]);
 
   // Ember.js upgrade computed properties - implementing the same logic as ui/app/app.js
   const upgradeInit = upgradeState === "NOT_REQUIRED";
@@ -750,8 +716,8 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
   const upgradeAborted = upgradeState === "ABORTED" && !upgradeSuspend;
   const upgradeSuspended = upgradeState === "ABORTED" && upgradeSuspend;
   const upgradeIsRunning = upgradeInProgress || upgradeHolding;
-  // TODO: Add wizardWatcherController.isNonWizardUser check when available
-  const wizardIsNotFinished = upgradeIsRunning || upgradeSuspended;
+  const isNonWizardUser = Boolean(wizardUser && wizardUser !== loginName);
+  const wizardIsNotFinished = upgradeIsRunning || upgradeSuspended || isNonWizardUser;
 
   return (
     <AppContext.Provider
@@ -764,9 +730,14 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
         clusterName,
         cluster,
         isAppLoaded,
+        initializationError,
+        retryInitialization: () => setInitializationAttempt((value) => value + 1),
         services,
         serviceComponentInfo,
         ambariProperties,
+        ambariServerVersion,
+        supports,
+        setSupports,
         isKerberosEnabled,
         stackConfigurations,
         allHostNames,
@@ -784,11 +755,12 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
         upgradeVersionDisplayName,
         setUpgradeVersionDisplayName,
         userUrl,
-        sessionExists,
-        sessionsValidated,
+        sessionExists: true,
+        sessionsValidated: true,
         clusterState,
         userBgPreferences,
         setUserBgPreferences: setUserBgPreferencesData,
+        syncUserBgPreferences: setUserBgPreferences,
         // Background Operations
         backgroundOperations,
         setBackgroundOperations,
@@ -804,6 +776,7 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
         upgradeIsFinalizeItem,
         setUpgradeIsFinalizeItem,
         wizardIsNotFinished,
+        isNonWizardUser,
         isClusterInstalled,
       }}
     >
diff --git a/ambari-web/latest/src/components/UserSettingsModal.tsx b/ambari-web/latest/src/test/setup.ts
similarity index 53%
copy from ambari-web/latest/src/components/UserSettingsModal.tsx
copy to ambari-web/latest/src/test/setup.ts
index 4d574303a2..3eb6781b61 100644
--- a/ambari-web/latest/src/components/UserSettingsModal.tsx
+++ b/ambari-web/latest/src/test/setup.ts
@@ -16,29 +16,39 @@
  * limitations under the License.
  */
 
-import Modal from "./Modal";
-
-type UserSettingsModalProps = {
-  isOpen: boolean;
-  onClose: () => void;
-};
-
-const UserSettingsModal = ({ isOpen, onClose }: UserSettingsModalProps) => {
-  return (
-    <Modal
-      isOpen={isOpen}
-      onClose={onClose}
-      modalTitle="User Settings"
-      modalBody={<div/>}
-      successCallback={() => {}}
-      options={{
-        modalSize: "modal-lg",
-        okButtonText: "SAVE",
-        cancelButtonText: "CANCEL",
-        okButtonVariant: "success"
-      }}
-    />
-  )
+class MemoryStorage implements Storage {
+  private values = new Map<string, string>();
+
+  get length() {
+    return this.values.size;
+  }
+
+  clear() {
+    this.values.clear();
+  }
+
+  getItem(key: string) {
+    return this.values.get(key) ?? null;
+  }
+
+  key(index: number) {
+    return Array.from(this.values.keys())[index] ?? null;
+  }
+
+  removeItem(key: string) {
+    this.values.delete(key);
+  }
+
+  setItem(key: string, value: string) {
+    this.values.set(key, String(value));
+  }
 }
 
-export default UserSettingsModal;
\ No newline at end of file
+Object.defineProperty(globalThis, "localStorage", {
+  configurable: true,
+  value: new MemoryStorage(),
+});
+Object.defineProperty(globalThis, "sessionStorage", {
+  configurable: true,
+  value: new MemoryStorage(),
+});
diff --git a/ambari-web/latest/src/types/auth.ts b/ambari-web/latest/src/types/auth.ts
index cfa5cc8a65..7e3481ec26 100644
--- a/ambari-web/latest/src/types/auth.ts
+++ b/ambari-web/latest/src/types/auth.ts
@@ -53,6 +53,7 @@ export interface UserContextType {
   user: User | null;
   isAuthenticated: boolean;
   isLoading: boolean;
+  sessionError: string | null;
   
   // Authorization data
   authorizations: Authorization[];
@@ -75,11 +76,17 @@ export interface UserContextType {
   isClusterUser: () => boolean;
   isClusterOperator: () => boolean;
   loginError: string | null;
+  loginMessage: {
+    text: string;
+    buttonText: string;
+  } | null;
   
   // Actions
   login: (username: string, password: string) => Promise<boolean>;
   logout: () => Promise<void>;
   refreshUserData: () => Promise<void>;
+  retrySession: () => Promise<void>;
+  acknowledgeLoginMessage: () => void;
 }
 
 export interface LoginResponse {
diff --git a/ambari-web/latest/vite.config.ts b/ambari-web/latest/vite.config.ts
index 929c08ff59..abf5bd97c5 100755
--- a/ambari-web/latest/vite.config.ts
+++ b/ambari-web/latest/vite.config.ts
@@ -16,10 +16,14 @@
  * limitations under the License.
  */
 
-import { defineConfig } from 'vite'
+import { defineConfig } from 'vitest/config'
 import react from '@vitejs/plugin-react'
 
 export default defineConfig({
   plugins: [react()],
-  base:"./"
+  base:"./",
+  test: {
+    environment: 'jsdom',
+    setupFiles: ['./src/test/setup.ts'],
+  },
 })
diff --git a/ambari-web/pom.xml b/ambari-web/pom.xml
index a9d61682da..8dabfb03a6 100755
--- a/ambari-web/pom.xml
+++ b/ambari-web/pom.xml
@@ -138,6 +138,9 @@
                         <configuration>
                             <arguments>run build</arguments>
                             <workingDirectory>${ui2dir}</workingDirectory>
+                            <environmentVariables>
+                                <VITE_AMBARI_VERSION>${ambariVersion}</VITE_AMBARI_VERSION>
+                            </environmentVariables>
                         </configuration>
                     </execution>
                 </executions>
diff --git a/docs/frontend-refactor/ember-baseline/01-auth-shell.md b/docs/frontend-refactor/ember-baseline/01-auth-shell.md
new file mode 100644
index 0000000000..b5a65955ab
--- /dev/null
+++ b/docs/frontend-refactor/ember-baseline/01-auth-shell.md
@@ -0,0 +1,50 @@
+# 认证、应用外壳与全局导航
+
+## 功能清单
+
+| ID | 功能与行为 | 前置/边界 | 后端请求 | 主要证据 |
+| --- | --- | --- | --- | --- |
+| AUTH-001 | 访问 `/` 自动跳转登录页;登录页清空主界面标题并连接 login outlet | 未认证 | `router.login.clusters` | `app/router.js`,`app/templates/login.hbs` |
+| AUTH-002 | 用户名密码使用 UTF-8 Base64 组成 Basic Authorization,先验证身份,再加载当前用户、privileges 和集群 | 失败时 403 显示认证错误,500 显示服务错误,其他错误显示通用失败 | `router.login`、`router.afterLogin` | `app/router.js`,`app/controllers/login_controller.js` |
+| AUTH-003 | 支持 Knox/外部 JWT 登录跳转;401/403 响应中的 `jwtProviderUrl` 附加当前 URL 作为返回地址 | `/login/local` 永不跳外部认证;重定向次数超限显示错误弹窗 | `router.login.clusters` | `app/router.js` |
+| AUTH-004 | 已被服务端认证但 localStorage 未标记时,从响应 `User` header 恢复用户会话 | 用户数据加载后调用 authorization 请求,但旧代码使用 `.complete()` 串联后续流程,权限加载失败也会继续加载登录消息并尝试进入主界面;这是迁移时不能复刻的失败路径缺陷 | `router.afterLogin`、authorization load | `app/router.js#afterLoginSuccessCallback` |
+| AUTH-005 | 登录成功加载登录消息设置;启用的消息以 modal 展示,用户确认后继续 | 设置不存在、JSON 非法或请求失败时直接继续 | `router.login.message` | `app/router.js` |
+| AUTH-006 | 根据集群和权限决定首屏:已安装集群进入 Dashboard/恢复路径,无集群进入 Admin View,仅 View 用户进入 Views,未完成集群进入 Installer | preferred target 只按首字符接受 `/` 或 `#` 并排除 login;`//host/path` 因此前缀检查仍会被接受,构成经典 UI 的 protocol-relative 跳转缺陷 | `router.login.clusters`、`ambari.service.load_server_version` | `app/router.js#restorePreferedPath` |
+| AUTH-007 | 退出时停止主界面 polling、清 localStorage/向导状态/权限、发送 logoff,并回登录页 | 用户主动退出才向服务端发请求;已有集群数据时 complete 后刷新页面;keep-alive 标志只在 logoff 成功回调中清除,失败回调为空 | `router.logoff` | `app/router.js#logOff`、`app/router.js#logOffSuccessCallback` |
+| AUTH-008 | 维持服务端会话 keep-alive,主路由进入后启动 | updater 由 `isPollerRunning` 控制;正常 logoff 成功会停止,但 logoff 失败时标志可能保持为 true,不能把“退出必定停止”作为等价要求 | 见“认证与应用外壳”模块接口目录 | `app/controllers/application.js#startKeepAlivePoller`,`app/routes/main.js`,`app/router.js#logOffErrorCallback` |
+| SHELL-001 | 主应用进入时升级 localStorage schema,加载 supports、Ambari properties、cluster name、Views,再初始化全局模型和 polling | 未认证保存当前位置并跳登录;仅 View 用户不加载完整集群运维数据 | 多个 router/cluster 初始化请求 | `app/routes/main.js`,`app/controllers/main.js`,`app/controllers/global/cluster_controller.js` |
+| SHELL-002 | 全局导航包含 Dashboard、Services、Hosts、Alerts、Admin、Views,并由权限、安装状态、已安装服务和 feature flag 控制 | 菜单可见不等于 route 可进入;route 仍执行权限重定向 | 无单一请求 | `app/templates/main.hbs`,`app/views/main/menu.js`,`app/routes/main.js` |
+| SHELL-003 | 保存用户 preferred path,登录后恢复到原 route | 接受以 `/` 或 `#` 开头且不是 login 的值;经典实现错误接受 `//host`,React 必须使用同源/规范化 URL 校验而不是复刻该缺陷 | 无 | `app/router.js#savePreferedPath`、`app/router.js#restorePreferedPath` |
+| SHELL-004 | 页面标题、breadcrumb、侧边服务菜单、全局 spinner/loading overlay 随 route 和模型加载状态更新 | 数据未加载时 route 等待 `dataLoading()` 或相应 deferred | 全局加载请求 | `app/routes/main.js`,`app/views/common/breadcrumbs_view.js` |
+| SHELL-005 | Inactivity timeout 按 admin 与 readonly Ambari property 选择时长,监听 window/iframe 的 mousemove、keypress、click,每秒检查活跃时间,并在超时前 60 秒显示继续会话/退出倒计时 | wizard step 与 `/stack/upgrade` 期间跳过超时检查;超时或用户选择退出都会调用 logoff;设置来源是 Ambari properties,不是 User Settings popup | Ambari properties load;超时本身无额外读取请求 | `app/controllers/main.js#monitorInactivity`、`app/controllers/main.js#checkActiveness` |
+| SHELL-006 | About 弹窗显示 controller 中已缓存的 Ambari Server 版本;没有 click-time 版本请求 | 缓存来自 Installer 或 Main controller;均没有值时显示 N/A,因此不存在 About 点击请求失败流程 | 无 click-time 请求 | `app/controllers/application.js#ambariVersion`、`app/controllers/application.js#showAboutPopup` |
+| SHELL-007 | Experimental 页面读取支持开关,允许逐项修改并 Save/Cancel;Reset UI States 会清本地 DB/cache/向导/cluster status,并向服务端持久化 `wizard-data={}` | route 仅 `AMBARI.MANAGE_SETTINGS` 可进入;Reset 按钮另需 `CLUSTER.MANAGE_USER_PERSISTED_DATA` 且 non-wizard user 时禁用 | user supports preference、`persist.post` | `app/router.js#experimental`,`app/controllers/experimental.js`,`app/templates/experimental.hbs` |
+| SHELL-008 | 版本不一致检查在 Installer 进入前阻止继续并提示 server/web client mismatch | 真实行为依赖打包版本,`CONDITIONAL` | Server 版本请求 | `app/controllers/installer.js`,`app/routes/installer.js` |
+| SHELL-009 | User Settings popup 可保存是否自动展示 Background Operations 和 timezone,并列出当前用户的 cluster/View privileges | 菜单由 `AMBARI.MANAGE_SETTINGS` 控制,handler 又要求 `CLUSTER.UPGRADE_DOWNGRADE_STACK`,持久化最终要求 `CLUSTER.MANAGE_USER_PERSISTED_DATA`;这些不一致 gate 必须分别保留为基线事实 | `persist.get`、`persist.post`、`router.user.privileges` | `app/templates/application.hbs`,`app/controllers/global/user_settings_controller.js`,`app/templates/common/settings.hbs`,`app/mixins/common/persist.js` |
+
+## 权限模型
+
+登录后的 `privileges[].PrivilegeInfo.permission_name` 决定全局角色:
+
+- `AMBARI.ADMINISTRATOR` 设置 `App.isAdmin`。
+- 当前集群的 `CLUSTER.ADMINISTRATOR` 同时设置 `isAdmin` 和 `isOperator`。
+- Cluster Operator、Service Administrator、Service Operator、Cluster User 等细粒度动作最终使用 `App.isAuthorized(permission)` 判断。
+- 只有 View 权限、没有集群运维权限的用户进入 Views,不初始化完整运维页面。
+
+详细 permission 与功能关联见 [13-permissions-flags.md](13-permissions-flags.md)。
+
+## 全局 HTTP 语义
+
+- 所有命名请求默认 timeout 为 `App.timeout`,默认 `Content-Type: text/plain`,用于避免 Knox 修改 JSON body。
+- 没有自定义 error callback 时,500、401、407、413 等状态使用全局错误 modal,展示 method、URL、status 和服务端 message。
+- KDC 相关 400 错误会被识别并改用 Invalid KDC popup。
+- 同一 request 可以设置 loading popup;超过 500ms 才显示,complete 时清除。
+- 超长 GET 自动转换为带 `X-Http-Method-Override: GET` 的 POST。
+
+[generated/api-by-module/auth-shell.md](generated/api-by-module/auth-shell.md) 只是按请求名和 caller path 宽匹配的候选索引,可能混入或漏掉请求。权威网络核对必须联合 [AJAX 定义](generated/ajax-endpoints.md)、[AJAX 调用点](generated/ajax-calls.md)、[direct HTTP](generated/direct-http-calls.md)、[browser entrypoints](generated/browser-network-entrypoints.md) 和 [realtime channels](generated/realtime-channels.md)。
+
+## 占位与边界
+
+- `/main/admin/authentication`、`/main/admin/advanced`、`/main/admin/audit` 在 `routes/main.js` 有 outlet 路由,但经典 Ember 树中未找到对应完整 controller/template,标记为 `PLACEHOLDER`,不能作为 React 必须复刻的已确认页面。
+- `/main/test` 只在 `routes/main.js` 中连接 `mainTest` outlet;经典 Ember 树中没有对应 controller、template 或 view,标记为 `PLACEHOLDER`。该 route fragment 仍保留在静态目录中,但不生成 React 功能需求。
+- 用户、组、角色和 View 实例管理由独立 AngularJS Admin Console 提供,不属于本文 Ember 基线。
diff --git a/docs/frontend-refactor/ember-baseline/13-permissions-flags.md b/docs/frontend-refactor/ember-baseline/13-permissions-flags.md
new file mode 100644
index 0000000000..28535521b8
--- /dev/null
+++ b/docs/frontend-refactor/ember-baseline/13-permissions-flags.md
@@ -0,0 +1,158 @@
+# 权限、Feature Flag 与运行时条件索引
+
+本文不是权限名称清单,而是经典 Ember 对功能入口和操作能力的实际判定基线。React 必须同时复刻 RBAC、全局长流程互斥、feature flag、stack/service metadata、组件状态和 maintenance 条件;仅在页面加载后依赖后端返回 403 不等价。
+
+## 权限判定语义
+
+| ID | 规则 | 旧版精确行为 | 主要证据 |
+| --- | --- | --- | --- |
+| GATE-AUTH-001 | 逗号分隔权限是 OR | `App.havePermissions("A, B")` trim 后逐项检查,只要 `App.auth` 包含任意一个即为 true;React 不得解释为全部满足 | `app/app.js#havePermissions`、`test/app_test.js` |
+| GATE-AUTH-002 | `isAuthorized` 增加向导所有权限制 | `App.isAuthorized(x) = App.havePermissions(x) && !wizardWatcherController.isNonWizardUser`;另一用户占有长向导时,即使当前用户有 RBAC 权限也不能修改 | `app/app.js#isAuthorized`、`app/utils/helper.js#isAuthorized` |
+| GATE-AUTH-003 | `havePermissions` 不受向导所有权限制 | 模板 `havePermissions` 适合只读可见性;因此相同 permission 在 `havePermissions` 与 `isAuthorized` 下可能有不同结果 | `app/utils/helper.js` |
+| GATE-AUTH-004 | Upgrade 全局互斥及 OR 表达式污染 | upgrade 非 `NOT_REQUIRED/COMPLETED`、非 suspended,且 `supports.opsDuringRollingUpgrade=false` 时,一条权限表达式只有在整个字符串既不包含 `CLUSTER.UPGRADE_DOWNGRADE_STACK` 也不包含 `CLUSTER.MANAGE_USER_PERSISTED_DATA` 时才被提前拒绝;只要组合串包含任一例外,整条 OR 绕过 upgrade deny,随后命中串中任意其他权限也返回 true。例外不是逐 permission 判断 | `app/app.js#havePermissions`、`app/views/main/menu.js` |
+| GATE-AUTH-005 | 无权限模型即全部拒绝 | `App.auth` 尚未加载时 `havePermissions` 返回 false,UI 保持隐藏/禁用而不是先闪现操作 | `app/app.js`、登录初始化 |
+| GATE-AUTH-006 | 只有 View 权限的用户走独立导航 | `App.auth` 空数组,或权限恰好只有 `VIEW.USE` 时 `isOnlyViewUser=true`,登录后进入 Views,不加载完整集群运维外壳 | `AUTH-006`、`app/app.js#isOnlyViewUser`、`app/router.js` |
+| GATE-AUTH-007 | Cluster Administrator 提升前端角色标志 | 当前 cluster privilege 含 `CLUSTER.ADMINISTRATOR` 时同时设置 `App.isAdmin=true`、`isOperator=true`;Ambari Administrator 只由 `AMBARI.ADMINISTRATOR` 确认 | `app/router.js#loginGetClustersSuccessCallback` |
+| GATE-AUTH-008 | UI 权限不是服务端授权替代品 | Ember 只为已知动作隐藏/禁用入口;所有 mutation 仍需服务端验证。React 对照必须同时测试无权限深链和直接 API 调用 | 所有 route/template/controller、Ambari REST RBAC |
+
+## Ambari 级权限
+
+| Permission | 旧版 Ember 作用 | 关联功能 ID | 闸门类型/边界 |
+| --- | --- | --- | --- |
+| `AMBARI.ADD_DELETE_CLUSTERS` | 无集群时进入 Installer;安装 Step 7 中决定全新集群特有配置行为 | `AUTH-006`、`INST-FLOW-001`、`INST-MODE-001`、`INST-0-*` 到 `INST-10-*` | Installer route 硬重定向;没有权限转 Views |
+| `AMBARI.MANAGE_SETTINGS` | 进入 `/experimental`、编辑 supports;Background Operations footer 的管理动作 | `SHELL-007`、`BG-001` 到 `BG-005` | route guard + template action;非权限用户转 Views/Dashboard |
+| `AMBARI.MANAGE_STACK_VERSIONS` | Versions 页的 Manage Versions 外链、out-of-sync reinstall/remove 入口及 repository URL 编辑 | `VER-LIST-001` 到 `VER-LIST-009`、`VIEW-ADMIN-003` | 不能与 version install/reinstall/upgrade/hide-discard 混为一项;后者由 `CLUSTER.UPGRADE_DOWNGRADE_STACK` 禁用逻辑控制 |
+| `AMBARI.EDIT_STACK_REPOS` | repository URL 编辑的权限模型成员 | `STACK-SVC-003` 到 `STACK-SVC-005`、`INST-1-*` | application Admin Console 链接包含此权限;经典 stack 页面还依赖 cluster upgrade/version 权限,真实角色组合需运行态验证 |
+| `AMBARI.ASSIGN_ROLES` | 显示独立 Ambari Admin Console 入口 | `SHELL-002` | Ember 只负责跳转;用户/角色页面属于 AngularJS,`OUT_OF_SCOPE` |
+| `AMBARI.MANAGE_USERS` | 显示独立 Ambari Admin Console 入口 | `SHELL-002` | AngularJS Admin Console,`OUT_OF_SCOPE` |
+| `AMBARI.MANAGE_GROUPS` | 显示独立 Ambari Admin Console 入口 | `SHELL-002` | AngularJS Admin Console,`OUT_OF_SCOPE` |
+| `AMBARI.MANAGE_VIEWS` | 显示独立 Ambari Admin Console 入口 | `SHELL-002`、Views 文档的 Admin View 跳转 | View instance 管理由 AngularJS 完成,Ember 仅发现版本并跳转 |
+| `AMBARI.RENAME_CLUSTER` | 显示独立 Ambari Admin Console 入口 | `SHELL-002` | 当前 Ember 树没有 rename form,`OUT_OF_SCOPE` |
+| `AMBARI.ADMINISTRATOR` | 设置全局 `App.isAdmin` 并通常由服务端 privilege 集合赋予其他能力 | `AUTH-002`、`AUTH-006` | 不是所有按钮直接检查的万能 bypass;前端仍按返回的具体 permission names 判断 |
+
+## Cluster 级权限
+
+| Permission | 旧版 Ember 作用 | 关联功能 ID | 闸门类型/边界 |
+| --- | --- | --- | --- |
+| `CLUSTER.ADMINISTRATOR` | 设置 `isAdmin/isOperator` 角色标志 | `AUTH-002`、`SHELL-001` | 前端角色派生;动作仍按细粒度权限 |
+| `CLUSTER.USER` | 标识只有只读 cluster user 权限的用户 | `AUTH-006`、`SHELL-002` | mapper 分类;无直接 mutation 按钮 |
+| `CLUSTER.VIEW_CONFIGS` | Service Config tab 可见 | `SVC-CONFIG-001`、`SVC-CONFIG-002`、`HOST-TAB-001` | 使用 `havePermissions`,长向导占用时仍可只读查看 |
+| `CLUSTER.VIEW_STACK_DETAILS` | Admin 主入口和 Stack/Versions 只读页面可见 | `STACK-SVC-*`、`VER-LIST-*` | 与 upgrade permission OR;Admin route 本身另有 routePath 限制,深链需逐路由测试 |
+| `CLUSTER.TOGGLE_KERBEROS` | Kerberos 页面、Enable/Disable/Edit、长流程恢复 | `KRB-ENTRY-*` 到 `KRB-REC-*` | route guard + button guard +恢复 guard;还需 `supports.enableToggleKerberos` |
+| `CLUSTER.UPGRADE_DOWNGRADE_STACK` | Admin/Stack/Upgrade route、发起和控制 upgrade、Admin View 跳转、Kerberos CSV | `UPG-START-*`、`UPG-RUN-*`、`VER-LIST-*`、`KRB-MGMT-008` | route guard + mutation;upgrade 中该权限是全局互斥例外 |
+| `CLUSTER.MANAGE_ALERT_NOTIFICATIONS` | Actions 中 Manage Notifications,创建/编辑/删除 targets | `ALERT-LIST-005`、`ALERT-NOTIFY-*`、`ALERT-GROUP-005` | controller action 构造时过滤 |
+| `CLUSTER.TOGGLE_ALERTS` | Definition detail 中 enable/disable cluster-scope alert | `ALERT-LIST-004`、`ALERT-DEF-004`、`ALERT-SET-003` | template guard;service-scope 另用 `SERVICE.TOGGLE_ALERTS` |
+| `CLUSTER.MODIFY_CONFIGS` | Admin menu/Auto Start 的组合可见性以及 cluster-scope config 能力 | `SHELL-002`、`ADMIN-AUTO-*`、部分 `SVC-CONFIG-*` | 常与 service permission OR;具体 Service Config 编辑主要检查 `SERVICE.MODIFY_CONFIGS` |
+| `CLUSTER.MANAGE_AUTO_START` | 进入 Service Auto Start,并实际启用全局开关、全选和每个 component recovery checkbox | `ADMIN-AUTO-001` 到 `ADMIN-AUTO-004` | route 与 `SERVICE.MANAGE_AUTO_START` OR,但整个页面的编辑控件统一只看此 cluster permission |
+| `CLUSTER.MANAGE_USER_PERSISTED_DATA` | 允许写 UI persist/user preferences;upgrade 时仍可用 | `INST-FLOW-002`、所有长流程恢复、用户 UI 设置 | persist mixin guard;无权限时不能把客户端状态写到服务端 |
+| `CLUSTER.MANAGE_WIDGETS` | Dashboard/Service Metrics Widget 编辑 | `OUT_OF_SCOPE` | 因用户明确排除 Metrics,不作为 React 验收项 |
+
+## Service 与 Host 级权限
+
+| Permission | 旧版 Ember 作用 | 关联功能 ID | 闸门类型/边界 |
+| --- | --- | --- | --- |
+| `SERVICE.START_STOP` | service、component、host bulk start/stop/restart;abort eligible request;restart-required 操作 | `SVC-ALL-002` 到 `SVC-ALL-004`、`SVC-ACT-001`、`SVC-CONFIG-009`、`HOST-BULK-001`、`HOST-COMP-001` | 菜单构造 +模板;仍按 current/desired state、maintenance、upgrade过滤 |
+| `SERVICE.RUN_SERVICE_CHECK` | Run Service Check 的语义权限 | `SVC-ACT-002`、安装/安全/HA流程中的测试结果 | Service Actions 旧实现不是该项的独立 gate;宽 OR 分支可能因其他 service permission而加入 smoke test,最终依赖后端授权 |
+| `SERVICE.RUN_CUSTOM_COMMAND` | stack custom commands、Refresh Queues、Rebalance、特定 service 命令的语义权限 | `SVC-ACT-006` 到 `SVC-ACT-009`、`HOST-COMP-007`、`HOST-COMP-008` | metadata/status过滤存在,但 Service Actions 宽 OR 后多数命令没有对应 permission的第二次前端检查;最终依赖后端授权 |
+| `SERVICE.ADD_DELETE_SERVICES` | Add Service、Delete Service、Stack Services 中添加 | `SVC-ALL-001`、`SVC-ACT-005`、`SVC-ADD-*`、`STACK-SVC-002` | 同时要求 `supports.enableAddDeleteServices`;Add Service route 硬保护 |
+| `SERVICE.MODIFY_CONFIGS` | Service Config 编辑/保存/revert、override、DB test关联编辑、部分 restart UI | `SVC-CONFIG-003` 到 `SVC-CONFIG-010`、`CFG-GROUP-006`、`HOST-TAB-001` | 编辑控件检查 + save controller;旧版本/compare 模式始终只读 |
+| `SERVICE.COMPARE_CONFIGS` | 配置版本 Compare | `SVC-CONFIG-006` | `havePermissions`,只读能力 |
+| `SERVICE.MANAGE_CONFIG_GROUPS` | Manage Config Groups、group/host/override 管理 | `CFG-GROUP-001` 到 `CFG-GROUP-006`、`HOST-ADD-004` | template action;实际 config 保存还可能需 modify configs |
+| `SERVICE.TOGGLE_MAINTENANCE` | service/component maintenance 动作的语义权限 | `SVC-ACT-003`、`SVC-SUM-003`、`HOST-COMP-006` | Service Actions 的 toggle 在宽 OR 分支中直接加入,无独立前端 permission复查;组件路径也可能由 Host maintenance 权限显示,最终仍需后端按 scope授权 |
+| `SERVICE.TOGGLE_ALERTS` | Alerts 页面入口动作、创建向导 route、service-scope definition enable/disable/edit/delete | `ALERT-LIST-004`、`ALERT-DEF-002` 到 `ALERT-DEF-006`、`ALERT-CREATE-*` | Create 还要求 `supports.createAlerts`;Metric 类型创建排除 |
+| `SERVICE.DECOMMISSION_RECOMMISSION` | DataNode/NodeManager/RegionServer 等批量与单组件退役/重新服役 | `HOST-BULK-004`、`HOST-COMP-005` | 还要求 component 在 `components.decommissionAllowed` 且状态安全 |
+| `SERVICE.ENABLE_HA` | NN/RM/Ranger HA、JournalNode Management、Federation、HAWQ standby 入口及恢复 | `SVC-ACT-010`、`NNHA-*`、`JN-*`、`RMHA-*`、`RAHA-*`、`FED-*`、`RBF-*`、`HAWQ-*` | service action + `serviceTypes`/feature/组件前置;向导所有权会统一撤销操作能力 |
+| `SERVICE.MOVE` | Reassign Master 入口 | `SVC-MOVE-*`、`HOST-COMP-009` | 仅 `components.reassignable` master;所有 hosts 已有该 master 时禁用 |
+| `SERVICE.SET_SERVICE_USERS_GROUPS` | Admin Service Accounts 页面 | `ADMIN-ACCT-001`、`ADMIN-ACCT-002` | route 硬保护;与 Config edit 权限是两套能力 |
+| `SERVICE.MANAGE_AUTO_START` | 允许进入 Auto Start route | `ADMIN-AUTO-*` | 与 cluster auto-start permission OR;但页面没有按授权 service过滤,且所有开关/checkbox统一因缺 `CLUSTER.MANAGE_AUTO_START` 而 disabled,所以 service-only 用户只能查看 |
+| `SERVICE.VIEW_OPERATIONAL_LOGS` | Host Logs tab、task Log Search 链接 | `HOST-TAB-004`、`BG-003` | 还需 LOGSEARCH service、`supports.logSearch` 和目标 host/log metadata |
+| `HOST.ADD_DELETE_HOSTS` | Add Host、单/批删除 host | `HOST-ADD-*`、`HOST-DETAIL-004`、`HOST-BULK-008` | Add Host 入口还受 wizard/upgrade互斥;删除前做组件安全检查 |
+| `HOST.ADD_DELETE_COMPONENTS` | 添加、安装、删除、重新安装 host components | `HOST-BULK-005`、`HOST-BULK-009`、`HOST-COMP-002` 到 `HOST-COMP-004` | stack cardinality、最后 master、component state 是额外硬条件 |
+| `HOST.TOGGLE_MAINTENANCE` | host maintenance、部分 host/component maintenance 菜单 | `HOST-BULK-002`、`HOST-BULK-003`、`HOST-DETAIL-003`、`HOST-COMP-006` | scope 不同使用不同 endpoint |
+| `SERVICE.VIEW_METRICS` | Metrics 列/视图 | `OUT_OF_SCOPE` | 明确排除 |
+
+## `App.supports` Feature Flags
+
+本节权威枚举生成器可识别的 `App.supports.*` flags。它们的初始默认值在 `app/config.js`,进入 main/installer 时由 `experimentalController.loadSupports()` 从服务端覆盖。React 不得将当前默认值编译成永久行为;非 `App.supports` 的 runtime UI gates 另见下一节。
+
+| Flag(classic 默认) | 功能影响 | 关联功能 ID | 边界 |
+| --- | --- | --- | --- |
+| `enableAddDeleteServices=true` | Add/Delete Service 与 Stack Services add link | `SVC-ALL-001`、`SVC-ACT-005`、`SVC-ADD-*`、`STACK-SVC-002` | 与 `SERVICE.ADD_DELETE_SERVICES` AND |
+| `enableToggleKerberos=true` | Kerberos Admin menu/route/Enable/Disable/Edit | `KRB-ENTRY-002`、`KRB-MGMT-*` | 与 `CLUSTER.TOGGLE_KERBEROS` AND;Windows stack menu 另有排除 |
+| `preKerberizeCheck=false` | Enable Kerberos 前 server checks | `KRB-ENTRY-004` | false 时直接进入向导 |
+| `kerberosStackAdvisor=true` | Configure Identities recommendations | `KRB-4-002` | 仅没有 stored values 时调用 |
+| `regenerateKeytabsOnSingleHost=false` | Host action Regenerate Keytabs | `HOST-DETAIL-007`、`KRB-MGMT-006` | Kerberos enabled AND |
+| `autoRollbackHA=false` | NN HA 关键阶段 Close/rollback 模式 | `NNHA-REC-*` | true 隐藏关键 step 关闭按钮并进入自动 rollback;false 给 manual rollback instructions |
+| `manageJournalNode=true` | HDFS Service Action 的 Manage JournalNodes | `JN-ENTRY-*` | 还需 HA_MODE 且 host/JN count 可调整 |
+| `preInstallChecks=false` | Installer Customize Services 的 Pre-Install Checks | `INST-7-006` | 只用于全新 installer,不应误加到所有复用 Step 7 场景 |
+| `customizeAgentUserAccount=false` | SSH install options 的 Ambari Agent OS user | `INST-MODE-007`、`INST-2-002`、`HOST-ADD-001` | false 时 bootstrap payload 强制 `userRunAs=root` |
+| `skipComponentStartAfterInstall=false` | Install/Add Host/Add Service Step 9 状态机可在 install 后跳过 start/check | `INST-9-001`、`INST-9-004`、`HOST-ADD-006`、`SVC-ADD-007` | 改变进度权重、可完成状态和 retry路径;有专门 controller tests |
+| `disableCredentialsAutocompleteForRepoUrls=true` | repository URL 变化时是否保留浏览器 credential/autocomplete 相关输入 | `INST-1-004`、`STACK-SVC-004` | 仅交互细节,仍需保留避免凭据意外复用 |
+| `alwaysEnableManagedMySQLForHive=false` | 安装/Add Service 的 Hive managed MySQL option 可见性 | `INST-7-002`、`SVC-ADD-004` | 在普通 Service Configs route 与安装向导行为不同 |
+| `createAlerts=false` | Create Alert action | `ALERT-LIST-005`、`ALERT-CREATE-*` | 还需 toggle alert 权限;Metric type 排除 |
+| `preUpgradeCheck=true` | Pre-Upgrade checks 与 custom cluster check 流程 | `UPG-START-004`、`UPG-START-005` | 服务端仍决定具体 checks/bypassability |
+| `enabledWizardForHostOrderedUpgrade=true` | Host Ordered upgrade wizard 可选/受限 | `UPG-START-002`、`UPG-RUN-*` | false 时 controller `isWizardRestricted` 禁止相关 mutations |
+| `displayOlderVersions=false` | Hosts/Versions 对旧 stack version 的显示 | `HOST-TAB-003`、`VER-LIST-001` 到 `VER-LIST-004` | false 时过滤低于 current 的旧版本记录 |
+| `opsDuringRollingUpgrade=false` | Upgrade 期间是否允许普通 permission 操作 | `GATE-AUTH-004`、`UPG-RUN-010` | 位于全局 `havePermissions`,影响所有模块 |
+| `serviceAutoStart=true` | Admin Auto Start menu/page | `ADMIN-AUTO-*` | 仍需 cluster/service auto-start permission |
+| `enableNewServiceRestartOptions=false` | Service actions 中新的 Restart Service 选项 | `SVC-ACT-001`、`SVC-ACT-004` | 旧 Restart All/rolling actions仍可能存在 |
+| `logSearch=true` | Host Logs tab、task/host Log Search link和额外 logging resource load | `HOST-TAB-004`、`BG-003` | 还需 LOGSEARCH installed 和 operational logs permission |
+| `logCountVizualization=false` | Host Summary 的 log count visualization | `HOST-DETAIL-001` | 属于 operational logs,不是 Metrics;需 LOGSEARCH运行态验证 |
+| `installGanglia=false` | 是否保留旧 GANGLIA service model | `OUT_OF_SCOPE` | Ganglia 是旧 Metrics service,本文不作为安装功能要求 |
+| `customizedWidgetLayout=false` | Metrics widgets | `OUT_OF_SCOPE` | 明确排除 |
+| `showPageLoadTime=false` | 开发/实验 page load timer | `OUT_OF_SCOPE` | 不构成最终用户业务功能 |
+| `redhatSatellite=false` | config 中有默认定义,当前 classic `app/` 无消费调用 | 无 | `STATIC_ONLY` 遗留 capability,不作为已确认功能 |
+| `addingNewRepository=false` | config 中有默认定义,当前 classic `app/` 无消费调用 | 无 | `STATIC_ONLY` 遗留 capability |
+| `kerberosAutomated` | Add Service route 只有 TODO 注释,当前运行代码不读取 | 无 | 不得据注释创建 React 功能开关 |
+
+## 其他 Runtime UI Gates
+
+| ID | Gate(classic 默认) | 运行时来源与功能影响 | 边界 |
+| --- | --- | --- | --- |
+| GATE-RUNTIME-001 | `App.stackVersionsAvailable=true` | 初始加载所有 upgrades/repository versions 后重算为 `App.StackVersion` 是否非空;控制 Admin Versions tab 与 Host Stack Versions route | 不是 per-user supports,也不是 RBAC;无 version 时 Host深链回 Summary |
+| GATE-RUNTIME-002 | `App.upgradeHistoryAvailable=false` | `restoreUpgradeState()` 根据是否存在已完成/非运行 upgrade重算;控制 Upgrade History tab | 运行中/暂停的唯一一条 upgrade本身不使 history tab可见 |
+| GATE-RUNTIME-003 | `App.enableDigitalClock=false` | 本地 config直接控制顶栏 Clock view | 当前未从 supports覆盖;属于 shell UI gate,不是业务授权 |
+
+## Stack、Service 与 Component Metadata 条件
+
+| ID | 条件来源 | 旧版影响 | 关联功能 |
+| --- | --- | --- | --- |
+| GATE-META-001 | `StackService.isInstallable`、已安装 service names、service dependencies | Installer/Add Service 可选项,Delete Service 依赖限制 | `INST-4-*`、`SVC-ADD-001`、`SVC-ACT-005` |
+| GATE-META-002 | service `serviceTypes` 包含 `HA_MODE` | HDFS/YARN/RANGER/HAWQ 显示对应 HA action | `NNHA-*`、`JN-*`、`RMHA-*`、`RAHA-*`、`HAWQ-*` |
+| GATE-META-003 | service `serviceTypes` 包含 `FEDERATION` | HDFS 显示 NameNode Federation | `FED-*` |
+| GATE-META-004 | service `serviceTypes` 包含 `DFSRouter` | HDFS 显示 Router-based Federation | `RBF-*` |
+| GATE-META-005 | component cardinality/min/max、`isMaster`、`isClient`、`isSlave`、`isHAComponentOnly` | host assignment、add/delete component、skip master/slave steps | `INST-5-*`、`INST-6-*`、`HOST-COMP-002` 到 `HOST-COMP-004`、`SVC-ADD-*` |
+| GATE-META-006 | `components.reassignable` | Move Master action与目标 host | `SVC-MOVE-*`、`HOST-COMP-009` |
+| GATE-META-007 | `components.decommissionAllowed` | Decommission菜单 | `HOST-BULK-004`、`HOST-COMP-005` |
+| GATE-META-008 | stack component custom commands | Refresh/Rebalance/Knox/HBase及任意 command 可见性 | `SVC-ACT-006` 到 `SVC-ACT-009`、`HOST-COMP-007`、`HOST-COMP-008` |
+| GATE-META-009 | config types/themes/value attributes/dependencies | Config tab是否存在、控件、必填/只读/override/recommendations | `SVC-CONFIG-*`、`INST-7-*` |
+| GATE-META-010 | Windows stack/stack family/version | Kerberos menu、部分 services/commands、旧 HAWQ 能力 | `KRB-ENTRY-002`、`HAWQ-*` |
+
+## 状态、Maintenance 与长流程互斥
+
+| ID | 条件 | 必须保持的行为 | 关联功能 |
+| --- | --- | --- | --- |
+| GATE-STATE-001 | service/component desired/current state | 已 STARTED 不再显示 Start;已 INSTALLED 不再 Stop;transition/request中禁重复提交 | `SVC-ACT-001`、`HOST-COMP-001`、所有 progress wizard |
+| GATE-STATE-002 | host/component/service maintenance | start/stop、delete、HA前置等动作按 scope 禁用或跳过;maintenance 本身仍可由有权用户切换 | `HOST-BULK-*`、`HOST-COMP-*`、`SVC-ACT-*`、HA文档 |
+| GATE-STATE-003 | heartbeat/host health | Add Host registration、Recover、delete、HA host selection和decommission安全判断 | `INST-3-*`、`HOST-DETAIL-004`、`HOST-DETAIL-005`、HA文档 |
+| GATE-STATE-004 | stale configs/restart required | Restart Required/Refresh Configs 只对受影响 components | `SVC-ALL-004`、`SVC-ACT-004`、`SVC-CONFIG-009`、`HOST-COMP-007` |
+| GATE-STATE-005 | background request/schedule already active | 避免同一 action 重复发起,显示现有 operation或禁用 | `BG-*`、`HOST-BULK-010`、`SVC-ACT-001` |
+| GATE-STATE-006 | `wizardWatcherController.isNonWizardUser` | 全局 mutation权限临时失效;当前向导所有者继续,其他窗口只读或路由到流程 | `INST-FLOW-003`、`INST-FLOW-006`、`KRB-REC-*`、所有 HA recovery |
+| GATE-STATE-007 | upgrade state | 普通操作默认失效;upgrade owner/authorized user可控制 pause/retry/finalize | `UPG-RUN-*`、`GATE-AUTH-004` |
+| GATE-STATE-008 | Kerberos security type/KDC session | Add Host/Add Service/Reassign/HA等创建 component前验证或更新 identities | `INST-MODE-009`、`INST-MODE-010`、`KRB-X-*`、HA文档 |
+| GATE-STATE-009 | cluster provisioning/wizard `clusterState` | 刷新/崩溃后恢复当前 step/request,禁止从入口重新并发创建 | `INST-FLOW-*`、`KRB-REC-*`、所有 HA/Federation recovery |
+
+## 已知优先级与迁移风险
+
+| ID | 旧版表达式 | 风险/基线结论 |
+| --- | --- | --- |
+| GATE-RISK-001 | `!isWindows && isAuthorized(KERBEROS) \|\| upgradeRunning` | JavaScript 中 `&&` 优先于 `\|\|`,所以 upgrade running 时即使 Windows/无 Kerberos 权限也可能显示 Admin Kerberos category;这是旧版静态行为,不应无意当作安全授权。React 矩阵标 `BEHAVIOR_DIFF` 后由维护者决定是否修复 |
+| GATE-RISK-002 | Admin parent `enter` 接受四种权限 OR,但 `routePath` 主要要求 upgrade permission | 菜单可见、父 route 与子 route条件不完全一致;每个深链必须单测,不可只实现一个统一 Admin guard |
+| GATE-RISK-003 | Service actions 外层用多个 permissions OR | 旧代码在宽 OR 后直接加入 Refresh Configs、Restart All、Run Smoke Test、Toggle Maintenance以及多种 custom command;多数 action/controller没有按语义 permission二次过滤,只有 HA、Move、Add Component等局部另查。用户仅有 OR 中任一权限即可看到/触发若干其他语义动作,最终由 server授权。React 必须逐 action + route + server授权,不复制漏闸 |
+| GATE-RISK-006 | upgrade例外按整条 permission字符串判断 | 组合串只要包含 upgrade/persist例外,就会让串中其他权限一起绕过全局 upgrade deny;React 若按逐权限修正,应在矩阵标 `BEHAVIOR_DIFF`,不得误称经典已有严格隔离 |
+| GATE-RISK-004 | Client feature flags 可在 Experimental 页面修改 | flags 是服务端/用户环境事实,不是可信安全边界;后端必须继续授权 |
+| GATE-RISK-005 | 某些权限只出现在独立 Admin Console入口 | 不得因 permission 名存在就推断经典 Ember 有相应 CRUD 页面 |
+
+## Metrics 权限边界
+
+`SERVICE.VIEW_METRICS`、`CLUSTER.MANAGE_WIDGETS`、Metrics Widget 相关 feature flags 全部为 `OUT_OF_SCOPE`。HA checkpoint、decommission 或 component health 为安全判定读取 `metrics/...` 字段不受此排除影响,因为它们关联的是运维状态机而非指标展示。
diff --git a/docs/frontend-refactor/ember-baseline/14-react-gap-matrix.md b/docs/frontend-refactor/ember-baseline/14-react-gap-matrix.md
new file mode 100644
index 0000000000..d671269eca
--- /dev/null
+++ b/docs/frontend-refactor/ember-baseline/14-react-gap-matrix.md
@@ -0,0 +1,101 @@
+# React 功能差异矩阵规范
+
+本文定义后续 `ambari-web/latest` 对照 `ambari-web/classic` 的验收格式。当前阶段只建立规则和模板,不提前判断 React 覆盖状态。旧版事实保存在模块文档,React 状态是可反复更新的派生数据。
+
+## 对照单位
+
+矩阵一行对应一个稳定功能 ID,而不是一个 Ember route、React component 或 REST URL。同一个页面中的查看、修改、失败重试和恢复可能是不同功能;同一个接口也可能服务多个功能。
+
+对每个功能必须核对以下维度:
+
+| 维度 | `COVERED` 的最低要求 |
+| --- | --- |
+| 入口 | route、菜单、按钮、弹窗或自动触发点存在,深链和返回路径等价 |
+| 可见条件 | permission、feature flag、stack/service/component/status/maintenance/upgrade/wizard 条件等价 |
+| 正常行为 | 用户输入、校验、确认、请求顺序、成功结果和模型刷新等价 |
+| API | method、URL、predicate、fields、payload、header、operation level、context 和 response 分支等价 |
+| 异步行为 | request ID、polling/STOMP、task detail、日志、终止状态和进度展示等价 |
+| 异常行为 | 禁用、错误信息、retry、skip、rollback、cancel、partial failure 和重复提交保护等价 |
+| 恢复 | 刷新、崩溃、另一窗口、向导所有权和服务端状态恢复等价 |
+| 测试 | 至少有自动化测试;`STATIC_ONLY/CONDITIONAL` 还需记录真实集群场景证据 |
+
+任何一个必需维度不等价,都不能标 `COVERED`。
+
+## 状态枚举
+
+| 状态 | 定义 |
+| --- | --- |
+| `COVERED` | UI、API、条件、异常、恢复和测试均与旧版基线等价 |
+| `PARTIAL` | 主路径存在,但一个或多个必需维度尚未实现或缺少证据 |
+| `MISSING` | React 没有可达入口/行为,或只有无功能的页面壳 |
+| `BEHAVIOR_DIFF` | React 有意或无意改变旧版行为;必须记录差异、影响和维护者决策 |
+| `NOT_APPLICABLE` | 经维护者明确决定不迁移;必须给出理由/issue,不得用于隐藏缺失 |
+| `NEEDS_RUNTIME_VALIDATION` | 静态代码看似覆盖,但需真实 stack、KDC、HA、外部 DB/LDAP/Log Search 等环境确认 |
+| `BLOCKED` | 被明确的后端、依赖或基础设施问题阻塞;必须关联 issue,不能代替 `MISSING` |
+
+`PLACEHOLDER` 和 `OUT_OF_SCOPE` 是旧版证据等级/范围,不是 React 实现状态。旧版为 `PLACEHOLDER` 的条目通常不进入必做矩阵;Metrics `OUT_OF_SCOPE` 永远不作为缺口。
+
+## 矩阵字段
+
+| 字段 | 必填 | 内容 |
+| --- | --- | --- |
+| `feature_id` | 是 | 模块文档中的稳定 ID |
+| `legacy_doc` | 是 | 旧版模块文档和锚点/标题 |
+| `legacy_evidence` | 是 | `CONFIRMED/STATIC_ONLY/CONDITIONAL/PLACEHOLDER/OUT_OF_SCOPE` |
+| `react_status` | 是 | 上述状态枚举 |
+| `react_route` | 条件 | 可达 route/入口;自动行为写触发位置 |
+| `react_ui` | 条件 | component/hook/store 文件 |
+| `react_api` | 条件 | API client/query/mutation 文件和请求名/endpoint |
+| `condition_evidence` | 是 | 权限、flags、状态等价性的代码/测试证据 |
+| `happy_path_test` | 是 | unit/component/e2e/真实集群测试引用 |
+| `failure_recovery_test` | 是 | error/retry/cancel/refresh/ownership 等测试引用;不适用需说明 |
+| `differences` | 条件 | 缺失维度或有意行为变化的精确描述 |
+| `runtime_scenario` | 条件 | stack、service、KDC、HA topology、权限角色等复现条件 |
+| `issue` | 条件 | 修复/决策 issue 或 PR |
+| `reviewed_commit` | 是 | React 对照时使用的完整 Git commit |
+| `reviewed_by` | 是 | 审核人及日期 |
+
+推荐用 CSV/JSON 保存可编辑矩阵,Markdown 只作为自动渲染结果。旧版功能索引应由文档生成,禁止人工复制 ID 清单。
+
+## 行模板
+
+```csv
+feature_id,legacy_doc,legacy_evidence,react_status,react_route,react_ui,react_api,condition_evidence,happy_path_test,failure_recovery_test,differences,runtime_scenario,issue,reviewed_commit,reviewed_by
+INST-MODE-008,07-cluster-installation.md,CONFIRMED,MISSING,,,,,,,,,,,
+KRB-MODE-003,08-kerberos.md,CONDITIONAL,NEEDS_RUNTIME_VALIDATION,/main/admin/kerberos,...,...,...,...,...,,Existing IPA with Kerberos enabled,...,...,...
+```
+
+## 对照工作流
+
+1. 在固定 Ember baseline commit 上重跑提取器和基线检查,冻结功能 ID 与接口目录。
+2. 在固定 React commit 上扫描 routes、UI actions、API clients、permissions 和 tests,先建立静态候选关联。
+3. 逐功能核对全部维度;不能仅因 route 或 endpoint 存在就标覆盖。
+4. 对安装、Kerberos、HA/Federation、upgrade、external DB/LDAP/Log Search 等场景执行真实集群测试。
+5. 由第二位 reviewer 复查所有 `COVERED` 和 `NOT_APPLICABLE`;复杂长流程至少再做一次故障注入/刷新恢复。
+6. 每次 React PR 只更新受影响行的状态和证据,不改旧版事实;发现旧版基线错误时单独提交基线修正及源码证据。
+
+## 复杂流程场景矩阵
+
+下列场景不能由单一 happy path 代表:
+
+| 场景组 | 至少覆盖的变量 |
+| --- | --- |
+| Cluster Installation | Public/Local repository;VDF file/URL;SSH/manual Agent;warning acceptance;bootstrap/registration failure;install/start/check retry;刷新恢复 |
+| Add Host/Add Service | Kerberos off/automatic/manual;有/无 master/slave/config step;config group;descriptor/CSV;partial deployment failure |
+| Kerberos | MIT/AD/IPA/Manual;KDC connection failure;lost heartbeat;descriptor create/update;Step 7 force retry;Disable skip;credential persistent/temporary;keytab all/missing/restart |
+| NameNode/JournalNode HA | secure/non-secure;依赖 service动态任务;checkpoint等待;JN add/remove/delete-only;auto/manual rollback;刷新/另窗口 |
+| RM/Ranger HA | service/topology前置失败;host冲突;Stack Advisor changes;条件 HAWQ/HDFS配置;progress failure/retry |
+| Federation/HAWQ | NameNode/Router federation;format/bootstrap failure;Kerberos;旧 HAWQ stack 的 add/remove/activate standby |
+| Upgrade | Rolling/Express/Host Ordered;precheck required/warning/bypass;pause/resume/retry/skip/abort/downgrade/finalize;non-owner |
+| Permissions | 至少 read-only、service operator、cluster admin、Ambari admin、View-only;upgrade/wizard互斥下再次验证 |
+
+## 评审门禁
+
+一个模块可以宣布迁移完成,必须满足:
+
+1. 该模块所有纳入范围的 feature IDs 都有矩阵行且没有空白状态。
+2. `MISSING/PARTIAL/BLOCKED/BEHAVIOR_DIFF` 均有 issue 或明确维护决策。
+3. 所有 `COVERED` 都有正常路径和异常/恢复证据;无异常路径的简单只读项需明确写 `not applicable` 原因。
+4. 所有 `CONDITIONAL/STATIC_ONLY/NEEDS_RUNTIME_VALIDATION` 都有运行场景和测试结果。
+5. API 反向扫描没有未关联 mutation,route/action 反向扫描没有未关联可达用户行为。
+6. Metrics 排除项没有被错误计入完成率。
diff --git a/docs/frontend-refactor/ember-baseline/generated/ajax-calls.md b/docs/frontend-refactor/ember-baseline/generated/ajax-calls.md
new file mode 100644
index 0000000000..222693932b
--- /dev/null
+++ b/docs/frontend-refactor/ember-baseline/generated/ajax-calls.md
@@ -0,0 +1,440 @@
+# Ember 非 Metrics AJAX 调用点
+
+> 由 `tools/extract-ember-baseline.mjs` 生成。每行是一个 `App.ajax.send(...)` 调用点;动态请求按 `tools/contracts/dynamic-ajax-resolutions.mjs` 的人工审计契约解析。参数键只列调用对象中 `data: {...}` 的顶层键。
+
+共 394 个调用点,其中 364 个命中纳入范围的注册请求,3 个静态请求名未在注册表定义,27 个使用动态表达式。
+
+| 请求名/表达式 | 注册状态 | 调用参数 | 回调 | 位置 |
+| --- | --- | --- | --- | --- |
+| `router.login.clusters` | `REGISTERED` | 未发现内联 `data` | `callback` | [source](../../../../ambari-web/classic/app/controllers/application.js#L124) |
+| DYNAMIC: `queryParams.name` | `DYNAMIC` | 表达式:`queryParams.data` | `success`, `callback` | [source](../../../../ambari-web/classic/app/controllers/global/background_operations_controller.js#L178) |
+| `cluster.load_cluster_name` | `REGISTERED` | `args`, `callback`, `errorLogMessage`, `reloadPopupText`, `shouldUseDefaultHandler` | `success`, `error`, `callback` | [source](../../../../ambari-web/classic/app/controllers/global/cluster_controller.js#L110) |
+| `components.get.staleConfigs` | `REGISTERED` | 未发现内联 `data` | `success`, `callback` | [source](../../../../ambari-web/classic/app/controllers/global/cluster_controller.js#L263) |
+| `wizard.service_components` | `REGISTERED` | `stackUrl`, `stackVersion` | `success` | [source](../../../../ambari-web/classic/app/controllers/global/cluster_controller.js#L398) |
+| `ambari.service` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/global/cluster_controller.js#L410) |
+| `router.user.authorizations` | `REGISTERED` | `userName` | `success` | [source](../../../../ambari-web/classic/app/controllers/global/cluster_controller.js#L419) |
+| `hosts.all` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/global/cluster_controller.js#L460) |
+| `cluster.load_detailed_repo_version` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/global/cluster_controller.js#L501) |
+| `cluster.load_last_upgrade` | `REGISTERED` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/global/cluster_controller.js#L532) |
+| `config.tags` | `REGISTERED` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/global/configuration_controller.js#L144) |
+| `hosts.metrics.lazy_load` | `REGISTERED` | `parameters`, `url` | `success` | [source](../../../../ambari-web/classic/app/controllers/global/update_controller.js#L404) |
+| `hosts.host_components.pre_load` | `REGISTERED` | `callback`, `url` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/global/update_controller.js#L472) |
+| `config.tags.site` | `REGISTERED` | `site` | `callback` | [source](../../../../ambari-web/classic/app/controllers/global/update_controller.js#L703) |
+| `host.logging` | `REGISTERED` | `callback`, `fields`, `hostName` | `success` | [source](../../../../ambari-web/classic/app/controllers/global/update_controller.js#L729) |
+| `router.user.privileges` | `REGISTERED` | `userName` | `success` | [source](../../../../ambari-web/classic/app/controllers/global/user_settings_controller.js#L201) |
+| `wizard.stacks` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/installer.js#L237) |
+| `wizard.stacks_versions_definitions` | `REGISTERED` | `dfd`, `stackName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/installer.js#L271) |
+| `ambari.service` | `REGISTERED` | `fields` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/installer.js#L425) |
+| DYNAMIC: `name` | `DYNAMIC` | `data`, `dfd` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/installer.js#L590) |
+| DYNAMIC: `name` | `DYNAMIC` | `data`, `dfd` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/installer.js#L656) |
+| `wizard.step1.get_supported_os_types` | `REGISTERED` | `dfd`, `stackInfo`, `stackName`, `stackVersion`, `versionDefinition` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/installer.js#L701) |
+| `admin.stack_versions.edit.repo` | `REGISTERED` | `repoVersion`, `repoVersionId`, `stackName`, `stackVersion` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/installer.js#L822) |
+| `wizard.advanced_repositories.valid_url` | `REGISTERED` | `data`, `dfd`, `osId`, `osType`, `repoId`, `stackName`, `stackVersion` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/installer.js#L909) |
+| `cluster.save_provisioning_state` | `REGISTERED` | `state` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/installer.js#L1123) |
+| `config.tags` | `REGISTERED` | 未发现内联 `data` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/federation/routerBasedFederation/step3_controller.js#L50) |
+| `admin.get.all_configurations` | `REGISTERED` | `urlParams` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/federation/routerBasedFederation/step3_controller.js#L62) |
+| `common.service.configurations` | `REGISTERED` | `desired_config` | `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/federation/routerBasedFederation/step3_controller.js#L96) |
+| `common.service.multiConfigurations` | `REGISTERED` | `configs` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/federation/routerBasedFederation/step4_controller.js#L54) |
+| `config.tags` | `REGISTERED` | 未发现内联 `data` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/federation/step3_controller.js#L53) |
+| `admin.get.all_configurations` | `REGISTERED` | `urlParams` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/federation/step3_controller.js#L72) |
+| `common.service.multiConfigurations` | `REGISTERED` | `configs` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/federation/step4_controller.js#L76) |
+| `nameNode.federation.formatNameNode` | `REGISTERED` | `host` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/federation/step4_controller.js#L118) |
+| `nameNode.federation.formatZKFC` | `REGISTERED` | `host` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/federation/step4_controller.js#L130) |
+| `nameNode.federation.bootstrapNameNode` | `REGISTERED` | `host` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/federation/step4_controller.js#L164) |
+| `restart.custom.filter` | `REGISTERED` | `context`, `filter` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/federation/step4_controller.js#L184) |
+| `service.item.executeCustomCommand` | `REGISTERED` | `command`, `componentName`, `context`, `hosts`, `serviceName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/activateStandby/step3_controller.js#L42) |
+| `config.tags` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/activateStandby/step3_controller.js#L62) |
+| `reassign.load_configs` | `REGISTERED` | `type`, `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/activateStandby/step3_controller.js#L71) |
+| `common.service.configurations` | `REGISTERED` | `desired_config` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/activateStandby/step3_controller.js#L93) |
+| `config.tags` | `REGISTERED` | `serviceConfig` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/addStandby/step3_controller.js#L81) |
+| `reassign.load_configs` | `REGISTERED` | `serviceConfig`, `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/addStandby/step3_controller.js#L95) |
+| `config.tags` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/addStandby/step4_controller.js#L43) |
+| `reassign.load_configs` | `REGISTERED` | `type`, `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/addStandby/step4_controller.js#L52) |
+| `common.service.configurations` | `REGISTERED` | `desired_config` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/addStandby/step4_controller.js#L72) |
+| `service.item.executeCustomCommand` | `REGISTERED` | `command`, `componentName`, `context`, `hosts`, `serviceName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/removeStandby/step3_controller.js#L41) |
+| `config.tags` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/removeStandby/step3_controller.js#L61) |
+| `reassign.load_configs` | `REGISTERED` | `type`, `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/removeStandby/step3_controller.js#L70) |
+| `common.service.configurations` | `REGISTERED` | `desired_config` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/removeStandby/step3_controller.js#L87) |
+| `config.tags` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/journalNode/step2_controller.js#L65) |
+| `admin.get.all_configurations` | `REGISTERED` | `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/journalNode/step2_controller.js#L80) |
+| `admin.high_availability.getNnCheckPointsStatuses` | `REGISTERED` | 表达式:`{ hostNames }` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/journalNode/step3_controller.js#L50) |
+| `common.service.configurations` | `REGISTERED` | `desired_config` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/journalNode/step4_controller.js#L71) |
+| `admin.high_availability.getNnCheckPointStatus` | `REGISTERED` | `hostName` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollbackHA/step2_controller.js#L26) |
+| `common.services.update` | `REGISTERED` | `ServiceInfo`, `context` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js#L222) |
+| `admin.high_availability.load_hbase_configs` | `REGISTERED` | `hbaseSiteTag` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js#L238) |
+| `admin.high_availability.load_accumulo_configs` | `UNREGISTERED` | `accumuloSiteTag` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js#L251) |
+| `admin.high_availability.load_hawq_configs` | `UNREGISTERED` | `tagName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js#L265) |
+| `common.services.update` | `REGISTERED` | `ServiceInfo`, `context` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js#L347) |
+| `admin.save_configs` | `REGISTERED` | `properties`, `siteName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js#L363) |
+| `admin.save_configs` | `REGISTERED` | `properties`, `siteName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js#L376) |
+| `admin.save_configs` | `REGISTERED` | `properties`, `siteName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js#L390) |
+| `admin.high_availability.load_configs` | `REGISTERED` | `coreSiteTag`, `hdfsSiteTag` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js#L413) |
+| `admin.save_configs` | `REGISTERED` | `properties`, `siteName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js#L427) |
+| `admin.save_configs` | `REGISTERED` | `properties`, `siteName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js#L437) |
+| `common.delete.host_component` | `REGISTERED` | `componentName`, `hostName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js#L460) |
+| `common.host.host_component.passive` | `REGISTERED` | `callback`, `componentName`, `hostName`, `passive_state`, `taskNum` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js#L478) |
+| `admin.high_availability.getHostComponent` | `REGISTERED` | `callback`, `componentName`, `hostName`, `taskNum` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js#L500) |
+| `common.delete.host_component` | `REGISTERED` | `componentName`, `hostName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js#L549) |
+| `config.tags` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step3_controller.js#L64) |
+| `admin.get.all_configurations` | `REGISTERED` | `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step3_controller.js#L213) |
+| `admin.high_availability.getNnCheckPointStatus` | `REGISTERED` | `hostName` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step4_controller.js#L37) |
+| `common.host.host_component.passive` | `REGISTERED` | `componentName`, `hostName`, `passive_state` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step5_controller.js#L51) |
+| `common.service.configurations` | `REGISTERED` | `desired_config` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step5_controller.js#L82) |
+| `admin.high_availability.getJnCheckPointStatus` | `REGISTERED` | `hostName` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step6_controller.js#L79) |
+| `common.service.multiConfigurations` | `REGISTERED` | `configs` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step9_controller.js#L227) |
+| `common.service.configurations` | `REGISTERED` | `desired_config` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step9_controller.js#L246) |
+| `common.delete.host_component` | `REGISTERED` | `componentName`, `hostName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step9_controller.js#L310) |
+| DYNAMIC: `name` | `DYNAMIC` | `requestId`, `stageId` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/progress_popup_controller.js#L122) |
+| `background_operations.get_by_task` | `REGISTERED` | `requestId`, `taskId` | `success`, `callback` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/progress_popup_controller.js#L274) |
+| `config.tags` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/rangerAdmin/step4_controller.js#L45) |
+| `reassign.load_configs` | `REGISTERED` | `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/rangerAdmin/step4_controller.js#L61) |
+| `common.service.multiConfigurations` | `REGISTERED` | `configs` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/rangerAdmin/step4_controller.js#L87) |
+| `config.tags` | `REGISTERED` | `serviceConfig` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/resourceManager/step3_controller.js#L69) |
+| `reassign.load_configs` | `REGISTERED` | `serviceConfig`, `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/resourceManager/step3_controller.js#L85) |
+| `config.recommendations` | `REGISTERED` | `dataToSend`, `stackVersionUrl` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/resourceManager/step3_controller.js#L201) |
+| `config.tags` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/resourceManager/step4_controller.js#L65) |
+| `reassign.load_configs` | `REGISTERED` | `type`, `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/resourceManager/step4_controller.js#L74) |
+| `reassign.load_configs` | `REGISTERED` | `type`, `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/resourceManager/step4_controller.js#L87) |
+| `reassign.load_configs` | `REGISTERED` | `type`, `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/resourceManager/step4_controller.js#L100) |
+| `common.service.configurations` | `REGISTERED` | `desired_config` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/resourceManager/step4_controller.js#L120) |
+| `admin.kerberos_security.regenerate_keytabs` | `REGISTERED` | `type`, `withAutoRestart` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos.js#L182) |
+| `admin.kerberos_security.checks` | `REGISTERED` | 未发现内联 `data` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos.js#L234) |
+| `admin.security_status` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos.js#L307) |
+| `kerberos.session.state` | `REGISTERED` | `callback` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos.js#L422) |
+| `admin.security.cluster_configs.kerberos` | `REGISTERED` | `additionalCallback`, `clusterName` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos.js#L449) |
+| `admin.kerberos.cluster.artifact.update` | `REGISTERED` | `artifactName`, `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos.js#L556) |
+| `admin.kerberos.cluster.artifact.create` | `REGISTERED` | 表达式:`{ artifactName, data }` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos.js#L583) |
+| `admin.unkerberize.cluster` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/disable_controller.js#L47) |
+| `admin.unkerberize.cluster.skip` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/disable_controller.js#L56) |
+| `common.delete.service` | `REGISTERED` | `serviceName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/disable_controller.js#L65) |
+| `common.across.services.configurations` | `REGISTERED` | `data` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/step2_controller.js#L228) |
+| `common.cluster.update` | `REGISTERED` | `clusterName`, `data` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/step2_controller.js#L312) |
+| `common.services.update` | `REGISTERED` | `ServiceInfo`, `context`, `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/step3_controller.js#L42) |
+| `hosts.heartbeat_lost` | `REGISTERED` | `clusterName` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/step3_controller.js#L66) |
+| `common.service_component.info` | `REGISTERED` | `componentName`, `serviceName`, `urlParams` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/step3_controller.js#L76) |
+| `service.item.smoke` | `REGISTERED` | `actionName`, `displayName`, `operationLevel`, `serviceName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/step3_controller.js#L89) |
+| `config.recommendations` | `REGISTERED` | `dataToSend`, `stackVersionUrl` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/step4_controller.js#L342) |
+| `admin.kerberos.cluster.csv` | `REGISTERED` | `skipDownload` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/step5_controller.js#L63) |
+| `admin.unkerberize.cluster` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/step5_controller.js#L103) |
+| `common.services.update` | `REGISTERED` | `ServiceInfo`, `context` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/step6_controller.js#L25) |
+| `common.delete.host_component` | `REGISTERED` | `componentName`, `hostName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/step6_controller.js#L59) |
+| `admin.unkerberize.cluster` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/step7_controller.js#L69) |
+| `common.services.update` | `REGISTERED` | `ServiceInfo`, `context`, `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/step8_controller.js#L26) |
+| `wizard.step8.create_selected_services` | `REGISTERED` | `cluster`, `data` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/wizard_controller.js#L203) |
+| `common.delete.service` | `REGISTERED` | `serviceName` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/wizard_controller.js#L229) |
+| `admin.unkerberize.cluster` | `REGISTERED` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/wizard_controller.js#L244) |
+| `wizard.step8.register_host_to_component` | `REGISTERED` | `cluster`, `data` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/admin/kerberos/wizard_controller.js#L274) |
+| `components.get_category` | `REGISTERED` | 未发现内联 `data` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/service_auto_start.js#L133) |
+| `admin.save_configs` | `REGISTERED` | `properties`, `siteName` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/admin/service_auto_start.js#L151) |
+| `components.update` | `REGISTERED` | `ServiceComponentInfo`, `query` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/admin/service_auto_start.js#L163) |
+| DYNAMIC: `(onlyState) ? 'admin.upgrade.state' : 'admin.upgrade.data'` | `DYNAMIC` | `id` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L461) |
+| `admin.upgrade.get_compatible_versions` | `REGISTERED` | `stackName`, `stackVersion` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L500) |
+| `admin.upgrade.upgrade_item` | `REGISTERED` | `groupId`, `stageId`, `upgradeId` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L626) |
+| `admin.upgrade.upgrade_task` | `REGISTERED` | `groupId`, `stageId`, `task`, `taskId`, `upgradeId` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L680) |
+| `admin.upgrade.abort` | `REGISTERED` | `isDowngrade`, `upgradeId` | `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L773) |
+| `admin.downgrade.start` | `REGISTERED` | `id`, `isDowngrade`, `label`, `upgradeType`, `value` | `success`, `callback` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L790) |
+| `admin.upgrade.suspend` | `REGISTERED` | `isDowngrade`, `upgradeId` | `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L812) |
+| `admin.upgrade.retry` | `REGISTERED` | `upgradeId` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L861) |
+| `admin.upgrade.start` | `REGISTERED` | 表达式:`version` | `success`, `error`, `callback` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L876) |
+| `admin.upgrade.update.options` | `REGISTERED` | `skipComponentFailures`, `skipSCFailures`, `upgradeId` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L1233) |
+| `admin.upgrade.pre_upgrade_check` | `REGISTERED` | 表达式:`data` | `success`, `error`, `callback` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L1295) |
+| `admin.upgrade.get_supported_upgradeTypes` | `REGISTERED` | 表达式:`data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L1318) |
+| `admin.upgrade.pre_upgrade_check` | `REGISTERED` | 表达式:`params` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L1462) |
+| `admin.stack_version.install.repo_version` | `REGISTERED` | 表达式:`data` | `success`, `error`, `callback` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L1595) |
+| `host.host_component.delete_components` | `REGISTERED` | `data` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L1654) |
+| `common.host_components.update` | `REGISTERED` | `HostRoles`, `context`, `query` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L1679) |
+| `admin.stack_versions.edit.repo` | `REGISTERED` | `repoVersion`, `repoVersionId`, `stackName`, `stackVersion` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L1769) |
+| `admin.stack_versions.validate.repo` | `REGISTERED` | `baseUrl`, `osType`, `repo`, `repoId`, `repoName`, `stackName`, `stackVersion` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L1832) |
+| `admin.upgrade.finalizeContext` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L1962) |
+| `admin.upgrade.upgradeItem.setState` | `REGISTERED` | `groupId`, `itemId`, `status`, `upgradeId` | `callback` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L2056) |
+| `cluster.load_repositories` | `REGISTERED` | `stackName`, `stackVersion` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L2089) |
+| `admin.upgrade.revert` | `REGISTERED` | `id`, `isDowngrade`, `label`, `upgradeId`, `value` | `success`, `error`, `callback` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L2218) |
+| `admin.stack_versions.discard` | `REGISTERED` | `id`, `stackName`, `stackVersion` | `callback` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L2253) |
+| `admin.upgrade.data` | `REGISTERED` | `id` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_upgrade_history_controller.js#L83) |
+| `admin.upgrade.upgrade_item` | `REGISTERED` | `groupId`, `stageId`, `upgradeId` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_upgrade_history_controller.js#L160) |
+| `admin.upgrade.upgrade_task` | `REGISTERED` | `groupId`, `stageId`, `task`, `taskId`, `upgradeId` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_upgrade_history_controller.js#L213) |
+| `alerts.update_alert_definition` | `REGISTERED` | `data`, `id` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/alert_definitions_controller.js#L109) |
+| `alerts.create_alert_definition` | `REGISTERED` | `data` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/alerts/add_alert_definition/add_alert_definition_controller.js#L37) |
+| `admin.save_configs` | `REGISTERED` | `properties`, `siteName` | `error` | [source](../../../../ambari-web/classic/app/controllers/main/alerts/alert_definitions_actions_controller.js#L239) |
+| DYNAMIC: `ajaxData` | `DYNAMIC` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/alerts/alert_instances_controller.js#L118) |
+| `alerts.update_alert_definition` | `REGISTERED` | `data`, `id` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/alerts/definition_configs_controller.js#L653) |
+| `alerts.get_instances_history` | `REGISTERED` | `definitionName`, `timestamp` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/alerts/definition_details_controller.js#L90) |
+| `alerts.update_alert_definition` | `REGISTERED` | `data`, `id` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/alerts/definition_details_controller.js#L154) |
+| `alerts.delete_alert_definition` | `REGISTERED` | `id` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/alerts/definition_details_controller.js#L188) |
+| `alerts.update_alert_definition` | `REGISTERED` | `data`, `id` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/alerts/definition_details_controller.js#L245) |
+| `alerts.update_alert_definition` | `REGISTERED` | `data`, `id` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/alerts/definition_details_controller.js#L264) |
+| `alerts.update_alert_definition` | `REGISTERED` | `data`, `id` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/alerts/definition_details_controller.js#L308) |
+| `alerts.notifications` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/alerts/manage_alert_groups_controller.js#L209) |
+| DYNAMIC: `sendData` | `DYNAMIC` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/alerts/manage_alert_groups_controller.js#L493) |
+| DYNAMIC: `sendData` | `DYNAMIC` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/alerts/manage_alert_groups_controller.js#L530) |
+| DYNAMIC: `sendData` | `DYNAMIC` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/alerts/manage_alert_groups_controller.js#L561) |
+| `alerts.notifications` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/alerts/manage_alert_notifications_controller.js#L362) |
+| `alerts.create_alert_notification` | `REGISTERED` | `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/alerts/manage_alert_notifications_controller.js#L859) |
+| `alerts.update_alert_notification` | `REGISTERED` | `data`, `id` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/alerts/manage_alert_notifications_controller.js#L897) |
+| `alerts.delete_alert_notification` | `REGISTERED` | `id` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/alerts/manage_alert_notifications_controller.js#L938) |
+| `alerts.update_alert_notification` | `REGISTERED` | `data`, `id` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/alerts/manage_alert_notifications_controller.js#L976) |
+| `service.serviceConfigVersions.get.total` | `REGISTERED` | 表达式:`{}` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/dashboard/config_history_controller.js#L140) |
+| `service.serviceConfigVersions.get.suggestions` | `REGISTERED` | 表达式:`{ key }` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/dashboard/config_history_controller.js#L209) |
+| `config_groups.all_fields` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/host/addHost/step4_controller.js#L27) |
+| `config_groups.update_config_group` | `REGISTERED` | `configGroup`, `id` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/host/add_controller.js#L269) |
+| `common.host_components.update` | `REGISTERED` | `HostRoles`, `context`, `level`, `query` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/host/add_controller.js#L445) |
+| `common.host_components.update` | `REGISTERED` | `HostRoles`, `context`, `hostName`, `noOpsMessage`, `query` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js#L149) |
+| `common.host_components.update` | `REGISTERED` | `HostRoles`, `context`, `noOpsMessage`, `query` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js#L250) |
+| `common.hosts.delete` | `REGISTERED` | `hosts`, `query` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js#L374) |
+| `bulk_request.hosts.passive_state` | `REGISTERED` | `hostNames`, `passive_state`, `requestInfo` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js#L501) |
+| `host.host_component.add_new_components` | `REGISTERED` | `context`, `data` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js#L591) |
+| `common.host_components.update` | `REGISTERED` | `HostRoles`, `context`, `query` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js#L618) |
+| `host.host_component.delete_components` | `REGISTERED` | `componentName`, `data` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js#L717) |
+| `common.host_components.update` | `REGISTERED` | `HostRoles`, `context`, `level`, `noOpsMessage`, `query` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js#L830) |
+| `bulk_request.decommission` | `REGISTERED` | `componentName`, `context`, `noOpsMessage`, `parameters`, `serviceName` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js#L921) |
+| `host_components.hbase_regionserver.active` | `REGISTERED` | `hostNames` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js#L953) |
+| `hosts.bulk.operations` | `REGISTERED` | `operationData`, `parameters` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js#L1096) |
+| `hosts.with_searchTerm` | `REGISTERED` | `facet`, `page_size`, `searchTerm` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/host/combo_search_box.js#L27) |
+| DYNAMIC: `(Array.isArray(component)) ? 'common.host.host_components.update' : 'common.host.host_component.update'` | `DYNAMIC` | 表达式:`data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L337) |
+| `common.host_component.getNnCheckPointTime` | `REGISTERED` | `host` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L421) |
+| DYNAMIC: `(Em.isNone(componentName)) ? 'common.delete.host' : 'common.delete.host_component'` | `DYNAMIC` | `componentName`, `hostName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L606) |
+| `host.host_component.upgrade` | `UNREGISTERED` | `component`, `componentName`, `data`, `hostName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L654) |
+| `admin.get.all_configurations` | `REGISTERED` | `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L926) |
+| `admin.get.all_configurations` | `REGISTERED` | `urlParams` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L960) |
+| `admin.get.all_configurations` | `REGISTERED` | `urlParams` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L979) |
+| `admin.get.all_configurations` | `REGISTERED` | `webHCat` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L1180) |
+| `admin.get.all_configurations` | `REGISTERED` | 表达式:`{ urlParams }` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L1202) |
+| `common.service.configurations` | `REGISTERED` | `componentName`, `desired_config` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L1335) |
+| `admin.get.all_configurations` | `REGISTERED` | 表达式:`{ urlParams }` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L1423) |
+| `host.host_component.refresh_configs` | `REGISTERED` | `context`, `resource_filters` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L1661) |
+| `config.tags` | `REGISTERED` | `callback` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L1688) |
+| `reassign.load_configs` | `REGISTERED` | `urlParams` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L1718) |
+| `common.host.host_component.update` | `REGISTERED` | `HostRoles`, `component`, `componentName`, `context`, `hostName`, `serviceName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L1838) |
+| `host.host_component.decommission_slave` | `REGISTERED` | `command`, `componentName`, `context`, `hostName`, `serviceName`, `slaveType` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L1955) |
+| `host.region_servers.in_inservice` | `REGISTERED` | `hostNames` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L1992) |
+| `common.batch.request_schedules` | `REGISTERED` | `batches`, `intervalTimeSeconds`, `tolerateSize` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L2142) |
+| `common.batch.request_schedules` | `REGISTERED` | `batches`, `intervalTimeSeconds`, `tolerateSize` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L2250) |
+| `bulk_request.hosts.passive_state` | `REGISTERED` | `hostNames`, `passive_state`, `requestInfo` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L2341) |
+| `common.host.host_component.passive` | `REGISTERED` | `component`, `componentName`, `context`, `hostName`, `passive_state` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L2374) |
+| `common.delete.host` | `REGISTERED` | `hostName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L2828) |
+| `service.item.executeCustomCommand` | `REGISTERED` | `command`, `componentName`, `context`, `hosts`, `serviceName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L3046) |
+| `common.batch.request_schedules` | `REGISTERED` | `batches`, `intervalTimeSeconds`, `tolerateSize` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L3194) |
+| `admin.kerberos_security.regenerate_keytabs.host` | `REGISTERED` | `clusterName`, `hostName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L3267) |
+| `common.services.update` | `REGISTERED` | `ServiceInfo`, `context`, `query` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service.js#L163) |
+| `restart.allServices` | `REGISTERED` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/service.js#L183) |
+| `common.services.update` | `REGISTERED` | `ServiceInfo`, `context` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/service.js#L202) |
+| `common.services.update` | `REGISTERED` | `ServiceInfo`, `context` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/service.js#L253) |
+| `restart.staleConfigs` | `REGISTERED` | 未发现内联 `data` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/service.js#L348) |
+| DYNAMIC: `name` | `DYNAMIC` | 表达式:`data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/add_controller.js#L468) |
+| `config.tags` | `REGISTERED` | 未发现内联 `data` | `success`, `callback` | [source](../../../../ambari-web/classic/app/controllers/main/service/info/summary.js#L148) |
+| `reassign.load_configs` | `REGISTERED` | `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/info/summary.js#L178) |
+| `service.flume.agent.command` | `REGISTERED` | `agentName`, `context`, `host`, `state` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/service/info/summary.js#L317) |
+| `common.service.hdfs.getNnCheckPointTime` | `REGISTERED` | 表达式:`{ clusterIdValue }` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/service/item.js#L405) |
+| `common.service.host_component.update` | `REGISTERED` | `query`, `serviceName`, `state` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/main/service/item.js#L572) |
+| `common.service.update` | `REGISTERED` | 表达式:`data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/item.js#L599) |
+| `service.item.refreshQueueYarnRequest` | `REGISTERED` | `command`, `componentName`, `context`, `forceRefreshConfigTags`, `hosts`, `serviceName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/item.js#L645) |
+| `service.item.startStopLdapKnox` | `REGISTERED` | `command`, `componentName`, `context`, `host`, `serviceName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/item.js#L692) |
+| `service.item.executeCustomCommand` | `REGISTERED` | `command`, `componentName`, `context`, `hosts`, `serviceName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/item.js#L740) |
+| `common.batch.request_schedules` | `REGISTERED` | `batches`, `intervalTimeSeconds`, `tolerateSize` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/item.js#L788) |
+| `service.item.rebalanceHdfsNodes` | `REGISTERED` | `hosts`, `threshold` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/item.js#L842) |
+| `admin.kerberos_security.regenerate_keytabs.service` | `REGISTERED` | `clusterName`, `serviceName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/item.js#L889) |
+| `service.item.updateHBaseReplication` | `REGISTERED` | `hosts`, `replication_cluster_keys`, `replication_peers` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/item.js#L975) |
+| `service.item.stopHBaseReplication` | `REGISTERED` | `hosts`, `replication_peers` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/item.js#L1040) |
+| `service.item.smoke` | `REGISTERED` | 表达式:`requestData` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/item.js#L1292) |
+| `service.item.executeCustomCommand` | `REGISTERED` | `command`, `componentName`, `context`, `hosts`, `serviceName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/item.js#L1544) |
+| `service.item.executeCustomCommand` | `REGISTERED` | `command`, `componentName`, `context`, `forceRefreshConfigTags`, `hosts`, `serviceName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/item.js#L1568) |
+| `common.delete.service` | `REGISTERED` | `serviceName`, `servicesToDeleteNext` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/item.js#L2057) |
+| `hosts.config_groups` | `REGISTERED` | 表达式:`{}` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/manage_config_groups_controller.js#L307) |
+| `service.load_config_groups` | `REGISTERED` | `serviceName` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/service/manage_config_groups_controller.js#L445) |
+| `config.host_overrides` | `REGISTERED` | `params`, `typeTagToGroupMap` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/service/manage_config_groups_controller.js#L539) |
+| `hosts.info.install` | `REGISTERED` | `hostNames` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/service/manage_config_groups_controller.js#L1057) |
+| `config.tags` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step1_controller.js#L44) |
+| `reassign.load_configs` | `REGISTERED` | `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step1_controller.js#L78) |
+| `config.tags` | `REGISTERED` | 未发现内联 `data` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step3_controller.js#L292) |
+| `reassign.load_configs` | `REGISTERED` | `urlParams` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step3_controller.js#L315) |
+| `common.host.host_component.passive` | `REGISTERED` | `componentName`, `hostName`, `passive_state` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js#L254) |
+| `common.across.services.configurations` | `REGISTERED` | `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js#L293) |
+| `common.delete.host_component` | `REGISTERED` | `componentName`, `hostName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js#L442) |
+| `service.mysql.clean` | `REGISTERED` | `host` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js#L485) |
+| `service.mysql.configure` | `REGISTERED` | `host` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js#L506) |
+| `common.host.host_component.update` | `REGISTERED` | `HostRoles`, `componentName`, `context`, `hostName`, `serviceName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js#L518) |
+| `restart.hostComponents` | `REGISTERED` | `context`, `operation_level`, `resource_filters` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js#L551) |
+| `common.host.host_component.update` | `REGISTERED` | `HostRoles`, `componentName`, `context`, `hostName`, `serviceName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js#L565) |
+| `cluster.custom_action.create` | `REGISTERED` | `filteredHosts`, `requestInfo` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js#L663) |
+| `custom_action.request` | `REGISTERED` | `requestId` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js#L681) |
+| `custom_action.request` | `REGISTERED` | `requestId`, `taskId` | `success` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js#L705) |
+| `common.delete.host_component` | `REGISTERED` | `componentName`, `hostName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step6_controller.js#L133) |
+| `common.host.host_component.passive` | `REGISTERED` | `componentName`, `hostName`, `passive_state` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step6_controller.js#L160) |
+| `common.host.host_component.update` | `REGISTERED` | 表达式:`data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step6_controller.js#L201) |
+| `common.host.host_component.passive` | `REGISTERED` | `componentName`, `hostName`, `passive_state` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step7_controller.js#L65) |
+| `common.delete.host_component` | `REGISTERED` | `componentName`, `hostName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/service/reassign/step7_controller.js#L84) |
+| `views.info` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/views_controller.js#L49) |
+| `views.instances` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/main/views_controller.js#L60) |
+| DYNAMIC: `isRetry ? 'common.host_components.update' : 'common.services.update'` | `DYNAMIC` | 表达式:`data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard.js#L426) |
+| `wizard.launch_bootstrap` | `REGISTERED` | `bootStrapData`, `popup` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard.js#L514) |
+| `wizard.service_components` | `REGISTERED` | `stackUrl`, `stackVersion` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard.js#L629) |
+| `cluster.load_current_repo_stack_services` | `REGISTERED` | `clusterName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard.js#L686) |
+| `hosts.confirmed` | `REGISTERED` | 表达式:`{}` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard.js#L1281) |
+| `ambari.service` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard/step2_controller.js#L522) |
+| `wizard.step3.bootstrap` | `REGISTERED` | `bootRequestId`, `callback`, `numPolls`, `shouldUseDefaultHandler`, `timeout` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard/step3_controller.js#L484) |
+| `wizard.step3.is_hosts_registered` | `REGISTERED` | `callback`, `shouldUseDefaultHandler`, `timeout` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard/step3_controller.js#L574) |
+| `wizard.step3.is_hosts_registered` | `REGISTERED` | 未发现内联 `data` | `success` | [source](../../../../ambari-web/classic/app/controllers/wizard/step3_controller.js#L669) |
+| `ambari.service` | `REGISTERED` | `fields` | `success` | [source](../../../../ambari-web/classic/app/controllers/wizard/step3_controller.js#L706) |
+| `wizard.step3.jdk_check` | `REGISTERED` | `host_names`, `java_home`, `jdk_location` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard/step3_controller.js#L731) |
+| `wizard.step3.jdk_check.get_results` | `REGISTERED` | `requestIndex` | `success` | [source](../../../../ambari-web/classic/app/controllers/wizard/step3_controller.js#L750) |
+| `request_schedule.get.pending` | `REGISTERED` | `hostComponent` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard/step7/assign_master_controller.js#L85) |
+| `service.serviceConfigVersions.get.current.not.default` | `REGISTERED` | 未发现内联 `data` | `success` | [source](../../../../ambari-web/classic/app/controllers/wizard/step7_controller.js#L420) |
+| `config.tags` | `REGISTERED` | 未发现内联 `data` | `success` | [source](../../../../ambari-web/classic/app/controllers/wizard/step7_controller.js#L1175) |
+| `ambari.service` | `REGISTERED` | `fields` | `success` | [source](../../../../ambari-web/classic/app/controllers/wizard/step7_controller.js#L1565) |
+| `admin.kerberize.cluster_descriptor_artifact` | `REGISTERED` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/wizard/step7_controller.js#L1883) |
+| `cluster.load_repo_version` | `REGISTERED` | `repositoryVersion`, `stackName` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard/step8_controller.js#L346) |
+| `cluster.load_repositories` | `REGISTERED` | `stackName`, `stackVersion` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard/step8_controller.js#L403) |
+| `wizard.step8.existing_cluster_names` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard/step8_controller.js#L718) |
+| `common.delete.cluster` | `REGISTERED` | `isLast`, `name` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard/step8_controller.js#L753) |
+| `wizard.get_version_definitions` | `REGISTERED` | 未发现内联 `data` | `success` | [source](../../../../ambari-web/classic/app/controllers/wizard/step8_controller.js#L835) |
+| `wizard.delete_repository_versions` | `REGISTERED` | `id`, `stackName`, `stackVersion` | `success` | [source](../../../../ambari-web/classic/app/controllers/wizard/step8_controller.js#L862) |
+| DYNAMIC: `ajaxOpts` | `DYNAMIC` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/controllers/wizard/step8_controller.js#L906) |
+| DYNAMIC: `name` | `DYNAMIC` | 表达式:`data` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard/step9_controller.js#L511) |
+| `background_operations.get_by_task` | `REGISTERED` | `clusterName`, `requestId`, `taskId` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard/step9_controller.js#L989) |
+| `wizard.step9.load_log` | `REGISTERED` | `args`, `callback`, `cluster`, `numPolls`, `polling`, `requestId`, `timeout` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard/step9_controller.js#L1040) |
+| `wizard.step9.installer.get_host_status` | `REGISTERED` | `cluster` | `success`, `error` | [source](../../../../ambari-web/classic/app/controllers/wizard/step9_controller.js#L1108) |
+| `ambari.service` | `REGISTERED` | `fields` | `success` | [source](../../../../ambari-web/classic/app/controllers/wizard/step9_controller.js#L1225) |
+| `hosts.ips` | `REGISTERED` | `hostNames` | 默认处理 | [source](../../../../ambari-web/classic/app/mappers/service_metrics_mapper.js#L334) |
+| `hiveServerInteractive.getStatus` | `REGISTERED` | `hsiHost`, `port` | 默认处理 | [source](../../../../ambari-web/classic/app/mappers/service_metrics_mapper.js#L352) |
+| `service.serviceConfigVersions.get.multiple` | `REGISTERED` | `serviceConfigVersions`, `serviceName` | 默认处理 | [source](../../../../ambari-web/classic/app/mixins/common/configs/configs_comparator.js#L392) |
+| `service.serviceConfigVersions.get` | `REGISTERED` | `serviceName` | `success` | [source](../../../../ambari-web/classic/app/mixins/common/configs/configs_loader.js#L48) |
+| `service.serviceConfigVersions.get.current` | `REGISTERED` | `serviceNames` | `success` | [source](../../../../ambari-web/classic/app/mixins/common/configs/configs_loader.js#L109) |
+| `service.serviceConfigVersions.get.multiple` | `REGISTERED` | `additionalParams`, `serviceConfigVersions`, `serviceName` | `success` | [source](../../../../ambari-web/classic/app/mixins/common/configs/configs_loader.js#L176) |
+| DYNAMIC: `ajaxOptions` | `DYNAMIC` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/mixins/common/configs/configs_saver.js#L595) |
+| DYNAMIC: `ajaxOptions` | `DYNAMIC` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/mixins/common/configs/configs_saver.js#L617) |
+| DYNAMIC: `ajaxData` | `DYNAMIC` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/mixins/common/configs/configs_saver.js#L644) |
+| `components.filter_by_status` | `REGISTERED` | `clusterName`, `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/common/configs/configs_saver.js#L880) |
+| `config.recommendations` | `REGISTERED` | `dataToSend`, `stackVersionUrl` | `success`, `error`, `callback` | [source](../../../../ambari-web/classic/app/mixins/common/configs/enhanced_configs.js#L335) |
+| `wizard.loadrecommendations` | `REGISTERED` | 表达式:`recommendationData` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/common/hosts/host_component_recommendation_mixin.js#L116) |
+| `config.validations` | `REGISTERED` | 表达式:`validationData` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/common/hosts/host_component_validation_mixin.js#L95) |
+| `persist.get` | `REGISTERED` | `data`, `key` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/common/persist.js#L48) |
+| `persist.get.text` | `REGISTERED` | `key` | 默认处理 | [source](../../../../ambari-web/classic/app/mixins/common/persist.js#L67) |
+| `persist.post` | `REGISTERED` | `keyValuePair` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/common/persist.js#L128) |
+| `config.validations` | `REGISTERED` | 表达式:`validationData` | `success` | [source](../../../../ambari-web/classic/app/mixins/common/serverValidator.js#L196) |
+| `preinstalled.checks` | `REGISTERED` | `RequestInfo`, `resource_filters` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/main/host/details/actions/check_host.js#L144) |
+| `preinstalled.checks.tasks` | `REGISTERED` | `requestId` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/main/host/details/actions/check_host.js#L188) |
+| `wizard.step3.host_info` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/main/host/details/actions/check_host.js#L253) |
+| `wizard.step3.rerun_checks` | `REGISTERED` | 未发现内联 `data` | `success`, `error`, `callback` | [source](../../../../ambari-web/classic/app/mixins/main/host/details/actions/check_host.js#L443) |
+| `host.stack_versions.install` | `REGISTERED` | `hostName`, `version` | `success` | [source](../../../../ambari-web/classic/app/mixins/main/host/details/actions/install_new_version.js#L41) |
+| `host.host_component.slave_desired_admin_state` | `REGISTERED` | `componentName`, `hostName` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/main/host/details/host_components/decommissionable.js#L157) |
+| `host.host_component.decommission_status` | `REGISTERED` | `componentName`, `hostName`, `serviceName` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/main/host/details/host_components/decommissionable.js#L200) |
+| `host.host_component.add_new_component` | `REGISTERED` | `component`, `data`, `hostName` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/main/host/details/host_components/install_component.js#L29) |
+| `common.host.host_component.update` | `REGISTERED` | `HostRoles`, `component`, `componentName`, `context`, `hostName`, `serviceName`, `urlParams` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/main/host/details/host_components/install_component.js#L67) |
+| `common.create_component` | `REGISTERED` | `componentName`, `serviceName` | 默认处理 | [source](../../../../ambari-web/classic/app/mixins/main/host/details/host_components/install_component.js#L133) |
+| DYNAMIC: `config_action.get('popupProperties').primaryButton.metaData.name` | `DYNAMIC` | `command`, `componentName`, `context`, `forceRefreshConfigTags`, `hosts`, `serviceName` | `error` | [source](../../../../ambari-web/classic/app/mixins/main/service/configs/component_actions_by_configs.js#L112) |
+| `common.batch.request_schedules` | `REGISTERED` | `batches`, `displayName`, `hostName`, `intervalTimeSeconds`, `tolerateSize` | `success` | [source](../../../../ambari-web/classic/app/mixins/main/service/configs/component_actions_by_configs.js#L232) |
+| `common.batch.request_schedules` | `REGISTERED` | `batches`, `intervalTimeSeconds`, `tolerateSize` | 默认处理 | [source](../../../../ambari-web/classic/app/mixins/main/service/configs/component_actions_by_configs.js#L296) |
+| `config_groups.create` | `REGISTERED` | `data`, `modelData` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/main/service/configs/config_overridable.js#L259) |
+| DYNAMIC: `sendData` | `DYNAMIC` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/mixins/main/service/configs/config_overridable.js#L329) |
+| DYNAMIC: `sendData` | `DYNAMIC` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/mixins/main/service/configs/config_overridable.js#L439) |
+| `service.serviceConfigVersions.get.current` | `REGISTERED` | `serviceNames` | `success` | [source](../../../../ambari-web/classic/app/mixins/main/service/configs/hive_interactive_check.js#L25) |
+| `configs.config_groups.load.services` | `REGISTERED` | `dfd`, `serviceNames` | `success` | [source](../../../../ambari-web/classic/app/mixins/main/service/groups_mapping.js#L40) |
+| `configs.theme` | `REGISTERED` | `serviceName`, `stackVersionUrl` | `success` | [source](../../../../ambari-web/classic/app/mixins/main/service/themes_mapping.js#L39) |
+| `configs.theme.services` | `REGISTERED` | `serviceNames`, `stackVersionUrl` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/main/service/themes_mapping.js#L73) |
+| `admin.kerberize.cluster_descriptor` | `REGISTERED` | `queryParams` | 默认处理 | [source](../../../../ambari-web/classic/app/mixins/wizard/addSecurityConfigs.js#L421) |
+| `admin.kerberize.cluster_descriptor.stack` | `REGISTERED` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/mixins/wizard/addSecurityConfigs.js#L431) |
+| `config.recommendations` | `REGISTERED` | `dataToSend`, `stackVersionUrl` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/wizard/addSecurityConfigs.js#L758) |
+| `admin.kerberos.cluster.artifact.create` | `REGISTERED` | `artifactName`, `data` | 默认处理 | [source](../../../../ambari-web/classic/app/mixins/wizard/addSecurityConfigs.js#L859) |
+| `admin.kerberos.cluster.artifact.update` | `REGISTERED` | `artifactName`, `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/wizard/addSecurityConfigs.js#L877) |
+| DYNAMIC: `isInstaller ? 'hosts.info.install' : 'hosts.high_availability.wizard'` | `DYNAMIC` | `hostNames` | 默认处理 | [source](../../../../ambari-web/classic/app/mixins/wizard/assign_master_components.js#L604) |
+| `hosts.high_availability.wizard` | `REGISTERED` | 表达式:`{}` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/wizard/wizardHostsLoading.js#L43) |
+| DYNAMIC: `this.get('request.ajaxName')` | `DYNAMIC` | 表达式:`this.get('request.ajaxData')` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/wizard/wizardProgressPageController.js#L129) |
+| `admin.poll.kerberize.cluster.request` | `REGISTERED` | `requestId` | `success` | [source](../../../../ambari-web/classic/app/mixins/wizard/wizardProgressPageController.js#L158) |
+| `host_component.installed.on_hosts` | `REGISTERED` | `componentName`, `hostNames` | 默认处理 | [source](../../../../ambari-web/classic/app/mixins/wizard/wizardProgressPageController.js#L417) |
+| `common.services.update` | `REGISTERED` | 表达式:`data` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/wizard/wizardProgressPageController.js#L462) |
+| `common.services.update` | `REGISTERED` | 表达式:`data` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/wizard/wizardProgressPageController.js#L510) |
+| `wizard.step8.register_host_to_component` | `REGISTERED` | `cluster`, `componentName`, `data`, `hostName`, `serviceName`, `taskNum` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/wizard/wizardProgressPageController.js#L568) |
+| `common.host_components.update` | `REGISTERED` | `HostRoles`, `componentName`, `context`, `hostName`, `query`, `serviceName`, `taskNum` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/wizard/wizardProgressPageController.js#L625) |
+| `common.host.host_component.update` | `REGISTERED` | `HostRoles`, `componentName`, `hostName`, `serviceName`, `taskNum` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/wizard/wizardProgressPageController.js#L652) |
+| `background_operations.get_by_request` | `REGISTERED` | `requestId` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/wizard/wizardProgressPageController.js#L688) |
+| `common.delete.host_component` | `REGISTERED` | `componentName`, `hostName` | `success`, `error` | [source](../../../../ambari-web/classic/app/mixins/wizard/wizardProgressPageController.js#L768) |
+| `router.login.clusters` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/router.js#L274) |
+| `router.afterLogin` | `REGISTERED` | `loginName`, `usr` | `success`, `error` | [source](../../../../ambari-web/classic/app/router.js#L296) |
+| `router.login` | `REGISTERED` | `auth`, `loginName`, `usr` | `success`, `error` | [source](../../../../ambari-web/classic/app/router.js#L421) |
+| `router.afterLogin` | `REGISTERED` | `loginName`, `usr` | `success`, `error` | [source](../../../../ambari-web/classic/app/router.js#L442) |
+| `router.login.message` | `REGISTERED` | 表达式:`requestData` | `success`, `error` | [source](../../../../ambari-web/classic/app/router.js#L463) |
+| `router.login.clusters` | `REGISTERED` | 表达式:`requestData` | `success` | [source](../../../../ambari-web/classic/app/router.js#L571) |
+| `ambari.service.load_server_version` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/router.js#L631) |
+| `router.logoff` | `REGISTERED` | 未发现内联 `data` | `success`, `error` | [source](../../../../ambari-web/classic/app/router.js#L737) |
+| `ambari.service.load_server_version` | `REGISTERED` | 未发现内联 `data` | `success` | [source](../../../../ambari-web/classic/app/router.js#L976) |
+| DYNAMIC: `queue.shift()` | `DYNAMIC` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax_queue.js#L151) |
+| `host.host_components.filtered` | `REGISTERED` | `callback`, `fields`, `parameters` | `success` | [source](../../../../ambari-web/classic/app/utils/batch_scheduled_requests.js#L187) |
+| `restart.hostComponents` | `REGISTERED` | `context`, `operation_level`, `query`, `resource_filters` | `success`, `error` | [source](../../../../ambari-web/classic/app/utils/batch_scheduled_requests.js#L255) |
+| `common.service.passive` | `REGISTERED` | `passive_state`, `requestInfo`, `serviceName` | `success` | [source](../../../../ambari-web/classic/app/utils/batch_scheduled_requests.js#L299) |
+| `common.batch.request_schedules` | `REGISTERED` | `batches`, `intervalTimeSeconds`, `tolerateSize` | `success`, `error` | [source](../../../../ambari-web/classic/app/utils/batch_scheduled_requests.js#L332) |
+| `common.batch.request_schedules` | `REGISTERED` | `batches`, `intervalTimeSeconds`, `tolerateSize` | `success` | [source](../../../../ambari-web/classic/app/utils/batch_scheduled_requests.js#L631) |
+| `request_schedule.get` | `REGISTERED` | `request_schedule_id` | `success`, `error` | [source](../../../../ambari-web/classic/app/utils/batch_scheduled_requests.js#L713) |
+| `common.delete.request_schedule` | `REGISTERED` | `request_schedule_id` | `success`, `error` | [source](../../../../ambari-web/classic/app/utils/batch_scheduled_requests.js#L741) |
+| `config.on_site` | `REGISTERED` | `params` | 默认处理 | [source](../../../../ambari-web/classic/app/utils/config.js#L818) |
+| `configs.stack_configs.load.cluster_configs` | `REGISTERED` | `stackVersionUrl` | `success` | [source](../../../../ambari-web/classic/app/utils/config.js#L1044) |
+| DYNAMIC: `name` | `DYNAMIC` | `serviceList`, `stackVersionUrl` | `success` | [source](../../../../ambari-web/classic/app/utils/config.js#L1063) |
+| `credentials.create` | `REGISTERED` | `alias`, `clusterName`, `resource` | `error` | [source](../../../../ambari-web/classic/app/utils/credentials.js#L68) |
+| `credentials.get` | `REGISTERED` | `alias`, `callback`, `clusterName` | `success`, `error` | [source](../../../../ambari-web/classic/app/utils/credentials.js#L126) |
+| `credentials.update` | `REGISTERED` | `alias`, `clusterName`, `resource` | 默认处理 | [source](../../../../ambari-web/classic/app/utils/credentials.js#L157) |
+| `credentials.list` | `REGISTERED` | `callback`, `clusterName` | `success` | [source](../../../../ambari-web/classic/app/utils/credentials.js#L176) |
+| `credentials.delete` | `REGISTERED` | `alias`, `clusterName` | 默认处理 | [source](../../../../ambari-web/classic/app/utils/credentials.js#L194) |
+| `credentials.store.info` | `REGISTERED` | `callback`, `clusterName` | `success` | [source](../../../../ambari-web/classic/app/utils/credentials.js#L212) |
+| `background_operations.abort_request` | `REGISTERED` | `requestId`, `requestName`, `serviceInfo` | `success`, `error` | [source](../../../../ambari-web/classic/app/utils/host_progress_popup.js#L254) |
+| `bulk_request.hosts.update_rack_id` | `REGISTERED` | `hostNames`, `hostNamesArray`, `rackId`, `requestInfo` | `success`, `error` | [source](../../../../ambari-web/classic/app/utils/hosts.js#L247) |
+| `background_operations.get_by_task` | `REGISTERED` | `requestId`, `taskId` | `success` | [source](../../../../ambari-web/classic/app/utils/polling.js#L117) |
+| `background_operations.get_by_request` | `REGISTERED` | `callback`, `requestId` | `success`, `error` | [source](../../../../ambari-web/classic/app/utils/polling.js#L148) |
+| `service.serviceConfigVersion.revert` | `REGISTERED` | `data` | `success` | [source](../../../../ambari-web/classic/app/views/common/configs/config_versions_control_view.js#L200) |
+| DYNAMIC: `(isServiceInstalled) ? 'cluster.custom_action.create' : 'custom_action.create'` | `DYNAMIC` | `filteredHosts`, `requestInfo` | `success`, `error` | [source](../../../../ambari-web/classic/app/views/common/configs/widgets/test_db_connection_widget_view.js#L226) |
+| `custom_action.request` | `REGISTERED` | `requestId` | `success` | [source](../../../../ambari-web/classic/app/views/common/configs/widgets/test_db_connection_widget_view.js#L246) |
+| `custom_action.request` | `REGISTERED` | `requestId`, `taskId` | `success` | [source](../../../../ambari-web/classic/app/views/common/configs/widgets/test_db_connection_widget_view.js#L267) |
+| `admin.kerberos_security.test_connection` | `REGISTERED` | `kdcHostname` | `success`, `error` | [source](../../../../ambari-web/classic/app/views/common/controls_view.js#L1332) |
+| DYNAMIC: `(isServiceInstalled) ? 'cluster.custom_action.create' : 'custom_action.create'` | `DYNAMIC` | `filteredHosts`, `requestInfo` | `success`, `error` | [source](../../../../ambari-web/classic/app/views/common/controls_view.js#L1368) |
+| `custom_action.request` | `REGISTERED` | `requestId` | `success` | [source](../../../../ambari-web/classic/app/views/common/controls_view.js#L1388) |
+| `custom_action.request` | `REGISTERED` | `requestId`, `taskId` | `success` | [source](../../../../ambari-web/classic/app/views/common/controls_view.js#L1409) |
+| `cancel.background.operation` | `REGISTERED` | `requestId` | 默认处理 | [source](../../../../ambari-web/classic/app/views/common/host_progress_popup_body_view.js#L1017) |
+| `logtail.get` | `REGISTERED` | `hostName`, `logComponentName`, `pageSize`, `startIndex` | `error` | [source](../../../../ambari-web/classic/app/views/common/log_tail_view.js#L133) |
+| `configs.quicklinksconfig` | `REGISTERED` | `serviceName`, `stackVersionUrl` | `success` | [source](../../../../ambari-web/classic/app/views/common/quick_view_link_view.js#L132) |
+| `hosts.for_quick_links` | `REGISTERED` | `clusterName`, `hosts`, `urlParams` | `success` | [source](../../../../ambari-web/classic/app/views/common/quick_view_link_view.js#L212) |
+| `hosts.all` | `REGISTERED` | 表达式:`{}` | `success`, `error` | [source](../../../../ambari-web/classic/app/views/main/admin/highAvailability/nameNode/rollbackHA/step1_view.js#L34) |
+| `bulk_request.hosts.passive_state` | `REGISTERED` | `hostNames`, `passive_state`, `requestInfo` | 默认处理 | [source](../../../../ambari-web/classic/app/views/main/admin/stack_upgrade/custom_cluster_checks/custom_cluster_checks_maintenance_view.js#L29) |
+| `common.services.update` | `REGISTERED` | 表达式:`data` | `success` | [source](../../../../ambari-web/classic/app/views/main/admin/stack_upgrade/custom_cluster_checks/custom_cluster_checks_view.js#L49) |
+| `wizard.advanced_repositories.valid_url` | `REGISTERED` | `data`, `osType`, `repoId`, `stackName`, `stackVersion` | `success`, `error` | [source](../../../../ambari-web/classic/app/views/main/admin/stack_upgrade/services_view.js#L240) |
+| `admin.upgrade.service_checks` | `REGISTERED` | `upgradeId` | `success` | [source](../../../../ambari-web/classic/app/views/main/admin/stack_upgrade/upgrade_wizard_view.js#L310) |
+| `ambari.service.load_server_version` | `REGISTERED` | 未发现内联 `data` | 默认处理 | [source](../../../../ambari-web/classic/app/views/main/admin/stack_upgrade/versions_view.js#L220) |
+| `hosts.confirmed.minimal` | `REGISTERED` | 表达式:`{}` | `success`, `error` | [source](../../../../ambari-web/classic/app/views/main/host/add_view.js#L40) |
+| `host.host_component.decommission_status_datanode` | `REGISTERED` | `componentName`, `hostNames` | `success`, `error` | [source](../../../../ambari-web/classic/app/views/main/host/details/host_component_views/datanode_view.js#L34) |
+| `host.host_component.decommission_status_regionserver` | `REGISTERED` | 表达式:`{ hostName, desiredAdminState }` | `success`, `error` | [source](../../../../ambari-web/classic/app/views/main/host/details/host_component_views/regionserver_view.js#L31) |
+| `hosts.high_availability.wizard` | `REGISTERED` | 表达式:`{}` | `success`, `error` | [source](../../../../ambari-web/classic/app/views/main/service/reassign_view.js#L37) |
+
+## 动态调用解析
+
+动态解析:27/27 个调用点已绑定契约,45 个唯一候选请求名,0 个候选未命中纳入注册表。
+
+| 位置/表达式 | 解析状态 | 分派种类 | 候选请求名 | 分派条件 | 运行时边界 |
+| --- | --- | --- | --- | --- | --- |
+| [source](../../../../ambari-web/classic/app/controllers/global/background_operations_controller.js#L178)<br>`queryParams.name` | `RESOLVED_CLOSED` | `HELPER_RETURN_PROPERTY` | `background_operations.get_most_recent`<br>`background_operations.get_by_task`<br>`background_operations.get_by_request` | getQueryParams() defaults to get_most_recent; TASK_DETAILS selects get_by_task; TASKS_LIST or HOSTS_LIST selects get_by_request. | The current production set is closed. Unknown levelInfo.name [...]
+| [source](../../../../ambari-web/classic/app/controllers/installer.js#L590)<br>`name` | `RESOLVED_CLOSED` | `LOCAL_TERNARY` | `wizard.step1.post_version_definition_file.xml`<br>`wizard.step1.post_version_definition_file.url` | A truthy isXMLdata selects the XML dry-run request; otherwise the URL/JSON dry-run request is used. | The current production set is closed.<br>证据:`ambari-web/classic/app/controllers/installer.js:586-600`<br>`ambari-web/classic/app/utils/ajax/ajax.js:2025,2039` |
+| [source](../../../../ambari-web/classic/app/controllers/installer.js#L656)<br>`name` | `RESOLVED_CLOSED` | `LOCAL_TERNARY` | `wizard.step8.post_version_definition_file.xml`<br>`wizard.step8.post_version_definition_file` | isXMLdata == true selects the XML request; otherwise the JSON request is used. | The current production set is closed. The source intentionally uses loose equality.<br>证据:`ambari-web/classic/app/controllers/installer.js:653-666`<br>`ambari-web/classic/app/utils/ajax/a [...]
+| [source](../../../../ambari-web/classic/app/controllers/main/admin/highAvailability/progress_popup_controller.js#L122)<br>`name` | `RESOLVED_CLOSED` | `LOCAL_BRANCH` | `background_operations.get_by_request`<br>`common.request.polling` | A null or undefined stageId selects get_by_request; every other value selects common.request.polling. Numeric zero is normalized to string "0". | The current production set is closed. One request is sent for every requestId with the same selected name.< [...]
+| [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L461)<br>`(onlyState) ? 'admin.upgrade.state' : 'admin.upgrade.data'` | `RESOLVED_CLOSED` | `INLINE_TERNARY` | `admin.upgrade.state`<br>`admin.upgrade.data` | A truthy onlyState loads only Upgrade fields; otherwise the full upgrade group and item data is loaded. | The current production set is closed.<br>证据:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:452 [...]
+| [source](../../../../ambari-web/classic/app/controllers/main/alerts/alert_instances_controller.js#L118)<br>`ajaxData` | `RESOLVED_CLOSED` | `OPTIONS_OBJECT_SWITCH` | `alerts.instances.by_host`<br>`alerts.instances.by_definition`<br>`alerts.instances` | sourceType HOST selects by_host; ALERT_DEFINITION selects by_definition; all other values select all alert instances. | The current production set is closed. Scheduled refreshes reuse the previously selected sourceType and sourceName.<br [...]
+| [source](../../../../ambari-web/classic/app/controllers/main/alerts/manage_alert_groups_controller.js#L493)<br>`sendData` | `RESOLVED_CLOSED` | `STATIC_OPTIONS_OBJECT` | `alert_groups.create` | postNewAlertGroup constructs an options object with a fixed request name. | This is semantically static; it is classified as dynamic only because the complete options object is passed by variable.<br>证据:`ambari-web/classic/app/controllers/main/alerts/manage_alert_groups_controller.js:464-493`<br [...]
+| [source](../../../../ambari-web/classic/app/controllers/main/alerts/manage_alert_groups_controller.js#L530)<br>`sendData` | `RESOLVED_CLOSED` | `STATIC_OPTIONS_OBJECT` | `alert_groups.update` | updateAlertGroup constructs an options object with a fixed request name. | This is semantically static; it is classified as dynamic only because the complete options object is passed by variable.<br>证据:`ambari-web/classic/app/controllers/main/alerts/manage_alert_groups_controller.js:506-530`<br> [...]
+| [source](../../../../ambari-web/classic/app/controllers/main/alerts/manage_alert_groups_controller.js#L561)<br>`sendData` | `RESOLVED_CLOSED` | `STATIC_OPTIONS_OBJECT` | `alert_groups.delete` | removeAlertGroup constructs an options object with a fixed request name. | This is semantically static; it is classified as dynamic only because the complete options object is passed by variable.<br>证据:`ambari-web/classic/app/controllers/main/alerts/manage_alert_groups_controller.js:540-561`<br> [...]
+| [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L337)<br>`(Array.isArray(component)) ? 'common.host.host_components.update' : 'common.host.host_component.update'` | `RESOLVED_CLOSED` | `INLINE_TERNARY` | `common.host.host_components.update`<br>`common.host.host_component.update` | An array selects the multi-component update on one host; a non-array component selects the single host-component update. | The current production set is closed. The non-array bran [...]
+| [source](../../../../ambari-web/classic/app/controllers/main/host/details.js#L606)<br>`(Em.isNone(componentName)) ? 'common.delete.host' : 'common.delete.host_component'` | `RESOLVED_CLOSED` | `INLINE_TERNARY` | `common.delete.host`<br>`common.delete.host_component` | A null or undefined componentName deletes the host; every other value deletes a host component. | The current production set is closed. An empty string is not Em.isNone and therefore selects the host-component endpoint.<b [...]
+| [source](../../../../ambari-web/classic/app/controllers/main/service/add_controller.js#L468)<br>`name` | `RESOLVED_OPEN_BOUNDARY` | `PARAMETER_WRAPPER` | `common.services.update` | The only production caller invokes installServicesRequest with common.services.update. | The wrapper can accept any future request name. A test injects wizard.step3.host_info, but that is not a production call path.<br>证据:`ambari-web/classic/app/controllers/main/service/add_controller.js:459-474`<br>`ambari- [...]
+| [source](../../../../ambari-web/classic/app/controllers/wizard.js#L426)<br>`isRetry ? 'common.host_components.update' : 'common.services.update'` | `RESOLVED_CLOSED` | `INLINE_TERNARY` | `common.host_components.update`<br>`common.services.update` | A retry installs unfinished host components; an initial run installs services in INIT state. | The current production set is closed.<br>证据:`ambari-web/classic/app/controllers/wizard.js:401-432`<br>`ambari-web/classic/app/utils/ajax/ajax.js:4 [...]
+| [source](../../../../ambari-web/classic/app/controllers/wizard/step8_controller.js#L906)<br>`ajaxOpts` | `RESOLVED_CLOSED` | `OPTIONS_OBJECT_TERNARY` | `admin.kerberos.cluster.artifact.update`<br>`admin.kerberos.cluster.artifact.create` | descriptorExists === true selects update; otherwise create. A truthy instant sends here, while a falsy instant enqueues the same options object. | The current production set is closed. The same pair also appears in the upstream set for queue.shift().< [...]
+| [source](../../../../ambari-web/classic/app/controllers/wizard/step9_controller.js#L511)<br>`name` | `RESOLVED_CLOSED` | `LOCAL_SWITCH` | `common.host_components.update`<br>`common.services.update` | addHostController selects host_components; addServiceController and the default branch select services. | The current production set is closed. Unknown controllerName values retain the common.services.update default.<br>证据:`ambari-web/classic/app/controllers/wizard/step9_controller.js:466- [...]
+| [source](../../../../ambari-web/classic/app/mixins/common/configs/configs_saver.js#L595)<br>`ajaxOptions` | `RESOLVED_CLOSED` | `STATIC_OPTIONS_OBJECT` | `wizard.step8.apply_configuration_groups` | createConfigGroup constructs an options object with a fixed request name. | This is semantically static. An optional success callback does not change the request name.<br>证据:`ambari-web/classic/app/mixins/common/configs/configs_saver.js:584-595`<br>`ambari-web/classic/app/utils/ajax/ajax.js:2185` |
+| [source](../../../../ambari-web/classic/app/mixins/common/configs/configs_saver.js#L617)<br>`ajaxOptions` | `RESOLVED_CLOSED` | `STATIC_OPTIONS_OBJECT` | `config_groups.update_config_group` | updateConfigGroup constructs an options object with a fixed request name. | This is semantically static. An optional success callback does not change the request name.<br>证据:`ambari-web/classic/app/mixins/common/configs/configs_saver.js:605-617`<br>`ambari-web/classic/app/utils/ajax/ajax.js:915` |
+| [source](../../../../ambari-web/classic/app/mixins/common/configs/configs_saver.js#L644)<br>`ajaxData` | `RESOLVED_CLOSED` | `STATIC_OPTIONS_OBJECT` | `common.across.services.configurations` | putChangedConfigurations constructs an options object with a fixed request name. | This is semantically static. Optional success and always callbacks do not change the request name.<br>证据:`ambari-web/classic/app/mixins/common/configs/configs_saver.js:629-644`<br>`ambari-web/classic/app/utils/ajax [...]
+| [source](../../../../ambari-web/classic/app/mixins/main/service/configs/component_actions_by_configs.js#L112)<br>`config_action.get('popupProperties').primaryButton.metaData.name` | `RESOLVED_OPEN_BOUNDARY` | `MODEL_METADATA_LOOKUP` | `service.item.refreshQueueYarnRequest` | The code selects ConfigAction records with actionType showPopup. The only current showPopup fixture supplies service.item.refreshQueueYarnRequest. | The current data set is closed, but ConfigAction is an extension  [...]
+| [source](../../../../ambari-web/classic/app/mixins/main/service/configs/config_overridable.js#L329)<br>`sendData` | `RESOLVED_CLOSED` | `STATIC_OPTIONS_OBJECT` | `config_groups.update` | updateConfigurationGroup constructs an options object with a fixed request name. | This is semantically static; callbacks stored on the options object do not change the request name.<br>证据:`ambari-web/classic/app/mixins/main/service/configs/config_overridable.js:308-329`<br>`ambari-web/classic/app/util [...]
+| [source](../../../../ambari-web/classic/app/mixins/main/service/configs/config_overridable.js#L439)<br>`sendData` | `RESOLVED_CLOSED` | `STATIC_OPTIONS_OBJECT` | `common.delete.config_group` | deleteConfigurationGroup constructs an options object with a fixed request name. | This is semantically static; callbacks stored on the options object do not change the request name.<br>证据:`ambari-web/classic/app/mixins/main/service/configs/config_overridable.js:416-439`<br>`ambari-web/classic/ap [...]
+| [source](../../../../ambari-web/classic/app/mixins/wizard/assign_master_components.js#L604)<br>`isInstaller ? 'hosts.info.install' : 'hosts.high_availability.wizard'` | `RESOLVED_CLOSED` | `LOCAL_TERNARY` | `hosts.info.install`<br>`hosts.high_availability.wizard` | installerController in wizardController.name or content.controllerName selects install host information; all other wizards select existing-cluster HA host information. | The current production set is closed.<br>证据:`ambari-we [...]
+| [source](../../../../ambari-web/classic/app/mixins/wizard/wizardProgressPageController.js#L129)<br>`this.get('request.ajaxName')` | `RESOLVED_OPEN_BOUNDARY` | `MIXIN_REQUEST_PROPERTY` | `admin.kerberize.cluster`<br>`admin.kerberize.cluster.force` | submitRequest is reached only on an isSingleRequestPage path. Kerberos Step 7 is the only current production controller setting that flag; initial execution selects the normal request and retry selects force. | The base mixin remains an exte [...]
+| [source](../../../../ambari-web/classic/app/utils/ajax/ajax_queue.js#L151)<br>`queue.shift()` | `RESOLVED_OPEN_BOUNDARY` | `FIFO_OPTIONS_QUEUE` | `wizard.step8.create_cluster`<br>`wizard.step8.create_selected_services`<br>`wizard.step8.create_components`<br>`wizard.step8.register_host_to_cluster`<br>`wizard.step8.register_host_to_component`<br>`common.across.services.configurations`<br>`wizard.step8.apply_configuration_groups`<br>`config_groups.update_config_group`<br>`alerts.create_al [...]
+| [source](../../../../ambari-web/classic/app/utils/config.js#L1063)<br>`name` | `RESOLVED_CLOSED` | `LOCAL_TERNARY` | `configs.stack_configs.load.services`<br>`configs.stack_configs.load.all` | serviceNames is normalized to an array; a nonempty array selects specified services and an empty array selects all services. | The current production set is closed.<br>证据:`ambari-web/classic/app/utils/config.js:1060-1071`<br>`ambari-web/classic/app/utils/ajax/ajax.js:814,819` |
+| [source](../../../../ambari-web/classic/app/views/common/configs/widgets/test_db_connection_widget_view.js#L226)<br>`(isServiceInstalled) ? 'cluster.custom_action.create' : 'custom_action.create'` | `RESOLVED_CLOSED` | `INLINE_TERNARY` | `cluster.custom_action.create`<br>`custom_action.create` | A loaded service model selects the cluster-scoped custom action; otherwise the top-level pre-cluster custom action is used. | The current production set is closed. The runtime boundary is the s [...]
+| [source](../../../../ambari-web/classic/app/views/common/controls_view.js#L1368)<br>`(isServiceInstalled) ? 'cluster.custom_action.create' : 'custom_action.create'` | `RESOLVED_CLOSED` | `INLINE_TERNARY` | `cluster.custom_action.create`<br>`custom_action.create` | A loaded parent service model selects the cluster-scoped custom action; otherwise the top-level pre-cluster custom action is used. | The current production set is closed. This is a separate legacy database-control implementat [...]
+
+`RESOLVED_CLOSED` 表示当前生产分支可穷举;`RESOLVED_OPEN_BOUNDARY` 表示当前候选已穷举,但通用 wrapper、model metadata、mixin property 或 FIFO queue 仍可被未来代码扩展。
+
+`UNREGISTERED` 是经典代码实际调用但 `app/utils/ajax/ajax.js` 没有同名定义的遗留行为。它不是有效后端契约;迁移时应先确认预期 endpoint,再决定修复或保留兼容行为。
diff --git a/docs/frontend-refactor/ember-baseline/generated/ajax-endpoints.md b/docs/frontend-refactor/ember-baseline/generated/ajax-endpoints.md
new file mode 100644
index 0000000000..fec3c5bdcb
--- /dev/null
+++ b/docs/frontend-refactor/ember-baseline/generated/ajax-endpoints.md
@@ -0,0 +1,632 @@
+# Ember 非 Metrics 命名 AJAX 接口目录
+
+> 由 `tools/extract-ember-baseline.mjs` 生成,请勿手工编辑。URL 默认追加 `/api/v1`;Method 中的 `DYNAMIC` 只表示 HTTP method 由运行时表达式决定,动态 URL 单独标为 `DYNAMIC_URL`。调用者计数为经典前端 `app/` 目录中的字符串引用数。
+
+- 纳入定义:288
+- 排除 Metrics 定义:95
+- 有经典前端调用证据:235
+- 未发现经典前端调用证据:53
+
+| 请求名 | Method | URL(不含默认 prefix) | format 输入键 | Prefix | 模块 | 调用者 | 定义 |
+| --- | --- | --- | --- | --- | --- | ---: | --- |
+| `common.cluster.update` | `PUT` | `/clusters/{clusterName}` | `data` | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L34) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos/step2_controller.js:313` |  |  |  |  |  |
+| `common.services.update` | `PUT` | `/clusters/{clusterName}/services?{urlParams}` | `ServiceInfo`, `clusterName`, `context` | `/api/v1 (default)` | 安装向导、服务与配置、Stack 与升级、安全、高可用与联邦、Views | 15 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L46) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js:223`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js:348`<br>`ambari-web/classic/app/controllers/main/admin/kerberos/step3_controller.js:43`<br>`ambari-web/classic/app/controllers/main/admin/kerberos/step6_controller.js:26`<br>`ambari-web/classic/app/controllers/main/admin/kerberos/step8_controller.js:27`<br>`ambari-web/classic/ap [...]
+| `common.service.update` | `PUT` | `/clusters/{clusterName}/services/{serviceName}` | `ServiceInfo`, `clusterName`, `context`, `serviceName` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L68) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/item.js:600` |  |  |  |  |  |
+| `common.service_component.info` | `GET` | `/clusters/{clusterName}/services/{serviceName}/components/{componentName}?{urlParams}` | 无 `format()` | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L91) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos/step3_controller.js:77` |  |  |  |  |  |
+| `common.service.hdfs.getNnCheckPointTime` | `GET` | `/clusters/{clusterName}/services/HDFS/components/NAMENODE?fields=host_components/metrics/dfs/FSNamesystem/HAState,host_components/metrics/dfs/FSNamesystem/LastCheckpointTime,host_components/metrics/dfs/namenode/ClusterId` | 无 `format()` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L96) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/item.js:406` |  |  |  |  |  |
+| `common.host_component.getNnCheckPointTime` | `GET` | `/clusters/{clusterName}/hosts/{host}/host_components/NAMENODE?fields=metrics/dfs/FSNamesystem/HAState,metrics/dfs/FSNamesystem/LastCheckpointTime` | 无 `format()` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L101) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/details.js:422` |  |  |  |  |  |
+| `common.host_component.update` | `PUT` | `/clusters/{clusterName}/host_components` | `HostRoles`, `context`, `query` | `/api/v1 (default)` | 主机、服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L106) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/add_controller.js:506` |  |  |  |  |  |
+| `common.host.host_components.create` | `POST` | `/clusters/{clusterName}/hosts` | `host_components`, `query` | `/api/v1 (default)` | 主机 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L125) |
+| `common.host.host_components.update` | `PUT` | `/clusters/{clusterName}/hosts/{hostName}/host_components?{urlParams}` | `HostRoles`, `clusterName`, `context`, `hostName`, `query` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L143) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/details.js:338` |  |  |  |  |  |
+| `common.host.host_component.update` | `PUT` | `/clusters/{clusterName}/hosts/{hostName}/host_components/{componentName}?{urlParams}` | `HostRoles`, `clusterName`, `context`, `hostName`, `serviceName` | `/api/v1 (default)` | 主机、服务与配置 | 7 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L167) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/details.js:338`<br>`ambari-web/classic/app/controllers/main/host/details.js:1839`<br>`ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js:519`<br>`ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js:566`<br>`ambari-web/classic/app/controllers/main/service/reassign/step6_controller.js:202`<br>`ambari-web/classic/app/mixins/main/host/details/host_components/install_component.js:68`< [...]
+| `common.hosts.all` | `GET` | `/clusters/{clusterName}/host_components?{urlParams}&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L191) |
+| `common.service.configurations` | `PUT` | `/clusters/{clusterName}` | `desired_config` | `/api/v1 (default)` | 认证与应用外壳、主机、安全、高可用与联邦 | 9 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L196) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/federation/routerBasedFederation/step3_controller.js:97`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/activateStandby/step3_controller.js:94`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/addStandby/step4_controller.js:73`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/removeStandby/step3_controller.js:88`<br>`ambari-web/classic/app/controllers/main/ad [...]
+| `common.service.multiConfigurations` | `PUT` | `/clusters/{clusterName}` | `configs` | `/api/v1 (default)` | 认证与应用外壳、安全、高可用与联邦 | 4 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L211) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/federation/routerBasedFederation/step4_controller.js:55`<br>`ambari-web/classic/app/controllers/main/admin/federation/step4_controller.js:77`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step9_controller.js:228`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/rangerAdmin/step4_controller.js:88` |  |  |  |  |  |
+| `common.across.services.configurations` | `PUT` | `/clusters/{clusterName}` | `data` | `/api/v1 (default)` | 安装向导、服务与配置、安全、高可用与联邦 | 4 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L222) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos/step2_controller.js:229`<br>`ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js:294`<br>`ambari-web/classic/app/controllers/wizard/step8_controller.js:1549`<br>`ambari-web/classic/app/mixins/common/configs/configs_saver.js:631` |  |  |  |  |  |
+| `common.request.polling` | `GET` | `/clusters/{clusterName}/requests/{requestId}?fields=tasks/Tasks/request_id,tasks/Tasks/command,tasks/Tasks/command_detail,tasks/Tasks/ops_display_name,tasks/Tasks/start_time,tasks/Tasks/end_time,tasks/Tasks/exit_code,tasks/Tasks/host_name,tasks/Tasks/id,tasks/Tasks/role,tasks/Tasks/status,tasks/Tasks/structured_out,Requests/*&tasks/Tasks/stage_id={stageId}` | 无 `format()` | `/api/v1 (default)` | 安全、高可用与联邦、后台操作与通用能力 | 1 | [source](../../../../ambari-w [...]
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/highAvailability/progress_popup_controller.js:113` |  |  |  |  |  |
+| `ambari.service.load_server_version` | `GET` | `/services/AMBARI?fields=components/RootServiceComponents/component_version&components/RootServiceComponents/component_name=AMBARI_SERVER&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 认证与应用外壳、Stack 与升级、Views | 3 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L239) |
+|  |  | 调用位置:`ambari-web/classic/app/router.js:632`<br>`ambari-web/classic/app/router.js:977`<br>`ambari-web/classic/app/views/main/admin/stack_upgrade/versions_view.js:221` |  |  |  |  |  |
+| `service.flume.agent.command` | `PUT` | `/clusters/{clusterName}/hosts/{host}/host_components/FLUME_HANDLER` | `agentName`, `clusterName`, `context`, `host`, `state` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L245) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/info/summary.js:318` |  |  |  |  |  |
+| `common.host_components.update` | `PUT` | `/clusters/{clusterName}/host_components?{urlParams}` | `HostRoles`, `clusterName`, `context`, `level`, `query` | `/api/v1 (default)` | 安装向导、主机、Stack 与升级 | 9 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L272) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:1680`<br>`ambari-web/classic/app/controllers/main/host/add_controller.js:446`<br>`ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js:150`<br>`ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js:251`<br>`ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js:619`<br>`ambari-web/classic/app/controllers/main/host/bulk_operations_contr [...]
+| `common.hosts.delete` | `DELETE` | `/clusters/{clusterName}/hosts{urlParams}` | `query` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L295) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js:375` |  |  |  |  |  |
+| `common.service.passive` | `PUT` | `/clusters/{clusterName}/services/{serviceName}` | `passive_state`, `requestInfo` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L309) |
+|  |  | 调用位置:`ambari-web/classic/app/utils/batch_scheduled_requests.js:300` |  |  |  |  |  |
+| `common.service.host_component.update` | `PUT` | `/clusters/{clusterName}/host_components` | `clusterName`, `context`, `query`, `serviceName`, `state` | `/api/v1 (default)` | 主机、服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L329) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/item.js:573` |  |  |  |  |  |
+| `common.host.host_component.passive` | `PUT` | `/clusters/{clusterName}/hosts/{hostName}/host_components/{componentName}` | `context`, `passive_state` | `/api/v1 (default)` | 主机、服务与配置、安全、高可用与联邦 | 6 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L355) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js:479`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step5_controller.js:52`<br>`ambari-web/classic/app/controllers/main/host/details.js:2375`<br>`ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js:255`<br>`ambari-web/classic/app/controllers/main/service/reassign/step6_controller.js:161`<br>`ambari-web/classic/app/controllers/m [...]
+| `common.host.with_host_component` | `GET` | `/clusters/{clusterName}/hosts?host_components/HostRoles/component_name={componentName}&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L375) |
+| `common.batch.request_schedules` | `POST` | `/clusters/{clusterName}/request_schedules` | `batches`, `intervalTimeSeconds`, `tolerateSize` | `/api/v1 (default)` | 主机、服务与配置、后台操作与通用能力 | 8 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L380) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/details.js:2143`<br>`ambari-web/classic/app/controllers/main/host/details.js:2251`<br>`ambari-web/classic/app/controllers/main/host/details.js:3195`<br>`ambari-web/classic/app/controllers/main/service/item.js:789`<br>`ambari-web/classic/app/mixins/main/service/configs/component_actions_by_configs.js:233`<br>`ambari-web/classic/app/mixins/main/service/configs/component_actions_by_configs.js:297`<br>`ambari-web/classic/app/utils/ba [...]
+| `common.delete.host` | `DELETE` | `/clusters/{clusterName}/hosts/{hostName}` | 无 `format()` | `/api/v1 (default)` | 主机 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L402) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/details.js:607`<br>`ambari-web/classic/app/controllers/main/host/details.js:2829` |  |  |  |  |  |
+| `common.delete.host_component` | `DELETE` | `/clusters/{clusterName}/hosts/{hostName}/host_components/{componentName}` | 无 `format()` | `/api/v1 (default)` | 主机、服务与配置、安全、高可用与联邦 | 9 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L406) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js:461`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js:550`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step9_controller.js:311`<br>`ambari-web/classic/app/controllers/main/admin/kerberos/step6_controller.js:60`<br>`ambari-web/classic/app/controllers/main/host/details.js:607`<br>`ambari-web/classic/ap [...]
+| `common.delete.user` | `DELETE` | `/users/{user}` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L410) |
+| `common.delete.config_group` | `DELETE` | `/clusters/{clusterName}/config_groups/{id}` | 无 `format()` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L414) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/main/service/configs/config_overridable.js:419` |  |  |  |  |  |
+| `common.delete.cluster` | `DELETE` | `/clusters/{name}` | 无 `format()` | `/api/v1 (default)` | 安装向导 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L418) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step8_controller.js:754` |  |  |  |  |  |
+| `common.delete.service` | `DELETE` | `/clusters/{clusterName}/services/{serviceName}` | 无 `format()` | `/api/v1 (default)` | 服务与配置、安全、高可用与联邦 | 3 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L422) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos/disable_controller.js:66`<br>`ambari-web/classic/app/controllers/main/admin/kerberos/wizard_controller.js:230`<br>`ambari-web/classic/app/controllers/main/service/item.js:2058` |  |  |  |  |  |
+| `common.delete.request_schedule` | `DELETE` | `/clusters/{clusterName}/request_schedules/{request_schedule_id}` | 无 `format()` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L427) |
+|  |  | 调用位置:`ambari-web/classic/app/utils/batch_scheduled_requests.js:742` |  |  |  |  |  |
+| `common.get.request.status` | `GET` | `/clusters/{clusterName}/requests/{requestId}?fields=Requests/request_status` | 无 `format()` | `/api/v1 (default)` | 后台操作与通用能力 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L431) |
+| `alerts.load_alert_groups` | `GET` | `/clusters/{clusterName}/alert_groups?fields=*` | 无 `format()` | `/api/v1 (default)` | 告警 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L435) |
+| `alerts.load_an_alert_group` | `GET` | `/clusters/{clusterName}/alert_groups/{group_id}` | 无 `format()` | `/api/v1 (default)` | 告警 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L439) |
+| `alert_groups.create` | `POST` | `/clusters/{clusterName}/alert_groups` | `definitions`, `name`, `targets` | `/api/v1 (default)` | 告警 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L443) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/alerts/manage_alert_groups_controller.js:476` |  |  |  |  |  |
+| `alert_groups.update` | `PUT` | `/clusters/{clusterName}/alert_groups/{group_id}` | `definitions`, `name`, `targets` | `/api/v1 (default)` | 告警 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L459) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/alerts/manage_alert_groups_controller.js:508` |  |  |  |  |  |
+| `alert_groups.delete` | `DELETE` | `/clusters/{clusterName}/alert_groups/{group_id}` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 告警 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L475) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/alerts/manage_alert_groups_controller.js:542` |  |  |  |  |  |
+| `alerts.load_all_alert_definitions` | `GET` | `/clusters/{clusterName}/alert_definitions?fields=*` | 无 `format()` | `/api/v1 (default)` | 告警 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L484) |
+| `alerts.notifications` | `GET` | `/alert_targets?fields=*` | 无 `format()` | `/api/v1 (default)` | 告警 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L488) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/alerts/manage_alert_groups_controller.js:210`<br>`ambari-web/classic/app/controllers/main/alerts/manage_alert_notifications_controller.js:363` |  |  |  |  |  |
+| `alerts.instances` | `GET` | `/clusters/{clusterName}/alerts?fields=*` | 无 `format()` | `/api/v1 (default)` | 告警 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L492) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/alerts/alert_instances_controller.js:113` |  |  |  |  |  |
+| `alerts.instances.unhealthy` | `GET` | `/clusters/{clusterName}/alerts?fields=*&Alert/state.in(CRITICAL,WARNING)&{paginationInfo}` | 无 `format()` | `/api/v1 (default)` | 告警 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L496) |
+| `alerts.instances.by_definition` | `GET` | `/clusters/{clusterName}/alerts?fields=*&Alert/definition_id={definitionId}` | 无 `format()` | `/api/v1 (default)` | 告警 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L500) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/alerts/alert_instances_controller.js:104` |  |  |  |  |  |
+| `alerts.instances.by_host` | `GET` | `/clusters/{clusterName}/alerts?fields=*&Alert/host_name={hostName}` | 无 `format()` | `/api/v1 (default)` | 主机、告警 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L504) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/alerts/alert_instances_controller.js:95` |  |  |  |  |  |
+| `alerts.update_alert_definition` | `PUT` | `/clusters/{clusterName}/alert_definitions/{id}` | `data` | `/api/v1 (default)` | 告警 | 6 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L508) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/alert_definitions_controller.js:110`<br>`ambari-web/classic/app/controllers/main/alerts/definition_configs_controller.js:654`<br>`ambari-web/classic/app/controllers/main/alerts/definition_details_controller.js:155`<br>`ambari-web/classic/app/controllers/main/alerts/definition_details_controller.js:246`<br>`ambari-web/classic/app/controllers/main/alerts/definition_details_controller.js:265`<br>`ambari-web/classic/app/controllers/main/a [...]
+| `alerts.create_alert_definition` | `POST` | `/clusters/{clusterName}/alert_definitions/` | `data` | `/api/v1 (default)` | 告警 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L518) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/alerts/add_alert_definition/add_alert_definition_controller.js:38` |  |  |  |  |  |
+| `alerts.delete_alert_definition` | `DELETE` | `/clusters/{clusterName}/alert_definitions/{id}` | 无 `format()` | `/api/v1 (default)` | 告警 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L528) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/alerts/definition_details_controller.js:189` |  |  |  |  |  |
+| `alerts.create_alert_notification` | `POST` | `/alert_targets?{urlParams}` | `data` | `/api/v1 (default)` | 安装向导、告警 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L533) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/alerts/manage_alert_notifications_controller.js:860`<br>`ambari-web/classic/app/controllers/wizard/step8_controller.js:1706` |  |  |  |  |  |
+| `alerts.update_alert_notification` | `PUT` | `/alert_targets/{id}` | `data` | `/api/v1 (default)` | 告警 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L543) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/alerts/manage_alert_notifications_controller.js:898`<br>`ambari-web/classic/app/controllers/main/alerts/manage_alert_notifications_controller.js:977` |  |  |  |  |  |
+| `alerts.delete_alert_notification` | `DELETE` | `/alert_targets/{id}` | 无 `format()` | `/api/v1 (default)` | 告警 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L553) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/alerts/manage_alert_notifications_controller.js:939` |  |  |  |  |  |
+| `alerts.get_instances_history` | `GET` | `/clusters/{clusterName}/alert_history?(AlertHistory/definition_name={definitionName})&(AlertHistory/timestamp>={timestamp})` | 无 `format()` | `/api/v1 (default)` | 告警 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L558) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/alerts/definition_details_controller.js:91` |  |  |  |  |  |
+| `background_operations.get_most_recent` | `GET` | `/clusters/{clusterName}/requests?to=end&page_size={operationsCount}&fields=Requests/end_time,Requests/id,Requests/progress_percent,Requests/request_context,Requests/request_status,Requests/start_time,Requests/cluster_name,Requests/user_name&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L562) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/background_operations_controller.js:208` |  |  |  |  |  |
+| `background_operations.get_by_request` | `GET` | `/clusters/{clusterName}/requests/{requestId}?fields=*,tasks/Tasks/request_id,tasks/Tasks/command,tasks/Tasks/command_detail,tasks/Tasks/ops_display_name,tasks/Tasks/host_name,tasks/Tasks/id,tasks/Tasks/role,tasks/Tasks/status&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 安全、高可用与联邦、后台操作与通用能力 | 4 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L569) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/background_operations_controller.js:222`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/progress_popup_controller.js:111`<br>`ambari-web/classic/app/mixins/wizard/wizardProgressPageController.js:689`<br>`ambari-web/classic/app/utils/polling.js:149` |  |  |  |  |  |
+| `background_operations.get_by_task` | `GET` | `/clusters/{clusterName}/requests/{requestId}/tasks/{taskId}` | 无 `format()` | `/api/v1 (default)` | 安装向导、安全、高可用与联邦、后台操作与通用能力 | 4 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L574) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/background_operations_controller.js:215`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/progress_popup_controller.js:275`<br>`ambari-web/classic/app/controllers/wizard/step9_controller.js:990`<br>`ambari-web/classic/app/utils/polling.js:118` |  |  |  |  |  |
+| `background_operations.abort_request` | `PUT` | `/clusters/{clusterName}/requests/{requestId}` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 主机、后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L579) |
+|  |  | 调用位置:`ambari-web/classic/app/utils/host_progress_popup.js:255` |  |  |  |  |  |
+| `service.item.smoke` | `POST` | `/clusters/{clusterName}/requests` | `actionName`, `displayName`, `operationLevel`, `serviceName` | `/api/v1 (default)` | 服务与配置、安全、高可用与联邦 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L594) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos/step3_controller.js:90`<br>`ambari-web/classic/app/controllers/main/service/item.js:1293` |  |  |  |  |  |
+| `service.item.rebalanceHdfsNodes` | `POST` | `/clusters/{clusterName}/requests` | `hosts`, `threshold` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L614) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/item.js:843` |  |  |  |  |  |
+| `cancel.background.operation` | `PUT` | `/clusters/{clusterName}/requests/{requestId}` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 主机、Views、后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L636) |
+|  |  | 调用位置:`ambari-web/classic/app/views/common/host_progress_popup_body_view.js:1018` |  |  |  |  |  |
+| `service.item.refreshQueueYarnRequest` | `POST` | `/clusters/{clusterName}/requests` | `command`, `componentName`, `context`, `forceRefreshConfigTags`, `hosts`, `serviceName` | `/api/v1 (default)` | 服务与配置、后台操作与通用能力 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L657) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/item.js:646`<br>`ambari-web/classic/app/models/configs/theme/config_action.js:110` |  |  |  |  |  |
+| `service.item.startStopLdapKnox` | `POST` | `/clusters/{clusterName}/requests` | `command`, `componentName`, `context`, `host`, `serviceName` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L679) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/item.js:693` |  |  |  |  |  |
+| `service.item.updateHBaseReplication` | `POST` | `/clusters/{clusterName}/requests` | `hosts`, `replication_cluster_keys`, `replication_peers` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L700) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/item.js:976` |  |  |  |  |  |
+| `service.item.stopHBaseReplication` | `POST` | `/clusters/{clusterName}/requests` | `hosts`, `replication_peers` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L725) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/item.js:1041` |  |  |  |  |  |
+| `service.item.executeCustomCommand` | `POST` | `/clusters/{clusterName}/requests` | `command`, `componentName`, `context`, `hosts`, `serviceName` | `/api/v1 (default)` | 主机、服务与配置、安全、高可用与联邦 | 6 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L751) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/activateStandby/step3_controller.js:43`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/removeStandby/step3_controller.js:42`<br>`ambari-web/classic/app/controllers/main/host/details.js:3047`<br>`ambari-web/classic/app/controllers/main/service/item.js:741`<br>`ambari-web/classic/app/controllers/main/service/item.js:1545`<br>`ambari-web/classic/app/controllers/main/service/item.js:156 [...]
+| `configs.theme` | `GET` | `{stackVersionUrl}/services/{serviceName}/themes?ThemeInfo/default=true&fields=*` | 无 `format()` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L773) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/main/service/themes_mapping.js:40` |  |  |  |  |  |
+| `configs.theme.services` | `GET` | `{stackVersionUrl}/services?StackServices/service_name.in({serviceNames})&themes/ThemeInfo/default=true&fields=themes/*` | 无 `format()` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L778) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/main/service/themes_mapping.js:74` |  |  |  |  |  |
+| `configs.quicklinksconfig` | `GET` | `{stackVersionUrl}/services/{serviceName}/quicklinks?QuickLinkInfo/default=true&fields=*` | 无 `format()` | `/api/v1 (default)` | Views | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L785) |
+|  |  | 调用位置:`ambari-web/classic/app/views/common/quick_view_link_view.js:133` |  |  |  |  |  |
+| `configs.quicklinksconfig.services` | `GET` | `{stackVersionUrl}/services?StackServices/service_name.in({serviceNames})&quicklinks/QuickLinkInfo/default=true&fields=quicklinks/*` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L790) |
+| `configs.config_groups.load.all` | `GET` | `/clusters/{clusterName}/config_groups?fields=*` | 无 `format()` | `/api/v1 (default)` | 服务与配置 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L797) |
+| `configs.config_groups.load.services` | `GET` | `/clusters/{clusterName}/config_groups?ConfigGroup/tag.in({serviceNames})&fields=*` | 无 `format()` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L802) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/main/service/groups_mapping.js:41` |  |  |  |  |  |
+| `configs.stack_configs.load.cluster_configs` | `GET` | `{stackVersionUrl}?fields=configurations/*,Versions/config_types/*` | 无 `format()` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L809) |
+|  |  | 调用位置:`ambari-web/classic/app/utils/config.js:1045` |  |  |  |  |  |
+| `configs.stack_configs.load.all` | `GET` | `{stackVersionUrl}/services?fields=configurations/*,StackServices/config_types/*` | 无 `format()` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L814) |
+|  |  | 调用位置:`ambari-web/classic/app/utils/config.js:1062` |  |  |  |  |  |
+| `configs.stack_configs.load.services` | `GET` | `{stackVersionUrl}/services?StackServices/service_name.in({serviceList})&fields=configurations/*,configurations/dependencies/*,StackServices/config_types/*` | 无 `format()` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L819) |
+|  |  | 调用位置:`ambari-web/classic/app/utils/config.js:1062` |  |  |  |  |  |
+| `configs.stack_configs.load.service` | `GET` | `{stackVersionUrl}/services/{serviceName}?fields=configurations/*,StackServices/config_types/*` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L824) |
+| `configs.config_versions.load` | `GET` | `/clusters/{clusterName}/configurations/service_config_versions?service_name={serviceName}&service_config_version={configVersion}&fields=*` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L831) |
+| `configs.config_versions.load.group` | `GET` | `/clusters/{clusterName}/configurations/service_config_versions?service_name={serviceName}&group_id={id}&fields=*` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L836) |
+| `configs.config_versions.load.current_versions` | `GET` | `/clusters/{clusterName}/configurations/service_config_versions?service_name.in({serviceNames})&is_current=true&fields=*` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L841) |
+| `service.load_config_groups` | `GET` | `/clusters/{clusterName}/config_groups?ConfigGroup/tag={serviceName}&fields=*` | 无 `format()` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L847) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/manage_config_groups_controller.js:446` |  |  |  |  |  |
+| `reassign.load_configs` | `GET` | `/clusters/{clusterName}/configurations?{urlParams}` | 无 `format()` | `/api/v1 (default)` | 主机、服务与配置、安全、高可用与联邦 | 13 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L851) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/activateStandby/step3_controller.js:72`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/addStandby/step3_controller.js:96`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/addStandby/step4_controller.js:53`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/removeStandby/step3_controller.js:71`<br>`ambari-web/classic/app/controllers/main/ad [...]
+| `reassign.save_configs` | `PUT` | `/clusters/{clusterName}` | `properties`, `service_config_version_note`, `siteName` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L856) |
+| `config.cluster` | `GET` | `{stackVersionUrl}/configurations?fields=*` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L875) |
+| `config.advanced` | `GET` | `{stackVersionUrl}/services/{serviceName}/configurations?fields=*` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L879) |
+| `config.advanced.multiple.services` | `GET` | `{stackVersionUrl}/services?StackServices/service_name.in({serviceNames})&fields=configurations/*` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L883) |
+| `config.advanced.partial` | `GET` | `{stackVersionUrl}/services/?StackServices/service_name.in({serviceList})&fields=configurations/*{queryFilter}` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L887) |
+| `config.config_types` | `GET` | `{stackVersionUrl}/services/{serviceName}?fields=StackServices/config_types` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L891) |
+| `config.tags` | `GET` | `/clusters/{clusterName}?fields=Clusters/desired_configs` | 无 `format()` | `/api/v1 (default)` | 认证与应用外壳、安装向导、主机、服务与配置、安全、高可用与联邦 | 17 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L895) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/configuration_controller.js:145`<br>`ambari-web/classic/app/controllers/main/admin/federation/routerBasedFederation/step3_controller.js:51`<br>`ambari-web/classic/app/controllers/main/admin/federation/step3_controller.js:54`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/activateStandby/step3_controller.js:63`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/addStandby/step3_controller [...]
+| `config.tags.site` | `GET` | `/clusters/{clusterName}?fields=Clusters/desired_configs/{site}` | 无 `format()` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L899) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/update_controller.js:704` |  |  |  |  |  |
+| `config.tags_and_groups` | `GET` | `/clusters/{clusterName}?fields=Clusters/desired_configs,config_groups/*{urlParams}` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L903) |
+| `config_groups.all_fields` | `GET` | `/clusters/{clusterName}/config_groups?fields=*` | 无 `format()` | `/api/v1 (default)` | 主机、服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L907) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/addHost/step4_controller.js:28` |  |  |  |  |  |
+| `config_groups.get_config_group_by_id` | `GET` | `/clusters/{clusterName}/config_groups/{id}` | 无 `format()` | `/api/v1 (default)` | 服务与配置 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L911) |
+| `config_groups.update_config_group` | `PUT` | `/clusters/{clusterName}/config_groups/{id}` | `configGroup` | `/api/v1 (default)` | 安装向导、主机、服务与配置 | 3 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L915) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/add_controller.js:270`<br>`ambari-web/classic/app/controllers/wizard/step8_controller.js:1598`<br>`ambari-web/classic/app/mixins/common/configs/configs_saver.js:607` |  |  |  |  |  |
+| `config.on_site` | `GET` | `/clusters/{clusterName}/configurations?{params}` | 无 `format()` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L929) |
+|  |  | 调用位置:`ambari-web/classic/app/utils/config.js:819` |  |  |  |  |  |
+| `config.host_overrides` | `GET` | `/clusters/{clusterName}/configurations?{params}` | 无 `format()` | `/api/v1 (default)` | 主机、服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L933) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/manage_config_groups_controller.js:540` |  |  |  |  |  |
+| `config.tags.selected` | `GET` | `/clusters/{clusterName}/configurations?type.in({tags})` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L938) |
+| `credentials.store.info` | `GET` | `/clusters/{clusterName}?fields=Clusters/credential_store_properties` | 无 `format()` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L943) |
+|  |  | 调用位置:`ambari-web/classic/app/utils/credentials.js:214` |  |  |  |  |  |
+| `credentials.list` | `GET` | `/clusters/{clusterName}/credentials?fields=Credential/*` | 无 `format()` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L948) |
+|  |  | 调用位置:`ambari-web/classic/app/utils/credentials.js:178` |  |  |  |  |  |
+| `credentials.get` | `GET` | `/clusters/{clusterName}/credentials/{alias}` | 无 `format()` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L953) |
+|  |  | 调用位置:`ambari-web/classic/app/utils/credentials.js:128` |  |  |  |  |  |
+| `credentials.create` | `POST` | `/clusters/{clusterName}/credentials/{alias}` | `resource` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L958) |
+|  |  | 调用位置:`ambari-web/classic/app/utils/credentials.js:70` |  |  |  |  |  |
+| `credentials.update` | `PUT` | `/clusters/{clusterName}/credentials/{alias}` | `resource` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L971) |
+|  |  | 调用位置:`ambari-web/classic/app/utils/credentials.js:159` |  |  |  |  |  |
+| `credentials.delete` | `DELETE` | `/clusters/{clusterName}/credentials/{alias}` | 无 `format()` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L984) |
+|  |  | 调用位置:`ambari-web/classic/app/utils/credentials.js:196` |  |  |  |  |  |
+| `host.host_component.add_new_component` | `POST` | `/clusters/{clusterName}/hosts?Hosts/host_name={hostName}` | `data` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L990) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/main/host/details/host_components/install_component.js:30` |  |  |  |  |  |
+| `host.host_component.add_new_components` | `POST` | `/clusters/{clusterName}/hosts` | `data` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1001) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js:592` |  |  |  |  |  |
+| `host.host_component.delete_components` | `DELETE` | `/clusters/{clusterName}/host_components` | `data` | `/api/v1 (default)` | 主机、Stack 与升级 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1012) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:1655`<br>`ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js:718` |  |  |  |  |  |
+| `host.host_component.slave_desired_admin_state` | `GET` | `/clusters/{clusterName}/hosts/{hostName}/host_components/{componentName}/?fields=HostRoles/desired_admin_state` | 无 `format()` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1022) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/main/host/details/host_components/decommissionable.js:158` |  |  |  |  |  |
+| `host.host_component.decommission_status` | `GET` | `/clusters/{clusterName}/services/{serviceName}/components/{componentName}/?fields=ServiceComponentInfo,host_components/HostRoles/state` | 无 `format()` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1026) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/main/host/details/host_components/decommissionable.js:201` |  |  |  |  |  |
+| `host_components.hbase_regionserver.active` | `GET` | `/clusters/{clusterName}/host_components?HostRoles/component_name=HBASE_REGIONSERVER&HostRoles/maintenance_state=OFF&HostRoles/desired_admin_state=INSERVICE&HostRoles/host_name.in({hostNames})` | 无 `format()` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1030) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js:954` |  |  |  |  |  |
+| `host.host_component.decommission_status_datanode` | `GET` | `/clusters/{clusterName}/host_components?HostRoles/component_name=NAMENODE&HostRoles/host_name.in({hostNames})&fields=metrics/dfs/namenode` | 无 `format()` | `/api/v1 (default)` | 主机、Views | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1034) |
+|  |  | 调用位置:`ambari-web/classic/app/views/main/host/details/host_component_views/datanode_view.js:35` |  |  |  |  |  |
+| `host.host_component.decommission_status_regionserver` | `GET` | `/clusters/{clusterName}/host_components?HostRoles/component_name=HBASE_MASTER&HostRoles/host_name={hostName}&fields=metrics/hbase/master/liveRegionServersHosts,metrics/hbase/master/deadRegionServersHosts&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机、Views | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1038) |
+|  |  | 调用位置:`ambari-web/classic/app/views/main/host/details/host_component_views/regionserver_view.js:32` |  |  |  |  |  |
+| `host.region_servers.in_inservice` | `GET` | `/clusters/{clusterName}/host_components?HostRoles/component_name=HBASE_REGIONSERVER&HostRoles/desired_admin_state=INSERVICE&fields=HostRoles/host_name&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1041) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/details.js:1993` |  |  |  |  |  |
+| `host.host_component.decommission_slave` | `POST` | `/clusters/{clusterName}/requests` | `clusterName`, `command`, `componentName`, `context`, `hostName`, `serviceName`, `slaveType` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1045) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/details.js:1956` |  |  |  |  |  |
+| `host.host_component.refresh_configs` | `POST` | `/clusters/{clusterName}/requests` | `context`, `resource_filters` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1072) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/details.js:1662` |  |  |  |  |  |
+| `admin.security_status` | `GET` | `/clusters/{clusterName}?fields=Clusters/security_type` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1420) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos.js:308` |  |  |  |  |  |
+| `cluster.load_cluster_name` | `GET` | `/clusters?fields=Clusters/security_type,Clusters/version,Clusters/cluster_id` | 无 `format()` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1429) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/cluster_controller.js:111` |  |  |  |  |  |
+| `cluster.load_last_upgrade` | `GET` | `/clusters/{clusterName}/upgrades?fields=Upgrade/*` | 无 `format()` | `/api/v1 (default)` | Stack 与升级、后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1433) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/cluster_controller.js:533` |  |  |  |  |  |
+| `cluster.update_upgrade_version` | `GET` | `/stacks/{stackName}/versions?fields=services/StackServices,Versions` | `data` | `/api/v1 (default)` | Stack 与升级 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1437) |
+| `cluster.load_repositories` | `GET` | `/stacks/{stackName}/versions/{stackVersion}/operating_systems?fields=repositories/*,OperatingSystems/*` | `data` | `/api/v1 (default)` | 安装向导、Stack 与升级 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1446) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:2090`<br>`ambari-web/classic/app/controllers/wizard/step8_controller.js:404` |  |  |  |  |  |
+| `cluster.load_repo_version` | `GET` | `/stacks/{stackName}/versions?fields=repository_versions/operating_systems/repositories/*,repository_versions/operating_systems/OperatingSystems/*,repository_versions/RepositoryVersions/display_name&repository_versions/RepositoryVersions/repository_version={repositoryVersion}` | 无 `format()` | `/api/v1 (default)` | 安装向导、Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1455) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step8_controller.js:347` |  |  |  |  |  |
+| `cluster.load_detailed_repo_version` | `GET` | `/clusters/{clusterName}/stack_versions?fields=repository_versions/RepositoryVersions/repository_version,ClusterStackVersions/stack,ClusterStackVersions/version&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | Stack 与升级、后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1459) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/cluster_controller.js:502` |  |  |  |  |  |
+| `cluster.load_current_repo_stack_services` | `GET` | `/clusters/{clusterName}/stack_versions?fields=repository_versions/RepositoryVersions/stack_services,ClusterStackVersions/stack,ClusterStackVersions/version` | 无 `format()` | `/api/v1 (default)` | 安装向导、Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1463) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard.js:687` |  |  |  |  |  |
+| `cluster.save_provisioning_state` | `PUT` | `/clusters/{clusterName}` | `state` | `/api/v1 (default)` | 安装向导 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1467) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/installer.js:1124` |  |  |  |  |  |
+| `cluster.logging.searchEngine` | `GET` | `/clusters/{clusterName}/logging/searchEngine?{query}` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1481) |
+| `admin.high_availability.polling` | `GET` | `/clusters/{clusterName}/requests/{requestId}?fields=tasks/*,Requests/*` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1485) |
+| `admin.high_availability.getNnCheckPointStatus` | `GET` | `/clusters/{clusterName}/hosts/{hostName}/host_components/NAMENODE` | 无 `format()` | `/api/v1 (default)` | 安全、高可用与联邦 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1489) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollbackHA/step2_controller.js:27`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step4_controller.js:38` |  |  |  |  |  |
+| `admin.high_availability.getNnCheckPointsStatuses` | `GET` | `/clusters/{clusterName}/host_components?HostRoles/component_name=NAMENODE&HostRoles/host_name.in({hostNames})&fields=HostRoles/desired_state,metrics/dfs/namenode&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1493) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/highAvailability/journalNode/step3_controller.js:51` |  |  |  |  |  |
+| `admin.high_availability.getJnCheckPointStatus` | `GET` | `/clusters/{clusterName}/hosts/{hostName}/host_components/JOURNALNODE?fields=metrics` | 无 `format()` | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1497) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step6_controller.js:80` |  |  |  |  |  |
+| `admin.high_availability.getHostComponent` | `GET` | `/clusters/{clusterName}/hosts/{hostName}/host_components/{componentName}` | 无 `format()` | `/api/v1 (default)` | 主机、安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1501) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js:501` |  |  |  |  |  |
+| `common.create_component` | `POST` | `/clusters/{clusterName}/services?ServiceInfo/service_name={serviceName}` | `componentName` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1505) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/main/host/details/host_components/install_component.js:134` |  |  |  |  |  |
+| `admin.high_availability.load_configs` | `GET` | `/clusters/{clusterName}/configurations?(type=core-site&tag={coreSiteTag})\|(type=hdfs-site&tag={hdfsSiteTag})` | 无 `format()` | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1523) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js:414` |  |  |  |  |  |
+| `admin.save_configs` | `PUT` | `/clusters/{clusterName}` | `properties`, `siteName` | `/api/v1 (default)` | 告警、安全、高可用与联邦 | 7 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1527) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js:364`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js:377`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js:391`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js:428`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability [...]
+| `admin.high_availability.load_hbase_configs` | `GET` | `/clusters/{clusterName}/configurations?type=hbase-site&tag={hbaseSiteTag}` | 无 `format()` | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1544) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/rollback_controller.js:239` |  |  |  |  |  |
+| `admin.security.cluster_configs` | `GET` | `/clusters/{clusterName}` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 安全、高可用与联邦 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1548) |
+| `admin.security.cluster_configs.kerberos` | `GET` | `/clusters/{clusterName}/configurations/service_config_versions?service_name=KERBEROS&is_current=true` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1557) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos.js:450` |  |  |  |  |  |
+| `admin.get.all_configurations` | `GET` | `/clusters/{clusterName}/configurations?{urlParams}` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 认证与应用外壳、主机、安全、高可用与联邦 | 10 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1566) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/federation/routerBasedFederation/step3_controller.js:63`<br>`ambari-web/classic/app/controllers/main/admin/federation/step3_controller.js:73`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/journalNode/step2_controller.js:81`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step3_controller.js:214`<br>`ambari-web/classic/app/controllers/main/host/details.js:927`<br>`ambari-web/classi [...]
+| `kerberos.session.state` | `GET` | `/clusters/{clusterName}/services/KERBEROS?fields=Services/attributes/kdc_validation_result,Services/attributes/kdc_validation_failure_details` | 无 `format()` | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1576) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos.js:423` |  |  |  |  |  |
+| `admin.kerberize.cluster` | `PUT` | `/clusters/{clusterName}` | `data` | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1581) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos/step7_controller.js:42` |  |  |  |  |  |
+| `admin.unkerberize.cluster` | `PUT` | `/clusters/{clusterName}` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 安全、高可用与联邦 | 4 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1591) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos/disable_controller.js:48`<br>`ambari-web/classic/app/controllers/main/admin/kerberos/step5_controller.js:104`<br>`ambari-web/classic/app/controllers/main/admin/kerberos/step7_controller.js:70`<br>`ambari-web/classic/app/controllers/main/admin/kerberos/wizard_controller.js:245` |  |  |  |  |  |
+| `admin.kerberize.cluster.force` | `PUT` | `/clusters/{clusterName}?force_toggle_kerberos=true` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1605) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos/step7_controller.js:55` |  |  |  |  |  |
+| `admin.unkerberize.cluster.skip` | `PUT` | `/clusters/{clusterName}?manage_kerberos_identities=false` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1620) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos/disable_controller.js:57` |  |  |  |  |  |
+| `get.cluster.artifact` | `GET` | `/clusters/{clusterName}/artifacts/{artifactName}?fields=artifact_data` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1635) |
+| `admin.kerberize.stack_descriptor` | `GET` | `/clusters/{clusterName}/kerberos_descriptors/STACK` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1639) |
+| `admin.kerberize.cluster_descriptor_artifact` | `GET` | `/clusters/{clusterName}/artifacts/kerberos_descriptor?fields=artifact_data` | 无 `format()` | `/api/v1 (default)` | 安装向导 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1643) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step7_controller.js:1885` |  |  |  |  |  |
+| `admin.kerberize.cluster_descriptor` | `GET` | `/clusters/{clusterName}/kerberos_descriptors/COMPOSITE{queryParams}` | 无 `format()` | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1647) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/wizard/addSecurityConfigs.js:423` |  |  |  |  |  |
+| `admin.kerberize.cluster_descriptor.stack` | `GET` | `/clusters/{clusterName}/kerberos_descriptors/STACK` | 无 `format()` | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1651) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/wizard/addSecurityConfigs.js:433` |  |  |  |  |  |
+| `admin.kerberos.cluster.artifact.create` | `POST` | `/clusters/{clusterName}/artifacts/{artifactName}` | `data` | `/api/v1 (default)` | 安装向导、安全、高可用与联邦 | 3 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1655) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos.js:584`<br>`ambari-web/classic/app/controllers/wizard/step8_controller.js:895`<br>`ambari-web/classic/app/mixins/wizard/addSecurityConfigs.js:860` |  |  |  |  |  |
+| `admin.kerberos.cluster.artifact.update` | `PUT` | `/clusters/{clusterName}/artifacts/{artifactName}` | `data` | `/api/v1 (default)` | 安装向导、安全、高可用与联邦 | 3 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1664) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos.js:557`<br>`ambari-web/classic/app/controllers/wizard/step8_controller.js:895`<br>`ambari-web/classic/app/mixins/wizard/addSecurityConfigs.js:878` |  |  |  |  |  |
+| `admin.kerberos.cluster.csv` | `GET` | `/clusters/{clusterName}/kerberos_identities?fields=*&format=csv` | `data` | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1673) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos/step5_controller.js:64` |  |  |  |  |  |
+| `admin.poll.kerberize.cluster.request` | `GET` | `/clusters/{clusterName}/requests/{requestId}?fields=stages/Stage/context,stages/Stage/status,stages/Stage/progress_percent,stages/tasks/*,Requests/*` | 无 `format()` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1683) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/wizard/wizardProgressPageController.js:159` |  |  |  |  |  |
+| `admin.stack_upgrade.run_upgrade` | `PUT` | `/clusters/{clusterName}` | `data` | `/api/v1 (default)` | Stack 与升级 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1687) |
+| `admin.user.create` | `POST` | `/users/{user}` | `data` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1697) |
+| `admin.user.edit` | `PUT` | `/users/{user}` | `data` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1708) |
+| `admin.stack_upgrade.do_poll` | `GET` | `/clusters/{cluster}/requests/{requestId}?fields=tasks/*` | 无 `format()` | `/api/v1 (default)` | Stack 与升级 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1719) |
+| `admin.upgrade.data` | `GET` | `/clusters/{clusterName}/upgrades/{id}?upgrade_groups/UpgradeGroup/status!=PENDING&fields=Upgrade/progress_percent,Upgrade/request_context,Upgrade/request_status,Upgrade/direction,Upgrade/downgrade_allowed,upgrade_groups/UpgradeGroup,Upgrade/*,upgrade_groups/upgrade_items/UpgradeItem/status,upgrade_groups/upgrade_items/UpgradeItem/display_status,upgrade_groups/upgrade_items/UpgradeItem/context,upgrade_groups/upgrade_items/UpgradeItem/group_id,upgrade_grou [...]
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:462`<br>`ambari-web/classic/app/controllers/main/admin/stack_upgrade_history_controller.js:84` |  |  |  |  |  |
+| `admin.upgrade.state` | `GET` | `/clusters/{clusterName}/upgrades/{id}?fields=Upgrade/*` | 无 `format()` | `/api/v1 (default)` | Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1740) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:462` |  |  |  |  |  |
+| `admin.upgrade.finalizeContext` | `GET` | `/clusters/{clusterName}/upgrades/{id}?upgrade_groups/upgrade_items/UpgradeItem/status=HOLDING&fields=upgrade_groups/upgrade_items/UpgradeItem/context` | 无 `format()` | `/api/v1 (default)` | Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1744) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:1963` |  |  |  |  |  |
+| `admin.upgrade.upgrade_item` | `GET` | `/clusters/{clusterName}/upgrades/{upgradeId}/upgrade_groups/{groupId}/upgrade_items/{stageId}?fields=UpgradeItem/group_id,UpgradeItem/stage_id,tasks/Tasks/command_detail,tasks/Tasks/host_name,tasks/Tasks/role,tasks/Tasks/request_id,tasks/Tasks/stage_id,tasks/Tasks/status,tasks/Tasks/structured_out&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | Stack 与升级 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1748) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:627`<br>`ambari-web/classic/app/controllers/main/admin/stack_upgrade_history_controller.js:161` |  |  |  |  |  |
+| `admin.upgrade.upgrade_task` | `GET` | `/clusters/{clusterName}/upgrades/{upgradeId}/upgrade_groups/{groupId}/upgrade_items/{stageId}/tasks/{taskId}` | 无 `format()` | `/api/v1 (default)` | Stack 与升级 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1762) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:681`<br>`ambari-web/classic/app/controllers/main/admin/stack_upgrade_history_controller.js:214` |  |  |  |  |  |
+| `admin.upgrade.service_checks` | `GET` | `/clusters/{clusterName}/upgrades/{upgradeId}/upgrade_groups?upgrade_items/UpgradeItem/status=COMPLETED&upgrade_items/tasks/Tasks/status.in(FAILED,ABORTED,TIMEDOUT)&upgrade_items/tasks/Tasks/command=SERVICE_CHECK&fields=upgrade_items/tasks/Tasks/command_detail,tasks/Tasks/ops_display_name,upgrade_items/tasks/Tasks/status&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | Stack 与升级、Views | 1 | [source](../../../../ambari-web/classic/ap [...]
+|  |  | 调用位置:`ambari-web/classic/app/views/main/admin/stack_upgrade/upgrade_wizard_view.js:311` |  |  |  |  |  |
+| `admin.upgrade.update.options` | `PUT` | `/clusters/{clusterName}/upgrades/{upgradeId}` | `skipComponentFailures`, `skipSCFailures` | `/api/v1 (default)` | Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1769) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:1234` |  |  |  |  |  |
+| `admin.upgrade.start` | `POST` | `/clusters/{clusterName}/upgrades` | `id`, `skipComponentFailures`, `skipSCFailures`, `type` | `/api/v1 (default)` | Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1784) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:877` |  |  |  |  |  |
+| `admin.downgrade.start` | `POST` | `/clusters/{clusterName}/upgrades` | `upgradeType` | `/api/v1 (default)` | Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1803) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:791` |  |  |  |  |  |
+| `admin.upgrade.abort` | `PUT` | `/clusters/{clusterName}/upgrades/{upgradeId}` | 无静态 `data.*` 引用 | `/api/v1 (default)` | Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1818) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:774` |  |  |  |  |  |
+| `admin.upgrade.suspend` | `PUT` | `/clusters/{clusterName}/upgrades/{upgradeId}` | 无静态 `data.*` 引用 | `/api/v1 (default)` | Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1833) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:813` |  |  |  |  |  |
+| `admin.upgrade.retry` | `PUT` | `/clusters/{clusterName}/upgrades/{upgradeId}` | 无静态 `data.*` 引用 | `/api/v1 (default)` | Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1848) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:862` |  |  |  |  |  |
+| `admin.upgrade.revert` | `POST` | `/clusters/{clusterName}/upgrades` | `upgradeId` | `/api/v1 (default)` | Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1862) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:2219` |  |  |  |  |  |
+| `admin.upgrade.upgradeItem.setState` | `PUT` | `/clusters/{clusterName}/upgrades/{upgradeId}/upgrade_groups/{groupId}/upgrade_items/{itemId}` | `status` | `/api/v1 (default)` | Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1877) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:2057` |  |  |  |  |  |
+| `admin.stack_versions.all` | `GET` | `/clusters/{clusterName}/stack_versions?fields=ClusterStackVersions/*,repository_versions/RepositoryVersions/*&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1891) |
+| `admin.stack_version.install.repo_version` | `POST` | `/clusters/{clusterName}/stack_versions` | `ClusterStackVersions` | `/api/v1 (default)` | Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1895) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:1596` |  |  |  |  |  |
+| `admin.stack_versions.edit.repo` | `PUT` | `/stacks/{stackName}/versions/{stackVersion}/repository_versions/{repoVersionId}` | `repoVersion` | `/api/v1 (default)` | 安装向导、Stack 与升级 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1908) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/installer.js:823`<br>`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:1770` |  |  |  |  |  |
+| `admin.stack_versions.validate.repo` | `POST` | `/stacks/{stackName}/versions/{stackVersion}/operating_systems/{osType}/repositories/{repoId}?validate_only=true` | `baseUrl`, `repoName` | `/api/v1 (default)` | Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1918) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:1833` |  |  |  |  |  |
+| `admin.stack_versions.discard` | `PUT` | `/stacks/{stackName}/versions/{stackVersion}/repository_versions/{id}` | 无静态 `data.*` 引用 | `/api/v1 (default)` | Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1934) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:2254` |  |  |  |  |  |
+| `admin.upgrade.pre_upgrade_check` | `GET` | `/clusters/{clusterName}/rolling_upgrades_check?fields=*&UpgradeChecks/repository_version_id={id}&UpgradeChecks/upgrade_type={type}` | 无 `format()` | `/api/v1 (default)` | Stack 与升级 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1949) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:1296`<br>`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:1463` |  |  |  |  |  |
+| `admin.upgrade.get_supported_upgradeTypes` | `GET` | `/stacks/{stackName}/versions/{stackVersion}/compatible_repository_versions?CompatibleRepositoryVersions/repository_version={toVersion}` | 无 `format()` | `/api/v1 (default)` | Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1954) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:1319` |  |  |  |  |  |
+| `admin.upgrade.get_compatible_versions` | `GET` | `/stacks/{stackName}/versions/{stackVersion}/compatible_repository_versions?fields=CompatibleRepositoryVersions/repository_version&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1959) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js:501` |  |  |  |  |  |
+| `admin.kerberos_security.checks` | `GET` | `''` | 无 `format()` | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1964) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos.js:235` |  |  |  |  |  |
+| `admin.kerberos_security.test_connection` | `GET` | `/kdc_check/{kdcHostname}` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 安全、高可用与联邦、Views | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1970) |
+|  |  | 调用位置:`ambari-web/classic/app/views/common/controls_view.js:1333` |  |  |  |  |  |
+| `admin.kerberos_security.regenerate_keytabs` | `PUT` | `/clusters/{clusterName}?regenerate_keytabs={type}` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1980) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos.js:183` |  |  |  |  |  |
+| `admin.kerberos_security.regenerate_keytabs.service` | `PUT` | `/clusters/{clusterName}?regenerate_keytabs=all&regenerate_components={serviceName}&config_update_policy=none` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 服务与配置、安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1995) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/item.js:890` |  |  |  |  |  |
+| `admin.kerberos_security.regenerate_keytabs.host` | `PUT` | `/clusters/{clusterName}?regenerate_keytabs=all&regenerate_hosts={hostName}&config_update_policy=none` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 主机、安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2010) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/details.js:3268` |  |  |  |  |  |
+| `wizard.step1.post_version_definition_file.xml` | `POST` | `/version_definitions?dry_run=true` | `data` | `/api/v1 (default)` | 安装向导、Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2025) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/installer.js:588` |  |  |  |  |  |
+| `wizard.step1.post_version_definition_file.url` | `POST` | `/version_definitions?dry_run=true` | `data` | `/api/v1 (default)` | 安装向导、Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2039) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/installer.js:588` |  |  |  |  |  |
+| `wizard.step8.post_version_definition_file.xml` | `POST` | `/version_definitions` | `data` | `/api/v1 (default)` | 安装向导、Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2049) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/installer.js:655` |  |  |  |  |  |
+| `wizard.step8.post_version_definition_file` | `POST` | `/version_definitions` | `data` | `/api/v1 (default)` | 安装向导、Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2063) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/installer.js:655` |  |  |  |  |  |
+| `wizard.step1.get_repo_version_by_id` | `GET` | `/stacks/{stackName}/versions?fields=repository_versions/operating_systems/repositories/*,repository_versions/RepositoryVersions/*&repository_versions/RepositoryVersions/id={repoId}&Versions/stack_version={stackVersion}` | 无 `format()` | `/api/v1 (default)` | Stack 与升级 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2073) |
+| `wizard.step1.get_supported_os_types` | `GET` | `/stacks/{stackName}/versions/{stackVersion}?fields=operating_systems/repositories/Repositories` | 无 `format()` | `/api/v1 (default)` | 安装向导 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2080) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/installer.js:702` |  |  |  |  |  |
+| `wizard.advanced_repositories.valid_url` | `PUT` | `/stacks/{stackName}/versions/{stackVersion}/operating_systems/{osType}/repositories/{repoId}` | `data` | `/api/v1 (default)` | 安装向导、Stack 与升级、Views | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2085) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/installer.js:910`<br>`ambari-web/classic/app/views/main/admin/stack_upgrade/services_view.js:241` |  |  |  |  |  |
+| `wizard.get_version_definitions` | `GET` | `/version_definitions` | 无 `format()` | `/api/v1 (default)` | 安装向导、Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2095) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step8_controller.js:836` |  |  |  |  |  |
+| `wizard.delete_repository_versions` | `DELETE` | `/stacks/{stackName}/versions/{stackVersion}/repository_versions/{id}` | 无 `format()` | `/api/v1 (default)` | 安装向导、Stack 与升级 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2098) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step8_controller.js:863` |  |  |  |  |  |
+| `wizard.service_components` | `GET` | `{stackUrl}/services?fields=StackServices/*,components/*,components/dependencies/Dependencies/scope,components/dependencies/Dependencies/service_name,components/dependencies/Dependencies/type,artifacts/Artifacts/artifact_name` | 无 `format()` | `/api/v1 (default)` | 安装向导、后台操作与通用能力 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2102) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/cluster_controller.js:399`<br>`ambari-web/classic/app/controllers/wizard.js:630` |  |  |  |  |  |
+| `wizard.step9.installer.get_host_status` | `GET` | `/clusters/{cluster}/hosts?fields=Hosts/host_state,host_components/HostRoles/state` | 无 `format()` | `/api/v1 (default)` | 安装向导、主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2106) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step9_controller.js:1109` |  |  |  |  |  |
+| `wizard.step9.load_log` | `GET` | `/clusters/{cluster}/requests/{requestId}?fields=tasks/Tasks/command,tasks/Tasks/command_detail,tasks/Tasks/ops_display_name,tasks/Tasks/exit_code,tasks/Tasks/start_time,tasks/Tasks/end_time,tasks/Tasks/host_name,tasks/Tasks/id,tasks/Tasks/role,tasks/Tasks/status&minimal_response=true` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 安装向导 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2110) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step9_controller.js:1041` |  |  |  |  |  |
+| `wizard.step8.existing_cluster_names` | `GET` | `/clusters` | 无 `format()` | `/api/v1 (default)` | 安装向导 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2120) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step8_controller.js:719` |  |  |  |  |  |
+| `wizard.step8.create_cluster` | `POST` | `/clusters/{cluster}` | `data` | `/api/v1 (default)` | 安装向导 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2125) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step8_controller.js:992` |  |  |  |  |  |
+| `wizard.step8.create_selected_services` | `POST` | `/clusters/{cluster}/services` | `data` | `/api/v1 (default)` | 安装向导、安全、高可用与联邦 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2137) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos/wizard_controller.js:204`<br>`ambari-web/classic/app/controllers/wizard/step8_controller.js:1014` |  |  |  |  |  |
+| `wizard.step8.create_components` | `POST` | `/clusters/{cluster}/services?ServiceInfo/service_name={serviceName}` | `data` | `/api/v1 (default)` | 安装向导 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2149) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step8_controller.js:1088`<br>`ambari-web/classic/app/controllers/wizard/step8_controller.js:1108` |  |  |  |  |  |
+| `wizard.step8.register_host_to_cluster` | `POST` | `/clusters/{cluster}/hosts` | `data` | `/api/v1 (default)` | 安装向导、主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2161) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step8_controller.js:1133` |  |  |  |  |  |
+| `wizard.step8.register_host_to_component` | `POST` | `/clusters/{cluster}/hosts` | `data` | `/api/v1 (default)` | 安装向导、主机、安全、高可用与联邦 | 3 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2173) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos/wizard_controller.js:275`<br>`ambari-web/classic/app/controllers/wizard/step8_controller.js:1463`<br>`ambari-web/classic/app/mixins/wizard/wizardProgressPageController.js:569` |  |  |  |  |  |
+| `wizard.step8.apply_configuration_groups` | `POST` | `/clusters/{cluster}/config_groups` | `data` | `/api/v1 (default)` | 安装向导、服务与配置 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2185) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step8_controller.js:1582`<br>`ambari-web/classic/app/mixins/common/configs/configs_saver.js:586` |  |  |  |  |  |
+| `wizard.step8.set_local_repos` | `PUT` | `{stackVersionURL}/operating_systems/{osType}/repositories/{repoId}` | `data` | `/api/v1 (default)` | Stack 与升级 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2197) |
+| `wizard.step3.jdk_check` | `POST` | `/requests` | `host_names`, `java_home`, `jdk_location` | `/api/v1 (default)` | 安装向导 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2208) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step3_controller.js:732` |  |  |  |  |  |
+| `wizard.step3.jdk_check.get_results` | `GET` | `/requests/{requestIndex}?fields=*,tasks/Tasks/host_name,tasks/Tasks/status,tasks/Tasks/structured_out` | 无 `format()` | `/api/v1 (default)` | 安装向导 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2232) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step3_controller.js:751` |  |  |  |  |  |
+| `wizard.step3.host_info` | `GET` | `/hosts?fields=Hosts/total_mem,Hosts/cpu_count,Hosts/disk_info,Hosts/last_agent_env,Hosts/host_name,Hosts/os_type,Hosts/os_arch,Hosts/os_family,Hosts/ip` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2236) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/main/host/details/actions/check_host.js:254` |  |  |  |  |  |
+| `wizard.loadrecommendations` | `POST` | `{stackVersionUrl}/recommendations` | `hosts`, `recommend`, `recommendations`, `services` | `/api/v1 (default)` | 安装向导、主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2247) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/common/hosts/host_component_recommendation_mixin.js:117` |  |  |  |  |  |
+| `config.recommendations` | `POST` | `{stackVersionUrl}/recommendations` | `dataToSend` | `/api/v1 (default)` | 安装向导、服务与配置、安全、高可用与联邦 | 4 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2270) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/highAvailability/resourceManager/step3_controller.js:202`<br>`ambari-web/classic/app/controllers/main/admin/kerberos/step4_controller.js:343`<br>`ambari-web/classic/app/mixins/common/configs/enhanced_configs.js:336`<br>`ambari-web/classic/app/mixins/wizard/addSecurityConfigs.js:759` |  |  |  |  |  |
+| `config.validations` | `POST` | `{stackVersionUrl}/validations` | `hosts`, `recommendations`, `services`, `validate` | `/api/v1 (default)` | 主机 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2282) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/common/hosts/host_component_validation_mixin.js:96`<br>`ambari-web/classic/app/mixins/common/serverValidator.js:197` |  |  |  |  |  |
+| `preinstalled.checks` | `POST` | `/requests` | `RequestInfo`, `resource_filters` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2299) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/main/host/details/actions/check_host.js:145` |  |  |  |  |  |
+| `preinstalled.checks.tasks` | `GET` | `/requests/{requestId}?fields=Requests/inputs,Requests/request_status,tasks/Tasks/host_name,tasks/Tasks/structured_out/host_resolution_check/hosts_with_failures,tasks/Tasks/structured_out/host_resolution_check/failed_count,tasks/Tasks/structured_out/installed_packages,tasks/Tasks/structured_out/last_agent_env_check,tasks/Tasks/structured_out/transparentHugePage,tasks/Tasks/stdout,tasks/Tasks/stderr,tasks/Tasks/error_log,tasks/Tasks/command_detail,t [...]
+|  |  | 调用位置:`ambari-web/classic/app/mixins/main/host/details/actions/check_host.js:189` |  |  |  |  |  |
+| `persist.get.text` | `GET` | `/persist/{key}` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 跨模块与待人工归类 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2329) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/common/persist.js:68` |  |  |  |  |  |
+| `persist.get` | `GET` | `/persist/{key}` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2340) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/common/persist.js:49` |  |  |  |  |  |
+| `persist.post` | `POST` | `/persist` | `keyValuePair` | `/api/v1 (default)` | 跨模块与待人工归类 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2345) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/common/persist.js:129` |  |  |  |  |  |
+| `wizard.step3.rerun_checks` | `GET` | `/hosts?fields=Hosts/last_agent_env` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2356) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/main/host/details/actions/check_host.js:444` |  |  |  |  |  |
+| `wizard.step3.bootstrap` | `GET` | `/bootstrap/{bootRequestId}` | 无 `format()` | `/api/v1 (default)` | 安装向导 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2365) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step3_controller.js:485` |  |  |  |  |  |
+| `wizard.step3.is_hosts_registered` | `GET` | `/hosts?fields=Hosts/host_status` | 无 `format()` | `/api/v1 (default)` | 安装向导、主机 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2369) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step3_controller.js:575`<br>`ambari-web/classic/app/controllers/wizard/step3_controller.js:670` |  |  |  |  |  |
+| `wizard.stacks` | `GET` | `/stacks` | 无 `format()` | `/api/v1 (default)` | 安装向导 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2373) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/installer.js:238` |  |  |  |  |  |
+| `wizard.stacks_versions` | `GET` | `/stacks/{stackName}/versions?fields=Versions,operating_systems/repositories/Repositories` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2377) |
+| `wizard.stacks_versions_definitions` | `GET` | `/version_definitions?fields=VersionDefinition/stack_default,VersionDefinition/stack_repo_update_link_exists,VersionDefinition/max_jdk,VersionDefinition/min_jdk,operating_systems/repositories/Repositories/*,operating_systems/OperatingSystems/*,VersionDefinition/stack_services,VersionDefinition/repository_version&VersionDefinition/show_available=true&VersionDefinition/stack_name={stackName}` | 无 `format()` | `/api/v1 (default)` | 安装向导 | 1 | [...]
+|  |  | 调用位置:`ambari-web/classic/app/controllers/installer.js:272` |  |  |  |  |  |
+| `wizard.launch_bootstrap` | `POST` | `/bootstrap` | `bootStrapData`, `popup` | `/api/v1 (default)` | 安装向导 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2387) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard.js:515` |  |  |  |  |  |
+| `router.login` | `POST` | `/auth` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 认证与应用外壳 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2399) |
+|  |  | 调用位置:`ambari-web/classic/app/router.js:422` |  |  |  |  |  |
+| `router.afterLogin` | `GET` | `/users/{loginName}?fields=*,privileges/PrivilegeInfo/cluster_name,privileges/PrivilegeInfo/permission_name` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 认证与应用外壳 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2413) |
+|  |  | 调用位置:`ambari-web/classic/app/router.js:297`<br>`ambari-web/classic/app/router.js:443` |  |  |  |  |  |
+| `users.all` | `GET` | `/users/?fields=*` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2426) |
+| `users.privileges` | `GET` | `/privileges?fields=*` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2430) |
+| `router.user.privileges` | `GET` | `/users/{userName}/privileges?fields=*` | 无 `format()` | `/api/v1 (default)` | 认证与应用外壳 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2434) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/user_settings_controller.js:202` |  |  |  |  |  |
+| `router.user.authorizations` | `GET` | `/users/{userName}/authorizations?fields=*` | 无 `format()` | `/api/v1 (default)` | 认证与应用外壳、后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2438) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/cluster_controller.js:420` |  |  |  |  |  |
+| `router.login.clusters` | `GET` | `/clusters?fields=Clusters/provisioning_state,Clusters/security_type,Clusters/version,Clusters/cluster_id` | 无 `format()` | `/api/v1 (default)` | 认证与应用外壳 | 3 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2442) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/application.js:125`<br>`ambari-web/classic/app/router.js:275`<br>`ambari-web/classic/app/router.js:572` |  |  |  |  |  |
+| `router.login.message` | `GET` | `/settings/motd` | 无 `format()` | `/api/v1 (default)` | 认证与应用外壳 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2446) |
+|  |  | 调用位置:`ambari-web/classic/app/router.js:464` |  |  |  |  |  |
+| `router.logoff` | `GET` | `/logout` | 无静态 `data.*` 引用 | `/api/v1 (default)` | 认证与应用外壳 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2450) |
+|  |  | 调用位置:`ambari-web/classic/app/router.js:738` |  |  |  |  |  |
+| `ambari.service` | `GET` | `/services/AMBARI/components/AMBARI_SERVER{fields}` | 无 `format()` | `/api/v1 (default)` | 安装向导、后台操作与通用能力 | 6 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2461) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/cluster_controller.js:411`<br>`ambari-web/classic/app/controllers/installer.js:426`<br>`ambari-web/classic/app/controllers/wizard/step2_controller.js:523`<br>`ambari-web/classic/app/controllers/wizard/step3_controller.js:707`<br>`ambari-web/classic/app/controllers/wizard/step7_controller.js:1566`<br>`ambari-web/classic/app/controllers/wizard/step9_controller.js:1226` |  |  |  |  |  |
+| `config_groups.create` | `POST` | `/clusters/{clusterName}/config_groups` | `data` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2466) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/main/service/configs/config_overridable.js:260` |  |  |  |  |  |
+| `config_groups.update` | `PUT` | `/clusters/{clusterName}/config_groups/{id}` | `data` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2476) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/main/service/configs/config_overridable.js:310` |  |  |  |  |  |
+| `request_schedule.get` | `GET` | `/clusters/{clusterName}/request_schedules/{request_schedule_id}` | 无 `format()` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2486) |
+|  |  | 调用位置:`ambari-web/classic/app/utils/batch_scheduled_requests.js:714` |  |  |  |  |  |
+| `request_schedule.get.pending` | `GET` | `/clusters/{clusterName}/request_schedules?fields=*&(RequestSchedule/status.in(SCHEDULED,IN_PROGRESS))` | 无 `format()` | `/api/v1 (default)` | 安装向导、后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2490) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step7/assign_master_controller.js:86` |  |  |  |  |  |
+| `restart.hostComponents` | `POST` | `/clusters/{clusterName}/requests` | `context`, `operation_level`, `resource_filters` | `/api/v1 (default)` | 主机、服务与配置、后台操作与通用能力 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2494) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js:552`<br>`ambari-web/classic/app/utils/batch_scheduled_requests.js:256` |  |  |  |  |  |
+| `restart.allServices` | `POST` | `/clusters/{clusterName}/requests` | `clusterName` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2512) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service.js:184` |  |  |  |  |  |
+| `restart.staleConfigs` | `POST` | `/clusters/{clusterName}/requests` | `clusterName` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2534) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service.js:349` |  |  |  |  |  |
+| `restart.custom.filter` | `POST` | `/clusters/{clusterName}/requests` | `context`, `filter` | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2556) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/federation/step4_controller.js:185` |  |  |  |  |  |
+| `bulk_request.decommission` | `POST` | `/clusters/{clusterName}/requests` | `clusterName`, `componentName`, `context`, `parameters`, `serviceName` | `/api/v1 (default)` | 主机、后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2578) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js:922` |  |  |  |  |  |
+| `bulk_request.hosts.passive_state` | `PUT` | `/clusters/{clusterName}/hosts` | `hostNames`, `passive_state`, `requestInfo` | `/api/v1 (default)` | 主机、Stack 与升级、Views、后台操作与通用能力 | 3 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2600) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js:502`<br>`ambari-web/classic/app/controllers/main/host/details.js:2342`<br>`ambari-web/classic/app/views/main/admin/stack_upgrade/custom_cluster_checks/custom_cluster_checks_maintenance_view.js:30` |  |  |  |  |  |
+| `bulk_request.hosts.update_rack_id` | `PUT` | `/clusters/{clusterName}/hosts` | `hostNames`, `rackId`, `requestInfo` | `/api/v1 (default)` | 主机、后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2622) |
+|  |  | 调用位置:`ambari-web/classic/app/utils/hosts.js:248` |  |  |  |  |  |
+| `bulk_request.hosts.all_components.passive_state` | `PUT` | `/clusters/{clusterName}/host_components` | `passive_state`, `query`, `requestInfo` | `/api/v1 (default)` | 主机、后台操作与通用能力 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2643) |
+| `views.info` | `GET` | `/views` | 无 `format()` | `/api/v1 (default)` | Views | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2663) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/views_controller.js:50` |  |  |  |  |  |
+| `views.instances` | `GET` | `/views?fields=versions/instances/ViewInstanceInfo,versions/ViewVersionInfo/label&versions/ViewVersionInfo/system=false` | 无 `format()` | `/api/v1 (default)` | Views | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2670) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/views_controller.js:61` |  |  |  |  |  |
+| `host.host_components.filtered` | `POST` | `/clusters/{clusterName}/hosts?{fields}` | `parameters` | `/api/v1 (default)` | 主机、后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2687) |
+|  |  | 调用位置:`ambari-web/classic/app/utils/batch_scheduled_requests.js:188` |  |  |  |  |  |
+| `host.stack_versions.install` | `POST` | `/clusters/{clusterName}/hosts/{hostName}/stack_versions` | `version` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2702) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/main/host/details/actions/install_new_version.js:42` |  |  |  |  |  |
+| `host.logging` | `GET` | `/clusters/{clusterName}/hosts/{hostName}?fields=host_components/logging,host_components/HostRoles/service_name{fields}{query}&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机、后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2719) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/update_controller.js:730` |  |  |  |  |  |
+| `components.filter_by_status` | `GET` | `/clusters/{clusterName}/components?fields=host_components/HostRoles/host_name,ServiceComponentInfo/component_name,ServiceComponentInfo/started_count{urlParams}&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2723) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/common/configs/configs_saver.js:881` |  |  |  |  |  |
+| `components.get_category` | `GET` | `/clusters/{clusterName}/components?fields=ServiceComponentInfo/component_name,ServiceComponentInfo/service_name,ServiceComponentInfo/category,ServiceComponentInfo/recovery_enabled,ServiceComponentInfo/total_count&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2727) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/service_auto_start.js:134` |  |  |  |  |  |
+| `components.get.staleConfigs` | `GET` | `/clusters/{clusterName}/components?host_components/HostRoles/stale_configs=true&fields=host_components/HostRoles/host_name&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2731) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/cluster_controller.js:264` |  |  |  |  |  |
+| `components.update` | `PUT` | `/clusters/{clusterName}/components?{urlParams}` | `ServiceComponentInfo`, `query` | `/api/v1 (default)` | 跨模块与待人工归类 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2736) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/service_auto_start.js:164` |  |  |  |  |  |
+| `hosts.all.install` | `GET` | `/hosts?minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2751) |
+| `hosts.all` | `GET` | `/clusters/{clusterName}/hosts?minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机、安全、高可用与联邦、Views、后台操作与通用能力 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2755) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/cluster_controller.js:461`<br>`ambari-web/classic/app/views/main/admin/highAvailability/nameNode/rollbackHA/step1_view.js:35` |  |  |  |  |  |
+| `hosts.with_public_host_names` | `GET` | `/clusters/{clusterName}/hosts?fields=Hosts/public_host_name&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2759) |
+| `hosts.for_quick_links` | `GET` | `/clusters/{clusterName}/hosts?Hosts/host_name.in({hosts})&fields=Hosts/public_host_name{urlParams}&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机、Views | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2763) |
+|  |  | 调用位置:`ambari-web/classic/app/views/common/quick_view_link_view.js:213` |  |  |  |  |  |
+| `hosts.confirmed.install` | `GET` | `/hosts?fields=Hosts/cpu_count,Hosts/disk_info,Hosts/total_mem,Hosts/maintenance_state&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2767) |
+| `hosts.confirmed` | `GET` | `/clusters/{clusterName}/hosts?fields=host_components/HostRoles/state&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 安装向导、主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2771) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard.js:1282` |  |  |  |  |  |
+| `hosts.with_searchTerm` | `POST` | `/clusters/{clusterName}/hosts?fields=Hosts/{facet}&minimal_response=true&page_size={page_size}` | `facet`, `searchTerm` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2775) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/combo_search_box.js:28` |  |  |  |  |  |
+| `hosts.confirmed.minimal` | `GET` | `/clusters/{clusterName}/hosts?fields=host_components/HostRoles/state&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机、Views | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2790) |
+|  |  | 调用位置:`ambari-web/classic/app/views/main/host/add_view.js:41` |  |  |  |  |  |
+| `hosts.heartbeat_lost` | `GET` | `/clusters/{clusterName}/hosts?Hosts/host_state=HEARTBEAT_LOST` | 无 `format()` | `/api/v1 (default)` | 主机、安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2794) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/kerberos/step3_controller.js:67` |  |  |  |  |  |
+| `host_components.all` | `GET` | `/clusters/{clusterName}/host_components?fields=HostRoles/host_name&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2798) |
+| `host_components.with_services_names` | `GET` | `/clusters/{clusterName}/host_components?fields=component/ServiceComponentInfo/service_name,HostRoles/host_name&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2802) |
+| `components.get_installed` | `GET` | `/clusters/{clusterName}/components` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2806) |
+| `custom_action.create` | `POST` | `/requests` | `filteredHosts`, `requestInfo` | `/api/v1 (default)` | 服务与配置、Views | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2819) |
+|  |  | 调用位置:`ambari-web/classic/app/views/common/configs/widgets/test_db_connection_widget_view.js:227`<br>`ambari-web/classic/app/views/common/controls_view.js:1369` |  |  |  |  |  |
+| `cluster.custom_action.create` | `POST` | `/clusters/{clusterName}/requests` | `filteredHosts`, `requestInfo` | `/api/v1 (default)` | 服务与配置、Views | 3 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2841) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js:664`<br>`ambari-web/classic/app/views/common/configs/widgets/test_db_connection_widget_view.js:227`<br>`ambari-web/classic/app/views/common/controls_view.js:1369` |  |  |  |  |  |
+| `custom_action.request` | `GET` | `/requests/{requestId}/tasks/{taskId}` | `requestId`, `taskId` | `/api/v1 (default)` | 服务与配置、Views、后台操作与通用能力 | 6 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2863) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js:682`<br>`ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js:706`<br>`ambari-web/classic/app/views/common/configs/widgets/test_db_connection_widget_view.js:247`<br>`ambari-web/classic/app/views/common/configs/widgets/test_db_connection_widget_view.js:268`<br>`ambari-web/classic/app/views/common/controls_view.js:1389`<br>`ambari-web/classic/app/views/common/controls_view.js: [...]
+| `hosts.high_availability.wizard` | `GET` | `/clusters/{clusterName}/hosts?fields=Hosts/cpu_count,Hosts/disk_info,Hosts/total_mem,Hosts/maintenance_state&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机、服务与配置、Views | 3 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2873) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/wizard/assign_master_components.js:605`<br>`ambari-web/classic/app/mixins/wizard/wizardHostsLoading.js:44`<br>`ambari-web/classic/app/views/main/service/reassign_view.js:38` |  |  |  |  |  |
+| `hosts.security.wizard` | `GET` | `/clusters/{clusterName}/hosts?fields=host_components/HostRoles/service_name&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机、安全、高可用与联邦 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2877) |
+| `host_component.installed.on_hosts` | `GET` | `/clusters/{clusterName}/host_components?HostRoles/component_name={componentName}&HostRoles/host_name.in({hostNames})&fields=HostRoles/host_name&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2881) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/wizard/wizardProgressPageController.js:418` |  |  |  |  |  |
+| `hosts.by_component.one` | `GET` | `/clusters/{clusterName}/hosts?host_components/HostRoles/component_name.in({componentNames})&fields=host_components,Hosts/cpu_count,Hosts/disk_info,Hosts/total_mem,Hosts/ip,Hosts/os_type,Hosts/os_arch,Hosts/public_host_name&page_size=1&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2885) |
+| `hosts.by_component.all` | `GET` | `/clusters/{clusterName}/hosts?host_components/HostRoles/component_name.in({componentNames})&fields=host_components,Hosts/cpu_count,Hosts/disk_info,Hosts/total_mem,Hosts/ip,Hosts/os_type,Hosts/os_arch,Hosts/public_host_name&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2889) |
+| `hosts.config_groups` | `GET` | `/clusters/{clusterName}/hosts?fields=Hosts/cpu_count,Hosts/disk_info,Hosts/total_mem,Hosts/ip,Hosts/os_type,Hosts/os_arch,Hosts/public_host_name,host_components&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机、服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2893) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/manage_config_groups_controller.js:308` |  |  |  |  |  |
+| `hosts.info.install` | `GET` | `/hosts?Hosts/host_name.in({hostNames})&fields=Hosts/cpu_count,Hosts/disk_info,Hosts/total_mem,Hosts/ip,Hosts/os_type,Hosts/os_arch,Hosts/public_host_name&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 主机、服务与配置 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2897) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/manage_config_groups_controller.js:1058`<br>`ambari-web/classic/app/mixins/wizard/assign_master_components.js:605` |  |  |  |  |  |
+| `hosts.ips` | `GET` | `/hosts?Hosts/host_name.in({hostNames})&fields=Hosts/ip` | 无 `format()` | `/api/v1 (default)` | 主机 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2901) |
+|  |  | 调用位置:`ambari-web/classic/app/mappers/service_metrics_mapper.js:335` |  |  |  |  |  |
+| `hosts.host_components.pre_load` | `GET` | `data.url [DYNAMIC_URL]` | `url` | `/api/v1 (default)` | 主机、后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2905) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/update_controller.js:473` |  |  |  |  |  |
+| `hosts.metrics.lazy_load` | `POST` | `data.url [DYNAMIC_URL]` | `parameters`, `url` | `/api/v1 (default)` | 主机、后台操作与通用能力 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2914) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/global/update_controller.js:405` |  |  |  |  |  |
+| `hosts.bulk.operations` | `POST` | `/clusters/{clusterName}/hosts?fields=Hosts/host_name,Hosts/host_state,Hosts/maintenance_state,host_components/HostRoles/state,host_components/HostRoles/maintenance_state,Hosts/total_mem,stack_versions/HostStackVersions,stack_versions/repository_versions/RepositoryVersions/repository_version,stack_versions/repository_versions/RepositoryVersions/id,host_components/HostRoles/stale_configshost_components/HostRoles/service_name&minimal_response=true` | `p [...]
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js:1097` |  |  |  |  |  |
+| `logtail.get` | `GET` | `/clusters/{clusterName}/logging/searchEngine?component_name={logComponentName}&host_name={hostName}&pageSize={pageSize}&startIndex={startIndex}` | 无 `format()` | `/api/v1 (default)` | Views | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2951) |
+|  |  | 调用位置:`ambari-web/classic/app/views/common/log_tail_view.js:135` |  |  |  |  |  |
+| `service.serviceConfigVersions.get` | `GET` | `/clusters/{clusterName}/configurations/service_config_versions?service_name={serviceName}&fields=service_config_version,user,hosts,group_id,group_name,is_current,createtime,service_name,service_config_version_note,stack_id,is_cluster_compatible&sortBy=service_config_version.desc&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2956) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/common/configs/configs_loader.js:49` |  |  |  |  |  |
+| `service.serviceConfigVersions.get.current` | `GET` | `/clusters/{clusterName}/configurations/service_config_versions?service_name.in({serviceNames})&is_current=true&fields=*` | 无 `format()` | `/api/v1 (default)` | 服务与配置 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2960) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/common/configs/configs_loader.js:110`<br>`ambari-web/classic/app/mixins/main/service/configs/hive_interactive_check.js:26` |  |  |  |  |  |
+| `service.serviceConfigVersions.get.current.not.default` | `GET` | `/clusters/{clusterName}/configurations/service_config_versions?is_current=true&group_id>0&fields=*` | 无 `format()` | `/api/v1 (default)` | 安装向导 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2964) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/wizard/step7_controller.js:421` |  |  |  |  |  |
+| `service.serviceConfigVersions.get.total` | `GET` | `/clusters/{clusterName}/configurations/service_config_versions?page_size=1&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2968) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/dashboard/config_history_controller.js:141` |  |  |  |  |  |
+| `service.serviceConfigVersion.get` | `GET` | `/clusters/{clusterName}/configurations/service_config_versions?service_name={serviceName}&service_config_version={serviceConfigVersion}` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2972) |
+| `service.serviceConfigVersions.get.multiple` | `GET` | `/clusters/{clusterName}/configurations/service_config_versions?(service_name={serviceName}%26service_config_version.in({serviceConfigVersions})){additionalParams}` | `additionalParams`, `serviceConfigVersions` | `/api/v1 (default)` | 服务与配置 | 2 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2976) |
+|  |  | 调用位置:`ambari-web/classic/app/mixins/common/configs/configs_comparator.js:393`<br>`ambari-web/classic/app/mixins/common/configs/configs_loader.js:177` |  |  |  |  |  |
+| `service.serviceConfigVersions.get.suggestions` | `GET` | `/clusters/{clusterName}/configurations/service_config_versions?fields={key}&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2986) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/dashboard/config_history_controller.js:210` |  |  |  |  |  |
+| `service.serviceConfigVersion.revert` | `PUT` | `/clusters/{clusterName}` | `data` | `/api/v1 (default)` | 服务与配置、Views | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2990) |
+|  |  | 调用位置:`ambari-web/classic/app/views/common/configs/config_versions_control_view.js:201` |  |  |  |  |  |
+| `service.mysql.clean` | `POST` | `/clusters/{clusterName}/requests` | `host` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3001) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js:486` |  |  |  |  |  |
+| `service.mysql.configure` | `POST` | `/clusters/{clusterName}/requests` | `host` | `/api/v1 (default)` | 服务与配置 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3021) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/service/reassign/step4_controller.js:507` |  |  |  |  |  |
+| `service.mysql.testHiveConnection` | `POST` | `/requests` | `db_connection_url`, `db_name`, `db_pass`, `db_user`, `hosts`, `java_home`, `jdk_location`, `jdk_name` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3041) |
+| `service.components.load` | `GET` | `/clusters/{clusterName}/services?fields=components&minimal_response=true` | 无 `format()` | `/api/v1 (default)` | 跨模块与待人工归类 | 0 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3216) |
+| `nameNode.federation.formatNameNode` | `POST` | `/clusters/{clusterName}/requests` | `host` | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3221) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/federation/step4_controller.js:119` |  |  |  |  |  |
+| `nameNode.federation.formatZKFC` | `POST` | `/clusters/{clusterName}/requests` | `host` | `/api/v1 (default)` | 安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3241) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/federation/step4_controller.js:131` |  |  |  |  |  |
+| `nameNode.federation.bootstrapNameNode` | `POST` | `/clusters/{clusterName}/requests` | `host` | `/api/v1 (default)` | 安装向导、安全、高可用与联邦 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3260) |
+|  |  | 调用位置:`ambari-web/classic/app/controllers/main/admin/federation/step4_controller.js:165` |  |  |  |  |  |
+| `hiveServerInteractive.getStatus` | `GET` | `http://:/leader [dynamic: 'http://' + data.hsiHost + ':' + data.port + '/leader'] [DYNAMIC_URL]` | `hsiHost`, `port` | `/api/v1 (default)` | 跨模块与待人工归类 | 1 | [source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3279) |
+|  |  | 调用位置:`ambari-web/classic/app/mappers/service_metrics_mapper.js:353` |  |  |  |  |  |
+
+## 已排除的 Metrics 请求
+
+- `hosts.metrics` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1089))
+- `hosts.metrics.host_component` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1093))
+- `service.metrics.flume.channel_fill_percent` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1097))
+- `service.metrics.flume.channel_size` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1102))
+- `service.metrics.flume.sink_drain_success` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1107))
+- `service.metrics.flume.sink_connection_failed` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1112))
+- `service.metrics.flume.source_accepted` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1117))
+- `service.metrics.flume.channel_size_for_all` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1122))
+- `service.metrics.flume.channel_size_for_all.mma` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1125))
+- `service.metrics.flume.channel_size_for_all.sum` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1128))
+- `service.metrics.flume.gc` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1131))
+- `service.metrics.flume.jvm_heap_used` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1136))
+- `service.metrics.flume.jvm_threads_runnable` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1141))
+- `service.metrics.flume.cpu_user` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1146))
+- `service.metrics.flume.incoming_event_put_successCount` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1151))
+- `service.metrics.flume.incoming_event_put_successCount.mma` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1154))
+- `service.metrics.flume.incoming_event_put_successCount.sum` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1157))
+- `service.metrics.flume.outgoing_event_take_success_count` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1160))
+- `service.metrics.flume.outgoing_event_take_success_count.mma` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1163))
+- `service.metrics.flume.outgoing_event_take_success_count.sum` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1166))
+- `service.metrics.hbase.cluster_requests` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1169))
+- `service.metrics.hbase.hlog_split_size` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1174))
+- `service.metrics.hbase.hlog_split_time` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1179))
+- `service.metrics.hbase.regionserver_queuesize` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1184))
+- `service.metrics.hbase.regionserver_regions` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1189))
+- `service.metrics.hbase.regionserver_rw_requests` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1194))
+- `service.metrics.ambari_metrics.master.average_load` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1199))
+- `service.metrics.ambari_metrics.region_server.store_files` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1204))
+- `service.metrics.ambari_metrics.region_server.regions` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1209))
+- `service.metrics.ambari_metrics.region_server.request` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1214))
+- `service.metrics.ambari_metrics.region_server.block_cache_hit_percent` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1219))
+- `service.metrics.ambari_metrics.region_server.compaction_queue_size` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1224))
+- `service.metrics.ambari_metrics.aggregated` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1229))
+- `service.metrics.hdfs.block_status` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1234))
+- `service.metrics.hdfs.file_operations` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1239))
+- `service.metrics.hdfs.gc` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1244))
+- `service.metrics.hdfs.io` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1249))
+- `service.metrics.hdfs.jvm_heap` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1254))
+- `service.metrics.hdfs.jvm_threads` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1259))
+- `service.metrics.hdfs.rpc` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1264))
+- `service.metrics.hdfs.space_utilization` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1269))
+- `service.metrics.yarn.gc` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1274))
+- `service.metrics.yarn.jobs_threads` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1279))
+- `service.metrics.yarn.rpc` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1284))
+- `service.metrics.yarn.jobs_heap` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1289))
+- `service.metrics.yarn.queue.allocated` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1294))
+- `service.metrics.yarn.queue.allocated.container` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1299))
+- `service.metrics.yarn.node.manager.statuses` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1304))
+- `service.metrics.yarn.queue.memory.resource` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1309))
+- `service.metrics.yarn.queue.apps.states.current` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1328))
+- `service.metrics.yarn.queue.apps.states.finished` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1333))
+- `service.metrics.kafka.broker.topic` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1338))
+- `service.metrics.kafka.controller.KafkaController` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1342))
+- `service.metrics.kafka.controller.ControllerStats` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1346))
+- `service.metrics.kafka.log.LogFlushStats` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1350))
+- `service.metrics.kafka.server.ReplicaManager` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1354))
+- `service.metrics.kafka.server.ReplicaFetcherManager` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1358))
+- `service.metrics.storm.nimbus` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1362))
+- `dashboard.cluster_metrics.cpu` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1366))
+- `dashboard.cluster_metrics.load` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1371))
+- `dashboard.cluster_metrics.memory` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1376))
+- `dashboard.cluster_metrics.network` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1381))
+- `host.metrics.aggregated` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1386))
+- `host.metrics.cpu` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1390))
+- `host.metrics.disk` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1395))
+- `host.metrics.load` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1400))
+- `host.metrics.memory` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1405))
+- `host.metrics.network` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1410))
+- `host.metrics.processes` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L1415))
+- `host.host_component.flume.metrics` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2674))
+- `host.host_component.flume.metrics.timeseries` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2678))
+- `hosts.heatmaps` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2810))
+- `namenode.cpu_wio` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L2814))
+- `widgets.get` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3069))
+- `widgets.all.shared.get` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3074))
+- `widgets.all.mine.get` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3079))
+- `widgets.layout.stackDefined.get` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3084))
+- `widget.layout.id.get` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3089))
+- `widget.layout.name.get` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3094))
+- `widget.layout.delete` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3099))
+- `widget.layout.get` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3104))
+- `widget.layout.edit` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3109))
+- `widget.layout.create` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3120))
+- `widgets.layout.userDefined.get` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3131))
+- `widgets.layouts.get` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3136))
+- `widgets.layouts.active.get` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3141))
+- `widgets.layouts.all.active.get` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3146))
+- `widget.activelayouts.edit` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3152))
+- `widget.action.delete` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3164))
+- `widgets.serviceComponent.metrics.get` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3174))
+- `widgets.hostComponent.metrics.get` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3179))
+- `widgets.hosts.metrics.get` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3184))
+- `widgets.wizard.metrics.get` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3189))
+- `widgets.wizard.add` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3194))
+- `widgets.wizard.edit` ([source](../../../../ambari-web/classic/app/utils/ajax/ajax.js#L3205))
diff --git a/docs/frontend-refactor/ember-baseline/generated/api-by-module/auth-shell.md b/docs/frontend-refactor/ember-baseline/generated/api-by-module/auth-shell.md
new file mode 100644
index 0000000000..a968f95189
--- /dev/null
+++ b/docs/frontend-refactor/ember-baseline/generated/api-by-module/auth-shell.md
@@ -0,0 +1,21 @@
+# 认证与应用外壳:Ember 非 Metrics 命名 AJAX 候选索引
+
+> 由 `tools/extract-ember-baseline.mjs` 生成。该页只按请求名和调用者路径的宽正则启发式归类:共享请求可能跨模块混入或重复,模块请求也可能漏列或归到其他页。它不是模块接口全集;权威核对必须联合 `../ajax-endpoints.json`、`../ajax-calls.json`、`../direct-http-calls.json`、`../browser-network-entrypoints.json` 和 `../realtime-channels.json`。
+
+共 12 个命名请求候选。
+候选内容 SHA-256:`9a1933e4507e2d4ce46b4203ec35925e485fcaab947a02a1f92b81dd280cd464`。
+
+| 请求名 | Method | URL(不含默认 prefix) | format 输入键 | 调用位置 |
+| --- | --- | --- | --- | --- |
+| `common.service.configurations` | `PUT` | `/clusters/{clusterName}` | `desired_config` | `ambari-web/classic/app/controllers/main/admin/federation/routerBasedFederation/step3_controller.js:97`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/activateStandby/step3_controller.js:94`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/addStandby/step4_controller.js:73`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/remove [...]
+| `common.service.multiConfigurations` | `PUT` | `/clusters/{clusterName}` | `configs` | `ambari-web/classic/app/controllers/main/admin/federation/routerBasedFederation/step4_controller.js:55`<br>`ambari-web/classic/app/controllers/main/admin/federation/step4_controller.js:77`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step9_controller.js:228`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/rangerAdmin/step4_controller.js:88` |
+| `ambari.service.load_server_version` | `GET` | `/services/AMBARI?fields=components/RootServiceComponents/component_version&components/RootServiceComponents/component_name=AMBARI_SERVER&minimal_response=true` | 无 `format()` | `ambari-web/classic/app/router.js:632`<br>`ambari-web/classic/app/router.js:977`<br>`ambari-web/classic/app/views/main/admin/stack_upgrade/versions_view.js:221` |
+| `config.tags` | `GET` | `/clusters/{clusterName}?fields=Clusters/desired_configs` | 无 `format()` | `ambari-web/classic/app/controllers/global/configuration_controller.js:145`<br>`ambari-web/classic/app/controllers/main/admin/federation/routerBasedFederation/step3_controller.js:51`<br>`ambari-web/classic/app/controllers/main/admin/federation/step3_controller.js:54`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/hawq/activateStandby/step3_controller.js:63`<br>`ambari- [...]
+| `admin.get.all_configurations` | `GET` | `/clusters/{clusterName}/configurations?{urlParams}` | 无静态 `data.*` 引用 | `ambari-web/classic/app/controllers/main/admin/federation/routerBasedFederation/step3_controller.js:63`<br>`ambari-web/classic/app/controllers/main/admin/federation/step3_controller.js:73`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/journalNode/step2_controller.js:81`<br>`ambari-web/classic/app/controllers/main/admin/highAvailability/nameNode/step3_co [...]
+| `router.login` | `POST` | `/auth` | 无静态 `data.*` 引用 | `ambari-web/classic/app/router.js:422` |
+| `router.afterLogin` | `GET` | `/users/{loginName}?fields=*,privileges/PrivilegeInfo/cluster_name,privileges/PrivilegeInfo/permission_name` | 无静态 `data.*` 引用 | `ambari-web/classic/app/router.js:297`<br>`ambari-web/classic/app/router.js:443` |
+| `router.user.privileges` | `GET` | `/users/{userName}/privileges?fields=*` | 无 `format()` | `ambari-web/classic/app/controllers/global/user_settings_controller.js:202` |
+| `router.user.authorizations` | `GET` | `/users/{userName}/authorizations?fields=*` | 无 `format()` | `ambari-web/classic/app/controllers/global/cluster_controller.js:420` |
+| `router.login.clusters` | `GET` | `/clusters?fields=Clusters/provisioning_state,Clusters/security_type,Clusters/version,Clusters/cluster_id` | 无 `format()` | `ambari-web/classic/app/controllers/application.js:125`<br>`ambari-web/classic/app/router.js:275`<br>`ambari-web/classic/app/router.js:572` |
+| `router.login.message` | `GET` | `/settings/motd` | 无 `format()` | `ambari-web/classic/app/router.js:464` |
+| `router.logoff` | `GET` | `/logout` | 无静态 `data.*` 引用 | `ambari-web/classic/app/router.js:738` |
diff --git a/docs/frontend-refactor/ember-baseline/generated/browser-network-entrypoints.md b/docs/frontend-refactor/ember-baseline/generated/browser-network-entrypoints.md
new file mode 100644
index 0000000000..8331b0c905
--- /dev/null
+++ b/docs/frontend-refactor/ember-baseline/generated/browser-network-entrypoints.md
@@ -0,0 +1,66 @@
+# Ember 非 Metrics 浏览器网络与下载入口
+
+> 由 `tools/extract-ember-baseline.mjs` 生成。每个命令式入口、整页 reload/redirect、View iframe/icon 和非 Metrics 声明式导航链接均已人工分类。生成器遇到新入口而没有语义契约时会直接失败。普通启动脚本、样式表、favicon 和固定 UI 图片属于构建/静态资源,不在本目录范围。
+
+共 56 个候选调用点。
+
+| 语义 | 网络效果 | URL / 内容契约 | 表达式 | 边界 | 位置 |
+| --- | --- | --- | --- | --- | --- |
+| `STATIC_EXTERNAL_LINK`<br>target=_blank anchor | `NAVIGATION_REQUEST` | `http://www.apache.org/licenses/LICENSE-2.0` | `<a data-qa="license-link" href="http://www.apache.org/licenses/LICENSE-2.0" target="_blank" rel="noopener noreferrer">` | 经典应用 footer 的 Apache License 外链。 | [source](../../../../ambari-web/classic/app/assets/index.html#L48) |
+| `STATIC_NOTICE_DOCUMENT`<br>target=_blank anchor | `REMOTE_REQUEST` | `GET /licenses/NOTICE.txt` | `<a data-qa="third-party-link" href="/licenses/NOTICE.txt" target="_blank" rel="noopener noreferrer">` | 经典应用 footer 打开同源第三方 NOTICE 静态文档。 | [source](../../../../ambari-web/classic/app/assets/index.html#L49) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after show_bg/timezone preference requests settle` | `location.reload();` | 两个 preference mutation 都用 \`.always()\` 串联;成功或失败均可 reload 以应用 timezone。 | [source](../../../../ambari-web/classic/app/controllers/global/user_settings_controller.js#L327) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `delayed reload of current Ambari document after route/status change` | `location.reload();` | cluster 已安装时,route 或 install status observer 延迟 \`App.pageReloadTime\` 后 reload。 | [source](../../../../ambari-web/classic/app/controllers/main.js#L122) |
+| `LOCAL_DOCUMENT`<br>window.open | `LOCAL_ONLY` | `about:blank; writes generated HTML configuration diff` | `window.open();` | 新窗口只接收内存中生成的 HTML,不请求后端。 | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L2329) |
+| `PAGE_RELOAD`<br>window.location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after starting repeat-tolerance config save` | `window.location.reload();` | 配置 PUT 不被 await;保存请求仍在途时立即整页 reload,可能中断或隐藏失败结果。 | [source](../../../../ambari-web/classic/app/controllers/main/alerts/alert_definitions_actions_controller.js#L251) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after bulk host-delete result closes` | `location.reload();` | 成功/部分失败结果都整页刷新,先清除已删除 host selection。 | [source](../../../../ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js#L453) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after bulk host-component-delete result primary action` | `location.reload();` | 结果 popup 的 Primary 与 Close 各有独立 reload 调用。 | [source](../../../../ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js#L792) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after bulk host-component-delete result closes` | `location.reload();` | 结果 popup 的 Close 分支整页重建 host/component 模型。 | [source](../../../../ambari-web/classic/app/controllers/main/host/bulk_operations_controller.js#L797) |
+| `PAGE_RELOAD`<br>window.location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after service deletion confirmation` | `window.location.reload();` | 用户关闭删除成功确认后整页 reload,以 REST 启动链重新构建 cluster/service 模型。 | [source](../../../../ambari-web/classic/app/controllers/main/service/item.js#L2042) |
+| `INTERNAL_AMBARI_ROUTE`<br>window.open | `NAVIGATION_REQUEST` | `#/main/view/{viewName}/{shortUrl} or #/main/views/{viewName}/{version}/{instanceName}` | `window.open(event.context.get('internalAmbariUrl'));` | 新浏览上下文打开 Ember hash route,随后由 View details iframe 请求 context。 | [source](../../../../ambari-web/classic/app/controllers/main/views_controller.js#L112) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after generic long-running wizard exit cleanup` | `location.reload();` | HA/Federation 等共享 \`exitWizard()\` 在 status persist settle 后 transition 并 reload。 | [source](../../../../ambari-web/classic/app/controllers/wizard.js#L1528) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document from the stale-page popup link` | `location.reload();'>"` | reload 调用位于运行时编译的 inline onclick;用户点击时执行。 | [source](../../../../ambari-web/classic/app/mixins/common/reload_popup.js#L63) |
+| `AMBARI_REST_DOWNLOAD`<br>window.open | `REMOTE_REQUEST` | `GET one of the five generated client-config contracts (\`?format=client_config_tar\`)` | `window.open(url);` | 精确 resource-scope URL 见 client-config-downloads 清单。 | [source](../../../../ambari-web/classic/app/mixins/main/host/details/support_client_configs_download.js#L39) |
+| `JWT_PROVIDER_REDIRECT`<br>redirectByURL call | `NAVIGATION_REQUEST` | `{401/403 response jwtProviderUrl}{encoded current window URL}{redirected query flag}` | `this.redirectByURL(responseJson.jwtProviderUrl + encodeURIComponent(this.getCurrentLocationUrl()));` | 认证失败响应驱动的外部 IdP 整页跳转;redirect counter 仅限制循环,不约束 provider origin。 | [source](../../../../ambari-web/classic/app/router.js#L349) |
+| `ADMIN_VIEW_REDIRECT`<br>replaceWindowLocation call | `NAVIGATION_REQUEST` | `{appURLRoot}views/ADMIN_VIEW/{lexicallyLatestServerComponentVersion}/INSTANCE/#/` | `App.replaceWindowLocation(App.appURLRoot + 'views/ADMIN_VIEW/' + latestVersion + '/INSTANCE/#/')` | 无 cluster 的 Admin View 分流;location.replace 整页离开 Ember。 | [source](../../../../ambari-web/classic/app/router.js#L669) |
+| `PAGE_RELOAD`<br>window.location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after logout when cluster model was loaded` | `window.location.reload();` | 先 transition 到 login,下一 run-loop 再整页 reload。 | [source](../../../../ambari-web/classic/app/router.js#L773) |
+| `PREFERRED_PATH_REDIRECT`<br>window.location assignment | `NAVIGATION_REQUEST` | `{saved preferredPath beginning with / or #, except a path containing #/login}` | `window.location = preferredPath` | \`startsWith("/")\` 也接受 \`//host/path\`,因此旧实现可能发生 protocol-relative 跨源导航;React 应按已知安全缺陷处理。 | [source](../../../../ambari-web/classic/app/router.js#L806) |
+| `WINDOW_LOCATION_SETTER`<br>window.location assignment | `NAVIGATION_REQUEST` | `window.location={callerSuppliedUrl}` | `window.location = url` | JWT redirect helper 的最终可测试 setter;实际 provider 数据入口另列。 | [source](../../../../ambari-web/classic/app/router.js#L889) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after closing an unfinished Activate HAWQ Standby wizard` | `location.reload();` | cluster status persist 以 \`alwaysCallback\` 收尾,失败也 transition 后 reload。 | [source](../../../../ambari-web/classic/app/routes/activate_hawq_standby_routes.js#L56) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after completing Activate HAWQ Standby` | `location.reload();` | 完成清理 persist 以 \`alwaysCallback\` 收尾。 | [source](../../../../ambari-web/classic/app/routes/activate_hawq_standby_routes.js#L178) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after Add Alert Definition completes` | `location.reload();` | 清理向导状态后 transition 到 Alerts;status persist 失败也 reload。 | [source](../../../../ambari-web/classic/app/routes/add_alert_definition_routes.js#L141) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after closing an unfinished Add HAWQ Standby wizard` | `location.reload();` | cluster status persist 以 \`alwaysCallback\` 收尾,失败也 reload。 | [source](../../../../ambari-web/classic/app/routes/add_hawq_standby_routes.js#L57) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after completing Add HAWQ Standby` | `location.reload();` | 完成清理 persist 以 \`alwaysCallback\` 收尾。 | [source](../../../../ambari-web/classic/app/routes/add_hawq_standby_routes.js#L202) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after Disable Kerberos closes` | `location.reload();` | Disable 清理和 cluster status persist 以 \`alwaysCallback\` 收尾,随后返回 Kerberos 页并 reload。 | [source](../../../../ambari-web/classic/app/routes/main.js#L549) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after Ranger Admin HA completes` | `location.reload();` | 完成清理 persist 以 \`alwaysCallback\` 收尾。 | [source](../../../../ambari-web/classic/app/routes/ra_high_availability_routes.js#L179) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after Reassign Master completes` | `location.reload();` | cluster status persist 以 \`alwaysCallback\` 收尾,失败也 transition 后 reload。 | [source](../../../../ambari-web/classic/app/routes/reassign_master_routes.js#L314) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after confirmed close of an active Remove HAWQ Standby wizard` | `location.reload();` | 确认关闭后清理状态;persist 成功或失败都 reload。 | [source](../../../../ambari-web/classic/app/routes/remove_hawq_standby_routes.js#L63) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after closing an inactive Remove HAWQ Standby wizard` | `location.reload();` | 非活动阶段无需确认,但同样在 persist settle 后 reload。 | [source](../../../../ambari-web/classic/app/routes/remove_hawq_standby_routes.js#L81) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after completing Remove HAWQ Standby` | `location.reload();` | 完成清理 persist 以 \`alwaysCallback\` 收尾。 | [source](../../../../ambari-web/classic/app/routes/remove_hawq_standby_routes.js#L189) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `reload current Ambari document after NameNode HA rollback completes` | `location.reload();` | 本分支不等待 cluster status persist,transition 后下一 run-loop 直接 reload。 | [source](../../../../ambari-web/classic/app/routes/rollbackHA_routes.js#L154) |
+| `PAGE_RELOAD`<br>location.reload | `NAVIGATION_REQUEST` | `conditionally reload current Ambari document when closing an upgrade wizard` | `location.reload();` | 只在全局 upgrade state 为 NOT_REQUIRED 或 COMPLETED 时 reload 并 reset wizard owner。 | [source](../../../../ambari-web/classic/app/routes/stack_upgrade_routes.js#L83) |
+| `NEW_UI_NAVIGATION`<br>new UI anchor | `NAVIGATION_REQUEST` | `GET /latest/#` | `<a href="/latest/#">` | 用户菜单的 Switch Experience 整页导航到新版前端入口。 | [source](../../../../ambari-web/classic/app/templates/application.hbs#L70) |
+| `STATIC_EXTERNAL_LINK`<br>target=_blank anchor | `NAVIGATION_REQUEST` | `http://ambari.apache.org/` | `<a href="http://ambari.apache.org/" target="_blank" rel="noopener noreferrer">` | About 弹窗静态项目链接。 | [source](../../../../ambari-web/classic/app/templates/common/about.hbs#L30) |
+| `STATIC_EXTERNAL_LINK`<br>target=_blank anchor | `NAVIGATION_REQUEST` | `http://www.apache.org/licenses/LICENSE-2.0` | `<a href="http://www.apache.org/licenses/LICENSE-2.0" target="_blank" rel="noopener noreferrer">` | About 弹窗静态 license 链接。 | [source](../../../../ambari-web/classic/app/templates/common/about.hbs#L32) |
+| `EXTERNAL_LOG_SEARCH`<br>target=_blank anchor | `NAVIGATION_REQUEST` | `{Log Search UI quick-link URL}{hostLog.linkTail}` | `<a {{bindAttr href="view.formatedLink" class=":pull-right view.isLodaded::disabled"}} target="_blank" rel="noopener noreferrer">` | 依赖 Log Search service quick-link descriptor 和已加载日志上下文。 | [source](../../../../ambari-web/classic/app/templates/common/host_progress_popup.hbs#L345) |
+| `EXTERNAL_LOG_SEARCH`<br>target=_blank anchor | `NAVIGATION_REQUEST` | `{Log Search quick-link template}/#/logs/serviceLogs;{component/host/file query}` | `<a class="open-in-log-search" {{bindAttr href="view.logSearchUrl"}} target="_blank" rel="noopener noreferrer">` | 在 Log Search service 和 quick-link 可用时显示。 | [source](../../../../ambari-web/classic/app/templates/common/modal_popups/log_tail_popup.hbs#L32) |
+| `EXTERNAL_ALERT_HELP`<br>target=_blank anchor | `NAVIGATION_REQUEST` | `{AlertDefinition.help_url}` | `<a {{bindAttr href="controller.content.helpUrl"}} target="_blank" rel="noopener noreferrer">` | 只在定义有 help URL 时显示,URL 由后端数据提供。 | [source](../../../../ambari-web/classic/app/templates/main/alerts/definition_details.hbs#L196) |
+| `EXTERNAL_LOG_SEARCH`<br>target=_blank anchor | `NAVIGATION_REQUEST` | `{Log Search UI quick-link URL}{file.linkTail}` | `<a {{bindAttr href="view.formatedLink"}} target="_blank" rel="log-file-name-tooltip noopener noreferrer" {{translateAttr title="popup.logTail.openInLogSearch"}} class="pull-righ...` | 主机日志页按文件构造 Log Search 深链。 | [source](../../../../ambari-web/classic/app/templates/main/host/logs.hbs#L46) |
+| `EXTERNAL_QUICK_LINK`<br>target=_blank anchor | `NAVIGATION_REQUEST` | `{stack quicklinks descriptor + current configs + selected public host/protocol/port}` | `<a {{bindAttr href="url"}} target="_blank" rel="noopener noreferrer">` | 多 master/group 的服务 Quick Links。 | [source](../../../../ambari-web/classic/app/templates/main/service/info/summary.hbs#L103) |
+| `EXTERNAL_QUICK_LINK`<br>target=_blank anchor | `NAVIGATION_REQUEST` | `{stack quicklinks descriptor + current configs + selected public host/protocol/port}` | `<a {{bindAttr href="url"}} target="_blank" rel="noopener noreferrer">` | 单 host/平铺的服务 Quick Links。 | [source](../../../../ambari-web/classic/app/templates/main/service/info/summary.hbs#L111) |
+| `INTERNAL_AMBARI_ROUTE`<br>target=_blank anchor | `CONDITIONAL` | `{ViewInstance.internalAmbariUrl}` | `<a href="#" target="_blank" {{action goToView link.viewInstance target="controller"}}>` | 遗留 Hive-to-View hook;当前 \`viewsToShow\` 为空,默认无可见链接,有 runtime extension 时才路由。 | [source](../../../../ambari-web/classic/app/templates/main/service/services/hive.hbs#L54) |
+| `VIEW_ICON_RESOURCE`<br>dynamic image src | `REMOTE_REQUEST` | `GET {ViewInstanceInfo.icon_path}` | `<img {{bindAttr src="instance.iconPath"}}>` | 由后端 View instance metadata 驱动的动态图片请求;不是固定构建图片。 | [source](../../../../ambari-web/classic/app/templates/main/views.hbs#L28) |
+| `URL_PARSER_NO_NETWORK`<br>anchor href | `NO_NETWORK` | `none; assigns caller URL to a detached anchor to read \`.hostname\`` | `.href = url` | 只借浏览器 URL parser 解析 hostname。 | [source](../../../../ambari-web/classic/app/utils/configs/database.js#L223) |
+| `LOCAL_DOCUMENT`<br>window.open | `LOCAL_ONLY` | `about:blank; writes caller-provided text/HTML` | `window.open('');` | 本地内容预览。 | [source](../../../../ambari-web/classic/app/utils/file_utils.js#L76) |
+| `LOCAL_DOWNLOAD`<br>anchor href | `LOCAL_ONLY` | `data:attachment/{fileType};charset=utf-8,{encodedData}` | `.href = file` | Safari fallback 的本地 data URL 下载。 | [source](../../../../ambari-web/classic/app/utils/file_utils.js#L91) |
+| `ADMIN_VIEW_REDIRECT_HELPER`<br>location.replace | `NAVIGATION_REQUEST` | `window.location.replace({callerSuppliedLocation})` | `window.location.replace(location);` | 可测试的 helper 实现;真实业务调用点另列。 | [source](../../../../ambari-web/classic/app/utils/helper.js#L1115) |
+| `LOCAL_DOCUMENT`<br>window.open | `LOCAL_ONLY` | `about:blank; writes selected task/component log text` | `window.open();` | 不发新后端请求。 | [source](../../../../ambari-web/classic/app/views/common/host_progress_popup_body_view.js#L1044) |
+| `LOCAL_DOCUMENT`<br>window.open | `LOCAL_ONLY` | `about:blank; writes already-loaded log tail` | `window.open();` | 不发新后端请求。 | [source](../../../../ambari-web/classic/app/views/common/modal_popups/log_tail_popup.js#L57) |
+| `LOCAL_DOCUMENT`<br>window.open | `LOCAL_ONLY` | `about:blank; writes task log HTML already in the DOM` | `window.open();` | 不发新后端请求。 | [source](../../../../ambari-web/classic/app/views/common/modal_popups/logs_popup.js#L43) |
+| `LOCAL_DOCUMENT`<br>window.open | `LOCAL_ONLY` | `about:blank; writes failed-host JSON` | `window.open().document;` | 不发新后端请求。 | [source](../../../../ambari-web/classic/app/views/main/admin/stack_upgrade/failed_hosts_modal_view.js#L77) |
+| `LOCAL_DOCUMENT`<br>window.open | `LOCAL_ONLY` | `about:blank; writes already-loaded upgrade task log` | `window.open(),` | 不发新后端请求。 | [source](../../../../ambari-web/classic/app/views/main/admin/stack_upgrade/upgrade_task_view.js#L174) |
+| `ADMIN_VIEW_REDIRECT`<br>replaceWindowLocation call | `NAVIGATION_REQUEST` | `{appURLRoot}views/ADMIN_VIEW/{lexicallyLatestServerComponentVersion}/INSTANCE/#/` | `App.replaceWindowLocation(App.appURLRoot + 'views/ADMIN_VIEW/' + latestVersion + '/INSTANCE/#/')` | Manage Versions 确认后整页跳转 Admin View。 | [source](../../../../ambari-web/classic/app/views/main/admin/stack_upgrade/versions_view.js#L233) |
+| `VIEW_WEB_CONTEXT`<br>iframe computed src | `REMOTE_REQUEST` | `{window.location.protocol}//{window.location.host}{ViewInstanceInfo.context_path}/{viewPath}` | `src: function() {` | 同源 View application iframe;不是 \`/api/v1\` REST。 | [source](../../../../ambari-web/classic/app/views/main/views/details.js#L85) |
+| `LOCAL_DOCUMENT`<br>window.open | `LOCAL_ONLY` | `about:blank; writes bootstrap warning details` | `window.open('');` | 不发新后端请求。 | [source](../../../../ambari-web/classic/app/views/wizard/step3/hostWarningPopupBody_view.js#L480) |
+| `LOCAL_DOCUMENT`<br>window.open | `LOCAL_ONLY` | `about:blank; writes already-loaded install task logs` | `window.open();` | 不发新后端请求。 | [source](../../../../ambari-web/classic/app/views/wizard/step9/hostLogPopupBody_view.js#L197) |
+
+网络效果分类:`CONDITIONAL`、`LOCAL_ONLY`、`NAVIGATION_REQUEST`、`NO_NETWORK`、`REMOTE_REQUEST`。`LOCAL_ONLY` 和 `NO_NETWORK` 不是后端接口,但仍是 React 需保留或明确变更的用户行为。
diff --git a/docs/frontend-refactor/ember-baseline/generated/direct-http-calls.md b/docs/frontend-refactor/ember-baseline/generated/direct-http-calls.md
new file mode 100644
index 0000000000..d379bfda04
--- /dev/null
+++ b/docs/frontend-refactor/ember-baseline/generated/direct-http-calls.md
@@ -0,0 +1,29 @@
+# Ember 非 Metrics 直接 HTTP 调用
+
+> 由 `tools/extract-ember-baseline.mjs` 生成。这些调用绕过 `App.ajax` 命名注册表;每条已人工还原业务语义、URL 契约和本基线保留的运维字段。生成器遇到新调用而没有语义契约时会直接失败。
+
+共 19 个静态调用点。
+
+| 语义 | Method / 范围 | URL 契约 | 保留字段 | 行为/边界 | 位置 |
+| --- | --- | --- | --- | --- | --- |
+| `CLUSTER_MODEL_LOAD`<br>HttpClient | `GET` / `NON_METRICS` | `GET /api/v1/clusters/{clusterName}?fields=Clusters` | `Clusters/*` | 初始加载 cluster identity/security/credential-store 等基础模型;complete 在 mapper 后刷新 isCredentialStorePersistent。<br>调用:`App.HttpClient.get(clusterUrl, App.clusterMapper, {` | [source](../../../../ambari-web/classic/app/controllers/global/cluster_controller.js#L194) |
+| `DYNAMIC_HOST_MODEL_LOAD_HELPER`<br>HttpClient | `GET` / `NON_METRICS` | `GET {callerSuppliedRealUrl}; test mode uses /data/hosts/HDP2/hosts.json` | `caller supplied` | \`requestHosts()\` 在经典 app 树中无生产调用者,只有 controller unit test;保留为 STATIC_ONLY 遗留 helper。<br>调用:`App.HttpClient.get(url, App.hostsMapper, {` | [source](../../../../ambari-web/classic/app/controllers/global/cluster_controller.js#L384) |
+| `CLUSTER_MODEL_REFRESH`<br>HttpClient | `GET` / `NON_METRICS` | `GET /api/v1/clusters/{clusterName}?fields=Clusters` | `Clusters/*` | 运行期刷新 cluster mapper;complete callback 为空。<br>调用:`App.HttpClient.get(clusterUrl, App.clusterMapper, {` | [source](../../../../ambari-web/classic/app/controllers/global/cluster_controller.js#L449) |
+| `ORIGINAL_REQUEST_REPLAY`<br>jQuery AJAX | `DYNAMIC` / `NON_METRICS` | `{ajaxOpt.type} {ajaxOpt.url}; payload/headers/callbacks are the original failed jQuery request` | `original request dependent` | 先保存 KDC credential,再原样重放 \`ajaxOpt\`;不是新 endpoint,也不得丢失原 success/error/statusCode 处理。<br>调用:`$.ajax(ajaxOpt);` | [source](../../../../ambari-web/classic/app/controllers/global/cluster_controller.js#L485) |
+| `HOST_MODEL_LOAD`<br>HttpClient | `GET` / `MIXED` | `GET-over-POST /api/v1/clusters/{clusterName}/hosts?fields={runtimeFields}&minimal_response=true&{pagination}&{sort}; RequestInfo.query={hostPredicate}` | `Hosts rack/name/maintenance/public_host/cpu_count/ph_cpu_count/status/state/heartbeat/ip/os/agent-env`<br>`host component state/maintenance/stale-config/service/display/desired-admin-state`<br>`stack/repository version identity`<br>`logging metadata when supports.logSearch`<br>`Nam [...]
+| `COMPONENT_TOPOLOGY_LOAD`<br>HttpClient | `GET` / `MIXED` | `GET /api/v1/clusters/{clusterName}/components/?{runtimeComponentPredicates}&fields={runtimeFields}&minimal_response=true` | `service/component identity and master/client topology`<br>`host/display/public-host/state/maintenance/stale-config/ha-state/desired-admin-state`<br>`HDFS ClusterId and HBase IsActiveMaster operational selectors` | 共用 serviceMetricsMapper 且后续请求会携带大量指标;本基线只保留 topology/state/HA/Active-Standby 等运维语义。<br>调用: [...]
+| `SERVICE_STATE_LOAD`<br>HttpClient | `GET` / `NON_METRICS` | `GET /api/v1/clusters/{clusterName}/services?fields=ServiceInfo/state,ServiceInfo/maintenance_state,ServiceInfo/desired_repository_version_id,components/ServiceComponentInfo/component_name&minimal_response=true` | `service state/maintenance/desired repository version`<br>`component names` | 集群初始化的 service 模型加载。<br>调用:`App.HttpClient.get(componentConfigUrl, App.serviceMapper, {` | [source](../../../../ambari-web/classic/app/co [...]
+| `COMPONENT_STATE_LOAD`<br>HttpClient | `GET` / `NON_METRICS` | `GET /api/v1/clusters/{clusterName}/components/?fields=ServiceComponentInfo/{service_name,category,installed_count,started_count,init_count,install_failed_count,unknown_count,total_count,display_name},host_components/HostRoles/host_name&minimal_response=true` | `component category and aggregate state counts`<br>`host component host names` | 用于服务组件状态和拓扑映射。<br>调用:`App.HttpClient.get(url, App.componentsStateMapper, {` | [sourc [...]
+| `ALERT_DEFINITION_LOAD`<br>HttpClient | `GET` / `NON_METRICS` | `GET /api/v1/clusters/{clusterName}/alert_definitions?fields=AlertDefinition/{component_name,description,enabled,repeat_tolerance,repeat_tolerance_enabled,id,ignore_host,interval,label,name,scope,service_name,source,help_url}` | `alert definition configuration and source` | 初始加载全部告警定义。<br>调用:`App.HttpClient.get(url, App.alertDefinitionsMapper, {` | [source](../../../../ambari-web/classic/app/controllers/global/update_contr [...]
+| `UNHEALTHY_ALERT_LOAD`<br>HttpClient | `GET` / `NON_METRICS` | `GET /api/v1/clusters/{clusterName}/alerts?fields={Alert operational fields}&Alert/state.in(CRITICAL,WARNING)&Alert/maintenance_state.in(OFF)&from={from}&page_size={pageSize}` | `critical/warning non-maintenance alert instances`<br>`repeat-tolerance remaining`<br>`timestamps/text/host/service/component` | 只载入非健康且 maintenance OFF 的分页实例。<br>调用:`App.HttpClient.get(url, App.alertInstanceMapper, {` | [source](../../../../ambari- [...]
+| `ALERT_GROUPED_SUMMARY_LOAD`<br>HttpClient | `GET` / `NON_METRICS` | `GET /api/v1/clusters/{clusterName}/alerts?format=groupedSummary` | `server grouped alert summary` | 为告警定义摘要 mapper 加载服务端分组统计。<br>调用:`App.HttpClient.get(url, App.alertDefinitionSummaryMapper, {` | [source](../../../../ambari-web/classic/app/controllers/global/update_controller.js#L665) |
+| `ALERT_GROUP_LOAD`<br>HttpClient | `GET` / `NON_METRICS` | `GET /api/v1/clusters/{clusterName}/alert_groups?fields=AlertGroup/{default,definitions,id,name,targets}` | `alert group membership and targets` | 初始加载告警组。<br>调用:`App.HttpClient.get(url, App.alertGroupsMapper, {` | [source](../../../../ambari-web/classic/app/controllers/global/update_controller.js#L676) |
+| `ALERT_TARGET_LOAD`<br>HttpClient | `GET` / `NON_METRICS` | `GET /api/v1/alert_targets?fields=*` | `all alert target/notification fields` | 根级 alert target 资源,不带 cluster path。<br>调用:`App.HttpClient.get(App.get('apiPrefix') + '/alert_targets?fields=*', App.alertNotificationMapper, {` | [source](../../../../ambari-web/classic/app/controllers/global/update_controller.js#L682) |
+| `STACK_VERSION_LOAD`<br>HttpClient | `GET` / `NON_METRICS` | `GET /api/v1/clusters/{clusterName}/stack_versions?fields={full: *,repository_versions/*,... \| update: ClusterStackVersions/*}` | `cluster stack versions`<br>`repository/OS/repository details on full load` | \`fullLoad\` 选择初始全量 URL 或运行期轻量 URL。<br>调用:`App.HttpClient.get(this.getUrl(true, fullLoad), App.stackVersionMapper, {` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_and_upgrade_controller.js#L2025) |
+| `REPOSITORY_VERSION_LOAD`<br>HttpClient | `GET` / `NON_METRICS` | `GET /api/v1/stacks?fields=versions/repository_versions/RepositoryVersions,versions/repository_versions/operating_systems/*,versions/repository_versions/operating_systems/repositories/*` | `stack repository versions`<br>`OS and repository definitions` | 全量 repository-version model 加载。<br>调用:`App.HttpClient.get(this.getUrl(false, true), App.repoVersionMapper, {` | [source](../../../../ambari-web/classic/app/controllers/ma [...]
+| `UPGRADE_HISTORY_LOAD`<br>HttpClient | `GET` / `NON_METRICS` | `GET /api/v1/clusters/{clusterName}/upgrades?fields=Upgrade` | `Upgrade/*` | 历史列表 complete 时即 resolve;HttpClient error 不调 complete,可留下未解决 promise。<br>调用:`App.HttpClient.get(this.get('upgradeHistoryUrl'), App.stackUpgradeHistoryMapper, {` | [source](../../../../ambari-web/classic/app/controllers/main/admin/stack_upgrade_history_controller.js#L69) |
+| `CONFIG_HISTORY_LOAD`<br>HttpClient | `GET` / `NON_METRICS` | `GET /api/v1/clusters/{clusterName}/configurations/service_config_versions?{filterAndSortPredicates}fields=service_config_version,user,group_id,group_name,is_current,createtime,service_name,hosts,service_config_version_note,is_cluster_compatible,stack_id&minimal_response=true` | `service config version history and compatibility metadata` | 过滤、排序条件由 table mixin 动态生成。<br>调用:`App.HttpClient.get(this.getUrl(queryParams), App.ser [...]
+| `GENERIC_MUTATION_POLL_HELPER`<br>jQuery AJAX | `DYNAMIC` / `NON_METRICS` | `PUT {App.Poll.url}; body={App.Poll.data}; response is text or JSON with Requests.id` | `Requests.id when asynchronous`<br>`caller-defined task/request polling data` | 经典 app 树未发现 \`App.Poll.create()\` 生产调用,只有 unit test;空/非 JSON 成功会直接标记 success,有 request ID 才转轮询。<br>调用:`$.ajax({` | [source](../../../../ambari-web/classic/app/utils/polling.js#L67) |
+| `HOST_DELETE_PREFLIGHT_LOAD`<br>HttpClient | `GET` / `MIXED` | `GET /api/v1/clusters/{clusterName}/hosts/?Hosts/host_name.in({hostName})&fields={host,host-component,stack-version,logging and metric fields}&minimal_response=true&page_size=10&from=0&sortBy=Hosts/host_name.asc` | `host and host-component state/maintenance/stale-config/desired-admin-state`<br>`stack versions and logging`<br>`NameNode ClusterId/HAState safety selectors` | 升级前 heartbeat check 中的安全删除流程;响应携带 disk/load/cpu/memo [...]
+
+`MIXED` 调用同时返回指标字段和非 Metrics 运维字段。本文只纳入表中列明的 topology/state/maintenance/stale config/HA/Active-Standby/安全判断字段;指标数值仍排除。`ORIGINAL_REQUEST_REPLAY` 是保存 KDC credential 后重放原请求,不能当成一个固定 endpoint。
diff --git a/docs/frontend-refactor/ember-baseline/generated/realtime-channels.md b/docs/frontend-refactor/ember-baseline/generated/realtime-channels.md
new file mode 100644
index 0000000000..a0e5a9d41f
--- /dev/null
+++ b/docs/frontend-refactor/ember-baseline/generated/realtime-channels.md
@@ -0,0 +1,360 @@
+# Ember 非 Metrics 实时通道契约
+
+> 由 `tools/extract-ember-baseline.mjs` 从已审计的静态 contract 生成。本文冻结 classic UI 的 STOMP/WebSocket/SockJS 行为,不包含 Metrics 时序数据通道。
+
+## 可校验摘要
+
+- transports:2
+- destinations:11(10 static + 1 dynamic)
+- subscribe sites:11
+- addHandler sites:1
+- removeHandler sites:1
+- business unsubscribe sites:1
+- lifecycle contracts:4
+
+## Transport
+
+| ID / 类型 | URL | 协议与心跳 | Fallback / 重连 | 风险边界 | Source / Test |
+| --- | --- | --- | --- | --- | --- |
+| `RT-TRANSPORT-001`<br>`NATIVE_WEBSOCKET` | `{ws\|wss}://{window.location.hostname}{:window.location.port}/api/stomp/v1/websocket` | STOMP 1.1/1.0<br>heartbeat 10000/10000 ms<br>schemes ws/wss | fallback: Initial native WebSocket connection error before any successful connection.<br>reconnect: After any previously successful connection is lost, reconnect with the same transport and restore the captured in-memory subscriptions. | There is no REST polling substitute when both native WebSo [...]
+| `RT-TRANSPORT-002`<br>`SOCKJS_FALLBACK` | `{http\|https}://{window.location.hostname}{:window.location.port}/api/stomp/v1` | STOMP 1.1/1.0<br>heartbeat 10000/10000 ms<br>SockJS eventsource, xhr-polling, iframe-xhr-polling, jsonp-polling | fallback: Native WebSocket is unavailable or its initial connection fails.<br>reconnect: A previously successful SockJS connection reconnects through SockJS and restores the captured subscriptions. | The fallback deliberately excludes WebSocket, XHR s [...]
+
+## Destination 契约
+
+| ID / Destination | Event | Ember 消费字段 | Handler chain | Lifecycle | REST reconcile | 风险边界 | Source |
+| --- | --- | --- | --- | --- | --- | --- | --- |
+| `RT-SUB-001`<br>`/events/hostcomponents` | `HostComponentsUpdateEvent` | `hostComponents[].componentName`<br>`hostComponents[].hostName`<br>`hostComponents[].currentState`<br>`hostComponents[].previousState`<br>`hostComponents[].maintenanceState`<br>`hostComponents[].staleConfigs` | `App.hostComponentStatusMapper.map`<br>`App.ServerDataMapper.updatePropertiesByConfig`<br>`App.hostComponentStatusMapper.updateComponentsWithStaleConfigs`<br>`App.componentsStateMapper.updateComponentCountO [...]
+| `RT-SUB-002`<br>`/events/alerts` | `AlertUpdateEvent` | `summaries[App.clusterId][*].definition_id`<br>`summaries[App.clusterId][*].definition_name`<br>`summaries[App.clusterId][*].summary` | `App.alertSummaryMapper.map`<br>`App.alertDefinitionSummaryMapper.map` | Global application-lifetime subscription restored after reconnect. | Alert pages also load alert data through REST, but the socket client does not request a missed-event snapshot after reconnect. | A missing current-cluster e [...]
+| `RT-SUB-003`<br>`/events/ui_topologies` | `TopologyUpdateEvent` | `eventType`<br>`clusters[App.clusterId].components[].componentName`<br>`clusters[App.clusterId].components[].serviceName`<br>`clusters[App.clusterId].components[].displayName`<br>`clusters[App.clusterId].components[].hostNames`<br>`clusters[App.clusterId].components[].publicHostNames`<br>`clusters[App.clusterId].components[].commandParams.version`<br>`clusters[App.clusterId].components[].lastComponentState`<br>`clusters[ [...]
+| `RT-SUB-004`<br>`/events/configs` | `ConfigsUpdateEvent` | `configs[].type` | `default: App.UpdateController.configsChangedHandler`<br>`default: ConfigurationController.updateConfigTags`<br>`default when cluster-env changed: App.UpdateController.updateClusterEnv`<br>`history: MainConfigHistoryController.load(true)` | The global default handler is permanent. The named history handler is added when config history renders and removed when its view is destroyed. | Every event triggers a RE [...]
+| `RT-SUB-005`<br>`/events/services` | `ServiceUpdateEvent` | `service_name`<br>`maintenance_state`<br>`state` | `App.serviceStateMapper.map`<br>`App.ServerDataMapper.updatePropertiesByConfig`<br>`App.cache.services update` | Global application-lifetime subscription restored after reconnect. | Periodic service/component REST refreshes can converge service state, but missed events are not replayed. | Updates for a Service record that is not loaded are ignored by updatePropertiesByConfig.< [...]
+| `RT-SUB-006`<br>`/events/hosts` | `HostUpdateEvent` | `host_name`<br>`host_status`<br>`host_state`<br>`last_heartbeat_time`<br>`maintenance_state`<br>`alerts_summary` | `App.hostStateMapper.map`<br>`App.ServerDataMapper.updatePropertiesByConfig` | Global application-lifetime subscription restored after reconnect. | Host list/detail REST refreshes can replace missed status, heartbeat, maintenance, and alert-summary values; the channel itself has no snapshot request. | Updates for an unl [...]
+| `RT-SUB-007`<br>`/events/alert_definitions` | `AlertDefinitionsUIUpdateEvent` | `eventType`<br>`clusters[App.clusterId].alertDefinitions[]`<br>`clusters[App.clusterId].alertDefinitions[].definitionId`<br>`clusters[App.clusterId].alertDefinitions[].componentName`<br>`clusters[App.clusterId].alertDefinitions[].serviceName` | `App.alertDefinitionsMapperAdapter.map`<br>`UPDATE: normalize id/component_name/service_name and call App.alertDefinitionsMapper.map`<br>`DELETE: remove App.AlertDef [...]
+| `RT-SUB-008`<br>`/events/alert_group` | `AlertGroupsUpdateEvent` | `updateType`<br>`groups[].cluster_id`<br>`groups[].default`<br>`groups[].definitions`<br>`groups[].id`<br>`groups[].name`<br>`groups[].service_name`<br>`groups[].targets` | `App.alertGroupsMapperAdapter.map`<br>`CREATE/UPDATE: convert definition and target IDs to {id} and call App.alertGroupsMapper.map`<br>`DELETE: remove App.AlertGroup record`<br>`toggle ManageAlertGroupsController.changeTrigger` | Global application-l [...]
+| `RT-SUB-009`<br>`/events/upgrade` | `UpgradeUpdateEvent` | `type`<br>`request_id`<br>`request_status`<br>`suspended`<br>`all CREATE fields through restoreLastUpgrade` | `App.upgradeStateMapper.map`<br>`CREATE: MainAdminStackAndUpgradeController.restoreLastUpgrade({Upgrade:event})`<br>`matching UPDATE: persist upgradeState and isSuspended in controller/DB` | Global application-lifetime subscription; CREATE restores a new/current upgrade and UPDATE mutates only the matching request. | Up [...]
+| `RT-SUB-010`<br>`/events/requests` | `RequestUpdateEvent` | `requestId`<br>`requestContext`<br>`progressPercent`<br>`requestStatus`<br>`userName`<br>`startTime`<br>`endTime`<br>`Tasks[].id`<br>`Tasks[].requestId`<br>`Tasks[].status`<br>`Tasks[].hostName` | `BackgroundOperationsController.subscribeToUpdates`<br>`BackgroundOperationsController.updateRequests`<br>`parseRequestContext and generateTasksMapOfRequest`<br>`update or insert the visible background-operation model` | Subscribed o [...]
+| `RT-SUB-011`<br>`/events/tasks/{taskId}` | `NamedTaskUpdateEvent` | `id`<br>`requestId`<br>`hostName`<br>`status`<br>`errorLog`<br>`outLog`<br>`stderr`<br>`stdout`<br>`structured_out` | `BackgroundOperationsController.handleTaskUpdates`<br>`BackgroundOperationsController.updateTask`<br>`unsubscribe when the client classifies status as finished` | Dynamic subscription is created on entry to TASK_DETAILS for a task outside the client's FAILED/ABORTED/COMPLETED terminal set. A received me [...]
+
+## Payload Schema
+
+<details>
+<summary><code>RT-SUB-001</code> <code>/events/hostcomponents</code></summary>
+
+```json
+{
+  "type": "object",
+  "fields": {
+    "hostComponents": {
+      "type": "array",
+      "required": true,
+      "items": {
+        "clusterId": "integer",
+        "serviceName": "string",
+        "hostName": "string",
+        "componentName": "string",
+        "currentState": "State; omitted when null",
+        "previousState": "State; omitted when null",
+        "maintenanceState": "MaintenanceState; omitted when null",
+        "staleConfigs": "boolean; omitted when null"
+      }
+    }
+  }
+}
+```
+
+</details>
+
+<details>
+<summary><code>RT-SUB-002</code> <code>/events/alerts</code></summary>
+
+```json
+{
+  "type": "object",
+  "fields": {
+    "summaries": {
+      "type": "map<clusterId,map<definitionName,AlertDefinitionSummary>>",
+      "required": true,
+      "AlertDefinitionSummary": {
+        "definition_id": "integer",
+        "definition_name": "string",
+        "summary": {
+          "OK": "AlertStateValues",
+          "WARNING": "AlertStateValues",
+          "CRITICAL": "AlertStateValues",
+          "UNKNOWN": "AlertStateValues"
+        },
+        "AlertStateValues": {
+          "count": "integer",
+          "maintenance_count": "integer",
+          "original_timestamp": "integer",
+          "latest_text": "string; omitted when null"
+        }
+      }
+    }
+  }
+}
+```
+
+</details>
+
+<details>
+<summary><code>RT-SUB-003</code> <code>/events/ui_topologies</code></summary>
+
+```json
+{
+  "type": "object",
+  "fields": {
+    "clusters": {
+      "type": "map<clusterId,TopologyCluster>; omitted when null",
+      "TopologyCluster": {
+        "components": "array<TopologyComponent>; omitted when null",
+        "hosts": "array<TopologyHost>; omitted when null"
+      },
+      "TopologyComponent": {
+        "componentName": "string",
+        "serviceName": "string",
+        "displayName": "string",
+        "version": "string",
+        "hostIds": "array<integer>",
+        "hostNames": "array<string>",
+        "publicHostNames": "array<string>",
+        "componentLevelParams": "map<string,string>",
+        "commandParams": "map<string,string>",
+        "lastComponentState": "State"
+      },
+      "TopologyHost": {
+        "hostId": "integer",
+        "hostName": "string",
+        "rackName": "string",
+        "ipv4": "string"
+      }
+    },
+    "eventType": "CREATE|UPDATE|DELETE; omitted when null",
+    "hash": "string; omitted when null"
+  }
+}
+```
+
+</details>
+
+<details>
+<summary><code>RT-SUB-004</code> <code>/events/configs</code></summary>
+
+```json
+{
+  "type": "object",
+  "omittedWhenEmpty": true,
+  "fields": {
+    "serviceConfigId": "integer",
+    "clusterId": "integer",
+    "serviceName": "string",
+    "groupId": "integer",
+    "version": "integer",
+    "user": "string",
+    "note": "string",
+    "hostNames": "array<string>",
+    "createTime": "integer",
+    "groupName": "string",
+    "configs": "array<{clusterId: integer, type: string, tag: string, version: integer}>",
+    "changedConfigTypes": "array<string>"
+  }
+}
+```
+
+</details>
+
+<details>
+<summary><code>RT-SUB-005</code> <code>/events/services</code></summary>
+
+```json
+{
+  "type": "object",
+  "fields": {
+    "cluster_name": "string",
+    "maintenance_state": "MaintenanceState; omitted when null",
+    "service_name": "string",
+    "state": "State; omitted when null"
+  }
+}
+```
+
+</details>
+
+<details>
+<summary><code>RT-SUB-006</code> <code>/events/hosts</code></summary>
+
+```json
+{
+  "type": "object",
+  "fields": {
+    "cluster_name": "string",
+    "host_name": "string",
+    "host_status": "string; omitted when null",
+    "host_state": "HostState; omitted when null",
+    "last_heartbeat_time": "integer; omitted when null",
+    "maintenance_state": "MaintenanceState; omitted when null",
+    "alerts_summary": "AlertSummaryDTO; omitted when null"
+  }
+}
+```
+
+</details>
+
+<details>
+<summary><code>RT-SUB-007</code> <code>/events/alert_definitions</code></summary>
+
+```json
+{
+  "type": "object",
+  "fields": {
+    "eventType": "CREATE|UPDATE|DELETE",
+    "clusters": {
+      "type": "map<clusterId,AlertCluster>",
+      "AlertCluster": {
+        "alertDefinitions": "array<AlertDefinition>",
+        "hostName": "string; omitted when null",
+        "staleIntervalMultiplier": "integer; omitted when null"
+      },
+      "AlertDefinition": {
+        "clusterId": "integer",
+        "definitionId": "integer",
+        "serviceName": "string",
+        "componentName": "string",
+        "name": "string",
+        "scope": "Scope",
+        "interval": "integer",
+        "enabled": "boolean",
+        "source": "Alert Source object",
+        "label": "string",
+        "description": "string",
+        "uuid": "string",
+        "ignore_host": "boolean",
+        "help_url": "string",
+        "repeat_tolerance": "integer",
+        "repeat_tolerance_enabled": "boolean"
+      }
+    }
+  }
+}
+```
+
+</details>
+
+<details>
+<summary><code>RT-SUB-008</code> <code>/events/alert_group</code></summary>
+
+```json
+{
+  "type": "object",
+  "omittedWhenEmpty": true,
+  "fields": {
+    "updateType": "CREATE|UPDATE|DELETE",
+    "groups": {
+      "type": "array",
+      "items": {
+        "cluster_id": "integer",
+        "default": "boolean",
+        "definitions": "array<integer>",
+        "id": "integer",
+        "name": "string",
+        "service_name": "string",
+        "targets": "array<integer>"
+      }
+    }
+  }
+}
+```
+
+</details>
+
+<details>
+<summary><code>RT-SUB-009</code> <code>/events/upgrade</code></summary>
+
+```json
+{
+  "type": "object",
+  "omittedWhenEmpty": true,
+  "fields": {
+    "type": "CREATE|UPDATE",
+    "associated_version": "string; full CREATE event",
+    "cluster_id": "integer",
+    "direction": "UPGRADE|DOWNGRADE; full CREATE event",
+    "downgrade_allowed": "boolean; full CREATE event",
+    "request_id": "integer",
+    "request_status": "HostRoleStatus",
+    "skip_failures": "boolean; full CREATE event",
+    "skip_service_check_failures": "boolean; full CREATE event",
+    "upgrade_type": "UpgradeType; full CREATE event",
+    "start_time": "integer",
+    "end_time": "integer",
+    "upgrade_id": "integer; full CREATE event",
+    "suspended": "boolean",
+    "progress_percent": "number",
+    "revert_allowed": "boolean; full CREATE event"
+  }
+}
+```
+
+</details>
+
+<details>
+<summary><code>RT-SUB-010</code> <code>/events/requests</code></summary>
+
+```json
+{
+  "type": "object",
+  "fields": {
+    "clusterName": "string; omitted when null",
+    "endTime": "integer; omitted when null",
+    "requestId": "integer",
+    "progressPercent": "number; omitted when null",
+    "requestContext": "string; omitted when null",
+    "requestStatus": "HostRoleStatus; omitted when null",
+    "startTime": "integer; omitted when null",
+    "userName": "string; omitted when null",
+    "Tasks": {
+      "type": "array",
+      "items": {
+        "id": "integer",
+        "requestId": "integer",
+        "status": "HostRoleStatus",
+        "hostName": "string"
+      }
+    }
+  }
+}
+```
+
+</details>
+
+<details>
+<summary><code>RT-SUB-011</code> <code>/events/tasks/{taskId}</code></summary>
+
+```json
+{
+  "type": "object",
+  "fields": {
+    "id": "integer",
+    "requestId": "integer",
+    "hostName": "string",
+    "endTime": "integer; omitted when null",
+    "status": "HostRoleStatus",
+    "errorLog": "string; omitted when null",
+    "outLog": "string; omitted when null",
+    "stderr": "string; omitted when null",
+    "stdout": "string; omitted when null",
+    "structured_out": "string; omitted when null"
+  }
+}
+```
+
+</details>
+
+## Lifecycle
+
+| ID / 名称 | 行为 | 风险边界 | Source / Test |
+| --- | --- | --- | --- |
+| `RT-LIFE-001`<br>`APPLICATION_BOOTSTRAP` | MainController connects once, starts all global subscriptions on native success, or waits for the nested SockJS attempt to settle after native failure. Cluster REST loading starts independently. | Both transports failing still invokes startSubscriptions, but disconnected subscribe calls return null and are not queued.<br>There is no global unsubscribe or disconnect call on logout in classic application code. | 实现:[source](../../../../ambari-we [...]
+| `RT-LIFE-002`<br>`MESSAGE_DISPATCH` | Each broker message body is parsed once per registered handler iteration and delivered synchronously in handler insertion order. A destination has one default handler plus optional named handlers. | JSON.parse has no try/catch.<br>One throwing handler prevents later handlers for the same message and provides no isolation or replay.<br>A second default subscribe for an existing destination logs an error and retains the original handler. | 实现:[source [...]
+| `RT-LIFE-003`<br>`RECONNECT_AND_RESTORE` | After a previously successful connection fails, the wrapper waits 6000 ms, disconnects an open client, reconnects with the current transport, clears the subscription map, and recreates every captured default and named handler. | There is no replay cursor, sequence number, or forced REST snapshot after reconnect.<br>Subscription changes during the delay or reconnect can be resurrected or lost because restoration uses a shallow pre-delay snapsho [...]
+| `RT-LIFE-004`<br>`DYNAMIC_HANDLER_OWNERSHIP` | Config history owns a named handler on the permanent configs destination. Task detail owns an entire dynamic destination until selected client terminal states arrive. | Named handler removal assumes the destination exists.<br>Task ownership is not released merely by leaving TASK_DETAILS.<br>Client and server terminal-state definitions are inconsistent. | 实现:[source](../../../../ambari-web/classic/app/views/main/dashboard/config_history_vie [...]
diff --git a/docs/frontend-refactor/react-current/01-auth-shell-gap.md b/docs/frontend-refactor/react-current/01-auth-shell-gap.md
new file mode 100644
index 0000000000..305f8b0acc
--- /dev/null
+++ b/docs/frontend-refactor/react-current/01-auth-shell-gap.md
@@ -0,0 +1,113 @@
+# React 认证与应用外壳实现对照
+
+## 对照范围
+
+| 项目 | 值 |
+| --- | --- |
+| Ember 基准 | `ember-baseline/01-auth-shell.md` |
+| React 实现 | `ambari-web/latest`,分支 `auth-shell-module` |
+| React 基线提交 | `8ac5c5a1346687bc46b4651c42b07237fe0e9ca9` |
+| 模块功能 ID | `AUTH-001` 至 `AUTH-008`,`SHELL-001` 至 `SHELL-009` |
+| 复核日期 | 2026-08-13 |
+| Metrics 范围 | 排除;`/main/dashboard/metrics` 仅作为已有默认路由,不评价 Metrics 页面能力 |
+
+状态遵循 `ember-baseline/14-react-gap-matrix.md`。当前代码、单元测试和生产构建已通过静态验证,但尚未连接真实 Ambari Server、Knox 和 Admin View,因此 17 项均标记为 `NEEDS_RUNTIME_VALIDATION`,不能提前标记为 `COVERED`。
+
+## 当前结论
+
+| 状态 | 数量 |
+| --- | ---: |
+| `NEEDS_RUNTIME_VALIDATION` | 17 |
+| 合计 | 17 |
+
+认证与应用外壳的静态缺口已经补齐:Router 始终挂载,会话探测先于应用初始化,user/privileges/authorizations/MOTD 原子加载,四类 landing、统一 logout、keep-alive、inactivity、About、Experimental、User Settings 和 Installer 版本 gate 均已接线。剩余门禁是使用真实服务端完成本文列出的角色、SSO、错误和恢复场景。
+
+## 功能状态
+
+| ID | 状态 | React 实现与自动化证据 | 待运行态验证 |
+| --- | --- | --- | --- |
+| `AUTH-001` | `NEEDS_RUNTIME_VALIDATION` | `App.tsx` 始终挂载 hash router;`AuthenticatedApplication` 分隔公开登录与受保护应用;未认证深链保存安全目标后跳 `/login`;根 `/` 不作为 preferred path | 未登录直接访问根、Dashboard、Installer、View 深链;服务端 session 过期后的跳转与恢复 |
+| `AUTH-002` | `NEEDS_RUNTIME_VALIDATION` | `loginApi.authenticate()` 发送 UTF-8 Basic;用户名仅在 API URL 边界编码;`loadSession()` 要求 user 和 authorizations 全部成功;测试覆盖 UTF-8、单次编码和 authorization failure | 本地用户成功登录;403、500、网络错误的服务端实际 response body 与 UI 文案 |
+| `AUTH-003` | `NEEDS_RUNTIME_VALIDATION` | `/login/local`、`jwtProviderUrl`、返回 URL、三次循环限制和 local recovery 已实现;plain business 403 不再误判 session 失效 | Knox JWT 成功返回;Knox 401/403 payload;代理下完整 return URL;循环超限后的 local 登录 |
+| `AUTH-004` | `NEEDS_RUNTIME_VALIDATION` | `/clusters` session probe 读取 `User` header;本地状态缺失时恢复;authorization failure 不建 session;401 会同步清 React 内存 session | 容器/SSO 已认证且 localStorage 为空;header 大小写与代理透传;session 过期后的所有 lifecycle teardown |
+| `AUTH-005` | `NEEDS_RUNTIME_VALIDATION` | user、authorization 与 MOTD 并行加载;启用且有文本时在 AppProvider 前阻断;缺失、禁用、非法和请求失败时继续 | `/settings/motd` 的合法、换行、非法 JSON、disabled、404、500 实际响应 |
+| `AUTH-006` | `NEEDS_RUNTIME_VALIDATION` | `selectLandingPath()` 覆盖 installed、incomplete、View-only、no-cluster;Admin View 生成版本化浏览器 URL;测试覆盖四类 landing | 四种角色/集群组合;Admin View 部署版本选择;未完成 Installer 与进行中 wizard 的实际恢复 |
+| `AUTH-007` | `NEEDS_RUNTIME_VALIDATION` | Navbar 和 inactivity 共用 context logout;先清内存、本地 DB 和 preferred path,再 best-effort `/logout`;服务端失败不阻塞导航;测试覆盖 rejected logout | `/logout` 成功、500、网络断开和长时间 pending;cookie 删除;STOMP、polling 和 iframe teardown |
+| `AUTH-008` | `NEEDS_RUNTIME_VALIDATION` | 认证应用挂载后每 60 秒串行调用 `/clusters`,卸载或 logout 后停止 | 长会话保持;请求跨 60 秒时无并发;session 过期 response;登出后 Network 面板无后续请求 |
+| `SHELL-001` | `NEEDS_RUNTIME_VALIDATION` | AppProvider 加载 supports、wizard owner、Ambari properties/version、cluster;运维用户再加载 services/hosts/upgrade/background/STOMP;View-only 跳过运维初始化并使用最小壳层;初始化失败可 Retry | installed/incomplete/no-cluster/View-only 的实际请求清单;每个初始化 API 故障和 Retry;STOMP reconnect/deactivate |
+| `SHELL-002` | `NEEDS_RUNTIME_VALIDATION` | `/main`、`/installer`、`/experimental`、`/adminView` 位于统一认证边界;Admin 子菜单和 route 分开授权;Auto Start 菜单保留两组 AND、route 保留 Manage OR;Kerberos/Auto Start 使用 feature guard | read-only、service operator、cluster admin、Ambari admin、View-only;upgrade 与 non-wizard owner;直接输入各受限 route |
+| `SHELL-003` | `NEEDS_RUNTIME_VALIDATION` | `normalizeInternalPath()` 只接受单斜杠内部路径,排除根、login、absolute 和 `//host`;值恢复后消费;测试覆盖安全/拒绝样例 | 浏览器刷新、SSO 往返、session expiry 和主动 logout;各 wizard 写入/清除自己的服务端恢复键 |
+| `SHELL-004` | `NEEDS_RUNTIME_VALIDATION` | `DocumentTitleUpdater` 只更新 `document.title`;认证和 shell 初始化分别显示 loading/error/retry;不再渲染标题占位 DOM | cluster 名、Installer、Views、Experimental 和深链 title;慢请求进度;初始化失败后 Retry 成功 |
+| `SHELL-005` | `NEEDS_RUNTIME_VALIDATION` | admin/readonly property 分流;大于 0 才启用;60 秒 warning;Continue/Sign Out;window 和动态 iframe activity;wizard/upgrade 排除;测试覆盖 timeout 选择、倒计时和排除路径 | admin/readonly 实际 property 单位;同源和跨域 View iframe;动态 iframe;warning 两个按钮;路由切换和 timeout=0 |
+| `SHELL-006` | `NEEDS_RUNTIME_VALIDATION` | Ambari version 初始化时缓存;About 只读 context,缺失显示 `N/A`,无 click-time 请求 | Main、Installer、View-only 和 no-cluster;properties 请求失败;重复打开 About 的 Network 记录 |
+| `SHELL-007` | `NEEDS_RUNTIME_VALIDATION` | `/experimental` 由 `AMBARI.MANAGE_SETTINGS` 保护;经典 supports key;Save 同步共享 context;Reset 另需 persisted-data 权限且 non-owner 禁用;服务端 reset 成功后才清本地并整页 reload | supports 存在/缺失/非法;Save/Cancel;Reset 成功/失败;另一用户持有 Installer、Kerberos、HA wizard |
+| `SHELL-008` | `NEEDS_RUNTIME_VALIDATION` | Maven 注入 `VITE_AMBARI_VERSION`;client/server 都非空且不一致才阻断 Installer;测试覆盖 empty/match/mismatch | Maven packaged artifact;相同版本、不一致版本和开发构建空版本;阻断页面显示两端版本 |
+| `SHELL-009` | `NEEDS_RUNTIME_VALIDATION` | 菜单 `AMBARI.MANAGE_SETTINGS`、handler `CLUSTER.UPGRADE_DOWNGRADE_STACK`、保存 `CLUSTER.MANAGE_USER_PERSISTED_DATA` 三层 gate 保留;background/timezone/default persist、cluster/View privileges 和错误反馈已实现 | 三层不同权限组合;首次登录默认写回;字符串/JSON persist;timezone reload;无 privileges、Ambari admin 和 API failure |
+
+## 后端接口对照
+
+| Ember 契约 | React 实现 | 静态结论 | 运行态门禁 |
+| --- | --- | --- | --- |
+| `POST /api/v1/auth` | `LoginApi.authenticate()` | UTF-8 Basic、`Content-Type: text/plain`、`skipAuthRedirect` 已实现 | Knox/proxy header 和 403/500 body |
+| `GET /api/v1/users/{userName}?fields=*,privileges/...` | `LoginApi.handleSuccessfulLogin()` | username 单次编码;user 与 privilege 原子建 session | 特殊字符用户名和真实 privilege shape |
+| `GET /api/v1/users/{userName}/authorizations?fields=*` | `LoginApi.loadAuthorizationsCallback()` | 失败时整个 session 失败,不复刻 Ember `.complete()` 缺陷 | 不同角色 authorization 集合和 401/403/500 |
+| `GET /api/v1/users/{userName}/privileges?fields=*` | `LoginApi.loadPrivileges()` | User Settings 独立读取 cluster/View 字段 | View instance/version/name 和无 privilege response |
+| `GET /api/v1/clusters?...` | `probeSession()`、`getClusterData()`、`noopPolling()` | session probe、cluster 初始化和 keep-alive 职责分开;probe 可读 `User` header | SSO header、无集群、未完成集群、session expiry |
+| `GET /api/v1/settings/motd` | `LoginApi.loadLoginMessage()` | 容错解析和阻断确认已实现 | 合法/非法/404/500 payload |
+| `GET /api/v1/logout` | `LoginApi.logout()` | 统一调用;客户端清理不等待服务端成功 | cookie、proxy、pending/失败请求 |
+| `GET /api/v1/services/AMBARI/components/AMBARI_SERVER` | `ClusterApi.loadAmbariProperties()` | properties 和 component version 一次初始化并缓存 | 各部署版本的 response shape |
+| `GET/POST /api/v1/persist[/key]` | `ClusterApi.getPersistData()`、`postPersistData()` | User Settings、supports、wizard owner 使用经典 JSON-string value 契约 | 404/default、权限拒绝、并发 wizard owner |
+| `GET /api/v1/services/AMBARI?...component_version...` | `ServiceApi.getAmbariServerVersion()` | Admin View 选择最新版本并构造 browser URL | 多版本排序、空 response、Admin View 未安装 |
+
+## 五轮实现审计
+
+| 轮次 | 独立入口 | 本轮发现 | 修正结果 |
+| --- | --- | --- | --- |
+| 1. Route 与 landing | Router、公开/受保护边界、四类首屏、深链 | 根 `/` 被保存为 preferred path 会自重定向;`/adminView` 缺经典 route guard | 根路径不再保存;Admin View 对已有集群保留 Upgrade guard、无集群允许 Ambari admin |
+| 2. 认证与 session | Basic、JWT、User header、MOTD、logout、keep-alive | 全局 401 只改 hash但不清 React session;plain 403 会被误当 session expiry;logout 会等待服务端 | 增加 session-expired 事件;401/JWT challenge 与业务 403 分流;logout 先完成客户端清理并后台 logoff |
+| 3. API 与持久化 | 全部十类 Auth/Shell endpoint、URL 编码、persist value | session 探测失败会全量清 DB,破坏 Installer/HA/Kerberos 恢复;Reset 先清本地再请求服务端 | session-only cleanup 保留 wizard namespace;主动 logout/reset 才全量清理;Reset 改为 server-first |
+| 4. 权限与状态 | menu/route/handler/save gate、upgrade、wizard owner、feature flags | Settings 首次缺省值未写回;Navbar 在 upgrade 时显示无响应项;`wizardIsNotFinished` 漏 non-owner | 权限允许时写回默认值;受限菜单按经典语义隐藏;全局 wizard 状态包含 non-owner |
+| 5. 异步与失败恢复 | STOMP、polling、iframe、version、View-only、错误/Retry | STOMP client 每 render 新建;动态 iframe 未监听;Modal 泄漏内部 props;View-only 仍暴露集群控件 | 固定单一 STOMP client并 teardown;MutationObserver 管理 iframe;过滤 Modal props;View-only 使用最小导航 |
+
+## 有意不复刻的 Ember 缺陷
+
+| Ember 行为 | React 决定 |
+| --- | --- |
+| preferred path 只看首字符,错误接受 `//host` | 拒绝 protocol-relative 和 absolute URL |
+| authorization 请求通过 `.complete()` 串联,失败仍可能继续 | user、privileges、authorizations 任一失败均不建立 session |
+| logoff 失败可能保留 keep-alive 标志 | 客户端退出和 lifecycle teardown 不依赖服务端 logoff 结果 |
+| Experimental Reset 不等待服务端 persist 完成 | 先成功清服务端 wizard owner,再清本地并整页恢复 session |
+
+## 自动化证据
+
+`npm test` 当前运行 7 个 Vitest 文件、32 个测试,覆盖:
+
+- UTF-8 Basic Authorization 和 username 单次编码。
+- authorization failure 不建立 session,`User` header 恢复 session,logout failure cleanup。
+- JWT/local 分支、redirect limit、session-expired 事件和 plain 403 分流。
+- `//host`、absolute、login 和根路径拒绝,以及安全内部路径接受。
+- installed/default、preferred、incomplete、no-cluster 和 View-only landing。
+- client/server version empty、match、mismatch。
+- persist JSON values、fallback、admin/readonly inactivity、wizard/upgrade exclusion和倒计时。
+- Admin View existing-cluster guard 与 no-cluster Ambari admin landing。
+
+`npm run build` 已通过。现存 Sass deprecation、重复 switch、`eval` 和大 bundle 警告来自基线,不作为本模块完成证据,也不在本模块范围内修复。全仓 `npm run lint` 仍被 `frontend-refactor` 基线的大量 `any`、`@ts-ignore` 和 hook dependency 技术债阻断;本模块新增的策略、API、layout、guard、页面和测试文件无 ESLint error。
+
+## 运行态验收矩阵
+
+至少执行以下场景后才允许将对应 ID 改为 `COVERED`:
+
+1. 本地 Basic 登录:成功、错误密码、500、网络失败、特殊字符用户名。
+2. Knox JWT:成功往返、`/login/local` bypass、循环超限、代理 return URL。
+3. 服务端已有会话且 localStorage 为空;运行中 session 过期。
+4. installed cluster、incomplete cluster、no-cluster Ambari admin、View-only 四类 landing。
+5. admin timeout、readonly timeout、timeout=0、Continue、Sign Out、wizard/upgrade exclusion、同源/跨域动态 View iframe。
+6. MOTD enabled/disabled/malformed/404/500。
+7. logout 成功、失败、pending,确认无后续 keep-alive、polling 或 STOMP 工作。
+8. User Settings 三层权限、首次默认写回、timezone reload、cluster/View privilege 列表。
+9. Experimental Save/Cancel/Reset、persist failure、non-wizard owner。
+10. Admin View navigation、版本请求失败 fallback、Maven packaged version match/mismatch。
+
+## Issue 与 PR 粒度
+
+本模块只使用一个主 JIRA 和一个主 PR,不按单个功能 ID 拆分。JIRA 英文草稿位于 `react-current/issues/01-auth-shell.md`;PR 必须以 `apache/ambari:frontend-refactor` 为 base,并推送到 `JiaLiangC/ambari`。
diff --git a/docs/frontend-refactor/react-current/issues/01-auth-shell.md b/docs/frontend-refactor/react-current/issues/01-auth-shell.md
new file mode 100644
index 0000000000..2bea58b92c
--- /dev/null
+++ b/docs/frontend-refactor/react-current/issues/01-auth-shell.md
@@ -0,0 +1,85 @@
+## Problem
+
+The React frontend does not yet provide a reliable authentication and application-shell boundary equivalent to the classic Ember UI. Authentication, server-session recovery, protected routing, landing-page selection, session lifecycle handling, persisted user settings, and global shell behavior are either incomplete or split across unrelated components.
+
+The current implementation selects the login page from a one-time `window.location.hash` snapshot and mounts the router only after cluster initialization. An unauthenticated request to a protected URL can therefore remain on the loading screen. The `/main` and `/installer` trees do not share an authentication guard, authorization failures can still establish a client-side session, and usernames are URL-encoded by both the context and API layers.
+
+This issue covers the complete non-Metrics Auth/Shell module defined by the classic UI baseline. Metrics pages are not part of the scope; an existing Metrics route may only be referenced as a landing route.
+
+## Scope
+
+* Establish a single router-driven boundary between public login routes and the authenticated application.
+* Support local Basic authentication, Knox/external JWT redirection, `/login/local`, redirect-loop protection, and server-authenticated session recovery through the `User` response header.
+* Load the user, privileges, and authorizations before declaring the session authenticated. Treat any required authorization-loading failure as a failed session.
+* Load and display the login message of the day when enabled, while allowing login to continue when the setting is absent, invalid, or unavailable.
+* Select the initial destination for installed clusters, incomplete installation, users with only View privileges, and Ambari administrators with no cluster.
+* Preserve and restore only normalized same-origin application paths. Reject protocol-relative, absolute, and login targets.
+* Consolidate logout, local-state cleanup, polling teardown, and server logoff behavior.
+* Add the authenticated-session keep-alive lifecycle.
+* Load global shell state in the correct order and avoid initializing full cluster operations for View-only users.
+* Apply authentication and authorization checks independently to navigation visibility and direct route access.
+* Implement document-title updates, cached About information, and actionable global loading/error states.
+* Implement administrator and read-only inactivity timeouts, the 60-second warning, continue/sign-out actions, activity monitoring, and wizard/upgrade exclusions.
+* Implement the Experimental page, persisted support flags, and Reset UI States with its separate persisted-data permission.
+* Complete User Settings for background-operation preference, timezone, and current cluster/View privileges while preserving the classic UI's distinct visibility, handler, and persistence permission checks.
+* Block the Installer on a packaged web-client/server version mismatch when a client version is available.
+
+## Classic UI Baseline
+
+The acceptance baseline is `docs/frontend-refactor/ember-baseline/01-auth-shell.md`, feature IDs `AUTH-001` through `AUTH-008` and `SHELL-001` through `SHELL-009`. The React gap analysis is recorded in `docs/frontend-refactor/react-current/01-auth-shell-gap.md`.
+
+The authoritative network comparison must include all of the following classic contracts rather than only matching API file names:
+
+* `POST /api/v1/auth`
+* `GET /api/v1/users/{userName}` with privileges
+* `GET /api/v1/users/{userName}/authorizations`
+* `GET /api/v1/users/{userName}/privileges`
+* `GET /api/v1/clusters` for authentication, cluster state, and keep-alive
+* `GET /api/v1/settings/motd`
+* `GET /api/v1/logout`
+* `GET /api/v1/services/AMBARI/components/AMBARI_SERVER` for properties and version
+* `GET` and `POST /api/v1/persist[/key]`
+* Ambari Admin View browser navigation
+
+## Acceptance Criteria
+
+* Visiting `/`, `/main/...`, or `/installer/...` without an authenticated server session routes to `/login` and safely preserves an eligible target.
+* A valid local login sends UTF-8 Basic credentials and URL-encodes the username exactly once at the URL boundary.
+* User data, privileges, and authorizations must all load before protected content is rendered.
+* A server-authenticated user with empty local storage is restored from the `User` response header.
+* Authentication responses with `jwtProviderUrl` redirect to the provider with the current URL as the return target, except on `/login/local`; repeated redirects stop with a visible recovery path.
+* An enabled message of the day requires acknowledgment before navigation. Missing, malformed, disabled, or failed MOTD retrieval does not block login.
+* Landing behavior is correct for installed clusters, incomplete installation, no-cluster Ambari administrators, and View-only users.
+* Restored targets are internal normalized hash paths and never protocol-relative or absolute URLs.
+* Logout stops session-owned work, clears local UI state, attempts server logoff, and reaches login even when logoff fails.
+* Keep-alive starts only for an authenticated application and stops on unmount or logout.
+* Navigation visibility and direct route entry each enforce their required permissions.
+* Inactivity handling selects the administrator or read-only timeout, warns 60 seconds before expiration, supports continue/sign out, monitors the window and accessible iframes, and skips wizard and stack-upgrade routes.
+* About uses the server version cached during initialization and displays `N/A` when unavailable without a click-time request.
+* User Settings loads and saves background-operation preference and timezone and displays cluster/View privileges under the documented permission gates.
+* Experimental support flags can be loaded, edited, saved, canceled, and reset under the documented permission gates.
+* A packaged client/server version mismatch blocks the Installer and displays both versions.
+* Unit tests cover authentication branches, authorization failure, URL normalization, landing selection, permission gates, logout failure, inactivity behavior, and persisted settings.
+* Runtime validation covers local Basic auth, Knox JWT, server-session recovery, each landing mode, administrator/read-only inactivity, MOTD variants, logout failure, Admin View navigation, and a packaged version mismatch.
+
+## Out of Scope
+
+* Metrics dashboards, charts, widgets, and Metrics data APIs.
+* AngularJS Ambari Admin Console implementation. This issue only covers navigation into the existing Admin View.
+* Feature work belonging to Hosts, Services, Alerts, Upgrades, Installer steps, Kerberos, or HA beyond the shell entry and lifecycle behavior described above.
+
+## Compatibility Decisions
+
+The React implementation must not reproduce known unsafe or failure-prone classic UI behavior:
+
+* Protocol-relative preferred paths such as `//host/path` are rejected even though the classic prefix check accepts them.
+* User and privilege data are not sufficient to establish a session when the authorization request fails.
+* A plain business-operation `403 Forbidden` is not treated as proof that the authenticated session expired.
+* Client logout and lifecycle teardown do not wait for successful server logoff.
+* Reset UI States does not erase recoverable local wizard data until the server-side wizard owner reset succeeds.
+
+These are intentional compatibility corrections, not missing parity.
+
+## Verification Boundary
+
+The module is not complete based on static code or unit tests alone. All feature IDs remain `NEEDS_RUNTIME_VALIDATION` until the runtime matrix in `docs/frontend-refactor/react-current/01-auth-shell-gap.md` is executed against real Ambari Server, Knox, Admin View, packaged web-client versions, and representative permission roles.


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@ambari.apache.org
For additional commands, e-mail: commits-help@ambari.apache.org