From f3c0707666a9ee4b2f073ba0bb688a82059746b3 Mon Sep 17 00:00:00 2001 From: xion Date: Sat, 29 Jun 2024 23:09:32 +0800 Subject: [PATCH] feat: perfect router lib add validator --- package-lock.json | 20 +++-- package.json | 2 +- src/admin/index.ts | 170 +--------------------------------------- src/admin/manager.ts | 55 +++++++++++++ src/admin/router.ts | 183 +++++++++++++++++++++++++++++++++++++++++++ src/models/code.ts | 8 +- yarn.lock | 20 ++--- 7 files changed, 274 insertions(+), 184 deletions(-) create mode 100644 src/admin/manager.ts create mode 100644 src/admin/router.ts diff --git a/package-lock.json b/package-lock.json index f816c4b..39d38b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.2", "license": "ISC", "dependencies": { - "@abearxiong/router": "^0.0.1-alpha.8", + "@abearxiong/router": "^0.0.1-alpha.12", "@abearxiong/use-config": "^0.0.1", "@babel/core": "^7.24.7", "@babel/preset-env": "^7.24.7", @@ -50,12 +50,14 @@ } }, "node_modules/@abearxiong/router": { - "version": "0.0.1-alpha.8", - "resolved": "https://npm.pkg.github.com/download/@abearxiong/router/0.0.1-alpha.8/772276fc4290291f845b0bb7198acc1c439f72e3", - "integrity": "sha512-uX9YkeS+TDKKH8y1fShTKgxcXWeawaoLLi/kdX04M84o3sg1vZSic4wxEMuznAjESX/iYIc94vXyE6cevoWVLQ==", + "version": "0.0.1-alpha.12", + "resolved": "https://npm.pkg.github.com/download/@abearxiong/router/0.0.1-alpha.12/ca2bf4832b4f032965b4c579b7bbde04a05f893a", + "integrity": "sha512-Hqe8HdxUTCjl31ExVpb1Ro4KuvqsjcbZwcISyzImDe5xN4yMBh3zZCZOTrbSB9OsxukX5rqQ4kvufLWbbfp46g==", "license": "ISC", "dependencies": { - "nanoid": "^5.0.6" + "lodash": "^4.17.21", + "nanoid": "^5.0.6", + "zod": "^3.23.8" } }, "node_modules/@abearxiong/use-config": { @@ -8141,6 +8143,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmmirror.com/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 5831b6a..26f200f 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "author": "", "license": "ISC", "dependencies": { - "@abearxiong/router": "^0.0.1-alpha.8", + "@abearxiong/router": "^0.0.1-alpha.12", "@abearxiong/use-config": "^0.0.1", "@babel/core": "^7.24.7", "@babel/preset-env": "^7.24.7", diff --git a/src/admin/index.ts b/src/admin/index.ts index 243e5f8..5ce6c6a 100644 --- a/src/admin/index.ts +++ b/src/admin/index.ts @@ -1,168 +1,2 @@ -// admin 需要最后运行,并在route中进行过滤。 -import { Route } from '@abearxiong/router'; -import { router } from '../modules/router.ts'; -import { manager, updateNewCode, removeCode, stopCode, startCode } from './dashboard/manager.ts'; -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'); - -getRouterList.run = async (ctx) => { - ctx.body = router.getList().filter((r) => !r.path.startsWith('admin')); - // ctx.body = router.getList().filter((r) => r.path.startsWith('admin')); - return ctx; -}; - -router.add(getRouterList); - -// remove router -export const removeRouter = new Route('admin', 'removeRouter'); -removeRouter.run = async (ctx) => { - const { path, key } = ctx.query; - router.remove({ path, key }); - const routerCode = await RouterCodeModel.findOne({ where: { path, key } }); - if (routerCode) { - const id = routerCode.id; - removeCode(id); - await RouterCodeModel.destroy({ where: { id } }); - } - ctx.body = 'success'; - return ctx; -}; -router.add(removeRouter); - -// remove router by id -export const removeRouterById = new Route('admin', 'removeRouterById'); -removeRouterById.run = async (ctx) => { - const { id } = ctx.query; - router.removeById(id); - removeCode(id); - await RouterCodeModel.destroy({ where: { id } }); - ctx.body = 'success'; - return ctx; -}; -router.add(removeRouterById); - -// stop router by id -export const stopRouterById = new Route('admin', 'stopRouterById'); -stopRouterById.run = async (ctx) => { - const { id } = ctx.query; - router.removeById(id); - const routerCode = await RouterCodeModel.findByPk(id); - if (routerCode) { - routerCode.active = false; - await routerCode.save(); - } - stopCode(id); - 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(); - startCode(routerCode); - } - 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, type = 'route' } = ctx.query; - if (!path && !key) { - ctx.body = 'path and key is required'; - ctx.code = 500; - return ctx; - } - let codeRouter: RouterCodeModel | null = null; - const codeRouteCheck = await RouterCodeModel.findOne({ where: { path, key } }); // 检查是否存在 - 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 { - 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); - updateNewCode(codeOne); - - ctx.body = 'success'; - return ctx; -}; -router.add(updateRouter); - -// get manager status -export const managerRouter = new Route('admin', 'getManagerStatus'); -managerRouter.run = async (ctx) => { - ctx.body = { - status: manager.loaded, - msg: 'system is running, and load manager success.', - }; - return ctx; -}; -router.add(managerRouter); - -// get manager list -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); - -// get manager one -export const managerOne = new Route('admin', 'getManagerOne'); -managerOne.run = async (ctx) => { - const { id } = ctx.query; - const code = manager.list.find((c) => c.id === id); - if (code) { - ctx.body = code; - } else { - ctx.body = 'not found'; - ctx.code = 404; - } - - return ctx; -}; -router.add(managerOne); +import './router.ts'; +import './manager.ts'; diff --git a/src/admin/manager.ts b/src/admin/manager.ts new file mode 100644 index 0000000..96f3fb0 --- /dev/null +++ b/src/admin/manager.ts @@ -0,0 +1,55 @@ +// admin 需要最后运行,并在route中进行过滤。 +import { Route } from '@abearxiong/router'; +import { router } from '../modules/router.ts'; +import { manager, updateNewCode, removeCode, stopCode, startCode } from './dashboard/manager.ts'; +import { loadOne } from './dashboard/load.ts'; +import { RouterCodeModel } from '../models/code.ts'; +// get manager status +export const managerRouter = new Route('admin', 'getManagerStatus'); +managerRouter.run = async (ctx) => { + ctx.body = { + status: manager.loaded, + msg: 'system is running, and load manager success.', + }; + return ctx; +}; +router.add(managerRouter); + +// get manager list +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); + +// get manager one +export const managerOne = new Route('admin', 'getManagerOne'); +managerOne.run = async (ctx) => { + const verfiy = ctx.currentRoute.verify(ctx); + if (verfiy) return; + + const { id } = ctx.query; + const code = manager.list.find((c) => c.id === id); + if (code) { + ctx.body = code; + } else { + ctx.body = 'not found'; + ctx.code = 404; + } + + return ctx; +}; +managerOne.validator = { + id: { + type: 'string', + required: true, + }, +}; + +router.add(managerOne); diff --git a/src/admin/router.ts b/src/admin/router.ts new file mode 100644 index 0000000..51ea8a6 --- /dev/null +++ b/src/admin/router.ts @@ -0,0 +1,183 @@ +// admin router manger + +import { Route } from '@abearxiong/router'; +import { router } from '../modules/router.ts'; +import { manager, updateNewCode, removeCode, stopCode, startCode } from './dashboard/manager.ts'; +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'); + +getRouterList.run = async (ctx) => { + ctx.body = router.getList().filter((r) => !r.path.startsWith('admin')); + // ctx.body = router.getList().filter((r) => r.path.startsWith('admin')); + return ctx; +}; + +router.add(getRouterList); + +// remove router +export const removeRouter = new Route('admin', 'removeRouter'); +removeRouter.run = async (ctx) => { + const { path, key } = ctx.query; + router.remove({ path, key }); + const routerCode = await RouterCodeModel.findOne({ where: { path, key } }); + if (routerCode) { + const id = routerCode.id; + removeCode(id); + await RouterCodeModel.destroy({ where: { id } }); + } + ctx.body = 'success'; + return ctx; +}; +removeRouter.validator = { + path: { + type: 'string', + required: true, + }, + key: { + type: 'string', + required: true, + }, +}; +router.add(removeRouter); + +// remove router by id +export const removeRouterById = new Route('admin', 'removeRouterById'); +removeRouterById.run = async (ctx) => { + const { id } = ctx.query; + router.removeById(id); + removeCode(id); + await RouterCodeModel.destroy({ where: { id } }); + ctx.body = 'success'; + return ctx; +}; +removeRouterById.validator = { + id: { + type: 'string', + required: true, + }, +}; +router.add(removeRouterById); + +// stop router by id +export const stopRouterById = new Route('admin', 'stopRouterById'); +stopRouterById.run = async (ctx) => { + const { id } = ctx.query; + router.removeById(id); + const routerCode = await RouterCodeModel.findByPk(id); + if (routerCode) { + routerCode.active = false; + await routerCode.save(); + } + stopCode(id); + ctx.body = 'success'; + return ctx; +}; +stopRouterById.validator = { + id: { + type: 'string', + required: true, + }, +}; +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(); + startCode(routerCode); + } + ctx.body = 'success'; + return ctx; +}; +startRouterById.validator = { + id: { + type: 'string', + required: true, + }, +}; +router.add(startRouterById); + +// add or update router +export const updateRouter = new Route('admin', 'updateRouter'); +updateRouter.run = async (ctx) => { + let { path, key, id, code, type = 'route' } = ctx.query; + if (!path && !key) { + ctx.body = 'path and key is required'; + ctx.code = 500; + return ctx; + } + let codeRouter: RouterCodeModel | null = null; + const codeRouteCheck = await RouterCodeModel.findOne({ where: { path, key } }); // 检查是否存在 + 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 { + 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); + updateNewCode(codeOne); + + ctx.body = 'success'; + return ctx; +}; +router.add(updateRouter); + +export const getRouterApi = new Route('admin', 'getRouterApi'); +getRouterApi.description = 'get all router api list, and you can use this api to get router detail by path and key'; +getRouterApi.run = async (ctx) => { + const { origin = 'http://localhost:4000' } = ctx.query; + const routerList = router.getList(); + const apiList = routerList.map((item: any) => { + return { + path: item.path, + key: item.key, + query: `${origin}/api/router?path=${item.path}&key=${item.key}`, + description: item.description, + validator: item.validator, + }; + }); + ctx.body = apiList; + return ctx; +}; +getRouterApi.validator = { + origin: { + type: 'string', + required: false, + }, +}; + +router.add(getRouterApi); diff --git a/src/models/code.ts b/src/models/code.ts index c845da9..6b5c81d 100644 --- a/src/models/code.ts +++ b/src/models/code.ts @@ -13,6 +13,7 @@ export type RouterCode = { middleware: string[]; next: string; data: any; + validator: any; }; export enum RouterCodeType { @@ -31,7 +32,8 @@ export class RouterCodeModel extends Model { declare type: RouterCodeType; declare middleware: string[]; declare next: string; // 如果是中间件,不存在 - declare data: any; + declare data: any; // 内容 + declare validator: any; } RouterCodeModel.init( { @@ -81,6 +83,10 @@ RouterCodeModel.init( type: DataTypes.JSON, defaultValue: {}, }, + validator: { + type: DataTypes.JSON, + defaultValue: {}, + }, }, { sequelize, diff --git a/yarn.lock b/yarn.lock index 0306f3b..9cd79ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,12 +2,14 @@ # yarn lockfile v1 -"@abearxiong/router@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://npm.pkg.github.com/download/@abearxiong/router/0.0.1-alpha.8/772276fc4290291f845b0bb7198acc1c439f72e3" - integrity sha512-uX9YkeS+TDKKH8y1fShTKgxcXWeawaoLLi/kdX04M84o3sg1vZSic4wxEMuznAjESX/iYIc94vXyE6cevoWVLQ== +"@abearxiong/router@^0.0.1-alpha.12": + version "0.0.1-alpha.12" + resolved "https://npm.pkg.github.com/download/@abearxiong/router/0.0.1-alpha.12/ca2bf4832b4f032965b4c579b7bbde04a05f893a" + integrity sha512-Hqe8HdxUTCjl31ExVpb1Ro4KuvqsjcbZwcISyzImDe5xN4yMBh3zZCZOTrbSB9OsxukX5rqQ4kvufLWbbfp46g== dependencies: + lodash "^4.17.21" nanoid "^5.0.6" + zod "^3.23.8" "@abearxiong/use-config@^0.0.1": version "0.0.1" @@ -2625,11 +2627,6 @@ fs.realpath@^1.0.0: resolved "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2: - version "2.3.3" - resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz" @@ -4776,3 +4773,8 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zod@^3.23.8: + version "3.23.8" + resolved "https://registry.npmmirror.com/zod/-/zod-3.23.8.tgz" + integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==