import type { AlbyInvoice } from '@/types/alby' import { storageService } from './storage/indexedDB' interface StoredArticleData { content: string authorPubkey: string articleId: string invoice: { invoice: string paymentHash: string amount: number expiresAt: number } | null createdAt: number } // Default expiration: 30 days in milliseconds const DEFAULT_EXPIRATION = 30 * 24 * 60 * 60 * 1000 const MASTER_KEY_STORAGE_KEY = 'article_storage_master_key' function getOrCreateMasterKey(): string { if (typeof window === 'undefined') { throw new Error('Storage encryption requires browser environment') } const existing = localStorage.getItem(MASTER_KEY_STORAGE_KEY) if (existing) { return existing } const keyBytes = crypto.getRandomValues(new Uint8Array(32)) let binary = '' keyBytes.forEach((b) => { binary += String.fromCharCode(b) }) const key = btoa(binary) localStorage.setItem(MASTER_KEY_STORAGE_KEY, key) return key } function deriveSecret(articleId: string): string { const masterKey = getOrCreateMasterKey() return `${masterKey}:${articleId}` } /** * Store private content temporarily until payment is confirmed * Also stores the invoice if provided * Uses IndexedDB exclusively * Content expires after 30 days by default */ export async function storePrivateContent( articleId: string, content: string, authorPubkey: string, invoice?: AlbyInvoice ): Promise { try { const key = `article_private_content_${articleId}` const secret = deriveSecret(articleId) const data: StoredArticleData = { content, authorPubkey, articleId, invoice: invoice ? { invoice: invoice.invoice, paymentHash: invoice.paymentHash, amount: invoice.amount, expiresAt: invoice.expiresAt, } : null, createdAt: Date.now(), } // Store with expiration (30 days) await storageService.set(key, data, secret, DEFAULT_EXPIRATION) } catch (error) { console.error('Error storing private content:', error) } } /** * Get stored private content for an article * Returns null if not found or expired */ export async function getStoredPrivateContent(articleId: string): Promise<{ content: string authorPubkey: string invoice?: AlbyInvoice } | null> { try { const key = `article_private_content_${articleId}` const secret = deriveSecret(articleId) const data = await storageService.get(key, secret) if (!data) { return null } return { content: data.content, authorPubkey: data.authorPubkey, ...(data.invoice ? { invoice: { invoice: data.invoice.invoice, paymentHash: data.invoice.paymentHash, amount: data.invoice.amount, expiresAt: data.invoice.expiresAt, } as AlbyInvoice, } : {}), } } catch (error) { console.error('Error retrieving private content:', error) return null } } /** * Get stored invoice for an article */ export async function getStoredInvoice(articleId: string): Promise { const stored = await getStoredPrivateContent(articleId) return stored?.invoice ?? null } /** * Remove stored private content (after successful send or expiry) */ export async function removeStoredPrivateContent(articleId: string): Promise { try { const key = `article_private_content_${articleId}` await storageService.delete(key) } catch (error) { console.error('Error removing private content:', error) } }