feat(proxy): add full URL mode and refactor endpoint rewriting (#1561)

* feat(proxy): add full URL mode and refactor endpoint rewriting

- Add `isFullUrl` provider meta to treat base_url as complete API endpoint
- Remove hardcoded `?beta=true` from Claude adapter, pass through from client
- Refactor forwarder endpoint rewriting with proper query string handling
- Block provider switching when proxy is required but not running
- Add full URL toggle UI in endpoint field with i18n (zh/en/ja)

* refactor(proxy): remove beta query handling

* fix(proxy): strip beta query when rewriting Claude endpoints

* feat(codex): complete full URL support

* refactor(ui): refine full URL endpoint hint
This commit is contained in:
Dex Miller
2026-03-28 15:52:02 +08:00
committed by GitHub
parent eaf83f4fbe
commit 8cae7b7b73
16 changed files with 454 additions and 103 deletions
@@ -1,7 +1,8 @@
import { useTranslation } from "react-i18next";
import { FormLabel } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Zap } from "lucide-react";
import { Switch } from "@/components/ui/switch";
import { Link2, Zap } from "lucide-react";
interface EndpointFieldProps {
id: string;
@@ -13,6 +14,9 @@ interface EndpointFieldProps {
showManageButton?: boolean;
onManageClick?: () => void;
manageButtonLabel?: string;
showFullUrlToggle?: boolean;
isFullUrl?: boolean;
onFullUrlChange?: (value: boolean) => void;
}
export function EndpointField({
@@ -25,18 +29,56 @@ export function EndpointField({
showManageButton = true,
onManageClick,
manageButtonLabel,
showFullUrlToggle = false,
isFullUrl = false,
onFullUrlChange,
}: EndpointFieldProps) {
const { t } = useTranslation();
const defaultManageLabel = t("providerForm.manageAndTest", {
defaultValue: "管理和测速",
});
const effectiveHint =
showFullUrlToggle && isFullUrl
? t("providerForm.fullUrlHint", {
defaultValue:
"💡 请填写完整请求 URL,并且必须开启代理后使用;代理将直接使用此 URL,不拼接路径",
})
: hint;
return (
<div className="space-y-2">
<div className="flex items-center justify-between">
<FormLabel htmlFor={id}>{label}</FormLabel>
{showManageButton && onManageClick && (
<div className="flex flex-wrap items-center justify-between gap-2">
<div className="flex flex-wrap items-center gap-3">
<FormLabel htmlFor={id}>{label}</FormLabel>
{showFullUrlToggle && onFullUrlChange ? (
<div className="flex items-center gap-2 rounded-full border border-border/70 bg-muted/30 px-2.5 py-1">
<Link2
className={`h-3.5 w-3.5 ${
isFullUrl ? "text-primary" : "text-muted-foreground"
}`}
/>
<span
className={`text-xs font-medium ${
isFullUrl ? "text-foreground" : "text-muted-foreground"
}`}
>
{t("providerForm.fullUrlLabel", {
defaultValue: "完整 URL",
})}
</span>
<Switch
checked={isFullUrl}
onCheckedChange={onFullUrlChange}
aria-label={t("providerForm.fullUrlLabel", {
defaultValue: "完整 URL",
})}
className="h-5 w-9"
/>
</div>
) : null}
</div>
{showManageButton && onManageClick ? (
<button
type="button"
onClick={onManageClick}
@@ -45,7 +87,7 @@ export function EndpointField({
<Zap className="h-3.5 w-3.5" />
{manageButtonLabel || defaultManageLabel}
</button>
)}
) : null}
</div>
<Input
id={id}
@@ -55,9 +97,11 @@ export function EndpointField({
placeholder={placeholder}
autoComplete="off"
/>
{hint ? (
{effectiveHint ? (
<div className="p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-700 rounded-lg">
<p className="text-xs text-amber-600 dark:text-amber-400">{hint}</p>
<p className="text-xs text-amber-600 dark:text-amber-400">
{effectiveHint}
</p>
</div>
) : null}
</div>