feat: add delete route

This commit is contained in:
熊潇 2024-06-28 00:14:39 +08:00
parent c0a1460ad8
commit 60653a5ba8
16 changed files with 2760 additions and 200 deletions

View File

@ -4,15 +4,17 @@ FROM node:22-alpine
# 设置工作目录
WORKDIR /app
COPY script/package/package.json ./
# 复制 package.json 和 package-lock.json
COPY package*.json ./
# 安装依赖
# RUN npm install --production
# COPY package*.json ./
# 复制 dist 文件夹
COPY dist ./dist
COPY app.config.json5 ./dist/app.config.json5
COPY app.config.json5 ./app.config.json5
COPY .npmrc .
# 安装依赖
RUN npm install --production --registry=https://registry.npmmirror.com/
# 如果有其他静态资源文件夹,也可以一并复制
# COPY public ./public
@ -22,3 +24,4 @@ EXPOSE 4000
# 启动应用
CMD ["node", "dist/app.cjs"]
# CMD ["tail", "-f", "/dev/null"]

1732
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "code-flow",
"version": "1.0.0",
"version": "0.0.2",
"description": "code的flow流程成图",
"type": "module",
"main": "index.js",
@ -14,17 +14,21 @@
"deploy:sh": "",
"clean": "rm -rf dist",
"reload": "ssh light pm2 restart codeflow",
"docker:build": "docker build -t docker.xiongxiao.me/code-flow:v0.0.1 .",
"docker:push": "docker push docker.xiongxiao.me/code-flow:v0.0.1",
"docker:build:gitea": "docker build -t git.xiongxiao.me/abearxiong/code-flow:v0.0.1 .",
"docker:push:gitea": "docker push git.xiongxiao.me/abearxiong/code-flow:v0.0.1"
"docker:build": "docker build -t docker.xiongxiao.me/code-flow:v0.0.2 .",
"docker:push": "docker push docker.xiongxiao.me/code-flow:v0.0.2",
"docker:run": "docker run -it --name code-flow -p 4000:4000 docker.xiongxiao.me/code-flow:v0.0.2",
"docker:build:gitea": "docker build -t git.xiongxiao.me/abearxiong/code-flow:v0.0.2 .",
"docker:push:gitea": "docker push git.xiongxiao.me/abearxiong/code-flow:v0.0.2"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@abearxiong/router": "^0.0.1-alpha.6",
"@abearxiong/router": "^0.0.1-alpha.8",
"@abearxiong/use-config": "^0.0.1",
"@babel/core": "^7.24.7",
"@babel/preset-env": "^7.24.7",
"@babel/preset-typescript": "^7.24.7",
"dayjs": "^1.11.11",
"json5": "^2.2.3",
"jsonwebtoken": "^9.0.2",
@ -32,14 +36,15 @@
"nanoid": "^5.0.7",
"pg": "^8.12.0",
"sequelize": "^6.37.3",
"socket.io": "^4.7.5"
"socket.io": "^4.7.5",
"strip-ansi": "^7.1.0"
},
"devDependencies": {
"@types/crypto-js": "^4.2.2",
"@types/jest": "^29.5.12",
"@types/jsonwebtoken": "^9.0.6",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.14.8",
"@types/node": "^20.14.9",
"@types/superagent": "^8.1.7",
"@types/supertest": "^6.0.2",
"@types/webpack-env": "^1.18.5",
@ -59,4 +64,4 @@
"webpack-node-externals": "^3.0.0"
},
"packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
}
}

26
script/package/index.mjs Normal file
View File

@ -0,0 +1,26 @@
import fs from 'fs';
import path from 'path';
const currentPath = process.cwd();
const packagePath = path.join(currentPath, 'script/package/package.json');
fs.writeFileSync(
packagePath,
JSON.stringify(
{
name: 'codeflow',
version: '1.0.0',
dependencies: {
'@babel/core': '^7.24.7',
'@babel/preset-env': '^7.24.7',
'@babel/preset-typescript': '^7.24.7',
sequelize: '^6.37.3',
'socket.io': '^4.7.5',
pg: '^8.12.0'
},
},
null,
2,
),
'utf-8',
);

View File

@ -0,0 +1,12 @@
{
"name": "codeflow",
"version": "1.0.0",
"dependencies": {
"@babel/core": "^7.24.7",
"@babel/preset-env": "^7.24.7",
"@babel/preset-typescript": "^7.24.7",
"sequelize": "^6.37.3",
"socket.io": "^4.7.5",
"pg": "^8.12.0"
}
}

View File

