Merge pull request #5809 from menloresearch/refactor/simplify-proxy-settings

refactor: simplify proxy settings by removing unused SSL verification options
This commit is contained in:
Louis 2025-07-19 16:34:37 +07:00 committed by GitHub
parent 8f1a36c8e3
commit c550f6cf0d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 134 additions and 165 deletions

View File

@ -23,10 +23,6 @@ pub struct ProxyConfig {
pub password: Option<String>,
pub no_proxy: Option<Vec<String>>, // List of domains to bypass proxy
pub ignore_ssl: Option<bool>, // Ignore SSL certificate verification
pub verify_proxy_ssl: Option<bool>, // Verify proxy SSL certificate
pub verify_proxy_host_ssl: Option<bool>, // Verify proxy host SSL certificate
pub verify_peer_ssl: Option<bool>, // Verify peer SSL certificate
pub verify_host_ssl: Option<bool>, // Verify host SSL certificate
}
#[derive(serde::Deserialize, Clone, Debug)]
@ -456,10 +452,6 @@ mod tests {
password: None,
no_proxy: None,
ignore_ssl: None,
verify_proxy_ssl: None,
verify_proxy_host_ssl: None,
verify_peer_ssl: None,
verify_host_ssl: None,
}
}
@ -472,10 +464,6 @@ mod tests {
password: Some("pass".to_string()),
no_proxy: Some(vec!["localhost".to_string(), "*.example.com".to_string()]),
ignore_ssl: Some(true),
verify_proxy_ssl: Some(false),
verify_proxy_host_ssl: Some(false),
verify_peer_ssl: Some(false),
verify_host_ssl: Some(false),
};
assert!(validate_proxy_config(&config).is_ok());
@ -486,10 +474,6 @@ mod tests {
password: None,
no_proxy: None,
ignore_ssl: None,
verify_proxy_ssl: None,
verify_proxy_host_ssl: None,
verify_peer_ssl: None,
verify_host_ssl: None,
};
assert!(validate_proxy_config(&config).is_ok());
@ -500,10 +484,6 @@ mod tests {
password: None,
no_proxy: None,
ignore_ssl: None,
verify_proxy_ssl: None,
verify_proxy_host_ssl: None,
verify_peer_ssl: None,
verify_host_ssl: None,
};
assert!(validate_proxy_config(&config).is_ok());
@ -613,20 +593,12 @@ mod tests {
// Test proxy config with SSL verification settings
let mut config = create_test_proxy_config("https://proxy.example.com:8080");
config.ignore_ssl = Some(true);
config.verify_proxy_ssl = Some(false);
config.verify_proxy_host_ssl = Some(false);
config.verify_peer_ssl = Some(true);
config.verify_host_ssl = Some(true);
// Should validate successfully
assert!(validate_proxy_config(&config).is_ok());
// Test with all SSL settings as false
config.ignore_ssl = Some(false);
config.verify_proxy_ssl = Some(false);
config.verify_proxy_host_ssl = Some(false);
config.verify_peer_ssl = Some(false);
config.verify_host_ssl = Some(false);
// Should still validate successfully
assert!(validate_proxy_config(&config).is_ok());
@ -637,10 +609,6 @@ mod tests {
// Test with mixed SSL settings - ignore_ssl true, others false
let mut config = create_test_proxy_config("https://proxy.example.com:8080");
config.ignore_ssl = Some(true);
config.verify_proxy_ssl = Some(false);
config.verify_proxy_host_ssl = Some(true);
config.verify_peer_ssl = Some(false);
config.verify_host_ssl = Some(true);
assert!(validate_proxy_config(&config).is_ok());
assert!(create_proxy_from_config(&config).is_ok());
@ -652,10 +620,6 @@ mod tests {
let config = create_test_proxy_config("https://proxy.example.com:8080");
assert_eq!(config.ignore_ssl, None);
assert_eq!(config.verify_proxy_ssl, None);
assert_eq!(config.verify_proxy_host_ssl, None);
assert_eq!(config.verify_peer_ssl, None);
assert_eq!(config.verify_host_ssl, None);
assert!(validate_proxy_config(&config).is_ok());
assert!(create_proxy_from_config(&config).is_ok());
@ -666,7 +630,6 @@ mod tests {
// Test that DownloadItem can be created with SSL proxy configuration
let mut proxy_config = create_test_proxy_config("https://proxy.example.com:8080");
proxy_config.ignore_ssl = Some(true);
proxy_config.verify_proxy_ssl = Some(false);
let download_item = DownloadItem {
url: "https://example.com/file.zip".to_string(),
@ -677,7 +640,6 @@ mod tests {
assert!(download_item.proxy.is_some());
let proxy = download_item.proxy.unwrap();
assert_eq!(proxy.ignore_ssl, Some(true));
assert_eq!(proxy.verify_proxy_ssl, Some(false));
}
#[test]
@ -704,7 +666,6 @@ mod tests {
// Test that SSL settings work with HTTP proxy (though not typically used)
let mut config = create_test_proxy_config("http://proxy.example.com:8080");
config.ignore_ssl = Some(true);
config.verify_proxy_ssl = Some(false);
assert!(validate_proxy_config(&config).is_ok());
assert!(create_proxy_from_config(&config).is_ok());
@ -715,8 +676,6 @@ mod tests {
// Test that SSL settings work with SOCKS proxy
let mut config = create_test_proxy_config("socks5://proxy.example.com:1080");
config.ignore_ssl = Some(false);
config.verify_peer_ssl = Some(true);
config.verify_host_ssl = Some(true);
assert!(validate_proxy_config(&config).is_ok());
assert!(create_proxy_from_config(&config).is_ok());

View File

@ -629,12 +629,8 @@ Section Install
File "${MAINBINARYSRCPATH}"
; Copy resources
CreateDirectory "$INSTDIR\binaries"
CreateDirectory "$INSTDIR\binaries\engines"
CreateDirectory "$INSTDIR\resources"
CreateDirectory "$INSTDIR\resources\pre-install"
SetOutPath "$INSTDIR\binaries\engines"
File /nonfatal /a /r "D:\a\jan\jan\src-tauri\binaries\engines\"
SetOutPath $INSTDIR
File /a "/oname=vulkan-1.dll" "D:\a\jan\jan\src-tauri\resources\lib\vulkan-1.dll"
SetOutPath "$INSTDIR\resources\pre-install"
@ -769,7 +765,6 @@ Section Uninstall
Delete "$INSTDIR\resources\pre-install\janhq-model-extension-1.0.36.tgz"
; Delete external binaries
Delete "$INSTDIR\cortex-server.exe"
Delete "$INSTDIR\bun.exe"
Delete "$INSTDIR\uv.exe"
@ -781,9 +776,7 @@ Section Uninstall
; Delete uninstaller
Delete "$INSTDIR\uninstall.exe"
RMDir /r /REBOOTOK "$INSTDIR\binaries\engines"
RMDir /REBOOTOK "$INSTDIR\resources\pre-install"
RMDir /r /REBOOTOK "$INSTDIR\binaries"
RMDir /r /REBOOTOK "$INSTDIR\resources"
RMDir /r "$INSTDIR"

View File

@ -1,7 +1,7 @@
{
"bundle": {
"targets": ["app", "dmg"],
"resources": ["resources/pre-install/**/*", "binaries/**/*"],
"resources": ["resources/pre-install/**/*"],
"externalBin": ["resources/bin/bun", "resources/bin/uv"]
}
}

View File

@ -1,7 +1,7 @@
{
"bundle": {
"targets": ["nsis"],
"resources": ["resources/pre-install/**/*", "binaries/**/*"],
"resources": ["resources/pre-install/**/*"],
"externalBin": ["resources/bin/bun", "resources/bin/uv"],
"windows": {
"signCommand": "powershell -ExecutionPolicy Bypass -File ./sign.ps1 %1"

View File

@ -157,18 +157,19 @@ export const sendCompletion = async (
},
}),
} as ExtendedConfigOptions)
if (
thread.model.id &&
!(thread.model.id in Object.values(models).flat()) &&
!Object.values(models[providerName]).flat().includes(thread.model.id) &&
// eslint-disable-next-line @typescript-eslint/no-explicit-any
!tokenJS.extendedModelExist(providerName as any, thread.model?.id) &&
!tokenJS.extendedModelExist(providerName as any, thread.model.id) &&
provider.provider !== 'llamacpp'
) {
try {
tokenJS.extendModelList(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
providerName as any,
thread.model?.id,
thread.model.id,
// This is to inherit the model capabilities from another built-in model
// Can be anything that support all model capabilities
models.anthropic.models[0]

View File

@ -14,16 +14,34 @@ vi.mock('@/containers/HeaderPage', () => ({
}))
vi.mock('@/containers/Card', () => ({
Card: ({ title, children }: { title?: string; children: React.ReactNode }) => (
Card: ({
title,
children,
}: {
title?: string
children: React.ReactNode
}) => (
<div data-testid="card" data-title={title}>
{title && <div data-testid="card-title">{title}</div>}
{children}
</div>
),
CardItem: ({ title, description, actions, className }: { title?: string; description?: string; actions?: React.ReactNode; className?: string }) => (
CardItem: ({
title,
description,
actions,
className,
}: {
title?: string
description?: string
actions?: React.ReactNode
className?: string
}) => (
<div data-testid="card-item" data-title={title} className={className}>
{title && <div data-testid="card-item-title">{title}</div>}
{description && <div data-testid="card-item-description">{description}</div>}
{description && (
<div data-testid="card-item-description">{description}</div>
)}
{actions && <div data-testid="card-item-actions">{actions}</div>}
</div>
),
@ -63,7 +81,13 @@ vi.mock('@/i18n/react-i18next-compat', () => ({
}))
vi.mock('@/components/ui/switch', () => ({
Switch: ({ checked, onCheckedChange }: { checked: boolean; onCheckedChange: (checked: boolean) => void }) => (
Switch: ({
checked,
onCheckedChange,
}: {
checked: boolean
onCheckedChange: (checked: boolean) => void
}) => (
<input
data-testid="switch"
type="checkbox"
@ -74,15 +98,38 @@ vi.mock('@/components/ui/switch', () => ({
}))
vi.mock('@/components/ui/button', () => ({
Button: ({ children, onClick, disabled, ...props }: { children: React.ReactNode; onClick?: () => void; disabled?: boolean; [key: string]: any }) => (
<button data-testid="button" onClick={onClick} disabled={disabled} {...props}>
Button: ({
children,
onClick,
disabled,
...props
}: {
children: React.ReactNode
onClick?: () => void
disabled?: boolean
[key: string]: any
}) => (
<button
data-testid="button"
onClick={onClick}
disabled={disabled}
{...props}
>
{children}
</button>
),
}))
vi.mock('@/components/ui/input', () => ({
Input: ({ value, onChange, placeholder }: { value: string; onChange: (e: React.ChangeEvent<HTMLInputElement>) => void; placeholder?: string }) => (
Input: ({
value,
onChange,
placeholder,
}: {
value: string
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
placeholder?: string
}) => (
<input
data-testid="input"
value={value}
@ -93,14 +140,30 @@ vi.mock('@/components/ui/input', () => ({
}))
vi.mock('@/components/ui/dialog', () => ({
Dialog: ({ children }: { children: React.ReactNode }) => <div data-testid="dialog">{children}</div>,
DialogClose: ({ children }: { children: React.ReactNode }) => <div data-testid="dialog-close">{children}</div>,
DialogContent: ({ children }: { children: React.ReactNode }) => <div data-testid="dialog-content">{children}</div>,
DialogDescription: ({ children }: { children: React.ReactNode }) => <div data-testid="dialog-description">{children}</div>,
DialogFooter: ({ children }: { children: React.ReactNode }) => <div data-testid="dialog-footer">{children}</div>,
DialogHeader: ({ children }: { children: React.ReactNode }) => <div data-testid="dialog-header">{children}</div>,
DialogTitle: ({ children }: { children: React.ReactNode }) => <div data-testid="dialog-title">{children}</div>,
DialogTrigger: ({ children }: { children: React.ReactNode }) => <div data-testid="dialog-trigger">{children}</div>,
Dialog: ({ children }: { children: React.ReactNode }) => (
<div data-testid="dialog">{children}</div>
),
DialogClose: ({ children }: { children: React.ReactNode }) => (
<div data-testid="dialog-close">{children}</div>
),
DialogContent: ({ children }: { children: React.ReactNode }) => (
<div data-testid="dialog-content">{children}</div>
),
DialogDescription: ({ children }: { children: React.ReactNode }) => (
<div data-testid="dialog-description">{children}</div>
),
DialogFooter: ({ children }: { children: React.ReactNode }) => (
<div data-testid="dialog-footer">{children}</div>
),
DialogHeader: ({ children }: { children: React.ReactNode }) => (
<div data-testid="dialog-header">{children}</div>
),
DialogTitle: ({ children }: { children: React.ReactNode }) => (
<div data-testid="dialog-title">{children}</div>
),
DialogTrigger: ({ children }: { children: React.ReactNode }) => (
<div data-testid="dialog-trigger">{children}</div>
),
}))
vi.mock('@/services/app', () => ({
@ -213,12 +276,13 @@ describe('General Settings Route', () => {
expect(screen.getByText('v1.0.0')).toBeInTheDocument()
})
it('should render language switcher', () => {
const Component = GeneralRoute.component as React.ComponentType
render(<Component />)
// TODO: This test is currently commented out due to missing implementation
// it('should render language switcher', () => {
// const Component = GeneralRoute.component as React.ComponentType
// render(<Component />)
expect(screen.getByTestId('language-switcher')).toBeInTheDocument()
})
// expect(screen.getByTestId('language-switcher')).toBeInTheDocument()
// })
it('should render switches for experimental features and spell check', () => {
const Component = GeneralRoute.component as React.ComponentType
@ -243,7 +307,7 @@ describe('General Settings Route', () => {
const switches = screen.getAllByTestId('switch')
expect(switches.length).toBeGreaterThan(0)
// Test that switches are interactive
fireEvent.click(switches[0])
expect(switches[0]).toBeInTheDocument()
@ -255,7 +319,7 @@ describe('General Settings Route', () => {
const switches = screen.getAllByTestId('switch')
expect(switches.length).toBeGreaterThan(0)
// Test that switches are interactive
if (switches.length > 1) {
fireEvent.click(switches[1])
@ -269,7 +333,7 @@ describe('General Settings Route', () => {
const input = screen.getByTestId('input')
expect(input).toBeInTheDocument()
// Test that input is interactive
fireEvent.change(input, { target: { value: 'new-token' } })
expect(input).toBeInTheDocument()
@ -280,10 +344,10 @@ describe('General Settings Route', () => {
render(<Component />)
const buttons = screen.getAllByTestId('button')
const checkUpdateButton = buttons.find(button =>
const checkUpdateButton = buttons.find((button) =>
button.textContent?.includes('checkForUpdates')
)
if (checkUpdateButton) {
expect(checkUpdateButton).toBeInTheDocument()
fireEvent.click(checkUpdateButton)
@ -333,10 +397,10 @@ describe('General Settings Route', () => {
render(<Component />)
const buttons = screen.getAllByTestId('button')
const openLogsButton = buttons.find(button =>
const openLogsButton = buttons.find((button) =>
button.textContent?.includes('openLogs')
)
if (openLogsButton) {
expect(openLogsButton).toBeInTheDocument()
// Test that button is interactive
@ -350,10 +414,10 @@ describe('General Settings Route', () => {
render(<Component />)
const buttons = screen.getAllByTestId('button')
const revealLogsButton = buttons.find(button =>
const revealLogsButton = buttons.find((button) =>
button.textContent?.includes('showInFileExplorer')
)
if (revealLogsButton) {
expect(revealLogsButton).toBeInTheDocument()
// Test that button is interactive
@ -369,7 +433,9 @@ describe('General Settings Route', () => {
const Component = GeneralRoute.component as React.ComponentType
render(<Component />)
expect(screen.getByText('settings:general.showInFileExplorer')).toBeInTheDocument()
expect(
screen.getByText('settings:general.showInFileExplorer')
).toBeInTheDocument()
})
it('should disable check for updates button when checking', () => {
@ -377,13 +443,13 @@ describe('General Settings Route', () => {
render(<Component />)
const buttons = screen.getAllByTestId('button')
const checkUpdateButton = buttons.find(button =>
const checkUpdateButton = buttons.find((button) =>
button.textContent?.includes('checkForUpdates')
)
if (checkUpdateButton) {
fireEvent.click(checkUpdateButton)
expect(checkUpdateButton).toBeDisabled()
}
})
})
})

View File

@ -24,19 +24,11 @@ function HTTPSProxy() {
proxyUsername,
proxyPassword,
proxyIgnoreSSL,
verifyProxySSL,
verifyProxyHostSSL,
verifyPeerSSL,
verifyHostSSL,
noProxy,
setProxyEnabled,
setProxyUsername,
setProxyPassword,
setProxyIgnoreSSL,
setVerifyProxySSL,
setVerifyProxyHostSSL,
setVerifyPeerSSL,
setVerifyHostSSL,
setNoProxy,
setProxyUrl,
} = useProxyConfig()
@ -137,10 +129,6 @@ function HTTPSProxy() {
</div>
}
/>
</Card>
{/* SSL Verification */}
<Card title={t('settings:httpsProxy.sslVerification')}>
<CardItem
title={t('settings:httpsProxy.ignoreSsl')}
description={t('settings:httpsProxy.ignoreSslDesc')}
@ -151,48 +139,6 @@ function HTTPSProxy() {
/>
}
/>
<CardItem
title={t('settings:httpsProxy.proxySsl')}
description={t('settings:httpsProxy.proxySslDesc')}
actions={
<Switch
checked={verifyProxySSL}
onCheckedChange={(checked) => setVerifyProxySSL(checked)}
/>
}
/>
<CardItem
title={t('settings:httpsProxy.proxyHostSsl')}
description={t('settings:httpsProxy.proxyHostSslDesc')}
actions={
<Switch
checked={verifyProxyHostSSL}
onCheckedChange={(checked) =>
setVerifyProxyHostSSL(checked)
}
/>
}
/>
<CardItem
title={t('settings:httpsProxy.peerSsl')}
description={t('settings:httpsProxy.peerSslDesc')}
actions={
<Switch
checked={verifyPeerSSL}
onCheckedChange={(checked) => setVerifyPeerSSL(checked)}
/>
}
/>
<CardItem
title={t('settings:httpsProxy.hostSsl')}
description={t('settings:httpsProxy.hostSslDesc')}
actions={
<Switch
checked={verifyHostSSL}
onCheckedChange={(checked) => setVerifyHostSSL(checked)}
/>
}
/>
</Card>
</div>
</div>

View File

@ -85,11 +85,11 @@ function ProviderDetail() {
useEffect(() => {
// Initial data fetch
getActiveModels().then(setActiveModels)
getActiveModels().then((models) => setActiveModels(models || []))
// Set up interval for real-time updates
const intervalId = setInterval(() => {
getActiveModels().then(setActiveModels)
getActiveModels().then((models) => setActiveModels(models || []))
}, 5000)
return () => clearInterval(intervalId)
@ -291,7 +291,9 @@ function ProviderDetail() {
typeof newValue === 'string' &&
provider.provider === 'llamacpp'
) {
console.log(`Device setting manually changed to: "${newValue}"`)
console.log(
`Device setting manually changed to: "${newValue}"`
)
updateGPUActivationFromDeviceString(newValue)
}

View File

@ -42,14 +42,14 @@ function SystemMonitor() {
getHardwareInfo().then((data) => {
updateHardwareDataPreservingGpuOrder(data as unknown as HardwareData)
})
getActiveModels().then(setActiveModels)
getActiveModels().then((models) => setActiveModels(models || []))
// Set up interval for real-time updates
const intervalId = setInterval(() => {
getSystemUsage().then((data) => {
updateSystemUsage(data)
})
getActiveModels().then(setActiveModels)
getActiveModels().then((models) => setActiveModels(models || []))
}, 5000)
return () => clearInterval(intervalId)

View File

@ -29,14 +29,14 @@ export type ModelCatalog = CatalogModel[]
const defaultProvider = 'llamacpp'
const getEngine = (provider: string = defaultProvider) => {
return EngineManager.instance().get(provider) as AIEngine
return EngineManager.instance().get(provider) as AIEngine | undefined
}
/**
* Fetches all available models.
* @returns A promise that resolves to the models.
*/
export const fetchModels = async () => {
return getEngine().list()
return getEngine()?.list()
}
/**
@ -73,7 +73,7 @@ export const updateModel = async (
// provider: string,
) => {
if (model.settings)
getEngine().updateSettings(model.settings as SettingComponentProps[])
getEngine()?.updateSettings(model.settings as SettingComponentProps[])
}
/**
@ -82,7 +82,7 @@ export const updateModel = async (
* @returns A promise that resolves when the model download task is created.
*/
export const pullModel = async (id: string, modelPath: string) => {
return getEngine().import(id, {
return getEngine()?.import(id, {
modelPath,
})
}
@ -93,7 +93,7 @@ export const pullModel = async (id: string, modelPath: string) => {
* @returns
*/
export const abortDownload = async (id: string) => {
return getEngine().abortImport(id)
return getEngine()?.abortImport(id)
}
/**
@ -102,7 +102,7 @@ export const abortDownload = async (id: string) => {
* @returns
*/
export const deleteModel = async (id: string) => {
return getEngine().delete(id)
return getEngine()?.delete(id)
}
/**
@ -112,7 +112,7 @@ export const deleteModel = async (id: string) => {
*/
export const getActiveModels = async (provider?: string) => {
// getEngine(provider)
return getEngine(provider).getLoadedModels()
return getEngine(provider)?.getLoadedModels()
}
/**
@ -122,7 +122,7 @@ export const getActiveModels = async (provider?: string) => {
* @returns
*/
export const stopModel = async (model: string, provider?: string) => {
getEngine(provider).unload(model)
getEngine(provider)?.unload(model)
}
/**
@ -146,15 +146,15 @@ export const startModel = async (
provider: ProviderObject,
model: string
): Promise<SessionInfo | undefined> => {
if ((await getEngine(provider.provider).getLoadedModels()).includes(model))
return undefined
return getEngine(provider.provider)
.load(model)
.catch((error) => {
console.error(
`Failed to start model ${model} for provider ${provider.provider}:`,
error
)
throw error
})
const engine = getEngine(provider.provider)
if (!engine) return undefined
if ((await engine.getLoadedModels()).includes(model)) return undefined
return engine.load(model).catch((error) => {
console.error(
`Failed to start model ${model} for provider ${provider.provider}:`,
error
)
throw error
})
}

View File

@ -163,7 +163,9 @@ export const fetchModelsFromProvider = async (
// Direct array format: ["model-id1", "model-id2", ...]
return data
.filter(Boolean)
.map((model) => ('id' in model ? model.id : model))
.map((model) =>
typeof model === 'object' && 'id' in model ? model.id : model
)
} else if (data.models && Array.isArray(data.models)) {
// Alternative format: { models: [...] }
return data.models