diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 6ac9558..df8f23d 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -404,6 +404,7 @@ export function App() {
readProductEnvironmentConfigStatus(
selectedProductOverview.product,
environment.environment,
+ controller.signal,
).then((payload) => payload.config_status),
),
)
diff --git a/frontend/src/ProductOverviewShell.tsx b/frontend/src/ProductOverviewShell.tsx
index ee5e6c0..d19c5bb 100644
--- a/frontend/src/ProductOverviewShell.tsx
+++ b/frontend/src/ProductOverviewShell.tsx
@@ -144,6 +144,7 @@ function EnvironmentDetailStrip({
const blockedActions = environment.available_actions.filter(
(action) => !action.enabled,
);
+ const baseUrl = safeExternalHttpUrl(environment.base_url);
return (
@@ -151,11 +152,13 @@ function EnvironmentDetailStrip({
{environment.environment}
{environment.context}
- {environment.base_url ? (
-
+ {baseUrl ? (
+
{environment.base_url}
+ ) : environment.base_url ? (
+
{environment.base_url}
) : null}
@@ -193,3 +196,16 @@ function EnvironmentDetailStrip({
);
}
+
+function safeExternalHttpUrl(value: string): URL | null {
+ const trimmed = value.trim();
+ if (!trimmed) {
+ return null;
+ }
+ try {
+ const url = new URL(trimmed);
+ return url.protocol === "http:" || url.protocol === "https:" ? url : null;
+ } catch {
+ return null;
+ }
+}
diff --git a/frontend/src/api.ts b/frontend/src/api.ts
index 6714d4f..96eaa01 100644
--- a/frontend/src/api.ts
+++ b/frontend/src/api.ts
@@ -35,6 +35,7 @@ async function requestJson
(
path: string,
method: "GET" | "POST" = "GET",
body?: unknown,
+ signal?: AbortSignal,
): Promise {
const headers: HeadersInit = {
Accept: "application/json",
@@ -47,6 +48,7 @@ async function requestJson(
credentials: "same-origin",
headers,
body: body === undefined ? undefined : JSON.stringify(body),
+ signal,
});
const payload = (await response.json()) as T | ApiErrorPayload;
if (!response.ok) {
@@ -102,9 +104,13 @@ export function listProducts(): Promise {
export function readProductEnvironmentConfigStatus(
product: string,
environment: string,
+ signal?: AbortSignal,
): Promise {
return requestJson(
`/v1/products/${encodeURIComponent(product)}/environments/${encodeURIComponent(environment)}/config-status`,
+ "GET",
+ undefined,
+ signal,
);
}