diff --git a/src/aignostics/gui/_frame.py b/src/aignostics/gui/_frame.py
index f4470f5a9..633c8484d 100644
--- a/src/aignostics/gui/_frame.py
+++ b/src/aignostics/gui/_frame.py
@@ -213,7 +213,9 @@ def _update_health() -> None:
coroutine=_health_load_and_render(),
name="_health_load_and_render",
)
- ui.run_javascript("document.getElementById('betterstack').src = document.getElementById('betterstack').src;")
+ # Only refresh the status iframe if it exists (production/staging)
+ if settings().status_page_url:
+ ui.run_javascript("document.getElementById('betterstack').src = document.getElementById('betterstack').src;")
ui.timer(interval=HEALTH_UPDATE_INTERVAL, callback=_update_health, immediate=True)
@@ -342,13 +344,15 @@ def toggle_dark_mode() -> None:
ui.link("Get Support", "https://platform.aignostics.com/support", new_tab=True).mark(
"LINK_DOCUMENTATION"
)
- with ui.item().props("clickable"):
- with ui.item_section().props("avatar"):
- ui.icon("check_circle", color="primary")
- with ui.item_section():
- ui.link("Check Platform Status", "https://status.aignostics.com", new_tab=True).mark(
- "LINK_DOCUMENTATION"
- )
+ status_url = settings().status_page_url
+ if status_url:
+ with ui.item().props("clickable"):
+ with ui.item_section().props("avatar"):
+ ui.icon("check_circle", color="primary")
+ with ui.item_section():
+ ui.link("Check Platform Status", status_url, new_tab=True).mark(
+ "LINK_DOCUMENTATION"
+ )
with ui.item().props("clickable"):
with ui.item_section().props("avatar"):
ui.icon("handshake", color="primary")
@@ -368,14 +372,16 @@ def toggle_dark_mode() -> None:
ui.row(align_items="center").classes("justify-start w-full"),
):
health_link()
- with ui.row().style("padding: 0"):
- ui.html(
- '',
- sanitize=False,
- ).style("margin-left: 0px;")
- ui.tooltip("Check Platform Status")
+ status_url = settings().status_page_url
+ if status_url:
+ with ui.row().style("padding: 0"):
+ ui.html(
+ f'',
+ sanitize=False,
+ ).style("margin-left: 0px;")
+ ui.tooltip("Check Platform Status")
ui.space()
with ui.row():
flavor = " (native)" if getattr(sys, "frozen", False) else ""
diff --git a/src/aignostics/platform/__init__.py b/src/aignostics/platform/__init__.py
index 4a9d28efd..9fca31b16 100644
--- a/src/aignostics/platform/__init__.py
+++ b/src/aignostics/platform/__init__.py
@@ -71,6 +71,10 @@
REDIRECT_URI_TEST,
REDIRECT_URI_STAGING,
REDIRECT_URI_PRODUCTION,
+ STATUS_PAGE_URL_DEV,
+ STATUS_PAGE_URL_TEST,
+ STATUS_PAGE_URL_STAGING,
+ STATUS_PAGE_URL_PRODUCTION,
TOKEN_URL_DEV,
TOKEN_URL_TEST,
TOKEN_URL_STAGING,
diff --git a/src/aignostics/platform/_constants.py b/src/aignostics/platform/_constants.py
index c64315b44..8e9be78b2 100644
--- a/src/aignostics/platform/_constants.py
+++ b/src/aignostics/platform/_constants.py
@@ -8,6 +8,7 @@
REDIRECT_URI_DEV = "http://localhost:8989/"
DEVICE_URL_DEV = "https://dev-8ouohmmrbuh2h4vu.eu.auth0.com/oauth/device/code"
JWS_JSON_URL_DEV = "https://dev-8ouohmmrbuh2h4vu.eu.auth0.com/.well-known/jwks.json"
+STATUS_PAGE_URL_DEV = None # No dedicated status page for dev environment
API_ROOT_TEST = "https://platform-test.aignostics.ai"
CLIENT_ID_INTERACTIVE_TEST = "gqduveFvx7LX90drQPGzr4JGUYdh24gA" # not a secret, but a public client ID (same as dev)
@@ -17,6 +18,7 @@
REDIRECT_URI_TEST = "http://localhost:8989/"
DEVICE_URL_TEST = "https://dev-8ouohmmrbuh2h4vu.eu.auth0.com/oauth/device/code"
JWS_JSON_URL_TEST = "https://dev-8ouohmmrbuh2h4vu.eu.auth0.com/.well-known/jwks.json"
+STATUS_PAGE_URL_TEST = None # No dedicated status page for test environment
API_ROOT_STAGING = "https://platform-staging.aignostics.com"
CLIENT_ID_INTERACTIVE_STAGING = "fQkbvYzQPPVwLxc3uque5JsyFW00rJ7b" # not a secret, but a public client ID
@@ -26,6 +28,7 @@
REDIRECT_URI_STAGING = "http://localhost:8989/"
DEVICE_URL_STAGING = "https://aignostics-platform-staging.eu.auth0.com/oauth/device/code"
JWS_JSON_URL_STAGING = "https://aignostics-platform-staging.eu.auth0.com/.well-known/jwks.json"
+STATUS_PAGE_URL_STAGING = "https://status.platform-staging.aignostics.com"
API_ROOT_PRODUCTION = "https://platform.aignostics.com"
CLIENT_ID_INTERACTIVE_PRODUCTION = "YtJ7F9lAtxx16SZGQlYPe6wcjlXB78MM" # not a secret, but a public client ID
@@ -35,6 +38,7 @@
REDIRECT_URI_PRODUCTION = "http://localhost:8989/"
DEVICE_URL_PRODUCTION = "https://aignostics-platform.eu.auth0.com/oauth/device/code"
JWS_JSON_URL_PRODUCTION = "https://aignostics-platform.eu.auth0.com/.well-known/jwks.json"
+STATUS_PAGE_URL_PRODUCTION = "https://status.platform.aignostics.com"
# Pipeline orchestration defaults
DEFAULT_GPU_TYPE = "L4"
diff --git a/src/aignostics/platform/_settings.py b/src/aignostics/platform/_settings.py
index 71ca1d3a5..cf565466c 100644
--- a/src/aignostics/platform/_settings.py
+++ b/src/aignostics/platform/_settings.py
@@ -50,6 +50,10 @@
REDIRECT_URI_TEST,
REDIRECT_URI_STAGING,
REDIRECT_URI_PRODUCTION,
+ STATUS_PAGE_URL_DEV,
+ STATUS_PAGE_URL_TEST,
+ STATUS_PAGE_URL_STAGING,
+ STATUS_PAGE_URL_PRODUCTION,
TOKEN_URL_DEV,
TOKEN_URL_TEST,
TOKEN_URL_STAGING,
@@ -205,6 +209,9 @@ def profile_edit_url(self) -> str:
str, BeforeValidator(_validate_url), Field(description="JWS key set URL for token verification")
]
client_id_interactive: Annotated[str, Field(description="OAuth client ID for interactive flows")]
+ status_page_url: Annotated[
+ str | None, Field(description="Status page URL for monitoring platform health", default=None)
+ ] = None
@computed_field # type: ignore[prop-decorator]
@property
@@ -510,6 +517,20 @@ def pre_init(cls, values: dict) -> dict: # type: ignore[type-arg] # noqa: N805
# See https://github.com/pydantic/pydantic/issues/9789
api_root = values.get("api_root", API_ROOT_PRODUCTION)
+ # Set status_page_url based on environment (independent of auth fields)
+ if "status_page_url" not in values:
+ match api_root:
+ case x if x == API_ROOT_DEV:
+ values["status_page_url"] = STATUS_PAGE_URL_DEV
+ case x if x == API_ROOT_TEST:
+ values["status_page_url"] = STATUS_PAGE_URL_TEST
+ case x if x == API_ROOT_STAGING:
+ values["status_page_url"] = STATUS_PAGE_URL_STAGING
+ case x if x == API_ROOT_PRODUCTION:
+ values["status_page_url"] = STATUS_PAGE_URL_PRODUCTION
+ case _:
+ values["status_page_url"] = None
+
# Check if all required auth fields are already provided
auth_fields = [
"audience",
diff --git a/tests/aignostics/gui/__init__.py b/tests/aignostics/gui/__init__.py
new file mode 100644
index 000000000..ff8b2654f
--- /dev/null
+++ b/tests/aignostics/gui/__init__.py
@@ -0,0 +1 @@
+"""Tests for GUI module."""
diff --git a/tests/aignostics/gui/frame_test.py b/tests/aignostics/gui/frame_test.py
new file mode 100644
index 000000000..15f010b5e
--- /dev/null
+++ b/tests/aignostics/gui/frame_test.py
@@ -0,0 +1,84 @@
+"""Tests for GUI frame module."""
+
+import pytest
+
+from aignostics.platform import (
+ API_ROOT_DEV,
+ API_ROOT_PRODUCTION,
+ API_ROOT_STAGING,
+ API_ROOT_TEST,
+ STATUS_PAGE_URL_DEV,
+ STATUS_PAGE_URL_PRODUCTION,
+ STATUS_PAGE_URL_STAGING,
+ STATUS_PAGE_URL_TEST,
+ Settings,
+)
+
+
+@pytest.mark.unit
+def test_status_page_url_production(record_property) -> None:
+ """Test that production environment has correct status page URL in settings.
+
+ Args:
+ record_property: pytest record_property fixture
+ """
+ record_property("tested-item-id", "SPEC-PLATFORM-SETTINGS")
+ settings = Settings(api_root=API_ROOT_PRODUCTION)
+ assert settings.status_page_url == STATUS_PAGE_URL_PRODUCTION
+ assert settings.status_page_url == "https://status.platform.aignostics.com"
+
+
+@pytest.mark.unit
+def test_status_page_url_staging(record_property) -> None:
+ """Test that staging environment has correct status page URL in settings.
+
+ Args:
+ record_property: pytest record_property fixture
+ """
+ record_property("tested-item-id", "SPEC-PLATFORM-SETTINGS")
+ settings = Settings(api_root=API_ROOT_STAGING)
+ assert settings.status_page_url == STATUS_PAGE_URL_STAGING
+ assert settings.status_page_url == "https://status.platform-staging.aignostics.com"
+
+
+@pytest.mark.unit
+def test_status_page_url_dev(record_property) -> None:
+ """Test that dev environment has no status page URL in settings.
+
+ Args:
+ record_property: pytest record_property fixture
+ """
+ record_property("tested-item-id", "SPEC-PLATFORM-SETTINGS")
+ settings = Settings(api_root=API_ROOT_DEV)
+ assert settings.status_page_url == STATUS_PAGE_URL_DEV
+ assert settings.status_page_url is None
+
+
+@pytest.mark.unit
+def test_status_page_url_test(record_property) -> None:
+ """Test that test environment has no status page URL in settings.
+
+ Args:
+ record_property: pytest record_property fixture
+ """
+ record_property("tested-item-id", "SPEC-PLATFORM-SETTINGS")
+ settings = Settings(api_root=API_ROOT_TEST)
+ assert settings.status_page_url == STATUS_PAGE_URL_TEST
+ assert settings.status_page_url is None
+
+
+@pytest.mark.unit
+def test_status_page_url_configurable(record_property, monkeypatch) -> None:
+ """Test that status page URL can be explicitly configured via settings.
+
+ Args:
+ record_property: pytest record_property fixture
+ monkeypatch: pytest monkeypatch fixture
+ """
+ record_property("tested-item-id", "SPEC-PLATFORM-SETTINGS")
+ # Test that we can override the status page URL
+ custom_url = "https://custom-status.example.com"
+ monkeypatch.setenv("AIGNOSTICS_STATUS_PAGE_URL", custom_url)
+ settings = Settings(api_root=API_ROOT_PRODUCTION)
+ assert settings.status_page_url == custom_url
+