diff --git a/src/app/(dashboard)/dashboard/health/page.tsx b/src/app/(dashboard)/dashboard/health/page.tsx index 58afb12afd..6871ed33a0 100644 --- a/src/app/(dashboard)/dashboard/health/page.tsx +++ b/src/app/(dashboard)/dashboard/health/page.tsx @@ -44,6 +44,7 @@ export default function HealthPage() { const [telemetry, setTelemetry] = useState(null); const [cache, setCache] = useState(null); const [signatureCache, setSignatureCache] = useState(null); + const [resetting, setResetting] = useState(false); const fetchHealth = useCallback(async () => { try { @@ -82,6 +83,27 @@ export default function HealthPage() { return () => clearInterval(interval); }, [fetchHealth, fetchExtras]); + const handleResetHealth = async () => { + if ( + !confirm( + "Reset all circuit breakers to healthy state? This will clear all failure counts and restore all providers to operational status." + ) + ) + return; + setResetting(true); + try { + const res = await fetch("/api/monitoring/health", { method: "DELETE" }); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + // Refresh health data immediately + await fetchHealth(); + await fetchExtras(); + } catch (err) { + console.error("Failed to reset health:", err); + } finally { + setResetting(false); + } + }; + const fmtMs = (ms) => (ms != null ? `${Math.round(ms)}ms` : "—"); if (!data && !error) { @@ -332,19 +354,47 @@ export default function HealthPage() { Provider Health - {cbEntries.length > 0 && ( -
- - Healthy - - - Recovering - - - Down - -
- )} +
+ {cbEntries.some(([, cb]: [string, any]) => cb.state !== "CLOSED") && ( + + )} + {cbEntries.length > 0 && ( +
+ + Healthy + + + Recovering + + + Down + +
+ )} +
{cbEntries.length === 0 ? (

@@ -505,69 +555,75 @@ export default function HealthPage() {

- {entries.map(({ key, displayName, providerInfo, connectionId, model, status }: any) => { - const isActive = (status.queued || 0) + (status.running || 0) > 0; - const isQueued = (status.queued || 0) > 0; - return ( -
-
-
- {providerInfo?.textIcon || displayName.slice(0, 2).toUpperCase()} -
-
-

- {displayName} -

- {connectionId && ( -

- {connectionId.length > 12 - ? connectionId.slice(0, 8) + "…" - : connectionId} - {model && · {model}} + {entries.map( + ({ key, displayName, providerInfo, connectionId, model, status }: any) => { + const isActive = (status.queued || 0) + (status.running || 0) > 0; + const isQueued = (status.queued || 0) > 0; + return ( +

+
+
+ {providerInfo?.textIcon || displayName.slice(0, 2).toUpperCase()} +
+
+

+ {displayName}

- )} + {connectionId && ( +

+ {connectionId.length > 12 + ? connectionId.slice(0, 8) + "…" + : connectionId} + {model && ( + · {model} + )} +

+ )} +
+ + {isQueued ? "Queued" : isActive ? "Active" : "OK"} + +
+
+ + schedule + {status.queued || 0} queued + + + + play_arrow + + {status.running || 0} running +
- - {isQueued ? "Queued" : isActive ? "Active" : "OK"} -
-
- - schedule - {status.queued || 0} queued - - - play_arrow - {status.running || 0} running - -
-
- ); - })} + ); + } + )}
); diff --git a/src/app/api/monitoring/health/route.ts b/src/app/api/monitoring/health/route.ts index 99ac4b334a..cdf0b0d8f4 100644 --- a/src/app/api/monitoring/health/route.ts +++ b/src/app/api/monitoring/health/route.ts @@ -12,8 +12,7 @@ export async function GET() { try { const { getAllCircuitBreakerStatuses } = await import("@/../../src/shared/utils/circuitBreaker"); - const { getAllRateLimitStatus } = - await import("@omniroute/open-sse/services/rateLimitManager"); + const { getAllRateLimitStatus } = await import("@omniroute/open-sse/services/rateLimitManager"); const { getAllModelLockouts } = await import("@omniroute/open-sse/services/accountFallback"); const settings = await getSettings(); @@ -57,3 +56,32 @@ export async function GET() { return NextResponse.json({ status: "error", error: error.message }, { status: 500 }); } } + +/** + * DELETE /api/monitoring/health — Reset all circuit breakers + * + * Resets all provider circuit breakers to CLOSED state, + * clearing failure counts and persisted state. + */ +export async function DELETE() { + try { + const { resetAllCircuitBreakers, getAllCircuitBreakerStatuses } = + await import("@/../../src/shared/utils/circuitBreaker"); + + const before = getAllCircuitBreakerStatuses(); + const resetCount = before.length; + + resetAllCircuitBreakers(); + + console.log(`[API] DELETE /api/monitoring/health — Reset ${resetCount} circuit breakers`); + + return NextResponse.json({ + success: true, + message: `Reset ${resetCount} circuit breaker(s) to healthy state`, + resetCount, + }); + } catch (error) { + console.error("[API] DELETE /api/monitoring/health error:", error); + return NextResponse.json({ error: error.message }, { status: 500 }); + } +}