Skip to content

Commit 966c873

Browse files
fix: reload OpenCode config and invalidate provider caches after credential changes
- Trigger reloadConfig() after API key save/delete in providers route so the OpenCode server immediately recognizes newly connected providers - Wrap reloadConfig() in try/catch in both providers.ts and oauth.ts so credential operations succeed even if the reload fails transiently - Add invalidateProviderCaches() utility covering all provider-related query keys, replacing scattered manual invalidation across ProviderSettings - Remove duplicate provider key invalidation from invalidateConfigCaches() by delegating to invalidateProviderCaches() - Remove unused invalidateAllConfigRelatedCaches() export Fixes #180
1 parent 5620388 commit 966c873

4 files changed

Lines changed: 37 additions & 19 deletions

File tree

backend/src/routes/oauth.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,13 @@ export function createOAuthRoutes() {
7474
}
7575

7676
const data = await response.json()
77-
78-
logger.info(`OAuth callback successful for ${providerId}, reloading OpenCode configuration`)
79-
await opencodeServerManager.reloadConfig()
80-
77+
78+
try {
79+
await opencodeServerManager.reloadConfig()
80+
} catch (reloadError) {
81+
logger.warn(`Failed to reload OpenCode config after OAuth callback for ${providerId}:`, reloadError)
82+
}
83+
8184
return c.json(data)
8285
} catch (error) {
8386
logger.error('OAuth callback error:', error)

backend/src/routes/providers.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { AuthService } from '../services/auth'
44
import { SetCredentialRequestSchema } from '../../../shared/src/schemas/auth'
55
import { logger } from '../utils/logger'
66
import { setOpenCodeAuth, deleteOpenCodeAuth } from '../services/proxy'
7+
import { opencodeServerManager } from '../services/opencode-single-server'
78

89
export function createProvidersRoutes() {
910
const app = new Hono()
@@ -42,6 +43,13 @@ export function createProvidersRoutes() {
4243
}
4344

4445
await authService.set(providerId, validated.apiKey)
46+
47+
try {
48+
await opencodeServerManager.reloadConfig()
49+
} catch (reloadError) {
50+
logger.warn(`Failed to reload OpenCode config after saving credentials for ${providerId}:`, reloadError)
51+
}
52+
4553
return c.json({ success: true })
4654
} catch (error) {
4755
logger.error('Failed to set provider credentials:', error)
@@ -62,6 +70,13 @@ export function createProvidersRoutes() {
6270
}
6371

6472
await authService.delete(providerId)
73+
74+
try {
75+
await opencodeServerManager.reloadConfig()
76+
} catch (reloadError) {
77+
logger.warn(`Failed to reload OpenCode config after deleting credentials for ${providerId}:`, reloadError)
78+
}
79+
6580
return c.json({ success: true })
6681
} catch (error) {
6782
logger.error('Failed to delete provider credentials:', error)

frontend/src/components/settings/ProviderSettings.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useState, useMemo, useCallback } from 'react'
22
import { Button } from '@/components/ui/button'
3-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
3+
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'
44
import { Badge } from '@/components/ui/badge'
55
import { Input } from '@/components/ui/input'
66
import { DeleteDialog } from '@/components/ui/delete-dialog'
@@ -12,6 +12,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
1212
import { OAuthAuthorizeDialog } from './OAuthAuthorizeDialog'
1313
import { OAuthCallbackDialog } from './OAuthCallbackDialog'
1414
import { ApiKeyDialog } from '@/components/model/ApiKeyDialog'
15+
import { invalidateProviderCaches } from '@/lib/queryInvalidation'
1516

1617
export function ProviderSettings() {
1718
const [selectedProvider, setSelectedProvider] = useState<string | null>(null)
@@ -48,9 +49,7 @@ export function ProviderSettings() {
4849
const deleteCredentialMutation = useMutation({
4950
mutationFn: (providerId: string) => providerCredentialsApi.delete(providerId),
5051
onSuccess: () => {
51-
queryClient.invalidateQueries({ queryKey: ['provider-credentials'] })
52-
queryClient.invalidateQueries({ queryKey: ['providers'] })
53-
queryClient.invalidateQueries({ queryKey: ['providers-with-models'] })
52+
invalidateProviderCaches(queryClient)
5453
},
5554
})
5655

@@ -81,7 +80,7 @@ export function ProviderSettings() {
8180
}
8281

8382
const handleOAuthSuccess = () => {
84-
queryClient.invalidateQueries({ queryKey: ['provider-credentials'] })
83+
invalidateProviderCaches(queryClient)
8584
setOauthCallbackDialogOpen(false)
8685
setOauthResponse(null)
8786
setSelectedProvider(null)
@@ -138,9 +137,7 @@ export function ProviderSettings() {
138137
const handleApiKeySuccess = useCallback(() => {
139138
setApiKeyDialogOpen(false)
140139
setApiKeyProvider(null)
141-
queryClient.invalidateQueries({ queryKey: ['provider-credentials'] })
142-
queryClient.invalidateQueries({ queryKey: ['providers'] })
143-
queryClient.invalidateQueries({ queryKey: ['providers-with-models'] })
140+
invalidateProviderCaches(queryClient)
144141
}, [queryClient])
145142

146143
const handleApiKeyDialogClose = useCallback((open: boolean) => {

frontend/src/lib/queryInvalidation.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
import type { QueryClient } from '@tanstack/react-query'
22

3+
export function invalidateProviderCaches(queryClient: QueryClient) {
4+
queryClient.invalidateQueries({ queryKey: ['provider-credentials'] })
5+
queryClient.invalidateQueries({ queryKey: ['provider-auth-methods'] })
6+
queryClient.invalidateQueries({ queryKey: ['providers'] })
7+
queryClient.invalidateQueries({ queryKey: ['providers-with-models'] })
8+
queryClient.invalidateQueries({ queryKey: ['opencode', 'providers'] })
9+
queryClient.invalidateQueries({ queryKey: ['providers-for-execution-model'] })
10+
}
11+
312
export function invalidateConfigCaches(queryClient: QueryClient) {
413
queryClient.invalidateQueries({ queryKey: ['opencode', 'config'] })
514
queryClient.invalidateQueries({ queryKey: ['opencode', 'agents'] })
615
queryClient.invalidateQueries({ queryKey: ['opencode-config'] })
716
queryClient.invalidateQueries({ queryKey: ['health'] })
817
queryClient.invalidateQueries({ queryKey: ['mcp-status'] })
9-
queryClient.invalidateQueries({ queryKey: ['providers'] })
10-
queryClient.invalidateQueries({ queryKey: ['opencode', 'providers'] })
1118
queryClient.invalidateQueries({ queryKey: ['opencode-skills'] })
1219
queryClient.invalidateQueries({ queryKey: ['managed-skills'] })
20+
invalidateProviderCaches(queryClient)
1321
}
1422

1523
export function invalidateSettingsCaches(queryClient: QueryClient, userId = 'default') {
@@ -26,8 +34,3 @@ export function invalidateSessionCaches(queryClient: QueryClient) {
2634
query.queryKey[1] === 'messages'),
2735
})
2836
}
29-
30-
export function invalidateAllConfigRelatedCaches(queryClient: QueryClient, userId = 'default') {
31-
invalidateSettingsCaches(queryClient, userId)
32-
invalidateSessionCaches(queryClient)
33-
}

0 commit comments

Comments
 (0)