Files
CC-Switch/src/components/proxy/FailoverToggle.tsx
T
Jason a187380d6f fix(failover): switch to P1 immediately when enabling auto failover
Previously, enabling auto failover kept using the current provider until
the first failure, causing inconsistency when the current provider was
not in the failover queue. When stopping proxy, the restored config
would not match user expectations.

New behavior:
- Enable auto failover = immediately switch to queue P1
- Subsequent routing follows queue order (P1→P2→...)
- Auto-add current provider to queue if queue is empty

Changes:
- Add switch_proxy_target() for hot-switching during proxy mode
- Update provider_router to use queue order when failover enabled
- Sync tray menu Auto click with the same logic
- Update UI tooltips to reflect new semantics
- Add tests for queue-only routing scenario
2026-01-21 11:27:44 +08:00

77 lines
2.1 KiB
TypeScript

/**
* 故障转移切换开关组件
*
* 放置在主界面头部,用于一键启用/关闭自动故障转移
*/
import { Shuffle, Loader2 } from "lucide-react";
import { Switch } from "@/components/ui/switch";
import {
useAutoFailoverEnabled,
useSetAutoFailoverEnabled,
} from "@/lib/query/failover";
import { cn } from "@/lib/utils";
import { useTranslation } from "react-i18next";
import type { AppId } from "@/lib/api";
interface FailoverToggleProps {
className?: string;
activeApp: AppId;
}
export function FailoverToggle({ className, activeApp }: FailoverToggleProps) {
const { t } = useTranslation();
const { data: isEnabled = false, isLoading } =
useAutoFailoverEnabled(activeApp);
const setEnabled = useSetAutoFailoverEnabled();
const handleToggle = (checked: boolean) => {
setEnabled.mutate({ appType: activeApp, enabled: checked });
};
const appLabel =
activeApp === "claude"
? "Claude"
: activeApp === "codex"
? "Codex"
: "Gemini";
const tooltipText = isEnabled
? t("failover.tooltip.enabled", {
app: appLabel,
defaultValue: `${appLabel} 故障转移已启用\n按队列优先级(P1→P2→...)选择供应商`,
})
: t("failover.tooltip.disabled", {
app: appLabel,
defaultValue: `启用 ${appLabel} 故障转移\n将立即切换到队列 P1,并在失败时自动切换到下一个`,
});
return (
<div
className={cn(
"flex items-center gap-1 px-1.5 h-8 rounded-lg bg-muted/50 transition-all",
className,
)}
title={tooltipText}
>
{setEnabled.isPending || isLoading ? (
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
) : (
<Shuffle
className={cn(
"h-4 w-4 transition-colors",
isEnabled
? "text-emerald-500 animate-pulse"
: "text-muted-foreground",
)}
/>
)}
<Switch
checked={isEnabled}
onCheckedChange={handleToggle}
disabled={setEnabled.isPending || isLoading}
/>
</div>
);
}