mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-14 06:07:23 +08:00
feat: support bypass proxy (#8791)
* feat(ProxyManager): implement SelectiveDispatcher for localhost handling - Added SelectiveDispatcher to manage proxy and direct connections based on the hostname. - Introduced isLocalhost function to check for localhost addresses. - Updated ProxyManager to bypass proxy for localhost in dispatch methods and set proxy bypass rules. - Enhanced global dispatcher setup to utilize SelectiveDispatcher for both EnvHttpProxyAgent and SOCKS dispatcher. * refactor(ProxyManager): update axios configuration to use fetch adapter - Changed axios to use the 'fetch' adapter for proxy requests. - Removed previous proxy settings for axios, streamlining the configuration. - Updated HTTP methods to bind with the new proxy agent. * feat(Proxy): add support for proxy bypass rules - Updated IPC handler to accept optional bypass rules for proxy configuration. - Enhanced ProxyManager to store and utilize bypass rules for localhost and other specified addresses. - Modified settings and UI components to allow users to input and manage bypass rules. - Added translations for bypass rules in multiple languages. * feat(ProxyManager): add HTTP_PROXY environment variable support - Added support for the HTTP_PROXY environment variable in ProxyManager to enhance proxy configuration capabilities. * lint * refactor(ProxyManager): optimize bypass rules handling - Updated bypass rules initialization to split the rules string into an array for improved performance. - Simplified the isByPass function to directly check against the array of bypass rules. - Enhanced configuration handling to ensure bypass rules are correctly parsed from the provided settings. * refactor(ProxyManager): streamline bypass rules initialization - Consolidated the initialization of bypass rules by directly splitting the default rules string into an array. - Updated configuration handling to ensure bypass rules are correctly assigned without redundant splitting. * style(GeneralSettings): adjust proxy bypass rules input width to improve UI layout * refactor(ProxyManager): enhance proxy configuration logging and handling - Added proxy bypass rules to the configuration method for improved flexibility. - Updated logging to include bypass rules for better debugging. - Refactored the setGlobalProxy method to accept configuration parameters directly, streamlining proxy setup. - Adjusted the useAppInit hook to handle proxy settings more cleanly. * refactor(ProxyManager): implement close and destroy methods for proxy dispatcher - Added close and destroy methods to the SelectiveDispatcher class for better resource management. - Updated ProxyManager to handle the lifecycle of the proxyDispatcher, ensuring proper closure and destruction. - Enhanced error handling during dispatcher closure and destruction to prevent resource leaks. * refactor(ProxyManager): manage proxy agent lifecycle - Introduced proxyAgent property to ProxyManager for better management of the proxy agent. - Implemented error handling during the destruction of the proxy agent to prevent potential issues. - Updated the proxy setup process to ensure the proxy agent is correctly initialized and cleaned up. * refactor(ProxyManager): centralize default bypass rules management - Moved default bypass rules to a shared constant for consistency across components. - Updated ProxyManager and GeneralSettings to utilize the centralized bypass rules. - Adjusted migration logic to set default bypass rules from the shared constant, ensuring uniformity in configuration.
This commit is contained in:
parent
0e1df2460e
commit
efda20c143
@ -206,3 +206,5 @@ export enum UpgradeChannel {
|
||||
export const defaultTimeout = 10 * 1000 * 60
|
||||
|
||||
export const occupiedDirs = ['logs', 'Network', 'Partitions/webview/Network']
|
||||
|
||||
export const defaultByPassRules = 'localhost,127.0.0.1,::1'
|
||||
|
||||
@ -90,7 +90,7 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
|
||||
installPath: path.dirname(app.getPath('exe'))
|
||||
}))
|
||||
|
||||
ipcMain.handle(IpcChannel.App_Proxy, async (_, proxy: string) => {
|
||||
ipcMain.handle(IpcChannel.App_Proxy, async (_, proxy: string, bypassRules?: string) => {
|
||||
let proxyConfig: ProxyConfig
|
||||
|
||||
if (proxy === 'system') {
|
||||
@ -101,6 +101,10 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
|
||||
proxyConfig = { mode: 'direct' }
|
||||
}
|
||||
|
||||
if (bypassRules) {
|
||||
proxyConfig.proxyBypassRules = bypassRules
|
||||
}
|
||||
|
||||
await proxyManager.configureProxy(proxyConfig)
|
||||
})
|
||||
|
||||
|
||||
@ -7,14 +7,63 @@ import https from 'https'
|
||||
import { getSystemProxy } from 'os-proxy-config'
|
||||
import { ProxyAgent } from 'proxy-agent'
|
||||
import { Dispatcher, EnvHttpProxyAgent, getGlobalDispatcher, setGlobalDispatcher } from 'undici'
|
||||
import { defaultByPassRules } from '@shared/config/constant'
|
||||
|
||||
const logger = loggerService.withContext('ProxyManager')
|
||||
let byPassRules = defaultByPassRules.split(',')
|
||||
|
||||
const isByPass = (hostname: string) => {
|
||||
return byPassRules.includes(hostname)
|
||||
}
|
||||
|
||||
class SelectiveDispatcher extends Dispatcher {
|
||||
private proxyDispatcher: Dispatcher
|
||||
private directDispatcher: Dispatcher
|
||||
|
||||
constructor(proxyDispatcher: Dispatcher, directDispatcher: Dispatcher) {
|
||||
super()
|
||||
this.proxyDispatcher = proxyDispatcher
|
||||
this.directDispatcher = directDispatcher
|
||||
}
|
||||
|
||||
dispatch(opts: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandlers) {
|
||||
if (opts.origin) {
|
||||
const url = new URL(opts.origin)
|
||||
// 检查是否为 localhost 或本地地址
|
||||
if (isByPass(url.hostname)) {
|
||||
return this.directDispatcher.dispatch(opts, handler)
|
||||
}
|
||||
}
|
||||
|
||||
return this.proxyDispatcher.dispatch(opts, handler)
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
try {
|
||||
await this.proxyDispatcher.close()
|
||||
} catch (error) {
|
||||
logger.error('Failed to close dispatcher:', error as Error)
|
||||
this.proxyDispatcher.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
async destroy(): Promise<void> {
|
||||
try {
|
||||
await this.proxyDispatcher.destroy()
|
||||
} catch (error) {
|
||||
logger.error('Failed to destroy dispatcher:', error as Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ProxyManager {
|
||||
private config: ProxyConfig = { mode: 'direct' }
|
||||
private systemProxyInterval: NodeJS.Timeout | null = null
|
||||
private isSettingProxy = false
|
||||
|
||||
private proxyDispatcher: Dispatcher | null = null
|
||||
private proxyAgent: ProxyAgent | null = null
|
||||
|
||||
private originalGlobalDispatcher: Dispatcher
|
||||
private originalSocksDispatcher: Dispatcher
|
||||
// for http and https
|
||||
@ -44,7 +93,8 @@ export class ProxyManager {
|
||||
|
||||
await this.configureProxy({
|
||||
mode: 'system',
|
||||
proxyRules: currentProxy?.proxyUrl.toLowerCase()
|
||||
proxyRules: currentProxy?.proxyUrl.toLowerCase(),
|
||||
proxyBypassRules: this.config.proxyBypassRules
|
||||
})
|
||||
}, 1000 * 60)
|
||||
}
|
||||
@ -57,7 +107,8 @@ export class ProxyManager {
|
||||
}
|
||||
|
||||
async configureProxy(config: ProxyConfig): Promise<void> {
|
||||
logger.debug(`configureProxy: ${config?.mode} ${config?.proxyRules}`)
|
||||
logger.info(`configureProxy: ${config?.mode} ${config?.proxyRules} ${config?.proxyBypassRules}`)
|
||||
|
||||
if (this.isSettingProxy) {
|
||||
return
|
||||
}
|
||||
@ -65,11 +116,6 @@ export class ProxyManager {
|
||||
this.isSettingProxy = true
|
||||
|
||||
try {
|
||||
if (config?.mode === this.config?.mode && config?.proxyRules === this.config?.proxyRules) {
|
||||
logger.debug('proxy config is the same, skip configure')
|
||||
return
|
||||
}
|
||||
|
||||
this.config = config
|
||||
this.clearSystemProxyMonitor()
|
||||
if (config.mode === 'system') {
|
||||
@ -81,7 +127,8 @@ export class ProxyManager {
|
||||
this.monitorSystemProxy()
|
||||
}
|
||||
|
||||
this.setGlobalProxy()
|
||||
byPassRules = config.proxyBypassRules?.split(',') || defaultByPassRules.split(',')
|
||||
this.setGlobalProxy(this.config)
|
||||
} catch (error) {
|
||||
logger.error('Failed to config proxy:', error as Error)
|
||||
throw error
|
||||
@ -115,12 +162,12 @@ export class ProxyManager {
|
||||
}
|
||||
}
|
||||
|
||||
private setGlobalProxy() {
|
||||
this.setEnvironment(this.config.proxyRules || '')
|
||||
this.setGlobalFetchProxy(this.config)
|
||||
this.setSessionsProxy(this.config)
|
||||
private setGlobalProxy(config: ProxyConfig) {
|
||||
this.setEnvironment(config.proxyRules || '')
|
||||
this.setGlobalFetchProxy(config)
|
||||
this.setSessionsProxy(config)
|
||||
|
||||
this.setGlobalHttpProxy(this.config)
|
||||
this.setGlobalHttpProxy(config)
|
||||
}
|
||||
|
||||
private setGlobalHttpProxy(config: ProxyConfig) {
|
||||
@ -129,21 +176,18 @@ export class ProxyManager {
|
||||
http.request = this.originalHttpRequest
|
||||
https.get = this.originalHttpsGet
|
||||
https.request = this.originalHttpsRequest
|
||||
|
||||
axios.defaults.proxy = undefined
|
||||
axios.defaults.httpAgent = undefined
|
||||
axios.defaults.httpsAgent = undefined
|
||||
try {
|
||||
this.proxyAgent?.destroy()
|
||||
} catch (error) {
|
||||
logger.error('Failed to destroy proxy agent:', error as Error)
|
||||
}
|
||||
this.proxyAgent = null
|
||||
return
|
||||
}
|
||||
|
||||
// ProxyAgent 从环境变量读取代理配置
|
||||
const agent = new ProxyAgent()
|
||||
|
||||
// axios 使用代理
|
||||
axios.defaults.proxy = false
|
||||
axios.defaults.httpAgent = agent
|
||||
axios.defaults.httpsAgent = agent
|
||||
|
||||
this.proxyAgent = agent
|
||||
http.get = this.bindHttpMethod(this.originalHttpGet, agent)
|
||||
http.request = this.bindHttpMethod(this.originalHttpRequest, agent)
|
||||
|
||||
@ -176,16 +220,19 @@ export class ProxyManager {
|
||||
callback = args[1]
|
||||
}
|
||||
|
||||
// filter localhost
|
||||
if (url) {
|
||||
const hostname = typeof url === 'string' ? new URL(url).hostname : url.hostname
|
||||
if (isByPass(hostname)) {
|
||||
return originalMethod(url, options, callback)
|
||||
}
|
||||
}
|
||||
|
||||
// for webdav https self-signed certificate
|
||||
if (options.agent instanceof https.Agent) {
|
||||
;(agent as https.Agent).options.rejectUnauthorized = options.agent.options.rejectUnauthorized
|
||||
}
|
||||
|
||||
// 确保只设置 agent,不修改其他网络选项
|
||||
if (!options.agent) {
|
||||
options.agent = agent
|
||||
}
|
||||
|
||||
options.agent = agent
|
||||
if (url) {
|
||||
return originalMethod(url, options, callback)
|
||||
}
|
||||
@ -198,22 +245,33 @@ export class ProxyManager {
|
||||
if (config.mode === 'direct' || !proxyUrl) {
|
||||
setGlobalDispatcher(this.originalGlobalDispatcher)
|
||||
global[Symbol.for('undici.globalDispatcher.1')] = this.originalSocksDispatcher
|
||||
axios.defaults.adapter = 'http'
|
||||
this.proxyDispatcher?.close()
|
||||
this.proxyDispatcher = null
|
||||
return
|
||||
}
|
||||
|
||||
// axios 使用 fetch 代理
|
||||
axios.defaults.adapter = 'fetch'
|
||||
|
||||
const url = new URL(proxyUrl)
|
||||
if (url.protocol === 'http:' || url.protocol === 'https:') {
|
||||
setGlobalDispatcher(new EnvHttpProxyAgent())
|
||||
this.proxyDispatcher = new SelectiveDispatcher(new EnvHttpProxyAgent(), this.originalGlobalDispatcher)
|
||||
setGlobalDispatcher(this.proxyDispatcher)
|
||||
return
|
||||
}
|
||||
|
||||
global[Symbol.for('undici.globalDispatcher.1')] = socksDispatcher({
|
||||
port: parseInt(url.port),
|
||||
type: url.protocol === 'socks4:' ? 4 : 5,
|
||||
host: url.hostname,
|
||||
userId: url.username || undefined,
|
||||
password: url.password || undefined
|
||||
})
|
||||
this.proxyDispatcher = new SelectiveDispatcher(
|
||||
socksDispatcher({
|
||||
port: parseInt(url.port),
|
||||
type: url.protocol === 'socks4:' ? 4 : 5,
|
||||
host: url.hostname,
|
||||
userId: url.username || undefined,
|
||||
password: url.password || undefined
|
||||
}),
|
||||
this.originalSocksDispatcher
|
||||
)
|
||||
global[Symbol.for('undici.globalDispatcher.1')] = this.proxyDispatcher
|
||||
}
|
||||
|
||||
private async setSessionsProxy(config: ProxyConfig): Promise<void> {
|
||||
|
||||
@ -41,7 +41,8 @@ export function tracedInvoke(channel: string, spanContext: SpanContext | undefin
|
||||
const api = {
|
||||
getAppInfo: () => ipcRenderer.invoke(IpcChannel.App_Info),
|
||||
reload: () => ipcRenderer.invoke(IpcChannel.App_Reload),
|
||||
setProxy: (proxy: string | undefined) => ipcRenderer.invoke(IpcChannel.App_Proxy, proxy),
|
||||
setProxy: (proxy: string | undefined, bypassRules?: string) =>
|
||||
ipcRenderer.invoke(IpcChannel.App_Proxy, proxy, bypassRules),
|
||||
checkForUpdate: () => ipcRenderer.invoke(IpcChannel.App_CheckForUpdate),
|
||||
showUpdateDialog: () => ipcRenderer.invoke(IpcChannel.App_ShowUpdateDialog),
|
||||
setLanguage: (lang: string) => ipcRenderer.invoke(IpcChannel.App_SetLanguage, lang),
|
||||
|
||||
@ -27,7 +27,16 @@ const logger = loggerService.withContext('useAppInit')
|
||||
|
||||
export function useAppInit() {
|
||||
const dispatch = useAppDispatch()
|
||||
const { proxyUrl, language, windowStyle, autoCheckUpdate, proxyMode, customCss, enableDataCollection } = useSettings()
|
||||
const {
|
||||
proxyUrl,
|
||||
proxyBypassRules,
|
||||
language,
|
||||
windowStyle,
|
||||
autoCheckUpdate,
|
||||
proxyMode,
|
||||
customCss,
|
||||
enableDataCollection
|
||||
} = useSettings()
|
||||
const { minappShow } = useRuntime()
|
||||
const { setDefaultModel, setTopicNamingModel, setTranslateModel } = useDefaultModel()
|
||||
const avatar = useLiveQuery(() => db.settings.get('image://avatar'))
|
||||
@ -77,13 +86,13 @@ export function useAppInit() {
|
||||
|
||||
useEffect(() => {
|
||||
if (proxyMode === 'system') {
|
||||
window.api.setProxy('system')
|
||||
window.api.setProxy('system', proxyBypassRules)
|
||||
} else if (proxyMode === 'custom') {
|
||||
proxyUrl && window.api.setProxy(proxyUrl)
|
||||
proxyUrl && window.api.setProxy(proxyUrl, proxyBypassRules)
|
||||
} else {
|
||||
window.api.setProxy('')
|
||||
}
|
||||
}, [proxyUrl, proxyMode])
|
||||
}, [proxyUrl, proxyMode, proxyBypassRules])
|
||||
|
||||
useEffect(() => {
|
||||
i18n.changeLanguage(language || navigator.language || defaultLanguage)
|
||||
|
||||
@ -3238,6 +3238,7 @@
|
||||
},
|
||||
"proxy": {
|
||||
"address": "Proxy Address",
|
||||
"bypass": "Bypass Rules",
|
||||
"mode": {
|
||||
"custom": "Custom Proxy",
|
||||
"none": "No Proxy",
|
||||
|
||||
@ -3238,6 +3238,7 @@
|
||||
},
|
||||
"proxy": {
|
||||
"address": "プロキシアドレス",
|
||||
"bypass": "バイパスルール",
|
||||
"mode": {
|
||||
"custom": "カスタムプロキシ",
|
||||
"none": "プロキシを使用しない",
|
||||
|
||||
@ -3238,6 +3238,7 @@
|
||||
},
|
||||
"proxy": {
|
||||
"address": "Адрес прокси",
|
||||
"bypass": "Правила обхода",
|
||||
"mode": {
|
||||
"custom": "Пользовательский прокси",
|
||||
"none": "Не использовать прокси",
|
||||
|
||||
@ -3238,6 +3238,7 @@
|
||||
},
|
||||
"proxy": {
|
||||
"address": "代理地址",
|
||||
"bypass": "代理绕过规则",
|
||||
"mode": {
|
||||
"custom": "自定义代理",
|
||||
"none": "不使用代理",
|
||||
|
||||
@ -3238,6 +3238,7 @@
|
||||
},
|
||||
"proxy": {
|
||||
"address": "代理伺服器位址",
|
||||
"bypass": "代理略過規則",
|
||||
"mode": {
|
||||
"custom": "自訂代理伺服器",
|
||||
"none": "不使用代理伺服器",
|
||||
|
||||
@ -3236,6 +3236,7 @@
|
||||
},
|
||||
"proxy": {
|
||||
"address": "Διεύθυνση διαμεσολάβησης",
|
||||
"bypass": "Κανόνες Παράκαμψης",
|
||||
"mode": {
|
||||
"custom": "προσαρμοσμένη προξενική",
|
||||
"none": "χωρίς πρόξενο",
|
||||
|
||||
@ -3236,6 +3236,7 @@
|
||||
},
|
||||
"proxy": {
|
||||
"address": "Dirección del proxy",
|
||||
"bypass": "Reglas de omisión",
|
||||
"mode": {
|
||||
"custom": "Proxy personalizado",
|
||||
"none": "No usar proxy",
|
||||
|
||||
@ -3236,6 +3236,7 @@
|
||||
},
|
||||
"proxy": {
|
||||
"address": "Adresse du proxy",
|
||||
"bypass": "Règles de contournement",
|
||||
"mode": {
|
||||
"custom": "Proxy personnalisé",
|
||||
"none": "Ne pas utiliser de proxy",
|
||||
|
||||
@ -3236,6 +3236,7 @@
|
||||
},
|
||||
"proxy": {
|
||||
"address": "Endereço do proxy",
|
||||
"bypass": "Regras de Contorno",
|
||||
"mode": {
|
||||
"custom": "Proxy Personalizado",
|
||||
"none": "Não Usar Proxy",
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
setEnableSpellCheck,
|
||||
setLanguage,
|
||||
setNotificationSettings,
|
||||
setProxyBypassRules as _setProxyBypassRules,
|
||||
setProxyMode,
|
||||
setProxyUrl as _setProxyUrl,
|
||||
setSpellCheckLanguages
|
||||
@ -17,7 +18,7 @@ import {
|
||||
import { LanguageVarious } from '@renderer/types'
|
||||
import { NotificationSource } from '@renderer/types/notification'
|
||||
import { isValidProxyUrl } from '@renderer/utils'
|
||||
import { defaultLanguage } from '@shared/config/constant'
|
||||
import { defaultByPassRules, defaultLanguage } from '@shared/config/constant'
|
||||
import { Flex, Input, Switch, Tooltip } from 'antd'
|
||||
import { FC, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -29,6 +30,7 @@ const GeneralSettings: FC = () => {
|
||||
const {
|
||||
language,
|
||||
proxyUrl: storeProxyUrl,
|
||||
proxyBypassRules: storeProxyBypassRules,
|
||||
setLaunch,
|
||||
setTray,
|
||||
launchOnBoot,
|
||||
@ -42,6 +44,7 @@ const GeneralSettings: FC = () => {
|
||||
setDisableHardwareAcceleration
|
||||
} = useSettings()
|
||||
const [proxyUrl, setProxyUrl] = useState<string | undefined>(storeProxyUrl)
|
||||
const [proxyBypassRules, setProxyBypassRules] = useState<string | undefined>(storeProxyBypassRules)
|
||||
const { theme } = useTheme()
|
||||
const { enableDeveloperMode, setEnableDeveloperMode } = useEnableDeveloperMode()
|
||||
|
||||
@ -97,6 +100,10 @@ const GeneralSettings: FC = () => {
|
||||
dispatch(_setProxyUrl(proxyUrl))
|
||||
}
|
||||
|
||||
const onSetProxyBypassRules = () => {
|
||||
dispatch(_setProxyBypassRules(proxyBypassRules))
|
||||
}
|
||||
|
||||
const proxyModeOptions: { value: 'system' | 'custom' | 'none'; label: string }[] = [
|
||||
{ value: 'system', label: t('settings.proxy.mode.system') },
|
||||
{ value: 'custom', label: t('settings.proxy.mode.custom') },
|
||||
@ -109,6 +116,7 @@ const GeneralSettings: FC = () => {
|
||||
dispatch(_setProxyUrl(undefined))
|
||||
} else if (mode === 'none') {
|
||||
dispatch(_setProxyUrl(undefined))
|
||||
dispatch(_setProxyBypassRules(undefined))
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,6 +218,7 @@ const GeneralSettings: FC = () => {
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.proxy.address')}</SettingRowTitle>
|
||||
<Input
|
||||
spellCheck={false}
|
||||
placeholder="socks5://127.0.0.1:6153"
|
||||
value={proxyUrl}
|
||||
onChange={(e) => setProxyUrl(e.target.value)}
|
||||
@ -220,6 +229,22 @@ const GeneralSettings: FC = () => {
|
||||
</SettingRow>
|
||||
</>
|
||||
)}
|
||||
{(storeProxyMode === 'custom' || storeProxyMode === 'system') && (
|
||||
<>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.proxy.bypass')}</SettingRowTitle>
|
||||
<Input
|
||||
spellCheck={false}
|
||||
placeholder={defaultByPassRules}
|
||||
value={proxyBypassRules}
|
||||
onChange={(e) => setProxyBypassRules(e.target.value)}
|
||||
style={{ width: 180 }}
|
||||
onBlur={() => onSetProxyBypassRules()}
|
||||
/>
|
||||
</SettingRow>
|
||||
</>
|
||||
)}
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<HStack justifyContent="space-between" alignItems="center" style={{ flex: 1, marginRight: 16 }}>
|
||||
|
||||
@ -14,7 +14,7 @@ import db from '@renderer/databases'
|
||||
import i18n from '@renderer/i18n'
|
||||
import { Assistant, LanguageCode, Model, Provider, WebSearchProvider } from '@renderer/types'
|
||||
import { getDefaultGroupName, getLeadingEmoji, runAsyncFunction, uuid } from '@renderer/utils'
|
||||
import { UpgradeChannel } from '@shared/config/constant'
|
||||
import { defaultByPassRules, UpgradeChannel } from '@shared/config/constant'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { createMigrate } from 'redux-persist'
|
||||
|
||||
@ -1969,6 +1969,10 @@ const migrateConfig = {
|
||||
try {
|
||||
addProvider(state, 'poe')
|
||||
|
||||
if (!state.settings.proxyBypassRules) {
|
||||
state.settings.proxyBypassRules = defaultByPassRules
|
||||
}
|
||||
|
||||
// 迁移api选项设置
|
||||
state.llm.providers.forEach((provider) => {
|
||||
// 新字段默认支持
|
||||
|
||||
@ -49,6 +49,7 @@ export interface SettingsState {
|
||||
targetLanguage: TranslateLanguageVarious
|
||||
proxyMode: 'system' | 'custom' | 'none'
|
||||
proxyUrl?: string
|
||||
proxyBypassRules?: string
|
||||
userName: string
|
||||
userId: string
|
||||
showPrompt: boolean
|
||||
@ -220,6 +221,7 @@ export const initialState: SettingsState = {
|
||||
targetLanguage: 'en-us',
|
||||
proxyMode: 'system',
|
||||
proxyUrl: undefined,
|
||||
proxyBypassRules: undefined,
|
||||
userName: '',
|
||||
userId: uuid(),
|
||||
showPrompt: true,
|
||||
@ -423,6 +425,9 @@ const settingsSlice = createSlice({
|
||||
setProxyUrl: (state, action: PayloadAction<string | undefined>) => {
|
||||
state.proxyUrl = action.payload
|
||||
},
|
||||
setProxyBypassRules: (state, action: PayloadAction<string | undefined>) => {
|
||||
state.proxyBypassRules = action.payload
|
||||
},
|
||||
setUserName: (state, action: PayloadAction<string>) => {
|
||||
state.userName = action.payload
|
||||
},
|
||||
@ -826,6 +831,7 @@ export const {
|
||||
setTargetLanguage,
|
||||
setProxyMode,
|
||||
setProxyUrl,
|
||||
setProxyBypassRules,
|
||||
setUserName,
|
||||
setShowPrompt,
|
||||
setShowTokens,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user