import { useState, useEffect, useCallback } from "react"; import { encryptText, decryptText } from "@/lib/crypto"; import { trackUmamiEvent, AnalyticsEvent } from "@/lib/umami"; const STORAGE_KEY = "pram_api_keys_v2"; export interface ApiKeyConfig { id: string; name: string; provider: string; baseUrl: string; apiKey: string; modelName: string; maxTokens?: number; } function generateId() { return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 9)}`; } export function useApiKeys() { const [configs, setConfigs] = useState([]); const [isLoading, setIsLoading] = useState(true); useEffect(() => { loadConfigs(); }, []); const loadConfigs = async () => { try { const encrypted = localStorage.getItem(STORAGE_KEY); if (encrypted) { const decrypted = await decryptText(encrypted); setConfigs(JSON.parse(decrypted)); } } catch { localStorage.removeItem(STORAGE_KEY); } finally { setIsLoading(false); } }; const persist = useCallback(async (next: ApiKeyConfig[]) => { const encrypted = await encryptText(JSON.stringify(next)); localStorage.setItem(STORAGE_KEY, encrypted); setConfigs(next); }, []); const addConfig = useCallback( async (config: Omit) => { const next = [...configs, { ...config, id: generateId() }]; await persist(next); trackUmamiEvent(AnalyticsEvent.API_KEY_ADDED, { provider: config.provider }); return next[next.length - 1].id; }, [configs, persist], ); const updateConfig = useCallback( async (id: string, updates: Partial>) => { const next = configs.map((c) => { if (c.id !== id) return c; // If apiKey is empty string, keep the old one (don't overwrite) const apiKey = updates.apiKey === "" || updates.apiKey === undefined ? c.apiKey : updates.apiKey; return { ...c, ...updates, apiKey }; }); await persist(next); }, [configs, persist], ); const removeConfig = useCallback( async (id: string) => { const target = configs.find((c) => c.id === id); const next = configs.filter((c) => c.id !== id); await persist(next); if (target) { trackUmamiEvent(AnalyticsEvent.API_KEY_REMOVED, { provider: target.provider }); } }, [configs, persist], ); const getConfig = useCallback( (id: string) => configs.find((c) => c.id === id), [configs], ); return { configs, isLoading, addConfig, updateConfig, removeConfig, getConfig, hasConfigs: configs.length > 0, }; } // Backward-compatible hook for places that only need the first (default) key export function useApiKey() { const { configs, isLoading, addConfig, removeConfig } = useApiKeys(); const storedKey = configs[0] ?? null; return { storedKey, isLoading, saveKey: addConfig, removeKey: removeConfig, hasKey: !!storedKey, }; }