From 6c75397901d5f9218d23f7a4303e9c5827bd076c Mon Sep 17 00:00:00 2001 From: rx18-eng Date: Sat, 9 May 2026 15:28:54 +0530 Subject: [PATCH 1/2] surface backend error message when start mining fails Signed-off-by: rx18-eng --- src/pages/UnifiedDashboard.tsx | 83 ++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 24 deletions(-) diff --git a/src/pages/UnifiedDashboard.tsx b/src/pages/UnifiedDashboard.tsx index 2d7d5f7..47972d0 100644 --- a/src/pages/UnifiedDashboard.tsx +++ b/src/pages/UnifiedDashboard.tsx @@ -122,19 +122,28 @@ export function UnifiedDashboard() { const diagnostics = logDiagnostics?.diagnostics ?? []; const [isStarting, setIsStarting] = useState(false); + const [startError, setStartError] = useState(null); const handleStartMining = async () => { setIsStarting(true); + setStartError(null); try { const response = await fetch('/api/restart', { method: 'POST' }); - if (response.ok) { - // Give containers time to start, then refresh health checks - setTimeout(() => { - window.location.reload(); - }, 3000); + const errorData = await response.json().catch(() => ({})); + if (!response.ok) { + throw new Error( + errorData.error || errorData.message || `Failed (${response.status})`, + ); } + // Give containers time to start, then refresh health checks + setTimeout(() => { + window.location.reload(); + }, 3000); } catch (error) { console.error('Failed to start mining:', error); + setStartError( + error instanceof Error ? error.message : 'Failed to start mining services', + ); setIsStarting(false); } }; @@ -467,26 +476,52 @@ export function UnifiedDashboard() { {/* Start Mining Banner (configured but stopped) */} {configuredButStopped && showError && ( -
-
- - Mining services are stopped. + startError ? ( +
+
+ +
+ Could not start mining services + {startError} +
+
+
- -
+ ) : ( +
+
+ + Mining services are stopped. +
+ +
+ ) )} {/* Connection Error Banner (not configured or unknown error) */} From 2f1d6c7079a9ece1ae45fd4f5e577d1317c9936f Mon Sep 17 00:00:00 2001 From: rx18-eng Date: Sat, 9 May 2026 15:33:06 +0530 Subject: [PATCH 2/2] surface docker reachability in dashboard banner Signed-off-by: rx18-eng --- server/src/index.ts | 10 +++++++++- server/src/types.ts | 3 +++ src/hooks/useSetupStatus.ts | 5 +++++ src/pages/UnifiedDashboard.tsx | 28 +++++++++++++++++++++++++++- 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/server/src/index.ts b/server/src/index.ts index f56a7ea..72d6379 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -103,7 +103,14 @@ app.get('/api/status', async (_req, res) => { try { const state = await loadState(); const containers = await getStackStatus(state.mode); - + + // Cap the Docker reachability probe so a slow daemon ping does not stall + // the /api/status response. The frontend has its own 1.5s abort on top. + const dockerReachable = await Promise.race([ + isDockerAvailable(), + new Promise((resolve) => setTimeout(() => resolve(false), 500)), + ]); + const running = state.mode === 'jd' ? (containers.translator?.status === 'healthy' || containers.translator?.status === 'starting') && (containers.jdc?.status === 'healthy' || containers.jdc?.status === 'starting') @@ -118,6 +125,7 @@ app.get('/api/status', async (_req, res) => { ? 'Sovereign Solo Mining' : (state.data?.pool?.name ?? null), containers, + docker: { reachable: dockerReachable }, }; res.json(response); diff --git a/server/src/types.ts b/server/src/types.ts index d3dcafe..d3f889a 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -66,6 +66,9 @@ export interface StatusResponse { translator: ContainerStatus | null; jdc: ContainerStatus | null; }; + docker: { + reachable: boolean; + }; } export interface SetupResponse { diff --git a/src/hooks/useSetupStatus.ts b/src/hooks/useSetupStatus.ts index 31af5a0..c768657 100644 --- a/src/hooks/useSetupStatus.ts +++ b/src/hooks/useSetupStatus.ts @@ -10,6 +10,9 @@ export interface SetupStatus { translator: { id: string; name: string; status: string } | null; jdc: { id: string; name: string; status: string } | null; }; + docker?: { + reachable: boolean; + }; } /** @@ -73,6 +76,8 @@ export function useSetupStatus() { mode: status?.mode ?? null, poolName: status?.poolName ?? null, containers: status?.containers ?? { translator: null, jdc: null }, + // null when standalone or before first status response; true/false from the backend. + dockerReachable: status?.docker?.reachable ?? null, // User needs setup if: orchestrated mode AND not yet configured needsSetup: status !== null && status !== undefined && !status.configured, refetch: query.refetch, diff --git a/src/pages/UnifiedDashboard.tsx b/src/pages/UnifiedDashboard.tsx index 47972d0..4b55b2b 100644 --- a/src/pages/UnifiedDashboard.tsx +++ b/src/pages/UnifiedDashboard.tsx @@ -81,6 +81,7 @@ export function UnifiedDashboard() { miningMode, mode: templateMode, poolName: configPoolName, + dockerReachable, } = useSetupStatus(); // Header connection status (shared with Settings via hook) @@ -476,7 +477,32 @@ export function UnifiedDashboard() { {/* Start Mining Banner (configured but stopped) */} {configuredButStopped && showError && ( - startError ? ( + dockerReachable === false ? ( +
+
+ +
+ Docker isn't running + We couldn't reach the Docker daemon, so mining services can't start. + Start Docker Desktop or Docker Engine, then click Start Mining. +
+
+ +
+ ) : startError ? (