| 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<ApiKeyConfig[]>([]); |
| 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<ApiKeyConfig, "id">) => { |
| 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<Omit<ApiKeyConfig, "id">>) => { |
| const next = configs.map((c) => { |
| if (c.id !== id) return c; |
| |
| 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, |
| }; |
| } |
|
|
| |
| export function useApiKey() { |
| const { configs, isLoading, addConfig, removeConfig } = |
| useApiKeys(); |
|
|
| const storedKey = configs[0] ?? null; |
|
|
| return { |
| storedKey, |
| isLoading, |
| saveKey: addConfig, |
| removeKey: removeConfig, |
| hasKey: !!storedKey, |
| }; |
| } |
|
|