import { app, safeStorage } from 'electron'; import fs from 'node:fs/promises'; import path from 'node:path'; export interface SecretStore { set(key: string, value: string): Promise; get(key: string): Promise; clear(key: string): Promise; isHardwareBacked(): Promise; } const memoryStore = new Map(); const storePath = path.join(app.getPath('userData'), 'secrets.enc.json'); async function readDiskStore(): Promise> { try { const raw = await fs.readFile(storePath, 'utf8'); return JSON.parse(raw) as Record; } catch { return {}; } } async function writeDiskStore(data: Record): Promise { await fs.mkdir(path.dirname(storePath), { recursive: true }); await fs.writeFile(storePath, JSON.stringify(data), { encoding: 'utf8', mode: 0o600 }); } async function canPersistEncryptedSecrets(): Promise { try { if (safeStorage.getSelectedStorageBackend?.() === 'basic_text') { return false; } return await safeStorage.isAsyncEncryptionAvailable(); } catch { return false; } } export const secretStore: SecretStore = { async set(key, value) { if (!(await canPersistEncryptedSecrets())) { memoryStore.set(key, value); return; } const encrypted = await safeStorage.encryptStringAsync(value); const data = await readDiskStore(); data[key] = encrypted.toString('base64'); await writeDiskStore(data); }, async get(key) { if (!(await canPersistEncryptedSecrets())) { return memoryStore.get(key) ?? null; } const data = await readDiskStore(); const encoded = data[key]; if (!encoded) return null; try { const decrypted = await safeStorage.decryptStringAsync(Buffer.from(encoded, 'base64')); if (decrypted.shouldReEncrypt) { await this.set(key, decrypted.result); } return decrypted.result; } catch { await this.clear(key); return null; } }, async clear(key) { memoryStore.delete(key); const data = await readDiskStore(); if (key in data) { delete data[key]; await writeDiskStore(data); } }, async isHardwareBacked() { return canPersistEncryptedSecrets(); }, };