feat: encrypted credential vault

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
ahmed mohammed
2026-09-22 07:34:01 +03:00
co-authored by Claude Opus 4.7
parent 5f8746cfae
commit dd6b880f49
4 changed files with 123 additions and 2 deletions
+6 -2
View File
@@ -62,10 +62,14 @@ export const getSettings = (userId: string) =>
one<SettingsInput & { onboarded: number }>("SELECT * FROM settings WHERE user_id = ?", userId);
export function saveSettings(userId: string, s: SettingsInput) {
// A blank secret means "keep what is stored" — enforced here, not only in the form layer, so no caller
// (password change, onboarding re-save, a future import) can blank a stored key by omitting it.
db.prepare(`INSERT INTO settings (user_id, analyst_base_url, analyst_key, analyst_model, analyst_extra,
jev_key, jev_model, onboarded, updated_at) VALUES (?,?,?,?,?,?,?,?,?)
ON CONFLICT(user_id) DO UPDATE SET analyst_base_url=excluded.analyst_base_url, analyst_key=excluded.analyst_key,
analyst_model=excluded.analyst_model, analyst_extra=excluded.analyst_extra, jev_key=excluded.jev_key,
ON CONFLICT(user_id) DO UPDATE SET analyst_base_url=excluded.analyst_base_url,
analyst_key=COALESCE(NULLIF(excluded.analyst_key, ''), settings.analyst_key),
analyst_model=excluded.analyst_model, analyst_extra=excluded.analyst_extra,
jev_key=COALESCE(NULLIF(excluded.jev_key, ''), settings.jev_key),
jev_model=excluded.jev_model, onboarded=excluded.onboarded, updated_at=excluded.updated_at`)
.run(userId, s.analyst_base_url, s.analyst_key, s.analyst_model, s.analyst_extra, s.jev_key, s.jev_model,
s.onboarded, now());