import { useState, useEffect } from "react"; import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Save, Loader2, Info } from "lucide-react"; import { toast } from "sonner"; import { useCircuitBreakerConfig, useUpdateCircuitBreakerConfig, } from "@/lib/query/failover"; export interface AutoFailoverConfigPanelProps { enabled: boolean; onEnabledChange: (enabled: boolean) => void; } export function AutoFailoverConfigPanel({ enabled, onEnabledChange: _onEnabledChange, }: AutoFailoverConfigPanelProps) { // Note: onEnabledChange is currently unused but kept in the interface // for potential future use by parent components void _onEnabledChange; const { t } = useTranslation(); const { data: config, isLoading, error } = useCircuitBreakerConfig(); const updateConfig = useUpdateCircuitBreakerConfig(); const [formData, setFormData] = useState({ failureThreshold: 5, successThreshold: 2, timeoutSeconds: 60, errorRateThreshold: 0.5, minRequests: 10, }); useEffect(() => { if (config) { setFormData({ ...config, }); } }, [config]); const handleSave = async () => { try { await updateConfig.mutateAsync({ failureThreshold: formData.failureThreshold, successThreshold: formData.successThreshold, timeoutSeconds: formData.timeoutSeconds, errorRateThreshold: formData.errorRateThreshold, minRequests: formData.minRequests, }); toast.success( t("proxy.autoFailover.configSaved", "自动故障转移配置已保存"), { closeButton: true }, ); } catch (e) { toast.error( t("proxy.autoFailover.configSaveFailed", "保存失败") + ": " + String(e), ); } }; const handleReset = () => { if (config) { setFormData({ ...config, }); } }; if (isLoading) { return (
); } return (
{/* Header Switch moved to parent accordion logic or kept here absolutely positioned if styling permits. Since we need it in the accordion header, and this component is inside the content, we can use a portal or absolute positioning trick similar to ProxyPanel, OR cleaner, just duplicate the switch logic in SettingsPage and pass it down. But for now, let's use the absolute positioning trick to "lift" it visually. Better yet, let's just render the content directly without the wrapping Card header/collapse logic since the user requested "click to expand is detailed info, no need to fold again" (implying the accordion handles folding). */}
{error && ( {String(error)} )} {t( "proxy.autoFailover.info", "当故障转移队列中配置了多个供应商时,系统会在请求失败时按优先级顺序依次尝试。当某个供应商连续失败达到阈值时,熔断器会打开并在一段时间内跳过该供应商。", )} {/* 重试与超时配置 */}

{t("proxy.autoFailover.retrySettings", "重试与超时设置")}

setFormData({ ...formData, failureThreshold: parseInt(e.target.value) || 5, }) } disabled={!enabled} />

{t( "proxy.autoFailover.failureThresholdHint", "连续失败多少次后打开熔断器(建议: 3-10)", )}

setFormData({ ...formData, timeoutSeconds: parseInt(e.target.value) || 60, }) } disabled={!enabled} />

{t( "proxy.autoFailover.timeoutHint", "熔断器打开后,等待多久后尝试恢复(建议: 30-120)", )}

{/* 熔断器高级配置 */}

{t("proxy.autoFailover.circuitBreakerSettings", "熔断器高级设置")}

setFormData({ ...formData, successThreshold: parseInt(e.target.value) || 2, }) } disabled={!enabled} />

{t( "proxy.autoFailover.successThresholdHint", "半开状态下成功多少次后关闭熔断器", )}

setFormData({ ...formData, errorRateThreshold: (parseInt(e.target.value) || 50) / 100, }) } disabled={!enabled} />

{t( "proxy.autoFailover.errorRateHint", "错误率超过此值时打开熔断器", )}

setFormData({ ...formData, minRequests: parseInt(e.target.value) || 10, }) } disabled={!enabled} />

{t( "proxy.autoFailover.minRequestsHint", "计算错误率前的最小请求数", )}

{/* 操作按钮 */}
{/* 说明信息 */}

{t("proxy.autoFailover.explanationTitle", "工作原理")}

  • •{" "} {t("proxy.autoFailover.failureThresholdLabel", "失败阈值")} : {t( "proxy.autoFailover.failureThresholdExplain", "连续失败达到此次数时,熔断器打开,该供应商暂时不可用", )}
  • •{" "} {t("proxy.autoFailover.timeoutLabel", "恢复等待时间")} : {t( "proxy.autoFailover.timeoutExplain", "熔断器打开后,等待此时间后尝试半开状态", )}
  • •{" "} {t("proxy.autoFailover.successThresholdLabel", "恢复成功阈值")} : {t( "proxy.autoFailover.successThresholdExplain", "半开状态下,成功达到此次数时关闭熔断器,供应商恢复可用", )}
  • •{" "} {t("proxy.autoFailover.errorRateLabel", "错误率阈值")} : {t( "proxy.autoFailover.errorRateExplain", "错误率超过此值时,即使未达到失败阈值也会打开熔断器", )}
); }