clear code

This commit is contained in:
熊潇 2025-04-03 01:08:37 +08:00
parent aadd8266b1
commit 43ce37b1ce
21 changed files with 269 additions and 1868 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@kevisual/envision-cli",
"version": "0.0.35",
"version": "0.0.37",
"description": "envision command tools",
"main": "dist/app.mjs",
"type": "module",
@ -40,10 +40,9 @@
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-replace": "^6.0.2",
"@rollup/plugin-typescript": "^12.1.2",
"@types/crypto-js": "^4.2.2",
"@types/jsonwebtoken": "^9.0.9",
"@types/node": "^22.13.14",
"@types/node": "^22.13.17",
"chalk": "^5.4.1",
"commander": "^13.1.0",
"fast-glob": "^3.3.3",
@ -51,17 +50,12 @@
"form-data": "^4.0.2",
"ignore": "^7.0.3",
"inquirer": "^12.5.0",
"rimraf": "^6.0.1",
"rollup": "^4.38.0",
"rollup-plugin-dts": "^6.2.1",
"rollup": "^4.39.0",
"rollup-plugin-esbuild": "^6.2.1",
"tar": "^7.4.3",
"tslib": "^2.8.1",
"tsup": "^8.4.0",
"typescript": "^5.8.2"
},
"resolutions": {
"picomatch": "^4"
"@kevisual/load": "^0.0.6",
"crypto-js": "^4.2.0",
"jsonwebtoken": "^9.0.2"
},
"engines": {
"node": ">=22.0.0"
@ -69,14 +63,5 @@
"publishConfig": {
"access": "public"
},
"dependencies": {
"@kevisual/load": "^0.0.6",
"@kevisual/router": "^0.0.9",
"crypto-js": "^4.2.0",
"jsonwebtoken": "^9.0.2",
"pg-hstore": "^2.3.4",
"sequelize": "^6.37.7",
"sqlite3": "^5.1.7",
"vite": "^6.2.3"
}
"dependencies": {}
}

1651
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,4 @@
// rollup.config.js
import typescript from '@rollup/plugin-typescript';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
@ -11,7 +9,7 @@ import esbuild from 'rollup-plugin-esbuild';
import alias from '@rollup/plugin-alias';
import replace from '@rollup/plugin-replace';
const pkgs = JSON.parse(fs.readFileSync('./package.json', 'utf-8'));
const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8'));
/**
* @type {import('rollup').RollupOptions}
*/
@ -26,7 +24,7 @@ const config = {
plugins: [
replace({
preventAssignment: true, // 防止意外赋值
VERSION: JSON.stringify(pkgs.version || '1.0.0'),
VERSION: JSON.stringify(pkg.version || '1.0.0'),
}),
alias({
// only esbuild needs to be configured
@ -66,15 +64,7 @@ const config = {
tsconfig: 'tsconfig.json',
}),
json(),
// typescript({
// allowImportingTsExtensions: true,
// noEmit: true,
// // 不生成声明文件
// declaration: false,
// }), // 使用 @rollup/plugin-typescript 处理 TypeScript 文件
],
// 将 sqlite3 作为外部依赖
external: ['sqlite3', 'sequelize', 'vite', 'sequelize', '@kevisual/router', 'ioredis', 'socket.io', 'minio'],
};
export default [config];

View File

@ -1,30 +0,0 @@
import { App } from '@kevisual/router';
import { app } from './app.ts';
type Message = {
path: string;
key?: string;
payload?: any;
};
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 };
}
};
export const loadApp = async (mainApp: App) => {
mainApp.importApp(app);
};

View File

