feat: 更新版本至 0.0.61,重构登录缓存逻辑,添加浏览器缓存支持
This commit is contained in:
123
query/query-login/browser-cache/cache-store.ts
Normal file
123
query/query-login/browser-cache/cache-store.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { createStore, UseStore, get, set, del, clear, keys, values, entries, update, setMany, getMany, delMany } from 'idb-keyval';
|
||||
|
||||
/**
|
||||
* 缓存存储选项
|
||||
*/
|
||||
export type CacheStoreOpts = {
|
||||
/**
|
||||
* 数据库名称
|
||||
*/
|
||||
dbName?: string;
|
||||
/**
|
||||
* 存储空间名称
|
||||
*/
|
||||
storeName?: string;
|
||||
};
|
||||
export class BaseCacheStore {
|
||||
store: UseStore;
|
||||
constructor(opts?: CacheStoreOpts) {
|
||||
this.store = createStore(opts?.dbName || 'default-db', opts?.storeName || 'cache-store');
|
||||
}
|
||||
async get(key: string) {
|
||||
return get(key, this.store);
|
||||
}
|
||||
async set(key: string, value: any) {
|
||||
return set(key, value, this.store);
|
||||
}
|
||||
async del(key: string) {
|
||||
return del(key, this.store);
|
||||
}
|
||||
async clear() {
|
||||
return clear(this.store);
|
||||
}
|
||||
async keys() {
|
||||
return keys(this.store);
|
||||
}
|
||||
async values() {
|
||||
return values(this.store);
|
||||
}
|
||||
async entries() {
|
||||
return entries(this.store);
|
||||
}
|
||||
async update(key: string, updater: (value: any) => any) {
|
||||
return update(key, updater, this.store);
|
||||
}
|
||||
async setMany(entries: [string, any][]) {
|
||||
return setMany(entries, this.store);
|
||||
}
|
||||
async getMany(keys: string[]) {
|
||||
return getMany(keys, this.store);
|
||||
}
|
||||
async delMany(keys: string[]) {
|
||||
return delMany(keys, this.store);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存存储
|
||||
*/
|
||||
export class CacheStore extends BaseCacheStore {
|
||||
constructor(opts?: CacheStoreOpts) {
|
||||
super(opts);
|
||||
}
|
||||
async getData<T = any>(key: string) {
|
||||
const data = await this.get(key);
|
||||
return data.data as T;
|
||||
}
|
||||
async setData(key: string, data: any) {
|
||||
return this.set(key, data);
|
||||
}
|
||||
/**
|
||||
* 获取缓存数据,并检查是否过期
|
||||
* @param key 缓存键
|
||||
* @returns 缓存数据
|
||||
*/
|
||||
async getCheckData<T = any>(key: string) {
|
||||
const data = await this.get(key);
|
||||
if (data.expireTime && data.expireTime < Date.now()) {
|
||||
await super.del(key);
|
||||
return null;
|
||||
}
|
||||
return data.data as T;
|
||||
}
|
||||
/**
|
||||
* 设置缓存数据,并检查是否过期
|
||||
* @param key 缓存键
|
||||
* @param data 缓存数据
|
||||
* @param opts 缓存选项
|
||||
* @returns 缓存数据
|
||||
*/
|
||||
async setCheckData(key: string, data: any, opts?: { expireTime?: number; updatedAt?: number }) {
|
||||
const now = Date.now();
|
||||
const expireTime = now + (opts?.expireTime || 1000 * 60 * 60 * 24 * 10);
|
||||
const newData = {
|
||||
data,
|
||||
updatedAt: opts?.updatedAt || Date.now(),
|
||||
expireTime,
|
||||
};
|
||||
await this.set(key, newData);
|
||||
return data;
|
||||
}
|
||||
async checkNew(key: string, data: any): Promise<boolean> {
|
||||
const existing = await this.get(key);
|
||||
if (!existing) {
|
||||
return true;
|
||||
}
|
||||
if (!data?.updatedAt) {
|
||||
return false;
|
||||
}
|
||||
const updatedAt = new Date(data.updatedAt).getTime();
|
||||
if (isNaN(updatedAt)) {
|
||||
return false;
|
||||
}
|
||||
return updatedAt > existing.updatedAt;
|
||||
}
|
||||
/**
|
||||
* 删除缓存数据
|
||||
* @param key 缓存键
|
||||
* @returns 缓存数据
|
||||
*/
|
||||
async delCheckData(key: string) {
|
||||
return this.del(key);
|
||||
}
|
||||
}
|
||||
29
query/query-login/browser-cache/cache.ts
Normal file
29
query/query-login/browser-cache/cache.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
export { CacheStore, BaseCacheStore } from './cache-store.ts'
|
||||
import { CacheStore } from './cache-store.ts'
|
||||
|
||||
/**
|
||||
* 一个简单的缓存类,用于存储字符串。
|
||||
* 对数据进行添加对比内容。
|
||||
*/
|
||||
export class MyCache<T = any> extends CacheStore {
|
||||
key: string;
|
||||
constructor(opts?: { key?: string }) {
|
||||
const { key, ...rest } = opts || {};
|
||||
super(rest);
|
||||
this.key = key || 'my-cache';
|
||||
}
|
||||
async getData<U = T>(key: string = this.key): Promise<U> {
|
||||
return super.getCheckData<U>(key) as any;
|
||||
}
|
||||
/**
|
||||
* 设置缓存数据,默认过期时间为10天
|
||||
* @param data
|
||||
* @param opts
|
||||
*/
|
||||
async setData<U = T>(data: U, opts?: { expireTime?: number, updatedAt?: number }) {
|
||||
super.setCheckData(this.key, data, opts);
|
||||
}
|
||||
async del(): Promise<void> {
|
||||
await super.del(this.key);
|
||||
}
|
||||
}
|
||||
@@ -146,19 +146,21 @@ export class LoginCacheStore<T extends Cache = Cache> implements CacheStore<T> {
|
||||
/**
|
||||
* 初始化,设置默认值
|
||||
*/
|
||||
async init() {
|
||||
async init(): Promise<CacheLogin> {
|
||||
const defaultData: CacheLogin = { ...this.cacheData };
|
||||
if (this.cache.init) {
|
||||
try {
|
||||
const cacheData = await this.cache.init();
|
||||
this.cacheData = cacheData || defaultData;
|
||||
} catch (error) {
|
||||
console.log('cacheInit error', error);
|
||||
return new Promise(async (resolve) => {
|
||||
if (this.cache.init) {
|
||||
try {
|
||||
const cacheData = await this.cache.init();
|
||||
this.cacheData = cacheData || defaultData;
|
||||
} catch (error) {
|
||||
console.log('cacheInit error', error);
|
||||
}
|
||||
} else {
|
||||
this.cacheData = (await this.getValue()) || defaultData;
|
||||
}
|
||||
} else {
|
||||
this.cacheData = (await this.getValue()) || defaultData;
|
||||
}
|
||||
return this.cacheData;
|
||||
resolve(this.cacheData);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 设置当前用户
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { QueryLogin, QueryLoginOpts } from './query-login.ts';
|
||||
import { MyCache } from '@kevisual/cache';
|
||||
import { MyCache } from './browser-cache/cache.ts';
|
||||
type QueryLoginNodeOptsWithoutCache = Omit<QueryLoginOpts, 'cache'>;
|
||||
|
||||
export class QueryLoginBrowser extends QueryLogin {
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { Result, DataOpts } from '@kevisual/query/query';
|
||||
import { LoginCacheStore, CacheStore, User } from './login-cache.ts';
|
||||
import { Cache } from './login-cache.ts';
|
||||
import { BaseLoad } from '@kevisual/load';
|
||||
import { EventEmitter } from 'eventemitter3'
|
||||
export type QueryLoginOpts<T extends Cache = Cache> = {
|
||||
query?: Query;
|
||||
isBrowser?: boolean;
|
||||
@@ -26,9 +27,11 @@ export class QueryLogin<T extends Cache = Cache> extends BaseQuery {
|
||||
*/
|
||||
cacheStore: CacheStore<T>;
|
||||
isBrowser: boolean;
|
||||
load?: boolean;
|
||||
storage: Storage;
|
||||
load: boolean = false;
|
||||
status: 'init' | 'logining' | 'loginSuccess' | 'loginError' = 'init';
|
||||
onLoad?: () => void;
|
||||
emitter = new EventEmitter();
|
||||
|
||||
constructor(opts?: QueryLoginOpts<T>) {
|
||||
super({
|
||||
@@ -42,14 +45,29 @@ export class QueryLogin<T extends Cache = Cache> extends BaseQuery {
|
||||
if (!this.storage) {
|
||||
throw new Error('storage is required');
|
||||
}
|
||||
this.cacheStore.init().then(() => {
|
||||
this.onLoad?.();
|
||||
this.load = true;
|
||||
this.emitter.emit('load');
|
||||
});
|
||||
}
|
||||
setQuery(query: Query) {
|
||||
this.query = query;
|
||||
}
|
||||
private async init() {
|
||||
await this.cacheStore.init();
|
||||
this.load = true;
|
||||
this.onLoad?.();
|
||||
async init() {
|
||||
if (this.load) {
|
||||
return this.cacheStore.cacheData;
|
||||
}
|
||||
return new Promise(async (resolve) => {
|
||||
const timer = setTimeout(() => {
|
||||
resolve(this.cacheStore.cacheData);
|
||||
}, 1000 * 20); // 20秒超时,避免一直等待
|
||||
const listener = () => {
|
||||
clearTimeout(timer);
|
||||
resolve(this.cacheStore.cacheData);
|
||||
}
|
||||
this.emitter.once('load', listener);
|
||||
});
|
||||
}
|
||||
async post<T = any>(data: any, opts?: DataOpts) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user