diff --git a/app/src/admin/channel.ts b/app/src/admin/channel.ts index 88aae07..0e3371e 100644 --- a/app/src/admin/channel.ts +++ b/app/src/admin/channel.ts @@ -23,6 +23,8 @@ export type Channel = { proxy?: { proxy: string; proxy_type: number; + username: string; + password: string; }; }; diff --git a/app/src/components/admin/ChannelSettings.tsx b/app/src/components/admin/ChannelSettings.tsx index b60e687..af0ca85 100644 --- a/app/src/components/admin/ChannelSettings.tsx +++ b/app/src/components/admin/ChannelSettings.tsx @@ -3,6 +3,13 @@ import ChannelTable from "@/components/admin/assemblies/ChannelTable.tsx"; import ChannelEditor from "@/components/admin/assemblies/ChannelEditor.tsx"; import { Channel, getChannelInfo } from "@/admin/channel.ts"; +const initialProxyState = { + proxy: "", + proxy_type: 0, + username: "", + password: "", +}; + const initialState: Channel = { id: -1, type: "openai", @@ -16,10 +23,7 @@ const initialState: Channel = { mapper: "", state: true, group: [], - proxy: { - proxy: "", - proxy_type: 0, - }, + proxy: { ...initialProxyState }, }; function reducer(state: Channel, action: any): Channel { @@ -87,6 +91,8 @@ function reducer(state: Channel, action: any): Channel { proxy: { proxy: action.value as string, proxy_type: state?.proxy?.proxy_type || 0, + password: state?.proxy?.password || "", + username: state?.proxy?.username || "", }, }; case "set-proxy-type": @@ -95,6 +101,28 @@ function reducer(state: Channel, action: any): Channel { proxy: { proxy: state?.proxy?.proxy || "", proxy_type: action.value as number, + password: state?.proxy?.password || "", + username: state?.proxy?.username || "", + }, + }; + case "set-proxy-username": + return { + ...state, + proxy: { + proxy: state?.proxy?.proxy || "", + proxy_type: state?.proxy?.proxy_type || 0, + password: state?.proxy?.password || "", + username: action.value as string, + }, + }; + case "set-proxy-password": + return { + ...state, + proxy: { + proxy: state?.proxy?.proxy || "", + proxy_type: state?.proxy?.proxy_type || 0, + password: action.value as string, + username: state?.proxy?.username || "", }, }; case "set": diff --git a/app/src/components/admin/assemblies/ChannelEditor.tsx b/app/src/components/admin/assemblies/ChannelEditor.tsx index 1115043..7cbb212 100644 --- a/app/src/components/admin/assemblies/ChannelEditor.tsx +++ b/app/src/components/admin/assemblies/ChannelEditor.tsx @@ -453,6 +453,38 @@ function ChannelEditor({ disabled={edit.proxy?.proxy_type === 0} /> + +
+ {t("admin.channels.proxy-username")} +
+ + dispatch({ + type: "set-proxy-username", + value: e.target.value, + }) + } + disabled={edit.proxy?.proxy_type === 0} + /> +
+ +
+ {t("admin.channels.proxy-password")} +
+ + dispatch({ + type: "set-proxy-password", + value: e.target.value, + }) + } + disabled={edit.proxy?.proxy_type === 0} + /> +
{t("admin.channels.proxy-desc")} diff --git a/app/src/components/admin/assemblies/ChannelTable.tsx b/app/src/components/admin/assemblies/ChannelTable.tsx index 1a21a84..c97acb1 100644 --- a/app/src/components/admin/assemblies/ChannelTable.tsx +++ b/app/src/components/admin/assemblies/ChannelTable.tsx @@ -100,7 +100,7 @@ function SyncDialog({ dispatch, open, setOpen }: SyncDialogProps) { mapper: "", state: true, group: [], - proxy: { proxy: "", proxy_type: 0 }, + proxy: { proxy: "", proxy_type: 0, username: "", password: "" }, }; dispatch({ type: "set", value: data }); diff --git a/app/src/resources/i18n/cn.json b/app/src/resources/i18n/cn.json index 2a94870..741346e 100644 --- a/app/src/resources/i18n/cn.json +++ b/app/src/resources/i18n/cn.json @@ -633,6 +633,10 @@ "proxy-type": "代理类型", "proxy-endpoint": "代理地址", "proxy-endpoint-placeholder": "请输入正向代理地址,如:socks5://example.com:1080", + "proxy-username": "代理用户名", + "proxy-username-placeholder": "请输入代理的鉴权用户名 (可选)", + "proxy-password": "代理密码", + "proxy-password-placeholder": "请输入代理的鉴权密码 (可选)", "proxy-desc": "正向代理,支持 HTTP/HTTPS/SOCKS5 代理 (反向代理请填写接入点, 非特殊情况无需设置正向代理)" }, "charge": { diff --git a/app/src/resources/i18n/en.json b/app/src/resources/i18n/en.json index c5b0fd7..f94a464 100644 --- a/app/src/resources/i18n/en.json +++ b/app/src/resources/i18n/en.json @@ -461,7 +461,11 @@ "proxy-type": "Delegate Type", "proxy-endpoint": "proxy address", "proxy-endpoint-placeholder": "Please enter forward proxy address, ex: socks5://example.com: 1080", - "proxy-desc": "Forward proxy, supports HTTP/HTTPS/SOCKS5 proxy (reverse proxy please fill in the access point, no need to set forward proxy in non-special cases)" + "proxy-desc": "Forward proxy, supports HTTP/HTTPS/SOCKS5 proxy (reverse proxy please fill in the access point, no need to set forward proxy in non-special cases)", + "proxy-username": "Proxy User", + "proxy-username-placeholder": "Please enter the agent's authentication username (optional)", + "proxy-password": "Proxy password", + "proxy-password-placeholder": "Please enter the agent's authentication password (optional)" }, "charge": { "id": "ID", diff --git a/app/src/resources/i18n/ja.json b/app/src/resources/i18n/ja.json index 58c006f..c183e1e 100644 --- a/app/src/resources/i18n/ja.json +++ b/app/src/resources/i18n/ja.json @@ -461,7 +461,11 @@ "proxy-type": "プロキシのタイプ", "proxy-endpoint": "プロキシアドレス", "proxy-endpoint-placeholder": "転送プロキシアドレスを入力してください。例: socks 5 :// example.com: 1080", - "proxy-desc": "フォワードプロキシ、HTTP/HTTPS/SOCKS 5プロキシをサポート(リバースプロキシはアクセスポイントに記入してください。特別な場合以外はフォワードプロキシを設定する必要はありません)" + "proxy-desc": "フォワードプロキシ、HTTP/HTTPS/SOCKS 5プロキシをサポート(リバースプロキシはアクセスポイントに記入してください。特別な場合以外はフォワードプロキシを設定する必要はありません)", + "proxy-username": "プロキシユーザー名", + "proxy-username-placeholder": "エージェントの認証ユーザー名を入力してください(オプション)", + "proxy-password": "プロキシパスワード", + "proxy-password-placeholder": "エージェントの認証パスワードを入力してください(オプション)" }, "charge": { "id": "ID", diff --git a/app/src/resources/i18n/ru.json b/app/src/resources/i18n/ru.json index d06a273..daf4386 100644 --- a/app/src/resources/i18n/ru.json +++ b/app/src/resources/i18n/ru.json @@ -461,7 +461,11 @@ "proxy-type": "Тип прокси- сервера", "proxy-endpoint": "Адрес прокси-сервера", "proxy-endpoint-placeholder": "Введите адрес прямого прокси-сервера, например: socks5://example.com: 1080", - "proxy-desc": "Прямой прокси-сервер, поддерживает HTTP/HTTPS/Socks5 прокси-сервер (обратный прокси-сервер, пожалуйста, заполните точку доступа, нет необходимости устанавливать прокси-сервер в неспециальных случаях)" + "proxy-desc": "Прямой прокси-сервер, поддерживает HTTP/HTTPS/Socks5 прокси-сервер (обратный прокси-сервер, пожалуйста, заполните точку доступа, нет необходимости устанавливать прокси-сервер в неспециальных случаях)", + "proxy-username": "Имя пользователя прокси-сервера", + "proxy-username-placeholder": "Введите имя пользователя для аутентификации агента (необязательно)", + "proxy-password": "Пароль прокси-сервера", + "proxy-password-placeholder": "Введите пароль аутентификации агента (необязательно)" }, "charge": { "id": "ID", diff --git a/globals/types.go b/globals/types.go index dcaa359..9da33eb 100644 --- a/globals/types.go +++ b/globals/types.go @@ -49,4 +49,6 @@ type ListModelsItem struct { type ProxyConfig struct { ProxyType int `json:"proxy_type" mapstructure:"proxytype"` Proxy string `json:"proxy" mapstructure:"proxy"` + Username string `json:"username" mapstructure:"username"` + Password string `json:"password" mapstructure:"password"` } diff --git a/utils/net.go b/utils/net.go index 5017377..b747061 100644 --- a/utils/net.go +++ b/utils/net.go @@ -38,6 +38,10 @@ func newClient(c []globals.ProxyConfig) *http.Client { if config.ProxyType == globals.HttpProxyType || config.ProxyType == globals.HttpsProxyType { proxyUrl, err := url.Parse(config.Proxy) + if len(config.Username) > 0 || len(config.Password) > 0 { + proxyUrl.User = url.UserPassword(config.Username, config.Password) + } + if err != nil { globals.Warn(fmt.Sprintf("failed to parse proxy url: %s", err)) return client @@ -47,7 +51,15 @@ func newClient(c []globals.ProxyConfig) *http.Client { TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } } else if config.ProxyType == globals.Socks5ProxyType { - dialer, err := proxy.SOCKS5("tcp", config.Proxy, nil, proxy.Direct) + var auth *proxy.Auth + if len(config.Username) > 0 || len(config.Password) > 0 { + auth = &proxy.Auth{ + User: config.Username, + Password: config.Password, + } + } + + dialer, err := proxy.SOCKS5("tcp", config.Proxy, auth, proxy.Direct) if err != nil { globals.Warn(fmt.Sprintf("failed to create socks5 proxy: %s", err)) return client