From 35f7b77c1b1d55915a47cf97ccd0e412b5f42c4e Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sat, 3 Jan 2026 19:50:12 -0300 Subject: [PATCH] ui: improve account/apps layout and lab status responsiveness --- backend/atlas_portal/routes/lab.py | 13 ++++++++++++- backend/atlas_portal/settings.py | 2 +- frontend/src/App.vue | 18 ++++++++++++++++-- frontend/src/views/AccountView.vue | 23 ++++++++++++++++++++--- frontend/src/views/AppsView.vue | 29 ++++++++++++----------------- 5 files changed, 61 insertions(+), 24 deletions(-) diff --git a/backend/atlas_portal/routes/lab.py b/backend/atlas_portal/routes/lab.py index 9dba035..5909176 100644 --- a/backend/atlas_portal/routes/lab.py +++ b/backend/atlas_portal/routes/lab.py @@ -60,6 +60,9 @@ def register(app) -> None: if cached and (now - float(_LAB_STATUS_CACHE.get("ts", 0.0)) < settings.LAB_STATUS_CACHE_SEC): return jsonify(cached) + t_total = time.perf_counter() + timings_ms: dict[str, int] = {} + connected = False atlas_up = False atlas_known = False @@ -71,7 +74,9 @@ def register(app) -> None: # Atlas try: + t_probe = time.perf_counter() atlas_grafana_ok = _http_ok(settings.GRAFANA_HEALTH_URL, expect_substring="ok") + timings_ms["grafana"] = int((time.perf_counter() - t_probe) * 1000) if atlas_grafana_ok: connected = True atlas_up = True @@ -82,7 +87,9 @@ def register(app) -> None: if not atlas_known: try: + t_probe = time.perf_counter() value = _vm_query("up") + timings_ms["victoria_metrics"] = int((time.perf_counter() - t_probe) * 1000) if value is not None: connected = True atlas_known = True @@ -93,7 +100,9 @@ def register(app) -> None: # Oceanus (node-exporter direct probe) try: + t_probe = time.perf_counter() if _http_ok(settings.OCEANUS_NODE_EXPORTER_URL): + timings_ms["oceanus_node_exporter"] = int((time.perf_counter() - t_probe) * 1000) connected = True oceanus_known = True oceanus_up = True @@ -101,14 +110,16 @@ def register(app) -> None: except Exception: pass + timings_ms["total"] = int((time.perf_counter() - t_total) * 1000) + payload = { "connected": connected, "atlas": {"up": atlas_up, "known": atlas_known, "source": atlas_source}, "oceanus": {"up": oceanus_up, "known": oceanus_known, "source": oceanus_source}, "checked_at": int(now), + "timings_ms": timings_ms, } _LAB_STATUS_CACHE["ts"] = now _LAB_STATUS_CACHE["value"] = payload return jsonify(payload) - diff --git a/backend/atlas_portal/settings.py b/backend/atlas_portal/settings.py index dd1729f..df2c30f 100644 --- a/backend/atlas_portal/settings.py +++ b/backend/atlas_portal/settings.py @@ -16,7 +16,7 @@ VM_QUERY_TIMEOUT_SEC = float(os.getenv("VM_QUERY_TIMEOUT_SEC", "2")) HTTP_CHECK_TIMEOUT_SEC = float(os.getenv("HTTP_CHECK_TIMEOUT_SEC", "2")) K8S_API_TIMEOUT_SEC = float(os.getenv("K8S_API_TIMEOUT_SEC", "5")) LAB_STATUS_CACHE_SEC = float(os.getenv("LAB_STATUS_CACHE_SEC", "30")) -GRAFANA_HEALTH_URL = os.getenv("GRAFANA_HEALTH_URL", "https://metrics.bstein.dev/api/health") +GRAFANA_HEALTH_URL = os.getenv("GRAFANA_HEALTH_URL", "http://grafana.monitoring.svc.cluster.local/api/health") OCEANUS_NODE_EXPORTER_URL = os.getenv("OCEANUS_NODE_EXPORTER_URL", "http://192.168.22.24:9100/metrics") AI_CHAT_API = os.getenv("AI_CHAT_API", "http://ollama.ai.svc.cluster.local:11434").rstrip("/") diff --git a/frontend/src/App.vue b/frontend/src/App.vue index da6d4e6..38a0b86 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -25,19 +25,33 @@ const networkData = ref(fallbackNetwork()); const metricsData = ref(fallbackMetrics()); const statusLoading = ref(true); +const statusFetching = ref(false); const statusError = ref(""); async function refreshLabStatus() { + if (statusFetching.value) return; + statusFetching.value = true; + const controller = new AbortController(); + const timeoutId = window.setTimeout(() => controller.abort(), 6000); try { - const resp = await fetch("/api/lab/status", { headers: { Accept: "application/json" } }); + const resp = await fetch("/api/lab/status", { + headers: { Accept: "application/json" }, + signal: controller.signal, + }); if (!resp.ok) throw new Error(`status ${resp.status}`); labStatus.value = await resp.json(); statusError.value = ""; } catch (err) { labStatus.value = null; - statusError.value = "Live data unavailable"; + if (err?.name === "AbortError") { + statusError.value = "Live data timed out"; + } else { + statusError.value = "Live data unavailable"; + } } finally { + window.clearTimeout(timeoutId); statusLoading.value = false; + statusFetching.value = false; } } diff --git a/frontend/src/views/AccountView.vue b/frontend/src/views/AccountView.vue index b539e8b..ce11950 100644 --- a/frontend/src/views/AccountView.vue +++ b/frontend/src/views/AccountView.vue @@ -25,7 +25,7 @@
-
+