UNPKG

aura-storage

Version:
703 lines (687 loc) 18.8 kB
import Cookies from 'js-cookie'; import { encrypt, decrypt } from 'crypto-js/aes'; import pkcs7 from 'crypto-js/pad-pkcs7'; import ECB from 'crypto-js/mode-ecb'; import UTF8 from 'crypto-js/enc-utf8'; import { AlgorithmUtils } from 'aura-core'; import { isUndefinedOrNull } from 'aura-shared'; import localforage from 'localforage'; var CacheType = /* @__PURE__ */ ((CacheType2) => { CacheType2["LOCALSTORAGE"] = "LOCALSTORAGE"; CacheType2["SESSIONSTORAGE"] = "SESSIONSTORAGE"; CacheType2["INDEXEDDB"] = "INDEXEDDB"; CacheType2["UNIAPP"] = "UNIAPP"; CacheType2["COOKIE"] = "COOKIE"; return CacheType2; })(CacheType || {}); class CookieStorageAdapter { constructor() { this.storageType = CacheType.COOKIE; } getItem(key) { return Cookies.get(key); } setItem(key, value) { Cookies.set(key, value); } removeItem(key) { Cookies.remove(key); } clear() { const keys = this.keys(); keys.forEach((key) => this.removeItem(key)); } keys() { return document.cookie.split(";").map((cookie) => cookie.trim().split("=")[0]); } size() { return this.keys().length; } } class UniAppStorageAdapter { constructor() { this.storageType = CacheType.UNIAPP; } getItem(key) { try { return uni.getStorageSync(key); } catch (error) { console.error("UniAppStorageAdapter getItem error:", error); return null; } } setItem(key, value) { try { uni.setStorageSync(key, value); } catch (error) { console.error("UniAppStorageAdapter setItem error:", error); } } removeItem(key) { try { uni.removeStorageSync(key); } catch (error) { console.error("UniAppStorageAdapter removeItem error:", error); } } clear() { try { const keys = this.keys(); keys.forEach((key) => uni.removeStorageSync(key)); } catch (error) { console.error("UniAppStorageAdapter clear error:", error); } } keys() { try { const storage = uni.getStorageSync(this.storageType); return Object.keys(storage); } catch (error) { console.error("UniAppStorageAdapter keys error:", error); return []; } } size() { try { const storage = uni.getStorageSync(this.storageType); return Object.keys(storage).length; } catch (error) { console.error("UniAppStorageAdapter size error:", error); return 0; } } } class WebStorageAdapter { constructor(storage) { this.storage = storage; this.storage = storage; this.storageType = CacheType.LOCALSTORAGE || CacheType.SESSIONSTORAGE; } /** * 获取存储项 * @param key 键名 * @returns 存储的值,如果不存在则返回 null */ getItem(key) { try { return this.storage.getItem(key); } catch (error) { console.error("WebStorageAdapter getItem error:", error); return null; } } /** * 设置存储项 * @param key 键名 * @param value 要存储的值 */ setItem(key, value) { try { this.storage.setItem(key, value); } catch (error) { console.error("WebStorageAdapter setItem error:", error); } } /** * 移除存储项 * @param key 键名 */ removeItem(key) { try { this.storage.removeItem(key); } catch (error) { console.error("WebStorageAdapter removeItem error:", error); } } /** * 清空所有存储项 */ clear() { try { this.storage.clear(); } catch (error) { console.error("WebStorageAdapter clear error:", error); } } /** * 获取所有存储项的键名 * @returns 键名数组 */ keys() { try { return Object.keys(this.storage); } catch (error) { console.error("WebStorageAdapter keys error:", error); return []; } } /** * 获取存储项的数量 * @returns 存储项数量 */ size() { try { return this.storage.length; } catch (error) { console.error("WebStorageAdapter size error:", error); return 0; } } } class AesEncryption { constructor(opt) { const { key, iv } = opt; if (key) { this.key = key; } if (iv) { this.iv = iv; } } get getOptions() { return { mode: ECB, padding: pkcs7, iv: this.iv }; } encryptByAES(cipherText) { if (!this.key) { throw new Error("Encryption key is required"); } return encrypt(cipherText, this.key, this.getOptions).toString(); } decryptByAES(cipherText) { if (!this.key) { throw new Error("Encryption key is required"); } return decrypt(cipherText, this.key, this.getOptions).toString(UTF8); } } class EncryptionService { constructor({ key, iv, hasEncrypt }) { if (hasEncrypt && this.handleEncryption(key, iv)) { this.encryption = new AesEncryption({ key, iv }); } else { this.encryption = { encryptByAES: (data) => data, decryptByAES: (data) => data }; } } encryptData(data) { try { return this.encryption.encryptByAES(data); } catch (error) { console.error("\u52A0\u5BC6\u6570\u636E\u5931\u8D25:", error); throw error; } } decryptData(encryptedData) { try { return this.encryption.decryptByAES(encryptedData); } catch (error) { console.error("\u89E3\u5BC6\u6570\u636E\u5931\u8D25:", error); throw error; } } handleEncryption(key, iv) { if ([key.length, iv.length].some((item) => item !== 16)) { console.error("When hasEncrypt is true, the key or iv must be 16 bits!"); throw new Error("When hasEncrypt is true, the key or iv must be 16 bits!"); } return true; } } class CacheService { constructor(cacheSize = 100) { this.cache = new AlgorithmUtils.LRUCache(cacheSize); } set(key, value) { this.cache.set(key, value); } has(key) { return this.cache.has(key); } get(key) { return this.cache.get(key); } delete(key) { this.cache.delete(key); } clear() { this.cache.clear(); } getCacheInstance() { return this.cache; } } const cacheService = new CacheService(200); function handleStringify(value) { return JSON.stringify(value); } function handleParse(value) { return JSON.parse(value); } var __async$1 = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; class BaseStorage { constructor({ prefix = "", hasEncrypt, key, iv }) { this.hasEncrypt = hasEncrypt; BaseStorage.encryptionService = new EncryptionService({ key, iv, hasEncrypt }); this.cache = cacheService.getCacheInstance(); this.prefix = prefix; } // 设置缓存 setCache(key, value, expire = null) { const keyItem = this.setCacheKey(key); const cacheItem = this.createCacheItem(keyItem, value, expire); this.cache.set(keyItem, cacheItem); } // 从缓存中获取数据 getCache(key) { const keyItem = this.setCacheKey(key); if (this.cache.has(keyItem)) { const cacheItem = this.cache.get(keyItem); if (cacheItem && this.isNotExpired(cacheItem)) { return cacheItem; } this.cache.delete(keyItem); this.removeItem(keyItem); } return void 0; } // 从缓存中删除数据 deleteCache(key) { const keyItem = this.setCacheKey(key); this.cache.delete(keyItem); } // 清除所有缓存 clearCache() { this.cache.clear(); } /** * 生成带有前缀(如果有)并转换为大写的键。 * * * @param {string} key - 需要格式化的原始键。 * @returns {string} - 带有前缀并转换为大写的键。 */ getPrefixedKey(key, prefixKey = this.prefix) { return `${prefixKey ? `${prefixKey}_` : ""}${key}`.toUpperCase().trim(); } setCacheKey(key) { return key + ":" + this.storageType; } /** * 检查数据是否未过期。 * * @param {StorageData} data - 数据对象。 * @returns {boolean} - 是否未过期。 */ isNotExpired(data) { return isUndefinedOrNull(data.expire) || data.expire >= Date.now(); } /** * 准备要存储的数据。 * * @param {*} value - 要存储的数据。 * @param {number} [expire=0] - 过期时间(单位`分`,默认`0`分钟,永久缓存)。 * @returns {string} - 准备好的 JSON 字符串。 */ prepareStorageData(key, value, expire = null) { const data = handleStringify(this.createCacheItem(key, value, expire)); return this.hasEncrypt ? BaseStorage.encryptionService.encryptData(data) : data; } /** * 解析存储的数据 */ parseStorageData(data) { try { const val = this.hasEncrypt ? BaseStorage.encryptionService.decryptData(data) : data; const parsedData = handleParse(val); if (this.isNotExpired(parsedData)) { return parsedData; } this.deleteCache(parsedData.key); return null; } catch (error) { console.error("\u89E3\u6790\u5B58\u50A8\u6570\u636E\u5931\u8D25:", error); return null; } } /** * 创建缓存项对象 */ createCacheItem(key, value, expire = null) { return { key, value, expire: expire ? Date.now() + expire * 60 * 1e3 : null, createdAt: Date.now(), isEncrypted: this.hasEncrypt }; } /** * 检查键是否存在 */ hasItem(key) { return __async$1(this, null, function* () { const keys = yield this.keys(); return keys.includes(this.getPrefixedKey(key)); }); } /** * 获取多个键的值 */ getItems(keys) { return __async$1(this, null, function* () { const result = {}; for (const key of keys) { result[key] = yield this.getItem(key); } return result; }); } /** * 设置多个键值对 */ setItems(items, expire) { return __async$1(this, null, function* () { for (const [key, value] of Object.entries(items)) { yield this.setItem(key, value, expire); } }); } /** * 移除多个键 */ removeItems(keys) { return __async$1(this, null, function* () { for (const key of keys) { yield this.removeItem(key); } }); } /** * 设置自动清理机制 */ // @ts-ignore setupAutoCleanup() { const cleanupIntervalMs = 3e5; BaseStorage.cleanupInterval = setInterval(() => { this.cleanCache(); }, cleanupIntervalMs); } /** * 清理过期的缓存项并重新获取存储数据 */ cleanCache() { return __async$1(this, null, function* () { const keys = Array.from(this.cache.keys()); for (const key of keys) { const cacheItem = this.cache.get(key); if (cacheItem && this.isNotExpired(cacheItem)) { this.cache.delete(key); } } const storageKeys = yield this.keys(); for (const storageKey of storageKeys) { const data = yield this.getItem(storageKey); if (data !== null) { this.createCacheItem(data); this.setCache(storageKey, data); } } }); } // 在类实例销毁时清除定时器 destroy() { if (BaseStorage.cleanupInterval) { clearInterval(BaseStorage.cleanupInterval); BaseStorage.cleanupInterval = null; } } } var __defProp$1 = Object.defineProperty; var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols; var __hasOwnProp$1 = Object.prototype.hasOwnProperty; var __propIsEnum$1 = Object.prototype.propertyIsEnumerable; var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues$1 = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp$1.call(b, prop)) __defNormalProp$1(a, prop, b[prop]); if (__getOwnPropSymbols$1) for (var prop of __getOwnPropSymbols$1(b)) { if (__propIsEnum$1.call(b, prop)) __defNormalProp$1(a, prop, b[prop]); } return a; }; class Storage extends BaseStorage { constructor(options) { super(__spreadValues$1({}, options)); this.storageAdapter = options.storageAdapter; this.storageType = options.storageAdapter.storageType; } getItem(key) { const prefixedKey = this.getPrefixedKey(key); const cachedValue = this.getCache(prefixedKey); if (cachedValue) { return cachedValue.value; } const data = this.storageAdapter.getItem(prefixedKey); if (!data) return null; const parsedData = this.parseStorageData(data); if (!parsedData) { this.removeItem(key); return null; } this.setCache(prefixedKey, parsedData.value); return parsedData.value; } setItem(key, value, expire) { const prefixedKey = this.getPrefixedKey(key); const data = this.prepareStorageData(prefixedKey, value, expire); this.storageAdapter.setItem(prefixedKey, data); this.setCache(prefixedKey, value); } removeItem(key) { const prefixedKey = this.getPrefixedKey(key); this.storageAdapter.removeItem(prefixedKey); this.deleteCache(prefixedKey); } clear() { this.storageAdapter.clear(); this.clearCache(); } keys() { return this.storageAdapter.keys().filter((key) => key.startsWith(this.prefix)).map((key) => key.slice(this.prefix.length + 1)); } size() { return this.storageAdapter.size(); } } var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; class IndexedDBStorage { constructor({ cacheName }) { this.storageType = CacheType.INDEXEDDB; this.storage = localforage.createInstance({ driver: [localforage.INDEXEDDB, localforage.LOCALSTORAGE], name: cacheName // 可以考虑使用更具有描述性的名称 }); } /** * 获取存储项 * @param key 键名 * @returns 存储的值,如果不存在则返回 null */ //@ts-ignore getItem(key) { return __async(this, null, function* () { try { return yield this.storage.getItem(key); } catch (error) { console.error("WebStorageAdapter getItem error:", error); return null; } }); } /** * 异步设置缓存项,并可以选择设置过期时间。 * * @param {string} key - 要存储数据的键。 * @param {*} value - 要存储的数据。 * @param {number} [expire] - 过期时间(单位`分`,默认`0`分钟,永久缓存)。 * @returns {Promise<void>} - 一个 Promise 对象,表示设置操作的结果。 */ setItem(key, value) { return __async(this, null, function* () { try { yield this.storage.setItem(key, value); } catch (error) { console.error("Error setting item in storage:", error); throw error; } }); } /** * 异步从离线仓库中删除对应键名的值。 * * @param {string} key - 键名。 * @returns {Promise<boolean>} - 操作是否成功。 */ removeItem(key) { return __async(this, null, function* () { try { yield this.storage.removeItem(key); } catch (error) { console.error("Error removing item from storage:", error); } }); } /** * 异步从离线仓库中删除所有的键名,重置数据库。 * * @returns {Promise<boolean>} - 操作是否成功。 */ clear() { try { this.storage.clear(); } catch (error) { console.error("Error clearing storage:", error); throw new Error("Failed to fetch clear from storage"); } } /** * 异步获取数据仓库中所有的key。 * * @returns {Promise<string[]>} - 一个 Promise 对象,表示获取所有键的结果。 */ Keys() { try { this.storage.keys(); } catch (error) { console.error("Error fetching keys from storage:", error); throw new Error("Failed to fetch keys from storage"); } } } const createStorage = (options) => { return new Storage(options); }; function cookieAdapter() { return new CookieStorageAdapter(); } function webLocalAdapter(localStorageType) { const localStorage = localStorageType || window.localStorage; return new WebStorageAdapter(localStorage); } function webSessionAdapter(localStorageType) { const localStorage = localStorageType || window.localStorage; return new WebStorageAdapter(localStorage); } function uniAppAdapter() { return new UniAppStorageAdapter(); } function indexedDBAdapter({ cacheName = "INDEXEDDB_APP" }) { return new IndexedDBStorage({ cacheName }); } const cacheConfig = { enableStorageEncryption: true, DEFAULT_CACHE_TIME: 3e3, prefix: "" }; const cacheCipher = { key: "_11111000001111@", iv: "@11111000001111_" }; process.env.NODE_ENV !== "production"; var __defProp = Object.defineProperty; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; const { DEFAULT_CACHE_TIME, enableStorageEncryption, prefix } = cacheConfig; function createAuraStorage(options) { const defaultOptions = __spreadValues({ hasEncrypt: enableStorageEncryption, key: cacheCipher.key, iv: cacheCipher.iv, prefix, timeout: DEFAULT_CACHE_TIME }, options); return createStorage(defaultOptions); } export { cookieAdapter, createAuraStorage, indexedDBAdapter, uniAppAdapter, webLocalAdapter, webSessionAdapter };