feat(app): add encrypted API key vault with IDB storage
Create key-vault.ts with AES-256-GCM encrypted IndexedDB storage for API keys (Claude, OpenRouter). Add ApiKeyManager.vue settings UI with masked key display and add/remove functionality. Integrate vault lookups into useAI.ts streaming functions with graceful fallback when IDB is unavailable. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
621a324859
commit
9e4a2c30e1
@@ -1,6 +1,7 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { useChatStore } from '@/stores/chat'
|
||||
import { searchWeb } from '@/composables/useWebSearch'
|
||||
import { getApiKey } from '@/utils/key-vault'
|
||||
|
||||
type Provider = 'claude' | 'openrouter' | 'mock'
|
||||
|
||||
@@ -136,9 +137,17 @@ async function streamClaude(
|
||||
webSearch: boolean,
|
||||
signal?: AbortSignal,
|
||||
): Promise<void> {
|
||||
const headers: Record<string, string> = { 'Content-Type': 'application/json' }
|
||||
|
||||
// Use vault key if available, proxy uses its own key as fallback
|
||||
const vaultKey = await getApiKey('claude')
|
||||
if (vaultKey) {
|
||||
headers['x-api-key'] = vaultKey
|
||||
}
|
||||
|
||||
const res = await fetch(CLAUDE_PATH, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
model: activeModel.value,
|
||||
system: systemPrompt,
|
||||
@@ -177,13 +186,21 @@ async function streamOpenRouter(
|
||||
...messages.map((m) => ({ role: m.role as 'user' | 'assistant', content: m.content })),
|
||||
]
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'HTTP-Referer': window.location.origin,
|
||||
'X-Title': 'AIUI',
|
||||
}
|
||||
|
||||
// Use vault key if available, otherwise proxy handles auth
|
||||
const vaultKey = await getApiKey('openrouter')
|
||||
if (vaultKey) {
|
||||
headers['Authorization'] = `Bearer ${vaultKey}`
|
||||
}
|
||||
|
||||
const res = await fetch(OPENROUTER_PATH, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'HTTP-Referer': window.location.origin,
|
||||
'X-Title': 'AIUI',
|
||||
},
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
model: activeModel.value,
|
||||
messages: orMessages,
|
||||
|
||||
Reference in New Issue
Block a user