fix(ai): revalidate credentials when base URL changes in openai-compat mode

This commit is contained in:
digua authored and digua committed 2026-05-27 23:47:27 +08:00
1 parent 61380f53cc
commit 46204db9b8
3 files changed
+68 -2

No files matched your search

@@ -44,4 +44,52 @@ describe('canReuseExistingApiKey', () => {
assert.equal(result, false)
})
it('requires a new key when base URL changes in openai-compat mode', () => {
const result = canReuseExistingApiKey({
mode: 'edit',
existingApiKeySet: true,
hasNewApiKey: false,
originalProvider: 'openai-compatible',
currentProvider: 'openai-compatible',
originalConnectionMode: 'openai-compat',
currentConnectionMode: 'openai-compat',
originalBaseUrl: 'https://api.provider-a.com',
currentBaseUrl: 'https://api.provider-b.com',
})
assert.equal(result, false)
})
it('allows reusing key when base URL is unchanged in openai-compat mode (trailing slash normalized)', () => {
const result = canReuseExistingApiKey({
mode: 'edit',
existingApiKeySet: true,
hasNewApiKey: false,
originalProvider: 'openai-compatible',
currentProvider: 'openai-compatible',
originalConnectionMode: 'openai-compat',
currentConnectionMode: 'openai-compat',
originalBaseUrl: 'https://api.provider-a.com/',
currentBaseUrl: 'https://api.provider-a.com',
})
assert.equal(result, true)
})
it('allows reusing key when base URL is unchanged in openai-compat mode', () => {
const result = canReuseExistingApiKey({
mode: 'edit',
existingApiKeySet: true,
hasNewApiKey: false,
originalProvider: 'openai-compatible',
currentProvider: 'openai-compatible',
originalConnectionMode: 'openai-compat',
currentConnectionMode: 'openai-compat',
originalBaseUrl: 'https://api.provider-a.com',
currentBaseUrl: 'https://api.provider-a.com',
})
assert.equal(result, true)
})
})
@@ -8,6 +8,10 @@ export interface ApiKeyReuseInput {
currentProvider: string
originalConnectionMode?: ConnectionMode
currentConnectionMode: ConnectionMode
/** Original base URL (for openai-compat mode endpoint change detection) */
originalBaseUrl?: string
/** Current base URL entered by the user */
currentBaseUrl?: string
}
export function getConnectionModeForConfig(config: {
@@ -23,11 +27,21 @@ export function getConnectionModeForConfig(config: {
}
export function canReuseExistingApiKey(input: ApiKeyReuseInput): boolean {
return (
const baseConditions =
input.mode === 'edit' &&
!!input.existingApiKeySet &&
!input.hasNewApiKey &&
input.originalProvider === input.currentProvider &&
input.originalConnectionMode === input.currentConnectionMode
)
if (!baseConditions) return false
// For openai-compat mode, a base URL change means a different endpoint/service,
// so the existing credential must not be silently reused without re-validation.
if (input.currentConnectionMode === 'openai-compat') {
const normalizeUrl = (url?: string) => url?.trim().replace(/\/+$/, '') || ''
return normalizeUrl(input.originalBaseUrl) === normalizeUrl(input.currentBaseUrl)
}
return true
}
@@ -174,6 +174,8 @@ export function useAIConfigForm(props: {
currentProvider: provider,
originalConnectionMode: props.config.value ? getConnectionModeForConfig(props.config.value) : undefined,
currentConnectionMode: connectionMode.value,
originalBaseUrl: props.config.value?.baseUrl,
currentBaseUrl: baseUrl.trim() || undefined,
})
if (isLocalMode.value) {
@@ -709,6 +711,8 @@ export function useAIConfigForm(props: {
currentProvider: formData.value.provider,
originalConnectionMode: props.config.value ? getConnectionModeForConfig(props.config.value) : undefined,
currentConnectionMode: connectionMode.value,
originalBaseUrl: props.config.value?.baseUrl,
currentBaseUrl: formData.value.baseUrl.trim() || undefined,
})
if (isEditWithExistingKey) {
return doSave()