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

hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin-gateway.git


The following commit(s) were added to refs/heads/master by this push:
     new fc0e950  fix(web): show a readable error when the backend is 
unreachable
fc0e950 is described below

commit fc0e950a493fcae54d22cefcd5930bb60bbe2d75
Author: Yang Luo <[email protected]>
AuthorDate: Fri Aug 21 16:27:52 2026 +0800

    fix(web): show a readable error when the backend is unreachable
---
 web/src/App.tsx                    | 28 ++++++++++++++++------------
 web/src/backend/request.ts         | 27 +++++++++++++++++++++++++--
 web/src/locales/en/data.json       |  1 +
 web/src/locales/zh/data.json       |  1 +
 web/src/pages/AgentConfigsPage.tsx |  2 +-
 5 files changed, 44 insertions(+), 15 deletions(-)

diff --git a/web/src/App.tsx b/web/src/App.tsx
index 94268b5..c65cfaf 100644
--- a/web/src/App.tsx
+++ b/web/src/App.tsx
@@ -134,19 +134,23 @@ export default function App() {
   };
 
   const getAccount = React.useCallback(() => {
-    AccountBackend.getAccount().then(res => {
-      const user = res.data;
-      if (user !== null && user !== undefined) {
-        user.hostname = res.data2;
-        const language = localStorage.getItem("language");
-        if (language && language !== Setting.getLanguage()) {
-          Setting.setLanguage(language);
+    AccountBackend.getAccount()
+      .then(res => {
+        const user = res.data;
+        if (user !== null && user !== undefined) {
+          user.hostname = res.data2;
+          const language = localStorage.getItem("language");
+          if (language && language !== Setting.getLanguage()) {
+            Setting.setLanguage(language);
+          }
+          setAccount(user);
+        } else {
+          setAccount(null);
         }
-        setAccount(user);
-      } else {
-        setAccount(null);
-      }
-    });
+      })
+      // An unreachable backend is not a signed-in session: without this the
+      // app waits on account forever behind the full-page spinner.
+      .catch(() => setAccount(null));
   }, []);
 
   React.useEffect(() => {
diff --git a/web/src/backend/request.ts b/web/src/backend/request.ts
index c8c150a..e837c57 100644
--- a/web/src/backend/request.ts
+++ b/web/src/backend/request.ts
@@ -12,9 +12,21 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import i18next from "i18next";
+
 import {ServerUrl} from "@/Setting";
 import type {ApiResponse} from "@/types";
 
+/**
+ * The API answers in JSON and nothing else, so a body that will not parse is a
+ * request that never reached the Go server: a proxy error page, or the SPA's 
own
+ * index.html served as the fallback for an unhandled /api path.
+ */
+function unreachable(status?: number) {
+  const text = i18next.t("general:Cannot connect to the server, please check 
that the backend is running");
+  return new Error(status === undefined ? text : `${text} (HTTP ${status})`);
+}
+
 /**
  * One place where every call to the Go API is made. `credentials: include`
  * carries the beego session cookie, which is what the backend authenticates
@@ -35,8 +47,19 @@ export async function request<T = any, T2 = any>(
     options.body = JSON.stringify(body);
   }
 
-  const response = await fetch(`${ServerUrl}${path}`, options);
-  return (await response.json()) as ApiResponse<T, T2>;
+  let response: Response;
+  try {
+    response = await fetch(`${ServerUrl}${path}`, options);
+  } catch {
+    throw unreachable();
+  }
+
+  const text = await response.text();
+  try {
+    return JSON.parse(text) as ApiResponse<T, T2>;
+  } catch {
+    throw unreachable(response.ok ? undefined : response.status);
+  }
 }
 
 /** Builds a query string, dropping the parameters that were left empty. */
diff --git a/web/src/locales/en/data.json b/web/src/locales/en/data.json
index 6169967..6938064 100644
--- a/web/src/locales/en/data.json
+++ b/web/src/locales/en/data.json
@@ -244,6 +244,7 @@
     "At Any Time": "At Any Time",
     "Back Home": "Back Home",
     "Cancel": "Cancel",
+    "Cannot connect to the server, please check that the backend is running": 
"Cannot connect to the server, please check that the backend is running",
     "Certs": "Certs",
     "Client IP": "Client IP",
     "Client ip": "Client ip",
diff --git a/web/src/locales/zh/data.json b/web/src/locales/zh/data.json
index c36bca0..2b243d5 100644
--- a/web/src/locales/zh/data.json
+++ b/web/src/locales/zh/data.json
@@ -244,6 +244,7 @@
     "At Any Time": "任意时间",
     "Back Home": "返回首页",
     "Cancel": "取消",
+    "Cannot connect to the server, please check that the backend is running": 
"无法连接到服务器,请检查后端是否已启动",
     "Certs": "证书",
     "Client IP": "客户端 IP",
     "Client ip": "客户端IP",
diff --git a/web/src/pages/AgentConfigsPage.tsx 
b/web/src/pages/AgentConfigsPage.tsx
index 9b656f1..34e8f03 100644
--- a/web/src/pages/AgentConfigsPage.tsx
+++ b/web/src/pages/AgentConfigsPage.tsx
@@ -307,7 +307,7 @@ export default function AgentConfigsPage({account}: 
{account: Account}) {
 
       {error ? <MessageAlert description={error} /> : null}
 
-      {scanned && inventories.length === 0 ? (
+      {scanned && !error && inventories.length === 0 ? (
         <EmptyState
           icon={Package}
           title={i18next.t("agentConfig:No agents found")}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to