feat: add S/MIME store for managing key records and public certificates
- Implemented Zustand store for S/MIME functionality, including state management for key records and public certificates. - Added methods for importing PKCS#12 files and public certificates, binding identities to keys, and managing unlocked keys. - Introduced session storage for remembering unlocked keys across sessions. - Enhanced error handling and loading states during data operations.
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
import type { SmimeKeyRecord, SmimePublicCert } from './types';
|
||||
|
||||
const DB_NAME = 'smime-store';
|
||||
const DB_VERSION = 1;
|
||||
const KEY_RECORDS_STORE = 'key-records';
|
||||
const PUBLIC_CERTS_STORE = 'public-certs';
|
||||
|
||||
function openDB(): Promise<IDBDatabase> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
||||
request.onupgradeneeded = () => {
|
||||
const db = request.result;
|
||||
if (!db.objectStoreNames.contains(KEY_RECORDS_STORE)) {
|
||||
const keyStore = db.createObjectStore(KEY_RECORDS_STORE, { keyPath: 'id' });
|
||||
keyStore.createIndex('email', 'email', { unique: false });
|
||||
}
|
||||
if (!db.objectStoreNames.contains(PUBLIC_CERTS_STORE)) {
|
||||
const certStore = db.createObjectStore(PUBLIC_CERTS_STORE, { keyPath: 'id' });
|
||||
certStore.createIndex('email', 'email', { unique: false });
|
||||
}
|
||||
};
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
function txPromise<T>(
|
||||
db: IDBDatabase,
|
||||
storeName: string,
|
||||
mode: globalThis.IDBTransactionMode,
|
||||
fn: (store: IDBObjectStore) => IDBRequest<T>,
|
||||
): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(storeName, mode);
|
||||
const store = tx.objectStore(storeName);
|
||||
const req = fn(store);
|
||||
req.onsuccess = () => resolve(req.result);
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
|
||||
// ── Key record CRUD ─────────────────────────────────────────────────
|
||||
|
||||
export async function saveKeyRecord(record: SmimeKeyRecord): Promise<void> {
|
||||
const db = await openDB();
|
||||
await txPromise(db, KEY_RECORDS_STORE, 'readwrite', (s) => s.put(record));
|
||||
}
|
||||
|
||||
export async function getKeyRecord(id: string): Promise<SmimeKeyRecord | undefined> {
|
||||
const db = await openDB();
|
||||
return txPromise(db, KEY_RECORDS_STORE, 'readonly', (s) => s.get(id));
|
||||
}
|
||||
|
||||
export async function getKeyRecordForEmail(email: string): Promise<SmimeKeyRecord | undefined> {
|
||||
const db = await openDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(KEY_RECORDS_STORE, 'readonly');
|
||||
const idx = tx.objectStore(KEY_RECORDS_STORE).index('email');
|
||||
const req = idx.get(email.toLowerCase());
|
||||
req.onsuccess = () => resolve(req.result ?? undefined);
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
|
||||
export async function listKeyRecords(): Promise<SmimeKeyRecord[]> {
|
||||
const db = await openDB();
|
||||
return txPromise(db, KEY_RECORDS_STORE, 'readonly', (s) => s.getAll());
|
||||
}
|
||||
|
||||
export async function deleteKeyRecord(id: string): Promise<void> {
|
||||
const db = await openDB();
|
||||
await txPromise(db, KEY_RECORDS_STORE, 'readwrite', (s) => s.delete(id));
|
||||
}
|
||||
|
||||
// ── Public cert CRUD ────────────────────────────────────────────────
|
||||
|
||||
export async function savePublicCert(cert: SmimePublicCert): Promise<void> {
|
||||
const db = await openDB();
|
||||
await txPromise(db, PUBLIC_CERTS_STORE, 'readwrite', (s) => s.put(cert));
|
||||
}
|
||||
|
||||
export async function getPublicCertForEmail(email: string): Promise<SmimePublicCert | undefined> {
|
||||
const db = await openDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(PUBLIC_CERTS_STORE, 'readonly');
|
||||
const idx = tx.objectStore(PUBLIC_CERTS_STORE).index('email');
|
||||
const req = idx.get(email.toLowerCase());
|
||||
req.onsuccess = () => resolve(req.result ?? undefined);
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
|
||||
export async function listPublicCerts(): Promise<SmimePublicCert[]> {
|
||||
const db = await openDB();
|
||||
return txPromise(db, PUBLIC_CERTS_STORE, 'readonly', (s) => s.getAll());
|
||||
}
|
||||
|
||||
export async function deletePublicCert(id: string): Promise<void> {
|
||||
const db = await openDB();
|
||||
await txPromise(db, PUBLIC_CERTS_STORE, 'readwrite', (s) => s.delete(id));
|
||||
}
|
||||
Reference in New Issue
Block a user