update: 更新light-code部分的代码模块
This commit is contained in:
@@ -70,6 +70,20 @@ appManagerCommand
|
||||
console.log('Restart App:', appKey);
|
||||
});
|
||||
|
||||
appManagerCommand
|
||||
.command('reload')
|
||||
.description('重载配置项,对路径的app的内容变更有效')
|
||||
.argument('<app-key-name>', '应用的 key 名称')
|
||||
.action(async (appKey: string) => {
|
||||
const manager = new AssistantApp(assistantConfig);
|
||||
try {
|
||||
await manager.loadConfig();
|
||||
await manager.reload(appKey);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
console.log('Reload App:', appKey);
|
||||
});
|
||||
appManagerCommand
|
||||
.command('delete')
|
||||
.alias('del')
|
||||
|
||||
@@ -19,6 +19,7 @@ export class HttpsPem {
|
||||
assistantConfig: AssistantConfig;
|
||||
key: string;
|
||||
cert: string;
|
||||
isHttps = false;
|
||||
constructor(assistantConfig: AssistantConfig) {
|
||||
this.assistantConfig = assistantConfig;
|
||||
this.#initKeyCert();
|
||||
@@ -32,6 +33,7 @@ export class HttpsPem {
|
||||
console.log(chalk.yellow('当前配置文件 https.type 不是 https, 不使用证书'));
|
||||
return;
|
||||
}
|
||||
this.isHttps = true;
|
||||
if (config.https.keyPath) {
|
||||
const keyPath = config.https.keyPath;
|
||||
const certPath = config.https.certPath;
|
||||
@@ -45,6 +47,7 @@ export class HttpsPem {
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!this.isHttps) return;
|
||||
const { key, cert } = this.getCert();
|
||||
this.key = key;
|
||||
this.cert = cert;
|
||||
|
||||
6
assistant/src/module/cache/auth.ts
vendored
Normal file
6
assistant/src/module/cache/auth.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import { LRUCache } from 'lru-cache'
|
||||
|
||||
export const authCache = new LRUCache<string, any>({
|
||||
max: 10000, // 最大缓存数量
|
||||
ttl: 1000 * 60 * 60 * 24 * 7, // 缓存过期时间,单位为毫秒,这里设置为7天
|
||||
});
|
||||
149
assistant/src/module/light-code/run.ts
Normal file
149
assistant/src/module/light-code/run.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
import { fork } from 'child_process'
|
||||
import fs from 'fs';
|
||||
|
||||
export const fileExists = (path: string): boolean => {
|
||||
try {
|
||||
fs.accessSync(path, fs.constants.F_OK);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export type RunCodeParams = {
|
||||
path?: string;
|
||||
key?: string;
|
||||
payload?: string;
|
||||
[key: string]: any
|
||||
}
|
||||
type RunCodeOptions = {
|
||||
timeout?: number; // 超时时间,单位毫秒
|
||||
[key: string]: any
|
||||
}
|
||||
type RunCode = {
|
||||
// 调用进程的功能
|
||||
success?: boolean
|
||||
data?: {
|
||||
// 调用router的结果
|
||||
code?: number
|
||||
data?: any
|
||||
message?: string
|
||||
[key: string]: any
|
||||
};
|
||||
error?: any
|
||||
timestamp?: string
|
||||
[key: string]: any
|
||||
}
|
||||
export const runCode = async (tsPath: string, params: RunCodeParams = {}, opts?: RunCodeOptions): Promise<RunCode> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (fileExists(tsPath) === false) {
|
||||
resolve({
|
||||
success: false,
|
||||
error: `文件不存在: ${tsPath}`
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const timeoutMs = opts?.timeout || 30000; // 默认30秒超时
|
||||
|
||||
let child
|
||||
let resolved = false // 防止多次 resolve
|
||||
let isSendParam = false;
|
||||
let timeout: NodeJS.Timeout
|
||||
|
||||
const cleanup = () => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
if (child && !child.killed) {
|
||||
child.kill()
|
||||
}
|
||||
}
|
||||
const sendParam = () => {
|
||||
if (!resolved && child && !isSendParam) {
|
||||
isSendParam = true
|
||||
child.send(params)
|
||||
}
|
||||
}
|
||||
|
||||
const resolveOnce = (result: RunCode) => {
|
||||
if (!resolved) {
|
||||
resolved = true
|
||||
cleanup()
|
||||
resolve(result)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用 Bun 的 fork 模式启动子进程
|
||||
child = fork(tsPath, [], {
|
||||
silent: true // 启用 stdio 重定向
|
||||
})
|
||||
// 监听来自子进程的消息
|
||||
child.on('message', (msg: RunCode) => {
|
||||
resolveOnce(msg)
|
||||
})
|
||||
|
||||
// 捕获 stderr 输出(语法错误等)
|
||||
if (child.stderr) {
|
||||
child.stderr.on('data', (data) => {
|
||||
console.log('子进程错误输出:', data.toString())
|
||||
resolveOnce({
|
||||
success: false,
|
||||
error: `子进程错误输出: ${data.toString()}`
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 监听子进程退出事件
|
||||
child.on('exit', (code, signal) => {
|
||||
if (code !== 0) {
|
||||
resolveOnce({
|
||||
success: false,
|
||||
error: `子进程异常退出,退出码: ${code},信号: ${signal}`
|
||||
})
|
||||
} else {
|
||||
resolveOnce({
|
||||
success: false,
|
||||
error: '子进程正常退出但未返回消息'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 监听子进程关闭事件
|
||||
child.on('close', (code, signal) => {
|
||||
resolveOnce({
|
||||
success: false,
|
||||
error: `子进程已关闭,退出码: ${code},信号: ${signal}`
|
||||
})
|
||||
})
|
||||
|
||||
// 监听子进程错误事件
|
||||
child.on('error', (error) => {
|
||||
resolveOnce({
|
||||
success: false,
|
||||
error: `子进程错误: ${error?.message}`
|
||||
})
|
||||
})
|
||||
|
||||
// 添加超时处理
|
||||
timeout = setTimeout(() => {
|
||||
resolveOnce({
|
||||
success: false,
|
||||
error: '子进程执行超时'
|
||||
})
|
||||
}, timeoutMs)
|
||||
|
||||
setTimeout(() => {
|
||||
sendParam()
|
||||
}, 10); // 35秒响应超时
|
||||
// 向子进程发送消息
|
||||
|
||||
} catch (error) {
|
||||
resolveOnce({
|
||||
success: false,
|
||||
error: `启动子进程失败: ${error instanceof Error ? error.message : '未知错误'}`
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { app, assistantConfig } from '../app.ts';
|
||||
import './config/index.ts';
|
||||
import './shop-install/index.ts';
|
||||
import './ai/index.ts';
|
||||
import './light-code/index.ts';
|
||||
|
||||
import os from 'node:os';
|
||||
|
||||
|
||||
30
assistant/src/routes/light-code/call.ts
Normal file
30
assistant/src/routes/light-code/call.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { app, assistantConfig } from '../../app.ts'
|
||||
import path from 'path'
|
||||
import { runCode } from '../../module/light-code/run.ts'
|
||||
|
||||
// http://localhost:4005/api/router?path=call
|
||||
app.route({
|
||||
path: 'call',
|
||||
// middleware: ['auth']
|
||||
}).define(async (ctx) => {
|
||||
const filename = ctx.query?.filename || 'root/light-code-demo/demo-router.ts'
|
||||
const data = ctx.query?.data || {}
|
||||
const appsConfigPath = assistantConfig.configPath?.appsDir || '';
|
||||
const testA = path.join(appsConfigPath, filename)
|
||||
try {
|
||||
const resulst = await runCode(testA, data);
|
||||
if (resulst.success) {
|
||||
const callResult = resulst.data;
|
||||
if (callResult.code === 200) ctx.body = callResult.data
|
||||
else {
|
||||
const callError = `调用程序错误: ${callResult.message}`
|
||||
ctx.throw(callResult.code, callError)
|
||||
}
|
||||
} else {
|
||||
ctx.body = `执行脚本错误: ${resulst.error}`
|
||||
}
|
||||
} catch (error) {
|
||||
ctx.body = `执行脚本异常: ${error?.message || error}`
|
||||
}
|
||||
|
||||
}).addTo(app)
|
||||
1
assistant/src/routes/light-code/index.ts
Normal file
1
assistant/src/routes/light-code/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './call.ts'
|
||||
@@ -71,14 +71,11 @@ export class AppDownload {
|
||||
const command = 'ev';
|
||||
const args = ['app', 'download'];
|
||||
args.push('-i', id);
|
||||
if (type) {
|
||||
args.push('-t', type);
|
||||
}
|
||||
const appName = opts?.appName || id.split('/').pop();
|
||||
|
||||
if (type === 'web') {
|
||||
args.push('-o', pagesDir);
|
||||
} else if (type === 'app') {
|
||||
args.push('-o', path.join(appsDir, appName));
|
||||
args.push('-o', path.join(appsDir));
|
||||
} else {
|
||||
throw new Error('应用类型错误,只能是 web 或 app');
|
||||
}
|
||||
@@ -120,7 +117,7 @@ export class AppDownload {
|
||||
const pagePath = path.join(pagesDir, id);
|
||||
deletePath = pagePath;
|
||||
} else if (type === 'app') {
|
||||
const appPath = path.join(appsDir, appName);
|
||||
const appPath = path.join(appsDir, id);
|
||||
deletePath = appPath;
|
||||
}
|
||||
if (deletePath && checkFileExists(deletePath)) {
|
||||
|
||||
@@ -59,9 +59,11 @@ export class AssistantInit extends AssistantConfig {
|
||||
}
|
||||
// create pem dir //
|
||||
const pemDir = path.join(this.configPath?.configDir, 'pem');
|
||||
if (!checkFileExists(pemDir)) {
|
||||
new HttpsPem(this);
|
||||
console.log(chalk.green('助手证书目录创建成功'));
|
||||
const httpsPem = new HttpsPem(this);
|
||||
if (httpsPem.isHttps) {
|
||||
if (!checkFileExists(pemDir)) {
|
||||
console.log(chalk.green('助手证书目录创建成功'));
|
||||
}
|
||||
}
|
||||
}
|
||||
createAssistantConfig() {
|
||||
|
||||
Reference in New Issue
Block a user