| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { getDb } from './db'; |
| import { decryptString, encryptString } from './crypto'; |
|
|
| export interface UserSettings { |
| language?: string; |
| country?: string; |
| units?: 'metric' | 'imperial'; |
| defaultModel?: string; |
| theme?: 'light' | 'dark' | 'auto'; |
| |
| ehr?: Record<string, any>; |
| |
| hfToken?: string; |
| } |
|
|
| export function getUserSettings(userId: string): UserSettings { |
| if (!userId) return {}; |
| const db = getDb(); |
| const row = db |
| .prepare( |
| `SELECT language, country, units, default_model, theme, ehr, hf_token_encrypted |
| FROM user_settings WHERE user_id = ?`, |
| ) |
| .get(userId) as any; |
| if (!row) return { ehr: {} }; |
| return { |
| language: row.language || undefined, |
| country: row.country || undefined, |
| units: (row.units as 'metric' | 'imperial' | null) || undefined, |
| defaultModel: row.default_model || undefined, |
| theme: (row.theme as 'light' | 'dark' | 'auto' | null) || undefined, |
| ehr: row.ehr ? safeJson(row.ehr, {}) : {}, |
| hfToken: row.hf_token_encrypted ? decryptString(row.hf_token_encrypted) : undefined, |
| }; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| export function upsertUserSettings( |
| userId: string, |
| patch: Partial<UserSettings>, |
| ): void { |
| if (!userId) throw new Error('upsertUserSettings: userId required'); |
|
|
| const cur = getUserSettings(userId); |
|
|
| const merged: UserSettings = { |
| language: pick(patch.language, cur.language), |
| country: pick(patch.country, cur.country), |
| units: pick(patch.units, cur.units), |
| defaultModel: pick(patch.defaultModel, cur.defaultModel), |
| theme: pick(patch.theme, cur.theme), |
| ehr: patch.ehr ? { ...(cur.ehr || {}), ...patch.ehr } : cur.ehr || {}, |
| |
| hfToken: patch.hfToken === undefined ? cur.hfToken : patch.hfToken || undefined, |
| }; |
|
|
| const ehrJson = JSON.stringify(merged.ehr || {}); |
| const tokenEnc = merged.hfToken ? encryptString(merged.hfToken) : null; |
|
|
| const db = getDb(); |
| db.prepare( |
| `INSERT INTO user_settings |
| (user_id, language, country, units, default_model, theme, ehr, hf_token_encrypted, updated_at) |
| VALUES (?, ?, ?, ?, ?, ?, ?, ?, datetime('now')) |
| ON CONFLICT(user_id) DO UPDATE SET |
| language = excluded.language, |
| country = excluded.country, |
| units = excluded.units, |
| default_model = excluded.default_model, |
| theme = excluded.theme, |
| ehr = excluded.ehr, |
| hf_token_encrypted = excluded.hf_token_encrypted, |
| updated_at = datetime('now')`, |
| ).run( |
| userId, |
| merged.language || null, |
| merged.country || null, |
| merged.units || null, |
| merged.defaultModel || null, |
| merged.theme || null, |
| ehrJson, |
| tokenEnc, |
| ); |
| } |
|
|
| function pick<T>(next: T | undefined, prev: T | undefined): T | undefined { |
| return next === undefined ? prev : next; |
| } |
|
|
| function safeJson<T>(raw: string, fallback: T): T { |
| try { |
| return JSON.parse(raw) as T; |
| } catch { |
| return fallback; |
| } |
| } |
|
|