"use client"; import { useEffect, useRef, useState } from "react"; import { LoaderCircle, PlugZap, Save, Wifi, } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { fetchProxy, testProxy, updateProxy, type ProxySettings, type ProxyTestResult, } from "@/lib/api"; export function ProxySettingsCard() { const didLoadRef = useRef(false); const [settings, setSettings] = useState({ enabled: false, url: "" }); const [formUrl, setFormUrl] = useState(""); const [formEnabled, setFormEnabled] = useState(false); const [isLoading, setIsLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); const [isTesting, setIsTesting] = useState(false); const [testResult, setTestResult] = useState(null); const load = async () => { setIsLoading(true); try { const data = await fetchProxy(); setSettings(data.proxy); setFormUrl(data.proxy.url); setFormEnabled(data.proxy.enabled); } catch (error) { toast.error(error instanceof Error ? error.message : "加载代理配置失败"); } finally { setIsLoading(false); } }; useEffect(() => { if (didLoadRef.current) { return; } didLoadRef.current = true; void load(); }, []); const urlChanged = formUrl.trim() !== settings.url; const enabledChanged = formEnabled !== settings.enabled; const dirty = urlChanged || enabledChanged; const handleSave = async () => { if (formEnabled && !formUrl.trim()) { toast.error("启用代理时必须填写代理地址"); return; } setIsSaving(true); try { const payload: { enabled?: boolean; url?: string } = {}; if (enabledChanged) payload.enabled = formEnabled; if (urlChanged) payload.url = formUrl.trim(); const data = await updateProxy(payload); setSettings(data.proxy); setFormUrl(data.proxy.url); setFormEnabled(data.proxy.enabled); toast.success("代理配置已保存"); } catch (error) { toast.error(error instanceof Error ? error.message : "保存失败"); } finally { setIsSaving(false); } }; const handleTest = async () => { const candidate = formUrl.trim(); if (!candidate) { toast.error("请先填写代理地址"); return; } setIsTesting(true); setTestResult(null); try { const data = await testProxy(candidate); setTestResult(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 { setIsTesting(false); } }; return (

上游代理配置

为 chatgpt.com 的请求配置出网代理,适合国内服务器部署;Sub2API / CPA 请求不受影响。

{isLoading ? (
) : (
setFormUrl(event.target.value)} placeholder="http://user:pass@host:port 或 socks5://host:port" className="h-11 rounded-xl border-stone-200 bg-white font-mono text-xs" />
支持 http / https / socks4 / socks5 / socks5h
{testResult ? (
{testResult.ok ? ( <> 代理可用:HTTP {testResult.status},用时 {testResult.latency_ms} ms ) : ( <>代理不可用:{testResult.error ?? "未知错误"}(用时 {testResult.latency_ms} ms) )}
) : null}
)}
); }