mirror of https://github.com/certd/certd
refactor(plugin): 重构插件定义和安装流程
- 更新插件配置格式,增加依赖库和插件类型字段 - 修改插件安装流程,支持安装依赖插件和第三方库 - 优化插件列表过滤逻辑,按类型筛选插件 - 调整 Dockerfile,使用 Node.js22 镜像并更新 pnpm 安装方式pull/370/head
parent
420b0394a7
commit
3d9620abb0
|
@ -44,7 +44,7 @@ jobs:
|
||||||
# cache: 'npm'
|
# cache: 'npm'
|
||||||
# working-directory: ./packages/ui/certd-client
|
# working-directory: ./packages/ui/certd-client
|
||||||
- run: |
|
- run: |
|
||||||
npm install -g pnpm@8.15.7
|
npm install -g pnpm
|
||||||
pnpm install
|
pnpm install
|
||||||
npm run build
|
npm run build
|
||||||
working-directory: ./packages/ui/certd-client
|
working-directory: ./packages/ui/certd-client
|
||||||
|
|
|
@ -65,6 +65,8 @@ export type PluginDefine = Registrable & {
|
||||||
};
|
};
|
||||||
needPlus?: boolean;
|
needPlus?: boolean;
|
||||||
showRunStrategy?: boolean;
|
showRunStrategy?: boolean;
|
||||||
|
pluginType?: string; //类型
|
||||||
|
type?: string; //来源
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ITaskPlugin = {
|
export type ITaskPlugin = {
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
FROM node:20-alpine AS builder
|
FROM node:22-alpine AS builder
|
||||||
WORKDIR /workspace/
|
WORKDIR /workspace/
|
||||||
COPY . /workspace/
|
COPY . /workspace/
|
||||||
# armv7 目前只能用node18, pnpm9不支持node18,所以pnpm只能用8.15.7版本
|
# armv7 目前只能用node18, pnpm9不支持node18,所以pnpm只能用8.15.7版本
|
||||||
# https://github.com/nodejs/docker-node/issues/1946
|
# 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 cd /workspace/certd-client && pnpm install && npm run build
|
||||||
RUN cp /workspace/certd-client/dist/* /workspace/certd-server/public/ -rf
|
RUN cp /workspace/certd-client/dist/* /workspace/certd-server/public/ -rf
|
||||||
RUN cd /workspace/certd-server && pnpm install && npm run build-on-docker
|
RUN cd /workspace/certd-server && pnpm install && npm run build-on-docker
|
||||||
|
|
||||||
|
|
||||||
FROM node:20-alpine
|
FROM node:22-alpine
|
||||||
EXPOSE 7001
|
EXPOSE 7001
|
||||||
EXPOSE 7002
|
EXPOSE 7002
|
||||||
RUN apk add --no-cache openssl
|
RUN apk add --no-cache openssl
|
||||||
|
|
|
@ -32,7 +32,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
async sleep(ms: number) {
|
async sleep(ms: number) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
},
|
},
|
||||||
|
|
||||||
maxLength(str?: string, length = 100) {
|
maxLength(str?: string, length = 100) {
|
||||||
|
@ -42,6 +42,9 @@ export default {
|
||||||
return "";
|
return "";
|
||||||
},
|
},
|
||||||
transformLink(desc: string = "") {
|
transformLink(desc: string = "") {
|
||||||
return desc.replace(/\[(.*)\]\((.*)\)/g, '<a href="$2" target="_blank">$1</a>');
|
if (!desc) {
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
return desc.replace(/\[(.*)\]\((.*)\)/g, '<a href="$2" target="_blank">$1</a>');
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -241,7 +241,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
data: [
|
data: [
|
||||||
{ label: "授权", value: "access" },
|
{ label: "授权", value: "access" },
|
||||||
{ label: "DNS", value: "dnsProvider" },
|
{ label: "DNS", value: "dnsProvider" },
|
||||||
{ label: "部署插件", value: "plugin" },
|
{ label: "部署插件", value: "deploy" },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
column: {
|
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": {
|
"extra.default.strategy.runStrategy": {
|
||||||
title: "运行策略",
|
title: "运行策略",
|
||||||
type: "dict-select",
|
type: "dict-select",
|
||||||
|
|
||||||
dict: dict({
|
dict: dict({
|
||||||
data: [
|
data: [
|
||||||
{ value: 0, label: "正常运行" },
|
{ value: 0, label: "正常运行" },
|
||||||
|
@ -293,6 +343,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
value: 1,
|
value: 1,
|
||||||
rules: [{ required: true }],
|
rules: [{ required: true }],
|
||||||
helper: "默认运行策略",
|
helper: "默认运行策略",
|
||||||
|
show: compute(({ form }) => {
|
||||||
|
return form.extra.showRunStrategy;
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 100,
|
width: 100,
|
||||||
|
@ -300,6 +353,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
component: {
|
component: {
|
||||||
color: "auto",
|
color: "auto",
|
||||||
},
|
},
|
||||||
|
show: false,
|
||||||
},
|
},
|
||||||
valueBuilder({ row }) {
|
valueBuilder({ row }) {
|
||||||
if (row.extra) {
|
if (row.extra) {
|
||||||
|
|
|
@ -13,6 +13,10 @@ export class BuiltInPluginService {
|
||||||
if (Plugin?.define?.deprecated) {
|
if (Plugin?.define?.deprecated) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
//@ts-ignore
|
||||||
|
if(Plugin.define?.type && Plugin.define?.type !== 'builtin'){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
list.push({ ...Plugin.define, key });
|
list.push({ ...Plugin.define, key });
|
||||||
}
|
}
|
||||||
list = list.sort((a, b) => {
|
list = list.sort((a, b) => {
|
||||||
|
|
|
@ -1,28 +1,36 @@
|
||||||
import yaml from "js-yaml";
|
import yaml from "js-yaml";
|
||||||
const CertOutputs = [
|
import { CertApplyPluginNames } from "@certd/plugin-cert";
|
||||||
"CertApply",
|
|
||||||
"CertApplyLego",
|
|
||||||
"CertApplyUpload"
|
|
||||||
];
|
|
||||||
|
|
||||||
export function getDefaultAccessPlugin() {
|
export function getDefaultAccessPlugin() {
|
||||||
const metadata = {
|
const metadata = `
|
||||||
username: {
|
input:
|
||||||
title: "用户名",
|
username: # 授权参数名
|
||||||
required: true,
|
title: 用户名 # 授权参数标题
|
||||||
encrypt: false
|
required: true # 是否必填项
|
||||||
},
|
encrypt: false # 是否加密
|
||||||
password: {
|
component: # 输入组件配置
|
||||||
title: "密码",
|
name: a-input #输入组件名称
|
||||||
required: true,
|
allowClear: true # 组件的参数,参考 https://www.antdv.com/components/input#api
|
||||||
|
password:
|
||||||
|
title: 密码
|
||||||
|
required: true
|
||||||
encrypt: 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 {
|
return class DemoAccess extends BaseAccess {
|
||||||
username;
|
# 授权的字段,跟左边input一一对应
|
||||||
password;
|
username;
|
||||||
|
password;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
return {
|
return {
|
||||||
|
@ -32,21 +40,27 @@ password;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDefaultDeployPlugin() {
|
export function getDefaultDeployPlugin() {
|
||||||
const metadata = {
|
|
||||||
cert: {
|
let certApplyNames = ''
|
||||||
title: "前置任务证书",
|
for (const name of CertApplyPluginNames) {
|
||||||
component: {
|
certApplyNames += `
|
||||||
name: "output-selector",
|
- ${name}`
|
||||||
from: [...CertOutputs]
|
}
|
||||||
},
|
const metadata =`
|
||||||
|
input: # 插件的输入参数
|
||||||
|
cert:
|
||||||
|
title: 前置任务证书
|
||||||
|
helper: 请选择前置任务产生的证书 # 帮助说明
|
||||||
|
component:
|
||||||
|
name: output-selector # 输入组件名称
|
||||||
|
vModel: modelValue # 组件参数
|
||||||
|
from:${certApplyNames}
|
||||||
required: true
|
required: true
|
||||||
},
|
certDomains:
|
||||||
certDomains: {
|
title: 当前证书域名
|
||||||
title: "当前证书域名",
|
component:
|
||||||
component: {
|
name: cert-domains-getter
|
||||||
name: "cert-domains-getter"
|
mergeScript: |
|
||||||
},
|
|
||||||
mergeScript: `
|
|
||||||
return {
|
return {
|
||||||
component:{
|
component:{
|
||||||
inputKey: ctx.compute(({form})=>{
|
inputKey: ctx.compute(({form})=>{
|
||||||
|
@ -54,60 +68,65 @@ export function getDefaultDeployPlugin() {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
|
||||||
required: true
|
required: true
|
||||||
},
|
accessId:
|
||||||
accessId: {
|
title: Access授权
|
||||||
title: "Access授权",
|
helper: xxxx的授权
|
||||||
helper: "xxxx的授权",
|
component:
|
||||||
component: {
|
name: access-selector # 授权选择组件名称
|
||||||
name: "access-selector",
|
type: aliyun # 授权类型
|
||||||
type: "aliyun"
|
|
||||||
},
|
|
||||||
required: true
|
required: true
|
||||||
},
|
key1:
|
||||||
key1: {
|
title: 输入示例1
|
||||||
title: "输入示例1",
|
|
||||||
required: false
|
required: false
|
||||||
},
|
key2:
|
||||||
key2: {
|
title: 可选项
|
||||||
title: "可选项",
|
component:
|
||||||
component: {
|
name: a-select
|
||||||
name: "a-select",
|
vMode: value
|
||||||
vMode: "value",
|
options:
|
||||||
options: [
|
- value: "1"
|
||||||
{ value: "1", label: "选项1" },
|
label: 选项1
|
||||||
{ value: "2", label: "选项2" }
|
- value: "2"
|
||||||
]
|
label: 选项2
|
||||||
},
|
|
||||||
required: false
|
required: false
|
||||||
}
|
#output: # 输出参数,一般插件都不需要配置此项
|
||||||
};
|
# outputName:
|
||||||
|
#
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
const script = `
|
const script = `
|
||||||
|
// 要用await来import模块
|
||||||
const { AbstractTaskPlugin } = await import("@certd/pipeline")
|
const { AbstractTaskPlugin } = await import("@certd/pipeline")
|
||||||
|
// 要返回一个继承AbstractTaskPlugin的class
|
||||||
return class DemoTask extends AbstractTaskPlugin {
|
return class DemoTask extends AbstractTaskPlugin {
|
||||||
|
// 这里是插件的输入参数,对应左边的input配置
|
||||||
cert;
|
cert;
|
||||||
certDomains;
|
certDomains;
|
||||||
accessId;
|
accessId;
|
||||||
key1;
|
key1;
|
||||||
key2;
|
key2;
|
||||||
|
// 编写执行方法
|
||||||
async execute(){
|
async execute(){
|
||||||
|
# 根据accessId获取授权配置
|
||||||
const access = await this.accessService.getById(this.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("certDomains:",this.certDomains);
|
||||||
this.logger.info("access:",access);
|
this.logger.info("access:",access);
|
||||||
this.logger.info("key1:",this.key1);
|
this.logger.info("key1:",this.key1);
|
||||||
this.logger.info("key2:",this.key2);
|
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){
|
if(res.error){
|
||||||
//抛出异常,终止任务,否则将被判定为执行成功
|
//抛出异常,终止任务,否则将被判定为执行成功
|
||||||
throw new Error("部署失败:"+res.message)
|
throw new Error("部署失败:"+res.message)
|
||||||
}
|
}
|
||||||
//必须使用this.logger打印日志
|
|
||||||
this.logger.info("执行成功")
|
this.logger.info("执行成功")
|
||||||
|
// this.outputName = xxxx //设置输出参数,可以被其他插件选择使用
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
@ -118,7 +137,14 @@ return class DemoTask extends AbstractTaskPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDefaultDnsPlugin() {
|
export function getDefaultDnsPlugin() {
|
||||||
const metadata = `accessType: aliyun #授权类型名称`
|
const metadata = `
|
||||||
|
accessType: aliyun # 授权类型名称
|
||||||
|
#dependPlugins: # 依赖第三方库,安装插件时会安装依赖库,尽量使用certd已安装的库,比如http、lodash-es、utils
|
||||||
|
# @alicloud/openapi-client: ^0.4.12
|
||||||
|
#dependLibs: # 依赖的插件,应用商店安装时会先安装依赖插件
|
||||||
|
# aliyun: *
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
const script = `
|
const script = `
|
||||||
const { AbstractDnsProvider } = await import("@certd/pipeline")
|
const { AbstractDnsProvider } = await import("@certd/pipeline")
|
||||||
|
|
|
@ -86,7 +86,9 @@ export class PluginService extends BaseService<PluginEntity> {
|
||||||
});
|
});
|
||||||
const disabledNames = list.map(it => it.name);
|
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() {
|
async getBuiltInEntityList() {
|
||||||
|
@ -253,7 +255,7 @@ export class PluginService extends BaseService<PluginEntity> {
|
||||||
let registry = null;
|
let registry = null;
|
||||||
if (item.pluginType === "access") {
|
if (item.pluginType === "access") {
|
||||||
registry = accessRegistry;
|
registry = accessRegistry;
|
||||||
} else if (item.pluginType === "plugin") {
|
} else if (item.pluginType === "deploy") {
|
||||||
registry = pluginRegistry;
|
registry = pluginRegistry;
|
||||||
} else if (item.pluginType === "dnsProvider") {
|
} else if (item.pluginType === "dnsProvider") {
|
||||||
registry = dnsProviderRegistry;
|
registry = dnsProviderRegistry;
|
||||||
|
|
Loading…
Reference in New Issue