@ -1,54 +0,0 @@
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';
export { sequelize };
export const app = new App({
serverOptions: {
cors: {
origin: '*', // 允许所有来源
},
httpType: 'https',
},
});
// 处理进程退出或中断信号,确保删除 pid 文件
const cleanUp = () => {
const pidList = getPidList();
const findPid = pidList.find((item) => Number(item.pid) === process.pid);
// if (checkFileExists(pidFilePath)) {
// const pid = fs.readFileSync(pidFilePath, 'utf-8');
// if (Number(pid) === process.pid) {
// fs.unlinkSync(pidFilePath);
// console.log('server id', process.pid, 'is exit');
// }
// }
if (findPid) {
console.log('server id', process.pid, 'is exit');
try {
fs.unlinkSync(findPid.file);
} catch (e) {
console.log('unlinkSync error', findPid);
}
}
process.exit(0); // 退出进程
};
// 当进程收到以下信号时,删除 pid 文件
process.on('SIGINT', cleanUp); // 例如 Ctrl+C
process.on('SIGTERM', cleanUp); // 终止信号
process.on('exit', cleanUp); // 进程退出
export const createApp = async () => {
if (checkFileExists(pidFilePath)) {
const pid = fs.readFileSync(pidFilePath, 'utf-8');
console.log('服务已经启动,请勿重复启动。', 'server id', pid);
return;
}
fs.writeFileSync(pidFilePath, process.pid.toString());
app.listen(21015, () => {
console.log('Server is running on port 21015', 'https://localhost:21015/api/router', 'server id', process.pid);
});
// import('./route.ts');
};

View File

@ -119,3 +119,15 @@ const command = new Command('me')
});
program.addCommand(command);
const logoutCommand = new Command('logout').description('退出登陆').action(async () => {
try {
await queryLogin.logout();
storage.removeItem('token');
console.log('退出成功');
} catch (error) {
console.log('退出失败', error);
}
});
program.addCommand(logoutCommand);

View File

@ -1,11 +0,0 @@
import { program as app, Command } from '@/program.ts';
import { getConfig, writeConfig } from '@/module/index.ts';
const command = new Command('logout')
.description('')
.action(async () => {
const config = getConfig();
writeConfig({ ...config, token: '' });
});
app.addCommand(command);

View File

