refactor(plugin): 重构插件定义和安装流程

- 更新插件配置格式,增加依赖库和插件类型字段
- 修改插件安装流程,支持安装依赖插件和第三方库
- 优化插件列表过滤逻辑,按类型筛选插件
- 调整 Dockerfile,使用 Node.js22 镜像并更新 pnpm 安装方式
pull/370/head
xiaojunnuo 2025-04-11 23:39:40 +08:00
parent 420b0394a7
commit 3d9620abb0
8 changed files with 176 additions and 85 deletions

View File

@ -44,7 +44,7 @@ jobs:
# cache: 'npm'
# working-directory: ./packages/ui/certd-client
- run: |
npm install -g pnpm@8.15.7
npm install -g pnpm
pnpm install
npm run build
working-directory: ./packages/ui/certd-client

View File

@ -65,6 +65,8 @@ export type PluginDefine = Registrable & {
};
needPlus?: boolean;
showRunStrategy?: boolean;
pluginType?: string; //类型
type?: string; //来源
};
export type ITaskPlugin = {

View File

@ -1,16 +1,16 @@
FROM node:20-alpine AS builder
FROM node:22-alpine AS builder
WORKDIR /workspace/
COPY . /workspace/
# armv7 目前只能用node18 pnpm9不支持node18,所以pnpm只能用8.15.7版本
# https://github.com/nodejs/docker-node/issues/1946
RUN npm install -g pnpm@8.15.7
RUN npm install -g pnpm
#RUN cd /workspace/certd-client && pnpm install && npm run build
RUN cp /workspace/certd-client/dist/* /workspace/certd-server/public/ -rf
RUN cd /workspace/certd-server && pnpm install && npm run build-on-docker
FROM node:20-alpine
FROM node:22-alpine
EXPOSE 7001
EXPOSE 7002
RUN apk add --no-cache openssl

View File

@ -32,7 +32,7 @@ export default {
},
async sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
return new Promise(resolve => setTimeout(resolve, ms));
},
maxLength(str?: string, length = 100) {
@ -42,6 +42,9 @@ export default {
return "";
},
transformLink(desc: string = "") {
if (!desc) {
return "";
}
return desc.replace(/\[(.*)\]\((.*)\)/g, '<a href="$2" target="_blank">$1</a>');
}
},
};

View File

@ -241,7 +241,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
data: [
{ label: "授权", value: "access" },
{ label: "DNS", value: "dnsProvider" },
{ label: "部署插件", value: "plugin" },
{ label: "部署插件", value: "deploy" },
],
}),
column: {
@ -279,10 +279,60 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
},
},
},
"extra.dependLibs": {
title: "第三方依赖",
type: "text",
form: {
helper: "依赖的第三方库,package.dependencies的格式name[:^version]",
component: {
name: "a-select",
mode: "tags",
allowClear: true,
open: false,
},
},
column: {
show: false,
},
},
"extra.dependPlugins": {
title: "插件依赖",
type: "text",
form: {
component: {
name: "a-select",
mode: "tags",
open: false,
allowClear: true,
},
helper: "安装时会先安装依赖的插件,格式:[author/]pluginName[:version]",
},
column: {
show: false,
},
},
"extra.showRunStrategy": {
title: "可修改运行策略",
type: "dict-switch",
dict: dict({
data: [
{ value: false, label: "不可修改" },
{ value: true, label: "可修改" },
],
}),
form: {
value: false,
rules: [{ required: true }],
},
column: {
width: 100,
align: "left",
show: false,
},
},
"extra.default.strategy.runStrategy": {
title: "运行策略",
type: "dict-select",
dict: dict({
data: [
{ value: 0, label: "正常运行" },
@ -293,6 +343,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
value: 1,
rules: [{ required: true }],
helper: "默认运行策略",
show: compute(({ form }) => {
return form.extra.showRunStrategy;
}),
},
column: {
width: 100,
@ -300,6 +353,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
component: {
color: "auto",
},
show: false,
},
valueBuilder({ row }) {
if (row.extra) {

View File

@ -13,6 +13,10 @@ export class BuiltInPluginService {
if (Plugin?.define?.deprecated) {
continue;
}
//@ts-ignore
if(Plugin.define?.type && Plugin.define?.type !== 'builtin'){
continue;
}
list.push({ ...Plugin.define, key });
}
list = list.sort((a, b) => {

View File

@ -1,28 +1,36 @@
import yaml from "js-yaml";
const CertOutputs = [
"CertApply",
"CertApplyLego",
"CertApplyUpload"
];
import { CertApplyPluginNames } from "@certd/plugin-cert";
export function getDefaultAccessPlugin() {
const metadata = {
username: {
title: "用户名",
required: true,
encrypt: false
},
password: {
title: "密码",
required: true,
encrypt: true
}
};
const metadata = `
input:
username: #
title: #
required: true #
encrypt: false #
component: #
name: a-input #
allowClear: true # https://www.antdv.com/components/input#api
password:
title:
required: true
encrypt: true
component:
name: a-input
allowClear: true
const script = `const { BaseAccess } = await import("@certd/pipeline")
`
const script = `
# 使 await import
const { BaseAccess } = await import("@certd/pipeline")
# BaseAccess
return class DemoAccess extends BaseAccess {
username;
password;
# input
username;
password;
}
`;
return {
@ -32,82 +40,93 @@ password;
}
export function getDefaultDeployPlugin() {
const metadata = {
cert: {
title: "前置任务证书",
component: {
name: "output-selector",
from: [...CertOutputs]
},
required: true
},
certDomains: {
title: "当前证书域名",
component: {
name: "cert-domains-getter"
},
mergeScript: `
return {
component:{
inputKey: ctx.compute(({form})=>{
return form.cert
}),
}
let certApplyNames = ''
for (const name of CertApplyPluginNames) {
certApplyNames += `
- ${name}`
}
const metadata =`
input: #
cert:
title:
helper: #
component:
name: output-selector #
vModel: modelValue #
from:${certApplyNames}
required: true
certDomains:
title:
component:
name: cert-domains-getter
mergeScript: |
return {
component:{
inputKey: ctx.compute(({form})=>{
return form.cert
}),
}
`,
required: true
},
accessId: {
title: "Access授权",
helper: "xxxx的授权",
component: {
name: "access-selector",
type: "aliyun"
},
required: true
},
key1: {
title: "输入示例1",
required: false
},
key2: {
title: "可选项",
component: {
name: "a-select",
vMode: "value",
options: [
{ value: "1", label: "选项1" },
{ value: "2", label: "选项2" }
]
},
required: false
}
};
}
required: true
accessId:
title: Access
helper: xxxx
component:
name: access-selector #
type: aliyun #
required: true
key1:
title: 1
required: false
key2:
title:
component:
name: a-select
vMode: value
options:
- value: "1"
label: 1
- value: "2"
label: 2
required: false
#output: #
# outputName:
#
`
const script = `
// 要用await来import模块
const { AbstractTaskPlugin } = await import("@certd/pipeline")
// 要返回一个继承AbstractTaskPlugin的class
return class DemoTask extends AbstractTaskPlugin {
// 这里是插件的输入参数对应左边的input配置
cert;
certDomains;
accessId;
key1;
key2;
// 编写执行方法
async execute(){
# accessId
const access = await this.accessService.getById(this.accessId)
this.logger.info("cert:",this.cert);
//必须使用this.logger打印日志
// this.logger.info("cert:",this.cert);
this.logger.info("certDomains:",this.certDomains);
this.logger.info("access:",access);
this.logger.info("key1:",this.key1);
this.logger.info("key2:",this.key2);
//开始你的部署任务
const res = await this.ctx.http.request({url:"xxxxxx"})
// 开始你的部署任务
// this.ctx里面有一些常用的方法类比如utils、http、logger等
const res = await this.ctx.http.request({url:"https://www.baidu.com"})
if(res.error){
//抛出异常,终止任务,否则将被判定为执行成功
throw new Error("部署失败:"+res.message)
}
//必须使用this.logger打印日志
this.logger.info("执行成功")
// this.outputName = xxxx //设置输出参数,可以被其他插件选择使用
}
}
`
@ -118,7 +137,14 @@ return class DemoTask extends AbstractTaskPlugin {
}
export function getDefaultDnsPlugin() {
const metadata = `accessType: aliyun #授权类型名称`
const metadata = `
accessType: aliyun #
#dependPlugins: # 使certdhttplodash-esutils
# @alicloud/openapi-client: ^0.4.12
#dependLibs: #
# aliyun: *
`
const script = `
const { AbstractDnsProvider } = await import("@certd/pipeline")

View File

@ -86,7 +86,9 @@ export class PluginService extends BaseService<PluginEntity> {
});
const disabledNames = list.map(it => it.name);
return builtInList.filter(it => !disabledNames.includes(it.name));
return builtInList.filter(it =>{
return !disabledNames.includes(it.name)
});
}
async getBuiltInEntityList() {
@ -253,7 +255,7 @@ export class PluginService extends BaseService<PluginEntity> {
let registry = null;
if (item.pluginType === "access") {
registry = accessRegistry;
} else if (item.pluginType === "plugin") {
} else if (item.pluginType === "deploy") {
registry = pluginRegistry;
} else if (item.pluginType === "dnsProvider") {
registry = dnsProviderRegistry;