This commit is contained in:
熊潇 2025-05-30 18:16:36 +08:00
parent 84d9fb2455
commit 239ba3ad10
7 changed files with 115 additions and 37 deletions

View File

@ -8,13 +8,18 @@ import { useContextKey } from '@kevisual/use-config/context';
const manualParse = parseHomeArg(HomeConfigDir); const manualParse = parseHomeArg(HomeConfigDir);
const _configDir = manualParse.configDir; const _configDir = manualParse.configDir;
export const configDir = AssistantInit.detectConfigDir(_configDir); export const configDir = AssistantInit.detectConfigDir(_configDir);
const isInit = manualParse?.options?.help ? false : true;
export const assistantConfig = new AssistantInit({ export const assistantConfig = new AssistantInit({
path: configDir, path: configDir,
init: manualParse?.options?.help ? false : true, init: isInit,
}); });
const httpsPem = new HttpsPem(assistantConfig); const httpsPem = new HttpsPem(assistantConfig);
export const app = useContextKey('app', () => { export const app = useContextKey('app', () => {
const init = isInit;
if (init) {
// const config = assistantConfig.getConfig();
}
return new App({ return new App({
serverOptions: { serverOptions: {
path: '/client/router', path: '/client/router',

View File

@ -62,6 +62,8 @@ export type ReturnInitConfigType = ReturnType<typeof initConfig>;
type AuthPermission = { type AuthPermission = {
type?: 'auth-proxy' | 'public' | 'private' | 'project'; type?: 'auth-proxy' | 'public' | 'private' | 'project';
username?: string; // 用户名
admin?: Omit<AuthPermission, 'admin'>;
}; };
export type AssistantConfigData = { export type AssistantConfigData = {
pageApi?: string; // https://kevisual.cn pageApi?: string; // https://kevisual.cn
@ -99,6 +101,11 @@ export type AssistantConfigData = {
[key: string]: string; [key: string]: string;
}; };
auth?: AuthPermission; auth?: AuthPermission;
https?: {
type?: 'https' | 'http';
keyPath?: string; // 证书私钥路径
certPath?: string; // 证书路径
};
}; };
let assistantConfig: AssistantConfigData; let assistantConfig: AssistantConfigData;
type AssistantConfigOptions = { type AssistantConfigOptions = {

View File

@ -21,6 +21,30 @@ export class HttpsPem {
cert: string; cert: string;
constructor(assistantConfig: AssistantConfig) { constructor(assistantConfig: AssistantConfig) {
this.assistantConfig = assistantConfig; this.assistantConfig = assistantConfig;
this.#initKeyCert();
}
#initKeyCert() {
this.assistantConfig.checkMounted();
const config = this.assistantConfig.getConfig();
if (config.https) {
const httpsType = config.https?.type || 'https';
if (httpsType !== 'https') {
console.log(chalk.yellow('当前配置文件 https.type 不是 https, 不使用证书'));
return;
}
if (config.https.keyPath) {
const keyPath = config.https.keyPath;
const certPath = config.https.certPath;
if (checkFileExists(keyPath) && checkFileExists(certPath)) {
this.key = fs.readFileSync(keyPath, 'utf-8');
this.cert = fs.readFileSync(certPath, 'utf-8');
console.log(chalk.green('使用配置文件 https.keyPath 和 https.certPath 的证书'), keyPath, certPath);
return;
} else {
console.log(chalk.red('证书路径不存在,请检查配置文件 https.keyPath 和 https.certPath 是否正确'));
}
}
}
const { key, cert } = this.getCert(); const { key, cert } = this.getCert();
this.key = key; this.key = key;
this.cert = cert; this.cert = cert;

View File

@ -0,0 +1,25 @@
import pm2 from 'pm2';
import { logger } from './logger.js';
export async function reload() {
return new Promise<void>((resolve, reject) => {
pm2.connect((err) => {
if (err) {
logger.error('PM2 connection error:', err);
return reject(err);
}
pm2.reload('assistant-server', (err) => {
if (err) {
logger.error('PM2 reload error:', err);
pm2.disconnect();
return reject(err);
}
logger.info('PM2 server reloaded successfully');
pm2.disconnect();
resolve();
});
});
});
}

View File

@ -1,12 +1,11 @@
import { app, assistantConfig } from '@/app.ts'; import { app, assistantConfig } from '@/app.ts';
// import { getCacheAssistantConfig, setConfig } from '@/modules/config/index.ts'; import { reload } from '../../module/reload-server.ts';
// import { reload } from '@/modules/parent-msg.ts';
app app
.route({ .route({
path: 'config', path: 'config',
description: '获取配置', description: '获取配置',
middleware: ['auth'], middleware: ['admin-auth'],
}) })
.define(async (ctx) => { .define(async (ctx) => {
ctx.body = assistantConfig.getCacheAssistantConfig(); ctx.body = assistantConfig.getCacheAssistantConfig();
@ -18,12 +17,11 @@ app
path: 'config', path: 'config',
key: 'set', key: 'set',
description: '设置配置', description: '设置配置',
middleware: ['auth'], middleware: ['admin-auth'],
}) })
.define(async (ctx) => { .define(async (ctx) => {
const { data } = ctx.query; const { data } = ctx.query;
// reload();
ctx.body = assistantConfig.setConfig(data); ctx.body = assistantConfig.setConfig(data);
reload();
}) })
.addTo(app); .addTo(app);

View File

@ -1,4 +1,4 @@
import { Query } from '@kevisual/query'; import { Query } from '@kevisual/query/query';
import { app, assistantConfig } from '../app.ts'; import { app, assistantConfig } from '../app.ts';
import './config/index.ts'; import './config/index.ts';
import './shop-install/index.ts'; import './shop-install/index.ts';
@ -6,40 +6,58 @@ import './ai/index.ts';
import os from 'node:os'; import os from 'node:os';
const checkAuth = async (ctx: any, isAdmin = false) => {
const config = assistantConfig.getConfig();
const { auth } = config;
const host = config.pageApi || config.registry || 'https://kevisual.cn';
const url = new URL('/api/router', host);
const token = ctx.query.token;
if (auth && auth.type !== 'public') {
if (!token) {
return ctx.throw(401, 'not login');
}
url.searchParams.set('token', token);
// 鉴权代理
// TODO:
const query = new Query({ url: url.toString() });
const res = await query.post({
path: 'user',
key: 'me',
});
if (res.code !== 200) {
return ctx.throw(401, 'not login');
}
const tokenUser = res.data || {};
ctx.state = {
...ctx.state,
tokenUser,
};
const { username } = tokenUser;
if (isAdmin) {
if (auth.username && auth.username !== username) {
return ctx.throw(403, 'not admin user');
}
}
}
};
app app
.route({ .route({
path: 'auth', path: 'auth',
id: 'auth', id: 'auth',
isDebug: true,
description: '获取当前登录用户信息',
}) })
.define(async (ctx) => { .define(async (ctx) => {
const config = assistantConfig.getConfig(); await checkAuth(ctx);
const { auth } = config; })
const host = config.pageApi || config.registry || 'https://kevisual.cn'; .addTo(app);
const url = new URL('/api/router', host); app
const token = ctx.token; .route({
if (auth && auth.type !== 'public') { path: 'admin-auth',
if (!token) { id: 'admin-auth',
return ctx.throw(4001, 'not login'); })
} .define(async (ctx) => {
url.searchParams.set('token', token); await checkAuth(ctx, true);
// 鉴权代理
// TODO:
const query = new Query({ baseURL: url.toString() });
const res = await query.post({
path: 'user',
key: 'me',
});
console.log('res', res);
if (res.code !== 200) {
return ctx.throw(4001, 'not login');
}
const tokenUser = res.data || {};
ctx.state = {
...ctx.state,
tokenUser,
};
const { username } = tokenUser;
}
}) })
.addTo(app); .addTo(app);

View File

@ -1,6 +1,6 @@
{ {
"name": "@kevisual/cli", "name": "@kevisual/cli",
"version": "0.0.55-beta-3", "version": "0.0.56-beta.4",
"description": "envision command tools", "description": "envision command tools",
"main": "dist/app.mjs", "main": "dist/app.mjs",
"type": "module", "type": "module",
@ -31,6 +31,7 @@
"dev": "NODE_ENV=development bun src/run.ts ", "dev": "NODE_ENV=development bun src/run.ts ",
"dev:tsx": "tsx src/run.ts ", "dev:tsx": "tsx src/run.ts ",
"build": "rimraf dist && bun run bun.config.mjs", "build": "rimraf dist && bun run bun.config.mjs",
"pub:me": "npm publish --registry https://npm.xiongxiao.me --tag beta",
"postbuild": "cd assistant && pnpm build ", "postbuild": "cd assistant && pnpm build ",
"dts": "dts-bundle-generator --external-inlines=@types/jsonwebtoken src/index.ts -o dist/index.d.ts ", "dts": "dts-bundle-generator --external-inlines=@types/jsonwebtoken src/index.ts -o dist/index.d.ts ",
"build:all": "rimraf dist && bun run bun.config.mjs && bun run assistant/bun.config.mjs" "build:all": "rimraf dist && bun run bun.config.mjs && bun run assistant/bun.config.mjs"