mirror of https://github.com/certd/certd
				
				
				
			perf: 创建证书任务可以选择lege插件
							parent
							
								
									4afbf20c1a
								
							
						
					
					
						commit
						affef13037
					
				| 
						 | 
				
			
			@ -23,6 +23,7 @@ export type TaskInputDefine = FormItemProps;
 | 
			
		|||
 | 
			
		||||
export type PluginDefine = Registrable & {
 | 
			
		||||
  default?: any;
 | 
			
		||||
  group?: string;
 | 
			
		||||
  input?: {
 | 
			
		||||
    [key: string]: TaskInputDefine;
 | 
			
		||||
  };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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),
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
export * from "./api.js";
 | 
			
		||||
export * from "./registry.js";
 | 
			
		||||
export * from "./decorator.js";
 | 
			
		||||
export * from "./group.js";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<AbstractTaskPlugin>("plugin");
 | 
			
		||||
const onRegister = ({ key, value }: OnRegisterContext<AbstractTaskPlugin>) => {
 | 
			
		||||
  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<AbstractTaskPlugin>("plugin", onRegister);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,20 +4,31 @@ export type Registrable = {
 | 
			
		|||
  name: string;
 | 
			
		||||
  title: string;
 | 
			
		||||
  desc?: string;
 | 
			
		||||
  group?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type RegistryItem<T> = {
 | 
			
		||||
  define: Registrable;
 | 
			
		||||
  target: T;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type OnRegisterContext<T> = {
 | 
			
		||||
  registry: Registry<T>;
 | 
			
		||||
  key: string;
 | 
			
		||||
  value: RegistryItem<T>;
 | 
			
		||||
};
 | 
			
		||||
export type OnRegister<T> = (ctx: OnRegisterContext<T>) => void;
 | 
			
		||||
export class Registry<T> {
 | 
			
		||||
  type = "";
 | 
			
		||||
  storage: {
 | 
			
		||||
    [key: string]: RegistryItem<T>;
 | 
			
		||||
  } = {};
 | 
			
		||||
 | 
			
		||||
  constructor(type: string) {
 | 
			
		||||
  onRegister?: OnRegister<T>;
 | 
			
		||||
 | 
			
		||||
  constructor(type: string, onRegister?: OnRegister<T>) {
 | 
			
		||||
    this.type = type;
 | 
			
		||||
    this.onRegister = onRegister;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  register(key: string, value: RegistryItem<T>) {
 | 
			
		||||
| 
						 | 
				
			
			@ -25,6 +36,13 @@ export class Registry<T> {
 | 
			
		|||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this.storage[key] = value;
 | 
			
		||||
    if (this.onRegister) {
 | 
			
		||||
      this.onRegister({
 | 
			
		||||
        registry: this,
 | 
			
		||||
        key,
 | 
			
		||||
        value,
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    logger.info(`注册插件:${this.type}:${key}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<void> {}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 (
 | 
			
		||||
                  <div>
 | 
			
		||||
                    <div>支持通配符域名,例如: *.foo.com 、 *.test.handsfree.work</div>
 | 
			
		||||
                    <div>支持多个域名、多个子域名、多个通配符域名打到一个证书上(域名必须是在同一个DNS提供商解析)</div>
 | 
			
		||||
                    <div>多级子域名要分成多个域名输入(*.foo.com的证书不能用于xxx.yyy.foo.com)</div>
 | 
			
		||||
                    <div>输入一个回车之后,再输入下一个</div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <ul>
 | 
			
		||||
                    <li>Lego-ACME:基于Lego实现,支持海量DNS提供商</li>
 | 
			
		||||
                    <li>JS-ACME:如果你的域名DNS属于阿里云、腾讯云、Cloudflare可以选择用它来申请</li>
 | 
			
		||||
                  </ul>
 | 
			
		||||
                );
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            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
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <fs-form-wrapper ref="formWrapperRef" />
 | 
			
		||||
  <fs-form-wrapper v-if="formWrapperOptions" ref="formWrapperRef" />
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
| 
						 | 
				
			
			@ -7,28 +7,37 @@ import { useColumns, useExpose } from "@fast-crud/fast-crud";
 | 
			
		|||
import createCrudOptions from "./crud.jsx";
 | 
			
		||||
import { ref } from "vue";
 | 
			
		||||
import _ from "lodash-es";
 | 
			
		||||
import * as api from "../api.plugin";
 | 
			
		||||
import { PluginGroup, PluginGroups } from "/@/views/certd/pipeline/pipeline/type";
 | 
			
		||||
export default {
 | 
			
		||||
  name: "PiCertdForm",
 | 
			
		||||
  setup(props: any, ctx: any) {
 | 
			
		||||
    // 自定义表单配置
 | 
			
		||||
    const { buildFormOptions } = useColumns();
 | 
			
		||||
    //使用crudOptions结构来构建自定义表单配置
 | 
			
		||||
    let { crudOptions } = createCrudOptions();
 | 
			
		||||
    const doSubmitRef = ref();
 | 
			
		||||
    const formOptions = buildFormOptions(
 | 
			
		||||
      _.merge(crudOptions, {
 | 
			
		||||
        form: {
 | 
			
		||||
          doSubmit({ form }: any) {
 | 
			
		||||
            // 创建certd 的pipeline
 | 
			
		||||
            doSubmitRef.value({ form });
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }) as any
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const formWrapperRef = ref();
 | 
			
		||||
    const formWrapperOptions = ref();
 | 
			
		||||
    formWrapperOptions.value = formOptions;
 | 
			
		||||
    const doSubmitRef = ref();
 | 
			
		||||
    async function buildFormOptions() {
 | 
			
		||||
      const pluginGroups: { [key: string]: PluginGroup } = await api.GetGroups({});
 | 
			
		||||
      const certPluginGroup = pluginGroups.cert;
 | 
			
		||||
 | 
			
		||||
      // 自定义表单配置
 | 
			
		||||
      const { buildFormOptions } = useColumns();
 | 
			
		||||
      //使用crudOptions结构来构建自定义表单配置
 | 
			
		||||
      let { crudOptions } = createCrudOptions(certPluginGroup, formWrapperRef);
 | 
			
		||||
 | 
			
		||||
      const formOptions = buildFormOptions(
 | 
			
		||||
        _.merge(crudOptions, {
 | 
			
		||||
          form: {
 | 
			
		||||
            doSubmit({ form }: any) {
 | 
			
		||||
              // 创建certd 的pipeline
 | 
			
		||||
              doSubmitRef.value({ form });
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }) as any
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      formWrapperOptions.value = formOptions;
 | 
			
		||||
    }
 | 
			
		||||
    buildFormOptions();
 | 
			
		||||
    function open(doSubmit: any) {
 | 
			
		||||
      doSubmitRef.value = doSubmit;
 | 
			
		||||
      formWrapperRef.value.open(formWrapperOptions.value);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,7 +58,7 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
 | 
			
		|||
                    strategy: {
 | 
			
		||||
                      runStrategy: 0 // 正常执行
 | 
			
		||||
                    },
 | 
			
		||||
                    type: "CertApply"
 | 
			
		||||
                    type: form.certApplyPlugin
 | 
			
		||||
                  }
 | 
			
		||||
                ]
 | 
			
		||||
              }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,14 +11,14 @@ import * as pluginApi from "./api.plugin";
 | 
			
		|||
import * as historyApi from "./api.history";
 | 
			
		||||
import * as api from "./api";
 | 
			
		||||
import { useRoute } from "vue-router";
 | 
			
		||||
import { PipelineDetail, PipelineOptions, RunHistory } from "./pipeline/type";
 | 
			
		||||
import { PipelineDetail, PipelineOptions, PluginGroups, RunHistory } from "./pipeline/type";
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  name: "PipelineDetail",
 | 
			
		||||
  components: { PipelineEdit },
 | 
			
		||||
  setup() {
 | 
			
		||||
    const route = useRoute();
 | 
			
		||||
    const pipelineId:Ref = ref(route.query.id);
 | 
			
		||||
    const pipelineId: Ref = ref(route.query.id);
 | 
			
		||||
 | 
			
		||||
    const pipelineOptions: PipelineOptions = {
 | 
			
		||||
      async getPipelineDetail({ pipelineId }) {
 | 
			
		||||
| 
						 | 
				
			
			@ -43,9 +43,9 @@ export default defineComponent({
 | 
			
		|||
        return detail;
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      async getPlugins() {
 | 
			
		||||
        const plugins = await pluginApi.GetList({});
 | 
			
		||||
        return plugins as any[];
 | 
			
		||||
      async getPluginGroups() {
 | 
			
		||||
        const groups = await pluginApi.GetGroups({});
 | 
			
		||||
        return new PluginGroups(groups);
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      async doSave(pipelineConfig: any) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,13 +4,9 @@
 | 
			
		|||
      <div class="title">我的流水线</div>
 | 
			
		||||
    </template>
 | 
			
		||||
    <fs-crud ref="crudRef" v-bind="crudBinding">
 | 
			
		||||
      <template #actionbar-right>
 | 
			
		||||
        <!--        <span style="margin-left: 10px">出现<a-tag>Promise rejected attempt #18,retrying in 10000ms:No TXT recordsfound for name</a-tag>属于正常现象,多重试几次</span>-->
 | 
			
		||||
      </template>
 | 
			
		||||
      <template #actionbar-right> </template>
 | 
			
		||||
      <template #form-bottom>
 | 
			
		||||
        <div>
 | 
			
		||||
          申请证书
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>申请证书</div>
 | 
			
		||||
      </template>
 | 
			
		||||
      <pi-certd-form ref="certdFormRef"></pi-certd-form>
 | 
			
		||||
    </fs-crud>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,7 @@ export default {
 | 
			
		|||
    }
 | 
			
		||||
  },
 | 
			
		||||
  emits: ["update:modelValue"],
 | 
			
		||||
  setup(props:any, ctx:any) {
 | 
			
		||||
  setup(props: any, ctx: any) {
 | 
			
		||||
    const options = ref<any[]>([]);
 | 
			
		||||
 | 
			
		||||
    const pipeline = inject("pipeline") as Ref<any>;
 | 
			
		||||
| 
						 | 
				
			
			@ -23,8 +23,10 @@ export default {
 | 
			
		|||
    const currentStepIndex = inject("currentStepIndex") as Ref<number>;
 | 
			
		||||
    const currentTask = inject("currentTask") as Ref<any>;
 | 
			
		||||
 | 
			
		||||
    const getPluginGroups = inject("getPluginGroups") as Ref<any>;
 | 
			
		||||
    const pluginGroups = getPluginGroups();
 | 
			
		||||
    function onCreate() {
 | 
			
		||||
      options.value = pluginManager.getPreStepOutputOptions({
 | 
			
		||||
      options.value = pluginGroups.getPreStepOutputOptions({
 | 
			
		||||
        pipeline: pipeline.value,
 | 
			
		||||
        currentStageIndex: currentStageIndex.value,
 | 
			
		||||
        currentStepIndex: currentStepIndex.value,
 | 
			
		||||
| 
						 | 
				
			
			@ -40,14 +42,14 @@ export default {
 | 
			
		|||
 | 
			
		||||
    watch(
 | 
			
		||||
      () => {
 | 
			
		||||
        return pluginManager.map;
 | 
			
		||||
        return pluginGroups.value.map;
 | 
			
		||||
      },
 | 
			
		||||
      () => {
 | 
			
		||||
        onCreate();
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    function onChanged(value:any) {
 | 
			
		||||
    function onChanged(value: any) {
 | 
			
		||||
      ctx.emit("update:modelValue", value);
 | 
			
		||||
    }
 | 
			
		||||
    return {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <a-drawer v-model:open="stepDrawerVisible" placement="right" :closable="true" width="600px" @after-open-change="stepDrawerOnAfterVisibleChange">
 | 
			
		||||
  <a-drawer v-model:open="stepDrawerVisible" placement="right" :closable="true" width="700px" @after-open-change="stepDrawerOnAfterVisibleChange">
 | 
			
		||||
    <template #title>
 | 
			
		||||
      编辑步骤
 | 
			
		||||
      <a-button v-if="editMode" @click="stepDelete()">
 | 
			
		||||
| 
						 | 
				
			
			@ -8,30 +8,36 @@
 | 
			
		|||
    </template>
 | 
			
		||||
    <template v-if="currentStep">
 | 
			
		||||
      <pi-container v-if="currentStep._isAdd" class="pi-step-form">
 | 
			
		||||
        <a-row :gutter="10">
 | 
			
		||||
          <a-col v-for="(item, index) of stepPluginDefineList" :key="index" class="step-plugin" :span="12">
 | 
			
		||||
            <a-card
 | 
			
		||||
              hoverable
 | 
			
		||||
              :class="{ current: item.name === currentStep.type }"
 | 
			
		||||
              @click="stepTypeSelected(item)"
 | 
			
		||||
              @dblclick="
 | 
			
		||||
                stepTypeSelected(item);
 | 
			
		||||
                stepTypeSave();
 | 
			
		||||
              "
 | 
			
		||||
            >
 | 
			
		||||
              <a-card-meta>
 | 
			
		||||
                <template #title>
 | 
			
		||||
                  <a-avatar :src="item.icon || '/images/plugin.png'" />
 | 
			
		||||
                  <span class="title">{{ item.title }}</span>
 | 
			
		||||
                </template>
 | 
			
		||||
                <template #description>
 | 
			
		||||
                  <span :title="item.desc">{{ item.desc }}</span>
 | 
			
		||||
                </template>
 | 
			
		||||
              </a-card-meta>
 | 
			
		||||
            </a-card>
 | 
			
		||||
          </a-col>
 | 
			
		||||
        </a-row>
 | 
			
		||||
        <a-button v-if="editMode" type="primary" @click="stepTypeSave"> 确定 </a-button>
 | 
			
		||||
        <a-tabs tab-position="left">
 | 
			
		||||
          <a-tab-pane v-for="group of pluginGroups.groups" :key="group.key" :tab="group.title">
 | 
			
		||||
            <a-row :gutter="10">
 | 
			
		||||
              <a-col v-for="item of group.plugins" :key="item.key" class="step-plugin" :span="12">
 | 
			
		||||
                <a-card
 | 
			
		||||
                  hoverable
 | 
			
		||||
                  :class="{ current: item.name === currentStep.type }"
 | 
			
		||||
                  @click="stepTypeSelected(item)"
 | 
			
		||||
                  @dblclick="
 | 
			
		||||
                    stepTypeSelected(item);
 | 
			
		||||
                    stepTypeSave();
 | 
			
		||||
                  "
 | 
			
		||||
                >
 | 
			
		||||
                  <a-card-meta>
 | 
			
		||||
                    <template #title>
 | 
			
		||||
                      <a-avatar :src="item.icon || '/images/plugin.png'" />
 | 
			
		||||
                      <span class="title">{{ item.title }}</span>
 | 
			
		||||
                    </template>
 | 
			
		||||
                    <template #description>
 | 
			
		||||
                      <span :title="item.desc">{{ item.desc }}</span>
 | 
			
		||||
                    </template>
 | 
			
		||||
                  </a-card-meta>
 | 
			
		||||
                </a-card>
 | 
			
		||||
              </a-col>
 | 
			
		||||
            </a-row>
 | 
			
		||||
          </a-tab-pane>
 | 
			
		||||
        </a-tabs>
 | 
			
		||||
        <div style="padding: 20px; margin-left: 100px">
 | 
			
		||||
          <a-button v-if="editMode" type="primary" @click="stepTypeSave"> 确定 </a-button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </pi-container>
 | 
			
		||||
      <pi-container v-else class="pi-step-form">
 | 
			
		||||
        <a-form ref="stepFormRef" class="step-form" :model="currentStep" :label-col="labelCol" :wrapper-col="wrapperCol">
 | 
			
		||||
| 
						 | 
				
			
			@ -91,6 +97,8 @@ import { computed, inject, Ref, ref } from "vue";
 | 
			
		|||
import _ from "lodash-es";
 | 
			
		||||
import { nanoid } from "nanoid";
 | 
			
		||||
import { CopyOutlined } from "@ant-design/icons-vue";
 | 
			
		||||
import { PluginGroups } from "/@/views/certd/pipeline/pipeline/type";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: "PiStepForm",
 | 
			
		||||
  components: { CopyOutlined },
 | 
			
		||||
| 
						 | 
				
			
			@ -107,8 +115,8 @@ export default {
 | 
			
		|||
     * @returns
 | 
			
		||||
     */
 | 
			
		||||
    function useStepForm() {
 | 
			
		||||
      const stepPluginDefineList: any = inject("plugins");
 | 
			
		||||
 | 
			
		||||
      const getPluginGroups: any = inject("getPluginGroups");
 | 
			
		||||
      const pluginGroups: PluginGroups = getPluginGroups();
 | 
			
		||||
      const mode: Ref = ref("add");
 | 
			
		||||
      const callback: Ref = ref();
 | 
			
		||||
      const currentStep: Ref = ref({ title: undefined, input: {} });
 | 
			
		||||
| 
						 | 
				
			
			@ -199,9 +207,8 @@ export default {
 | 
			
		|||
 | 
			
		||||
      const changeCurrentPlugin = (step: any) => {
 | 
			
		||||
        const stepType = step.type;
 | 
			
		||||
        const pluginDefine = stepPluginDefineList.value.find((p: any) => {
 | 
			
		||||
          return p.name === stepType;
 | 
			
		||||
        });
 | 
			
		||||
        const pluginDefine = pluginGroups.get(stepType);
 | 
			
		||||
        debugger;
 | 
			
		||||
        if (pluginDefine) {
 | 
			
		||||
          step.type = stepType;
 | 
			
		||||
          step._isAdd = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -271,7 +278,7 @@ export default {
 | 
			
		|||
      return {
 | 
			
		||||
        stepTypeSelected,
 | 
			
		||||
        stepTypeSave,
 | 
			
		||||
        stepPluginDefineList,
 | 
			
		||||
        pluginGroups,
 | 
			
		||||
        stepFormRef,
 | 
			
		||||
        mode,
 | 
			
		||||
        stepAdd,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,12 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <a-drawer v-model:open="taskDrawerVisible" placement="right" :closable="true" width="600px" class="pi-task-form" @after-open-change="taskDrawerOnAfterVisibleChange">
 | 
			
		||||
  <a-drawer
 | 
			
		||||
    v-model:open="taskDrawerVisible"
 | 
			
		||||
    placement="right"
 | 
			
		||||
    :closable="true"
 | 
			
		||||
    width="700px"
 | 
			
		||||
    class="pi-task-form"
 | 
			
		||||
    @after-open-change="taskDrawerOnAfterVisibleChange"
 | 
			
		||||
  >
 | 
			
		||||
    <template #title>
 | 
			
		||||
      编辑任务
 | 
			
		||||
      <a-button v-if="editMode" @click="taskDelete()">
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +31,13 @@
 | 
			
		|||
          />
 | 
			
		||||
 | 
			
		||||
          <div class="steps">
 | 
			
		||||
            <a-form-item :value="currentTask.steps" name="steps" label="" :wrapper-col="{ span: 24 }" :rules="[{ required: true, message: '至少需要一个步骤,或者你可以点击标题右边删除按钮删除此任务' }]">
 | 
			
		||||
            <a-form-item
 | 
			
		||||
              :value="currentTask.steps"
 | 
			
		||||
              name="steps"
 | 
			
		||||
              label=""
 | 
			
		||||
              :wrapper-col="{ span: 24 }"
 | 
			
		||||
              :rules="[{ required: true, message: '至少需要一个步骤,或者你可以点击标题右边删除按钮删除此任务' }]"
 | 
			
		||||
            >
 | 
			
		||||
              <a-descriptions title="任务步骤" size="small">
 | 
			
		||||
                <template #extra>
 | 
			
		||||
                  <a-button type="primary" @click="stepAdd(currentTask)">添加步骤</a-button>
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +83,7 @@ import { provide, Ref, ref } from "vue";
 | 
			
		|||
import _ from "lodash-es";
 | 
			
		||||
import { nanoid } from "nanoid";
 | 
			
		||||
import PiStepForm from "../step-form/index.vue";
 | 
			
		||||
import { message, Modal } from "ant-design-vue";
 | 
			
		||||
import { Modal } from "ant-design-vue";
 | 
			
		||||
import { CopyOutlined } from "@ant-design/icons-vue";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +101,7 @@ export default {
 | 
			
		|||
      const stepFormRef: Ref<any> = ref(null);
 | 
			
		||||
      const currentStepIndex = ref(0);
 | 
			
		||||
      provide("currentStepIndex", currentStepIndex);
 | 
			
		||||
      const stepAdd = (task: any, stepDef: any) => {
 | 
			
		||||
      const stepAdd = (task: any, stepDef?: any) => {
 | 
			
		||||
        currentStepIndex.value = task.steps.length;
 | 
			
		||||
        stepFormRef.value.stepAdd((type: any, value: any) => {
 | 
			
		||||
          if (type === "save") {
 | 
			
		||||
| 
						 | 
				
			
			@ -179,7 +192,7 @@ export default {
 | 
			
		|||
 | 
			
		||||
      const taskAdd = (emit: any, taskMerge: any) => {
 | 
			
		||||
        mode.value = "add";
 | 
			
		||||
        const blankTask = { id: nanoid(), title: "新任务", steps: [], status: null };
 | 
			
		||||
        const blankTask: any = { id: nanoid(), title: "新任务", steps: [], status: null };
 | 
			
		||||
        const task: any = _.merge(blankTask, taskMerge);
 | 
			
		||||
        taskOpen(task, emit);
 | 
			
		||||
      };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -223,7 +223,7 @@ import _ from "lodash-es";
 | 
			
		|||
import { message, Modal, notification } from "ant-design-vue";
 | 
			
		||||
import { pluginManager } from "/@/views/certd/pipeline/pipeline/plugin";
 | 
			
		||||
import { nanoid } from "nanoid";
 | 
			
		||||
import { PipelineDetail, PipelineOptions, RunHistory } from "./type";
 | 
			
		||||
import { PipelineDetail, PipelineOptions, PluginGroups, RunHistory } from "./type";
 | 
			
		||||
import type { Runnable } from "@certd/pipeline";
 | 
			
		||||
import PiHistoryTimelineItem from "/@/views/certd/pipeline/pipeline/component/history-timeline-item.vue";
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
| 
						 | 
				
			
			@ -348,17 +348,17 @@ export default defineComponent({
 | 
			
		|||
      }
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const plugins: Ref<any> = ref([]);
 | 
			
		||||
    const pluginGroupsRef: Ref<PluginGroups> = ref();
 | 
			
		||||
 | 
			
		||||
    const fetchPlugins = async () => {
 | 
			
		||||
      const list = await props.options.getPlugins();
 | 
			
		||||
      plugins.value = list;
 | 
			
		||||
      pluginManager.init(list);
 | 
			
		||||
      pluginGroupsRef.value = await props.options.getPluginGroups();
 | 
			
		||||
    };
 | 
			
		||||
    fetchPlugins();
 | 
			
		||||
 | 
			
		||||
    provide("pipeline", pipeline);
 | 
			
		||||
    provide("plugins", plugins);
 | 
			
		||||
    provide("getPluginGroups", () => {
 | 
			
		||||
      return pluginGroupsRef.value;
 | 
			
		||||
    });
 | 
			
		||||
    provide("currentHistory", currentHistory);
 | 
			
		||||
 | 
			
		||||
    function useTask() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,49 +17,7 @@ export class PluginManager {
 | 
			
		|||
    this.map = map;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get(name: string) {
 | 
			
		||||
    return this.map[name];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getPreStepOutputOptions({ pipeline, currentStageIndex, currentStepIndex, currentTask }: any) {
 | 
			
		||||
    const steps = this.collectionPreStepOutputs({
 | 
			
		||||
      pipeline,
 | 
			
		||||
      currentStageIndex,
 | 
			
		||||
      currentStepIndex,
 | 
			
		||||
      currentTask
 | 
			
		||||
    });
 | 
			
		||||
    const options: any[] = [];
 | 
			
		||||
    for (const step of steps) {
 | 
			
		||||
      const stepDefine = this.get(step.type);
 | 
			
		||||
      for (const key in stepDefine?.output) {
 | 
			
		||||
        options.push({
 | 
			
		||||
          value: `step.${step.id}.${key}`,
 | 
			
		||||
          label: `${stepDefine.output[key].title}【from:${step.title}】`,
 | 
			
		||||
          type: step.type
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return options;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  collectionPreStepOutputs({ pipeline, currentStageIndex, currentStepIndex, currentTask }: any) {
 | 
			
		||||
    const steps: any[] = [];
 | 
			
		||||
    // 开始放step
 | 
			
		||||
    for (let i = 0; i < currentStageIndex; i++) {
 | 
			
		||||
      const stage = pipeline.stages[i];
 | 
			
		||||
      for (const task of stage.tasks) {
 | 
			
		||||
        for (const step of task.steps) {
 | 
			
		||||
          steps.push(step);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    //放当前任务下的step
 | 
			
		||||
    for (let i = 0; i < currentStepIndex; i++) {
 | 
			
		||||
      const step = currentTask.steps[i];
 | 
			
		||||
      steps.push(step);
 | 
			
		||||
    }
 | 
			
		||||
    return steps;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const pluginManager = new PluginManager();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
import type { Pipeline } from "@certd/pipeline";
 | 
			
		||||
import { FormItemProps } from "@fast-crud/fast-crud";
 | 
			
		||||
export type PipelineDetail = {
 | 
			
		||||
  pipeline: Pipeline;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -10,6 +11,110 @@ export type RunHistory = {
 | 
			
		|||
    [id: string]: string[];
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
export type PluginGroup = {
 | 
			
		||||
  key: string;
 | 
			
		||||
  title: string;
 | 
			
		||||
  desc?: string;
 | 
			
		||||
  order: number;
 | 
			
		||||
  plugins: any[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type PluginDefine = {
 | 
			
		||||
  key: string;
 | 
			
		||||
  title: string;
 | 
			
		||||
  desc?: string;
 | 
			
		||||
  input: {
 | 
			
		||||
    [key: string]: FormItemProps;
 | 
			
		||||
  };
 | 
			
		||||
  output: {
 | 
			
		||||
    [key: string]: any;
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export class PluginGroups {
 | 
			
		||||
  groups: { [key: string]: PluginGroup };
 | 
			
		||||
  map: { [key: string]: PluginDefine };
 | 
			
		||||
  constructor(groups: { [key: string]: PluginGroup }) {
 | 
			
		||||
    this.groups = groups;
 | 
			
		||||
    this.initGroup(groups);
 | 
			
		||||
    this.initMap();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private initGroup(groups: { [p: string]: PluginGroup }) {
 | 
			
		||||
    const all: PluginGroup = {
 | 
			
		||||
      key: "all",
 | 
			
		||||
      title: "全部",
 | 
			
		||||
      order: 0,
 | 
			
		||||
      plugins: []
 | 
			
		||||
    };
 | 
			
		||||
    for (const key in groups) {
 | 
			
		||||
      all.plugins.push(...groups[key].plugins);
 | 
			
		||||
    }
 | 
			
		||||
    this.groups = {
 | 
			
		||||
      all,
 | 
			
		||||
      ...groups
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  initMap() {
 | 
			
		||||
    const map: { [key: string]: PluginDefine } = {};
 | 
			
		||||
    for (const key in this.groups) {
 | 
			
		||||
      const group = this.groups[key];
 | 
			
		||||
      for (const plugin of group.plugins) {
 | 
			
		||||
        map[plugin.name] = plugin;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    this.map = map;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getGroups() {
 | 
			
		||||
    return this.groups;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get(name: string) {
 | 
			
		||||
    return this.map[name];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getPreStepOutputOptions({ pipeline, currentStageIndex, currentStepIndex, currentTask }: any) {
 | 
			
		||||
    const steps = this.collectionPreStepOutputs({
 | 
			
		||||
      pipeline,
 | 
			
		||||
      currentStageIndex,
 | 
			
		||||
      currentStepIndex,
 | 
			
		||||
      currentTask
 | 
			
		||||
    });
 | 
			
		||||
    const options: any[] = [];
 | 
			
		||||
    for (const step of steps) {
 | 
			
		||||
      const stepDefine = this.get(step.type);
 | 
			
		||||
      for (const key in stepDefine?.output) {
 | 
			
		||||
        options.push({
 | 
			
		||||
          value: `step.${step.id}.${key}`,
 | 
			
		||||
          label: `${stepDefine.output[key].title}【from:${step.title}】`,
 | 
			
		||||
          type: step.type
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return options;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  collectionPreStepOutputs({ pipeline, currentStageIndex, currentStepIndex, currentTask }: any) {
 | 
			
		||||
    const steps: any[] = [];
 | 
			
		||||
    // 开始放step
 | 
			
		||||
    for (let i = 0; i < currentStageIndex; i++) {
 | 
			
		||||
      const stage = pipeline.stages[i];
 | 
			
		||||
      for (const task of stage.tasks) {
 | 
			
		||||
        for (const step of task.steps) {
 | 
			
		||||
          steps.push(step);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    //放当前任务下的step
 | 
			
		||||
    for (let i = 0; i < currentStepIndex; i++) {
 | 
			
		||||
      const step = currentTask.steps[i];
 | 
			
		||||
      steps.push(step);
 | 
			
		||||
    }
 | 
			
		||||
    return steps;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type PipelineOptions = {
 | 
			
		||||
  doTrigger(options: { pipelineId: number }): Promise<void>;
 | 
			
		||||
| 
						 | 
				
			
			@ -17,5 +122,5 @@ export type PipelineOptions = {
 | 
			
		|||
  getPipelineDetail(query: { pipelineId: number }): Promise<PipelineDetail>;
 | 
			
		||||
  getHistoryList(query: { pipelineId: number }): Promise<RunHistory[]>;
 | 
			
		||||
  getHistoryDetail(query: { historyId: number }): Promise<RunHistory>;
 | 
			
		||||
  getPlugins(): Promise<Pipeline[]>;
 | 
			
		||||
  getPluginGroups(): Promise<PluginGroups>;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,2 +1,3 @@
 | 
			
		|||
koa:
 | 
			
		||||
  port: 7001
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,6 +14,7 @@
 | 
			
		|||
    "lint:fix": "mwts fix",
 | 
			
		||||
    "ci": "npm run cov",
 | 
			
		||||
    "build": "mwtsc --cleanOutDir --skipLibCheck",
 | 
			
		||||
    "build-on-docker": "node ./before-build.js && npm run build",
 | 
			
		||||
    "up-mw-deps": "npx midway-version -u -w"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,7 +37,9 @@ const development = {
 | 
			
		|||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  cron: {},
 | 
			
		||||
  cron: {
 | 
			
		||||
    immediateTriggerOnce: false,
 | 
			
		||||
  },
 | 
			
		||||
  /**
 | 
			
		||||
   * 演示环境
 | 
			
		||||
   */
 | 
			
		||||
| 
						 | 
				
			
			@ -84,6 +86,8 @@ const development = {
 | 
			
		|||
    resetAdminPasswd: false,
 | 
			
		||||
  },
 | 
			
		||||
} as MidwayConfig;
 | 
			
		||||
mergeConfig(development, 'development');
 | 
			
		||||
 | 
			
		||||
mergeConfig(development, env);
 | 
			
		||||
 | 
			
		||||
export default development;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,9 @@ const production = {
 | 
			
		|||
  preview: {
 | 
			
		||||
    enabled: false,
 | 
			
		||||
  },
 | 
			
		||||
  cron: {
 | 
			
		||||
    immediateTriggerOnce: true,
 | 
			
		||||
  },
 | 
			
		||||
} as MidwayConfig;
 | 
			
		||||
 | 
			
		||||
mergeConfig(production, 'production');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
import { Autoload, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core';
 | 
			
		||||
import { Autoload, Config, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core';
 | 
			
		||||
import { PipelineService } from '../service/pipeline-service.js';
 | 
			
		||||
import { logger } from '../../../utils/logger.js';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -10,11 +10,13 @@ export class AutoRegisterCron {
 | 
			
		|||
 | 
			
		||||
  // @Inject()
 | 
			
		||||
  // echoPlugin: EchoPlugin;
 | 
			
		||||
  @Config('cron.immediateTriggerOnce')
 | 
			
		||||
  private immediateTriggerOnce = false;
 | 
			
		||||
 | 
			
		||||
  @Init()
 | 
			
		||||
  async init() {
 | 
			
		||||
    logger.info('加载定时trigger开始');
 | 
			
		||||
    await this.pipelineService.onStartup();
 | 
			
		||||
    await this.pipelineService.onStartup(this.immediateTriggerOnce);
 | 
			
		||||
    // logger.info(this.echoPlugin, this.echoPlugin.test);
 | 
			
		||||
    // logger.info('加载定时trigger完成');
 | 
			
		||||
    //
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,4 @@
 | 
			
		|||
import {
 | 
			
		||||
  ALL,
 | 
			
		||||
  Controller,
 | 
			
		||||
  Inject,
 | 
			
		||||
  Post,
 | 
			
		||||
  Provide,
 | 
			
		||||
  Query,
 | 
			
		||||
} from '@midwayjs/core';
 | 
			
		||||
import { ALL, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
 | 
			
		||||
import { BaseController } from '../../../basic/base-controller.js';
 | 
			
		||||
import { PluginService } from '../service/plugin-service.js';
 | 
			
		||||
import { Constants } from '../../../basic/constants.js';
 | 
			
		||||
| 
						 | 
				
			
			@ -25,4 +18,11 @@ export class PluginController extends BaseController {
 | 
			
		|||
    const list = this.service.getList();
 | 
			
		||||
    return this.ok(list);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Post('/groups', { summary: Constants.per.authOnly })
 | 
			
		||||
  async groups(@Query(ALL) query) {
 | 
			
		||||
    query.userId = this.ctx.user.id;
 | 
			
		||||
    const group = this.service.getGroups();
 | 
			
		||||
    return this.ok(group);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
import { Config, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
 | 
			
		||||
import { Config, Inject, Provide, Scope, ScopeEnum, sleep } from '@midwayjs/core';
 | 
			
		||||
import { InjectEntityModel } from '@midwayjs/typeorm';
 | 
			
		||||
import { In, Repository } from 'typeorm';
 | 
			
		||||
import { BaseService } from '../../../basic/base-service.js';
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +80,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
 | 
			
		|||
  /**
 | 
			
		||||
   * 应用启动后初始加载记录
 | 
			
		||||
   */
 | 
			
		||||
  async onStartup() {
 | 
			
		||||
  async onStartup(immediateTriggerOnce: boolean) {
 | 
			
		||||
    logger.info('加载定时trigger开始');
 | 
			
		||||
    const idEntityList = await this.repository.find({
 | 
			
		||||
      select: {
 | 
			
		||||
| 
						 | 
				
			
			@ -114,7 +114,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
 | 
			
		|||
      for (const entity of list) {
 | 
			
		||||
        const pipeline = JSON.parse(entity.content ?? '{}');
 | 
			
		||||
        try {
 | 
			
		||||
          this.registerTriggers(pipeline);
 | 
			
		||||
          await this.registerTriggers(pipeline, immediateTriggerOnce);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          logger.error('加载定时trigger失败:', e);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -123,13 +123,18 @@ export class PipelineService extends BaseService<PipelineEntity> {
 | 
			
		|||
    logger.info('定时器数量:', this.cron.getListSize());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  registerTriggers(pipeline?: Pipeline) {
 | 
			
		||||
  async registerTriggers(pipeline?: Pipeline, immediateTriggerOnce = false) {
 | 
			
		||||
    if (pipeline?.triggers == null) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    for (const trigger of pipeline.triggers) {
 | 
			
		||||
      this.registerCron(pipeline.id, trigger);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (immediateTriggerOnce) {
 | 
			
		||||
      await this.trigger(pipeline.id);
 | 
			
		||||
      await sleep(1000);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async trigger(id) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
 | 
			
		||||
import { pluginRegistry } from '@certd/pipeline';
 | 
			
		||||
import { pluginGroups, pluginRegistry } from '@certd/pipeline';
 | 
			
		||||
@Provide()
 | 
			
		||||
@Scope(ScopeEnum.Singleton)
 | 
			
		||||
export class PluginService {
 | 
			
		||||
| 
						 | 
				
			
			@ -12,4 +12,8 @@ export class PluginService {
 | 
			
		|||
    }
 | 
			
		||||
    return list;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getGroups() {
 | 
			
		||||
    return pluginGroups;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput, utils } from '@certd/pipeline';
 | 
			
		||||
import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, utils } from '@certd/pipeline';
 | 
			
		||||
// @ts-ignore
 | 
			
		||||
import { ROAClient } from '@alicloud/pop-core';
 | 
			
		||||
import { AliyunAccess } from '../../access/index.js';
 | 
			
		||||
| 
						 | 
				
			
			@ -9,6 +9,7 @@ import { CertInfo } from '@certd/plugin-cert';
 | 
			
		|||
@IsTaskPlugin({
 | 
			
		||||
  name: 'DeployCertToAliyunAckIngress',
 | 
			
		||||
  title: '部署到阿里云AckIngress',
 | 
			
		||||
  group: pluginGroups.aliyun.key,
 | 
			
		||||
  input: {},
 | 
			
		||||
  output: {},
 | 
			
		||||
  default: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@ import {
 | 
			
		|||
  AbstractTaskPlugin,
 | 
			
		||||
  IAccessService,
 | 
			
		||||
  ILogger,
 | 
			
		||||
  IsTaskPlugin,
 | 
			
		||||
  IsTaskPlugin, pluginGroups,
 | 
			
		||||
  RunStrategy,
 | 
			
		||||
  TaskInput,
 | 
			
		||||
} from '@certd/pipeline';
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +14,7 @@ import { AliyunAccess } from '../../access/index.js';
 | 
			
		|||
@IsTaskPlugin({
 | 
			
		||||
  name: 'DeployCertToAliyunCDN',
 | 
			
		||||
  title: '部署证书至阿里云CDN',
 | 
			
		||||
  group: pluginGroups.aliyun.key,
 | 
			
		||||
  desc: '依赖证书申请前置任务,自动部署域名证书至阿里云CDN',
 | 
			
		||||
  default: {
 | 
			
		||||
    strategy: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,4 @@
 | 
			
		|||
import {
 | 
			
		||||
  AbstractTaskPlugin,
 | 
			
		||||
  IAccessService,
 | 
			
		||||
  IsTaskPlugin,
 | 
			
		||||
  RunStrategy,
 | 
			
		||||
  TaskInput,
 | 
			
		||||
  TaskOutput,
 | 
			
		||||
} from '@certd/pipeline';
 | 
			
		||||
import { AbstractTaskPlugin, IAccessService, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskOutput } from '@certd/pipeline';
 | 
			
		||||
import Core from '@alicloud/pop-core';
 | 
			
		||||
import { AliyunAccess } from '../../access/index.js';
 | 
			
		||||
import { appendTimeSuffix, checkRet, ZoneOptions } from '../../utils/index.js';
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +7,7 @@ import { Logger } from 'log4js';
 | 
			
		|||
@IsTaskPlugin({
 | 
			
		||||
  name: 'uploadCertToAliyun',
 | 
			
		||||
  title: '上传证书到阿里云',
 | 
			
		||||
  group: pluginGroups.aliyun.key,
 | 
			
		||||
  desc: '',
 | 
			
		||||
  default: {
 | 
			
		||||
    strategy: {
 | 
			
		||||
| 
						 | 
				
			
			@ -76,9 +70,7 @@ export class UploadCertToAliyun extends AbstractTaskPlugin {
 | 
			
		|||
 | 
			
		||||
  async execute(): Promise<void> {
 | 
			
		||||
    console.log('开始部署证书到阿里云cdn');
 | 
			
		||||
    const access = (await this.accessService.getById(
 | 
			
		||||
      this.accessId
 | 
			
		||||
    )) as AliyunAccess;
 | 
			
		||||
    const access = (await this.accessService.getById(this.accessId)) as AliyunAccess;
 | 
			
		||||
    const client = this.getClient(access);
 | 
			
		||||
    const certName = appendTimeSuffix(this.name);
 | 
			
		||||
    const params = {
 | 
			
		||||
| 
						 | 
				
			
			@ -92,11 +84,7 @@ export class UploadCertToAliyun extends AbstractTaskPlugin {
 | 
			
		|||
      method: 'POST',
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const ret = (await client.request(
 | 
			
		||||
      'CreateUserCertificate',
 | 
			
		||||
      params,
 | 
			
		||||
      requestOption
 | 
			
		||||
    )) as any;
 | 
			
		||||
    const ret = (await client.request('CreateUserCertificate', params, requestOption)) as any;
 | 
			
		||||
    checkRet(ret);
 | 
			
		||||
    this.logger.info('证书上传成功:aliyunCertId=', ret.CertId);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,16 +1,10 @@
 | 
			
		|||
import {
 | 
			
		||||
  AbstractTaskPlugin,
 | 
			
		||||
  IAccessService,
 | 
			
		||||
  ILogger,
 | 
			
		||||
  IsTaskPlugin,
 | 
			
		||||
  RunStrategy,
 | 
			
		||||
  TaskInput,
 | 
			
		||||
} from '@certd/pipeline';
 | 
			
		||||
import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
 | 
			
		||||
import { CertInfo, CertReader } from '@certd/plugin-cert';
 | 
			
		||||
 | 
			
		||||
@IsTaskPlugin({
 | 
			
		||||
  name: 'CloudflareDeployToCDN',
 | 
			
		||||
  title: '部署证书到CF CDN',
 | 
			
		||||
  group: pluginGroups.other.key,
 | 
			
		||||
  desc: '暂未实现,不可用',
 | 
			
		||||
  default: {
 | 
			
		||||
    strategy: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,11 @@
 | 
			
		|||
import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput } from '@certd/pipeline';
 | 
			
		||||
import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
 | 
			
		||||
import { CertInfo, CertReader } from '@certd/plugin-cert';
 | 
			
		||||
import { K8sClient } from '@certd/lib-k8s';
 | 
			
		||||
 | 
			
		||||
@IsTaskPlugin({
 | 
			
		||||
  name: 'demoTest',
 | 
			
		||||
  title: 'Demo测试插件',
 | 
			
		||||
  group: pluginGroups.other.key,
 | 
			
		||||
  default: {
 | 
			
		||||
    strategy: {
 | 
			
		||||
      runStrategy: RunStrategy.SkipWhenSucceed,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,16 +1,10 @@
 | 
			
		|||
import {
 | 
			
		||||
  AbstractTaskPlugin,
 | 
			
		||||
  IAccessService,
 | 
			
		||||
  ILogger,
 | 
			
		||||
  IsTaskPlugin,
 | 
			
		||||
  RunStrategy,
 | 
			
		||||
  TaskInput,
 | 
			
		||||
} from '@certd/pipeline';
 | 
			
		||||
import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
 | 
			
		||||
import { SshClient } from '../../lib/ssh.js';
 | 
			
		||||
 | 
			
		||||
@IsTaskPlugin({
 | 
			
		||||
  name: 'hostShellExecute',
 | 
			
		||||
  title: '执行远程主机脚本命令',
 | 
			
		||||
  group: pluginGroups.host.key,
 | 
			
		||||
  input: {},
 | 
			
		||||
  default: {
 | 
			
		||||
    strategy: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput, TaskOutput } from '@certd/pipeline';
 | 
			
		||||
import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskOutput } from '@certd/pipeline';
 | 
			
		||||
import { SshClient } from '../../lib/ssh.js';
 | 
			
		||||
import { CertInfo, CertReader } from '@certd/plugin-cert';
 | 
			
		||||
import * as fs from 'fs';
 | 
			
		||||
| 
						 | 
				
			
			@ -7,6 +7,7 @@ import { SshAccess } from '../../access/index.js';
 | 
			
		|||
@IsTaskPlugin({
 | 
			
		||||
  name: 'uploadCertToHost',
 | 
			
		||||
  title: '上传证书到主机',
 | 
			
		||||
  group: pluginGroups.host.key,
 | 
			
		||||
  desc: '也支持复制证书到本机',
 | 
			
		||||
  default: {
 | 
			
		||||
    strategy: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,4 @@
 | 
			
		|||
import {
 | 
			
		||||
  AbstractTaskPlugin,
 | 
			
		||||
  IAccessService,
 | 
			
		||||
  ILogger,
 | 
			
		||||
  IsTaskPlugin,
 | 
			
		||||
  RunStrategy,
 | 
			
		||||
  TaskInput,
 | 
			
		||||
} from '@certd/pipeline';
 | 
			
		||||
import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
 | 
			
		||||
import tencentcloud from 'tencentcloud-sdk-nodejs';
 | 
			
		||||
import { TencentAccess } from '../../access/index.js';
 | 
			
		||||
import { CertInfo } from '@certd/plugin-cert';
 | 
			
		||||
| 
						 | 
				
			
			@ -13,6 +6,7 @@ import { CertInfo } from '@certd/plugin-cert';
 | 
			
		|||
@IsTaskPlugin({
 | 
			
		||||
  name: 'DeployCertToTencentCDN',
 | 
			
		||||
  title: '部署到腾讯云CDN',
 | 
			
		||||
  group: pluginGroups.tencent.key,
 | 
			
		||||
  default: {
 | 
			
		||||
    strategy: {
 | 
			
		||||
      runStrategy: RunStrategy.SkipWhenSucceed,
 | 
			
		||||
| 
						 | 
				
			
			@ -75,9 +69,7 @@ export class DeployToCdnPlugin extends AbstractTaskPlugin {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  async execute(): Promise<void> {
 | 
			
		||||
    const accessProvider: TencentAccess = (await this.accessService.getById(
 | 
			
		||||
      this.accessId
 | 
			
		||||
    )) as TencentAccess;
 | 
			
		||||
    const accessProvider: TencentAccess = (await this.accessService.getById(this.accessId)) as TencentAccess;
 | 
			
		||||
    const client = this.getClient(accessProvider);
 | 
			
		||||
    const params = this.buildParams();
 | 
			
		||||
    await this.doRequest(client, params);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,4 @@
 | 
			
		|||
import {
 | 
			
		||||
  AbstractTaskPlugin,
 | 
			
		||||
  IAccessService,
 | 
			
		||||
  ILogger,
 | 
			
		||||
  IsTaskPlugin,
 | 
			
		||||
  RunStrategy,
 | 
			
		||||
  TaskInput,
 | 
			
		||||
  utils,
 | 
			
		||||
} from '@certd/pipeline';
 | 
			
		||||
import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, utils } from '@certd/pipeline';
 | 
			
		||||
import tencentcloud from 'tencentcloud-sdk-nodejs';
 | 
			
		||||
import { TencentAccess } from '../../access/index.js';
 | 
			
		||||
import dayjs from 'dayjs';
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +6,7 @@ import dayjs from 'dayjs';
 | 
			
		|||
@IsTaskPlugin({
 | 
			
		||||
  name: 'DeployCertToTencentCLB',
 | 
			
		||||
  title: '部署到腾讯云CLB',
 | 
			
		||||
  group: pluginGroups.tencent.key,
 | 
			
		||||
  desc: '暂时只支持单向认证证书,暂时只支持通用负载均衡',
 | 
			
		||||
  default: {
 | 
			
		||||
    strategy: {
 | 
			
		||||
| 
						 | 
				
			
			@ -40,8 +33,7 @@ export class DeployToClbPlugin extends AbstractTaskPlugin {
 | 
			
		|||
 | 
			
		||||
  @TaskInput({
 | 
			
		||||
    title: '负载均衡ID',
 | 
			
		||||
    helper:
 | 
			
		||||
      '如果没有配置,则根据域名匹配负载均衡下的监听器(根据域名匹配时暂时只支持前100个)',
 | 
			
		||||
    helper: '如果没有配置,则根据域名匹配负载均衡下的监听器(根据域名匹配时暂时只支持前100个)',
 | 
			
		||||
    required: true,
 | 
			
		||||
  })
 | 
			
		||||
  loadBalancerId!: string;
 | 
			
		||||
| 
						 | 
				
			
			@ -88,9 +80,7 @@ export class DeployToClbPlugin extends AbstractTaskPlugin {
 | 
			
		|||
    this.logger = this.ctx.logger;
 | 
			
		||||
  }
 | 
			
		||||
  async execute(): Promise<void> {
 | 
			
		||||
    const accessProvider = (await this.accessService.getById(
 | 
			
		||||
      this.accessId
 | 
			
		||||
    )) as TencentAccess;
 | 
			
		||||
    const accessProvider = (await this.accessService.getById(this.accessId)) as TencentAccess;
 | 
			
		||||
    const client = this.getClient(accessProvider, this.region);
 | 
			
		||||
 | 
			
		||||
    const lastCertId = await this.getCertIdFromProps(client);
 | 
			
		||||
| 
						 | 
				
			
			@ -103,10 +93,7 @@ export class DeployToClbPlugin extends AbstractTaskPlugin {
 | 
			
		|||
    try {
 | 
			
		||||
      await utils.sleep(2000);
 | 
			
		||||
      let newCertId = await this.getCertIdFromProps(client);
 | 
			
		||||
      if (
 | 
			
		||||
        (lastCertId && newCertId === lastCertId) ||
 | 
			
		||||
        (!lastCertId && !newCertId)
 | 
			
		||||
      ) {
 | 
			
		||||
      if ((lastCertId && newCertId === lastCertId) || (!lastCertId && !newCertId)) {
 | 
			
		||||
        await utils.sleep(2000);
 | 
			
		||||
        newCertId = await this.getCertIdFromProps(client);
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -121,11 +108,7 @@ export class DeployToClbPlugin extends AbstractTaskPlugin {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  async getCertIdFromProps(client: any) {
 | 
			
		||||
    const listenerRet = await this.getListenerList(
 | 
			
		||||
      client,
 | 
			
		||||
      this.loadBalancerId,
 | 
			
		||||
      [this.listenerId]
 | 
			
		||||
    );
 | 
			
		||||
    const listenerRet = await this.getListenerList(client, this.loadBalancerId, [this.listenerId]);
 | 
			
		||||
    return this.getCertIdFromListener(listenerRet[0], this.domain);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -152,14 +135,7 @@ export class DeployToClbPlugin extends AbstractTaskPlugin {
 | 
			
		|||
    const params = this.buildProps();
 | 
			
		||||
    const ret = await client.ModifyListener(params);
 | 
			
		||||
    this.checkRet(ret);
 | 
			
		||||
    this.logger.info(
 | 
			
		||||
      '设置腾讯云CLB证书成功:',
 | 
			
		||||
      ret.RequestId,
 | 
			
		||||
      '->loadBalancerId:',
 | 
			
		||||
      this.loadBalancerId,
 | 
			
		||||
      'listenerId',
 | 
			
		||||
      this.listenerId
 | 
			
		||||
    );
 | 
			
		||||
    this.logger.info('设置腾讯云CLB证书成功:', ret.RequestId, '->loadBalancerId:', this.loadBalancerId, 'listenerId', this.listenerId);
 | 
			
		||||
    return ret;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,4 @@
 | 
			
		|||
import {
 | 
			
		||||
  AbstractTaskPlugin,
 | 
			
		||||
  IAccessService,
 | 
			
		||||
  IsTaskPlugin,
 | 
			
		||||
  RunStrategy,
 | 
			
		||||
  TaskInput,
 | 
			
		||||
  utils,
 | 
			
		||||
} from '@certd/pipeline';
 | 
			
		||||
import { AbstractTaskPlugin, IAccessService, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, utils } from '@certd/pipeline';
 | 
			
		||||
import tencentcloud from 'tencentcloud-sdk-nodejs';
 | 
			
		||||
import { K8sClient } from '@certd/lib-k8s';
 | 
			
		||||
import dayjs from 'dayjs';
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +7,7 @@ import { Logger } from 'log4js';
 | 
			
		|||
@IsTaskPlugin({
 | 
			
		||||
  name: 'DeployCertToTencentTKEIngress',
 | 
			
		||||
  title: '部署到腾讯云TKE-ingress',
 | 
			
		||||
  group: pluginGroups.tencent.key,
 | 
			
		||||
  desc: '需要【上传到腾讯云】作为前置任务',
 | 
			
		||||
  default: {
 | 
			
		||||
    strategy: {
 | 
			
		||||
| 
						 | 
				
			
			@ -104,10 +98,7 @@ export class DeployCertToTencentTKEIngressPlugin extends AbstractTaskPlugin {
 | 
			
		|||
  async execute(): Promise<void> {
 | 
			
		||||
    const accessProvider = await this.accessService.getById(this.accessId);
 | 
			
		||||
    const tkeClient = this.getTkeClient(accessProvider, this.region);
 | 
			
		||||
    const kubeConfigStr = await this.getTkeKubeConfig(
 | 
			
		||||
      tkeClient,
 | 
			
		||||
      this.clusterId
 | 
			
		||||
    );
 | 
			
		||||
    const kubeConfigStr = await this.getTkeKubeConfig(tkeClient, this.clusterId);
 | 
			
		||||
 | 
			
		||||
    this.logger.info('kubeconfig已成功获取');
 | 
			
		||||
    const k8sClient = new K8sClient(kubeConfigStr);
 | 
			
		||||
| 
						 | 
				
			
			@ -154,9 +145,7 @@ export class DeployCertToTencentTKEIngressPlugin extends AbstractTaskPlugin {
 | 
			
		|||
    };
 | 
			
		||||
    const ret = await client.DescribeClusterKubeconfig(params);
 | 
			
		||||
    this.checkRet(ret);
 | 
			
		||||
    this.logger.info(
 | 
			
		||||
      '注意:后续操作需要在【集群->基本信息】中开启外网或内网访问,https://console.cloud.tencent.com/tke2/cluster'
 | 
			
		||||
    );
 | 
			
		||||
    this.logger.info('注意:后续操作需要在【集群->基本信息】中开启外网或内网访问,https://console.cloud.tencent.com/tke2/cluster');
 | 
			
		||||
    return ret.Kubeconfig;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput, TaskOutput } from '@certd/pipeline';
 | 
			
		||||
import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskOutput } from '@certd/pipeline';
 | 
			
		||||
import tencentcloud from 'tencentcloud-sdk-nodejs';
 | 
			
		||||
import dayjs from 'dayjs';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -6,6 +6,7 @@ import dayjs from 'dayjs';
 | 
			
		|||
  name: 'UploadCertToTencent',
 | 
			
		||||
  title: '上传证书到腾讯云',
 | 
			
		||||
  desc: '上传成功后输出:tencentCertId',
 | 
			
		||||
  group: pluginGroups.tencent.key,
 | 
			
		||||
  default: {
 | 
			
		||||
    strategy: {
 | 
			
		||||
      runStrategy: RunStrategy.SkipWhenSucceed,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue