feat: implement vault synchronization and decryption improvements

- Added background synchronization for vault core data, including optional folder updates.
- Introduced a new API endpoint to retrieve the vault revision date.
- Enhanced vault synchronization logic to utilize a caching mechanism for improved performance.
- Created a new vault cache module to handle IndexedDB storage for vault core snapshots.
- Implemented a worker for asynchronous decryption of vault data, improving UI responsiveness.
- Updated main application settings to adjust query stale time for better data freshness.
- Refactored vault-related API functions to support cache keys for more efficient data retrieval.
This commit is contained in:
shuaiplus
2026-04-28 22:10:34 +08:00
parent aa6f9210b4
commit 1b0386bf78
10 changed files with 702 additions and 331 deletions
+108
View File
@@ -0,0 +1,108 @@
import type { Cipher, Folder } from './types';
export interface VaultCoreSnapshot {
ciphers: Cipher[];
folders: Folder[];
}
interface VaultCoreCacheRecord {
cacheKey: string;
revisionStamp: number;
savedAt: number;
snapshot: VaultCoreSnapshot;
}
const DB_NAME = 'nodewarden-web-cache';
const DB_VERSION = 1;
const VAULT_CORE_STORE = 'vault-core';
let dbPromise: Promise<IDBDatabase | null> | null = null;
function supportsIndexedDb(): boolean {
return typeof indexedDB !== 'undefined';
}
function openDatabase(): Promise<IDBDatabase | null> {
if (!supportsIndexedDb()) return Promise.resolve(null);
if (dbPromise) return dbPromise;
dbPromise = new Promise((resolve) => {
try {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onupgradeneeded = () => {
const db = request.result;
if (!db.objectStoreNames.contains(VAULT_CORE_STORE)) {
db.createObjectStore(VAULT_CORE_STORE, { keyPath: 'cacheKey' });
}
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => resolve(null);
request.onblocked = () => resolve(null);
} catch {
resolve(null);
}
});
return dbPromise;
}
function withStore<T>(
mode: IDBTransactionMode,
run: (store: IDBObjectStore) => Promise<T>
): Promise<T | null> {
return openDatabase().then((db) => {
if (!db) return null;
return new Promise<T | null>((resolve) => {
try {
const tx = db.transaction(VAULT_CORE_STORE, mode);
const store = tx.objectStore(VAULT_CORE_STORE);
void run(store).then(resolve).catch(() => resolve(null));
tx.onerror = () => resolve(null);
tx.onabort = () => resolve(null);
} catch {
resolve(null);
}
});
});
}
export async function loadCachedVaultCoreSnapshot(cacheKey: string): Promise<VaultCoreCacheRecord | null> {
const normalized = String(cacheKey || '').trim();
if (!normalized) return null;
return withStore('readonly', (store) => new Promise<VaultCoreCacheRecord | null>((resolve) => {
const request = store.get(normalized);
request.onsuccess = () => {
const record = request.result as VaultCoreCacheRecord | undefined;
resolve(record || null);
};
request.onerror = () => resolve(null);
}));
}
export async function saveCachedVaultCoreSnapshot(
cacheKey: string,
revisionStamp: number,
snapshot: VaultCoreSnapshot
): Promise<void> {
const normalized = String(cacheKey || '').trim();
if (!normalized) return;
await withStore('readwrite', (store) => new Promise<void>((resolve) => {
const record: VaultCoreCacheRecord = {
cacheKey: normalized,
revisionStamp,
savedAt: Date.now(),
snapshot,
};
const request = store.put(record);
request.onsuccess = () => resolve();
request.onerror = () => resolve();
}));
}
export async function clearCachedVaultCoreSnapshot(cacheKey: string): Promise<void> {
const normalized = String(cacheKey || '').trim();
if (!normalized) return;
await withStore('readwrite', (store) => new Promise<void>((resolve) => {
const request = store.delete(normalized);
request.onsuccess = () => resolve();
request.onerror = () => resolve();
}));
}