persistentStorage.js 3.38 KB
const { encrypt, decrypt } = CryptoJS.AES
const { parse } = CryptoJS.enc.Utf8;
const pkcs7 = CryptoJS.pad.Pkcs7;
const ECB = CryptoJS.mode.ECB;
const UTF8 = CryptoJS.enc.Utf8;

const isDEV = location.href.includes('dev=1')

const cacheCipher = {
	key: '_11111000001111@',
	iv: '@11111000001111_',
}

const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7;

const isNullOrUnDef = (val) => typeof val === 'undefined' || val === null

class AesEncryption {
	key;
	iv;

	constructor(opt = {}) {
		const { key, iv } = opt;
		if (key) {
			this.key = parse(key);
		}
		if (iv) {
			this.iv = parse(iv);
		}
	}

	get getOptions() {
		return {
			mode: ECB,
			padding: pkcs7,
			iv: this.iv,
		};
	}

	encryptByAES(cipherText) {
		return encrypt(cipherText, this.key, this.getOptions).toString();
	}

	decryptByAES(cipherText) {
		return decrypt(cipherText, this.key, this.getOptions).toString(UTF8);
	}
}


const createStorage = (
	{
		prefixKey = '',
		storage = sessionStorage,
		key = cacheCipher.key,
		iv = cacheCipher.iv,
		timeout = null,
		hasEncrypt = !isDEV,
	} = {}) => {
	if (hasEncrypt && [key.length, iv.length].some((item) => item !== 16)) {
		throw new Error('When hasEncrypt is true, the key or iv must be 16 bits!');
	}

	const encryption = new AesEncryption({ key, iv });

	/**
	 *Cache class
	 *Construction parameters can be passed into sessionStorage, localStorage,
	 * @class Cache
	 * @example
	 */
	const WebStorage = class WebStorage {
		storage
		prefixKey
		encryption
		hasEncrypt

		/**
		 *
		 * @param {*} storage
		 */
		constructor() {
			this.storage = storage;
			this.prefixKey = prefixKey;
			this.encryption = encryption;
			this.hasEncrypt = hasEncrypt;
		}

		getKey(key) {
			return `${this.prefixKey}${key}`.toUpperCase();
		}

		/**
		 *
		 *  Set cache
		 * @param {string} key
		 * @param {*} value
		 * @param {*} expire
		 * @expire Expiration time in seconds
		 * @memberof Cache
		 */
		set(key, value, expire = timeout) {
			const stringData = JSON.stringify({
				value,
				time: Date.now(),
				expire: !isNullOrUnDef(expire) ? new Date().getTime() + expire * 1000 : null,
			});
			const stringifyValue = this.hasEncrypt
				? this.encryption.encryptByAES(stringData)
				: stringData;
			this.storage.setItem(this.getKey(key), stringifyValue);
		}

		generatorValue(key, value, expire = timeout) {
			const stringData = {
				value,
				time: Date.now(),
				expire: !isNullOrUnDef(expire) ? new Date().getTime() + expire * 1000 : null,
			};
			const stringifyValue = this.hasEncrypt
				? this.encryption.encryptByAES(stringData)
				: stringData;

			return stringifyValue
		}

		/**
		 *Read cache
		 * @param {string} key
		 * @param {any} [def=null] def
		 * @memberof Cache
		 */
		get(key, def = null) {
			const val = this.storage.getItem(this.getKey(key));
			if (!val) return def;

			try {
				const decVal = this.hasEncrypt ? this.encryption.decryptByAES(val) : val;
				const data = JSON.parse(decVal);
				const { value, expire } = data;
				// if (isNullOrUnDef(expire) || expire >= new Date().getTime()) {
				// return value;
				// }
				return value
				// this.remove(key);
			} catch (e) {
				return def;
			}
		}

		/**
		 * Delete cache based on key
		 * @param {string} key
		 * @memberof Cache
		 */
		remove(key) {
			this.storage.removeItem(this.getKey(key));
		}

		/**
		 * Delete all caches of this instance
		 */
		clear() {
			this.storage.clear();
		}
	};
	return new WebStorage();
}