Remove decryption and api key storage

This commit is contained in:
Joey Yakimowich-Payne 2026-01-22 07:32:11 -07:00
commit 15b76f330b
No known key found for this signature in database
GPG key ID: 6BFE655FA5ABD1E1
8 changed files with 326 additions and 233 deletions

View file

@ -4,6 +4,56 @@ import { useAuthenticatedFetch } from './useAuthenticatedFetch';
import type { UserPreferences } from '../types';
import { COLOR_SCHEMES } from '../types';
const LOCAL_API_KEYS_STORAGE = 'kaboot.apiKeys';
type LocalApiKeys = Pick<UserPreferences, 'geminiApiKey' | 'openRouterApiKey' | 'openAIApiKey'>;
const normalizeApiKey = (value: unknown): string | undefined => {
if (typeof value !== 'string') return undefined;
const trimmed = value.trim();
return trimmed ? trimmed : undefined;
};
const readLocalApiKeys = (): LocalApiKeys => {
if (typeof window === 'undefined') return {};
try {
const raw = window.localStorage.getItem(LOCAL_API_KEYS_STORAGE);
if (!raw) return {};
const parsed = JSON.parse(raw) as Record<string, unknown>;
return {
geminiApiKey: normalizeApiKey(parsed.geminiApiKey),
openRouterApiKey: normalizeApiKey(parsed.openRouterApiKey),
openAIApiKey: normalizeApiKey(parsed.openAIApiKey),
};
} catch {
return {};
}
};
const writeLocalApiKeys = (keys: LocalApiKeys) => {
if (typeof window === 'undefined') return;
const normalized = {
geminiApiKey: normalizeApiKey(keys.geminiApiKey),
openRouterApiKey: normalizeApiKey(keys.openRouterApiKey),
openAIApiKey: normalizeApiKey(keys.openAIApiKey),
};
const hasAny = Boolean(
normalized.geminiApiKey || normalized.openRouterApiKey || normalized.openAIApiKey
);
if (!hasAny) {
window.localStorage.removeItem(LOCAL_API_KEYS_STORAGE);
return;
}
window.localStorage.setItem(LOCAL_API_KEYS_STORAGE, JSON.stringify(normalized));
};
const mergePreferencesWithLocalKeys = (prefs: UserPreferences, localKeys: LocalApiKeys): UserPreferences => ({
...prefs,
geminiApiKey: localKeys.geminiApiKey ?? prefs.geminiApiKey,
openRouterApiKey: localKeys.openRouterApiKey ?? prefs.openRouterApiKey,
openAIApiKey: localKeys.openAIApiKey ?? prefs.openAIApiKey,
});
const DEFAULT_PREFERENCES: UserPreferences = {
colorScheme: 'blue',
aiProvider: 'gemini',
@ -51,7 +101,7 @@ export const useUserPreferences = (): UseUserPreferencesReturn => {
const response = await authFetch('/api/users/me/preferences');
if (response.ok) {
const data = await response.json();
const prefs: UserPreferences = {
const serverPrefs: UserPreferences = {
colorScheme: data.colorScheme || 'blue',
aiProvider: data.aiProvider || 'gemini',
geminiApiKey: data.geminiApiKey || undefined,
@ -61,9 +111,17 @@ export const useUserPreferences = (): UseUserPreferencesReturn => {
openAIApiKey: data.openAIApiKey || undefined,
openAIModel: data.openAIModel || undefined,
};
setPreferences(prefs);
const localKeys = readLocalApiKeys();
const mergedPrefs = mergePreferencesWithLocalKeys(serverPrefs, localKeys);
const mergedLocalKeys: LocalApiKeys = {
geminiApiKey: localKeys.geminiApiKey ?? serverPrefs.geminiApiKey,
openRouterApiKey: localKeys.openRouterApiKey ?? serverPrefs.openRouterApiKey,
openAIApiKey: localKeys.openAIApiKey ?? serverPrefs.openAIApiKey,
};
writeLocalApiKeys(mergedLocalKeys);
setPreferences(mergedPrefs);
setHasAIAccess(data.hasAIAccess || false);
applyColorScheme(prefs.colorScheme);
applyColorScheme(mergedPrefs.colorScheme);
const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:3001';
try {
@ -91,17 +149,34 @@ export const useUserPreferences = (): UseUserPreferencesReturn => {
const savePreferences = useCallback(async (prefs: UserPreferences) => {
setSaving(true);
try {
const nextPrefs: UserPreferences = { ...preferences, ...prefs };
const localKeys: LocalApiKeys = {
geminiApiKey: nextPrefs.geminiApiKey,
openRouterApiKey: nextPrefs.openRouterApiKey,
openAIApiKey: nextPrefs.openAIApiKey,
};
writeLocalApiKeys(localKeys);
const serverPrefs = {
...nextPrefs,
geminiApiKey: undefined,
openRouterApiKey: undefined,
openAIApiKey: undefined,
};
const response = await authFetch('/api/users/me/preferences', {
method: 'PUT',
body: JSON.stringify(prefs),
body: JSON.stringify(serverPrefs),
});
if (!response.ok) {
throw new Error('Failed to save preferences');
}
setPreferences(prefs);
applyColorScheme(prefs.colorScheme);
const normalizedLocalKeys = readLocalApiKeys();
const mergedPrefs = mergePreferencesWithLocalKeys(nextPrefs, normalizedLocalKeys);
setPreferences(mergedPrefs);
applyColorScheme(mergedPrefs.colorScheme);
toast.success('Preferences saved!');
} catch (err) {
const message = err instanceof Error ? err.message : 'Failed to save preferences';
@ -110,7 +185,7 @@ export const useUserPreferences = (): UseUserPreferencesReturn => {
} finally {
setSaving(false);
}
}, [authFetch]);
}, [authFetch, preferences]);
useEffect(() => {
fetchPreferences();