diff --git a/packages/core/pipeline/src/plugin/api.ts b/packages/core/pipeline/src/plugin/api.ts index 0ddd7fa2..850d1a8c 100644 --- a/packages/core/pipeline/src/plugin/api.ts +++ b/packages/core/pipeline/src/plugin/api.ts @@ -23,6 +23,7 @@ export type TaskInputDefine = FormItemProps; export type PluginDefine = Registrable & { default?: any; + group?: string; input?: { [key: string]: TaskInputDefine; }; diff --git a/packages/core/pipeline/src/plugin/group.ts b/packages/core/pipeline/src/plugin/group.ts new file mode 100644 index 00000000..f7cb4bf9 --- /dev/null +++ b/packages/core/pipeline/src/plugin/group.ts @@ -0,0 +1,25 @@ +import { PluginDefine } from "./api"; + +export class PluginGroup { + key: string; + title: string; + desc?: string; + order: number; + plugins: PluginDefine[]; + constructor(key: string, title: string, order = 0, desc = "") { + this.key = key; + this.title = title; + this.order = order; + this.desc = desc; + this.plugins = []; + } +} + +export const pluginGroups = { + cert: new PluginGroup("cert", "证书申请", 1), + aliyun: new PluginGroup("aliyun", "阿里云", 2), + huawei: new PluginGroup("huawei", "华为云", 3), + tencent: new PluginGroup("tencent", "腾讯云", 4), + host: new PluginGroup("host", "主机", 5), + other: new PluginGroup("other", "其他", 7), +}; diff --git a/packages/core/pipeline/src/plugin/index.ts b/packages/core/pipeline/src/plugin/index.ts index 9b9e3a48..7881c08b 100644 --- a/packages/core/pipeline/src/plugin/index.ts +++ b/packages/core/pipeline/src/plugin/index.ts @@ -1,3 +1,4 @@ export * from "./api.js"; export * from "./registry.js"; export * from "./decorator.js"; +export * from "./group.js"; diff --git a/packages/core/pipeline/src/plugin/registry.ts b/packages/core/pipeline/src/plugin/registry.ts index 9d755e5d..684cd4ca 100644 --- a/packages/core/pipeline/src/plugin/registry.ts +++ b/packages/core/pipeline/src/plugin/registry.ts @@ -1,4 +1,16 @@ -import { Registry } from "../registry/index.js"; +import { OnRegisterContext, Registry } from "../registry/index.js"; import { AbstractTaskPlugin } from "./api.js"; +import { pluginGroups } from "./group.js"; -export const pluginRegistry = new Registry("plugin"); +const onRegister = ({ key, value }: OnRegisterContext) => { + const group = value?.define?.group as string; + if (group) { + if (pluginGroups.hasOwnProperty(group)) { + // @ts-ignore + pluginGroups[group].plugins.push(value.define); + } else { + pluginGroups.other.plugins.push(value.define); + } + } +}; +export const pluginRegistry = new Registry("plugin", onRegister); diff --git a/packages/core/pipeline/src/registry/registry.ts b/packages/core/pipeline/src/registry/registry.ts index b119c06a..c4d4271c 100644 --- a/packages/core/pipeline/src/registry/registry.ts +++ b/packages/core/pipeline/src/registry/registry.ts @@ -4,20 +4,31 @@ export type Registrable = { name: string; title: string; desc?: string; + group?: string; }; export type RegistryItem = { define: Registrable; target: T; }; + +export type OnRegisterContext = { + registry: Registry; + key: string; + value: RegistryItem; +}; +export type OnRegister = (ctx: OnRegisterContext) => void; export class Registry { type = ""; storage: { [key: string]: RegistryItem; } = {}; - constructor(type: string) { + onRegister?: OnRegister; + + constructor(type: string, onRegister?: OnRegister) { this.type = type; + this.onRegister = onRegister; } register(key: string, value: RegistryItem) { @@ -25,6 +36,13 @@ export class Registry { return; } this.storage[key] = value; + if (this.onRegister) { + this.onRegister({ + registry: this, + key, + value, + }); + } logger.info(`注册插件:${this.type}:${key}`); } diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts index e674c133..3f1b61f3 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts @@ -21,6 +21,7 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin { col: { span: 24, }, + order: -1, helper: "1、支持通配符域名,例如: *.foo.com、foo.com、*.test.handsfree.work\n" + "2、支持多个域名、多个子域名、多个通配符域名打到一个证书上(域名必须是在同一个DNS提供商解析)\n" + @@ -36,12 +37,14 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin { vModel: "value", }, required: true, + order: -1, helper: "请输入邮箱", }) email!: string; @TaskInput({ title: "更新天数", + value: 20, component: { name: "a-input-number", vModel: "value", diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts index 8d3e6887..eac31023 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts @@ -1,4 +1,4 @@ -import { Decorator, IsTaskPlugin, RunStrategy, TaskInput } from "@certd/pipeline"; +import { Decorator, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; import type { CertInfo, SSLProvider } from "./acme.js"; import { AcmeService } from "./acme.js"; import _ from "lodash-es"; @@ -11,7 +11,8 @@ export type { CertInfo }; @IsTaskPlugin({ name: "CertApply", - title: "证书申请", + title: "证书申请(JS版)", + group: pluginGroups.cert.key, desc: "免费通配符域名证书申请,支持多个域名打到同一个证书上", default: { input: { @@ -46,6 +47,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin { name: "pi-access-selector", type: "eab", }, + maybeNeed: true, helper: "如果使用ZeroSSL证书,需要提供EAB授权, 请前往 https://app.zerossl.com/developer 生成 'EAB Credentials for ACME Clients' ", }) eabAccessId!: number; diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/lego.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/lego.ts index 0e704737..d735d630 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/lego.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/lego.ts @@ -1,4 +1,4 @@ -import { IsTaskPlugin, RunStrategy, sp, Step, TaskInput } from "@certd/pipeline"; +import { IsTaskPlugin, pluginGroups, RunStrategy, sp, Step, TaskInput } from "@certd/pipeline"; import type { CertInfo } from "./acme.js"; import { CertReader } from "./cert-reader.js"; import { CertApplyBasePlugin } from "./base.js"; @@ -12,7 +12,8 @@ export type { CertInfo }; @IsTaskPlugin({ name: "CertApplyLego", title: "证书申请(Lego)", - desc: "支持海量DNS解析提供商,推荐使用", + group: pluginGroups.cert.key, + desc: "支持海量DNS解析提供商,推荐使用,一样的免费通配符域名证书申请,支持多个域名打到同一个证书上", default: { input: { renewDays: 20, @@ -29,7 +30,9 @@ export class CertApplyLegoPlugin extends CertApplyBasePlugin { component: { name: "a-input", vModel: "value", + placeholder: "alidns", }, + helper: "你的域名是通过哪家提供商进行解析的,具体应该配置什么请参考lego文档:https://go-acme.github.io/lego/dns/", required: true, }) dnsType!: string; @@ -39,7 +42,8 @@ export class CertApplyLegoPlugin extends CertApplyBasePlugin { component: { name: "a-textarea", vModel: "value", - rows: 6, + rows: 4, + placeholder: "ALICLOUD_ACCESS_KEY=abcdefghijklmnopqrstuvwx\nALICLOUD_SECRET_KEY=your-secret-key", }, required: true, helper: "一行一条,例如 appKeyId=xxxxx,具体配置请参考lego文档:https://go-acme.github.io/lego/dns/", @@ -52,16 +56,20 @@ export class CertApplyLegoPlugin extends CertApplyBasePlugin { name: "pi-access-selector", type: "eab", }, + maybeNeed: true, helper: "如果需要提供EAB授权", }) - eabAccessId!: number; + legoEabAccessId!: number; @TaskInput({ title: "自定义LEGO参数", component: { name: "a-input", vModel: "value", + placeholder: "--dns-timeout 30", }, + helper: "额外的lego命令行参数,参考文档:https://go-acme.github.io/lego/usage/cli/options/", + maybeNeed: true, }) customArgs = ""; @@ -73,8 +81,8 @@ export class CertApplyLegoPlugin extends CertApplyBasePlugin { this.userContext = this.ctx.userContext; this.http = this.ctx.http; this.lastStatus = this.ctx.lastStatus as Step; - if (this.eabAccessId) { - this.eab = await this.ctx.accessService.getById(this.eabAccessId); + if (this.legoEabAccessId) { + this.eab = await this.ctx.accessService.getById(this.legoEabAccessId); } } async onInit(): Promise {} diff --git a/packages/ui/Dockerfile b/packages/ui/Dockerfile index 6296ac98..2536668e 100644 --- a/packages/ui/Dockerfile +++ b/packages/ui/Dockerfile @@ -5,7 +5,7 @@ RUN npm install -g pnpm@8.15.7 --registry=https://registry.npmmirror.com RUN pnpm config set registry https://registry.npmmirror.com/ RUN cd /workspace/certd-client && pnpm install && npm run build -RUN cd /workspace/certd-server && pnpm install && npm run build +RUN cd /workspace/certd-server && pnpm install && npm run build-on-docker RUN cp /workspace/certd-client/dist/* /workspace/certd-server/public/ -rf diff --git a/packages/ui/certd-client/src/views/certd/pipeline/api.plugin.ts b/packages/ui/certd-client/src/views/certd/pipeline/api.plugin.ts index 0dbeba31..d64106f1 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/api.plugin.ts +++ b/packages/ui/certd-client/src/views/certd/pipeline/api.plugin.ts @@ -9,12 +9,7 @@ const defaultInputDefine = { } }; -export async function GetList(query: any) { - const plugins = await request({ - url: apiPrefix + "/list", - method: "post", - params: query - }); +function initPlugins(plugins: any) { for (const plugin of plugins) { for (const key in plugin.input) { const field = _.merge({}, defaultInputDefine, plugin.input[key]); @@ -24,7 +19,7 @@ export async function GetList(query: any) { //嵌套对象 field.key = ["input", key]; if (field.required) { - delete field.required; + // delete field.required; if (field.rules == null) { field.rules = []; } @@ -34,5 +29,28 @@ export async function GetList(query: any) { } } console.log("plugins", plugins); +} + +export async function GetList(query: any) { + const plugins = await request({ + url: apiPrefix + "/list", + method: "post", + params: query + }); + initPlugins(plugins); return plugins; } + +export async function GetGroups(query: any) { + const groups = await request({ + url: apiPrefix + "/groups", + method: "post", + params: query + }); + const plugins: any = []; + for (const groupKey in groups) { + plugins.push(...groups[groupKey].plugins); + } + initPlugins(plugins); + return groups; +} diff --git a/packages/ui/certd-client/src/views/certd/pipeline/certd-form/crud.tsx b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/crud.tsx index 1db95250..17877007 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/certd-form/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/crud.tsx @@ -1,7 +1,39 @@ -import { compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from "@fast-crud/fast-crud"; -import { Dicts } from "./dicts"; +import { compute, CreateCrudOptionsRet, dict } from "@fast-crud/fast-crud"; +import { PluginGroup } from "@certd/pipeline"; + +export default function (certPluginGroup: PluginGroup, formWrapperRef: any): CreateCrudOptionsRet { + const inputs: any = {}; + + for (const plugin of certPluginGroup.plugins) { + for (const inputKey in plugin.input) { + if (inputs[inputKey]) { + inputs[inputKey].form.show = true; + continue; + } + const inputDefine = plugin.input[inputKey]; + if (!inputDefine.required && !inputDefine.maybeNeed) { + continue; + } + inputs[inputKey] = { + title: inputDefine.title, + form: { + ...inputDefine, + show: compute((ctx) => { + console.log(formWrapperRef); + const form = formWrapperRef.value.getFormData(); + if (!form) { + return false; + } + return form?.certApplyPlugin === plugin.name; + }) + } + }; + } + } + + console.log(inputs); + debugger; -export default function (): CreateCrudOptionsRet { return { crudOptions: { form: { @@ -10,143 +42,31 @@ export default function (): CreateCrudOptionsRet { } }, columns: { - domains: { - title: "域名", + certApplyPlugin: { + title: "证书申请插件", type: "dict-select", - search: { - show: true, - component: { - name: "a-input" - } - }, + dict: dict({ + data: [ + { value: "CertApply", label: "JS-ACME" }, + { value: "CertApplyLego", label: "Lego-ACME" } + ] + }), form: { - col: { - span: 24 - }, - wrapperCol: { - span: null - }, - component: { - mode: "tags", - open: false - }, + order: 0, + value: "CertApply", helper: { render: () => { return ( -
-
支持通配符域名,例如: *.foo.com 、 *.test.handsfree.work
-
支持多个域名、多个子域名、多个通配符域名打到一个证书上(域名必须是在同一个DNS提供商解析)
-
多级子域名要分成多个域名输入(*.foo.com的证书不能用于xxx.yyy.foo.com)
-
输入一个回车之后,再输入下一个
-
+
    +
  • Lego-ACME:基于Lego实现,支持海量DNS提供商
  • +
  • JS-ACME:如果你的域名DNS属于阿里云、腾讯云、Cloudflare可以选择用它来申请
  • +
); } - }, - valueResolve({ form }) { - if (form.domains instanceof String) { - form.domains = form.domains?.join(","); - } - }, - rules: [{ required: true, message: "请填写域名" }] - } - }, - email: { - title: "邮箱", - type: "text", - search: { show: false }, - form: { - rules: [{ required: true, type: "email", message: "请填写邮箱" }] - } - }, - blank: { - title: "占位", - type: "text", - form: { - blank: true - } - }, - sslProvider: { - title: "证书提供商", - type: "dict-select", - dict: Dicts.sslProviderDict - }, - eabAccess: { - title: "EAB授权", - type: "dict-select", - form: { - component: { - name: "PiAccessSelector", - type: "eab", - vModel: "modelValue" - }, - helper: "如果是ZeroSSL,需要配置EAB授权,https://app.zerossl.com/developer 生成 'EAB' " - } - }, - dnsProviderType: { - title: "DNS提供商", - type: "dict-select", - dict: Dicts.dnsProviderTypeDict, - form: { - value: "aliyun", - rules: [{ required: true, message: "请选择DNS提供商" }], - valueChange({ form }) { - form.dnsProviderAccess = null; } } }, - dnsProviderAccess: { - title: "DNS授权", - type: "text", - form: { - component: { - name: "PiAccessSelector", - type: compute(({ form }) => { - return form.dnsProviderType; - }), - vModel: "modelValue" - }, - rules: [{ required: true, message: "请选择DNS授权" }] - } - } - // country: { - // title: "国家", - // type: "text", - // form: { - // value: "China" - // } - // }, - // state: { - // title: "省份", - // type: "text", - // form: { - // value: "GuangDong" - // } - // }, - // locality: { - // title: "市区", - // type: "text", - // form: { - // value: "NanShan" - // } - // }, - // organization: { - // title: "单位", - // type: "text", - // form: { - // value: "CertD" - // } - // }, - // organizationUnit: { - // title: "部门", - // type: "text", - // form: { - // value: "IT Dept" - // } - // }, - // remark: { - // title: "备注", - // type: "text" - // } + ...inputs } } }; diff --git a/packages/ui/certd-client/src/views/certd/pipeline/certd-form/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/index.vue index 115cf1f4..270ade33 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/certd-form/index.vue +++ b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/index.vue @@ -1,5 +1,5 @@