@ -17,21 +17,30 @@ const codeDemoRun = `async function run(ctx) {
ctx.body = 'test js';
return ctx;
}`;
export const loadOne = async (item: RouterCodeModel) => {
const { path, key, id, code, project } = item.toJSON();
try {
const fn: any = new Function(
'ctx',
`
${code}
const templateFn = (codeStr: string) => {
return `
${codeStr}
if(run) {
return run(ctx);
`,
);
}
if(main) {
return main(ctx);
}
return 'no run or main function';
`;
};
export const loadOne = async (item: RouterCodeModel) => {
const { path, key, id, code, exec, project } = item.toJSON();
const codeStr = exec || code;
try {
const fn: any = new Function('ctx', templateFn(codeStr));
// run code
const codeRunRoute = new Route(path, key, { id });
codeRunRoute.run = fn;
router.removeById(id); // TODO:
router.add(codeRunRoute);
return {
...item.toJSON(),
path,
key,
id,
@ -54,9 +63,10 @@ export const loadOne = async (item: RouterCodeModel) => {
export const load = async function () {
const codes = await RouterCodeModel.findAll();
const codeManager: CodeManager[] = codes.map((item) => {
const { path, key, id, code, project, active } = item.toJSON();
const { path, key, id, code, exec, project, active } = item.toJSON();
if (!active) {
return {
...item.toJSON(),
path,
key,
id,
@ -66,18 +76,14 @@ export const load = async function () {
};
}
try {
const fn: any = new Function(
'ctx',
`
${code}
return run(ctx);
`,
);
const codeStr = exec || code;
const fn: any = new Function('ctx', templateFn(codeStr));
// run code
const codeRunRoute = new Route(path, key, { id });
codeRunRoute.run = fn;
router.add(codeRunRoute);
return {
...item.toJSON(),
path,
key,
id,

View File

@ -36,6 +36,7 @@ export const stopCode = (id: string) => {
};
export const startCode = async (code: RouterCodeModel) => {
const index = manager.list.findIndex((item) => item.id === code.id);
console.log('index', index, code.toJSON())
if (index !== -1) {
manager.list[index] = await loadOne(code);
} else {

View File

@ -5,6 +5,7 @@ import { manager, updateNewCode, removeCode, stopCode, startCode } from './dashb
import { loadOne } from './dashboard/load.ts';
import { RouterCodeModel } from '../models/code.ts';
import { nanoid } from 'nanoid';
import { convertTsToJs as transform } from '../lib/ts2js.ts';
export const getRouterList = new Route('admin', 'getRouterList');
@ -58,11 +59,14 @@ stopRouterById.run = async (ctx) => {
ctx.body = 'success';
return ctx;
};
router.add(stopRouterById);
// start router by id
export const startRouterById = new Route('admin', 'startRouterById');
startRouterById.run = async (ctx) => {
const { id } = ctx.query;
const routerCode = await RouterCodeModel.findByPk(id);
console.log('routerCode', id, routerCode.toJSON());
if (routerCode) {
routerCode.active = true;
await routerCode.save();
@ -71,10 +75,12 @@ startRouterById.run = async (ctx) => {
ctx.body = 'success';
return ctx;
};
router.add(startRouterById);
// add or update router
export const updateRouter = new Route('admin', 'updateRouter');
updateRouter.run = async (ctx) => {
let { path, key, id, code } = ctx.query;
let { path, key, id, code, type = 'route' } = ctx.query;
if (!path && !key) {
ctx.body = 'path and key is required';
ctx.code = 500;
@ -85,16 +91,32 @@ updateRouter.run = async (ctx) => {
if (codeRouteCheck && codeRouteCheck.id !== id) {
key = `${key}-${nanoid(6)}`;
}
if (id) {
codeRouter = await RouterCodeModel.findByPk(id);
codeRouter.path = path;
codeRouter.key = key;
codeRouter.code = code;
try {
codeRouter.exec = await transform(code);
} catch (e) {
ctx.body = e.message.toString();
ctx.code = 500;
return ctx;
}
codeRouter.type = type;
await codeRouter.save();
} else {
const newCodeRouter = new RouterCodeModel({ path, key, code });
await newCodeRouter.save();
codeRouter = newCodeRouter;
try {
const exec = await transform(code);
const newCodeRouter = new RouterCodeModel({ path, key, code, exec, type });
await newCodeRouter.save();
codeRouter = newCodeRouter;
} catch (e) {
ctx.body = e.message.toString();
ctx.code = 500;
return ctx;
}
}
const codeOne = await loadOne(codeRouter);
@ -120,6 +142,11 @@ router.add(managerRouter);
export const managerList = new Route('admin', 'getManagerList');
managerList.run = async (ctx) => {
ctx.body = manager.list;
const routerList = router.getList().filter((r) => !r.path.startsWith('admin'));
ctx.body = {
list: manager.list,
routerList,
};
return ctx;
};
router.add(managerList);

View File

@ -13,6 +13,7 @@ const server = http.createServer((req, res) => {
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader('Access-Control-Max-Age', '86400');
res.setHeader('Content-Type', 'application/json');
// routerServer.handle(req, res);
return routerServer.callback()(req, res);
});

1
src/lib/index.ts Normal file
View File

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

32
src/lib/ts2js.ts Normal file
View File

@ -0,0 +1,32 @@
import * as babel from '@babel/core';
import stripAnsi from 'strip-ansi';
/**
* ts js
* @param tsCode
* @returns
*/
export const convertTsToJs = async (tsCode: string) => {
const presetEnv = await import('@babel/preset-env');
const presetTypescript = await import('@babel/preset-typescript');
try {
const result = babel.transformSync(tsCode, {
presets: [
presetTypescript.default,
[
presetEnv.default,
{
targets: {
node: 20,
},
},
],
],
filename: 'temp.ts',
});
return result.code;
} catch (e) {
const message = e.message.split('temp.ts:')[1] || e.message;
throw new Error(stripAnsi(message));
}
};

View File

@ -8,6 +8,7 @@ export type RouterCode = {
active: boolean;
project: string;
code: string;
exec: string;
type: RouterCodeType;
middleware: string[];
next: string;
@ -25,6 +26,7 @@ export class RouterCodeModel extends Model {
declare active: boolean;
declare project: string;
declare code: string;
declare exec: string;
declare type: RouterCodeType;
declare middleware: string[];
declare next: string; // 如果是中间件,不存在
@ -57,6 +59,10 @@ RouterCodeModel.init(
type: DataTypes.STRING,
defaultValue: '',
},
exec: {
type: DataTypes.STRING, // 对代码进行编译后的代码
defaultValue: '',
},
type: {
type: DataTypes.ENUM(RouterCodeType.route, RouterCodeType.middleware),
defaultValue: RouterCodeType.route,

View File

@ -3,14 +3,18 @@ import { router } from './modules/router.ts';
import './demo/index.ts';
import './admin/index.ts';
type Message = {
path: string;
key?: string;
};
export const handleMessage = async function (m: Message) {
console.log('message', m);
if (!m) {
return {
code: 400,
message: 'message is empty',
};
}
const res = await router.parse(m);
const { code, body, message } = res;

View File

@ -28,15 +28,17 @@ describe('RouterCodeModel', () => {
// yarn test --testNamePattern='RouterCodeModel:create'
test('RouterCodeModel:create', async () => {
try {
const demoCode = `async function run(ctx) {
ctx.body = 'test js';
return ctx;
}`;
const file = await RouterCodeModel.create({
path: 'demo',
key: 'returnDemo',
active: true,
project: 'default',
code: `async function run(ctx) {
ctx.body = 'test js';
return ctx;
}`,
code: demoCode,
exec: demoCode,
});
console.log('create success', file);
await sleep(2000);

View File

@ -9,9 +9,7 @@ const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const ENV = process.env.ENV;
const plugins = [
new ForkTsCheckerWebpackPlugin(),
];
const plugins = [new ForkTsCheckerWebpackPlugin()];
if (ENV === 'init') {
plugins.push(
new CopyPlugin({
@ -19,6 +17,9 @@ if (ENV === 'init') {
}),
);
}
/**
* @type {webpack.Configuration}
*/
module.exports = {
mode: 'production',
entry: path.join(__dirname, './src/app.ts'),
@ -56,16 +57,21 @@ module.exports = {
extensions: ['.ts', '.js'],
alias: {
'@': path.join(__dirname, './src'),
"hexoid": "hexoid/dist/index.js",
hexoid: 'hexoid/dist/index.js',
},
},
// externals: [
// externals: [
// // nodeExternals(),
// ],
externals: {
sequelize: 'commonjs sequelize',
'socket.io': 'commonjs socket.io',
'@babel/preset-env': 'commonjs @babel/preset-env',
'@babel/preset-typescript': 'commonjs @babel/preset-typescript',
},
plugins: [...plugins],
node: {},
stats: {
errorDetails: true,
},
};

1004
yarn.lock

File diff suppressed because it is too large Load Diff