Skip to content

Commit 1cd3a7a

Browse files
committed
feat(frontend): expose upstream network proxy settings
1 parent 9caddee commit 1cd3a7a

3 files changed

Lines changed: 70 additions & 0 deletions

File tree

frontend/src/features/settings/SettingsPage.test.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ const baseSettings = {
2121
usage_request_timeout_seconds: 15,
2222
proxy_host: "127.0.0.1",
2323
proxy_port: 6789,
24+
upstream_proxy_mode: "system",
25+
upstream_proxy_url: "",
26+
upstream_proxy_username: "",
27+
upstream_proxy_password: "",
2428
auto_failover_enabled: true,
2529
auto_backup_interval_hours: 24,
2630
backup_retention_count: 10,
@@ -107,6 +111,8 @@ describe("SettingsPage", () => {
107111
expect(screen.getByRole("tab", { name: "关于" })).toBeInTheDocument();
108112
fireEvent.click(screen.getByRole("tab", { name: "代理" }));
109113
expect(await screen.findByRole("switch", { name: "自动故障转移开关" })).toBeInTheDocument();
114+
fireEvent.click(screen.getByRole("radio", { name: "手动指定" }));
115+
fireEvent.change(screen.getByRole("textbox", { name: "上游代理地址" }), { target: { value: "http://127.0.0.1:7890" } });
110116
expect(screen.queryByText("自动故障转移队列")).not.toBeInTheDocument();
111117
fireEvent.click(screen.getByRole("tab", { name: "数据" }));
112118
expect(await screen.findByText("费用统计")).toBeInTheDocument();
@@ -125,6 +131,8 @@ describe("SettingsPage", () => {
125131
body: JSON.stringify({
126132
...baseSettings,
127133
launch_at_login: true,
134+
upstream_proxy_mode: "manual",
135+
upstream_proxy_url: "http://127.0.0.1:7890",
128136
provider_pricing: { codex: { input_per_million: 4.5, output_per_million: 0 } },
129137
account_pricing: { "1": { input_per_million: 0, output_per_million: 15.2 } },
130138
}),
@@ -134,12 +142,16 @@ describe("SettingsPage", () => {
134142
expect(applyDesktopAppSettings).toHaveBeenCalledWith({
135143
...baseSettings,
136144
launch_at_login: true,
145+
upstream_proxy_mode: "manual",
146+
upstream_proxy_url: "http://127.0.0.1:7890",
137147
provider_pricing: { codex: { input_per_million: 4.5, output_per_million: 0 } },
138148
account_pricing: { "1": { input_per_million: 0, output_per_million: 15.2 } },
139149
});
140150
expect(onSettingsChanged).toHaveBeenCalledWith({
141151
...baseSettings,
142152
launch_at_login: true,
153+
upstream_proxy_mode: "manual",
154+
upstream_proxy_url: "http://127.0.0.1:7890",
143155
provider_pricing: { codex: { input_per_million: 4.5, output_per_million: 0 } },
144156
account_pricing: { "1": { input_per_million: 0, output_per_million: 15.2 } },
145157
});

frontend/src/features/settings/SettingsPage.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,10 @@ export function SettingsPage({
183183
provider_pricing: initialSettings.provider_pricing ?? {},
184184
account_pricing: initialSettings.account_pricing ?? {},
185185
usage_request_timeout_seconds: initialSettings.usage_request_timeout_seconds ?? 15,
186+
upstream_proxy_mode: initialSettings.upstream_proxy_mode ?? "system",
187+
upstream_proxy_url: initialSettings.upstream_proxy_url ?? "",
188+
upstream_proxy_username: initialSettings.upstream_proxy_username ?? "",
189+
upstream_proxy_password: initialSettings.upstream_proxy_password ?? "",
186190
});
187191
}, [initialSettings]);
188192

@@ -580,6 +584,56 @@ export function SettingsPage({
580584
</div>
581585
</Card>
582586

587+
<Card className="settings-card" variant="borderless">
588+
<SectionHeader
589+
icon={<DesktopOutlined />}
590+
title={t("上游网络代理")}
591+
description={t("仅影响 AI Gate 发往上游服务商的网络请求,不影响 AI Gate 本地代理监听地址。")}
592+
/>
593+
<div className="settings-stack">
594+
<Radio.Group
595+
value={draftSettings.upstream_proxy_mode ?? "system"}
596+
onChange={(event) => updateDraft({ upstream_proxy_mode: event.target.value })}
597+
>
598+
<Radio value="system">{t("跟随系统")}</Radio>
599+
<Radio value="direct">{t("直连")}</Radio>
600+
<Radio value="manual">{t("手动指定")}</Radio>
601+
</Radio.Group>
602+
<Text type="secondary">{t("Clash / Mihomo 常见地址示例:")}http://127.0.0.1:7890</Text>
603+
</div>
604+
{draftSettings.upstream_proxy_mode === "manual" ? (
605+
<div className="settings-field-grid">
606+
<label className="settings-field">
607+
<span className="settings-field-label">{t("上游代理地址")}</span>
608+
<Input
609+
aria-label={t("上游代理地址")}
610+
value={draftSettings.upstream_proxy_url}
611+
onChange={(event) => updateDraft({ upstream_proxy_url: event.target.value })}
612+
placeholder="http://127.0.0.1:7890"
613+
/>
614+
</label>
615+
<label className="settings-field">
616+
<span className="settings-field-label">{t("上游代理用户名(可选)")}</span>
617+
<Input
618+
aria-label={t("上游代理用户名(可选)")}
619+
value={draftSettings.upstream_proxy_username}
620+
onChange={(event) => updateDraft({ upstream_proxy_username: event.target.value })}
621+
placeholder={t("留空则不使用认证")}
622+
/>
623+
</label>
624+
<label className="settings-field">
625+
<span className="settings-field-label">{t("上游代理密码(可选)")}</span>
626+
<Input.Password
627+
aria-label={t("上游代理密码(可选)")}
628+
value={draftSettings.upstream_proxy_password}
629+
onChange={(event) => updateDraft({ upstream_proxy_password: event.target.value })}
630+
placeholder={t("留空则不使用认证")}
631+
/>
632+
</label>
633+
</div>
634+
) : null}
635+
</Card>
636+
583637
</div>
584638
),
585639
},

frontend/src/lib/api.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ export type AppSettings = {
228228
usage_request_timeout_seconds?: number;
229229
proxy_host: string;
230230
proxy_port: number;
231+
upstream_proxy_mode?: "system" | "direct" | "manual";
232+
upstream_proxy_url?: string;
233+
upstream_proxy_username?: string;
234+
upstream_proxy_password?: string;
231235
auto_failover_enabled: boolean;
232236
auto_backup_interval_hours: number;
233237
backup_retention_count: number;

0 commit comments

Comments
 (0)