base module

This commit is contained in:
熊潇 2025-03-10 10:21:14 +08:00
parent 479eaccf57
commit b966ea68f2
40 changed files with 1594 additions and 73 deletions

1
.gitignore vendored
View File

@ -12,3 +12,4 @@ cache-file
/apps
logs
root/

1
.npmrc
View File

@ -1,2 +1,3 @@
//npm.xiongxiao.me/:_authToken=${ME_NPM_TOKEN}
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
ignore-workspace-root-check=true

2
assistant-module/.npmrc Normal file
View File

@ -0,0 +1,2 @@
//npm.xiongxiao.me/:_authToken=${ME_NPM_TOKEN}
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

View File

@ -0,0 +1,49 @@
{
"name": "@kevisual/assistant-module",
"version": "0.0.3",
"description": "assistant module",
"main": "dist/assistant-module.mjs",
"types": "dist/assistant-module.d.ts",
"scripts": {
"dev": "rollup -c -w",
"build": "npm run clean && rollup -c",
"clean": "rm -rf dist"
},
"keywords": [],
"author": "abearxiong <xiongxiao@xiongxiao.me>",
"license": "MIT",
"type": "module",
"devDependencies": {
"@types/node": "^22.13.10",
"@types/send": "^0.17.4",
"@types/ws": "^8.18.0"
},
"publishConfig": {
"access": "public"
},
"files": [
"dist"
],
"exports": {
".": {
"import": "./dist/assistant-module.mjs",
"types": "./dist/assistant-module.d.ts"
},
"./proxy": {
"import": "./dist/assistant-proxy.mjs",
"types": "./dist/assistant-proxy.d.ts"
},
"./assistant-config": {
"import": "./dist/assistant-config.mjs",
"types": "./dist/assistant-config.d.ts"
},
"./assistant-process": {
"import": "./dist/assistant-process.mjs",
"types": "./dist/assistant-process.d.ts"
}
},
"dependencies": {
"send": "^1.1.0",
"ws": "^8.18.1"
}
}

29
assistant-module/pnpm-lock.yaml generated Normal file
View File

@ -0,0 +1,29 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
devDependencies:
'@types/node':
specifier: ^22.13.10
version: 22.13.10
packages:
'@types/node@22.13.10':
resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==}
undici-types@6.20.0:
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
snapshots:
'@types/node@22.13.10':
dependencies:
undici-types: 6.20.0
undici-types@6.20.0: {}

View File

@ -0,0 +1,172 @@
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import path from 'path';
import esbuild from 'rollup-plugin-esbuild';
import alias from '@rollup/plugin-alias';
import replace from '@rollup/plugin-replace';
import dts from 'rollup-plugin-dts';
// @ts-ignore
import pkgs from './package.json' with {type: 'json'};
const isDev = process.env.NODE_ENV === 'development';
const input = './src/index.ts';
/**
* @type {import('rollup').RollupOptions}
*/
const config = {
input,
output: {
dir: './dist',
entryFileNames: 'assistant-module.mjs',
chunkFileNames: '[name]-[hash].mjs',
format: 'esm',
},
plugins: [
replace({
preventAssignment: true, // 防止意外赋值
DEV_SERVER: JSON.stringify(isDev), // 替换 process.env.NODE_ENV
VERSION: JSON.stringify(pkgs.version),
}),
alias({
// only esbuild needs to be configured
entries: [
{ find: '@', replacement: path.resolve('src') }, // 配置 @ 为 src 目录
{ find: 'http', replacement: 'node:http' },
{ find: 'https', replacement: 'node:https' },
{ find: 'fs', replacement: 'node:fs' },
{ find: 'path', replacement: 'node:path' },
{ find: 'crypto', replacement: 'node:crypto' },
{ find: 'zlib', replacement: 'node:zlib' },
{ find: 'stream', replacement: 'node:stream' },
{ find: 'net', replacement: 'node:net' },
{ find: 'tty', replacement: 'node:tty' },
{ find: 'tls', replacement: 'node:tls' },
{ find: 'buffer', replacement: 'node:buffer' },
{ find: 'timers', replacement: 'node:timers' },
// { find: 'string_decoder', replacement: 'node:string_decoder' },
{ find: 'dns', replacement: 'node:dns' },
{ find: 'domain', replacement: 'node:domain' },
{ find: 'os', replacement: 'node:os' },
{ find: 'events', replacement: 'node:events' },
{ find: 'url', replacement: 'node:url' },
{ find: 'assert', replacement: 'node:assert' },
{ find: 'util', replacement: 'node:util' },
],
}),
resolve({
preferBuiltins: true, // 强制优先使用内置模块
}),
commonjs(),
esbuild({
target: 'node22', //
minify: false, // 启用代码压缩
tsconfig: 'tsconfig.json',
}),
json(),
],
external: [
/@kevisual\/router(\/.*)?/, //, // 路由
/@kevisual\/use-config(\/.*)?/, //
],
};
const dtsConfig = [{
input,
output: {
file: 'dist/assistant-module.d.ts',
format: 'esm',
},
plugins: [dts()],
}];
const moduleConfig = {
input: './src/assistant-proxy.ts',
output: {
file: 'dist/assistant-proxy.mjs',
format: 'esm',
},
plugins: [
alias({
entries: [{ find: '@', replacement: path.resolve('src') }],
}),
resolve({
preferBuiltins: true, // 强制优先使用内置模块
}),
commonjs(),
esbuild({
target: 'node22', //
}),
json(),
],
};
const moduleDtsConfig = [{
input: './src/assistant-proxy.ts',
output: {
file: 'dist/assistant-proxy.d.ts',
format: 'esm',
},
plugins: [dts()],
}];
const assistantConfigConfig = {
input: './src/assistant-config.ts',
output: {
file: 'dist/assistant-config.mjs',
format: 'esm',
},
plugins: [
alias({
entries: [{ find: '@', replacement: path.resolve('src') }],
}),
resolve({
preferBuiltins: true, // 强制优先使用内置模块
}),
commonjs(),
esbuild({
target: 'node22', //
}),
json(),
],
};
const assistantConfigDtsConfig = [{
input: './src/assistant-config.ts',
output: {
file: 'dist/assistant-config.d.ts',
format: 'esm',
},
plugins: [dts()],
}];
const assistantProcessConfig = {
input: './src/assistant-process.ts',
output: {
file: 'dist/assistant-process.mjs',
format: 'esm',
},
plugins: [
alias({
entries: [{ find: '@', replacement: path.resolve('src') }],
}),
resolve({
preferBuiltins: true, // 强制优先使用内置模块
}),
commonjs(),
esbuild({
target: 'node22', //
}),
json(),
],
};
const assistantProcessDtsConfig = [{
input: './src/assistant-process.ts',
output: {
file: 'dist/assistant-process.d.ts',
format: 'esm',
},
plugins: [dts()],
}];
export default [config, ...dtsConfig, moduleConfig, ...moduleDtsConfig, assistantConfigConfig, ...assistantConfigDtsConfig, assistantProcessConfig, ...assistantProcessDtsConfig];

View File

@ -0,0 +1 @@
export * from './config/index.ts';

View File

@ -0,0 +1 @@
export * from './process/index.ts';

View File

@ -0,0 +1 @@
export * from './proxy/index.ts';

View File

