diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 881c12caeb3..628bc673f64 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -86,6 +86,7 @@ import { getClientConfig } from "../config/client"; import { useSyncStore } from "../store/sync"; import { nanoid } from "nanoid"; import { useMaskStore } from "../store/mask"; +import { PROXY_MODES, ProxyMode } from "../store/access"; import { ProviderType } from "../utils/cloud"; import { TTSConfigList } from "./tts-config"; import { RealtimeConfigList } from "./realtime-chat/realtime-config"; @@ -737,6 +738,66 @@ export function Settings() { > ); + const desktopProxyConfigComponent = clientConfig?.isApp && ( + <> + + + + + {accessStore.proxyMode !== "system" && ( + <> + + + accessStore.update( + (access) => (access.proxyHost = e.currentTarget.value), + ) + } + > + + + + + accessStore.update( + (access) => (access.proxyPort = e.currentTarget.value), + ) + } + > + + + )} + + ); const openAIConfigComponent = accessStore.provider === ServiceProvider.OpenAI && ( @@ -1459,44 +1520,44 @@ export function Settings() { ); - const ai302ConfigComponent = accessStore.provider === ServiceProvider["302.AI"] && ( + const ai302ConfigComponent = accessStore.provider === + ServiceProvider["302.AI"] && ( <> + + accessStore.update( + (access) => (access.ai302Url = e.currentTarget.value), + ) } - > - - accessStore.update( - (access) => (access.ai302Url = e.currentTarget.value), - ) - } - > - - - { - accessStore.update( - (access) => (access.ai302ApiKey = e.currentTarget.value), - ); - }} - /> - - + > + + + { + accessStore.update( + (access) => (access.ai302ApiKey = e.currentTarget.value), + ); + }} + /> + + ); return ( @@ -1868,6 +1929,7 @@ export function Settings() { )} )} + {desktopProxyConfigComponent} {!shouldHideBalanceQuery && !clientConfig?.isApp ? ( { method: method.toUpperCase(), url, headers, + proxy_url: getDesktopProxyUrl(), // TODO FormData body: typeof body === "string" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 8a11c3b6f98..9cabbc4b702 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -38,7 +38,7 @@ tauri = { version = "1.5.4", features = [ "http-all", ] } tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } percent-encoding = "2.3.1" -reqwest = "0.11.18" +reqwest = { version = "0.11.18", features = ["socks"] } futures-util = "0.3.30" bytes = "1.7.2" diff --git a/src-tauri/src/stream.rs b/src-tauri/src/stream.rs index 8320db3e48a..45ea39369b0 100644 --- a/src-tauri/src/stream.rs +++ b/src-tauri/src/stream.rs @@ -6,7 +6,7 @@ use std::error::Error; use std::sync::atomic::{AtomicU32, Ordering}; use std::collections::HashMap; use futures_util::{StreamExt}; -use reqwest::Client; +use reqwest::{Client, Proxy}; use reqwest::header::{HeaderName, HeaderMap}; static REQUEST_COUNTER: AtomicU32 = AtomicU32::new(0); @@ -38,6 +38,7 @@ pub async fn stream_fetch( url: String, headers: HashMap, body: Vec, + proxy_url: Option, ) -> Result { let event_name = "stream-response"; @@ -54,10 +55,21 @@ pub async fn stream_fetch( // println!("headers: {:?}", _headers); let method = method.parse::().map_err(|err| format!("failed to parse method: {}", err))?; - let client = Client::builder() + let mut builder = Client::builder() .default_headers(_headers) .redirect(reqwest::redirect::Policy::limited(3)) - .connect_timeout(Duration::new(3, 0)) + .connect_timeout(Duration::new(3, 0)); + + if let Some(ref pu) = proxy_url { + let pu = pu.trim().to_string(); + if !pu.is_empty() { + let proxy = Proxy::all(&pu) + .map_err(|err| format!("failed to parse proxy url: {}", err))?; + builder = builder.no_proxy().proxy(proxy); + } + } + + let client = builder .build() .map_err(|err| format!("failed to generate client: {}", err))?;