init query login

This commit is contained in:
2025-03-21 21:34:26 +08:00
commit 2cbd309c2a
31 changed files with 1922 additions and 0 deletions

200
dist/query-login.d.ts vendored Normal file
View File

@@ -0,0 +1,200 @@
import { Query } from '@kevisual/query';
import { DataOpts, Result } from '@kevisual/query/query';
type CacheLoginUser = {
user?: any;
id?: string;
accessToken?: string;
refreshToken?: string;
};
type CacheLogin = {
loginUsers: CacheLoginUser[];
} & CacheLoginUser;
interface CacheStore<T = any> {
name: string;
cacheData: CacheLogin;
cache: T;
/**
* @update 获取缓存
*/
get(key: string): Promise<any>;
/**
* @update 设置缓存
*/
set(key: string, value: CacheLogin): Promise<CacheLogin>;
/**
* @update 删除缓存
*/
del(): Promise<void>;
/**
* 设置当前用户
*/
setLoginUser(user: CacheLoginUser): Promise<void>;
/**
* 获取当前用户
*/
getCurrentUser(): Promise<CacheLoginUser>;
/**
* 获取当前用户列表
*/
getCurrentUserList(): Promise<CacheLoginUser[]>;
/**
* 获取缓存的refreshToken
*/
getRefreshToken(): Promise<string>;
/**
* 获取缓存的accessToken
*/
getAccessToken(): Promise<string>;
/**
* 初始化
*/
init(): Promise<void>;
/**
* 清除当前用户
*/
clearCurrentUser(): Promise<void>;
/**
* 清除所有用户
*/
clearAll(): Promise<void>;
}
type QueryLoginOpts = {
query?: Query;
isBrowser?: boolean;
onLoad?: () => void;
storage?: Storage;
};
type QueryLoginData = {
username?: string;
password: string;
email?: string;
};
type QueryLoginResult = {
accessToken: string;
refreshToken: string;
};
declare class QueryLogin {
query: Query;
cache: CacheStore;
isBrowser: boolean;
load?: boolean;
storage: Storage;
onLoad?: () => void;
constructor(opts?: QueryLoginOpts);
setQuery(query: Query): void;
init(): Promise<void>;
post<T = any>(data: any, opts?: DataOpts): Promise<{
code: number;
data?: T;
message?: string;
success: boolean;
noMsg?: boolean;
showError: (fn?: () => void) => void;
}>;
/**
* 登录,
* @param data
* @returns
*/
login(data: QueryLoginData): Promise<{
code: number;
data?: QueryLoginResult;
message?: string;
success: boolean;
noMsg?: boolean;
showError: (fn?: () => void) => void;
}>;
/**
* 登陆成功,需要获取用户信息进行缓存
* @param param0
*/
beforeSetLoginUser({ accessToken, refreshToken, check401 }: {
accessToken?: string;
refreshToken?: string;
check401?: boolean;
}): Promise<void>;
queryRefreshToken(refreshToken?: string): Promise<{
code: number;
data?: any;
message?: string;
success: boolean;
noMsg?: boolean;
showError: (fn?: () => void) => void;
} | {
code: number;
message: string;
data: any;
}>;
/**
* 检查401错误并刷新token, 如果refreshToken存在则刷新token, 否则返回401
* @param response
* @param ctx
* @param refetch
* @returns
*/
afterCheck401ToRefreshToken(response: Result, ctx?: {
req?: any;
res?: any;
fetch?: any;
}, refetch?: boolean): Promise<any>;
/**
* 获取用户信息
* @param token
* @returns
*/
getMe(token?: string, check401?: boolean): Promise<{
code: number;
data?: any;
message?: string;
success: boolean;
noMsg?: boolean;
showError: (fn?: () => void) => void;
}>;
/**
* 请求更新,切换用户, 使用switchUser
* @param username
* @returns
*/
private postSwitchUser;
/**
* 切换用户
* @param username
* @returns
*/
switchUser(username: string): Promise<{
code: number;
data?: any;
message?: string;
success: boolean;
noMsg?: boolean;
showError: (fn?: () => void) => void;
} | {
code: number;
data: {
accessToken: string;
refreshToken: string;
};
success: boolean;
message: string;
}>;
logout(): Promise<{
code: number;
data?: Result;
message?: string;
success: boolean;
noMsg?: boolean;
showError: (fn?: () => void) => void;
}>;
hasUser(username: string): Promise<{
code: number;
data?: Result;
message?: string;
success: boolean;
noMsg?: boolean;
showError: (fn?: () => void) => void;
}>;
}
export { QueryLogin, type QueryLoginData, type QueryLoginResult };