@ -0,0 +1,111 @@
import path from 'path';
import { homedir } from 'os';
import fs from 'fs';
import { checkFileExists, createDir } from '../file/index.ts';
import { ProxyInfo } from '../proxy/proxy.ts';
export const kevisualUrl = 'https://kevisual.xiongxiao.me';
const configDir = createDir(path.join(homedir(), '.config/envision'));
export const configPath = path.join(configDir, 'assistant-config.json');
export const appConfigPath = path.join(configDir, 'assistant-app-config.json');
export const appDir = createDir(path.join(configDir, 'assistant-app/frontend'));
export const appPidPath = path.join(configDir, 'assistant-app.pid');
export const LocalElectronAppUrl = 'https://assistant.app/user/tiptap/';
type AssistantConfig = {
pageApi?: string; // https://kevisual.silkyai.cn
loadURL?: string; // https://assistant.app/user/tiptap/
proxy?: { user: string; key: string; path: string }[];
apiProxyList?: ProxyInfo[];
};
let assistantConfig: AssistantConfig;
export const getConfig = () => {
try {
if (!checkFileExists(configPath)) {
fs.writeFileSync(configPath, JSON.stringify({ proxy: [] }, null, 2));
return {
loadURL: LocalElectronAppUrl,
pageApi: '',
proxy: [],
};
}
assistantConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
return assistantConfig;
} catch (error) {
console.error(error);
return {
loadURL: LocalElectronAppUrl,
pageApi: '',
proxy: [],
};
}
};
export const getCacheAssistantConfig = () => {
if (assistantConfig) {
return assistantConfig;
}
return getConfig();
};
export const setConfig = (config?: AssistantConfig) => {
if (!config) {
return assistantConfig;
}
assistantConfig = config;
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
return assistantConfig;
};
type AppConfig = {
list: any[];
};
/**
*
* @returns
*/
export const getAppConfig = (): AppConfig => {
if (!checkFileExists(appConfigPath)) {
return {
list: [],
};
}
return JSON.parse(fs.readFileSync(appConfigPath, 'utf8'));
};
export const setAppConfig = (config: AppConfig) => {
fs.writeFileSync(appConfigPath, JSON.stringify(config, null, 2));
return config;
};
export const addAppConfig = (app: any) => {
const config = getAppConfig();
const assistantConfig = getCacheAssistantConfig();
const _apps = config.list;
const _proxy = assistantConfig.proxy || [];
const { user, key } = app;
const newProxyInfo = {
user,
key,
path: `/${user}/${key}`,
};
const _proxyIndex = _proxy.findIndex((_proxy: any) => _proxy.path === newProxyInfo.path);
if (_proxyIndex !== -1) {
_proxy[_proxyIndex] = newProxyInfo;
} else {
_proxy.push(newProxyInfo);
}
const _app = _apps.findIndex((_app: any) => _app.id === app.id);
if (_app !== -1) {
_apps[_app] = app;
} else {
_apps.push(app);
}
setAppConfig({ ...config, list: _apps });
setConfig({ ...assistantConfig, proxy: _proxy });
return config;
};
export const getAppList = () => {
const config = getAppConfig();
return config.list || [];
};

View File

