add install base
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
import { App } from '@kevisual/router';
|
||||
import { HttpsPem } from '@/module/assistant/https/sign.ts';
|
||||
// import { AssistantConfig } from '@/module/assistant/index.ts';
|
||||
import { AssistantInit } from '@/services/init/index.ts';
|
||||
export const configDir = AssistantInit.detectConfigDir();
|
||||
import { AssistantInit, parseHomeArg } from '@/services/init/index.ts';
|
||||
import { configDir as HomeConfigDir } from '@/module/assistant/config/index.ts';
|
||||
|
||||
const manualParse = parseHomeArg(HomeConfigDir);
|
||||
const _configDir = manualParse.configDir;
|
||||
export const configDir = AssistantInit.detectConfigDir(_configDir);
|
||||
export const assistantConfig = new AssistantInit({
|
||||
path: configDir,
|
||||
init: true,
|
||||
init: manualParse?.options?.help ? false : true,
|
||||
});
|
||||
|
||||
const httpsPem = new HttpsPem(assistantConfig);
|
||||
|
||||
@@ -66,3 +66,24 @@ appManagerCommand
|
||||
}
|
||||
console.log('Restart App:', appKey);
|
||||
});
|
||||
|
||||
const pageListCommand = new Command('page-list')
|
||||
.alias('pl')
|
||||
.option('-a, --all', '列出前端页面的所有信息')
|
||||
.description('列出安装的前端页面')
|
||||
.action(async (opts) => {
|
||||
const manager = new AssistantApp(assistantConfig);
|
||||
await manager.loadConfig();
|
||||
const showInfos = manager.pageList();
|
||||
if (opts.all) {
|
||||
console.log('Installed Pages:', showInfos);
|
||||
} else {
|
||||
console.log(
|
||||
'Installed Pages:',
|
||||
showInfos.map((item) => {
|
||||
return { app: item.app, user: item.user, version: item.version };
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
program.addCommand(pageListCommand);
|
||||
|
||||
@@ -7,8 +7,10 @@ const command = new Command('server')
|
||||
.option('-n, --name <name>', '服务名称')
|
||||
.option('-p, --port <port>', '服务端口')
|
||||
.option('-s, --start', '是否启动服务')
|
||||
.option('-i, --home', '是否以home方式运行')
|
||||
.action((options) => {
|
||||
const { port } = options;
|
||||
const [_interpreter, execPath] = process.argv;
|
||||
const shellCommands = [];
|
||||
if (options.daemon) {
|
||||
shellCommands.push('-d');
|
||||
@@ -22,10 +24,23 @@ const command = new Command('server')
|
||||
if (port) {
|
||||
shellCommands.push(`-p ${port}`);
|
||||
}
|
||||
console.log(`Assistant server shell command: asst-server ${shellCommands.join(' ')}`);
|
||||
const child = spawnSync('asst-server', shellCommands, {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
});
|
||||
if (options.home) {
|
||||
shellCommands.push('--home');
|
||||
}
|
||||
const basename = _interpreter.split('/').pop();
|
||||
|
||||
if (basename.includes('bun')) {
|
||||
console.log(`Assistant server shell command: bun src/run-server.ts server ${shellCommands.join(' ')}`);
|
||||
const child = spawnSync(_interpreter, ['src/run-server.ts', ...shellCommands], {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
});
|
||||
} else {
|
||||
console.log(`Assistant server shell command: asst-server ${shellCommands.join(' ')}`);
|
||||
const child = spawnSync('asst-server', shellCommands, {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
program.addCommand(command);
|
||||
|
||||
@@ -1,34 +1,9 @@
|
||||
import { AssistantConfig } from '@/module/assistant/index.ts';
|
||||
import { AssistantConfig, parseHomeArg } from '@/module/assistant/index.ts';
|
||||
|
||||
import { configDir as HomeConfigDir } from '@/module/assistant/config/index.ts';
|
||||
// 手动解析命令行参数
|
||||
function parseArgs(args: string[]) {
|
||||
const result: { root?: string; home?: boolean } = {};
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
// 处理 root 参数
|
||||
if (arg === 'root') {
|
||||
if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
|
||||
result.root = args[i + 1];
|
||||
i++; // 跳过下一个参数,因为它是值
|
||||
}
|
||||
}
|
||||
// 处理 home 参数
|
||||
if (arg === 'home') {
|
||||
result.home = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const options = parseArgs(args);
|
||||
let _configDir = undefined;
|
||||
if (options.home) {
|
||||
_configDir = HomeConfigDir;
|
||||
} else if (options.root) {
|
||||
_configDir = options.root;
|
||||
}
|
||||
const _configDir = parseHomeArg(HomeConfigDir).configDir;
|
||||
|
||||
// console.log('configDir', _configDir);
|
||||
// process.exit(0);
|
||||
export const configDir = AssistantConfig.detectConfigDir(_configDir);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { homedir } from 'os';
|
||||
import fs from 'fs';
|
||||
import { checkFileExists, createDir } from '../file/index.ts';
|
||||
import { ProxyInfo } from '../proxy/proxy.ts';
|
||||
|
||||
import dotenv from 'dotenv';
|
||||
/**
|
||||
* 助手配置文件路径, 全局配置文件目录
|
||||
*/
|
||||
@@ -18,7 +18,7 @@ export const initConfig = (configRootPath: string) => {
|
||||
const configDir = createDir(path.join(configRootPath, 'assistant-app'));
|
||||
const configPath = path.join(configDir, 'assistant-config.json');
|
||||
const pageConfigPath = path.join(configDir, 'assistant-page-config.json');
|
||||
const pageDir = createDir(path.join(configDir, 'page'));
|
||||
const pagesDir = createDir(path.join(configDir, 'pages'));
|
||||
const appsDir = createDir(path.join(configDir, 'apps'));
|
||||
const appsConfigPath = path.join(configDir, 'assistant-apps-config.json');
|
||||
const appPidPath = path.join(configDir, 'assistant-app.pid');
|
||||
@@ -43,7 +43,7 @@ export const initConfig = (configRootPath: string) => {
|
||||
/**
|
||||
* 应用目录, 前端应用目录
|
||||
*/
|
||||
pageDir,
|
||||
pagesDir,
|
||||
/**
|
||||
* 应用配置文件路径, assistant-page-config.json
|
||||
*/
|
||||
@@ -207,6 +207,20 @@ export class AssistantConfig {
|
||||
getAppList() {
|
||||
return this.getPageConfig().list;
|
||||
}
|
||||
getEnvConfig() {
|
||||
const envConfigPath = this.configPath.envConfigPath;
|
||||
if (!checkFileExists(envConfigPath)) {
|
||||
return {};
|
||||
}
|
||||
const envConfig = dotenv.parse(fs.readFileSync(envConfigPath));
|
||||
return envConfig;
|
||||
}
|
||||
setEnvConfig(config: string) {
|
||||
const envConfigPath = this.configPath.envConfigPath;
|
||||
fs.writeFileSync(envConfigPath, config);
|
||||
const envConfig = dotenv.parse(fs.readFileSync(envConfigPath));
|
||||
return envConfig;
|
||||
}
|
||||
/**
|
||||
* process.env.ASSISTANT_CONFIG_DIR || process.cwd()
|
||||
* configDir是助手配置文件目录,文件内部包函
|
||||
@@ -245,3 +259,58 @@ export class AssistantConfig {
|
||||
type AppConfig = {
|
||||
list: any[];
|
||||
};
|
||||
export function parseArgs(args: string[]) {
|
||||
const result: { root?: string; home?: boolean; help?: boolean } = {};
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
// 处理 root 参数
|
||||
if (arg === '--root') {
|
||||
if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
|
||||
result.root = args[i + 1];
|
||||
i++; // 跳过下一个参数,因为它是值
|
||||
}
|
||||
}
|
||||
// 处理 home 参数
|
||||
if (arg === '--home') {
|
||||
result.home = true;
|
||||
}
|
||||
if (arg === '--help' || arg === '-h') {
|
||||
result.help = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* 手动解析命令行参数
|
||||
* @param homedir
|
||||
* @returns
|
||||
*/
|
||||
export const parseHomeArg = (homedir?: string) => {
|
||||
const args = process.argv.slice(2);
|
||||
const options = parseArgs(args);
|
||||
let _configDir = undefined;
|
||||
if (options.home && homedir) {
|
||||
_configDir = homedir;
|
||||
} else if (options.root) {
|
||||
_configDir = options.root;
|
||||
}
|
||||
return {
|
||||
options,
|
||||
configDir: _configDir,
|
||||
};
|
||||
};
|
||||
|
||||
export const parseHelpArg = () => {
|
||||
const args = process.argv.slice(2);
|
||||
const options = parseArgs(args);
|
||||
return !!options?.help;
|
||||
};
|
||||
|
||||
export const parseIfJson = (content: string) => {
|
||||
try {
|
||||
return JSON.parse(content);
|
||||
} catch (error) {
|
||||
console.error('parseIfJson', error);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -34,6 +34,7 @@ export class HttpsPem {
|
||||
return pemDir;
|
||||
}
|
||||
getCert() {
|
||||
if (!this.assistantConfig.init) return;
|
||||
const pemDir = this.getPemDir();
|
||||
const pemPath = {
|
||||
key: path.join(pemDir, 'https-private-key.pem'),
|
||||
|
||||
@@ -1,19 +1,41 @@
|
||||
import { Manager } from '@kevisual/local-app-manager/manager';
|
||||
import { AssistantConfig } from '@/module/assistant/index.ts';
|
||||
import type { AssistantConfig } from '@/module/assistant/index.ts';
|
||||
import { parseIfJson } from '@/module/assistant/index.ts';
|
||||
import path from 'node:path';
|
||||
|
||||
import fs from 'node:fs';
|
||||
import glob from 'fast-glob';
|
||||
export class AssistantApp extends Manager {
|
||||
config: AssistantConfig;
|
||||
|
||||
pagesPath: string;
|
||||
constructor(config: AssistantConfig) {
|
||||
config.checkMounted();
|
||||
const appsPath = config?.configPath?.appsDir || path.join(process.cwd(), 'apps');
|
||||
const pagesPath = config?.configPath?.pagesDir || path.join(process.cwd(), 'pages');
|
||||
const appsConfigPath = config.configPath?.appsConfigPath;
|
||||
const configFimename = path.basename(appsConfigPath || '');
|
||||
super({
|
||||
appsPath,
|
||||
configFilename: configFimename,
|
||||
});
|
||||
this.pagesPath = pagesPath;
|
||||
this.config = config;
|
||||
}
|
||||
pageList() {
|
||||
const pages = glob.sync('*/*/package.json', {
|
||||
cwd: this.pagesPath,
|
||||
onlyFiles: true,
|
||||
});
|
||||
const pagesParse = pages.map((page) => {
|
||||
const [user, app] = page.split('/');
|
||||
const contentStr = fs.readFileSync(path.join(this.pagesPath, page), 'utf-8');
|
||||
const content = parseIfJson(contentStr);
|
||||
return {
|
||||
user,
|
||||
app,
|
||||
version: content?.version,
|
||||
content,
|
||||
};
|
||||
});
|
||||
return pagesParse;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,6 @@
|
||||
export class Logger {
|
||||
level: string;
|
||||
constructor(level: string) {
|
||||
this.level = level;
|
||||
}
|
||||
info(message: string, data?: any, ...args: any[]) {
|
||||
if (this.level === 'info') {
|
||||
console.log(`INFO: ${message}`, data, ...args);
|
||||
}
|
||||
}
|
||||
error(message: string, data?: any, ...args: any[]) {
|
||||
if (this.level === 'error') {
|
||||
console.error(`ERROR: ${message}`, data, ...args);
|
||||
}
|
||||
}
|
||||
warn(message: string, data?: any, ...args: any[]) {
|
||||
if (this.level === 'warn') {
|
||||
console.warn(`WARN: ${message}`, data, ...args);
|
||||
}
|
||||
}
|
||||
debug(message: string, data?: any, ...args: any[]) {
|
||||
if (this.level === 'debug') {
|
||||
console.debug(`DEBUG: ${message}`, data, ...args);
|
||||
}
|
||||
}
|
||||
log(message: string, ...args: any[]) {
|
||||
if (this.level === 'log') {
|
||||
console.log(`LOG: ${message}`, ...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
const logger = new Logger(process.env.LOG_LEVEL || 'info');
|
||||
import { Logger } from '@kevisual/logger';
|
||||
const level = process.env.LOG_LEVEL || 'info';
|
||||
const logger = new Logger({ level: level as any });
|
||||
|
||||
export const console = {
|
||||
log: logger.info,
|
||||
|
||||
@@ -16,14 +16,6 @@ const ls = new Command('ls').description('List files in the current directory').
|
||||
});
|
||||
program.addCommand(ls);
|
||||
|
||||
const home = new Command('home').description('启动以全局目录').action(() => {}); // @ts-ignore
|
||||
program.addCommand(home);
|
||||
const root = new Command('root')
|
||||
.argument('<path>', '自定义启动路径')
|
||||
.description('自定义启动路径')
|
||||
.action(() => {}); // @ts-ignore
|
||||
program.addCommand(root);
|
||||
|
||||
export { program, Command, assistantConfig };
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@ app
|
||||
.route({
|
||||
path: 'config',
|
||||
description: '获取配置',
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
ctx.body = assistantConfig.getCacheAssistantConfig();
|
||||
@@ -17,6 +18,7 @@ app
|
||||
path: 'config',
|
||||
key: 'set',
|
||||
description: '设置配置',
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const { data } = ctx.query;
|
||||
|
||||
@@ -1,2 +1,13 @@
|
||||
import './config/index.ts'
|
||||
import './shop-install/index.ts'
|
||||
import { app } from '../app.ts';
|
||||
import './config/index.ts';
|
||||
import './shop-install/index.ts';
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'auth',
|
||||
id: 'auth',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
//
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
@@ -23,6 +23,7 @@ app
|
||||
.route({
|
||||
path: 'shop',
|
||||
key: 'list-installed',
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
// https://localhost:51015/client/router?path=shop&key=list-installed
|
||||
@@ -35,6 +36,7 @@ app
|
||||
.route({
|
||||
path: 'shop',
|
||||
key: 'install',
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
// https://localhost:51015/client/router?path=shop&key=install
|
||||
@@ -51,6 +53,7 @@ app
|
||||
.route({
|
||||
path: 'shop',
|
||||
key: 'uninstall',
|
||||
middleware: ['auth'],
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
// https://localhost:51015/client/router?path=shop&key=uninstall
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { runServer } from "./server.ts";
|
||||
import { runParser } from './server.ts';
|
||||
|
||||
runServer();
|
||||
runParser(process.argv);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { app } from './app.ts';
|
||||
import { app, assistantConfig } from './app.ts';
|
||||
import { proxyRoute, proxyWs } from './services/proxy/proxy-page-index.ts';
|
||||
import './routes/index.ts';
|
||||
|
||||
import getPort, { portNumbers } from 'get-port';
|
||||
import { program } from 'commander';
|
||||
import { spawnSync } from 'child_process';
|
||||
import chalk from 'chalk';
|
||||
export const runServer = async (port?: number) => {
|
||||
let _port: number | undefined;
|
||||
if (port) {
|
||||
@@ -39,6 +40,7 @@ program
|
||||
.option('-n, --name <name>', '服务名称', 'assistant-server')
|
||||
.option('-p, --port <port>', '服务端口')
|
||||
.option('-s, --start', '是否启动服务')
|
||||
.option('-i, --home', 'home目录')
|
||||
.action(async (options) => {
|
||||
// console.log('当前执行路径:', execPath, inte);
|
||||
if (options.daemon) {
|
||||
@@ -49,6 +51,9 @@ program
|
||||
if (port) {
|
||||
pm2Command += ` -p ${port}`;
|
||||
}
|
||||
if (options.home) {
|
||||
pm2Command += ` --home`;
|
||||
}
|
||||
const result = spawnSync(pm2Command, {
|
||||
shell: true,
|
||||
stdio: 'inherit',
|
||||
@@ -59,7 +64,7 @@ program
|
||||
}
|
||||
console.log('以守护进程方式运行');
|
||||
} else if (options.start) {
|
||||
console.log('启动服务');
|
||||
console.log('启动服务', chalk.green(assistantConfig.configDir));
|
||||
const server = await runServer(options.port);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -57,7 +57,7 @@ export class AppDownload {
|
||||
const configDir = this.config.configDir;
|
||||
this.config?.checkMounted();
|
||||
const appsDir = this.config.configPath?.appsDir;
|
||||
const pageDir = this.config.configPath?.pageDir;
|
||||
const pagesDir = this.config.configPath?.pagesDir;
|
||||
if (!id) {
|
||||
throw new Error('应用名称不能为空');
|
||||
}
|
||||
@@ -69,7 +69,7 @@ export class AppDownload {
|
||||
}
|
||||
const appName = opts?.appName || id.split('/').pop();
|
||||
if (type === 'web') {
|
||||
args.push('-o', pageDir);
|
||||
args.push('-o', pagesDir);
|
||||
} else if (type === 'app') {
|
||||
args.push('-o', path.join(appsDir, appName));
|
||||
} else {
|
||||
@@ -96,7 +96,7 @@ export class AppDownload {
|
||||
const appName = opts?.appName || id.split('/').pop();
|
||||
this.config?.checkMounted();
|
||||
const appsDir = this.config.configPath?.appsDir;
|
||||
const pageDir = this.config.configPath?.pageDir;
|
||||
const pagesDir = this.config.configPath?.pagesDir;
|
||||
if (!id) {
|
||||
throw new Error('应用名称不能为空');
|
||||
}
|
||||
@@ -104,7 +104,7 @@ export class AppDownload {
|
||||
let isDelete = false;
|
||||
if (type === 'web') {
|
||||
// 直接删除路径就行
|
||||
const pagePath = path.join(pageDir, id);
|
||||
const pagePath = path.join(pagesDir, id);
|
||||
deletePath = pagePath;
|
||||
} else if (type === 'app') {
|
||||
const appPath = path.join(appsDir, appName);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { checkFileExists, AssistantConfig, AssistantConfigData } from '@/module/assistant/index.ts';
|
||||
import { checkFileExists, AssistantConfig, AssistantConfigData, parseHomeArg, parseHelpArg } from '@/module/assistant/index.ts';
|
||||
import { chalk } from '@/module/chalk.ts';
|
||||
import { HttpsPem } from '@/module/assistant/https/sign.ts';
|
||||
export { parseHomeArg, parseHelpArg };
|
||||
export type AssistantInitOptions = {
|
||||
path?: string;
|
||||
init?: boolean;
|
||||
@@ -30,7 +31,8 @@ export class AssistantInit extends AssistantConfig {
|
||||
super.init();
|
||||
} else {
|
||||
super.init();
|
||||
console.log(chalk.yellow('助手路径已存在'));
|
||||
const assistantConfig = this;
|
||||
console.log(chalk.yellow('助手路径已存在'), chalk.green(assistantConfig.configDir));
|
||||
}
|
||||
this.createAssistantConfig();
|
||||
this.createEnvConfig();
|
||||
|
||||
@@ -43,7 +43,7 @@ export class LocalProxy {
|
||||
}
|
||||
}
|
||||
init() {
|
||||
const frontAppDir = this.assistantConfig.configPath?.pageDir;
|
||||
const frontAppDir = this.assistantConfig.configPath?.pagesDir;
|
||||
if (frontAppDir) {
|
||||
const userList = fs.readdirSync(frontAppDir);
|
||||
const localProxyProxyList: ProxyType[] = [];
|
||||
|
||||
@@ -8,7 +8,7 @@ const localProxy = new LocalProxy({
|
||||
});
|
||||
export const proxyRoute = async (req: http.IncomingMessage, res: http.ServerResponse) => {
|
||||
const _assistantConfig = assistantConfig.getCacheAssistantConfig();
|
||||
const appDir = assistantConfig.configPath?.pageDir;
|
||||
const appDir = assistantConfig.configPath?.pagesDir;
|
||||
const url = new URL(req.url, 'http://localhost');
|
||||
const pathname = url.pathname;
|
||||
if (pathname === '/' && _assistantConfig?.home) {
|
||||
|
||||
Reference in New Issue
Block a user