token cache

This commit is contained in:
熊潇 2025-02-26 23:59:42 +08:00
parent 26c6248d10
commit c01820d1c6
12 changed files with 433 additions and 44 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@kevisual/envision-cli",
"version": "0.0.23",
"version": "0.0.24",
"description": "envision command tools",
"main": "dist/index.js",
"type": "module",
@ -60,7 +60,7 @@
},
"dependencies": {
"@kevisual/load": "^0.0.4",
"@kevisual/router": "^0.0.6",
"@kevisual/router": "^0.0.7",
"crypto-js": "^4.2.0",
"jsonwebtoken": "^9.0.2",
"pg-hstore": "^2.3.4",

10
pnpm-lock.yaml generated
View File

@ -15,8 +15,8 @@ importers:
specifier: ^0.0.4
version: 0.0.4
'@kevisual/router':
specifier: ^0.0.6
version: 0.0.6
specifier: ^0.0.7
version: 0.0.7
crypto-js:
specifier: ^4.2.0
version: 4.2.0
@ -410,8 +410,8 @@ packages:
'@kevisual/query@0.0.7-alpha.3':
resolution: {integrity: sha512-zNTIbyU87dlp8ZeLvPoc1ou7cZCL60to4xptyMD3VKsldL/dDSAMf7JWwUivNiq9lRxk9KVEZA7YX558mzeQcw==}
'@kevisual/router@0.0.6':
resolution: {integrity: sha512-7FQUY87Zy5A4V30OAggRbGpO/Asd7SUpnhHv8mlxnSFFTto25xpXmjHYp12mu/HJTsHM7RTaxVEyD1DeP44D2A==}
'@kevisual/router@0.0.7':
resolution: {integrity: sha512-4n1Tp4YLoraJv7jtfy7jbuLGyAj0B2QkTlnlEDHCUTlEUOvOkjtf7DHAe2SL92fTgXhSbod0I/0vUcDF85oj/w==}
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
@ -1973,7 +1973,7 @@ snapshots:
'@kevisual/query@0.0.7-alpha.3': {}
'@kevisual/router@0.0.6':
'@kevisual/router@0.0.7':
dependencies:
path-to-regexp: 8.2.0
selfsigned: 2.4.1

View File

@ -8,13 +8,23 @@ type Message = {
key?: string;
payload?: any;
};
export const runApp = async (msg: Message) => {
const { code, body, message } = await app.router.parse(msg);
const res = { code, data: body };
if (message) {
res['message'] = message;
type Response = {
code: number;
data?: any;
message?: string;
};
export const runApp = async (msg: Message): Promise<Response> => {
try {
const { code, body, message } = await app.call(msg);
const res = { code, data: body };
if (message) {
res['message'] = message;
}
return res as { code: number; data: any; message?: string };
} catch (error) {
console.error('runApp error', error);
return { code: 500, message: error.message };
}
return res;
};
export const loadApp = async (mainApp: App) => {

View File

@ -2,10 +2,13 @@ import { App } from '@kevisual/router';
import fs from 'fs';
import { getConfig, getPidList, pidFilePath, checkFileExists } from './module/get-config.ts';
import { sequelize } from './module/sequelize.ts';
import { spawn } from 'child_process';
export { sequelize };
export const app = new App();
export const app = new App({
serverOptions: {
httpType: 'https',
},
});
// 处理进程退出或中断信号,确保删除 pid 文件
const cleanUp = () => {
@ -42,7 +45,7 @@ export const createApp = async () => {
}
fs.writeFileSync(pidFilePath, process.pid.toString());
app.listen(21015, () => {
console.log('Server is running on port 21015', 'http://localhost:21015/api/router', 'server id', process.pid);
console.log('Server is running on port 21015', 'https://localhost:21015/api/router', 'server id', process.pid);
});
// import('./route.ts');
};

View File

@ -6,11 +6,40 @@ import inquirer from 'inquirer';
import { runApp } from '../app-run.ts';
import { chalk } from '@/module/chalk.ts';
import { loginInCommand } from '@/module/login/login-by-web.ts';
export const saveToken = async (token: string) => {
const baseURL = getBaseURL();
const res = await runApp({ path: 'config', key: 'saveToken', payload: { baseURL, token } });
if (res.code !== 200) {
console.log('Set token failed', res.message || '');
}
};
export const switchToken = async (baseURL: string) => {
const res = await runApp({ path: 'config', key: 'switchToken', payload: { baseURL } });
if (res.code !== 200 && res.code !== 404) {
console.log('switch token failed', res.message || '');
}
return res;
};
export const deleteToken = async (baseURL: string) => {
const res = await runApp({ path: 'config', key: 'deleteToken', payload: { baseURL } });
if (res.code !== 200) {
console.log('delete token failed', res.message || '');
}
return res;
};
export const getTokenList = async () => {
const res = await runApp({ path: 'config', key: 'getTokenList' });
if (res.code !== 200) {
console.log('get token list failed', res.message || '');
}
return res;
};
// 定义login命令支持 `-u` 和 `-p` 参数来输入用户名和密码
const loginCommand = new Command('login')
.description('Login to the application')
.option('-u, --username <username>', 'Specify username')
.option('-p, --password <password>', 'Specify password')
.option('-f, --force', 'Force login')
.option('-w, --web', 'Login on the web')
.action(async (options) => {
const config = getConfig();
@ -54,6 +83,7 @@ const loginCommand = new Command('login')
if (res.code === 200) {
const { token } = res.data;
writeConfig({ ...config, token });
saveToken(token);
console.log('welcome', username);
} else {
console.log('登录失败', res.message || '');
@ -100,6 +130,7 @@ const switchOrgCommand = new Command('switch').argument('<username>', 'Switch to
const token = res.data.token;
writeConfig({ ...config, token });
console.log(`Switch ${username} Success`);
saveToken(token);
await showMe();
} else {
console.log(`Switch ${username} Failed`, res.message || '');

View File

@ -1,12 +1,44 @@
import { program as app, Command } from '@/program.ts';
import { getConfig, query, writeConfig } from '@/module/index.ts';
import inquirer from 'inquirer';
import util from 'util';
import { saveToken, switchToken, deleteToken, getTokenList } from './login.ts';
const token = new Command('token').description('show token').action(async () => {
const config = getConfig();
console.log('token', config.token);
});
const tokenList = new Command('list')
.description('show token list')
.option('-r --remove <number>', 'remove token by number')
.action(async (opts) => {
const res = await getTokenList();
if (res.code !== 200) {
console.error('get token list failed', res.message || '');
return;
}
console.log(util.inspect(res.data.value, { colors: true, depth: 4 }));
const list = res.data.value || [];
if (opts.remove) {
const index = Number(opts.remove) - 1;
if (index < 0 || index >= list.length) {
console.log('index out of range');
return;
}
const removeBase = list.splice(index, 1);
const baseURL = removeBase[0];
if (baseURL.baseURL) {
const res = await deleteToken(baseURL?.baseURL);
if (res.code !== 200) {
return;
}
}
console.log('delete token success', 'delete', baseURL);
return;
}
});
token.addCommand(tokenList);
app.addCommand(token);
const baseURL = new Command('baseURL')
@ -20,7 +52,7 @@ const baseURL = new Command('baseURL')
.option('-c, --clear', 'clear baseURL')
.action(async (opts) => {
const config = getConfig();
let list = config.baseURLList || [];
let list = (config.baseURLList as Array<string>) || [];
const quineList = (list: string[]) => {
const newList = new Set(list);
return Array.from(newList);
@ -49,20 +81,23 @@ const baseURL = new Command('baseURL')
console.log('index out of range');
return;
}
list.splice(index, 1);
const removeBase = list.splice(index, 1);
list = quineList(list);
showList(list);
writeConfig({ ...config, baseURLList: list });
removeBase[0] && deleteToken(removeBase[0]);
return;
}
if (opts.set) {
const isNumber = !isNaN(Number(opts.set));
let baseURL = '';
if (isNumber) {
const index = Number(opts.set) - 1;
if (index < 0 || index >= list.length) {
console.log('index out of range');
return;
}
baseURL = list[index];
writeConfig({ ...config, baseURL: list[index] });
console.log('set baseURL success:', list[index]);
} else {
@ -72,9 +107,11 @@ const baseURL = new Command('baseURL')
console.log('invalid baseURL:', opts.set);
return;
}
baseURL = opts.set;
writeConfig({ ...config, baseURL: opts.set });
console.log('set baseURL success:', opts.set);
}
baseURL && switchToken(baseURL);
return;
}
if (opts.list) {
@ -100,6 +137,7 @@ const setBaseURL = new Command('set').description('set baseURL').action(async ()
]);
const baseURL = answers.baseURL;
writeConfig({ ...config, baseURL });
baseURL && switchToken(baseURL);
});
baseURL.addCommand(setBaseURL);

View File

@ -435,7 +435,7 @@ const servicesCommand = new Command('services')
);
});
} else {
console.log(chalk.red(res.message || '获取列表失败'));
console.log('error', chalk.red(res.message || '获取列表失败'));
}
return;
}

View File

@ -1,49 +1,102 @@
import { program as app, Command } from '@/program.ts';
import { program, Command } from '@/program.ts';
import inquirer from 'inquirer';
import { query } from '../module/index.ts';
import chalk from 'chalk';
import util from 'util';
import { runApp } from '@/app-run.ts';
const router = new Command('router').description('router get');
program.addCommand(router);
// web 开发模块
const command = new Command('router')
.description('router get')
.option('-p, --path <path>', '退出进程')
.option('-k, --key <key>', '启动进程')
const command = new Command('service')
.description('router services get')
.option('-p, --path <path>', '第一路径 path')
.option('-k, --key <key>', '第二路径 key')
.action(async (options) => {
let { path, key } = options;
// 如果没有传递参数,则通过交互式输入
if (!path || !key) {
if (!path) {
const answers = await inquirer.prompt([
{
type: 'input',
name: 'path',
required: true,
message: 'Enter your path:',
when: () => !path, // 当 username 为空时,提示用户输入
},
{
type: 'input',
name: 'key',
message: 'Enter your key:',
when: () => !key, // 当 password 为空时,提示用户输入
},
]);
path = answers.path || path;
}
if (!key) {
const answers = await inquirer.prompt([
{
type: 'input',
required: false,
name: 'key',
message: 'Enter your key:',
},
]);
key = answers.key || key;
}
const res = await query.post({ path, key });
if (res?.code === 200) {
const data = res.data.map((item: any) => {
// return `id: ${item.id}, title: ${item.title}`;
return {
id: item.id,
title: item.title,
};
});
console.log(chalk.green(util.inspect(data, { colors: true, depth: 4 })));
console.log('query success');
const _list = res.data?.list || res.data;
if (Array.isArray(_list)) {
const data = _list.map((item: any) => {
// return `id: ${item.id}, title: ${item.title}`;
return {
id: item.id,
title: item.title,
};
});
console.log(chalk.green(util.inspect(data, { colors: true, depth: 4 })));
}
} else {
console.log('error', res.message || '');
}
});
app.addCommand(command);
router.addCommand(command);
const localRouter = new Command('local')
.description('router local get')
.option('-p, --path <path>', '第一路径 path')
.option('-k, --key <key>', '第二路径 key')
.action(async (options) => {
let { path, key } = options;
// 如果没有传递参数,则通过交互式输入
if (!path) {
const answers = await inquirer.prompt([
{
type: 'input',
name: 'path',
required: true,
message: 'Enter your path:',
when: () => !path, // 当 username 为空时,提示用户输入
},
]);
path = answers.path || path;
}
if (!key) {
const answers = await inquirer.prompt([
{
type: 'input',
required: false,
name: 'key',
message: 'Enter your key:',
},
]);
key = answers.key || key;
}
const res = await runApp({ path, key });
if (res?.code === 200) {
console.log('query success');
console.log(chalk.green(util.inspect(res, { colors: true, depth: 4 })));
} else {
console.log('error query', res.code, res.message || '');
}
});
router.addCommand(localRouter);

View File

@ -4,7 +4,7 @@ import { chalk } from '../chalk.ts';
import jsonwebtoken from 'jsonwebtoken';
import { BaseLoad } from '@kevisual/load';
import { getConfig, writeConfig } from '../get-config.ts';
export const saveToken = async (token: string) => {
const saveToken = async (token: string) => {
const config = await getConfig();
writeConfig({
...config,

View File

@ -0,0 +1,200 @@
import { app } from '@/app.ts';
import { Config } from './model/config.ts';
import { writeConfig } from '@/module/get-config.ts';
const cacheToken = 'tokenList';
export type TokenCacheItem = {
baseURL?: string;
token?: string;
expireTime?: number;
};
app
.route({
path: 'config',
key: 'getTokenList',
description: 'Get token list',
})
.define(async (ctx) => {
const tokenList = await Config.findOne({
where: {
key: cacheToken,
},
logging: false,
});
if (tokenList) {
ctx.body = tokenList;
return;
}
ctx.body = {
value: [],
};
})
.addTo(app);
app
.route({
path: 'config',
key: 'setTokenList',
description: 'Set token list',
})
.define(async (ctx) => {
const { data } = ctx.query;
if (!data) {
ctx.throw(400, 'data is required');
}
let config = await Config.findOne({
where: { key: cacheToken }, // 自定义条件
logging: false,
});
if (!config) {
config = await Config.create(
{
key: cacheToken,
value: data,
},
{ logging: false },
);
ctx.body = config;
return;
} else {
config.value = data;
await config.save();
ctx.body = config;
}
})
.addTo(app);
app
.route({
path: 'config',
key: 'clearToken',
description: 'Clear token list',
})
.define(async (ctx) => {
const config = await Config.findOne({
where: { key: cacheToken },
logging: false,
});
if (config) {
await config.destroy();
}
ctx.body = 'success';
})
.addTo(app);
app
.route({
path: 'config',
key: 'saveToken',
description: 'Add token',
validator: {
baseURL: {
type: 'string',
required: true,
message: 'baseURL is required',
},
token: {
type: 'string',
required: true,
message: 'token is required',
},
},
})
.define(async (ctx) => {
const { baseURL, token } = ctx.query;
if (!baseURL || !token) {
ctx.throw(400, 'baseURL and token are required');
}
const data: TokenCacheItem = {
baseURL,
token,
};
const tokenRes = await ctx.call({ path: 'config', key: 'getTokenList' });
if (tokenRes.code !== 200) {
ctx.throw(tokenRes.code, tokenRes.message || 'Failed to get token list');
}
const tokenList: TokenCacheItem[] = tokenRes.body?.value || [];
// Check if the token already exists
const index = tokenList.findIndex((item) => item.baseURL === data.baseURL);
if (index > -1) {
tokenList[index] = data;
} else {
tokenList.push(data);
}
const res = await ctx.call({ path: 'config', key: 'setTokenList', payload: { data: tokenList } });
if (res.code === 200) {
ctx.body = res.body?.value;
} else ctx.throw(res.code, res.message || 'Failed to add token');
})
.addTo(app);
app
.route({
path: 'config',
key: 'switchToken',
description: 'Switch token user',
validator: {
baseURL: {
type: 'string',
required: true,
message: 'baseURL is required',
},
},
})
.define(async (ctx) => {
const { baseURL } = ctx.query;
const configRes = await ctx.call({ path: 'config', key: 'getTokenList' });
if (configRes.code !== 200) {
ctx.throw(configRes.code, configRes.message || 'Failed to get token list');
}
const tokenList: TokenCacheItem[] = configRes.body?.value || [];
const index = tokenList.findIndex((item) => item.baseURL === baseURL);
const token = index > -1 ? tokenList[index].token : '';
if (index > -1 && token) {
writeConfig({ token: tokenList[index].token });
ctx.body = {
baseURL: baseURL,
token: tokenList[index].token,
};
} else {
ctx.throw(404, 'Token not found');
}
})
.addTo(app);
app
.route({
path: 'config',
key: 'deleteToken',
description: 'Delete token',
validator: {
baseURL: {
type: 'string',
required: true,
message: 'baseURL is required',
},
},
})
.define(async (ctx) => {
const { baseURL } = ctx.query;
const config = await ctx.call({ path: 'config', key: 'getTokenList' });
if (config.code !== 200) {
ctx.throw(config.code, config.message || 'Failed to get token list');
}
const tokenList: TokenCacheItem[] = config.body?.value || [];
const index = tokenList.findIndex((item) => item.baseURL === baseURL);
if (index > -1) {
tokenList.splice(index, 1);
const res = await ctx.call({ path: 'config', key: 'setTokenList', payload: { data: tokenList } });
if (res.code === 200) {
ctx.body = res.body;
} else ctx.throw(res.code, res.message || 'Failed to delete token');
} else {
console.log('not has token', baseURL);
ctx.body = {
value: tokenList,
};
}
})
.addTo(app);

View File

@ -1 +1,2 @@
import './list.ts'
import './list.ts'
import './cache-token.ts'

View File

@ -0,0 +1,53 @@
import { getConfig } from '../module/get-config.ts';
import { runApp } from '../app-run.ts';
const getConfigList = async () => {
const res = await runApp({
path: 'config',
key: 'getTokenList',
});
console.log(res);
};
// getConfigList();
const setConfigList = async () => {
const config = getConfig();
const { baseURL, token } = config;
console.log(baseURL, token);
const res = await runApp({
path: 'config',
key: 'saveToken',
payload: {
baseURL: 'abc32',
token,
},
});
console.log(res);
};
// setConfigList();
const switchToken = async () => {
const res = await runApp({
path: 'config',
key: 'switchToken',
payload: {
baseURL: 'abc2',
},
});
console.log(res);
};
// switchToken();
const removeToken = async () => {
const res = await runApp({
path: 'config',
key: 'deleteToken',
payload: {
baseURL: 'abc32',
},
});
console.log(res);
};
removeToken();