@ -0,0 +1,20 @@
import fs from 'fs';
export const checkFileExists = (filePath: string, checkIsFile = false) => {
try {
fs.accessSync(filePath);
if (checkIsFile) {
return fs.statSync(filePath).isFile();
}
return true;
} catch (error) {
return false;
}
};
export const createDir = (dirPath: string) => {
if (!checkFileExists(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
return dirPath;
};

View File

@ -0,0 +1,2 @@
export * from './install/index.ts';
export * from './config/index.ts';

View File

@ -0,0 +1,127 @@
import path from 'path';
import fs from 'fs';
type DownloadTask = {
downloadPath: string;
downloadUrl: string;
user: string;
key: string;
version: string;
};
export type Package = {
id: string;
name?: string;
version?: string;
description?: string;
title?: string;
user?: string;
key?: string;
[key: string]: any;
};
type InstallAppOpts = {
appDir?: string;
kevisualUrl?: string;
/**
* , assistant-config的下面
*/
};
export const installApp = async (app: Package, opts: InstallAppOpts = {}) => {
// const _app = demoData;
const { appDir = '', kevisualUrl = 'https://kevisual.cn' } = opts;
const _app = app;
try {
let files = _app.data.files || [];
const version = _app.version;
const user = _app.user;
const key = _app.key;
const downFiles = files.map((file: any) => {
const noVersionPath = file.path.replace(`/${version}`, '');
return {
...file,
downloadPath: path.join(appDir, noVersionPath),
downloadUrl: `${kevisualUrl}/${noVersionPath}`,
};
});
const downloadTasks: DownloadTask[] = downFiles as any;
for (const file of downloadTasks) {
const downloadPath = file.downloadPath;
const downloadUrl = file.downloadUrl;
const dir = path.dirname(downloadPath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
const res = await fetch(downloadUrl);
const blob = await res.blob();
fs.writeFileSync(downloadPath, Buffer.from(await blob.arrayBuffer()));
}
let indexHtml = files.find((file: any) => file.name === 'index.html');
if (!indexHtml) {
files.push({
name: 'index.html',
path: `${user}/${key}/index.html`,
});
fs.writeFileSync(path.join(appDir, `${user}/${key}/index.html`), JSON.stringify(app, null, 2));
}
_app.data.files = files;
return {
code: 200,
data: _app,
message: 'Install app success',
};
} catch (error) {
console.error(error);
return {
code: 500,
message: 'Install app failed',
};
}
};
export const checkAppDir = (appDir: string) => {
const files = fs.readdirSync(appDir);
if (files.length === 0) {
fs.rmSync(appDir, { recursive: true });
}
};
export const checkFileExists = (path: string) => {
try {
fs.accessSync(path);
return true;
} catch (error) {
return false;
}
};
type UninstallAppOpts = {
appDir?: string;
};
export const uninstallApp = async (app: Partial<Package>, opts: UninstallAppOpts = {}) => {
const { appDir = '' } = opts;
try {
const { user, key } = app;
const keyDir = path.join(appDir, user, key);
const parentDir = path.join(appDir, user);
if (!checkFileExists(appDir) || !checkFileExists(keyDir)) {
return {
code: 200,
message: 'uninstall app success',
};
}
try {
// 删除appDir和文件
fs.rmSync(keyDir, { recursive: true });
} catch (error) {
console.error(error);
}
checkAppDir(parentDir);
return {
code: 200,
message: 'Uninstall app success',
};
} catch (error) {
console.error(error);
return {
code: 500,
message: 'Uninstall app failed',
};
}
};

View File

@ -0,0 +1,70 @@
import { ChildProcess, fork } from 'child_process';
export const runProcess = (appPath: string) => {
const process = fork(appPath);
process.on('exit', (code) => {
console.log(`Process exited with code ${code}`);
});
process.on('message', (message) => {
console.log('Message from child:', message);
});
// Example of sending a message to the child process
// process.send({ hello: 'world' });
};
class BaseProcess {
private process: ChildProcess;
status: 'running' | 'stopped' | 'error' = 'stopped';
appPath: string;
constructor(appPath: string) {
this.appPath = appPath;
// this.createProcess(appPath);
}
createProcess(appPath: string = this.appPath) {
if (this.process) {
this.process.kill();
}
this.appPath = appPath;
this.process = fork(appPath);
return this;
}
kill(signal?: NodeJS.Signals | number) {
if (this.process) {
this.process.kill(signal);
}
return this;
}
public send(message: any) {
this.process.send(message);
}
public on(event: string, callback: (message: any) => void) {
this.process.on(event, callback);
}
public onExit(callback: (code: number) => void) {
this.process.on('exit', callback);
}
public onError(callback: (error: Error) => void) {
this.process.on('error', callback);
}
public onMessage(callback: (message: any) => void) {
this.process.on('message', callback);
}
public onClose(callback: () => void) {
this.process.on('close', callback);
}
public onDisconnect(callback: () => void) {
this.process.on('disconnect', callback);
}
}
export class AssistantProcess extends BaseProcess {
constructor(appPath: string) {
super(appPath);
}
}

View File

@ -0,0 +1,82 @@
import http from 'http';
import https from 'https';
import { ProxyInfo } from './proxy.ts';
export const defaultApiProxy = [
{
path: '/api/router',
target: 'https://kevisual.xiongxiao.me',
},
{
path: '/v1',
target: 'https://kevisual.xiongxiao.me',
},
];
/**
* api代理
* @param api
* @param paths ['/api/router', '/v1' ]
* @returns
*/
export const createApiProxy = (api: string, paths: string[] = ['/api/router', '/v1']) => {
const pathList = paths.map((item) => {
return {
path: item,
target: new URL(api).origin,
};
});
return pathList;
};
export const apiProxy = (req: http.IncomingMessage, res: http.ServerResponse, proxyApi: ProxyInfo) => {
const _u = new URL(req.url, `${proxyApi.target}`);
console.log('proxyApi', req.url, _u.href);
// 设置代理请求的目标 URL 和请求头
let header: any = {};
if (req.headers?.['Authorization'] && !req.headers?.['authorization']) {
header.authorization = req.headers['Authorization'];
}
// 提取req的headers中的非HOST的header
const headers = Object.keys(req.headers).filter((item) => item && item.toLowerCase() !== 'host');
headers.forEach((item) => {
if (item.toLowerCase() === 'origin') {
header.origin = new URL(proxyApi.target).origin;
return;
}
if (item.toLowerCase() === 'referer') {
header.referer = new URL(req.url, proxyApi.target).href;
return;
}
header[item] = req.headers[item];
});
const options = {
host: _u.hostname,
path: req.url,
method: req.method,
headers: {
...header,
},
};
console.log('options', JSON.stringify(options, null, 2));
if (_u.port) {
// @ts-ignore
options.port = _u.port;
}
const httpProxy = _u.protocol === 'https:' ? https : http;
// 创建代理请求
const proxyReq = httpProxy.request(options, (proxyRes) => {
// 将代理服务器的响应头和状态码返回给客户端
res.writeHead(proxyRes.statusCode, proxyRes.headers);
// 将代理响应流写入客户端响应
proxyRes.pipe(res, { end: true });
});
// 处理代理请求的错误事件
proxyReq.on('error', (err) => {
console.error(`Proxy request error: ${err.message}`);
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.write(`Proxy request error: ${err.message}`);
});
// 处理 POST 请求的请求体(传递数据到目标服务器),end:true 表示当请求体结束时,关闭请求
req.pipe(proxyReq, { end: true });
return;
};

View File

@ -0,0 +1,47 @@
import http from 'http';
import send from 'send';
import fs from 'fs';
import { fileIsExist } from '@kevisual/use-config';
import path from 'path';
import { ProxyInfo } from './proxy.ts';
export const fileProxy = (req: http.IncomingMessage, res: http.ServerResponse, proxyApi: ProxyInfo) => {
// url开头的文件
const url = new URL(req.url, 'http://localhost');
let pathname = url.pathname.slice(1);
const { indexPath = '', target = '', rootPath = process.cwd() } = proxyApi;
try {
if (pathname.endsWith('/')) {
pathname = pathname + 'index.html';
}
// 检测文件是否存在如果文件不存在则返回404
let filePath = path.join(rootPath, target, pathname);
let exist = fileIsExist(filePath);
if (!exist) {
filePath = path.join(rootPath, target, '/' + indexPath);
exist = fileIsExist(filePath);
}
console.log('filePath', filePath, exist);
if (!exist) {
res.statusCode = 404;
res.end('Not Found File');
return;
}
const ext = path.extname(filePath);
let maxAge = 24 * 60 * 60 * 1000; // 24小时
if (ext === '.html') {
maxAge = 0;
}
let sendFilePath = filePath.replace(rootPath + '/', '');
const file = send(req, sendFilePath, {
root: rootPath,
maxAge,
});
file.pipe(res);
} catch (error) {
res.statusCode = 404;
res.end('Error:Not Found File');
return;
}
};

View File

@ -0,0 +1,5 @@
export * from './proxy.ts';
export * from './file-proxy.ts';
export { default as send } from 'send';
export * from './api-proxy.ts';
export * from './wx-proxy.ts';

View File

@ -0,0 +1,35 @@
export type ProxyInfo = {
path?: string;
target?: string;
type?: 'static' | 'dynamic' | 'minio';
/**
* index.html 访
*/
indexPath?: string;
/**
* , process.cwd()
*/
rootPath?: string;
};
export type ApiList = {
path: string;
/**
* url或者相对路径
*/
target: string;
/**
*
*/
type?: 'static' | 'dynamic' | 'minio';
}[];
/**
[
{
path: '/api/v1/user',
target: 'http://localhost:3000/api/v1/user',
type: 'dynamic',
},
]
*/

View File

@ -0,0 +1,48 @@
import { Server } from 'http';
import WebSocket from 'ws';
/**
* websocket代理
* apiList: [{ path: '/api/router', target: 'https://kevisual.xiongxiao.me' }]
* @param server
* @param config
*/
export const wsProxy = (server: Server, config: { apiList: any[] }) => {
console.log('Upgrade initialization started');
server.on('upgrade', (req, socket, head) => {
const proxyApiList = config?.apiList || [];
const proxyApi = proxyApiList.find((item) => req.url.startsWith(item.path));
if (proxyApi) {
const _u = new URL(req.url, `${proxyApi.target}`);
const isHttps = _u.protocol === 'https:';
const wsProtocol = isHttps ? 'wss' : 'ws';
const wsUrl = `${wsProtocol}://${_u.hostname}${_u.pathname}`;
const proxySocket = new WebSocket(wsUrl, {
headers: req.headers,
});
proxySocket.on('open', () => {
socket.on('data', (data) => {
proxySocket.send(data);
});
proxySocket.on('message', (message) => {
socket.write(message);
});
});
proxySocket.on('error', (err) => {
console.error(`WebSocket proxy error: ${err.message}`);
socket.end();
});
socket.on('error', () => {
proxySocket.close();
});
} else {
socket.end();
}
});
};

View File

@ -0,0 +1,33 @@
{
"compilerOptions": {
"module": "nodenext",
"target": "esnext",
"noImplicitAny": false,
"outDir": "./dist",
"sourceMap": false,
"allowJs": true,
"newLine": "LF",
"baseUrl": "./",
"typeRoots": [
"node_modules/@types",
"node_modules/@kevisual/types"
],
"declaration": true,
"noEmit": false,
"allowImportingTsExtensions": true,
"emitDeclarationOnly": true,
"moduleResolution": "NodeNext",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"esModuleInterop": true,
"paths": {
"@/*": [
"src/*"
]
}
},
"include": [
"src/**/*.ts",
],
"exclude": [],
}

View File

@ -1,19 +1,21 @@
{
"name": "demo-app",
"name": "assistant-center",
"version": "0.0.1",
"description": "",
"main": "index.js",
"app": {
"key": "demo-app",
"key": "assistant-center",
"entry": "dist/app.mjs",
"type": "system-app",
"files": [
"dist"
"dist",
"pem"
]
},
"scripts": {
"watch": "rollup -c rollup.config.mjs -w",
"dev": "cross-env NODE_ENV=development nodemon --delay 2.5 -e js,cjs,mjs --exec node dist/app.mjs",
"build": "rollup -c rollup.config.mjs",
"test": "tsx test/**/*.ts",
"dev:watch": "cross-env NODE_ENV=development concurrently -n \"Watch,Dev\" -c \"green,blue\" \"npm run watch\" \"sleep 1 && npm run dev\" ",
"clean": "rm -rf dist",
@ -31,20 +33,22 @@
"src"
],
"dependencies": {
"@kevisual/code-center-module": "0.0.11-alpha.1",
"@kevisual/mark": "0.0.6",
"@kevisual/router": "0.0.8-alpha.3",
"@kevisual/code-center-module": "0.0.13",
"@kevisual/mark": "0.0.7",
"@kevisual/router": "0.0.9",
"cookie": "^1.0.2",
"dayjs": "^1.11.13",
"formidable": "^3.5.2",
"json5": "^2.2.3",
"lodash-es": "^4.17.21"
"lodash-es": "^4.17.21",
"ws": "^8.18.1"
},
"devDependencies": {
"@kevisual/assistant-module": "workspace:*",
"@kevisual/types": "^0.0.6",
"@kevisual/use-config": "^1.0.9",
"@rollup/plugin-alias": "^5.1.1",
"@rollup/plugin-commonjs": "^28.0.2",
"@rollup/plugin-commonjs": "^28.0.3",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-replace": "^6.0.2",
@ -52,7 +56,8 @@
"@types/crypto-js": "^4.2.2",
"@types/formidable": "^3.4.5",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.13.8",
"@types/node": "^22.13.9",
"@types/ws": "^8.18.0",
"concurrently": "^9.1.2",
"cross-env": "^7.0.3",
"nodemon": "^3.1.9",

15
pem/https-cert.pem Normal file
View File

@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICXTCCAcagAwIBAgIJHsP036vqWER/MA0GCSqGSIb3DQEBBQUAMF8xCjAIBgNV
BAMTASoxCzAJBgNVBAYTAkNOMREwDwYDVQQIEwhaaGVKaWFuZzERMA8GA1UEBxMI
SGFuZ3pob3UxETAPBgNVBAoTCEVudmlzaW9uMQswCQYDVQQLEwJJVDAeFw0yNTAz
MDcxNDIwMTJaFw0yNjAzMDcxNDIwMTJaMF8xCjAIBgNVBAMTASoxCzAJBgNVBAYT
AkNOMREwDwYDVQQIEwhaaGVKaWFuZzERMA8GA1UEBxMISGFuZ3pob3UxETAPBgNV
BAoTCEVudmlzaW9uMQswCQYDVQQLEwJJVDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
gYkCgYEAquA2XnwduVSJHvnTW4r5yodz/joTPUi+r8kS/KJyR/NQ5xovtDY2gJoO
nJk8qekcLKuofskIIu4HFsCE7AYBkQGaYmc+0cCQCmEpwivesbeMB0ydz+6NwLQn
32HVjtMtx3gUcywGdMntiQb/P9FIhtE132wOmW9PeSl0dx/nyrUCAwEAAaMhMB8w
HQYDVR0RBBYwFIIBKoIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBBQUAA4GB
AJsjIZgb6iE4OTXoEDiBPmHM+byWs20K2eCvi79V9/vns90IroBQfGirIsovv923
SqjmdAFsZkRUbZvX99lBX0mmZK9KTE4K9YUm7bv+d8+fBPxAgNFSTRiSNBeNh0Lh
HdJUiI/tzIfI6RRg1pFDC1tOG083Cl/YElN879w3Iipi
-----END CERTIFICATE-----

15
pem/https-key.pem Normal file
View File

@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCq4DZefB25VIke+dNbivnKh3P+OhM9SL6vyRL8onJH81DnGi+0
NjaAmg6cmTyp6Rwsq6h+yQgi7gcWwITsBgGRAZpiZz7RwJAKYSnCK96xt4wHTJ3P
7o3AtCffYdWO0y3HeBRzLAZ0ye2JBv8/0UiG0TXfbA6Zb095KXR3H+fKtQIDAQAB
AoGADDEbL/qjFEoXzoH8tpdf4zdu60CxhrneASTTmfrtNH0D1LlllfIYSWy0hi/Y
yDa9r+I/j2xAjF13XAQ4d66mBdjCRATLx/aL495o+e6NkIBEAgdP88hHm13F6gg+
h8iMixs5mkwU41sghnCYeBqlziKPi8fsoTmhK0VETFUtDQECQQDT0kZ7OCEVNcz0
LAUPO7ukeHAYnGYns+Q3F3kgonzHPGflClH5dsg0NS1HFQj6Ny2oyUupjNePOCJK
88zNehIlAkEAzoO9zrE+AoTPleVpe7TAUlZB1YMa7W1C5owjyEkv4TjIe8mpwWM/
9vVe+SGUnc6DZy6xkk5zWmA2w18SexXJUQJBAJQbcyy1EmzCMYyJOwBrw8g8biTH
NqaMIgZjY05uTtEAa6S6kpbbdyEKDZ6mFqDd9A8QsNbco9yAY3oE/i6uLAECQHOt
a9aphZiXmEfYl3uJxejZFEtrAtxXxY+qlCiOhllcG0Drt0DyPVQyIZ7fZoX2tbhI
eYMAmrDXEBXj3VBA5eECQCLGpQKqo06QwP2qZ9mEaPB9KvVcABo97b9Lf7VUqcJx
tFWRSlpeICpDQZHqX92nwoD/2fGCH3br3o94k1oyApI=
-----END RSA PRIVATE KEY-----

240
pnpm-lock.yaml generated
View File

@ -9,14 +9,14 @@ importers:
.:
dependencies:
'@kevisual/code-center-module':
specifier: 0.0.11-alpha.1
version: 0.0.11-alpha.1(@kevisual/auth@1.0.5)(@kevisual/router@0.0.8-alpha.3)(@kevisual/use-config@1.0.9)(ioredis@5.5.0)(pg@8.13.3)(sequelize@6.37.5(pg@8.13.3))
specifier: 0.0.13
version: 0.0.13(@kevisual/auth@1.0.5)(@kevisual/router@0.0.9)(@kevisual/use-config@1.0.9)(ioredis@5.5.0)(pg@8.13.3)(sequelize@6.37.5(pg@8.13.3))
'@kevisual/mark':
specifier: 0.0.6
version: 0.0.6(esbuild@0.25.0)
specifier: 0.0.7
version: 0.0.7(esbuild@0.25.0)
'@kevisual/router':
specifier: 0.0.8-alpha.3
version: 0.0.8-alpha.3
specifier: 0.0.9
version: 0.0.9
cookie:
specifier: ^1.0.2
version: 1.0.2
@ -32,7 +32,13 @@ importers:
lodash-es:
specifier: ^4.17.21
version: 4.17.21
ws:
specifier: ^8.18.1
version: 8.18.1
devDependencies:
'@kevisual/assistant-module':
specifier: workspace:*
version: link:assistant-module
'@kevisual/types':
specifier: ^0.0.6
version: 0.0.6
@ -43,8 +49,8 @@ importers:
specifier: ^5.1.1
version: 5.1.1(rollup@4.34.9)
'@rollup/plugin-commonjs':
specifier: ^28.0.2
version: 28.0.2(rollup@4.34.9)
specifier: ^28.0.3
version: 28.0.3(rollup@4.34.9)
'@rollup/plugin-json':
specifier: ^6.1.0
version: 6.1.0(rollup@4.34.9)
@ -67,8 +73,11 @@ importers:
specifier: ^4.17.12
version: 4.17.12
'@types/node':
specifier: ^22.13.8
version: 22.13.8
specifier: ^22.13.9
version: 22.13.9
'@types/ws':
specifier: ^8.18.0
version: 8.18.0
concurrently:
specifier: ^9.1.2
version: 9.1.2
@ -106,6 +115,25 @@ importers:
specifier: ^5.8.2
version: 5.8.2
assistant-module:
dependencies:
send:
specifier: ^1.1.0
version: 1.1.0
ws:
specifier: ^8.18.1
version: 8.18.1
devDependencies:
'@types/node':
specifier: ^22.13.10
version: 22.13.10
'@types/send':
specifier: ^0.17.4
version: 0.17.4
'@types/ws':
specifier: ^8.18.0
version: 8.18.0
packages:
'@babel/code-frame@7.26.2':
@ -279,8 +307,8 @@ packages:
'@kevisual/auth@1.0.5':
resolution: {integrity: sha512-GwsLj7unKXi7lmMiIIgdig4LwwLiDJnOy15HHZR5gMbyK6s5/uJiMY5RXPB2+onGzTNDqFo/hXjsD2wkerHPVg==}
'@kevisual/code-center-module@0.0.11-alpha.1':
resolution: {integrity: sha512-0HPSZw4PmhejE7p4cBIe174/h434XE3dgrwHoRZLYZSZyJ/aaBfR+3RybdvDN5dnyusLkPdgRq+Qern53Lqp1A==}
'@kevisual/code-center-module@0.0.13':
resolution: {integrity: sha512-A82sX8rdG2igyVLIF+0dagcUsGfk2b0JAga1BDDr9mrChrG1HbG1uYN7JJdjJbGE6zGYqGxRZwxKZmzB/+KMnw==}
peerDependencies:
'@kevisual/auth': ^1.0.5
'@kevisual/router': ^0.0.7
@ -292,8 +320,8 @@ packages:
'@kevisual/load@0.0.4':
resolution: {integrity: sha512-TJBieKsEoEPfP4+tDyhNZdMX2LMAGiDZ/IrAXPFWB4jeFP0Ywm1W5xDV52LhhHq4nwTmuhyTVmPxJYiEVYTHtA==}
'@kevisual/mark@0.0.6':
resolution: {integrity: sha512-QhJXeJbeQIbouitqE3s67G92tkx44XoC4dDZUXCd28xGJyeCegkkQ6n4uxKPLBCh3XhDtek1e2EZjhK97wyZJA==}
'@kevisual/mark@0.0.7':
resolution: {integrity: sha512-PiEEy4yvWEpixw76PzgrIWeNelzm+FrhtzFmqJU92o5GkgawaFwighcvIxqcVZRKeEFF4uvlTjFrGeQvXw6F4A==}
'@kevisual/rollup-tools@0.0.1':
resolution: {integrity: sha512-TdCN+IU0fyHudiiqYvobXQ8r5MltfM/cKmSS59iopyL8YYwXwcipOS4S24NWA79g7uwJfSUNk5lg3yVhom79fQ==}
@ -302,8 +330,8 @@ packages:
'@kevisual/router@0.0.7':
resolution: {integrity: sha512-4n1Tp4YLoraJv7jtfy7jbuLGyAj0B2QkTlnlEDHCUTlEUOvOkjtf7DHAe2SL92fTgXhSbod0I/0vUcDF85oj/w==}
'@kevisual/router@0.0.8-alpha.3':
resolution: {integrity: sha512-iWFatFe0ggBTpSmCqtRIVbMq8YnekM2beIciWmoE9kIfadosdyKW/vWoK5wfTUu4nB4GORCiDO3YrAYnoCaQug==}
'@kevisual/router@0.0.9':
resolution: {integrity: sha512-qPyC2GVJ7iOIdJCCKNDsWMAKOQeSJW9HBpL5ZWKHTbi+t4jJBGTzIlXmjKeMHRd0lr/Qq1imQvlkSh4hlrbodA==}
'@kevisual/types@0.0.6':
resolution: {integrity: sha512-7yxe1QmuC5g7lI/1Hm+zXly8if0z+ZqGM1SVOVv2VNRwRAVYBJDc365zWCCfRwE+5YaB2daWTe5zBOU4EkltkQ==}
@ -354,8 +382,8 @@ packages:
rollup:
optional: true
'@rollup/plugin-commonjs@28.0.2':
resolution: {integrity: sha512-BEFI2EDqzl+vA1rl97IDRZ61AIwGH093d9nz8+dThxJNH8oSoB7MjWvPCX3dkaK1/RCJ/1v/R1XB15FuSs0fQw==}
'@rollup/plugin-commonjs@28.0.3':
resolution: {integrity: sha512-pyltgilam1QPdn+Zd9gaCfOLcnjMEJ9gV+bTw6/r73INdvzf1ah9zLIJBm+kW7R6IUFIQ1YO+VqZtYxZNWFPEQ==}
engines: {node: '>=16.0.0 || 14 >= 14.17'}
peerDependencies:
rollup: ^2.68.0||^3.0.0||^4.0.0
@ -549,6 +577,9 @@ packages:
'@types/lodash@4.17.15':
resolution: {integrity: sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==}
'@types/mime@1.3.5':
resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==}
'@types/minimatch@5.1.2':
resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==}
@ -558,15 +589,24 @@ packages:
'@types/node-forge@1.3.11':
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
'@types/node@22.13.8':
resolution: {integrity: sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ==}
'@types/node@22.13.10':
resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==}
'@types/node@22.13.9':
resolution: {integrity: sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==}
'@types/resolve@1.20.2':
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
'@types/send@0.17.4':
resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==}
'@types/validator@13.12.2':
resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==}
'@types/ws@8.18.0':
resolution: {integrity: sha512-8svvI3hMyvN0kKCJMvTJP/x6Y/EoQbepff882wL+Sn5QsXb3etnamgrJq4isrBxSJj5L2AuXcI0+bgkoAXGUJw==}
accepts@1.3.8:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
@ -854,6 +894,14 @@ packages:
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
engines: {node: '>=0.10'}
depd@2.0.0:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'}
destroy@1.2.0:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
dezalgo@1.0.4:
resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==}
@ -875,12 +923,19 @@ packages:
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
encodeurl@2.0.0:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'}
engine.io-parser@5.2.3:
resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==}
engines: {node: '>=10.0.0'}
@ -932,6 +987,9 @@ packages:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
escape-html@1.0.3:
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
escape-string-regexp@4.0.0:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
@ -960,6 +1018,10 @@ packages:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
etag@1.8.1:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'}
eventemitter2@0.4.14:
resolution: {integrity: sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ==}
@ -1020,6 +1082,10 @@ packages:
formidable@3.5.2:
resolution: {integrity: sha512-Jqc1btCy3QzRbJaICGwKcBfGWuLADRerLzDqi2NwSt/UkXLsHJw2TVResiaoBufHVHy9aSgClOHCeJsSsFLTbg==}
fresh@0.5.2:
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
engines: {node: '>= 0.6'}
fs-extra@8.1.0:
resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
engines: {node: '>=6 <7 || >=8'}
@ -1147,6 +1213,10 @@ packages:
resolution: {integrity: sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw==}
engines: {node: '>=8'}
http-errors@2.0.0:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'}
http-proxy-agent@7.0.2:
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
engines: {node: '>= 14'}
@ -1491,6 +1561,10 @@ packages:
resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
engines: {node: '>= 0.4'}
on-finished@2.4.1:
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
engines: {node: '>= 0.8'}
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
@ -1650,6 +1724,10 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
range-parser@1.2.1:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
read@1.0.7:
resolution: {integrity: sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==}
engines: {node: '>=0.8'}
@ -1780,6 +1858,10 @@ packages:
engines: {node: '>=10'}
hasBin: true
send@1.1.0:
resolution: {integrity: sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==}
engines: {node: '>= 18'}
sequelize-pool@7.1.0:
resolution: {integrity: sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==}
engines: {node: '>= 10.0.0'}
@ -1829,6 +1911,9 @@ packages:
resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
engines: {node: '>= 0.4'}
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@ -1922,6 +2007,10 @@ packages:
standard-as-callback@2.1.0:
resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
statuses@2.0.1:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
engines: {node: '>= 0.8'}
stop-iteration-iterator@1.1.0:
resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
engines: {node: '>= 0.4'}
@ -1984,6 +2073,10 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
toidentifier@1.0.1:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
engines: {node: '>=0.6'}
toposort-class@1.0.1:
resolution: {integrity: sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==}
@ -2127,8 +2220,8 @@ packages:
utf-8-validate:
optional: true
ws@8.18.0:
resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
ws@8.18.1:
resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
@ -2263,10 +2356,10 @@ snapshots:
'@kevisual/auth@1.0.5': {}
'@kevisual/code-center-module@0.0.11-alpha.1(@kevisual/auth@1.0.5)(@kevisual/router@0.0.8-alpha.3)(@kevisual/use-config@1.0.9)(ioredis@5.5.0)(pg@8.13.3)(sequelize@6.37.5(pg@8.13.3))':
'@kevisual/code-center-module@0.0.13(@kevisual/auth@1.0.5)(@kevisual/router@0.0.9)(@kevisual/use-config@1.0.9)(ioredis@5.5.0)(pg@8.13.3)(sequelize@6.37.5(pg@8.13.3))':
dependencies:
'@kevisual/auth': 1.0.5
'@kevisual/router': 0.0.8-alpha.3
'@kevisual/router': 0.0.9
'@kevisual/use-config': 1.0.9
ioredis: 5.5.0
nanoid: 5.1.2
@ -2283,13 +2376,14 @@ snapshots:
dependencies:
eventemitter3: 5.0.1
'@kevisual/mark@0.0.6(esbuild@0.25.0)':
'@kevisual/mark@0.0.7(esbuild@0.25.0)':
dependencies:
'@kevisual/auth': 1.0.5
'@kevisual/rollup-tools': 0.0.1(esbuild@0.25.0)
'@kevisual/router': 0.0.7
'@kevisual/use-config': 1.0.9
cookie: 1.0.2
nanoid: 5.1.2
pg: 8.13.3
sequelize: 6.37.5(pg@8.13.3)
transitivePeerDependencies:
@ -2310,12 +2404,12 @@ snapshots:
'@kevisual/rollup-tools@0.0.1(esbuild@0.25.0)':
dependencies:
'@rollup/plugin-alias': 5.1.1(rollup@4.34.9)
'@rollup/plugin-commonjs': 28.0.2(rollup@4.34.9)
'@rollup/plugin-commonjs': 28.0.3(rollup@4.34.9)
'@rollup/plugin-json': 6.1.0(rollup@4.34.9)
'@rollup/plugin-node-resolve': 15.3.1(rollup@4.34.9)
'@rollup/plugin-replace': 6.0.2(rollup@4.34.9)
'@rollup/plugin-typescript': 12.1.2(rollup@4.34.9)(tslib@2.8.1)(typescript@5.8.2)
'@types/node': 22.13.8
'@types/node': 22.13.10
chalk: 5.4.1
commander: 12.1.0
glob: 11.0.1
@ -2334,16 +2428,16 @@ snapshots:
dependencies:
path-to-regexp: 8.2.0
selfsigned: 2.4.1
ws: 8.18.0
ws: 8.18.1
transitivePeerDependencies:
- bufferutil
- utf-8-validate
'@kevisual/router@0.0.8-alpha.3':
'@kevisual/router@0.0.9':
dependencies:
path-to-regexp: 8.2.0
selfsigned: 2.4.1
ws: 8.18.0
ws: 8.18.1
transitivePeerDependencies:
- bufferutil
- utf-8-validate
@ -2430,7 +2524,7 @@ snapshots:
optionalDependencies:
rollup: 4.34.9
'@rollup/plugin-commonjs@28.0.2(rollup@4.34.9)':
'@rollup/plugin-commonjs@28.0.3(rollup@4.34.9)':
dependencies:
'@rollup/pluginutils': 5.1.4(rollup@4.34.9)
commondir: 1.0.1
@ -2555,7 +2649,7 @@ snapshots:
'@types/cors@2.8.17':
dependencies:
'@types/node': 22.13.8
'@types/node': 22.13.10
'@types/crypto-js@4.2.2': {}
@ -2567,16 +2661,16 @@ snapshots:
'@types/formidable@3.4.5':
dependencies:
'@types/node': 22.13.8
'@types/node': 22.13.10
'@types/fs-extra@8.1.5':
dependencies:
'@types/node': 22.13.8
'@types/node': 22.13.10
'@types/glob@7.2.0':
dependencies:
'@types/minimatch': 5.1.2
'@types/node': 22.13.8
'@types/node': 22.13.10
'@types/lodash-es@4.17.12':
dependencies:
@ -2584,22 +2678,37 @@ snapshots:
'@types/lodash@4.17.15': {}
'@types/mime@1.3.5': {}
'@types/minimatch@5.1.2': {}
'@types/ms@2.1.0': {}
'@types/node-forge@1.3.11':
dependencies:
'@types/node': 22.13.8
'@types/node': 22.13.10
'@types/node@22.13.8':
'@types/node@22.13.10':
dependencies:
undici-types: 6.20.0
'@types/node@22.13.9':
dependencies:
undici-types: 6.20.0
'@types/resolve@1.20.2': {}
'@types/send@0.17.4':
dependencies:
'@types/mime': 1.3.5
'@types/node': 22.13.10
'@types/validator@13.12.2': {}
'@types/ws@8.18.0':
dependencies:
'@types/node': 22.13.10
accepts@1.3.8:
dependencies:
mime-types: 2.1.35
@ -2889,6 +2998,10 @@ snapshots:
denque@2.1.0: {}
depd@2.0.0: {}
destroy@1.2.0: {}
dezalgo@1.0.4:
dependencies:
asap: 2.0.6
@ -2912,16 +3025,20 @@ snapshots:
eastasianwidth@0.2.0: {}
ee-first@1.1.1: {}
emoji-regex@8.0.0: {}
emoji-regex@9.2.2: {}
encodeurl@2.0.0: {}
engine.io-parser@5.2.3: {}
engine.io@6.6.4:
dependencies:
'@types/cors': 2.8.17
'@types/node': 22.13.8
'@types/node': 22.13.10
accepts: 1.3.8
base64id: 2.0.0
cookie: 0.7.2
@ -3057,6 +3174,8 @@ snapshots:
escalade@3.2.0: {}
escape-html@1.0.3: {}
escape-string-regexp@4.0.0: {}
escodegen@2.1.0:
@ -3077,6 +3196,8 @@ snapshots:
esutils@2.0.3: {}
etag@1.8.1: {}
eventemitter2@0.4.14: {}
eventemitter2@5.0.1: {}
@ -3134,6 +3255,8 @@ snapshots:
hexoid: 2.0.0
once: 1.4.0
fresh@0.5.2: {}
fs-extra@8.1.0:
dependencies:
graceful-fs: 4.2.11
@ -3278,6 +3401,14 @@ snapshots:
hexoid@2.0.0: {}
http-errors@2.0.0:
dependencies:
depd: 2.0.0
inherits: 2.0.4
setprototypeof: 1.2.0
statuses: 2.0.1
toidentifier: 1.0.1
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.3
@ -3626,6 +3757,10 @@ snapshots:
has-symbols: 1.1.0
object-keys: 1.1.1
on-finished@2.4.1:
dependencies:
ee-first: 1.1.1
once@1.4.0:
dependencies:
wrappy: 1.0.2
@ -3834,6 +3969,8 @@ snapshots:
queue-microtask@1.2.3: {}
range-parser@1.2.1: {}
read@1.0.7:
dependencies:
mute-stream: 0.0.8
@ -4009,6 +4146,23 @@ snapshots:
semver@7.7.1: {}
send@1.1.0:
dependencies:
debug: 4.4.0(supports-color@5.5.0)
destroy: 1.2.0
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
fresh: 0.5.2
http-errors: 2.0.0
mime-types: 2.1.35
ms: 2.1.3
on-finished: 2.4.1
range-parser: 1.2.1
statuses: 2.0.1
transitivePeerDependencies:
- supports-color
sequelize-pool@7.1.0: {}
sequelize@6.37.5(pg@8.13.3):
@ -4056,6 +4210,8 @@ snapshots:
es-errors: 1.3.0
es-object-atoms: 1.1.1
setprototypeof@1.2.0: {}
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
@ -4166,6 +4322,8 @@ snapshots:
standard-as-callback@2.1.0: {}
statuses@2.0.1: {}
stop-iteration-iterator@1.1.0:
dependencies:
es-errors: 1.3.0
@ -4260,6 +4418,8 @@ snapshots:
dependencies:
is-number: 7.0.0
toidentifier@1.0.1: {}
toposort-class@1.0.1: {}
touch@3.1.1: {}
@ -4396,7 +4556,7 @@ snapshots:
wkx@0.5.0:
dependencies:
'@types/node': 22.13.8
'@types/node': 22.13.10
wrap-ansi@7.0.0:
dependencies:
@ -4416,7 +4576,7 @@ snapshots:
ws@8.17.1: {}
ws@8.18.0: {}
ws@8.18.1: {}
xtend@4.0.2: {}

