aura-storage
Version:
703 lines (687 loc) • 18.8 kB
JavaScript
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 };