318
dist/query-login.js vendored Normal file
View File

@@ -0,0 +1,318 @@
// src/query-login.ts
import { Query } from "@kevisual/query";
import { setBaseResponse } from "@kevisual/query/query";
// src/login-cache.ts
import { MyCache } from "@kevisual/cache";
var LoginCacheStore = class {
cache;
name;
cacheData;
constructor(name) {
this.cache = new MyCache(name);
this.cacheData = {
loginUsers: [],
user: void 0,
id: void 0,
accessToken: void 0,
refreshToken: void 0
};
this.name = name;
}
/**
* 设置缓存
* @param key
* @param value
* @returns
*/
async set(key, value) {
await this.cache.set(key, value);
return value;
}
/**
* 删除缓存
*/
async del() {
await this.cache.del();
}
get(key) {
return this.cache.get(key);
}
async init() {
this.cacheData = await this.get(this.name) || {
loginUsers: [],
user: null,
id: null,
accessToken: null,
refreshToken: null
};
}
/**
* 设置当前用户
* @param user
*/
async setLoginUser(user) {
const has = this.cacheData.loginUsers.find((u) => u.id === user.id);
if (has) {
this.cacheData.loginUsers = this.cacheData?.loginUsers?.filter((u) => u?.id && u.id !== user.id);
}
this.cacheData.loginUsers.push(user);
this.cacheData.user = user.user;
this.cacheData.id = user.id;
this.cacheData.accessToken = user.accessToken;
this.cacheData.refreshToken = user.refreshToken;
await this.set(this.name, this.cacheData);
}
getCurrentUser() {
const cacheData = this.cacheData;
return Promise.resolve(cacheData.user);
}
getCurrentUserList() {
return Promise.resolve(this.cacheData.loginUsers.filter((u) => u?.id));
}
getRefreshToken() {
const cacheData = this.cacheData;
return Promise.resolve(cacheData.refreshToken || "");
}
getAccessToken() {
const cacheData = this.cacheData;
return Promise.resolve(cacheData.accessToken || "");
}
async clearCurrentUser() {
const user = await this.getCurrentUser();
const has = this.cacheData.loginUsers.find((u) => u.id === user.id);
if (has) {
this.cacheData.loginUsers = this.cacheData?.loginUsers?.filter((u) => u?.id && u.id !== user.id);
}
this.cacheData.user = void 0;
this.cacheData.id = void 0;
this.cacheData.accessToken = void 0;
this.cacheData.refreshToken = void 0;
await this.set(this.name, this.cacheData);
}
async clearAll() {
this.cacheData.loginUsers = [];
this.cacheData.user = void 0;
this.cacheData.id = void 0;
this.cacheData.accessToken = void 0;
this.cacheData.refreshToken = void 0;
await this.set(this.name, this.cacheData);
}
};
// src/query-login.ts
var QueryLogin = class {
query;
cache;
isBrowser;
load;
storage;
onLoad;
constructor(opts) {
this.query = opts?.query || new Query();
this.cache = new LoginCacheStore("login");
this.isBrowser = opts?.isBrowser ?? true;
this.init();
this.onLoad = opts?.onLoad;
this.storage = opts?.storage || localStorage;
}
setQuery(query) {
this.query = query;
}
async init() {
await this.cache.init();
this.load = true;
this.onLoad?.();
}
async post(data, opts) {
return this.query.post({ path: "user", ...data }, opts);
}
/**
* 登录,
* @param data
* @returns
*/
async login(data) {
const res = await this.post({ key: "login", ...data });
if (res.code === 200) {
const { accessToken, refreshToken } = res?.data || {};
this.storage.setItem("token", accessToken || "");
await this.beforeSetLoginUser({ accessToken, refreshToken });
}
return res;
}
/**
* 登陆成功,需要获取用户信息进行缓存
* @param param0
*/
async beforeSetLoginUser({ accessToken, refreshToken, check401 }) {
if (accessToken && refreshToken) {
const resUser = await this.getMe(accessToken, check401);
if (resUser.code === 200) {
const user = resUser.data;
if (user) {
this.cache.setLoginUser({
user,
id: user.id,
accessToken,
refreshToken
});
} else {
console.error("\u767B\u5F55\u5931\u8D25");
}
}
}
}
async queryRefreshToken(refreshToken) {
const _refreshToken = refreshToken || this.cache.getRefreshToken();
let data = { refreshToken: _refreshToken };
if (!_refreshToken) {
await this.cache.clearCurrentUser();
return {
code: 401,
message: "\u8BF7\u5148\u767B\u5F55",
data: {}
};
}
return this.post(
{ key: "refreshToken", data },
{
afterResponse: async (response, ctx) => {
setBaseResponse(response);
return response;
}
}
);
}
/**
* 检查401错误并刷新token, 如果refreshToken存在则刷新token, 否则返回401
* @param response
* @param ctx
* @param refetch
* @returns
*/
async afterCheck401ToRefreshToken(response, ctx, refetch) {
const that = this;
if (response?.code === 401) {
const hasRefreshToken = await that.cache.getRefreshToken();
if (hasRefreshToken) {
const res = await that.queryRefreshToken(hasRefreshToken);
if (res.code === 200) {
const { accessToken, refreshToken } = res?.data || {};
that.storage.setItem("token", accessToken || "");
await that.beforeSetLoginUser({ accessToken, refreshToken, check401: false });
if (refetch && ctx && ctx.req && ctx.req.url && ctx.fetch) {
await new Promise((resolve) => setTimeout(resolve, 1500));
const url = ctx.req?.url;
const body = ctx.req?.body;
const headers = ctx.req?.headers;
const res2 = await ctx.fetch(url, {
method: "POST",
body,
headers: { ...headers, Authorization: `Bearer ${accessToken}` }
});
setBaseResponse(res2);
return res2;
}
} else {
that.storage.removeItem("token");
await that.cache.clearCurrentUser();
}
return res;
}
}
return response;
}
/**
* 获取用户信息
* @param token
* @returns
*/
async getMe(token, check401 = true) {
const _token = token || this.storage.getItem("token");
const that = this;
return that.post(
{ key: "me" },
{
beforeRequest: async (config) => {
if (config.headers) {
config.headers["Authorization"] = `Bearer ${_token}`;
}
return config;
},
afterResponse: async (response, ctx) => {
if (response?.code === 401 && check401) {
return await that.afterCheck401ToRefreshToken(response, ctx);
}
return response;
}
}
);
}
/**
* 请求更新,切换用户, 使用switchUser
* @param username
* @returns
*/
async postSwitchUser(username) {
return this.post({ key: "switchCheck", data: { username } });
}
/**
* 切换用户
* @param username
* @returns
*/
async switchUser(username) {
const localUserList = await this.cache.getCurrentUserList();
const user = localUserList.find((userItem) => userItem.user.username === username);
if (user) {
this.storage.setItem("token", user.accessToken || "");
await this.beforeSetLoginUser({ accessToken: user.accessToken, refreshToken: user.refreshToken });
return {
code: 200,
data: {
accessToken: user.accessToken,
refreshToken: user.refreshToken
},
success: true,
message: "\u5207\u6362\u7528\u6237\u6210\u529F"
};
}
const res = await this.postSwitchUser(username);
if (res.code === 200) {
const { accessToken, refreshToken } = res?.data || {};
this.storage.setItem("token", accessToken || "");
await this.beforeSetLoginUser({ accessToken, refreshToken });
}
return res;
}
async logout() {
this.storage.removeItem("token");
this.cache.del();
return this.post({ key: "logout" });
}
async hasUser(username) {
const that = this;
return this.post(
{
path: "org",
key: "hasUser",
data: {
username
}
},
{
afterResponse: async (response, ctx) => {
if (response?.code === 401) {
const res = await that.afterCheck401ToRefreshToken(response, ctx, true);
return res;
}
return response;
}
}
);
}
};
export {
QueryLogin
};