feat: add admin router manager

This commit is contained in:
xion
2024-06-25 00:52:43 +08:00
parent f8777bd4ea
commit 3454e39ea4
18 changed files with 831 additions and 368 deletions

View File

@@ -0,0 +1 @@
export { manager } from './manager.ts';

View File

@@ -0,0 +1,57 @@
import { router } from '../../modules/router.ts';
import { Route } from '@abearxiong/router';
import { RouterCodeModel, RouterCode } from '../../models/code.ts';
enum CodeStatus {
success = 0,
fail = 1,
}
export type CodeManager = {
fn?: any;
status?: CodeStatus;
errorMsg?: string;
} & Partial<RouterCode>;
const codeDemoRun = `async function run(ctx) {
ctx.body = 'test js';
return ctx;
}`;
export const load = async function () {
const codes = await RouterCodeModel.findAll();
const codeManager: CodeManager[] = codes.map((item) => {
const { path, key, id, code, project } = item.toJSON();
console.log('item', item, 'code', item.code);
try {
const fn: any = new Function(
'ctx',
`
${code}
return run(ctx);
`,
);
// run code
const codeRunRoute = new Route(path, key, { id });
codeRunRoute.run = fn;
router.add(codeRunRoute);
return {
path,
key,
id,
project,
fn,
status: CodeStatus.success,
};
} catch (e) {
console.error('error id:', id, '\n', e);
return {
path,
key,
id,
project,
status: CodeStatus.fail,
errorMsg: e.message.toString(),
};
}
});
return codeManager;
};

View File

@@ -0,0 +1,33 @@
import { EventEmitter, once } from 'stream';
import { load, CodeManager } from './load.ts';
export enum LoadStatus {
LOADING = 'loading',
LOADED = 'loaded',
ERROR = 'error',
}
export const manager = {
loaded: LoadStatus.LOADING, // 是否已经加载
list: [] as CodeManager[],
};
export const events = new EventEmitter();
once(events, 'loaded')
.then(() => {
manager.loaded = LoadStatus.LOADED;
console.log('manager loaded');
})
.catch((e) => {
manager.loaded = LoadStatus.ERROR;
console.error('manager loaded error', e);
});
const init = async function () {
const r = await load();
manager.list = r;
events.emit('loaded');
};
init();

View File

@@ -1,7 +1,7 @@
// admin 需要最后运行并在route中进行过滤。
import { Route } from '@abearxiong/router';
import { router } from '../modules/router.ts';
import { manager } from './dashboard/manager.ts';
export const getRouterList = new Route('admin', 'getRouterList');
@@ -17,10 +17,10 @@ router.add(getRouterList);
export const removeRouter = new Route('admin', 'removeRouter');
removeRouter.run = async (ctx) => {
const { path, key } = ctx.query;
router.remove({path, key});
router.remove({ path, key });
ctx.body = 'success';
return ctx;
}
};
router.add(removeRouter);
// remove router by id
@@ -30,7 +30,7 @@ removeRouterById.run = async (ctx) => {
router.removeById(id);
ctx.body = 'success';
return ctx;
}
};
router.add(removeRouterById);
// add router
@@ -41,7 +41,7 @@ addRouter.run = async (ctx) => {
router.add(new Route(path, key));
ctx.body = 'success';
return ctx;
}
};
router.add(addRouter);
// update router
@@ -51,5 +51,21 @@ updateRouter.run = async (ctx) => {
router.add(new Route(path, key));
ctx.body = 'success';
return ctx;
}
};
router.add(updateRouter);
// get manager status
export const managerRouter = new Route('admin', 'getManagerStatus');
managerRouter.run = async (ctx) => {
ctx.body = manager.loaded;
return ctx;
};
router.add(managerRouter);
// get manager list
export const managerList = new Route('admin', 'getManagerList');
managerList.run = async (ctx) => {
ctx.body = manager.list;
return ctx;
};
router.add(managerList);

