"use client"; import { AlertTriangle, Cookie, LoaderCircle, PlugZap, Save, ShieldCheck } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; import { testProxy, testProxyClearance, type ClearanceTestResult, type ProxyRuntimeClearanceMode, type ProxyRuntimeEgressMode, type ProxyTestResult, } from "@/lib/api"; import { useSettingsStore } from "../store"; export function ProxyRuntimeCard() { const [isTestingProxy, setIsTestingProxy] = useState(false); const [isTestingClearance, setIsTestingClearance] = useState(false); const [proxyResult, setProxyResult] = useState(null); const [clearanceResult, setClearanceResult] = useState(null); const [targetUrl, setTargetUrl] = useState("https://chatgpt.com"); const config = useSettingsStore((state) => state.config); const isLoadingConfig = useSettingsStore((state) => state.isLoadingConfig); const isSavingConfig = useSettingsStore((state) => state.isSavingConfig); const saveConfig = useSettingsStore((state) => state.saveConfig); const setProxyRuntimeField = useSettingsStore((state) => state.setProxyRuntimeField); const setProxyRuntimeClearanceField = useSettingsStore((state) => state.setProxyRuntimeClearanceField); const setProxyRuntimeStatusCodesText = useSettingsStore((state) => state.setProxyRuntimeStatusCodesText); if (isLoadingConfig || !config?.proxy_runtime) { return ( ); } const runtime = config.proxy_runtime; const clearance = runtime.clearance; const runtimeEnabled = Boolean(runtime.enabled); const clearanceMode = clearance.mode; const hasStoredClearance = Boolean(clearance.has_cf_cookies || clearance.has_cf_clearance); const handleTestRuntimeProxy = async () => { setIsTestingProxy(true); setProxyResult(null); try { const saved = await saveConfig(); if (!saved) { return; } const data = await testProxy(); setProxyResult(data.result); if (data.result.ok) { toast.success(`清障代理可用(${data.result.latency_ms} ms,HTTP ${data.result.status})`); } else { toast.error(`清障代理不可用:${data.result.error ?? "未知错误"}`); } } catch (error) { toast.error(error instanceof Error ? error.message : "测试清障代理失败"); } finally { setIsTestingProxy(false); } }; const handleTestClearance = async () => { setIsTestingClearance(true); setClearanceResult(null); try { const saved = await saveConfig(); if (!saved) { return; } const data = await testProxyClearance(targetUrl.trim() || "https://chatgpt.com"); setClearanceResult(data.result); if (data.result.ok) { toast.success(`Clearance 获取成功(${data.result.latency_ms} ms)`); } else { toast.error(`Clearance 获取失败:${data.result.error ?? data.result.status}`); } } catch (error) { toast.error(error instanceof Error ? error.message : "测试 Clearance 失败"); } finally { setIsTestingClearance(false); } }; return (
FlareSolverr 清障

默认关闭。用于上游请求遇到 Cloudflare 拦截后获取 clearance,可配合 WARP / Privoxy 代理链路重试。

{runtimeEnabled ? "已启用" : "未启用"}
代理优先级:账号代理 > FlareSolverr 代理链路 > 显式代理 > 全局代理。Cookie / cf_clearance 不会在接口响应中明文返回。
使用 FlareSolverr 模式前,请先通过 Docker 启动 flaresolverr、privoxy、warp-proxy 等相关容器;容器内 URL 通常填写 http://flaresolverr:8191。

WARP compose 默认使用 single_proxy。

setProxyRuntimeField("proxy_url", event.target.value)} placeholder="http://privoxy:8118" className="h-10 rounded-xl border-stone-200 bg-white" disabled={!runtimeEnabled || runtime.egress_mode !== "single_proxy"} />

支持 http/https/socks5/socks5h,socks5 会转为 socks5h。带认证格式:协议://账号:密码@主机:端口,也可直接粘贴 主机:端口:账号:密码。

setProxyRuntimeField("resource_proxy_url", event.target.value)} placeholder="留空则复用清障代理" className="h-10 rounded-xl border-stone-200 bg-white" disabled={!runtimeEnabled || runtime.egress_mode !== "single_proxy"} />
setProxyRuntimeStatusCodesText(event.target.value)} placeholder="403" className="h-10 rounded-xl border-stone-200 bg-white" disabled={!runtimeEnabled} />

默认 403,只对 Cloudflare/挑战类错误触发。

{proxyResult ? (
{proxyResult.ok ? `代理可用:HTTP ${proxyResult.status},用时 ${proxyResult.latency_ms} ms,来源 ${proxyResult.proxy_source ?? "unknown"}` : `代理不可用:${proxyResult.error ?? "未知错误"}(用时 ${proxyResult.latency_ms} ms)`}
) : null}
Cloudflare Clearance
{clearance.enabled ? clearanceMode : "disabled"}
setProxyRuntimeClearanceField("flaresolverr_url", event.target.value)} placeholder="http://flaresolverr:8191" className="h-10 rounded-xl border-stone-200 bg-white" disabled={!runtimeEnabled || clearanceMode !== "flaresolverr"} />
setProxyRuntimeClearanceField("user_agent", event.target.value)} className="h-10 rounded-xl border-stone-200 bg-white font-mono text-xs" disabled={!runtimeEnabled || clearanceMode === "none"} />
setProxyRuntimeClearanceField("timeout_sec", event.target.value)} placeholder="60" className="h-10 rounded-xl border-stone-200 bg-white" disabled={!runtimeEnabled || clearanceMode === "none"} />
setProxyRuntimeClearanceField("refresh_interval", event.target.value)} placeholder="3600" className="h-10 rounded-xl border-stone-200 bg-white" disabled={!runtimeEnabled || clearanceMode === "none"} />