db (IndexedDB)

基於 Dexie 的 IndexedDB 簡易封裝,framework 用它快取 .bnc 解密後的資源(SwImage / SwAsyncText / useDecryptedAsset 的持久快取層)。業務需要用戶端持久化大塊二進位 / 結構化資料時也可直接用。3 個函式全部 auto-import。

運作原理

  1. 模組載入時即建立 Dexie 實例(資料庫名 database,版本 1),固定包含兩個 store:
    • image — 解密後的圖片二進位 / blob URL 快取
    • text — 解密後的文字片段快取
  2. 每筆記錄帶自增 ++id 和 updated_at 索引,便於按時間淘汰。
  3. 每個 API 首次呼叫會懶開啟資料庫,Dexie 處理並行競爭;實例為模組級單例,同一分頁內多處呼叫共享連線。
  4. dbPut 自動覆寫 updated_at(秒級時間戳),呼叫方無需手動維護。

API

函式簽名說明
dbPut(data: object, storeName: string) => Promise<void>新增或更新一筆記錄(data 需含 id 欄位作主鍵),自動寫 updated_at
dbGet(id: string, storeName: string) => Promise<unknown>按 id 查詢單筆記錄,未命中回傳 undefined
dbClear(storeName: string) => Promise<void>清空指定 store

使用範例

dbPut / dbGet 讀寫(重新整理仍在)

加载中...
// 快取解密後的圖片 blob URL
await dbPut({ id: 'cover.bnc', url: blobUrl }, 'image')

// 讀取快取,未命中則回源解密
const hit = await dbGet('cover.bnc', 'image')
const url = hit?.url ?? await decryptAndCache('cover.bnc')

// 登出時清空全部快取
await dbClear('image')
await dbClear('text')

注意事項

僅用戶端可用(DB-001,blocker)

IndexedDB 是瀏覽器 API,Node.js 端不存在 —— SSR 期呼叫直接報錯。所有 db API 必須在 onMounted / IS_CLIENT 守衛 / client-only 元件內呼叫:

// ❌ setup 頂層呼叫,SSR 期 indexedDB is not defined
const hit = await dbGet('cover.bnc', 'image')

// ✅ onMounted 內呼叫
onMounted(async () => {
  const hit = await dbGet('cover.bnc', 'image')
})

storeName 只能是 'image' / 'text'(DB-002,warning)

schema 固定只有這兩個 store,但 storeName 參數類型是裸 string、無編譯校驗。傳未定義的表名(如 'user' / 'cover'),Dexie 回傳「幽靈 table」 —— 寫入成功不報錯,但資料進虛空、永遠讀不回來,排查極難:

// ❌ 'cover' 不是 framework 建的 store → 寫入成功但讀不回
await dbPut({ id: 'x', url }, 'cover')
const hit = await dbGet('x', 'cover') // 永遠 undefined

// ✅ 只用框架建的兩個表名
await dbPut({ id: 'x', url }, 'image')

需要業務自有表結構時,請在業務層另建自己的 Dexie 實例,不要複用 framework 的 database。