2
pnpm-workspace.yaml Normal file
View File

@ -0,0 +1,2 @@
packages:
- 'assistant-module'

View File

@ -1,8 +1,19 @@
import { App } from '@kevisual/router';
import { useContextKey } from '@kevisual/use-config/context';
import { httpsConfig } from './modules/config.ts';
const init = () => {
return new App();
const app = new App({
serverOptions: {
path: '/client/router',
httpType: 'https',
httpsCert: httpsConfig.cert.toString(),
httpsKey: httpsConfig.key.toString(),
},
});
return app;
};
export const app = useContextKey('app', init);

View File

@ -1,16 +0,0 @@
import { app } from './app.ts';
import { useConfig } from '@kevisual/use-config';
app
.route({
path: 'demo',
key: 'demo',
})
.define(async (ctx) => {
ctx.body = '123';
})
.addTo(app);
const config = useConfig();
console.log('run demo: http://localhost:' + config.port + '/api/router?path=demo&key=demo');

View File

@ -1,8 +1,18 @@
import { useConfig } from '@kevisual/use-config';
import { app } from './index.ts';
import { proxyRoute } from './proxy-route/index.ts';
const config = useConfig();
app
.route({
path: 'demo',
})
.define(async (ctx) => {
ctx.body = 'hello world';
})
.addTo(app);
app.listen(config.port, () => {
console.log(`server is running at http://localhost:${config.port}`);
console.log('httpsConfig', `https://localhost:51015/client/router?path=demo`);
app.listen(51015, () => {
console.log('Router App is running on https://localhost:51015');
});
app.server.on(proxyRoute);

View File

@ -1,4 +1,4 @@
import { app } from './app.ts';
import './demo-route.ts';
import './route/index.ts';
export { app };

9
src/modules/config.ts Normal file
View File

@ -0,0 +1,9 @@
import fs from 'fs';
import path from 'path';
const pemDir = path.join(process.cwd(), 'pem');
export const httpsConfig = {
key: fs.readFileSync(path.join(pemDir, 'https-key.pem')),
cert: fs.readFileSync(path.join(pemDir, 'https-cert.pem')),
};

108
src/modules/config/index.ts Normal file
View File

@ -0,0 +1,108 @@
import path from 'path';
import { homedir } from 'os';
import fs from 'fs';
import { checkFileExists, createDir } from '../file/index.ts';
export const kevisualUrl = 'https://kevisual.xiongxiao.me';
const configDir = createDir(path.join(homedir(), '.config/envision'));
export const configPath = path.join(configDir, 'assistant-config.json');
export const appConfigPath = path.join(configDir, 'assistant-app-config.json');
export const appDir = createDir(path.join(configDir, 'assistant-app/frontend'));
export const LocalElectronAppUrl = 'https://assistant.app/user/tiptap/';
type AssistantConfig = {
pageApi?: string; // https://kevisual.silkyai.cn
loadURL?: string; // https://assistant.app/user/tiptap/
proxy?: { user: string; key: string; path: string }[];
};
let assistantConfig: AssistantConfig;
export const getConfig = () => {
try {
if (!checkFileExists(configPath)) {
fs.writeFileSync(configPath, JSON.stringify({ proxy: [] }, null, 2));
return {
loadURL: LocalElectronAppUrl,
pageApi: '',
proxy: [],
};
}
assistantConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
return assistantConfig;
} catch (error) {
console.error(error);
return {
loadURL: LocalElectronAppUrl,
pageApi: '',
proxy: [],
};
}
};
export const getCacheAssistantConfig = () => {
if (assistantConfig) {
return assistantConfig;
}
return getConfig();
};
export const setConfig = (config?: AssistantConfig) => {
if (!config) {
return assistantConfig;
}
assistantConfig = config;
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
return assistantConfig;
};
type AppConfig = {
list: any[];
};
/**
*
* @returns
*/
export const getAppConfig = (): AppConfig => {
if (!checkFileExists(appConfigPath)) {
return {
list: [],
};
}
return JSON.parse(fs.readFileSync(appConfigPath, 'utf8'));
};
export const setAppConfig = (config: AppConfig) => {
fs.writeFileSync(appConfigPath, JSON.stringify(config, null, 2));
return config;
};
export const addAppConfig = (app: any) => {
const config = getAppConfig();
const assistantConfig = getCacheAssistantConfig();
const _apps = config.list;
const _proxy = assistantConfig.proxy || [];
const { user, key } = app;
const newProxyInfo = {
user,
key,
path: `/${user}/${key}`,
};
const _proxyIndex = _proxy.findIndex((_proxy: any) => _proxy.path === newProxyInfo.path);
if (_proxyIndex !== -1) {
_proxy[_proxyIndex] = newProxyInfo;
} else {
_proxy.push(newProxyInfo);
}
const _app = _apps.findIndex((_app: any) => _app.id === app.id);
if (_app !== -1) {
_apps[_app] = app;
} else {
_apps.push(app);
}
setAppConfig({ ...config, list: _apps });
setConfig({ ...assistantConfig, proxy: _proxy });
return config;
};
export const getAppList = () => {
const config = getAppConfig();
return config.list || [];
};

20
src/modules/file/index.ts Normal file
View File

@ -0,0 +1,20 @@
import fs from 'fs';
export const checkFileExists = (filePath: string, checkIsFile = false) => {
try {
fs.accessSync(filePath);
if (checkIsFile) {
return fs.statSync(filePath).isFile();
}
return true;
} catch (error) {
return false;
}
};
export const createDir = (dirPath: string) => {
if (!checkFileExists(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
return dirPath;
};

156
src/modules/install.ts Normal file
View File

@ -0,0 +1,156 @@
import path from 'path';
import fs from 'fs';
import { appDir, kevisualUrl, addAppConfig, getAppConfig, setAppConfig, getCacheAssistantConfig, setConfig } from './config/index.ts';
export const demoData = {
id: '471ee96f-d7d8-4da1-b84f-4a34f4732f16',
title: 'tiptap',
description: '',
data: {
files: [
{
name: 'README.md',
path: 'user/tiptap/0.0.1/README.md',
},
{
name: 'app.css',
path: 'user/tiptap/0.0.1/app.css',
},
{
name: 'app.js',
path: 'user/tiptap/0.0.1/app.js',
},
{
name: 'create-BxEwtceK.js',
path: 'user/tiptap/0.0.1/create-BxEwtceK.js',
},
{
name: 'index.CrTXFMOJ.js',
path: 'user/tiptap/0.0.1/index.CrTXFMOJ.js',
},
{
name: 'index.html',
path: 'user/tiptap/0.0.1/index.html',
},
],
},
version: '0.0.1',
domain: '',
appType: '',
key: 'tiptap',
type: '',
uid: '2bebe6a0-3c64-4a64-89f9-cc47fd082a07',
pid: null,
proxy: false,
user: 'user',
status: 'running',
createdAt: '2024-12-14T15:39:30.684Z',
updatedAt: '2024-12-14T15:39:55.714Z',
deletedAt: null,
};
type DownloadTask = {
downloadPath: string;
downloadUrl: string;
user: string;
key: string;
version: string;
};
export type Package = {
id: string;
name?: string;
version?: string;
description?: string;
title?: string;
user?: string;
key?: string;
[key: string]: any;
};
export const installApp = async (app: Package) => {
// const _app = demoData;
const _app = app;
try {
let files = _app.data.files || [];
const version = _app.version;
const user = _app.user;
const key = _app.key;
const downFiles = files.map((file: any) => {
const noVersionPath = file.path.replace(`/${version}`, '');
return {
...file,
downloadPath: path.join(appDir, noVersionPath),
downloadUrl: `${kevisualUrl}/${noVersionPath}`,
};
});
const downloadTasks: DownloadTask[] = downFiles as any;
for (const file of downloadTasks) {
const downloadPath = file.downloadPath;
const downloadUrl = file.downloadUrl;
const dir = path.dirname(downloadPath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
const res = await fetch(downloadUrl);
const blob = await res.blob();
fs.writeFileSync(downloadPath, Buffer.from(await blob.arrayBuffer()));
}
let indexHtml = files.find((file: any) => file.name === 'index.html');
if (!indexHtml) {
files.push({
name: 'index.html',
path: `${user}/${key}/index.html`,
});
fs.writeFileSync(path.join(appDir, `${user}/${key}/index.html`), JSON.stringify(app, null, 2));
}
_app.data.files = files;
addAppConfig(_app);
return {
code: 200,
data: _app,
message: 'Install app success',
};
} catch (error) {
console.error(error);
return {
code: 500,
message: 'Install app failed',
};
}
};
export const uninstallApp = async (app: Package) => {
try {
const { user, key } = app;
const appConfig = getAppConfig();
const index = appConfig.list.findIndex((item: any) => item.user === user && item.key === key);
if (index !== -1) {
appConfig.list.splice(index, 1);
setAppConfig(appConfig);
// 删除appDir和文件
fs.rmSync(path.join(appDir, user, key), { recursive: true });
// 删除proxy
const proxyConfig = getCacheAssistantConfig();
const proxyIndex = proxyConfig.proxy.findIndex((item: any) => item.user === user && item.key === key);
if (proxyIndex !== -1) {
proxyConfig.proxy.splice(proxyIndex, 1);
setConfig(proxyConfig);
}
}
return {
code: 200,
message: 'Uninstall app success',
};
} catch (error) {
console.error(error);
return {
code: 500,
message: 'Uninstall app failed',
};
}
};
export const getInstallList = async () => {
const appConfig = getAppConfig();
return appConfig.list;
};

61
src/proxy-route/index.ts Normal file
View File

@ -0,0 +1,61 @@
import { fileProxy, apiProxy, createApiProxy } from '@kevisual/assistant-module/proxy';
import { getCacheAssistantConfig, appDir } from '@kevisual/assistant-module';
import http from 'http';
// https://localhost:51015/user/tiptap/
export const proxyRoute = async (req: http.IncomingMessage, res: http.ServerResponse) => {
const assistantConfig = getCacheAssistantConfig();
// const { apiList } = assistantConfig;
const url = new URL(req.url, 'http://localhost');
const pathname = url.pathname;
if (pathname.startsWith('/favicon.ico')) {
res.statusCode = 404;
res.end('Not Found Favicon');
return;
}
if (pathname.startsWith('/client')) {
console.log('handle by router');
return;
}
const apiProxyList = assistantConfig?.apiProxyList || [];
const defaultApiProxy = createApiProxy(assistantConfig?.pageApi || 'https://kevisual.xiongxiao.me');
const apiBackendProxy = [...apiProxyList, ...defaultApiProxy].find((item) => pathname.startsWith(item.path));
if (apiBackendProxy) {
console.log('apiBackendProxy', apiBackendProxy);
return apiProxy(req, res, {
path: apiBackendProxy.path,
target: apiBackendProxy.target,
});
}
// client, api, v1, serve 开头的拦截
const proxyApiList = assistantConfig?.proxy || [];
const proxyApi = proxyApiList.find((item) => pathname.startsWith(item.path));
if (proxyApi) {
console.log('proxyApi', proxyApi, pathname);
const { user, key } = proxyApi;
return fileProxy(req, res, {
path: proxyApi.path,
rootPath: appDir,
indexPath: `${user}/${key}/index.html`,
});
}
const localProxyProxy = localProxyProxyList.find((item) => pathname.startsWith(item.path));
if (localProxyProxy) {
return fileProxy(req, res, {
path: localProxyProxy.path,
rootPath: process.cwd(),
indexPath: localProxyProxy.indexPath,
});
}
console.log('handle by router 404');
res.statusCode = 404;
res.end('Not Found Proxy');
};
const localProxyProxyList = [
{
user: 'root',
key: 'assistant-base-app',
path: '/root/assistant-base-app',
indexPath: 'root/assistant-base-app/index.html',
},
];

View File

@ -0,0 +1,42 @@
import net from 'net';
import { App } from '@kevisual/router';
export const wsProxy = (app: App, config: { apiList: any[] }) => {
console.log('Upgrade initialization started');
app.server.server.on('upgrade', (req, socket, head) => {
const proxyApiList = config?.apiList || [];
const proxyApi = proxyApiList.find((item) => req.url.startsWith(item.path));
if (proxyApi) {
const _u = new URL(req.url, `${proxyApi.target}`);
const options = {
hostname: _u.hostname,
port: Number(_u.port) || 80,
path: _u.pathname,
headers: req.headers,
};
const proxySocket = net.connect(options.port, options.hostname, () => {
proxySocket.write(
`GET ${options.path} HTTP/1.1\r\n` +
`Host: ${options.hostname}\r\n` +
`Connection: Upgrade\r\n` +
`Upgrade: websocket\r\n` +
`Sec-WebSocket-Key: ${req.headers['sec-websocket-key']}\r\n` +
`Sec-WebSocket-Version: ${req.headers['sec-websocket-version']}\r\n` +
`\r\n`,
);
proxySocket.pipe(socket);
socket.pipe(proxySocket);
});
proxySocket.on('error', (err) => {
console.error(`WebSocket proxy error: ${err.message}`);
socket.end();
});
} else {
socket.end();
}
});
};

10
src/route/client/check.ts Normal file
View File

@ -0,0 +1,10 @@
import { app } from '@/app.ts';
app
.route({
path: 'check',
})
.define(async (ctx) => {
ctx.body = 'ok';
})
.addTo(app);

25
src/route/config/index.ts Normal file
View File

@ -0,0 +1,25 @@
import { app } from '@/app.ts';
import { getCacheAssistantConfig, setConfig } from '@/modules/config/index.ts';
app
.route({
path: 'config',
description: '获取配置',
})
.define(async (ctx) => {
ctx.body = getCacheAssistantConfig();
})
.addTo(app);
app
.route({
path: 'config',
key: 'set',
description: '设置配置',
})
.define(async (ctx) => {
const { data } = ctx.query;
ctx.body = setConfig(data);
})
.addTo(app);

3
src/route/index.ts Normal file
View File

@ -0,0 +1,3 @@
import './shop-install/index.ts';
import './client/check.ts';
import './config/index.ts';

View File

@ -0,0 +1,40 @@
import { app } from '@/app.ts';
import { getInstallList, installApp, uninstallApp } from '@/modules/install.ts';
app
.route({
path: 'shop',
key: 'list-installed',
})
.define(async (ctx) => {
// https://localhost:51015/client/router?path=shop&key=list-installed
const list = await getInstallList();
ctx.body = list;
})
.addTo(app);
app
.route({
path: 'shop',
key: 'install',
})
.define(async (ctx) => {
// https://localhost:51015/client/router?path=shop&key=install
const { pkg } = ctx.query.data;
const res = await installApp(pkg);
ctx.body = res;
})
.addTo(app);
app
.route({
path: 'shop',
key: 'uninstall',
})
.define(async (ctx) => {
// https://localhost:51015/client/router?path=shop&key=uninstall
const { pkg } = ctx.query.data;
const res = await uninstallApp(pkg);
ctx.body = res;
})
.addTo(app);

View File

@ -0,0 +1,8 @@
import { getCacheAssistantConfig, appConfigPath, appDir } from '@kevisual/assistant-module';
import fs from 'fs';
const assistantConfig = getCacheAssistantConfig();
console.log(assistantConfig);
console.log('appConfigPath', appConfigPath);
console.log('appDir', appDir);