14 changed files with 295 additions and 214 deletions
@ -1,22 +1,32 @@ |
|||||
import { getStorageShortName } from '/@/utils/env'; |
import { getStorageShortName } from '/@/utils/env'; |
||||
import { createStorage as create } from './storageCache'; |
import { createStorage as create, CreateStorageParams } from './storageCache'; |
||||
import { enableStorageEncryption } from '/@/settings/encryptionSetting'; |
import { enableStorageEncryption } from '/@/settings/encryptionSetting'; |
||||
import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting'; |
import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting'; |
||||
|
|
||||
const createOptions = (storage = sessionStorage) => { |
export type Options = Partial<CreateStorageParams>; |
||||
|
|
||||
|
const createOptions = (storage: Storage, options: Options = {}): Options => { |
||||
return { |
return { |
||||
// No encryption in debug mode
|
// No encryption in debug mode
|
||||
hasEncrypt: enableStorageEncryption, |
hasEncrypt: enableStorageEncryption, |
||||
storage, |
storage, |
||||
prefixKey: getStorageShortName(), |
prefixKey: getStorageShortName(), |
||||
timeout: DEFAULT_CACHE_TIME, |
|
||||
|
...options, |
||||
}; |
}; |
||||
}; |
}; |
||||
|
|
||||
export const WebStorage = create(createOptions()); |
export const WebStorage = create(createOptions(sessionStorage)); |
||||
|
|
||||
|
export const createStorage = (storage: Storage = sessionStorage, options: Options = {}) => { |
||||
|
return create(createOptions(storage, options)); |
||||
|
}; |
||||
|
|
||||
export const createStorage = (storage: Storage = sessionStorage) => { |
export const createPersistentStorage = ( |
||||
return create(createOptions(storage))!; |
storage: Storage = sessionStorage, |
||||
|
options: Options = {} |
||||
|
) => { |
||||
|
return createStorage(storage, { ...options, timeout: DEFAULT_CACHE_TIME }); |
||||
}; |
}; |
||||
|
|
||||
export default WebStorage; |
export default WebStorage; |
||||
|
|||||
@ -0,0 +1,98 @@ |
|||||
|
export interface Cache<V = any> { |
||||
|
value?: V; |
||||
|
timeoutId?: ReturnType<typeof setTimeout>; |
||||
|
time?: number; |
||||
|
alive?: number; |
||||
|
} |
||||
|
|
||||
|
const NOT_ALIVE = 0; |
||||
|
|
||||
|
export class Memory<T = any, V = any> { |
||||
|
private cache: { [key in keyof T]?: Cache<V> } = {}; |
||||
|
private alive: number; |
||||
|
|
||||
|
constructor(alive = NOT_ALIVE) { |
||||
|
// Unit second
|
||||
|
this.alive = alive * 1000; |
||||
|
} |
||||
|
|
||||
|
get getCache() { |
||||
|
return this.cache; |
||||
|
} |
||||
|
|
||||
|
setCache(cache) { |
||||
|
this.cache = cache; |
||||
|
} |
||||
|
|
||||
|
// get<K extends keyof T>(key: K) {
|
||||
|
// const item = this.getItem(key);
|
||||
|
// const time = item?.time;
|
||||
|
// if (!isNullOrUnDef(time) && time < new Date().getTime()) {
|
||||
|
// this.remove(key);
|
||||
|
// }
|
||||
|
// return item?.value ?? undefined;
|
||||
|
// }
|
||||
|
|
||||
|
get<K extends keyof T>(key: K) { |
||||
|
return this.cache[key]; |
||||
|
} |
||||
|
|
||||
|
set<K extends keyof T>(key: K, value: V, expires?: number) { |
||||
|
let item = this.get(key); |
||||
|
|
||||
|
if (!expires || (expires as number) <= 0) { |
||||
|
expires = this.alive; |
||||
|
} |
||||
|
if (item) { |
||||
|
if (item.timeoutId) { |
||||
|
clearTimeout(item.timeoutId); |
||||
|
item.timeoutId = undefined; |
||||
|
} |
||||
|
item.value = value; |
||||
|
} else { |
||||
|
item = { value, alive: expires }; |
||||
|
this.cache[key] = item; |
||||
|
} |
||||
|
|
||||
|
if (!expires) { |
||||
|
return value; |
||||
|
} |
||||
|
item.time = new Date().getTime() + this.alive * 1000; |
||||
|
item.timeoutId = setTimeout(() => { |
||||
|
this.remove(key); |
||||
|
}, expires); |
||||
|
|
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
remove<K extends keyof T>(key: K) { |
||||
|
const item = this.get(key); |
||||
|
Reflect.deleteProperty(this.cache, key); |
||||
|
if (item) { |
||||
|
clearTimeout(item.timeoutId!); |
||||
|
return item.value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
resetCache(cache: { [K in keyof T]: Cache }) { |
||||
|
Object.keys(cache).forEach((key) => { |
||||
|
const k = (key as any) as keyof T; |
||||
|
const item = cache[k]; |
||||
|
if (item && item.time) { |
||||
|
const now = new Date().getTime(); |
||||
|
const expire = now + item.time * 1000; |
||||
|
if (expire > now) { |
||||
|
this.set(k, item.value, expire); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
clear() { |
||||
|
Object.keys(this.cache).forEach((key) => { |
||||
|
const item = this.cache[key]; |
||||
|
item.timeoutId && clearTimeout(item.timeoutId); |
||||
|
}); |
||||
|
this.cache = {}; |
||||
|
} |
||||
|
} |
||||
@ -1,130 +1,109 @@ |
|||||
import { createStorage } from '/@/utils/cache'; |
import { createPersistentStorage } from '/@/utils/cache'; |
||||
|
import { Memory } from './memory'; |
||||
import { BASE_LOCAL_CACHE_KEY, BASE_SESSION_CACHE_KEY } from '/@/enums/cacheEnum'; |
import { |
||||
|
TOKEN_KEY, |
||||
const ls = createStorage(localStorage); |
USER_INFO_KEY, |
||||
const ss = createStorage(); |
ROLES_KEY, |
||||
|
LOCK_INFO_KEY, |
||||
interface CacheStore { |
PROJ_CFG_KEY, |
||||
local: Recordable; |
APP_LOCAL_CACHE_KEY, |
||||
session: Recordable; |
APP_SESSION_CACHE_KEY, |
||||
|
} from '/@/enums/cacheEnum'; |
||||
|
import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting'; |
||||
|
import { toRaw } from 'vue'; |
||||
|
|
||||
|
interface BasicStore { |
||||
|
[TOKEN_KEY]: string | number | null | undefined; |
||||
|
[USER_INFO_KEY]: Recordable; |
||||
|
[ROLES_KEY]: Recordable; |
||||
|
[LOCK_INFO_KEY]: Recordable; |
||||
|
[PROJ_CFG_KEY]: Recordable; |
||||
} |
} |
||||
|
|
||||
/** |
type LocalStore = BasicStore; |
||||
* @description: Persistent cache |
|
||||
*/ |
|
||||
const cacheStore: CacheStore = { |
|
||||
// localstorage cache
|
|
||||
local: {}, |
|
||||
// sessionstorage cache
|
|
||||
session: {}, |
|
||||
}; |
|
||||
|
|
||||
function initCache() { |
|
||||
cacheStore.local = ls.get(BASE_LOCAL_CACHE_KEY) || {}; |
|
||||
cacheStore.session = ss.get(BASE_SESSION_CACHE_KEY) || {}; |
|
||||
} |
|
||||
|
|
||||
initCache(); |
type SessionStore = BasicStore; |
||||
|
|
||||
export function setLocal(key: string, value: any, immediate = false) { |
export type BasicKeys = keyof BasicStore; |
||||
const local = ls.get(BASE_LOCAL_CACHE_KEY)?.[BASE_LOCAL_CACHE_KEY] || {}; |
type LocalKeys = keyof LocalStore; |
||||
|
type SessionKeys = keyof SessionStore; |
||||
|
|
||||
cacheStore.local[BASE_LOCAL_CACHE_KEY] = |
const ls = createPersistentStorage(localStorage); |
||||
{ ...local, ...cacheStore.local[BASE_LOCAL_CACHE_KEY] } || {}; |
const ss = createPersistentStorage(sessionStorage); |
||||
cacheStore.local[BASE_LOCAL_CACHE_KEY][key] = value; |
|
||||
|
|
||||
if (immediate) { |
const localMemory = new Memory(DEFAULT_CACHE_TIME); |
||||
ls.set(BASE_LOCAL_CACHE_KEY, cacheStore.local); |
const sessionMemory = new Memory(DEFAULT_CACHE_TIME); |
||||
} |
|
||||
} |
|
||||
|
|
||||
export function getLocal<T>(key: string): T | null { |
function initMemory() { |
||||
try { |
const localCache = ls.get(APP_LOCAL_CACHE_KEY); |
||||
return cacheStore.local[BASE_LOCAL_CACHE_KEY][key]; |
const sessionCache = ls.get(APP_SESSION_CACHE_KEY); |
||||
} catch (error) { |
localCache && localMemory.resetCache(localCache); |
||||
return null; |
sessionCache && sessionMemory.resetCache(sessionCache); |
||||
} |
|
||||
} |
} |
||||
|
initMemory(); |
||||
export function removeLocal(key: string) { |
export class Persistent { |
||||
if (cacheStore.local[BASE_LOCAL_CACHE_KEY]) { |
static getLocal<T>(key: LocalKeys) { |
||||
Reflect.deleteProperty(cacheStore.local[BASE_LOCAL_CACHE_KEY], key); |
return localMemory.get(key)?.value as Nullable<T>; |
||||
} |
} |
||||
} |
|
||||
|
|
||||
export function clearLocal(immediate = false) { |
|
||||
cacheStore.local = {}; |
|
||||
immediate && ls.remove(BASE_LOCAL_CACHE_KEY); |
|
||||
} |
|
||||
|
|
||||
export function setSession(key: string, value: any, immediate = false) { |
|
||||
const session = ss.get(BASE_SESSION_CACHE_KEY)?.[BASE_SESSION_CACHE_KEY] || {}; |
|
||||
|
|
||||
cacheStore.session[BASE_SESSION_CACHE_KEY] = |
static setLocal(key: LocalKeys, value: LocalStore[LocalKeys], immediate = false): void { |
||||
{ ...session, ...cacheStore.session[BASE_SESSION_CACHE_KEY] } || {}; |
localMemory.set(key, toRaw(value)); |
||||
|
immediate && ls.set(APP_LOCAL_CACHE_KEY, localMemory.getCache); |
||||
|
} |
||||
|
|
||||
cacheStore.session[BASE_SESSION_CACHE_KEY][key] = value; |
static removeLocal(key: LocalKeys): void { |
||||
|
localMemory.remove(key); |
||||
|
} |
||||
|
|
||||
if (immediate) { |
static clearLocal(): void { |
||||
ss.set(BASE_SESSION_CACHE_KEY, cacheStore.session); |
localMemory.clear(); |
||||
} |
} |
||||
} |
|
||||
|
|
||||
export function removeSession(key: string) { |
static getSession<T>(key: SessionKeys) { |
||||
if (cacheStore.session[BASE_SESSION_CACHE_KEY]) { |
return sessionMemory.get(key)?.value as Nullable<T>; |
||||
Reflect.deleteProperty(cacheStore.session[BASE_SESSION_CACHE_KEY], key); |
|
||||
} |
} |
||||
} |
|
||||
|
|
||||
export function getSession<T>(key: string): T | null { |
static setSession(key: SessionKeys, value: SessionStore[SessionKeys], immediate = false): void { |
||||
try { |
sessionMemory.set(key, toRaw(value)); |
||||
return cacheStore.session[BASE_SESSION_CACHE_KEY][key]; |
immediate && ss.set(APP_SESSION_CACHE_KEY, localMemory); |
||||
} catch (error) { |
|
||||
return null; |
|
||||
} |
} |
||||
} |
|
||||
|
|
||||
export function clearSession(immediate = false) { |
static removeSession(key: SessionKeys): void { |
||||
cacheStore.session = {}; |
sessionMemory.remove(key); |
||||
immediate && ss.remove(BASE_SESSION_CACHE_KEY); |
} |
||||
} |
static clearSession(): void { |
||||
|
sessionMemory.clear(); |
||||
|
} |
||||
|
|
||||
export function clearAll() { |
static clearAll() { |
||||
clearLocal(); |
sessionMemory.clear(); |
||||
clearSession(); |
localMemory.clear(); |
||||
|
} |
||||
} |
} |
||||
|
|
||||
export function persistentCache() { |
window.addEventListener('beforeunload', function () { |
||||
const localCache = cacheStore.local; |
ls.set(APP_LOCAL_CACHE_KEY, localMemory.getCache); |
||||
const sessionCache = cacheStore.session; |
ss.set(APP_SESSION_CACHE_KEY, sessionMemory.getCache); |
||||
ls.set(BASE_LOCAL_CACHE_KEY, localCache); |
}); |
||||
ss.set(BASE_SESSION_CACHE_KEY, sessionCache); |
|
||||
} |
|
||||
|
|
||||
(() => { |
function storageChange(e: any) { |
||||
// /** Write to local before closing window */
|
const { key, newValue, oldValue } = e; |
||||
window.addEventListener('beforeunload', () => { |
|
||||
persistentCache(); |
|
||||
}); |
|
||||
|
|
||||
function storageChange(e: any) { |
if (!key) { |
||||
const { key, newValue, oldValue } = e; |
Persistent.clearAll(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
if (!key) { |
if (!!newValue && !!oldValue) { |
||||
clearAll(); |
if (APP_LOCAL_CACHE_KEY === key) { |
||||
return; |
Persistent.clearLocal(); |
||||
} |
} |
||||
|
if (APP_SESSION_CACHE_KEY === key) { |
||||
if (!!newValue && !!oldValue) { |
Persistent.clearSession(); |
||||
if (BASE_LOCAL_CACHE_KEY === key) { |
|
||||
clearLocal(); |
|
||||
} |
|
||||
if (BASE_SESSION_CACHE_KEY === key) { |
|
||||
clearSession(); |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
|
|
||||
|
window.addEventListener('storage', storageChange); |
||||
|
|
||||
window.addEventListener('storage', storageChange); |
export default {}; |
||||
})(); |
|
||||
|
|||||
Loading…
Reference in new issue