chore: plugin管理

pull/213/head
xiaojunnuo 2024-10-13 01:27:08 +08:00
parent 6f8fe62087
commit ccfe72a0d9
29 changed files with 729 additions and 163 deletions

View File

@ -1,18 +1,16 @@
import { ConcurrencyStrategy, NotificationWhen, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../dt/index.js";
import _ from "lodash-es";
import { RunHistory, RunnableCollection } from "./run-history.js";
import { AbstractTaskPlugin, PluginDefine, pluginRegistry, TaskInstanceContext, UserInfo } from "../plugin/index.js";
import { ContextFactory, IContext } from "./context.js";
import { IStorage } from "./storage.js";
import { logger } from "../utils/index.js";
import { createAxiosService, hashUtils, logger, utils } from "../utils/index.js";
import { Logger } from "log4js";
import { createAxiosService } from "../utils/index.js";
import { IAccessService } from "../access/index.js";
import { RegistryItem } from "../registry/index.js";
import { Decorator } from "../decorator/index.js";
import { ICnameProxyService, IEmailService } from "../service/index.js";
import { ICnameProxyService, IEmailService, IPluginConfigService } from "../service/index.js";
import { FileStore } from "./file-store.js";
import { hashUtils, utils } from "../utils/index.js";
import { cloneDeep, forEach, merge } from "lodash-es";
export type ExecutorOptions = {
pipeline: Pipeline;
@ -21,6 +19,7 @@ export type ExecutorOptions = {
accessService: IAccessService;
emailService: IEmailService;
cnameProxyService: ICnameProxyService;
pluginConfigService: IPluginConfigService;
fileRootDir?: string;
user: UserInfo;
};
@ -42,7 +41,7 @@ export class Executor {
onChanged: (history: RunHistory) => Promise<void>;
constructor(options: ExecutorOptions) {
this.options = options;
this.pipeline = _.cloneDeep(options.pipeline);
this.pipeline = cloneDeep(options.pipeline);
this.onChanged = async (history: RunHistory) => {
await options.onChanged(history);
};
@ -219,7 +218,7 @@ export class Executor {
// @ts-ignore
const define: PluginDefine = plugin.define;
//从outputContext读取输入参数
const input = _.cloneDeep(step.input);
const input = cloneDeep(step.input);
Decorator.inject(define.input, instance, input, (item, key) => {
if (item.component?.name === "output-selector") {
const contextKey = input[key];
@ -269,6 +268,7 @@ export class Executor {
accessService: this.options.accessService,
emailService: this.options.emailService,
cnameProxyService: this.options.cnameProxyService,
pluginConfigService: this.options.pluginConfigService,
pipelineContext: this.pipelineContext,
userContext: this.contextFactory.getContext("user", this.options.user.id),
fileStore: new FileStore({
@ -290,7 +290,7 @@ export class Executor {
this.lastStatusMap.clear();
}
//输出上下文变量到output context
_.forEach(define.output, (item: any, key: any) => {
forEach(define.output, (item: any, key: any) => {
step.status!.output[key] = instance[key];
// const stepOutputKey = `step.${step.id}.${key}`;
// this.runtime.context[stepOutputKey] = instance[key];
@ -300,7 +300,7 @@ export class Executor {
if (Object.keys(instance._result.pipelineVars).length > 0) {
// 判断 pipelineVars 有值时更新
const vars = this.pipelineContext.getObj("vars");
_.merge(vars, instance._result.pipelineVars);
merge(vars, instance._result.pipelineVars);
await this.pipelineContext.setObj("vars", vars);
}
}

View File

@ -8,8 +8,8 @@ import { IContext, PluginRequestHandleReq, RunnableCollection } from "../core/in
import { ILogger, logger, utils } from "../utils/index.js";
import { HttpClient } from "../utils/index.js";
import dayjs from "dayjs";
import _ from "lodash-es";
import { IPluginConfigService } from "../service/config";
import { upperFirst } from "lodash-es";
export type UserInfo = {
role: "admin" | "user";
id: any;
@ -74,7 +74,7 @@ export type TaskInstanceContext = {
//cname记录服务
cnameProxyService: ICnameProxyService;
//插件配置服务
configService: IPluginConfigService;
pluginConfigService: IPluginConfigService;
//流水线上下文
pipelineContext: IContext;
//用户上下文
@ -172,7 +172,7 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
let methodName = req.action;
if (!req.action.startsWith("on")) {
methodName = `on${_.upperFirst(req.action)}`;
methodName = `on${upperFirst(req.action)}`;
}
// @ts-ignore

View File

@ -1,9 +1,9 @@
import _ from "lodash-es";
import { pluginRegistry } from "./registry.js";
import { PluginDefine, TaskInputDefine, TaskOutputDefine } from "./api.js";
import { Decorator } from "../decorator/index.js";
import { AUTOWIRE_KEY } from "../decorator/index.js";
import "reflect-metadata";
import { merge, sortBy } from "lodash-es";
// 提供一个唯一 key
export const PLUGIN_CLASS_KEY = "pipeline:plugin";
@ -42,13 +42,13 @@ export function IsTaskPlugin(define: PluginDefine): ClassDecorator {
}
inputArray.push([key, _input]);
}
inputArray = _.sortBy(inputArray, (item: any) => item[1].order);
inputArray = sortBy(inputArray, (item: any) => item[1].order);
const inputMap: any = {};
inputArray.forEach((item: any) => {
inputMap[item[0]] = item[1];
});
_.merge(define, { input: inputMap, autowire: autowires, output: outputs });
merge(define, { input: inputMap, autowire: autowires, output: outputs });
Reflect.defineMetadata(PLUGIN_CLASS_KEY, define, target);

View File

@ -1,9 +1,8 @@
import { FormItemProps } from "../d.ts/index.js";
export type PluginConfig = {
show: false;
sysInput: {
[key: string]: {};
name: string;
disabled: boolean;
sysSetting: {
[key: string]: any;
};
};

View File

@ -1,2 +1,3 @@
export * from "./email.js";
export * from "./cname.js";
export * from "./config.js";

View File

@ -2,59 +2,42 @@ import { ALL, Body, Post, Query } from '@midwayjs/core';
import { BaseController } from './base-controller.js';
export abstract class CrudController<T> extends BaseController {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
abstract getService<T>();
@Post('/page')
async page(
@Body(ALL)
body
) {
async page(@Body(ALL) body: any) {
const pageRet = await this.getService().page(body?.query, body?.page, body?.sort, null);
return this.ok(pageRet);
}
@Post('/list')
async list(
@Body(ALL)
body
) {
async list(@Body(ALL) body: any) {
const listRet = await this.getService().list(body, null, null);
return this.ok(listRet);
}
@Post('/add')
async add(
@Body(ALL)
bean
) {
async add(@Body(ALL) bean: any) {
delete bean.id;
const id = await this.getService().add(bean);
return this.ok(id);
}
@Post('/info')
async info(
@Query('id')
id
) {
async info(@Query('id') id: number) {
const bean = await this.getService().info(id);
return this.ok(bean);
}
@Post('/update')
async update(
@Body(ALL)
bean
) {
async update(@Body(ALL) bean: any) {
await this.getService().update(bean);
return this.ok(null);
}
@Post('/delete')
async delete(
@Query('id')
id
) {
async delete(@Query('id') id: number) {
await this.getService().delete([id]);
return this.ok(null);
}

View File

@ -68,6 +68,34 @@ export const sysResources = [
permission: "sys:settings:view"
}
},
{
title: "系统级授权",
name: "SysAccess",
path: "/sys/access",
component: "/sys/access/index.vue",
meta: {
show: () => {
const settingStore = useSettingStore();
return settingStore.isComm;
},
icon: "ion:disc-outline",
permission: "sys:settings:view"
}
},
{
title: "插件管理",
name: "SysPlugin",
path: "/sys/plugin",
component: "/sys/plugin/index.vue",
meta: {
show: () => {
const settingStore = useSettingStore();
return settingStore.isComm;
},
icon: "ion:extension-puzzle-outline",
permission: "sys:settings:view"
}
},
{
title: "账号绑定",
name: "AccountBind",

View File

@ -1,5 +1,4 @@
// @ts-ignore
import * as api from "/@/views/certd/access/api";
import { ref } from "vue";
import { getCommonColumnDefine } from "/@/views/certd/access/common";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
@ -9,25 +8,25 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const { props, ctx } = context;
const lastResRef = ref();
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
return await context.api.GetList(query);
};
const editRequest = async (req: EditReq) => {
const { form, row } = req;
form.id = row.id;
form.type = props.type;
const res = await api.UpdateObj(form);
const res = await context.api.UpdateObj(form);
lastResRef.value = res;
return res;
};
const delRequest = async (req: DelReq) => {
const { row } = req;
return await api.DelObj(row.id);
return await context.api.DelObj(row.id);
};
const addRequest = async (req: AddReq) => {
const { form } = req;
form.type = props.type;
const res = await api.AddObj(form);
const res = await context.api.AddObj(form);
lastResRef.value = res;
return res;
};
@ -41,7 +40,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const typeRef = ref("aliyun");
context.typeRef = typeRef;
const commonColumnsDefine = getCommonColumnDefine(crudExpose, typeRef);
const commonColumnsDefine = getCommonColumnDefine(crudExpose, typeRef, api);
commonColumnsDefine.type.form.component.disabled = true;
return {
typeRef,

View File

@ -8,6 +8,7 @@
import { defineComponent, onMounted, watch } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { createAccessApi } from "/@/views/certd/access/api";
export default defineComponent({
name: "CertAccessModal",
@ -16,11 +17,16 @@ export default defineComponent({
type: String,
default: "aliyun"
},
from: {
type: String, //user | sys
default: "user"
},
modelValue: {}
},
emits: ["update:modelValue"],
setup(props, ctx) {
const context: any = { props, ctx };
const api = createAccessApi(props.from === "sys" ? "/sys/access" : "/pi/access");
const context: any = { props, ctx, api };
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
// crud

View File

@ -18,9 +18,8 @@
<script>
import { defineComponent, reactive, ref, watch } from "vue";
import * as api from "../api";
import CertAccessModal from "./access/index.vue";
import { GetProviderDefineByAccessType } from "../api";
import { createAccessApi } from "../api";
export default defineComponent({
name: "AccessSelector",
@ -41,10 +40,16 @@ export default defineComponent({
size: {
type: String,
default: "middle"
},
from: {
type: String, //user | sys
default: "user"
}
},
emits: ["update:modelValue"],
setup(props, ctx) {
const api = createAccessApi(props.from === "sys" ? "/sys/access" : "/pi/access");
const target = ref({});
const selectedId = ref();
async function refreshTarget(value) {

View File

@ -1,57 +1,61 @@
import { request } from "/src/api/service";
const apiPrefix = "/pi/access";
export async function GetList(query: any) {
export function createAccessApi(apiPrefix = "/pi/access") {
return {
async GetList(query: any) {
return await request({
url: apiPrefix + "/page",
method: "post",
data: query
});
}
},
export async function AddObj(obj: any) {
async AddObj(obj: any) {
return await request({
url: apiPrefix + "/add",
method: "post",
data: obj
});
}
},
export async function UpdateObj(obj: any) {
async UpdateObj(obj: any) {
return await request({
url: apiPrefix + "/update",
method: "post",
data: obj
});
}
},
export async function DelObj(id: number) {
async DelObj(id: number) {
return await request({
url: apiPrefix + "/delete",
method: "post",
params: { id }
});
}
},
export async function GetObj(id: number) {
async GetObj(id: number) {
return await request({
url: apiPrefix + "/info",
method: "post",
params: { id }
});
}
},
export async function GetProviderDefine(type: string) {
async GetProviderDefine(type: string) {
return await request({
url: apiPrefix + "/define",
method: "post",
params: { type }
});
}
},
export async function GetProviderDefineByAccessType(type: string) {
async GetProviderDefineByAccessType(type: string) {
return await request({
url: apiPrefix + "/defineByAccessType",
method: "post",
params: { type }
});
}
};
}

View File

@ -1,12 +1,9 @@
import { ColumnCompositionProps, dict, compute } from "@fast-crud/fast-crud";
// @ts-ignore
import * as api from "./api";
// @ts-ignore
import _ from "lodash-es";
import { ColumnCompositionProps, dict } from "@fast-crud/fast-crud";
import { computed, ref, toRef } from "vue";
import { useReference } from "/@/use/use-refrence";
import { forEach, get, merge, set } from "lodash-es";
export function getCommonColumnDefine(crudExpose: any, typeRef: any) {
export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
const AccessTypeDictRef = dict({
url: "/pi/access/accessTypeDict"
});
@ -27,20 +24,20 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any) {
}
}
console.log('crudBinding.value[mode + "Form"].columns', columnsRef.value);
_.forEach(define.input, (value: any, mapKey: any) => {
forEach(define.input, (value: any, mapKey: any) => {
const key = "access." + mapKey;
const field = {
...value,
key
};
const column = _.merge({ title: key }, defaultPluginConfig, field);
const column = merge({ title: key }, defaultPluginConfig, field);
//eval
useReference(column);
//设置默认值
if (column.value != null && _.get(form, key) == null) {
_.set(form, key, column.value);
if (column.value != null && get(form, key) == null) {
set(form, key, column.value);
}
//字段配置赋值
columnsRef.value[key] = column;

View File

@ -1,12 +1,12 @@
// @ts-ignore
import * as api from "./api";
import { useI18n } from "vue-i18n";
import { ref } from "vue";
import { getCommonColumnDefine } from "/@/views/certd/access/common";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { t } = useI18n();
const api = context.api;
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
};
@ -28,7 +28,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
};
const typeRef = ref();
const commonColumnsDefine = getCommonColumnDefine(crudExpose, typeRef);
const commonColumnsDefine = getCommonColumnDefine(crudExpose, typeRef, api);
return {
crudOptions: {
request: {

View File

@ -14,11 +14,13 @@
import { defineComponent, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { createAccessApi } from "/@/views/certd/access/api";
export default defineComponent({
name: "AccessManager",
setup() {
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: {} });
const api = createAccessApi("/pi/access");
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { api } });
//
onMounted(() => {

View File

@ -0,0 +1,36 @@
<template>
<fs-page>
<template #header>
<div class="title">
系统级授权管理
<span class="sub">管理第三方系统的授权信息(系统级)</span>
</div>
</template>
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
</fs-page>
</template>
<script lang="ts">
import { defineComponent, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "../../certd/access/crud";
import { createAccessApi } from "/@/views/certd/access/api";
export default defineComponent({
name: "SysAccessManager",
setup() {
const api = createAccessApi("/sys/access");
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { api } });
//
onMounted(() => {
crudExpose.doRefresh();
});
return {
crudBinding,
crudRef
};
}
});
</script>

View File

@ -0,0 +1,59 @@
import { request } from "/src/api/service";
const apiPrefix = "/sys/plugin";
export async function GetList(query: any) {
return await request({
url: apiPrefix + "/page",
method: "post",
data: query
});
}
export async function AddObj(obj: any) {
return await request({
url: apiPrefix + "/add",
method: "post",
data: obj
});
}
export async function UpdateObj(obj: any) {
return await request({
url: apiPrefix + "/update",
method: "post",
data: obj
});
}
export async function DelObj(id: any) {
return await request({
url: apiPrefix + "/delete",
method: "post",
params: { id }
});
}
export async function GetObj(id: any) {
return await request({
url: apiPrefix + "/info",
method: "post",
params: { id }
});
}
export async function GetDetail(id: any) {
return await request({
url: apiPrefix + "/detail",
method: "post",
params: { id }
});
}
export async function DeleteBatch(ids: any[]) {
return await request({
url: apiPrefix + "/deleteByIds",
method: "post",
data: { ids }
});
}

View File

@ -0,0 +1,180 @@
import * as api from "./api";
import { useI18n } from "vue-i18n";
import { computed, Ref, ref } from "vue";
import { useRouter } from "vue-router";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, utils } from "@fast-crud/fast-crud";
import { useUserStore } from "/src/store/modules/user";
import { useSettingStore } from "/src/store/modules/settings";
import { Modal } from "ant-design-vue";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const router = useRouter();
const { t } = useI18n();
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
};
const editRequest = async ({ form, row }: EditReq) => {
form.id = row.id;
const res = await api.UpdateObj(form);
return res;
};
const delRequest = async ({ row }: DelReq) => {
return await api.DelObj(row.id);
};
const addRequest = async ({ form }: AddReq) => {
form.content = JSON.stringify({
title: form.title
});
const res = await api.AddObj(form);
return res;
};
const userStore = useUserStore();
const settingStore = useSettingStore();
const selectedRowKeys: Ref<any[]> = ref([]);
context.selectedRowKeys = selectedRowKeys;
return {
crudOptions: {
settings: {
plugins: {
//这里使用行选择插件生成行选择crudOptions配置最终会与crudOptions合并
rowSelection: {
enabled: true,
order: -2,
before: true,
// handle: (pluginProps,useCrudProps)=>CrudOptions,
props: {
multiple: true,
crossPage: true,
selectedRowKeys
}
}
}
},
request: {
pageRequest,
addRequest,
editRequest,
delRequest
},
actionbar: {
buttons: {
add: {
show: false
}
}
},
rowHandle: {
minWidth: 200,
fixed: "right",
buttons: {
edit: {
show: false
}
}
},
columns: {
id: {
title: "ID",
key: "id",
type: "number",
column: {
width: 100
},
form: {
show: false
}
},
name: {
title: "插件名称",
type: "text",
search: {
show: true
},
form: {
show: false
},
column: {
width: 100
}
},
title: {
title: "标题",
type: "text",
column: {
width: 300
}
},
desc: {
title: "描述",
type: "text",
column: {
width: 300
}
},
group: {
title: "分组",
type: "text",
column: {
width: 300
}
},
disabled: {
title: "禁用/启用",
type: "dict-switch",
dict: dict({
data: [
{ label: "启用", value: false, color: "success" },
{ label: "禁用", value: true, color: "error" }
]
}),
form: {
value: false
},
column: {
width: 100,
component: {
title: "点击可禁用/启用",
on: {
async click({ value, row }) {
Modal.confirm({
title: "提示",
content: `确定要${!value ? "禁用" : "启用"}吗?`,
onOk: async () => {
await api.SetDisabled(row.id, !value);
await crudExpose.doRefresh();
}
});
}
}
}
}
},
createTime: {
title: "创建时间",
type: "datetime",
form: {
show: false
},
column: {
sorter: true,
width: 160,
align: "center"
}
},
updateTime: {
title: "更新时间",
type: "datetime",
form: {
show: false
},
column: {
show: true
}
}
}
}
};
}

View File

@ -0,0 +1,51 @@
<template>
<fs-page class="page-cert">
<template #header>
<div class="title">插件管理</div>
</template>
<fs-crud ref="crudRef" v-bind="crudBinding">
<!-- <template #pagination-left>-->
<!-- <a-tooltip title="批量删除">-->
<!-- <fs-button icon="DeleteOutlined" @click="handleBatchDelete"></fs-button>-->
<!-- </a-tooltip>-->
<!-- </template>-->
</fs-crud>
</fs-page>
</template>
<script lang="ts" setup>
import { onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api";
defineOptions({
name: "SysPlugin"
});
const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions });
const selectedRowKeys = context.selectedRowKeys;
const handleBatchDelete = () => {
if (selectedRowKeys.value?.length > 0) {
Modal.confirm({
title: "确认",
content: `确定要批量删除这${selectedRowKeys.value.length}条记录吗`,
async onOk() {
await DeleteBatch(selectedRowKeys.value);
message.info("删除成功");
crudExpose.doRefresh();
selectedRowKeys.value = [];
}
});
} else {
message.error("请先勾选记录");
}
};
//
onMounted(() => {
crudExpose.doRefresh();
});
</script>
<style lang="less"></style>

View File

@ -0,0 +1,18 @@
CREATE TABLE "pi_plugin"
(
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" varchar(100) NOT NULL,
"icon" varchar(100),
"title" varchar(200),
"desc" varchar(500),
"group" varchar(100),
"version" varchar(100),
"setting" text,
"sysSetting" text,
"content" text,
"type" strinng NOT NULL,
"disabled" boolean NOT NULL,
"create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP),
"update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)
);

View File

@ -1,24 +1,24 @@
## sqlite与postgres不同点
1.
sl: AUTOINCREAMENT
pg: GENERATED BY DEFAULT AS IDENTITY
sqlite: AUTOINCREAMENT
postgresql: GENERATED BY DEFAULT AS IDENTITY
2.
datetime
timestamp
sqlite: datetime
postgresql: timestamp
3.
update sqlite_sequence set seq = 1000 where name = 'sys_role' ;
select setval('sys_role_id_seq', 1000);
sqlite: update sqlite_sequence set seq = 1000 where name = 'sys_role' ;
postgresql: select setval('sys_role_id_seq', 1000);
4.
"disabled" boolean DEFAULT (0)
"disabled" boolean DEFAULT (false)
sqlite: "disabled" boolean DEFAULT (0)
postgresql: "disabled" boolean DEFAULT (false)
5.
last_insert_rowid()
LASTVAL()
sqlite: last_insert_rowid()
postgresql: LASTVAL()
6.
sl: integer
pg: bigint
sqlite: integer
postgresql: bigint

View File

@ -12,6 +12,10 @@ export class AccessController extends CrudController<AccessService> {
@Inject()
service: AccessService;
userId() {
return this.getUserId();
}
getService(): AccessService {
return this.service;
}
@ -19,36 +23,36 @@ export class AccessController extends CrudController<AccessService> {
@Post('/page', { summary: Constants.per.authOnly })
async page(@Body(ALL) body) {
body.query = body.query ?? {};
body.query.userId = this.getUserId();
body.query.userId = this.userId();
return await super.page(body);
}
@Post('/list', { summary: Constants.per.authOnly })
async list(@Body(ALL) body) {
body.userId = this.getUserId();
body.userId = this.userId();
return super.list(body);
}
@Post('/add', { summary: Constants.per.authOnly })
async add(@Body(ALL) bean) {
bean.userId = this.getUserId();
bean.userId = this.userId();
return super.add(bean);
}
@Post('/update', { summary: Constants.per.authOnly })
async update(@Body(ALL) bean) {
await this.service.checkUserId(bean.id, this.getUserId());
await this.service.checkUserId(bean.id, this.userId());
return super.update(bean);
}
@Post('/info', { summary: Constants.per.authOnly })
async info(@Query('id') id: number) {
await this.service.checkUserId(id, this.getUserId());
await this.service.checkUserId(id, this.userId());
return super.info(id);
}
@Post('/delete', { summary: Constants.per.authOnly })
async delete(@Query('id') id: number) {
await this.service.checkUserId(id, this.getUserId());
await this.service.checkUserId(id, this.userId());
return super.delete(id);
}

View File

@ -1,28 +0,0 @@
import { ALL, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
import { BaseController } from '@certd/lib-server';
import { PluginService } from '../service/plugin-service.js';
import { Constants } from '@certd/lib-server';
/**
*
*/
@Provide()
@Controller('/api/pi/plugin')
export class PluginController extends BaseController {
@Inject()
service: PluginService;
@Post('/list', { summary: Constants.per.authOnly })
async list(@Query(ALL) query: any) {
query.userId = this.getUserId();
const list = this.service.getList();
return this.ok(list);
}
@Post('/groups', { summary: Constants.per.authOnly })
async groups(@Query(ALL) query: any) {
query.userId = this.getUserId();
const group = this.service.getGroups();
return this.ok(group);
}
}

View File

@ -20,6 +20,7 @@ import { UserService } from '../../sys/authority/service/user-service.js';
import { AccessGetter } from './access-getter.js';
import { CnameRecordService } from '../../cname/service/cname-record-service.js';
import { CnameProxyService } from './cname-proxy-service.js';
import { PluginConfigService } from './plugin-config-service.js';
const runningTasks: Map<string | number, Executor> = new Map();
const freeCount = 10;
@ -45,6 +46,9 @@ export class PipelineService extends BaseService<PipelineEntity> {
@Inject()
historyLogService: HistoryLogService;
@Inject()
pluginConfigService: PluginConfigService;
@Inject()
userService: UserService;
@ -356,6 +360,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
onChanged,
accessService: accessGetter,
cnameProxyService,
pluginConfigService: this.pluginConfigService,
storage: new DbStorage(userId, this.storageService),
emailService: this.emailService,
fileRootDir: this.certdConfig.fileRootDir,

View File

@ -0,0 +1,13 @@
import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { IPluginConfigService } from '@certd/pipeline';
/**
*
*/
@Provide()
@Scope(ScopeEnum.Singleton)
export class PluginConfigService implements IPluginConfigService {
getPluginConfig(pluginName: string) {
return Promise.resolve({});
}
}

View File

@ -1,8 +1,9 @@
import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { pluginGroups, pluginRegistry } from '@certd/pipeline';
@Provide()
@Scope(ScopeEnum.Singleton)
export class PluginService {
export class BuiltInPluginService {
getList() {
const collection = pluginRegistry.storage;
const list = [];

View File

@ -0,0 +1,62 @@
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
import { AccessService } from '../../pipeline/service/access-service.js';
import { AccessController } from '../../pipeline/controller/access-controller.js';
import { checkComm } from '@certd/pipeline';
/**
*
*/
@Provide()
@Controller('/api/sys/access')
export class SysAccessController extends AccessController {
@Inject()
service2: AccessService;
getService(): AccessService {
return this.service2;
}
userId() {
checkComm();
return 0;
}
@Post('/page', { summary: 'sys:settings:view' })
async page(@Body(ALL) body: any) {
return await await super.page(body);
}
@Post('/list', { summary: 'sys:settings:view' })
async list(@Body(ALL) body: any) {
return await super.list(body);
}
@Post('/add', { summary: 'sys:settings:edit' })
async add(@Body(ALL) bean: any) {
return await super.add(bean);
}
@Post('/update', { summary: 'sys:settings:edit' })
async update(@Body(ALL) bean: any) {
return await super.update(bean);
}
@Post('/info', { summary: 'sys:settings:view' })
async info(@Query('id') id: number) {
return await super.info(id);
}
@Post('/delete', { summary: 'sys:settings:edit' })
async delete(@Query('id') id: number) {
return await super.delete(id);
}
@Post('/define', { summary: 'sys:settings:view' })
async define(@Query('type') type: string) {
return await super.define(type);
}
@Post('/accessTypeDict', { summary: 'sys:settings:view' })
async getAccessTypeDict() {
return await super.getAccessTypeDict();
}
}

View File

@ -0,0 +1,68 @@
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
import { merge } from 'lodash-es';
import { CrudController } from '@certd/lib-server';
import { PluginService } from '../service/plugin-service.js';
import { checkComm } from '@certd/pipeline';
/**
*
*/
@Provide()
@Controller('/api/sys/plugin')
export class PluginController extends CrudController<PluginService> {
@Inject()
service: PluginService;
getService() {
checkComm();
return this.service;
}
@Post('/page', { summary: 'sys:settings:view' })
async page(@Body(ALL) body: any) {
body.query = body.query ?? {};
return await super.page(body);
}
@Post('/list', { summary: 'sys:settings:view' })
async list(@Body(ALL) body: any) {
return super.list(body);
}
@Post('/add', { summary: 'sys:settings:edit' })
async add(@Body(ALL) bean: any) {
const def: any = {
isDefault: false,
disabled: false,
};
merge(bean, def);
return super.add(bean);
}
@Post('/update', { summary: 'sys:settings:edit' })
async update(@Body(ALL) bean: any) {
return super.update(bean);
}
@Post('/info', { summary: 'sys:settings:view' })
async info(@Query('id') id: number) {
return super.info(id);
}
@Post('/delete', { summary: 'sys:settings:edit' })
async delete(@Query('id') id: number) {
return super.delete(id);
}
@Post('/deleteByIds', { summary: 'sys:settings:edit' })
async deleteByIds(@Body(ALL) body: { ids: number[] }) {
const res = await this.service.delete(body.ids);
return this.ok(res);
}
@Post('/setDisabled', { summary: 'sys:settings:edit' })
async setDisabled(@Body('id') id: number, @Body('disabled') disabled: boolean) {
await this.service.setDisabled(id, disabled);
return this.ok();
}
}

View File

@ -0,0 +1,50 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity('pi_plugin')
export class PluginEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({ name: 'name', comment: 'Key' })
name: string;
@Column({ name: 'icon', comment: '图标' })
icon: string;
@Column({ name: 'title', comment: '标题' })
title: string;
@Column({ name: 'group', comment: '分组' })
group: string;
@Column({ name: 'desc', comment: '描述' })
desc: string;
@Column({ comment: '配置', length: 40960 })
setting: string;
@Column({ comment: '系统配置', length: 40960 })
sysSetting: string;
@Column({ comment: '脚本', length: 40960 })
content: string;
@Column({ comment: '类型', length: 100, nullable: true })
type: string; // builtIn | custom
@Column({ comment: '启用/禁用', default: false })
disabled: boolean;
@Column({
name: 'create_time',
comment: '创建时间',
default: () => 'CURRENT_TIMESTAMP',
})
createTime: Date;
@Column({
name: 'update_time',
comment: '修改时间',
default: () => 'CURRENT_TIMESTAMP',
})
updateTime: Date;
}

View File

@ -0,0 +1,23 @@
import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { BaseService } from '@certd/lib-server';
import { PluginEntity } from '../entity/plugin.js';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { checkComm } from '@certd/pipeline';
@Provide()
@Scope(ScopeEnum.Singleton)
export class PluginService extends BaseService<PluginEntity> {
@InjectEntityModel(PluginEntity)
repository: Repository<PluginEntity>;
//@ts-ignore
getRepository() {
checkComm();
return this.repository;
}
async setDisabled(id: number, disabled: boolean) {
await this.repository.update({ id }, { disabled });
}
}