feat: add login by note modules
This commit is contained in:
parent
f88fd1037e
commit
af8ed90ab3
2
.env.example
Normal file
2
.env.example
Normal file
@ -0,0 +1,2 @@
|
||||
password=888888
|
||||
envision_registry=https://kevisual.silkyai.cn/api/router
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
dist
|
||||
node_modules
|
||||
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
.env
|
10
package.json
10
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kevisual/query-login",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"description": "",
|
||||
"main": "dist/query-login.js",
|
||||
"types": "dist/query-login.d.ts",
|
||||
@ -23,9 +23,13 @@
|
||||
"tsup": "^8.4.0"
|
||||
},
|
||||
"exports": {
|
||||
".": "./dist/query-login.js"
|
||||
".": "./dist/query-login-browser.js",
|
||||
"./base": "./dist/query-login.js",
|
||||
"./node": "./dist/query-login-node.js",
|
||||
"./browser": "./dist/query-login-browser.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@kevisual/cache": "^0.0.1"
|
||||
"@kevisual/cache": "^0.0.1",
|
||||
"dotenv": "^16.4.7"
|
||||
}
|
||||
}
|
@ -1,4 +1,17 @@
|
||||
import { MyCache } from '@kevisual/cache';
|
||||
export interface Cache {
|
||||
/**
|
||||
* @update 获取缓存
|
||||
*/
|
||||
get(key: string): Promise<any>;
|
||||
/**
|
||||
* @update 设置缓存
|
||||
*/
|
||||
set(key: string, value: any): Promise<any>;
|
||||
/**
|
||||
* @update 删除缓存
|
||||
*/
|
||||
del(): Promise<void>;
|
||||
}
|
||||
|
||||
export type CacheLoginUser = {
|
||||
user?: any;
|
||||
@ -10,22 +23,14 @@ type CacheLogin = {
|
||||
loginUsers: CacheLoginUser[];
|
||||
} & CacheLoginUser;
|
||||
|
||||
export interface CacheStore<T = any> {
|
||||
export type CacheStore<T = any> = {
|
||||
name: string;
|
||||
cacheData: CacheLogin;
|
||||
/**
|
||||
* 实际操作的cache
|
||||
*/
|
||||
cache: T;
|
||||
/**
|
||||
* @update 获取缓存
|
||||
*/
|
||||
get(key: string): Promise<any>;
|
||||
/**
|
||||
* @update 设置缓存
|
||||
*/
|
||||
set(key: string, value: CacheLogin): Promise<CacheLogin>;
|
||||
/**
|
||||
* @update 删除缓存
|
||||
*/
|
||||
del(): Promise<void>;
|
||||
|
||||
/**
|
||||
* 设置当前用户
|
||||
*/
|
||||
@ -58,13 +63,22 @@ export interface CacheStore<T = any> {
|
||||
* 清除所有用户
|
||||
*/
|
||||
clearAll(): Promise<void>;
|
||||
}
|
||||
export class LoginCacheStore implements CacheStore<MyCache<any>> {
|
||||
cache: MyCache<any>;
|
||||
} & Cache;
|
||||
|
||||
type LoginCacheStoreOpts = {
|
||||
name: string;
|
||||
cache: Cache;
|
||||
};
|
||||
export class LoginCacheStore implements CacheStore<any> {
|
||||
cache: Cache;
|
||||
name: string;
|
||||
cacheData: CacheLogin;
|
||||
constructor(name: string) {
|
||||
this.cache = new MyCache(name);
|
||||
constructor(opts: LoginCacheStoreOpts) {
|
||||
if (!opts.cache) {
|
||||
throw new Error('cache is required');
|
||||
}
|
||||
// @ts-ignore
|
||||
this.cache = opts.cache;
|
||||
this.cacheData = {
|
||||
loginUsers: [],
|
||||
user: undefined,
|
||||
@ -72,7 +86,7 @@ export class LoginCacheStore implements CacheStore<MyCache<any>> {
|
||||
accessToken: undefined,
|
||||
refreshToken: undefined,
|
||||
};
|
||||
this.name = name;
|
||||
this.name = opts.name;
|
||||
}
|
||||
/**
|
||||
* 设置缓存
|
||||
|
112
src/login-node-cache.ts
Normal file
112
src/login-node-cache.ts
Normal file
@ -0,0 +1,112 @@
|
||||
import { Cache } from './login-cache.ts';
|
||||
import { homedir } from 'node:os';
|
||||
import { join, dirname } from 'node:path';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { readFile, writeFile, unlink, stat, mkdir } from 'node:fs/promises';
|
||||
export const fileExists = async (filePath: string, createIfNotExists = false) => {
|
||||
try {
|
||||
await stat(filePath);
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (createIfNotExists) {
|
||||
await mkdir(dirname(filePath), { recursive: true });
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
export const readConfigFile = (filePath: string) => {
|
||||
try {
|
||||
const data = readFileSync(filePath, 'utf-8');
|
||||
const jsonData = JSON.parse(data);
|
||||
return jsonData;
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
export const getHostName = () => {
|
||||
const configDir = join(homedir(), '.config', 'envision');
|
||||
const configFile = join(configDir, 'config.json');
|
||||
const config = readConfigFile(configFile);
|
||||
const baseURL = config.baseURL || 'https://kevisual.cn';
|
||||
const hostname = new URL(baseURL).hostname;
|
||||
return hostname;
|
||||
};
|
||||
export class StorageNode implements Storage {
|
||||
cacheData: any;
|
||||
filePath: string;
|
||||
constructor() {
|
||||
this.cacheData = {};
|
||||
const configDir = join(homedir(), '.config', 'envision');
|
||||
const hostname = getHostName();
|
||||
this.filePath = join(configDir, 'config', `${hostname}-storage.json`);
|
||||
fileExists(this.filePath, true);
|
||||
}
|
||||
async loadCache() {
|
||||
const filePath = this.filePath;
|
||||
try {
|
||||
const data = await readFile(filePath, 'utf-8');
|
||||
const jsonData = JSON.parse(data);
|
||||
this.cacheData = jsonData;
|
||||
} catch (error) {
|
||||
this.cacheData = {};
|
||||
await writeFile(filePath, JSON.stringify(this.cacheData, null, 2));
|
||||
}
|
||||
}
|
||||
get length() {
|
||||
return Object.keys(this.cacheData).length;
|
||||
}
|
||||
getItem(key: string) {
|
||||
return this.cacheData[key];
|
||||
}
|
||||
setItem(key: string, value: any) {
|
||||
this.cacheData[key] = value;
|
||||
writeFile(this.filePath, JSON.stringify(this.cacheData, null, 2));
|
||||
}
|
||||
removeItem(key: string) {
|
||||
delete this.cacheData[key];
|
||||
writeFile(this.filePath, JSON.stringify(this.cacheData, null, 2));
|
||||
}
|
||||
clear() {
|
||||
this.cacheData = {};
|
||||
writeFile(this.filePath, JSON.stringify(this.cacheData, null, 2));
|
||||
}
|
||||
key(index: number) {
|
||||
return Object.keys(this.cacheData)[index];
|
||||
}
|
||||
}
|
||||
export class LoginNodeCache implements Cache {
|
||||
name: string;
|
||||
cacheData: any;
|
||||
filePath: string;
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
const hostname = getHostName();
|
||||
this.filePath = join(homedir(), '.config', 'envision', 'config', `${hostname}-${name}.json`);
|
||||
fileExists(this.filePath, true);
|
||||
this.loadCache(this.filePath);
|
||||
}
|
||||
|
||||
async loadCache(filePath: string) {
|
||||
try {
|
||||
const data = await readFile(filePath, 'utf-8');
|
||||
const jsonData = JSON.parse(data);
|
||||
this.cacheData = jsonData;
|
||||
} catch (error) {
|
||||
const defaultData = { loginUsers: [] };
|
||||
this.cacheData = defaultData;
|
||||
await writeFile(filePath, JSON.stringify(defaultData, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
async get() {
|
||||
return this.cacheData;
|
||||
}
|
||||
async set(key: string, value: any) {
|
||||
this.cacheData = value;
|
||||
await writeFile(this.filePath, JSON.stringify(this.cacheData, null, 2));
|
||||
}
|
||||
async del() {
|
||||
await unlink(this.filePath);
|
||||
}
|
||||
}
|
12
src/query-login-browser.ts
Normal file
12
src/query-login-browser.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { QueryLogin, QueryLoginOpts } from './query-login.ts';
|
||||
import { MyCache } from '@kevisual/cache';
|
||||
type QueryLoginNodeOptsWithoutCache = Omit<QueryLoginOpts, 'cache'>;
|
||||
|
||||
export class QueryLoginBrowser extends QueryLogin {
|
||||
constructor(opts: QueryLoginNodeOptsWithoutCache) {
|
||||
super({
|
||||
...opts,
|
||||
cache: new MyCache('login'),
|
||||
});
|
||||
}
|
||||
}
|
14
src/query-login-node.ts
Normal file
14
src/query-login-node.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { QueryLogin, QueryLoginOpts } from './query-login.ts';
|
||||
import { LoginNodeCache, StorageNode } from './login-node-cache.ts';
|
||||
type QueryLoginNodeOptsWithoutCache = Omit<QueryLoginOpts, 'cache'>;
|
||||
export const storage = new StorageNode();
|
||||
await storage.loadCache();
|
||||
export class QueryLoginNode extends QueryLogin {
|
||||
constructor(opts: QueryLoginNodeOptsWithoutCache) {
|
||||
super({
|
||||
...opts,
|
||||
storage,
|
||||
cache: new LoginNodeCache('login'),
|
||||
});
|
||||
}
|
||||
}
|
@ -2,12 +2,14 @@ import { Query } from '@kevisual/query';
|
||||
import type { Result, DataOpts } from '@kevisual/query/query';
|
||||
import { setBaseResponse } from '@kevisual/query/query';
|
||||
import { LoginCacheStore, CacheStore } from './login-cache.ts';
|
||||
import { Cache } from './login-cache.ts';
|
||||
|
||||
type QueryLoginOpts = {
|
||||
export type QueryLoginOpts = {
|
||||
query?: Query;
|
||||
isBrowser?: boolean;
|
||||
onLoad?: () => void;
|
||||
storage?: Storage;
|
||||
cache: Cache;
|
||||
};
|
||||
export type QueryLoginData = {
|
||||
username?: string;
|
||||
@ -21,6 +23,9 @@ export type QueryLoginResult = {
|
||||
|
||||
export class QueryLogin {
|
||||
query: Query;
|
||||
/**
|
||||
* query login cache, 非实际操作, 一个cache的包裹模块
|
||||
*/
|
||||
cache: CacheStore;
|
||||
isBrowser: boolean;
|
||||
load?: boolean;
|
||||
@ -29,7 +34,7 @@ export class QueryLogin {
|
||||
|
||||
constructor(opts?: QueryLoginOpts) {
|
||||
this.query = opts?.query || new Query();
|
||||
this.cache = new LoginCacheStore('login');
|
||||
this.cache = new LoginCacheStore({ name: 'login', cache: opts.cache });
|
||||
this.isBrowser = opts?.isBrowser ?? true;
|
||||
this.init();
|
||||
this.onLoad = opts?.onLoad;
|
||||
@ -257,4 +262,38 @@ export class QueryLogin {
|
||||
},
|
||||
);
|
||||
}
|
||||
/**
|
||||
* 检查登录状态
|
||||
* @param token
|
||||
* @returns
|
||||
*/
|
||||
async checkLoginStatus(token: string) {
|
||||
const res = await this.post({
|
||||
path: 'user',
|
||||
key: 'checkLoginStatus',
|
||||
loginToken: token,
|
||||
});
|
||||
if (res.code === 200) {
|
||||
const accessToken = res.data?.accessToken;
|
||||
this.storage.setItem('token', accessToken || '');
|
||||
await this.beforeSetLoginUser({ accessToken, refreshToken: res.data?.refreshToken });
|
||||
return res;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* 使用web登录,创建url地址, 需要MD5和jsonwebtoken
|
||||
*/
|
||||
loginWithWeb(baseURL: string, { MD5, jsonwebtoken }: { MD5: any; jsonwebtoken: any }) {
|
||||
const randomId = Math.random().toString(36).substring(2, 15);
|
||||
const timestamp = Date.now();
|
||||
const tokenSecret = 'xiao' + randomId;
|
||||
const sign = MD5(`${tokenSecret}${timestamp}`).toString();
|
||||
const token = jsonwebtoken.sign({ randomId, timestamp, sign }, tokenSecret, {
|
||||
// 10分钟过期
|
||||
expiresIn: 60 * 10, // 10分钟
|
||||
});
|
||||
const url = `${baseURL}/api/router?path=user&key=webLogin&p&loginToken=${token}&sign=${sign}&randomId=${randomId}`;
|
||||
return { url, token, tokenSecret };
|
||||
}
|
||||
}
|
||||
|
58
src/test-node/login.ts
Normal file
58
src/test-node/login.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { QueryLoginNode, storage } from '../query-login-node.ts';
|
||||
import { Query } from '@kevisual/query';
|
||||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
const { password, envision_registry } = process.env;
|
||||
const query = new Query({
|
||||
url: envision_registry,
|
||||
});
|
||||
query.before(async (options) => {
|
||||
const token = storage.getItem('token');
|
||||
if (token) {
|
||||
options.headers = {
|
||||
...options.headers,
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
}
|
||||
return options;
|
||||
});
|
||||
const queryLogin = new QueryLoginNode({
|
||||
query,
|
||||
isBrowser: false,
|
||||
});
|
||||
|
||||
const login = async () => {
|
||||
const res = await queryLogin
|
||||
.login({
|
||||
username: 'root',
|
||||
password: password,
|
||||
})
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
||||
// login();
|
||||
|
||||
const getMe = async () => {
|
||||
const res = await queryLogin.getMe();
|
||||
console.log(res);
|
||||
};
|
||||
getMe();
|
||||
|
||||
// console.log('token', storage.getItem('token'));
|
||||
|
||||
const switchUser = async (username: string) => {
|
||||
const res = await queryLogin.switchUser(username);
|
||||
console.log(res);
|
||||
};
|
||||
// switchUser('admin');
|
||||
|
||||
// switchUser('root');
|
||||
const logout = async () => {
|
||||
const res = await queryLogin.logout();
|
||||
console.log(res);
|
||||
};
|
||||
// logout();
|
@ -1,4 +1,4 @@
|
||||
import { QueryLogin } from '../query-login.ts';
|
||||
import { QueryLoginBrowser } from '../query-login-browser.ts';
|
||||
import { Query } from '@kevisual/query';
|
||||
const query = new Query({
|
||||
url: 'https://kevisual.silkyai.cn/api/router',
|
||||
@ -14,7 +14,7 @@ query.before(async (options) => {
|
||||
}
|
||||
return options;
|
||||
});
|
||||
const queryLogin = new QueryLogin({
|
||||
const queryLogin = new QueryLoginBrowser({
|
||||
query,
|
||||
isBrowser: true,
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { defineConfig } from 'tsup';
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/query-login.ts'],
|
||||
entry: ['src/query-login.ts', 'src/query-login-browser.ts', 'src/query-login-node.ts'],
|
||||
|
||||
splitting: false,
|
||||
sourcemap: false,
|
||||
@ -10,4 +10,7 @@ export default defineConfig({
|
||||
dts: true,
|
||||
outDir: 'dist',
|
||||
tsconfig: 'tsconfig.json',
|
||||
define: {
|
||||
IS_BROWSER: 'false',
|
||||
},
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user