@kevisual/api 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +30 -0
- package/query/index.ts +7 -0
- package/query/kevisual.json +25 -0
- package/query/query-ai/defines/ai.ts +42 -0
- package/query/query-ai/query-ai.ts +101 -0
- package/query/query-app/defines/index.ts +3 -0
- package/query/query-app/defines/user-app-list.ts +62 -0
- package/query/query-app/defines/user-app.ts +33 -0
- package/query/query-app/query-app-define.ts +1 -0
- package/query/query-app/query-app.ts +18 -0
- package/query/query-login/login-cache.ts +204 -0
- package/query/query-login/login-node-cache.ts +132 -0
- package/query/query-login/query-login-browser.ts +12 -0
- package/query/query-login/query-login-node.ts +14 -0
- package/query/query-login/query-login.ts +434 -0
- package/query/query-mark/query-mark.ts +154 -0
- package/query/query-resources/index.ts +71 -0
- package/query/query-shop/defines/query-shop-define.ts +27 -0
- package/query/query-shop/query-shop.ts +17 -0
- package/query/query-upload/core/upload-chunk.ts +134 -0
- package/query/query-upload/core/upload-progress.ts +103 -0
- package/query/query-upload/core/upload.ts +113 -0
- package/query/query-upload/query-upload-browser.ts +51 -0
- package/query/query-upload/query-upload-node.ts +1 -0
- package/query/query-upload/query-upload.ts +11 -0
- package/query/query-upload/utils/filter-files.ts +23 -0
- package/query/query-upload/utils/index.ts +3 -0
- package/query/query-upload/utils/random-id.ts +3 -0
- package/query/query-upload/utils/to-file.ts +105 -0
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kevisual/api",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "bun bun.config.mjs"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [],
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"query",
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"packageManager": "pnpm@10.6.2",
|
|
21
|
+
"type": "module",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@kevisual/query": "^0.0.18",
|
|
24
|
+
"@kevisual/router": "^0.0.20"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@kevisual/types": "^0.0.10",
|
|
28
|
+
"@types/node": "^22.15.27"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/query/index.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://kevisual.xiongxiao.me/root/ai/kevisual/tools/kevisual-sync/schema.json?v=2",
|
|
3
|
+
"metadata": {
|
|
4
|
+
"share": "public"
|
|
5
|
+
},
|
|
6
|
+
"checkDir": {
|
|
7
|
+
"src/query": {
|
|
8
|
+
"url": "https://kevisual.xiongxiao.me/root/ai/code/registry/query",
|
|
9
|
+
"enabled": true
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"syncDirectory": [
|
|
13
|
+
{
|
|
14
|
+
"files": [
|
|
15
|
+
"src/query/**/*"
|
|
16
|
+
],
|
|
17
|
+
"ignore": [],
|
|
18
|
+
"registry": "https://kevisual.xiongxiao.me/root/ai/code/registry",
|
|
19
|
+
"replace": {
|
|
20
|
+
"src/": ""
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"sync": {}
|
|
25
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { QueryUtil } from '@/query/index.ts';
|
|
2
|
+
|
|
3
|
+
type Message = {
|
|
4
|
+
role?: 'user' | 'assistant' | 'system' | 'tool';
|
|
5
|
+
content?: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
};
|
|
8
|
+
export type PostChat = {
|
|
9
|
+
messages?: Message[];
|
|
10
|
+
model?: string;
|
|
11
|
+
group?: string;
|
|
12
|
+
user?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type ChatDataOpts = {
|
|
16
|
+
id?: string;
|
|
17
|
+
title?: string;
|
|
18
|
+
messages?: any[];
|
|
19
|
+
data?: any;
|
|
20
|
+
type?: 'temp' | 'keep' | string;
|
|
21
|
+
};
|
|
22
|
+
export type ChatOpts = {
|
|
23
|
+
username: string;
|
|
24
|
+
model: string;
|
|
25
|
+
/**
|
|
26
|
+
* 获取完整消息回复
|
|
27
|
+
*/
|
|
28
|
+
getFull?: boolean;
|
|
29
|
+
group: string;
|
|
30
|
+
/**
|
|
31
|
+
* openai的参数
|
|
32
|
+
*/
|
|
33
|
+
options?: any;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const appDefine = QueryUtil.create({
|
|
37
|
+
chat: {
|
|
38
|
+
path: 'ai',
|
|
39
|
+
key: 'chat',
|
|
40
|
+
description: '与 AI 进行对话, 调用 GPT 的AI 服务,生成结果,并返回。',
|
|
41
|
+
},
|
|
42
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { appDefine } from './defines/ai.ts';
|
|
2
|
+
import { PostChat, ChatOpts, ChatDataOpts } from './defines/ai.ts';
|
|
3
|
+
|
|
4
|
+
import { BaseQuery, DataOpts, Query } from '@kevisual/query/query';
|
|
5
|
+
|
|
6
|
+
export { appDefine };
|
|
7
|
+
|
|
8
|
+
export class QueryApp<T extends Query = Query> extends BaseQuery<T, typeof appDefine> {
|
|
9
|
+
constructor(opts?: { query: T }) {
|
|
10
|
+
super({
|
|
11
|
+
...opts,
|
|
12
|
+
query: opts?.query!,
|
|
13
|
+
queryDefine: appDefine,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 与 AI 进行对话, 调用 GPT 的AI 服务,生成结果,并返回。
|
|
18
|
+
* @param data
|
|
19
|
+
* @param opts
|
|
20
|
+
* @returns
|
|
21
|
+
*/
|
|
22
|
+
postChat(data: PostChat, opts?: DataOpts) {
|
|
23
|
+
return this.chain('chat').post(data, opts);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 获取模型列表
|
|
27
|
+
* @param opts
|
|
28
|
+
* @returns
|
|
29
|
+
*/
|
|
30
|
+
getModelList(data?: { usernames?: string[] }, opts?: DataOpts) {
|
|
31
|
+
return this.query.post(
|
|
32
|
+
{
|
|
33
|
+
path: 'ai',
|
|
34
|
+
key: 'get-model-list',
|
|
35
|
+
data,
|
|
36
|
+
},
|
|
37
|
+
opts,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 聊天对话模型
|
|
42
|
+
* @param data
|
|
43
|
+
* @param chatOpts
|
|
44
|
+
* @param opts
|
|
45
|
+
* @returns
|
|
46
|
+
*/
|
|
47
|
+
chat(data: ChatDataOpts, chatOpts: ChatOpts, opts?: DataOpts) {
|
|
48
|
+
const { username, model, group, getFull = true } = chatOpts;
|
|
49
|
+
if (!username || !model || !group) {
|
|
50
|
+
throw new Error('username, model, group is required');
|
|
51
|
+
}
|
|
52
|
+
return this.query.post(
|
|
53
|
+
{
|
|
54
|
+
path: 'ai',
|
|
55
|
+
key: 'chat',
|
|
56
|
+
...chatOpts,
|
|
57
|
+
getFull,
|
|
58
|
+
data,
|
|
59
|
+
},
|
|
60
|
+
opts,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
clearConfigCache(opts?: DataOpts) {
|
|
64
|
+
return this.query.post(
|
|
65
|
+
{
|
|
66
|
+
path: 'ai',
|
|
67
|
+
key: 'clear-cache',
|
|
68
|
+
},
|
|
69
|
+
opts,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 获取聊天使用情况
|
|
74
|
+
* @param opts
|
|
75
|
+
* @returns
|
|
76
|
+
*/
|
|
77
|
+
getChatUsage(opts?: DataOpts) {
|
|
78
|
+
return this.query.post(
|
|
79
|
+
{
|
|
80
|
+
path: 'ai',
|
|
81
|
+
key: 'get-chat-usage',
|
|
82
|
+
},
|
|
83
|
+
opts,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 清除当前用户模型自己的统计
|
|
89
|
+
* @param opts
|
|
90
|
+
* @returns
|
|
91
|
+
*/
|
|
92
|
+
clearSelfUsage(opts?: DataOpts) {
|
|
93
|
+
return this.query.post(
|
|
94
|
+
{
|
|
95
|
+
path: 'ai',
|
|
96
|
+
key: 'clear-chat-limit',
|
|
97
|
+
},
|
|
98
|
+
opts,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { QueryUtil } from '@/query/index.ts';
|
|
2
|
+
|
|
3
|
+
export const appDefine = QueryUtil.create({
|
|
4
|
+
getApp: {
|
|
5
|
+
path: 'app',
|
|
6
|
+
key: 'get',
|
|
7
|
+
description: '获取应用信息',
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
updateApp: {
|
|
11
|
+
path: 'app',
|
|
12
|
+
key: 'update',
|
|
13
|
+
description: '更新应用信息',
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
deleteApp: {
|
|
17
|
+
path: 'app',
|
|
18
|
+
key: 'delete',
|
|
19
|
+
description: '删除应用信息',
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
listApps: {
|
|
23
|
+
path: 'app',
|
|
24
|
+
key: 'list',
|
|
25
|
+
description: '列出所有应用信息',
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
canUploadFiles: {
|
|
29
|
+
path: 'app',
|
|
30
|
+
key: 'canUploadFiles',
|
|
31
|
+
description: '检查是否可以上传文件',
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
uploadFiles: {
|
|
35
|
+
path: 'app',
|
|
36
|
+
key: 'uploadFiles',
|
|
37
|
+
description: '上传文件',
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
publishApp: {
|
|
41
|
+
path: 'app',
|
|
42
|
+
key: 'publish',
|
|
43
|
+
description: '发布应用',
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
getMinioList: {
|
|
47
|
+
path: 'app',
|
|
48
|
+
key: 'get-minio-list',
|
|
49
|
+
description: '获取 MinIO 文件列表',
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
detectVersionList: {
|
|
53
|
+
path: 'app',
|
|
54
|
+
key: 'detectVersionList',
|
|
55
|
+
description: '检测版本列表并同步 MinIO 数据',
|
|
56
|
+
},
|
|
57
|
+
publicList: {
|
|
58
|
+
path: 'app',
|
|
59
|
+
key: 'public-list',
|
|
60
|
+
description: '获取公开应用列表',
|
|
61
|
+
},
|
|
62
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { QueryUtil } from '@/query/index.ts';
|
|
2
|
+
|
|
3
|
+
export const userAppDefine = QueryUtil.create({
|
|
4
|
+
listUserApps: {
|
|
5
|
+
path: 'user-app',
|
|
6
|
+
key: 'list',
|
|
7
|
+
description: '列出当前用户的所有应用(不包含 data 字段)',
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
getUserApp: {
|
|
11
|
+
path: 'user-app',
|
|
12
|
+
key: 'get',
|
|
13
|
+
description: '获取用户应用信息,可以指定 id 或 key',
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
updateUserApp: {
|
|
17
|
+
path: 'user-app',
|
|
18
|
+
key: 'update',
|
|
19
|
+
description: '更新或创建用户应用',
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
deleteUserApp: {
|
|
23
|
+
path: 'user-app',
|
|
24
|
+
key: 'delete',
|
|
25
|
+
description: '删除用户应用及关联数据',
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
testUserApp: {
|
|
29
|
+
path: 'user-app',
|
|
30
|
+
key: 'test',
|
|
31
|
+
description: '对 user-app 的数据进行测试,获取版本信息',
|
|
32
|
+
},
|
|
33
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './defines/index.ts';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { appDefine, userAppDefine } from './defines/index.ts';
|
|
2
|
+
|
|
3
|
+
import { BaseQuery, DataOpts, Query } from '@kevisual/query/query';
|
|
4
|
+
|
|
5
|
+
export { appDefine, userAppDefine };
|
|
6
|
+
|
|
7
|
+
export class QueryApp extends BaseQuery {
|
|
8
|
+
appDefine = appDefine;
|
|
9
|
+
userAppDefine = userAppDefine;
|
|
10
|
+
constructor(opts?: { query: Query }) {
|
|
11
|
+
super(opts!);
|
|
12
|
+
this.appDefine.query = this.query;
|
|
13
|
+
this.userAppDefine.query = this.query;
|
|
14
|
+
}
|
|
15
|
+
getList(data: any, opts?: DataOpts) {
|
|
16
|
+
return this.appDefine.queryChain('listApps').post(data, opts);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
export interface Cache {
|
|
2
|
+
/**
|
|
3
|
+
* @update 获取缓存
|
|
4
|
+
*/
|
|
5
|
+
get(key: string): Promise<any>;
|
|
6
|
+
/**
|
|
7
|
+
* @update 设置缓存
|
|
8
|
+
*/
|
|
9
|
+
set(key: string, value: any): Promise<any>;
|
|
10
|
+
/**
|
|
11
|
+
* @update 删除缓存
|
|
12
|
+
*/
|
|
13
|
+
del(): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* 初始化
|
|
16
|
+
*/
|
|
17
|
+
init?: () => Promise<any>;
|
|
18
|
+
}
|
|
19
|
+
type User = {
|
|
20
|
+
avatar?: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
id?: string;
|
|
23
|
+
needChangePassword?: boolean;
|
|
24
|
+
orgs?: string[];
|
|
25
|
+
type?: string;
|
|
26
|
+
username?: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type CacheLoginUser = {
|
|
30
|
+
user?: User;
|
|
31
|
+
id?: string;
|
|
32
|
+
accessToken?: string;
|
|
33
|
+
refreshToken?: string;
|
|
34
|
+
};
|
|
35
|
+
type CacheLogin = {
|
|
36
|
+
loginUsers: CacheLoginUser[];
|
|
37
|
+
} & CacheLoginUser;
|
|
38
|
+
|
|
39
|
+
export type CacheStore<T = Cache> = {
|
|
40
|
+
name: string;
|
|
41
|
+
/**
|
|
42
|
+
* 缓存数据
|
|
43
|
+
* @important 需要先调用init
|
|
44
|
+
*/
|
|
45
|
+
cacheData: CacheLogin;
|
|
46
|
+
/**
|
|
47
|
+
* 实际操作的cache, 需要先调用init
|
|
48
|
+
*/
|
|
49
|
+
cache: T;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 设置当前用户
|
|
53
|
+
*/
|
|
54
|
+
setLoginUser(user: CacheLoginUser): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* 获取当前用户
|
|
57
|
+
*/
|
|
58
|
+
getCurrentUser(): Promise<User>;
|
|
59
|
+
/**
|
|
60
|
+
* 获取当前用户列表
|
|
61
|
+
*/
|
|
62
|
+
getCurrentUserList(): Promise<CacheLoginUser[]>;
|
|
63
|
+
/**
|
|
64
|
+
* 获取缓存的refreshToken
|
|
65
|
+
*/
|
|
66
|
+
getRefreshToken(): Promise<string>;
|
|
67
|
+
/**
|
|
68
|
+
* 获取缓存的accessToken
|
|
69
|
+
*/
|
|
70
|
+
getAccessToken(): Promise<string>;
|
|
71
|
+
/**
|
|
72
|
+
* 清除当前用户
|
|
73
|
+
*/
|
|
74
|
+
clearCurrentUser(): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* 清除所有用户
|
|
77
|
+
*/
|
|
78
|
+
clearAll(): Promise<void>;
|
|
79
|
+
|
|
80
|
+
getValue(): Promise<CacheLogin>;
|
|
81
|
+
setValue(value: CacheLogin): Promise<CacheLogin>;
|
|
82
|
+
delValue(): Promise<void>;
|
|
83
|
+
init(): Promise<any>;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export type LoginCacheStoreOpts = {
|
|
87
|
+
name: string;
|
|
88
|
+
cache: Cache;
|
|
89
|
+
};
|
|
90
|
+
export class LoginCacheStore implements CacheStore<any> {
|
|
91
|
+
cache: Cache;
|
|
92
|
+
name: string;
|
|
93
|
+
cacheData: CacheLogin;
|
|
94
|
+
constructor(opts: LoginCacheStoreOpts) {
|
|
95
|
+
if (!opts.cache) {
|
|
96
|
+
throw new Error('cache is required');
|
|
97
|
+
}
|
|
98
|
+
// @ts-ignore
|
|
99
|
+
this.cache = opts.cache;
|
|
100
|
+
this.cacheData = {
|
|
101
|
+
loginUsers: [],
|
|
102
|
+
user: undefined,
|
|
103
|
+
id: undefined,
|
|
104
|
+
accessToken: undefined,
|
|
105
|
+
refreshToken: undefined,
|
|
106
|
+
};
|
|
107
|
+
this.name = opts.name;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 设置缓存
|
|
111
|
+
* @param key
|
|
112
|
+
* @param value
|
|
113
|
+
* @returns
|
|
114
|
+
*/
|
|
115
|
+
async setValue(value: CacheLogin) {
|
|
116
|
+
await this.cache.set(this.name, value);
|
|
117
|
+
this.cacheData = value;
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* 删除缓存
|
|
122
|
+
*/
|
|
123
|
+
async delValue() {
|
|
124
|
+
await this.cache.del();
|
|
125
|
+
}
|
|
126
|
+
getValue(): Promise<CacheLogin> {
|
|
127
|
+
return this.cache.get(this.name);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* 初始化,设置默认值
|
|
131
|
+
*/
|
|
132
|
+
async init() {
|
|
133
|
+
const defaultData = {
|
|
134
|
+
loginUsers: [],
|
|
135
|
+
user: null,
|
|
136
|
+
id: null,
|
|
137
|
+
accessToken: null,
|
|
138
|
+
refreshToken: null,
|
|
139
|
+
};
|
|
140
|
+
if (this.cache.init) {
|
|
141
|
+
try {
|
|
142
|
+
const cacheData = await this.cache.init();
|
|
143
|
+
this.cacheData = cacheData || defaultData;
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.log('cacheInit error', error);
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
this.cacheData = (await this.getValue()) || defaultData;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 设置当前用户
|
|
153
|
+
* @param user
|
|
154
|
+
*/
|
|
155
|
+
async setLoginUser(user: CacheLoginUser) {
|
|
156
|
+
const has = this.cacheData.loginUsers.find((u) => u.id === user.id);
|
|
157
|
+
if (has) {
|
|
158
|
+
this.cacheData.loginUsers = this.cacheData?.loginUsers?.filter((u) => u?.id && u.id !== user.id);
|
|
159
|
+
}
|
|
160
|
+
this.cacheData.loginUsers.push(user);
|
|
161
|
+
this.cacheData.user = user.user;
|
|
162
|
+
this.cacheData.id = user.id;
|
|
163
|
+
this.cacheData.accessToken = user.accessToken;
|
|
164
|
+
this.cacheData.refreshToken = user.refreshToken;
|
|
165
|
+
await this.setValue(this.cacheData);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
getCurrentUser(): Promise<CacheLoginUser> {
|
|
169
|
+
const cacheData = this.cacheData;
|
|
170
|
+
return Promise.resolve(cacheData.user!);
|
|
171
|
+
}
|
|
172
|
+
getCurrentUserList(): Promise<CacheLoginUser[]> {
|
|
173
|
+
return Promise.resolve(this.cacheData.loginUsers.filter((u) => u?.id));
|
|
174
|
+
}
|
|
175
|
+
getRefreshToken(): Promise<string> {
|
|
176
|
+
const cacheData = this.cacheData;
|
|
177
|
+
return Promise.resolve(cacheData.refreshToken || '');
|
|
178
|
+
}
|
|
179
|
+
getAccessToken(): Promise<string> {
|
|
180
|
+
const cacheData = this.cacheData;
|
|
181
|
+
return Promise.resolve(cacheData.accessToken || '');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async clearCurrentUser() {
|
|
185
|
+
const user = await this.getCurrentUser();
|
|
186
|
+
const has = this.cacheData.loginUsers.find((u) => u.id === user.id);
|
|
187
|
+
if (has) {
|
|
188
|
+
this.cacheData.loginUsers = this.cacheData?.loginUsers?.filter((u) => u?.id && u.id !== user.id);
|
|
189
|
+
}
|
|
190
|
+
this.cacheData.user = undefined;
|
|
191
|
+
this.cacheData.id = undefined;
|
|
192
|
+
this.cacheData.accessToken = undefined;
|
|
193
|
+
this.cacheData.refreshToken = undefined;
|
|
194
|
+
await this.setValue(this.cacheData);
|
|
195
|
+
}
|
|
196
|
+
async clearAll() {
|
|
197
|
+
this.cacheData.loginUsers = [];
|
|
198
|
+
this.cacheData.user = undefined;
|
|
199
|
+
this.cacheData.id = undefined;
|
|
200
|
+
this.cacheData.accessToken = undefined;
|
|
201
|
+
this.cacheData.refreshToken = undefined;
|
|
202
|
+
await this.setValue(this.cacheData);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Cache } from './login-cache.ts';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join, dirname } from 'node:path';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import { readFileSync, writeFileSync, accessSync } from 'node:fs';
|
|
6
|
+
import { readFile, writeFile, unlink, mkdir } from 'node:fs/promises';
|
|
7
|
+
export const fileExists = async (
|
|
8
|
+
filePath: string,
|
|
9
|
+
{ createIfNotExists = true, isFile = true, isDir = false }: { createIfNotExists?: boolean; isFile?: boolean; isDir?: boolean } = {},
|
|
10
|
+
) => {
|
|
11
|
+
try {
|
|
12
|
+
accessSync(filePath, fs.constants.F_OK);
|
|
13
|
+
return true;
|
|
14
|
+
} catch (error) {
|
|
15
|
+
if (createIfNotExists && isDir) {
|
|
16
|
+
await mkdir(filePath, { recursive: true });
|
|
17
|
+
return true;
|
|
18
|
+
} else if (createIfNotExists && isFile) {
|
|
19
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
export const readConfigFile = (filePath: string) => {
|
|
26
|
+
try {
|
|
27
|
+
const data = readFileSync(filePath, 'utf-8');
|
|
28
|
+
const jsonData = JSON.parse(data);
|
|
29
|
+
return jsonData;
|
|
30
|
+
} catch (error) {
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
export const writeConfigFile = (filePath: string, data: any) => {
|
|
35
|
+
writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
36
|
+
};
|
|
37
|
+
export const getHostName = () => {
|
|
38
|
+
const configDir = join(homedir(), '.config', 'envision');
|
|
39
|
+
const configFile = join(configDir, 'config.json');
|
|
40
|
+
const config = readConfigFile(configFile);
|
|
41
|
+
const baseURL = config.baseURL || 'https://kevisual.cn';
|
|
42
|
+
const hostname = new URL(baseURL).hostname;
|
|
43
|
+
return hostname;
|
|
44
|
+
};
|
|
45
|
+
export class StorageNode implements Storage {
|
|
46
|
+
cacheData: any;
|
|
47
|
+
filePath: string;
|
|
48
|
+
constructor() {
|
|
49
|
+
this.cacheData = {};
|
|
50
|
+
const configDir = join(homedir(), '.config', 'envision');
|
|
51
|
+
const hostname = getHostName();
|
|
52
|
+
this.filePath = join(configDir, 'config', `${hostname}-storage.json`);
|
|
53
|
+
fileExists(this.filePath, { isFile: true });
|
|
54
|
+
}
|
|
55
|
+
async loadCache() {
|
|
56
|
+
const filePath = this.filePath;
|
|
57
|
+
try {
|
|
58
|
+
const data = await readConfigFile(filePath);
|
|
59
|
+
this.cacheData = data;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
this.cacheData = {};
|
|
62
|
+
await writeFile(filePath, JSON.stringify(this.cacheData, null, 2));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
get length() {
|
|
66
|
+
return Object.keys(this.cacheData).length;
|
|
67
|
+
}
|
|
68
|
+
getItem(key: string) {
|
|
69
|
+
return this.cacheData[key];
|
|
70
|
+
}
|
|
71
|
+
setItem(key: string, value: any) {
|
|
72
|
+
this.cacheData[key] = value;
|
|
73
|
+
writeFile(this.filePath, JSON.stringify(this.cacheData, null, 2));
|
|
74
|
+
}
|
|
75
|
+
removeItem(key: string) {
|
|
76
|
+
delete this.cacheData[key];
|
|
77
|
+
writeFile(this.filePath, JSON.stringify(this.cacheData, null, 2));
|
|
78
|
+
}
|
|
79
|
+
clear() {
|
|
80
|
+
this.cacheData = {};
|
|
81
|
+
writeFile(this.filePath, JSON.stringify(this.cacheData, null, 2));
|
|
82
|
+
}
|
|
83
|
+
key(index: number) {
|
|
84
|
+
return Object.keys(this.cacheData)[index];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export class LoginNodeCache implements Cache {
|
|
88
|
+
filepath: string;
|
|
89
|
+
|
|
90
|
+
constructor(filepath?: string) {
|
|
91
|
+
this.filepath = filepath || join(homedir(), '.config', 'envision', 'config', `${getHostName()}-login.json`);
|
|
92
|
+
fileExists(this.filepath, { isFile: true });
|
|
93
|
+
}
|
|
94
|
+
async get(_key: string) {
|
|
95
|
+
try {
|
|
96
|
+
const filePath = this.filepath;
|
|
97
|
+
const data = readConfigFile(filePath);
|
|
98
|
+
return data;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.log('get error', error);
|
|
101
|
+
return {};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async set(_key: string, value: any) {
|
|
105
|
+
try {
|
|
106
|
+
const data = readConfigFile(this.filepath);
|
|
107
|
+
const newData = { ...data, ...value };
|
|
108
|
+
writeConfigFile(this.filepath, newData);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.log('set error', error);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async del() {
|
|
114
|
+
await unlink(this.filepath);
|
|
115
|
+
}
|
|
116
|
+
async loadCache(filePath: string) {
|
|
117
|
+
try {
|
|
118
|
+
const data = await readFile(filePath, 'utf-8');
|
|
119
|
+
const jsonData = JSON.parse(data);
|
|
120
|
+
return jsonData;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
// console.log('loadCache error', error);
|
|
123
|
+
console.log('create new cache file:', filePath);
|
|
124
|
+
const defaultData = { loginUsers: [] };
|
|
125
|
+
writeConfigFile(filePath, defaultData);
|
|
126
|
+
return defaultData;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async init() {
|
|
130
|
+
return await this.loadCache(this.filepath);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { QueryLogin, QueryLoginOpts } from './query-login.ts';
|
|
2
|
+
import { MyCache } from '@kevisual/cache';
|
|
3
|
+
type QueryLoginNodeOptsWithoutCache = Omit<QueryLoginOpts, 'cache'>;
|
|
4
|
+
|
|
5
|
+
export class QueryLoginBrowser extends QueryLogin {
|
|
6
|
+
constructor(opts: QueryLoginNodeOptsWithoutCache) {
|
|
7
|
+
super({
|
|
8
|
+
...opts,
|
|
9
|
+
cache: new MyCache('login'),
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { QueryLogin, QueryLoginOpts } from './query-login.ts';
|
|
2
|
+
import { LoginNodeCache, StorageNode } from './login-node-cache.ts';
|
|
3
|
+
type QueryLoginNodeOptsWithoutCache = Omit<QueryLoginOpts, 'cache'>;
|
|
4
|
+
export const storage = new StorageNode();
|
|
5
|
+
await storage.loadCache();
|
|
6
|
+
export class QueryLoginNode extends QueryLogin {
|
|
7
|
+
constructor(opts: QueryLoginNodeOptsWithoutCache) {
|
|
8
|
+
super({
|
|
9
|
+
...opts,
|
|
10
|
+
storage,
|
|
11
|
+
cache: new LoginNodeCache(),
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
}
|