Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | 36x 36x 36x 36x 36x 36x 36x 36x 2x 2x 2x 36x 19x 19x 27x 27x 27x 27x 18x 16x 14x 14x 10x 10x 10x 26x 19x 19x 19x 19x 19x 36x 24x 2x 22x 1x 36x 32x 6x 6x 26x 2x 2x 36x 32x 32x 32x 32x 2x 36x 3x 2x 2x 1x 36x 5x 5x 36x | import { useState, useEffect, useRef, useCallback } from "react";
import { useToast } from "@/hooks/use-toast";
import { useWebSocket } from "@/hooks/use-websocket";
import type { QueryClient } from "@tanstack/react-query";
export function useBackendHealth(queryClient: QueryClient) {
const [backendReachable, setBackendReachable] = useState(true);
const [backendPingError, setBackendPingError] = useState<string | null>(null);
const [showErrorGlitch, setShowErrorGlitch] = useState(false);
// Ref to track if backend was ever unreachable (for recovery toast)
const wasBackendUnreachableRef = useRef(false);
// Ref to track previous backend reachable state for detecting transitions
const prevBackendReachableRef = useRef(true);
const { toast } = useToast();
const { isConnected, connectionError, hasEverConnected } = useWebSocket();
// Trigger visual glitch effect on compilation error
const triggerErrorGlitch = useCallback((duration = 600) => {
try {
setShowErrorGlitch(true);
window.setTimeout(() => setShowErrorGlitch(false), duration);
} catch {}
}, []);
// Lightweight backend ping every second
useEffect(() => {
let cancelled = false;
const ping = async () => {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 800);
try {
const res = await fetch("/api/health", {
method: "GET",
cache: "no-store",
signal: controller.signal,
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
if (!cancelled) {
setBackendReachable(true);
setBackendPingError(null);
}
} catch (err) {
Eif (!cancelled) {
setBackendReachable(false);
setBackendPingError((err as Error)?.message || "Health check failed");
}
} finally {
clearTimeout(timeout);
}
};
const interval = setInterval(ping, 1000);
ping();
return () => {
cancelled = true;
clearInterval(interval);
};
}, []);
// WebSocket reachability notifications
useEffect(() => {
if (connectionError) {
toast({
title: "Backend unreachable",
description: connectionError,
variant: "destructive",
});
} else if (!isConnected && hasEverConnected) {
toast({
title: "Connection lost",
description: "Trying to re-establish backend connection...",
variant: "destructive",
});
}
}, [connectionError, isConnected, hasEverConnected, toast]);
// Show toast when HTTP backend becomes unreachable or recovers
useEffect(() => {
if (!backendReachable) {
wasBackendUnreachableRef.current = true;
toast({
title: "Backend unreachable",
description: backendPingError || "Could not reach API server.",
variant: "destructive",
});
} else if (backendReachable && wasBackendUnreachableRef.current) {
// Backend recovered after being unreachable
wasBackendUnreachableRef.current = false;
toast({
title: "Backend reachable again",
description: "Connection restored.",
});
}
}, [backendReachable, backendPingError, toast]);
// Refetch sketches when backend becomes reachable again (false -> true transition)
useEffect(() => {
const wasUnreachable = !prevBackendReachableRef.current;
const isNowReachable = backendReachable;
// Update the ref for next check
prevBackendReachableRef.current = backendReachable;
if (wasUnreachable && isNowReachable) {
// Backend just transitioned from unreachable to reachable
queryClient.refetchQueries({ queryKey: ["/api/sketches"] });
}
}, [backendReachable, queryClient]);
const ensureBackendConnected = useCallback(
(actionLabel: string) => {
if (!backendReachable || !isConnected) {
toast({
title: "Backend unreachable",
description:
backendPingError ||
connectionError ||
`${actionLabel} failed because the backend is not reachable. Please check the server or retry in a moment.`,
variant: "destructive",
});
return false;
}
return true;
},
[backendReachable, isConnected, backendPingError, connectionError, toast],
);
const isBackendUnreachableError = useCallback((error: unknown) => {
const message = (error as Error | undefined)?.message || "";
return (
message.includes("Failed to fetch") ||
message.includes("NetworkError") ||
message.includes("ERR_CONNECTION") ||
message.includes("Network request failed")
);
}, []);
return {
backendReachable,
backendPingError,
showErrorGlitch,
ensureBackendConnected,
isBackendUnreachableError,
triggerErrorGlitch,
};
}
|