25
src/demo/index.ts Normal file
View File

@@ -0,0 +1,25 @@
import { router } from '../modules/router.ts';
import { Route } from '@abearxiong/router';
const getList = new Route('test', 'getList');
getList.run = async (ctx) => {
ctx.body = 'test';
return ctx;
};
router.add(getList);
const codeRun = `async function run(ctx) {
ctx.body = 'test js';
return ctx;
}`;
const fn: any = new Function(
'ctx',
`
${codeRun}
return run(ctx);
`,
);
const codeRunRoute = new Route('test', 'run');
codeRunRoute.run = fn;
router.add(codeRunRoute);

55
src/models/code.ts Normal file
View File

@@ -0,0 +1,55 @@
import { sequelize } from '../modules/sequelize.ts';
import { DataTypes, Model } from 'sequelize';
export type RouterCode = {
id: string;
path: string;
key: string;
active: boolean;
project: string;
code: string;
};
export class RouterCodeModel extends Model {
declare id: string;
path: string;
key: string;
active: boolean;
project: string;
public code: string;
}
RouterCodeModel.init(
{
id: {
type: DataTypes.UUID,
primaryKey: true,
defaultValue: DataTypes.UUIDV4,
comment: '用户id',
},
path: {
type: DataTypes.STRING,
allowNull: false,
},
key: {
type: DataTypes.STRING,
allowNull: false,
},
active: {
type: DataTypes.BOOLEAN,
defaultValue: false,
},
project: {
type: DataTypes.STRING,
defaultValue: 'default',
},
code: {
type: DataTypes.STRING,
defaultValue: '',
},
},
{
sequelize,
tableName: 'cf_router_code',
},
);
// RouterCodeModel.sync({ alter: true });

63
src/models/user.ts Normal file
View File

@@ -0,0 +1,63 @@
import { sequelize } from '@/modules/sequelize.ts';
import { DataTypes, Model } from 'sequelize';
export class User extends Model {
declare id: number;
username: string;
password: string;
salt: string;
remark: string;
}
User.init(
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
username: {
type: DataTypes.STRING,
allowNull: false,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
salt: {
type: DataTypes.STRING,
allowNull: false,
},
remark: {
type: DataTypes.STRING,
},
},
{
sequelize,
tableName: 'cf_user',
},
);
// User.sync({ alter: true });
export const initializeUser = async () => {
const w = await User.findOne();
const password = '2e8a305521bba54f49638ed25e46adf3';
const salt = '123';
const users = [
{ username: 'admin' },
{ username: 'user' },
{ username: 'root' },
];
if (!w) {
const newUsers = await User.bulkCreate(
users.map((user) => {
return {
...user,
password,
salt,
};
}),
);
console.info('[create new Users]', newUsers);
}
};

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

@@ -0,0 +1 @@
export { sequelize } from './sequelize.ts';

26
src/modules/sequelize.ts Normal file
View File

@@ -0,0 +1,26 @@
import { useConfig } from '@abearxiong/use-config';
import { Sequelize } from 'sequelize';
type PostgresConfig = {
postgres: {
username: string;
password: string;
host: string;
port: number;
database: string;
};
};
const config = useConfig<PostgresConfig>();
const postgresConfig = config.postgres;
if (!postgresConfig) {
console.error('postgres config is required');
process.exit(1);
}
// connect to db
export const sequelize = new Sequelize({
dialect: 'postgres',
...postgresConfig,
// logging: false,
});

View File

@@ -1,6 +1,6 @@
import { router } from './modules/router.ts';
import './test/index.ts';
import './demo/index.ts';
import './admin/index.ts';

View File

@@ -1,9 +0,0 @@
import { router } from '../modules/router.ts';
import { Route } from '@abearxiong/router';
const getList = new Route('test', 'getList');
getList.run = async (ctx) => {
ctx.body = 'test';
return ctx;
};
router.add(getList);