mirror of https://github.com/certd/certd
chore: plugin管理
parent
6f8fe62087
commit
ccfe72a0d9
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export * from "./email.js";
|
||||
export * from "./cname.js";
|
||||
export * from "./config.js";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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配置
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1,57 +1,61 @@
|
|||
import { request } from "/src/api/service";
|
||||
const apiPrefix = "/pi/access";
|
||||
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 function createAccessApi(apiPrefix = "/pi/access") {
|
||||
return {
|
||||
async GetList(query: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "post",
|
||||
data: query
|
||||
});
|
||||
},
|
||||
|
||||
export async function UpdateObj(obj: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
async AddObj(obj: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
},
|
||||
|
||||
export async function DelObj(id: number) {
|
||||
return await request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
async UpdateObj(obj: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
},
|
||||
|
||||
export async function GetObj(id: number) {
|
||||
return await request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
async DelObj(id: number) {
|
||||
return await request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
},
|
||||
|
||||
export async function GetProviderDefine(type: string) {
|
||||
return await request({
|
||||
url: apiPrefix + "/define",
|
||||
method: "post",
|
||||
params: { type }
|
||||
});
|
||||
}
|
||||
async GetObj(id: number) {
|
||||
return await request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
},
|
||||
|
||||
export async function GetProviderDefineByAccessType(type: string) {
|
||||
return await request({
|
||||
url: apiPrefix + "/defineByAccessType",
|
||||
method: "post",
|
||||
params: { type }
|
||||
});
|
||||
async GetProviderDefine(type: string) {
|
||||
return await request({
|
||||
url: apiPrefix + "/define",
|
||||
method: "post",
|
||||
params: { type }
|
||||
});
|
||||
},
|
||||
|
||||
async GetProviderDefineByAccessType(type: string) {
|
||||
return await request({
|
||||
url: apiPrefix + "/defineByAccessType",
|
||||
method: "post",
|
||||
params: { type }
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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>
|
|
@ -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 }
|
||||
});
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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>
|
|
@ -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)
|
||||
);
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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({});
|
||||
}
|
||||
}
|
|
@ -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 = [];
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 });
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue