mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-26 14:35:22 +08:00
a268127f1f
* fix(proxy): change default port from 5000 to 15721 Port 5000 conflicts with AirPlay Receiver on macOS 12+. Also adds error handling for proxy toggle and i18n placeholder updates. * fix(proxy): replace unwrap/expect with graceful error handling - Handle HTTP client initialization failure with no_proxy fallback - Fix potential panic on Unicode slicing in API key preview - Add proper error handling for response body builder - Handle edge case where SystemTime is before UNIX_EPOCH * fix(proxy): handle UTF-8 char boundary when truncating request body log Rust strings are UTF-8 encoded, slicing at a fixed byte index may cut in the middle of a multi-byte character (e.g., Chinese, emoji), causing a panic. Use is_char_boundary() to find the nearest safe cut point. * fix(proxy): improve robustness and prevent panics - Add reqwest socks feature to support SOCKS proxy environments - Fix UTF-8 safety in masked_key/masked_access_token (use chars() instead of byte slicing) - Fix UTF-8 boundary check in usage_script HTTP response truncation - Add defensive checks for JSON operations in proxy service - Remove verbose debug logs that could trigger panic-prone code paths
92 lines
2.7 KiB
TypeScript
92 lines
2.7 KiB
TypeScript
/**
|
|
* 代理模式切换开关组件
|
|
*
|
|
* 放置在主界面头部,用于一键启用/关闭代理模式
|
|
* 启用时自动接管 Live 配置,关闭时恢复原始配置
|
|
*/
|
|
|
|
import { Radio, Loader2 } from "lucide-react";
|
|
import { Switch } from "@/components/ui/switch";
|
|
import { useProxyStatus } from "@/hooks/useProxyStatus";
|
|
import { cn } from "@/lib/utils";
|
|
import { useTranslation } from "react-i18next";
|
|
import type { AppId } from "@/lib/api";
|
|
|
|
interface ProxyToggleProps {
|
|
className?: string;
|
|
activeApp: AppId;
|
|
}
|
|
|
|
export function ProxyToggle({ className, activeApp }: ProxyToggleProps) {
|
|
const { t } = useTranslation();
|
|
const { isRunning, takeoverStatus, setTakeoverForApp, isPending, status } =
|
|
useProxyStatus();
|
|
|
|
const handleToggle = async (checked: boolean) => {
|
|
try {
|
|
await setTakeoverForApp({ appType: activeApp, enabled: checked });
|
|
} catch (error) {
|
|
console.error("[ProxyToggle] Toggle takeover failed:", error);
|
|
}
|
|
};
|
|
|
|
const takeoverEnabled = takeoverStatus?.[activeApp] || false;
|
|
|
|
const appLabel =
|
|
activeApp === "claude"
|
|
? "Claude"
|
|
: activeApp === "codex"
|
|
? "Codex"
|
|
: "Gemini";
|
|
|
|
const tooltipText = takeoverEnabled
|
|
? isRunning
|
|
? t("proxy.takeover.tooltip.active", {
|
|
defaultValue: `${appLabel} 已接管 - ${status?.address}:${status?.port}\n切换该应用供应商为热切换`,
|
|
})
|
|
: t("proxy.takeover.tooltip.broken", {
|
|
defaultValue: `${appLabel} 已接管,但代理服务未运行`,
|
|
})
|
|
: t("proxy.takeover.tooltip.inactive", {
|
|
defaultValue: `接管 ${appLabel} 的 Live 配置,让该应用请求走本地代理`,
|
|
});
|
|
|
|
return (
|
|
<div
|
|
className={cn("p-1 rounded-xl transition-all", className)}
|
|
title={tooltipText}
|
|
>
|
|
<div className="flex items-center gap-2 px-2 h-8 rounded-md cursor-default">
|
|
{isPending ? (
|
|
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
|
|
) : (
|
|
<Radio
|
|
className={cn(
|
|
"h-4 w-4 transition-colors",
|
|
takeoverEnabled
|
|
? "text-emerald-500 animate-pulse"
|
|
: "text-muted-foreground",
|
|
)}
|
|
/>
|
|
)}
|
|
<span
|
|
className={cn(
|
|
"text-sm font-medium transition-colors select-none",
|
|
takeoverEnabled
|
|
? "text-emerald-600 dark:text-emerald-400"
|
|
: "text-muted-foreground",
|
|
)}
|
|
>
|
|
Proxy
|
|
</span>
|
|
<Switch
|
|
checked={takeoverEnabled}
|
|
onCheckedChange={handleToggle}
|
|
disabled={isPending}
|
|
className="ml-1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|