feat: add query-login
This commit is contained in:
@@ -32,7 +32,7 @@ const downloadAppCommand = new Command('download')
|
||||
data.id = id;
|
||||
}
|
||||
const res = await queryApp(data);
|
||||
let registry = 'https://kevisual.xiongxiao.me';
|
||||
let registry = 'https://kevisual.cn';
|
||||
if (options?.registry) {
|
||||
registry = new URL(options.registry).origin;
|
||||
}
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
import './micro-app/index.ts';
|
||||
import './front-app/index.ts';
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
/**
|
||||
* 下载 app serve client的包的命令
|
||||
*/
|
||||
|
||||
import { chalk } from '@/module/chalk.ts';
|
||||
import { program, Command } from '../../../program.ts';
|
||||
import fs from 'fs';
|
||||
import { Readable } from 'stream';
|
||||
import * as tar from 'tar';
|
||||
import path from 'path';
|
||||
import { fileIsExist } from '@/uitls/file.ts';
|
||||
|
||||
// Utility function to convert a web ReadableStream to a Node.js Readable stream
|
||||
function nodeReadableStreamFromWeb(webStream: ReadableStream<Uint8Array>) {
|
||||
const reader = webStream.getReader();
|
||||
return new Readable({
|
||||
async read() {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
this.push(null);
|
||||
} else {
|
||||
this.push(Buffer.from(value));
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export const appCommand = new Command('micro-app').description('micro-app 命令').action(() => {
|
||||
console.log('micro-app');
|
||||
});
|
||||
|
||||
program.addCommand(appCommand);
|
||||
|
||||
// https://kevisual.xiongxiao.me/api/micro-app/download/file?notNeedToken=y&title=mark-0.0.2.tgz
|
||||
const downloadAppCommand = new Command('download')
|
||||
.description('下载 app serve client的包. \nmicro-app download -i mark-0.0.2.tgz -o test/mark.tgz -x test2')
|
||||
.option('-i, --id <id>', '下载 app serve client的包, id 或者title, mark-0.0.2.tgz')
|
||||
.option('-o, --output <output>', '下载 app serve client的包, 输出路径')
|
||||
.option('-x, --extract <extract>', '下载 app serve client的包, 解压, 默认解压到当前目录')
|
||||
.option('-r, --registry <registry>', '下载 app serve client的包, 使用私有源')
|
||||
.action(async (options) => {
|
||||
const id = options.id || '';
|
||||
if (!id) {
|
||||
console.error(chalk.red('id is required'));
|
||||
return;
|
||||
}
|
||||
let title = '';
|
||||
if (id.includes('.tgz')) {
|
||||
title = id;
|
||||
}
|
||||
let registry = '';
|
||||
if (options?.registry) {
|
||||
registry = new URL(options.registry).origin;
|
||||
} else {
|
||||
registry = 'https://kevisual.xiongxiao.me';
|
||||
}
|
||||
let curlUrl = `${registry}/api/micro-app/download/${id}?notNeedToken=y`;
|
||||
if (title) {
|
||||
curlUrl = `${registry}/api/micro-app/download/file?notNeedToken=y&title=${title}`;
|
||||
}
|
||||
console.log(chalk.blue('下载地址:'), curlUrl);
|
||||
fetch(curlUrl)
|
||||
.then(async (res) => {
|
||||
const contentDisposition = res.headers.get('content-disposition');
|
||||
let filename = ''; // Default filename
|
||||
|
||||
if (contentDisposition) {
|
||||
const match = contentDisposition.match(/filename="?(.+)"?/);
|
||||
if (match && match[1]) {
|
||||
filename = match[1].replace(/^"|"$/g, '');
|
||||
}
|
||||
}
|
||||
if (!filename) {
|
||||
console.log(chalk.red('下载失败: 没有找到下载文件, 请检查下载地址是否正确,或手动下载'));
|
||||
return;
|
||||
}
|
||||
const outputPath = options.output || filename;
|
||||
if (!fileIsExist(outputPath)) {
|
||||
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
||||
}
|
||||
const fileStream = fs.createWriteStream(outputPath);
|
||||
|
||||
if (res.body) {
|
||||
nodeReadableStreamFromWeb(res.body).pipe(fileStream);
|
||||
|
||||
fileStream.on('finish', async () => {
|
||||
console.log(chalk.green(`下载成功: ${outputPath}`));
|
||||
if (options.extract) {
|
||||
console.log(chalk.green(`解压: ${outputPath}`));
|
||||
const extractPath = path.join(process.cwd(), options.extract || '.');
|
||||
if (!fileIsExist(extractPath)) {
|
||||
fs.mkdirSync(extractPath, { recursive: true });
|
||||
}
|
||||
const fileInput = path.join(process.cwd(), outputPath);
|
||||
tar
|
||||
.extract({
|
||||
file: fileInput,
|
||||
cwd: extractPath,
|
||||
})
|
||||
.then((res) => {
|
||||
console.log(chalk.green(`解压成功: ${outputPath}`));
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(chalk.red(`解压失败: ${outputPath}, 请手动解压, tar -xvf ${outputPath}`));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
fileStream.on('error', (err) => {
|
||||
console.error(chalk.red('文件写入错误:', err));
|
||||
fileStream.close(); // Ensure the stream is closed on error
|
||||
});
|
||||
} else {
|
||||
console.error(chalk.red('下载失败: 无法获取文件流'));
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(chalk.red('下载请求失败:', err));
|
||||
});
|
||||
});
|
||||
|
||||
appCommand.addCommand(downloadAppCommand);
|
||||
@@ -3,13 +3,12 @@ import glob from 'fast-glob';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import FormData from 'form-data';
|
||||
import { getBaseURL, query } from '@/module/query.ts';
|
||||
import { getBaseURL, query, storage } from '@/module/query.ts';
|
||||
import { getConfig } from '@/module/index.ts';
|
||||
import inquirer from 'inquirer';
|
||||
import { packLib, unpackLib } from './publish.ts';
|
||||
import chalk from 'chalk';
|
||||
import { installDeps } from '@/uitls/npm.ts';
|
||||
|
||||
const command = new Command('deploy')
|
||||
.description('把前端文件传到服务器')
|
||||
.argument('<filePath>', 'Path to the file to be uploaded, filepath or directory') // 定义文件路径参数
|
||||
@@ -123,10 +122,11 @@ const uploadFiles = async (
|
||||
if (username) {
|
||||
form.append('username', username);
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(async (resolve) => {
|
||||
const _baseURL = getBaseURL();
|
||||
const url = new URL(_baseURL);
|
||||
console.log('upload url', url.hostname, url.protocol, url.port);
|
||||
const token = await storage.getItem('token');
|
||||
form.submit(
|
||||
{
|
||||
path: '/api/app/upload',
|
||||
@@ -135,7 +135,7 @@ const uploadFiles = async (
|
||||
port: url.port,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + config.token,
|
||||
Authorization: 'Bearer ' + token,
|
||||
...form.getHeaders(),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,49 +1,12 @@
|
||||
import { program, Command } from '@/program.ts';
|
||||
import { getConfig, writeConfig } from '@/module/get-config.ts';
|
||||
import { getBaseURL } from '@/module/query.ts';
|
||||
import { queryLogin, queryMe, switchOrg, switchMe } from '@/query/index.ts';
|
||||
import { getConfig } from '@/module/get-config.ts';
|
||||
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 config = getConfig();
|
||||
writeConfig({ ...config, token });
|
||||
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;
|
||||
};
|
||||
export const setTokenList = async (data: any[]) => {
|
||||
const res = await runApp({ path: 'config', key: 'setTokenList', payload: { data } });
|
||||
if (res.code !== 200) {
|
||||
console.log('set token list failed', res.message || '');
|
||||
}
|
||||
return res;
|
||||
};
|
||||
// 定义login命令,支持 `-u` 和 `-p` 参数来输入用户名和密码
|
||||
import { queryLogin, storage } from '@/module/query.ts';
|
||||
|
||||
/**
|
||||
* 定义login命令,支持 `-u` 和 `-p` 参数来输入用户名和密码
|
||||
*/
|
||||
const loginCommand = new Command('login')
|
||||
.description('Login to the application')
|
||||
.option('-u, --username <username>', 'Specify username')
|
||||
@@ -51,10 +14,9 @@ const loginCommand = new Command('login')
|
||||
.option('-f, --force', 'Force login')
|
||||
.option('-w, --web', 'Login on the web')
|
||||
.action(async (options) => {
|
||||
const config = getConfig();
|
||||
let { username, password } = options;
|
||||
if (options.web) {
|
||||
await loginInCommand(saveToken);
|
||||
await loginInCommand();
|
||||
return;
|
||||
}
|
||||
// 如果没有传递参数,则通过交互式输入
|
||||
@@ -77,7 +39,8 @@ const loginCommand = new Command('login')
|
||||
username = answers.username || username;
|
||||
password = answers.password || password;
|
||||
}
|
||||
if (config.token) {
|
||||
const token = storage.getItem('token');
|
||||
if (token) {
|
||||
const res = await showMe(false);
|
||||
if (res.code === 200) {
|
||||
const data = res.data;
|
||||
@@ -88,11 +51,11 @@ const loginCommand = new Command('login')
|
||||
}
|
||||
}
|
||||
|
||||
const res = await queryLogin(username, password);
|
||||
const res = await queryLogin.login({
|
||||
username,
|
||||
password,
|
||||
});
|
||||
if (res.code === 200) {
|
||||
const { token } = res.data;
|
||||
writeConfig({ ...config, token });
|
||||
saveToken(token);
|
||||
console.log('welcome', username);
|
||||
} else {
|
||||
console.log('登录失败', res.message || '');
|
||||
@@ -102,47 +65,19 @@ const loginCommand = new Command('login')
|
||||
program.addCommand(loginCommand);
|
||||
|
||||
const showMe = async (show = true) => {
|
||||
const me = await queryMe();
|
||||
const me = await queryLogin.getMe();
|
||||
if (show) {
|
||||
// save me to config
|
||||
const meSet = await runApp({ path: 'config', key: 'meSet', payload: { data: me.data } });
|
||||
if (me.code === 200) {
|
||||
console.log('Me', me.data);
|
||||
} else {
|
||||
const config = getConfig();
|
||||
console.log('Show Me failed', me.message);
|
||||
writeConfig({ ...config, token: '' });
|
||||
}
|
||||
console.log('Me', me.data);
|
||||
}
|
||||
return me;
|
||||
};
|
||||
|
||||
const switchOrgCommand = new Command('switch').argument('<username>', 'Switch to another organization or username').action(async (username) => {
|
||||
const config = getConfig();
|
||||
if (!config.token) {
|
||||
console.log('Please login first');
|
||||
return;
|
||||
}
|
||||
const meGet = await runApp({ path: 'config', key: 'meGet' });
|
||||
if (meGet.code !== 200) {
|
||||
console.log('Please login first');
|
||||
return;
|
||||
}
|
||||
const me = meGet.data?.value || {};
|
||||
if (me?.username === username) {
|
||||
// console.log('Already in', options);
|
||||
console.log('success switch to', username);
|
||||
return;
|
||||
}
|
||||
const res = await switchOrg(username);
|
||||
const res = await queryLogin.switchUser(username);
|
||||
if (res.code === 200) {
|
||||
const token = res.data.token;
|
||||
writeConfig({ ...config, token });
|
||||
console.log(`Switch ${username} Success`);
|
||||
saveToken(token);
|
||||
await showMe();
|
||||
console.log('success switch to', username);
|
||||
} else {
|
||||
console.log(`Switch ${username} Failed`, res.message || '');
|
||||
console.log('switch to', username, 'failed', res.message || '');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,42 +1,19 @@
|
||||
import { program as app, Command } from '@/program.ts';
|
||||
import { getConfig, query, writeConfig } from '@/module/index.ts';
|
||||
import { getConfig, writeConfig } from '@/module/index.ts';
|
||||
import { queryLogin, storage } from '@/module/query.ts';
|
||||
import inquirer from 'inquirer';
|
||||
import util from 'util';
|
||||
|
||||
import { saveToken, switchToken, deleteToken, getTokenList, setTokenList } from './login.ts';
|
||||
const token = new Command('token').description('show token').action(async () => {
|
||||
const config = getConfig();
|
||||
console.log('token', config.token);
|
||||
const token = storage.getItem('token');
|
||||
console.log('token', token);
|
||||
});
|
||||
const tokenList = new Command('list')
|
||||
.description('show token list')
|
||||
.option('-r --remove <number>', 'remove token by number')
|
||||
// .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;
|
||||
}
|
||||
const res = queryLogin.cache.cache.cacheData;
|
||||
console.log(util.inspect(res, { colors: true, depth: 4 }));
|
||||
});
|
||||
token.addCommand(tokenList);
|
||||
app.addCommand(token);
|
||||
@@ -101,7 +78,7 @@ const baseURL = new Command('baseURL')
|
||||
list = quineList(list);
|
||||
showList(list);
|
||||
writeConfig({ ...config, baseURLList: list });
|
||||
removeBase[0] && deleteToken(removeBase[0]);
|
||||
removeBase[0];
|
||||
return;
|
||||
}
|
||||
if (opts.set) {
|
||||
@@ -119,7 +96,6 @@ const baseURL = new Command('baseURL')
|
||||
} else {
|
||||
baseURL = opts.set;
|
||||
}
|
||||
baseURL && switchToken(baseURL);
|
||||
return;
|
||||
}
|
||||
if (opts.list) {
|
||||
@@ -128,12 +104,11 @@ const baseURL = new Command('baseURL')
|
||||
}
|
||||
if (opts.clear) {
|
||||
writeConfig({ ...config, baseURLList: [] });
|
||||
setTokenList([]);
|
||||
return;
|
||||
}
|
||||
if (!config.baseURL) {
|
||||
config = getConfig();
|
||||
writeConfig({ ...config, baseURL: 'https://kevisual.xiongxiao.me' });
|
||||
writeConfig({ ...config, baseURL: 'https://kevisual.cn' });
|
||||
config = getConfig();
|
||||
}
|
||||
console.log('current baseURL:', config.baseURL);
|
||||
@@ -159,7 +134,6 @@ const setBaseURL = new Command('set')
|
||||
baseURL = answers.baseURL;
|
||||
}
|
||||
writeConfig({ ...config, baseURL });
|
||||
baseURL && switchToken(baseURL);
|
||||
});
|
||||
|
||||
baseURL.addCommand(setBaseURL);
|
||||
|
||||
@@ -122,9 +122,8 @@ const npmrc = new Command('set')
|
||||
const npmrcContent =
|
||||
config?.npmrc ||
|
||||
`//npm.xiongxiao.me/:_authToken=\${ME_NPM_TOKEN}
|
||||
@abearxiong:registry=https://npm.pkg.github.com
|
||||
//registry.npmjs.org/:_authToken=\${NPM_TOKEN}
|
||||
@kevisual:registry=https://npm.xiongxiao.me`;
|
||||
`;
|
||||
const execPath = process.cwd();
|
||||
const npmrcPath = path.resolve(execPath, '.npmrc');
|
||||
let writeFlag = false;
|
||||
|
||||
@@ -1,46 +1,26 @@
|
||||
import MD5 from 'crypto-js/md5.js';
|
||||
import { getBaseURL, query } from '../query.ts';
|
||||
import { getBaseURL, queryLogin } from '../query.ts';
|
||||
import { chalk } from '../chalk.ts';
|
||||
import jsonwebtoken from 'jsonwebtoken';
|
||||
import { BaseLoad } from '@kevisual/load';
|
||||
import { getConfig, writeConfig } from '../get-config.ts';
|
||||
|
||||
type LoginWithWebOptions = {};
|
||||
export const loginWithWeb = async (opts?: LoginWithWebOptions) => {
|
||||
const baseURL = getBaseURL();
|
||||
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 config = await getConfig();
|
||||
|
||||
const url = `${baseURL}/api/router?path=user&key=webLogin&p&loginToken=${token}&sign=${sign}&randomId=${randomId}`;
|
||||
|
||||
console.log(chalk.blue(url));
|
||||
return {
|
||||
url,
|
||||
token,
|
||||
tokenSecret,
|
||||
};
|
||||
const res = queryLogin.loginWithWeb(baseURL, { MD5, jsonwebtoken });
|
||||
console.log(chalk.blue(res.url));
|
||||
return res;
|
||||
};
|
||||
|
||||
type PollLoginOptions = {
|
||||
tokenSecret: string;
|
||||
saveToken?: any;
|
||||
};
|
||||
export const pollLoginStatus = async (token: string, opts: PollLoginOptions) => {
|
||||
const load = new BaseLoad();
|
||||
load.load(
|
||||
async () => {
|
||||
const res = await query.post({
|
||||
path: 'user',
|
||||
key: 'checkLoginStatus',
|
||||
loginToken: token,
|
||||
});
|
||||
const res = await queryLogin.checkLoginStatus(token);
|
||||
return res;
|
||||
},
|
||||
{
|
||||
@@ -55,36 +35,21 @@ export const pollLoginStatus = async (token: string, opts: PollLoginOptions) =>
|
||||
timeout: 60 * 3 * 1000, // 5分钟超时
|
||||
});
|
||||
if (res.code === 200 && res.data?.code === 200) {
|
||||
const data = res.data?.data;
|
||||
try {
|
||||
const payload = jsonwebtoken.verify(data, opts.tokenSecret) as UserPayload;
|
||||
type UserPayload = {
|
||||
userToken: {
|
||||
token: string;
|
||||
expireTime: number;
|
||||
};
|
||||
user: {
|
||||
id: string;
|
||||
username: string;
|
||||
};
|
||||
};
|
||||
const userToken = payload.userToken;
|
||||
// console.log('token:\n\n', userToken);
|
||||
console.log(chalk.green('网页登录成功', payload?.user?.username));
|
||||
console.log(chalk.green('token:', userToken.token));
|
||||
await opts?.saveToken(userToken.token);
|
||||
console.log(chalk.green('网页登录成功'));
|
||||
return;
|
||||
} catch (error) {
|
||||
console.log(chalk.red('登录失败'), error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
console.log(chalk.red('登录失败'), res);
|
||||
};
|
||||
|
||||
export const loginInCommand = async (saveToken: any) => {
|
||||
const { url, token, tokenSecret } = await loginWithWeb();
|
||||
await pollLoginStatus(token, { tokenSecret, saveToken });
|
||||
return url;
|
||||
export const loginInCommand = async () => {
|
||||
const baseURL = getBaseURL();
|
||||
const res = queryLogin.loginWithWeb(baseURL, { MD5, jsonwebtoken });
|
||||
console.log(chalk.blue(res.url));
|
||||
await pollLoginStatus(res.token, { tokenSecret: res.tokenSecret });
|
||||
return res.url;
|
||||
};
|
||||
|
||||
// loginInCommand();
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Query } from '@kevisual/query/query';
|
||||
import { getConfig } from './get-config.ts';
|
||||
import { QueryLoginNode, storage } from '@kevisual/query-login/node';
|
||||
const config = getConfig();
|
||||
export const baseURL = config?.baseURL || 'https://envision.xiongxiao.me';
|
||||
export const baseURL = config?.baseURL || 'https://kevisual.cn';
|
||||
export { storage };
|
||||
export const getBaseURL = () => {
|
||||
if (typeof config?.dev === 'undefined') {
|
||||
return baseURL;
|
||||
@@ -23,10 +25,29 @@ export const query = new Query({
|
||||
|
||||
query.beforeRequest = async (config) => {
|
||||
if (config.headers) {
|
||||
const token = await getConfig()?.token;
|
||||
const token = await storage.getItem('token');
|
||||
if (token) {
|
||||
config.headers['Authorization'] = 'Bearer ' + token;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
};
|
||||
query.afterResponse = async (response, ctx) => {
|
||||
if (response.code === 401) {
|
||||
if (query.stop) {
|
||||
return {
|
||||
code: 500,
|
||||
message: '登录已过期',
|
||||
};
|
||||
}
|
||||
query.stop = true;
|
||||
const res = await queryLogin.afterCheck401ToRefreshToken(response, ctx);
|
||||
query.stop = false;
|
||||
return res;
|
||||
}
|
||||
return response as any;
|
||||
};
|
||||
export const queryLogin = new QueryLoginNode({
|
||||
query: query as any,
|
||||
onLoad: async () => {},
|
||||
});
|
||||
|
||||
@@ -1,208 +0,0 @@
|
||||
import { app } from '@/app.ts';
|
||||
import { Config } from './model/config.ts';
|
||||
import { getConfig, writeConfig } from '@/module/get-config.ts';
|
||||
import { queryMe } from '@/query/index.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 (token) {
|
||||
const config = getConfig();
|
||||
const resMe = await queryMe();
|
||||
if (resMe.code !== 200) {
|
||||
writeConfig({ ...config, token: '' });
|
||||
ctx.throw(resMe.code, resMe.message || 'cache token is invalid');
|
||||
}
|
||||
writeConfig({ ...config, token: token });
|
||||
ctx.body = {
|
||||
baseURL: baseURL,
|
||||
token: tokenList[index].token,
|
||||
};
|
||||
} else {
|
||||
writeConfig({ ...getConfig(), token: '' });
|
||||
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);
|
||||
@@ -1,2 +1 @@
|
||||
import './list.ts'
|
||||
import './cache-token.ts'
|
||||
import './list.ts'
|
||||
@@ -1,53 +0,0 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user