feat: support authentication in http/https/socks5 proxies (#126)

This commit is contained in:
Zhang Minghan 2024-03-15 19:23:00 +08:00
parent 3f7e3f9d4b
commit 93c18bb34c
10 changed files with 101 additions and 9 deletions

View File

@ -23,6 +23,8 @@ export type Channel = {
proxy?: {
proxy: string;
proxy_type: number;
username: string;
password: string;
};
};

View File

@ -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":

View File

@ -453,6 +453,38 @@ function ChannelEditor({
disabled={edit.proxy?.proxy_type === 0}
/>
</ParagraphItem>
<ParagraphItem>
<div className={`channel-content`}>
{t("admin.channels.proxy-username")}
</div>
<Input
value={edit.proxy?.username || ""}
placeholder={t("admin.channels.proxy-username-placeholder")}
onChange={(e) =>
dispatch({
type: "set-proxy-username",
value: e.target.value,
})
}
disabled={edit.proxy?.proxy_type === 0}
/>
</ParagraphItem>
<ParagraphItem>
<div className={`channel-content`}>
{t("admin.channels.proxy-password")}
</div>
<Input
value={edit.proxy?.password || ""}
placeholder={t("admin.channels.proxy-password-placeholder")}
onChange={(e) =>
dispatch({
type: "set-proxy-password",
value: e.target.value,
})
}
disabled={edit.proxy?.proxy_type === 0}
/>
</ParagraphItem>
<ParagraphDescription>
{t("admin.channels.proxy-desc")}
</ParagraphDescription>

View File

@ -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 });

View File

@ -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": {

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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"`
}

View File

@ -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