fix: Docker health indicator shows immediately after login

Use $effect instead of onMount to start SSE and health polling,
so they activate on client-side navigation after login without
requiring a full page reload.
This commit is contained in:
2026-03-30 13:54:35 +03:00
parent 37cfa090ac
commit 4041252028
+29 -22
View File
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import '../app.css'; import '../app.css';
import type { Snippet } from 'svelte'; import type { Snippet } from 'svelte';
import { onMount, onDestroy } from 'svelte'; import { onDestroy } from 'svelte';
import { page } from '$app/stores'; import { page } from '$app/stores';
import Toast from '$lib/components/Toast.svelte'; import Toast from '$lib/components/Toast.svelte';
import ThemeToggle from '$lib/components/ThemeToggle.svelte'; import ThemeToggle from '$lib/components/ThemeToggle.svelte';
@@ -37,6 +37,7 @@
let sseConnection: SSEConnection | null = null; let sseConnection: SSEConnection | null = null;
let sidebarOpen = $state(false); let sidebarOpen = $state(false);
let dockerConnected = $state<boolean | null>(null); let dockerConnected = $state<boolean | null>(null);
let healthChecked = $state(false);
let healthInterval: ReturnType<typeof setInterval> | null = null; let healthInterval: ReturnType<typeof setInterval> | null = null;
// Hide sidebar and chrome on the login page. // Hide sidebar and chrome on the login page.
@@ -69,29 +70,35 @@
window.location.href = '/login'; window.location.href = '/login';
} }
onMount(() => { // Start SSE and health polling when authenticated.
if (isAuthenticated()) { // Uses $effect to react to route changes (e.g., after login navigation).
sseConnection = connectGlobalEvents({ $effect(() => {
onInstanceStatus(payload) { // Read pathname to re-run on navigation.
instanceStatusStore.update(payload); void $page.url.pathname;
},
onDeployStatus(payload) {
instanceStatusStore.notifyDeploy(payload);
}
});
// Poll Docker health every 30s. if (!isAuthenticated() || sseConnection) return;
async function checkHealth() {
try { sseConnection = connectGlobalEvents({
const h = await api.getHealth(); onInstanceStatus(payload) {
dockerConnected = h.docker; instanceStatusStore.update(payload);
} catch { },
dockerConnected = null; onDeployStatus(payload) {
} instanceStatusStore.notifyDeploy(payload);
} }
checkHealth(); });
healthInterval = setInterval(checkHealth, 30_000);
// Poll Docker health every 30s.
async function checkHealth() {
try {
const h = await api.getHealth();
dockerConnected = h.docker;
} catch {
dockerConnected = false;
}
healthChecked = true;
} }
checkHealth();
healthInterval = setInterval(checkHealth, 30_000);
}); });
onDestroy(() => { onDestroy(() => {
@@ -173,7 +180,7 @@
<!-- Footer controls --> <!-- Footer controls -->
<div class="space-y-3 border-t border-[var(--border-primary)] px-4 py-3"> <div class="space-y-3 border-t border-[var(--border-primary)] px-4 py-3">
{#if dockerConnected !== null} {#if healthChecked}
<div class="flex items-center gap-2 rounded-md px-2 py-1.5 text-xs {dockerConnected ? 'text-emerald-600' : 'text-red-500'}"> <div class="flex items-center gap-2 rounded-md px-2 py-1.5 text-xs {dockerConnected ? 'text-emerald-600' : 'text-red-500'}">
<span class="relative flex h-2 w-2"> <span class="relative flex h-2 w-2">
{#if dockerConnected} {#if dockerConnected}