@ -13,7 +13,7 @@ const tokenList = new Command('list')
// .option('-r --remove <number>', 'remove token by number')
.action(async (opts) => {
console.log('show token list');
queryLogin.cacheStore.init()
queryLogin.cacheStore.init();
// const res = await queryLogin.cacheStore.cache.get('token');
console.log(queryLogin.cacheStore.cacheData);
// console.log(util.inspect(res, { colors: true, depth: 4 }));
@ -33,6 +33,11 @@ const baseURL = new Command('baseURL')
.action(async (opts) => {
let config = getConfig();
let list = (config.baseURLList as Array<string>) || [];
if (!config.baseURL) {
list = ['https://kevisual.cn'];
writeConfig({ ...config, baseURL: 'https://kevisual.cn', baseURLList: list });
config = getConfig();
}
const quineList = (list: string[]) => {
const newList = new Set(list);
return Array.from(newList);

View File

@ -3,7 +3,6 @@ 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);
@ -58,45 +57,3 @@ const command = new Command('service')
});
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

@ -1,40 +0,0 @@
import { program as app, Command } from '@/program.ts';
import { getConfig, writeConfig, pidFilePath, checkFileExists } from '@/module/index.ts';
import { createApp } from '@/app.ts';
import fs from 'fs';
const command = new Command('serve')
.description('serve manager')
.option('-e, --exit', '退出进程')
.option('-s, --start', '启动进程')
.action(async (options) => {
console.log('options', options);
if (options.exit) {
if (checkFileExists(pidFilePath)) {
console.log('服务已经启动, 准备退出');
const pid = fs.readFileSync(pidFilePath, 'utf-8');
// 通知进程退出
process.kill(Number(pid), 'SIGTERM');
setTimeout(() => {
if (checkFileExists(pidFilePath)) {
// 强制删除 pid 文件
fs.unlinkSync(pidFilePath);
}
}, 2000);
return;
}
}
if (options.start) {
const config = getConfig();
await createApp();
}
});
app.addCommand(command);
const start = new Command('start').description('start serve').action(async () => {
const config = getConfig();
await createApp();
});
app.addCommand(start);

View File

@ -1,71 +0,0 @@
import { program as app, Command } from '@/program.ts';
import { getConfig, writeConfig, checkFileExists, query } from '@/module/index.ts';
import fs from 'fs';
import path from 'path';
import { startContainerServer } from '@/module/run-vite.ts';
// web 开发模块
const command = new Command('web')
.description('web dev manager')
.option('-e, --exit', '退出进程')
.option('-s, --start', '启动进程')
.action(async (options) => {
const { exit, start } = options || {};
});
app.addCommand(command);
// container 开发模块
const container = new Command('container')
.description('container manager')
.option('-d, --container <id>', '下载镜像')
.option('-f, --force', '下载镜像')
.option('-u, --upload <id>', '上传镜像')
.action(async (options) => {
const { container, upload, force } = options || {};
const config = getConfig();
const workdir = config.workdir;
if (!workdir) {
console.log('请先配置工作目录');
return;
}
if (!config.token) {
console.log('请先登录');
return;
}
// 32210aa6-3d5a-4687-b769-ae4e8137ec1e
if (container) {
console.log('下载镜像', container);
const res = await query.post({ path: 'container', key: 'get', id: container });
if (res.code !== 200) {
console.log('error', res.message || '');
return;
}
await startContainerServer(res.data, force);
}
if (upload) {
console.log('上传镜像', upload);
const directory = path.join(workdir, 'container', upload);
if (!checkFileExists(directory)) {
console.log('文件夹不存在');
return;
}
const code = fs.readFileSync(path.join(directory, 'index.js'), 'utf-8');
if (!code) {
console.log('文件不能为空');
return;
}
const res = await query.post({
path: 'container', //
key: 'update',
data: { id: upload, code },
});
if (res.code !== 200) {
console.log('error', res.message || '');
console.log(res);
return;
}
console.log('上传成功');
}
});
app.addCommand(container);

View File

@ -1,11 +1,8 @@
import { program } from '@/program.ts';
import './command/login.ts';
import './command/logout.ts';
import './command/ls-token.ts';
import './command/deploy.ts';
import './command/serve.ts';
import './command/config.ts';
import './command/web.ts';
import './command/router.ts';
import './command/npm.ts';
import './command/publish.ts';

View File

@ -1,5 +1,6 @@
import path from 'path';
import fs from 'fs';
import { storage } from '../query.ts';
type DownloadTask = {
downloadPath: string;
@ -62,8 +63,13 @@ export const installApp = async (app: Package, opts: InstallAppOpts = {}) => {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
console.log('downloadUrl', downloadUrl);
const res = await fetch(downloadUrl);
console.log('downloadUrwl', downloadUrl);
const token = process.env.KEVISUAL_TOKEN || storage.getItem('token');
const fetchURL = new URL(downloadUrl);
if (token) {
fetchURL.searchParams.set('token', token);
}
const res = await fetch(fetchURL.toString());
const blob = await res.blob();
fs.writeFileSync(downloadPath, Buffer.from(await blob.arrayBuffer()));
}

View File

@ -5,20 +5,6 @@ import fs from 'fs';
export const envisionPath = path.join(os.homedir(), '.config', 'envision');
const configPath = path.join(os.homedir(), '.config', 'envision', 'config.json');
export const pidFilePath = path.join(envisionPath, 'app.pid');
export const dbPath = path.join(envisionPath, 'db.sqlite');
const envisionPidDir = path.join(envisionPath);
export const getPidList = () => {
const files = fs.readdirSync(envisionPidDir);
const pidFiles = files.filter((file) => file.endsWith('.pid'));
return pidFiles.map((file) => {
const pid = fs.readFileSync(path.join(envisionPidDir, file), 'utf-8');
return { pid, file: path.join(envisionPidDir, file) };
});
};
export const writeVitePid = (pid: number) => {
fs.writeFileSync(path.join(envisionPath, `vite-${pid}.pid`), pid.toString());
};
export const checkFileExists = (filePath: string) => {
try {
fs.accessSync(filePath, fs.constants.F_OK);
@ -39,6 +25,9 @@ export const getConfig = () => {
return {};
}
}
writeConfig({
baseURL: 'https://kevisual.cn',
});
return {};
};

View File

@ -25,7 +25,11 @@ export const query = new Query({
query.beforeRequest = async (config) => {
if (config.headers) {
const token = await storage.getItem('token');
let token = process.env.KEVISUAL_TOKEN;
if (!token) {
token = await storage.getItem('token');
}
if (token) {
config.headers['Authorization'] = 'Bearer ' + token;
}

View File

@ -1,79 +0,0 @@
import fs from 'fs';
import path from 'path';
import { createServer } from 'vite';
import { checkFileExists, getConfig, writeVitePid } from './index.ts';
export const runVite = async (entry: string) => {
const entryDir = path.dirname(entry);
const server = await createServer({
// Vite 配置选项
root: entryDir,
server: {
port: 7101, // 可以根据需要设置端口
host: '0.0.0.0',
},
});
await server.listen();
console.log('Vite server is running at:', server.config.server.port);
};
const template = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="https://envision.xiongxiao.me/resources/root/avatar.png"/>
<title>Container Develop</title>
<style>
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
body {
font-size: 16px;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="module">
import { ContainerOne } from 'https://kevisual.xiongxiao.me/system/lib/container.js'
import { render, unmount } from './index.js'
const container = new ContainerOne({
root: '#root',
});
container.renderOne({
code: {render, unmount}
});
</script>
</body>
</html>`;
export const startContainerServer = async (container: any, force: boolean) => {
const { id, code } = container;
const config = getConfig();
const workdir = config.workdir;
if (!workdir) {
console.log('请先配置工作目录');
return;
}
if (!config.token) {
console.log('请先登录');
return;
}
const directory = path.join(workdir, 'container', id);
if (!checkFileExists(directory) || force) {
fs.mkdirSync(directory, { recursive: true });
fs.writeFileSync(path.join(directory, 'index.js'), code);
fs.writeFileSync(path.join(directory, 'index.html'), template);
} else {
console.log('文件夹已存在');
}
await runVite(path.join(directory, 'index.html'));
console.log('container server is running at:', 'http://localhost:7101');
console.log('pid:', process.pid);
writeVitePid(process.pid);
};

View File

@ -1,16 +0,0 @@
import { Sequelize } from 'sequelize';
import { dbPath } from './get-config.ts';
// connect to db
export const sequelize = new Sequelize({
dialect: 'sqlite',
// storage: 'db.sqlite',
storage: dbPath,
// logging: false,
});
sequelize
.authenticate({ logging: false })
.then(() => {})
.catch((err) => {
console.error('Unable to connect to the database:', err);
});

View File

@ -1 +0,0 @@
export * from './user/login.ts';

View File

@ -1,40 +0,0 @@
import { query } from '@/module/query.ts';
export const queryLogin = async (username: string, password: string) => {
return await query.post({
path: 'user',
key: 'login',
payload: {
username,
password,
loginType: 'plugin',
},
});
};
export const queryMe = async () => {
return await query.post({
path: 'user',
key: 'me',
});
};
export const switchOrg = async (username) => {
return await query.post({
path: 'user',
key: 'switchOrg',
data: {
loginType: 'plugin',
username,
},
});
};
export const switchMe = async () => {
return await query.post({
path: 'user',
key: 'switchOrg',
data: {
type: 'user',
},
});
};

View File

@ -1 +0,0 @@
import './route/system-config/index.ts';

@ -1 +1 @@
Subproject commit 98c8a2ad86331566058ca7cc12df5ee2b406fa2f
Subproject commit f1024941eda4162b0ef5f9a4cd3b13c6645c7974