import { encryptValue, decryptValue, isEncryptionAvailable } from '@/lib/auth/local-storage-crypto'; const ENCRYPTED_PREFIX = 'ENC:'; function isEncrypted(value: string): boolean { return value.startsWith(ENCRYPTED_PREFIX); } function stripPrefix(value: string): string { return value.slice(ENCRYPTED_PREFIX.length); } // Cache of recently decrypted values. The Zustand persist middleware calls // getItem frequently during rehydration, and we want to avoid re-decrypting // the same ciphertext on every read. Keyed by storage key. const decryptedCache = new Map(); function cacheKey(name: string): string { return `vncmail:decrypted:${name}`; } function getCachedDecrypted(name: string): string | null | undefined { return decryptedCache.get(cacheKey(name)); } function setCachedDecrypted(name: string, value: string | null): void { decryptedCache.set(cacheKey(name), value); } function invalidateDecryptedCache(name: string): void { decryptedCache.delete(cacheKey(name)); } export function createEncryptedStorage(): { getItem: (name: string) => Promise; setItem: (name: string, value: string) => Promise; removeItem: (name: string) => Promise; } { return { getItem: async (name: string): Promise => { try { const raw = localStorage.getItem(name); if (raw === null) return null; if (!isEncrypted(raw)) { if (isEncryptionAvailable()) { // Legacy plaintext value found — return as-is, but re-encrypt on // the next write (setItem below always encrypts when available). return raw; } return raw; } const cached = getCachedDecrypted(name); if (cached !== undefined) return cached; const ciphertext = stripPrefix(raw); const decrypted = await decryptValue(ciphertext); setCachedDecrypted(name, decrypted); return decrypted; } catch { return null; } }, setItem: async (name: string, value: string): Promise => { try { if (isEncryptionAvailable()) { const ciphertext = await encryptValue(value); localStorage.setItem(name, `${ENCRYPTED_PREFIX}${ciphertext}`); } else { localStorage.setItem(name, value); } invalidateDecryptedCache(name); } catch { try { localStorage.setItem(name, value); } catch { /* noop */ } } }, removeItem: async (name: string): Promise => { try { localStorage.removeItem(name); } catch { /* noop */ } invalidateDecryptedCache(name); }, }; } export const encryptedStorage = createEncryptedStorage();