feat: add per-API connection proxy URL settings
This commit is contained in:
1 parent
2432220677
commit
4d2aea3353
9 files changed
+196
-7
No files matched your search
@@ -13,6 +13,7 @@ import {
|
||||
parseProviderApiKeys,
|
||||
providerCategoryMatchesRecord,
|
||||
providerDragId,
|
||||
providerProxyDraftFromRecord,
|
||||
providerRecordWithDisabledState,
|
||||
providerRemarkIdentity,
|
||||
providerSectionOrder,
|
||||
@@ -438,6 +439,74 @@ describe('API 接入配置合并', () => {
|
||||
expect(result['proxy-url']).toBeUndefined();
|
||||
});
|
||||
|
||||
it('为单个 API 接入保存、更新和清除代理 URL', () => {
|
||||
const draft = {
|
||||
...createProviderDraft('codex-api-key'),
|
||||
apiKey: 'codex-key',
|
||||
baseUrl: 'https://api.example.com',
|
||||
proxyUrl: 'socks5://127.0.0.1:1080',
|
||||
};
|
||||
const saved = buildProviderRecord('codex-api-key', draft);
|
||||
expect(saved['proxy-url']).toBe('socks5://127.0.0.1:1080');
|
||||
expect(buildProviderRecord('codex-api-key', { ...draft, proxyUrl: 'direct' }, saved)['proxy-url']).toBe('direct');
|
||||
expect(buildProviderRecord('codex-api-key', { ...draft, proxyUrl: '' }, saved)['proxy-url']).toBeUndefined();
|
||||
expect(buildProviderRecord('codex-api-key', { ...draft, proxyUrl: '', proxyUrlEdited: false }, saved)['proxy-url'])
|
||||
.toBe('socks5://127.0.0.1:1080');
|
||||
});
|
||||
|
||||
it('每条 API 接入记录独立设置代理', () => {
|
||||
const current = {
|
||||
name: 'openrouter',
|
||||
'api-key-entries': [
|
||||
{ 'api-key': 'first', 'proxy-url': 'direct' },
|
||||
{ 'api-key': 'second', 'proxy-url': 'socks5://localhost:1080' },
|
||||
],
|
||||
};
|
||||
const draft = {
|
||||
...createProviderDraft('openai-compatibility'),
|
||||
...providerProxyDraftFromRecord('openai-compatibility', current),
|
||||
name: 'openrouter',
|
||||
apiKey: 'first\nsecond',
|
||||
baseUrl: 'https://openrouter.ai/api/v1',
|
||||
};
|
||||
expect(draft.proxyUrl).toBe('');
|
||||
expect(draft.proxyUrlEdited).toBe(false);
|
||||
expect(draft.proxyUrlMixed).toBe(true);
|
||||
const preserved = buildProviderRecord('openai-compatibility', draft, current);
|
||||
expect(preserved['api-key-entries']).toEqual(current['api-key-entries']);
|
||||
|
||||
const cleared = buildProviderRecord('openai-compatibility', {
|
||||
...draft,
|
||||
proxyUrl: '',
|
||||
proxyUrlEdited: true,
|
||||
proxyUrlMixed: false,
|
||||
}, current);
|
||||
expect(cleared['api-key-entries']).toEqual([
|
||||
{ 'api-key': 'first' },
|
||||
{ 'api-key': 'second' },
|
||||
]);
|
||||
|
||||
const updated = buildProviderRecord('openai-compatibility', {
|
||||
...draft,
|
||||
proxyUrl: 'http://127.0.0.1:7890',
|
||||
proxyUrlEdited: true,
|
||||
}, current);
|
||||
expect(updated['api-key-entries']).toEqual([
|
||||
{ 'api-key': 'first', 'proxy-url': 'http://127.0.0.1:7890' },
|
||||
{ 'api-key': 'second', 'proxy-url': 'http://127.0.0.1:7890' },
|
||||
]);
|
||||
const other = buildProviderRecord('openai-compatibility', {
|
||||
...createProviderDraft('openai-compatibility'),
|
||||
name: 'another-entry',
|
||||
apiKey: 'third',
|
||||
baseUrl: 'https://other.example/v1',
|
||||
proxyUrl: 'socks5://127.0.0.1:1080',
|
||||
});
|
||||
expect(other['api-key-entries']).toEqual([
|
||||
{ 'api-key': 'third', 'proxy-url': 'socks5://127.0.0.1:1080' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('编辑已停用的普通提供商时保留停用规则', () => {
|
||||
const result = buildProviderRecord('claude-api-key', {
|
||||
name: '',
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { normalizeProviderProxyUrl } from '../src/services/providerProxy';
|
||||
|
||||
describe('provider proxy URL', () => {
|
||||
it('accepts supported proxies and explicit direct connections', () => {
|
||||
expect(normalizeProviderProxyUrl(' socks5://user:pass@127.0.0.1:1080 ')).toBe('socks5://user:pass@127.0.0.1:1080');
|
||||
expect(normalizeProviderProxyUrl('HTTP://127.0.0.1:7890')).toBe('HTTP://127.0.0.1:7890');
|
||||
expect(normalizeProviderProxyUrl(' DIRECT ')).toBe('direct');
|
||||
expect(normalizeProviderProxyUrl('none')).toBe('none');
|
||||
expect(normalizeProviderProxyUrl('')).toBe('');
|
||||
});
|
||||
|
||||
it('rejects PAC URLs, paths, and missing SOCKS ports', () => {
|
||||
for (const url of ['https://proxy.example/proxy.pac', 'http://proxy.example/path', 'socks5://proxy.example', 'ftp://proxy.example:21', 'not a url']) {
|
||||
expect(() => normalizeProviderProxyUrl(url)).toThrow();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user