Remove decryption and api key storage
This commit is contained in:
parent
2e12edc249
commit
15b76f330b
8 changed files with 326 additions and 233 deletions
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue