fix(failover): patch P1-P3 reliability gaps surfaced by team review

- Forwarder buffers non-streaming bodies and primes streaming first
  chunk before signaling success, so body timeouts and SSE first-chunk
  failures route through the circuit breaker instead of being recorded
  as success on response-header arrival
- Atomic enable-failover: switch to P1 before persisting the flag, and
  roll back auto-added queue entries when the switch is rejected
  (e.g. official providers)
- Hot-reload circuit breaker config on per-app proxy config change
  instead of waiting for a proxy restart
- FailoverToggle / FailoverQueueManager / AutoFailoverConfigPanel
  require proxy takeover for the active app; the backend command also
  rejects enabling when takeover is off
- ProviderHealthBadge consumes the backend is_healthy flag instead of
  hardcoding the 5-failure threshold

Cleanup:
- impl From<&AppProxyConfig> for CircuitBreakerConfig and use it from
  the command layer
- Collapse three identical TabsContent blocks into a single map
This commit is contained in:
Jason
2026-05-14 21:35:02 +08:00
parent 940161fb0e
commit b642ef0633
14 changed files with 389 additions and 98 deletions
@@ -364,6 +364,7 @@ export function ProviderCard({
{isProxyRunning && isInFailoverQueue && health && (
<ProviderHealthBadge
consecutiveFailures={health.consecutive_failures}
isHealthy={health.is_healthy}
/>
)}
@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
interface ProviderHealthBadgeProps {
consecutiveFailures: number;
isHealthy?: boolean;
className?: string;
}
@@ -13,6 +14,7 @@ interface ProviderHealthBadgeProps {
*/
export function ProviderHealthBadge({
consecutiveFailures,
isHealthy,
className,
}: ProviderHealthBadgeProps) {
const { t } = useTranslation();
@@ -29,7 +31,7 @@ export function ProviderHealthBadge({
bgColor: "bg-green-500/10",
textColor: "text-green-600 dark:text-green-400",
};
} else if (consecutiveFailures < 5) {
} else if (isHealthy !== false) {
return {
labelKey: "health.degraded",
labelFallback: "降级",