mirror of https://github.com/certd/certd
feat: midway注解方式编写插件
parent
717d203622
commit
e4ec4e1404
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||||
|
"useWorkspaces": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"npmClient": "yarn"
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"lerna": "^3.18.4"
|
"lerna": "^6.3.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "lerna bootstrap --hoist",
|
"start": "lerna bootstrap --hoist",
|
||||||
|
@ -11,6 +11,9 @@
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash-es": "^4.17.20"
|
"lodash-es": "^4.17.21"
|
||||||
}
|
},
|
||||||
|
"workspaces": [
|
||||||
|
"packages/**"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 1c0e55a5339ef0ac92f96726c4c61e6918908730
|
Subproject commit 138c736ce6468e208a1055fd6289b8c2c8361306
|
|
@ -1,7 +1,5 @@
|
||||||
import { Registrable } from "../registry";
|
import { Registrable } from "../registry";
|
||||||
import { accessRegistry } from "./registry";
|
|
||||||
import { FormItemProps } from "../d.ts";
|
import { FormItemProps } from "../d.ts";
|
||||||
import { AbstractAccess } from "./abstract-access";
|
|
||||||
|
|
||||||
export type AccessInputDefine = FormItemProps & {
|
export type AccessInputDefine = FormItemProps & {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -13,5 +11,5 @@ export type AccessDefine = Registrable & {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export interface IAccessService {
|
export interface IAccessService {
|
||||||
getById(id: any): Promise<AbstractAccess>;
|
getById(id: any): Promise<any>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
saveModule,
|
saveModule,
|
||||||
} from "@midwayjs/decorator";
|
} from "@midwayjs/decorator";
|
||||||
import { AccessDefine, AccessInputDefine } from "./api";
|
import { AccessDefine, AccessInputDefine } from "./api";
|
||||||
import _ from "lodash-es";
|
import _ from "lodash";
|
||||||
import { accessRegistry } from "./registry";
|
import { accessRegistry } from "./registry";
|
||||||
|
|
||||||
// 提供一个唯一 key
|
// 提供一个唯一 key
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import { ConcurrencyStrategy, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../d.ts";
|
import { ConcurrencyStrategy, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../d.ts";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { RunHistory } from "./run-history";
|
import { RunHistory } from "./run-history";
|
||||||
import { pluginRegistry, ITaskPlugin, PluginDefine } from "../plugin";
|
import { PluginDefine, pluginRegistry } from "../plugin";
|
||||||
import { ContextFactory, IContext } from "./context";
|
import { ContextFactory, IContext } from "./context";
|
||||||
import { IStorage } from "./storage";
|
import { IStorage } from "./storage";
|
||||||
import { logger } from "../utils/util.log";
|
import { logger } from "../utils/util.log";
|
||||||
import { Logger } from "log4js";
|
import { Logger } from "log4js";
|
||||||
import { request } from "../utils/util.request";
|
import { request } from "../utils/util.request";
|
||||||
import { IAccessService } from "../access";
|
import { IAccessService } from "../access";
|
||||||
import { Registrable, RegistryItem } from "../registry";
|
import { RegistryItem } from "../registry";
|
||||||
|
|
||||||
export class Executor {
|
export class Executor {
|
||||||
userId: any;
|
userId: any;
|
||||||
pipeline: Pipeline;
|
pipeline: Pipeline;
|
||||||
|
@ -136,7 +137,7 @@ export class Executor {
|
||||||
private async runStep(step: Step) {
|
private async runStep(step: Step) {
|
||||||
//执行任务
|
//执行任务
|
||||||
const plugin: RegistryItem = pluginRegistry.get(step.type);
|
const plugin: RegistryItem = pluginRegistry.get(step.type);
|
||||||
const context = {
|
const context: any = {
|
||||||
logger: this.runtime.loggers[step.id],
|
logger: this.runtime.loggers[step.id],
|
||||||
accessService: this.accessService,
|
accessService: this.accessService,
|
||||||
pipelineContext: this.pipelineContext,
|
pipelineContext: this.pipelineContext,
|
||||||
|
@ -159,7 +160,9 @@ export class Executor {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_.forEach(define.autowire, (item, key: string) => {
|
||||||
|
instance[key] = context[key];
|
||||||
|
});
|
||||||
|
|
||||||
const res = await instance.execute();
|
const res = await instance.execute();
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,5 @@
|
||||||
// src/decorator/memoryCache.decorator.ts
|
// src/decorator/memoryCache.decorator.ts
|
||||||
import {
|
import { getClassMetadata, listModule, Provide, saveClassMetadata, saveModule, Scope, ScopeEnum } from "@midwayjs/decorator";
|
||||||
attachClassMetadata,
|
|
||||||
attachPropertyDataToClass,
|
|
||||||
createCustomPropertyDecorator,
|
|
||||||
getClassMetadata,
|
|
||||||
listModule,
|
|
||||||
listPropertyDataFromClass,
|
|
||||||
Provide,
|
|
||||||
saveClassMetadata,
|
|
||||||
saveModule,
|
|
||||||
Scope,
|
|
||||||
ScopeEnum,
|
|
||||||
} from "@midwayjs/decorator";
|
|
||||||
import _ from "lodash-es";
|
|
||||||
import { dnsProviderRegistry } from "./registry";
|
import { dnsProviderRegistry } from "./registry";
|
||||||
import { DnsProviderDefine } from "./api";
|
import { DnsProviderDefine } from "./api";
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,5 @@ export * from "./registry";
|
||||||
export * from "./dns-provider";
|
export * from "./dns-provider";
|
||||||
export * from "./plugin";
|
export * from "./plugin";
|
||||||
export * from "./utils";
|
export * from "./utils";
|
||||||
export * from "./decorator";
|
|
||||||
export * from "./midway";
|
export * from "./midway";
|
||||||
export * from "./context";
|
export * from "./context";
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { Config, Configuration, getClassMetadata, Inject, listModule, listPropertyDataFromClass, Logger } from "@midwayjs/decorator";
|
import { Config, Configuration, Inject, Logger } from "@midwayjs/decorator";
|
||||||
import _ from "lodash-es";
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { ILogger } from "@midwayjs/logger";
|
import { ILogger } from "@midwayjs/logger";
|
||||||
import { IMidwayContainer, MidwayDecoratorService } from "@midwayjs/core";
|
import { IMidwayContainer, MidwayDecoratorService } from "@midwayjs/core";
|
||||||
import { pluginRegistry } from "../plugin";
|
|
||||||
import { registerPlugins } from "../plugin/decorator";
|
import { registerPlugins } from "../plugin/decorator";
|
||||||
import { registerAccess } from "../access/decorator";
|
import { registerAccess } from "../access/decorator";
|
||||||
import { registerDnsProviders } from "../dns-provider";
|
import { registerDnsProviders } from "../dns-provider";
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Registrable } from "../registry";
|
import { Registrable } from "../registry";
|
||||||
import { pluginRegistry } from "./registry";
|
|
||||||
import { FormItemProps } from "../d.ts";
|
import { FormItemProps } from "../d.ts";
|
||||||
|
|
||||||
export enum ContextScope {
|
export enum ContextScope {
|
||||||
|
@ -33,6 +32,7 @@ export type PluginDefine = Registrable & {
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ITaskPlugin {
|
export interface ITaskPlugin {
|
||||||
|
onInit(): Promise<void>;
|
||||||
execute(): Promise<void>;
|
execute(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,13 @@
|
||||||
// src/decorator/memoryCache.decorator.ts
|
|
||||||
import {
|
import {
|
||||||
attachClassMetadata,
|
attachClassMetadata,
|
||||||
attachPropertyDataToClass,
|
attachPropertyDataToClass,
|
||||||
createCustomPropertyDecorator,
|
|
||||||
getClassMetadata,
|
getClassMetadata,
|
||||||
listModule,
|
listModule,
|
||||||
listPropertyDataFromClass,
|
listPropertyDataFromClass,
|
||||||
Provide,
|
|
||||||
saveClassMetadata,
|
saveClassMetadata,
|
||||||
saveModule,
|
saveModule,
|
||||||
Scope,
|
|
||||||
ScopeEnum,
|
|
||||||
} from "@midwayjs/decorator";
|
} from "@midwayjs/decorator";
|
||||||
import _ from "lodash-es";
|
import _ from "lodash";
|
||||||
import { pluginRegistry } from "./registry";
|
import { pluginRegistry } from "./registry";
|
||||||
import { PluginDefine, TaskInputDefine, TaskOutputDefine } from "./api";
|
import { PluginDefine, TaskInputDefine, TaskOutputDefine } from "./api";
|
||||||
|
|
||||||
|
@ -33,11 +28,11 @@ export function IsTaskPlugin(define: PluginDefine): ClassDecorator {
|
||||||
},
|
},
|
||||||
target
|
target
|
||||||
);
|
);
|
||||||
// 指定 IoC 容器创建实例的作用域,这里注册为请求作用域,这样能取到 ctx
|
// // 指定 IoC 容器创建实例的作用域,这里注册为请求作用域,这样能取到 ctx
|
||||||
Scope(ScopeEnum.Prototype)(target);
|
// Scope(ScopeEnum.Prototype)(target);
|
||||||
|
|
||||||
// 调用一下 Provide 装饰器,这样用户的 class 可以省略写 @Provide() 装饰器了
|
// 调用一下 Provide 装饰器,这样用户的 class 可以省略写 @Provide() 装饰器了
|
||||||
Provide()(target);
|
// Provide()(target);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import log4js, { LoggingEvent } from "log4js";
|
import log4js, { LoggingEvent, Logger } from "log4js";
|
||||||
|
|
||||||
const OutputAppender = {
|
const OutputAppender = {
|
||||||
configure: (config: any, layouts: any, findAppender: any, levels: any) => {
|
configure: (config: any, layouts: any, findAppender: any, levels: any) => {
|
||||||
|
@ -32,3 +32,4 @@ export function buildLogger(write: (text: string) => void) {
|
||||||
});
|
});
|
||||||
return logger;
|
return logger;
|
||||||
}
|
}
|
||||||
|
export type LOGGER = Logger;
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
"@certd/plugin-util": "^0.3.0"
|
"@certd/plugin-util": "^0.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"log4js": "^6.7.1",
|
||||||
"@types/lodash": "^4.14.186",
|
"@types/lodash": "^4.14.186",
|
||||||
"vue-tsc": "^0.38.9",
|
"vue-tsc": "^0.38.9",
|
||||||
"@alicloud/cs20151215": "^3.0.3",
|
"@alicloud/cs20151215": "^3.0.3",
|
||||||
|
@ -40,7 +41,6 @@
|
||||||
"eslint-plugin-import": "^2.26.0",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"log4js": "^6.3.0",
|
|
||||||
"mocha": "^10.1.0",
|
"mocha": "^10.1.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.8.4",
|
"typescript": "^4.8.4",
|
||||||
|
|
|
@ -1,27 +1,25 @@
|
||||||
import { AbstractAccess, IsAccess } from "@certd/pipeline";
|
import { IsAccess, IsAccessInput } from "@certd/pipeline";
|
||||||
|
|
||||||
@IsAccess({
|
@IsAccess({
|
||||||
name: "aliyun",
|
name: "aliyun",
|
||||||
title: "阿里云授权",
|
title: "阿里云授权",
|
||||||
desc: "",
|
desc: "",
|
||||||
input: {
|
|
||||||
accessKeyId: {
|
|
||||||
title: "accessKeyId",
|
|
||||||
component: {
|
|
||||||
placeholder: "accessKeyId",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
accessKeySecret: {
|
|
||||||
title: "accessKeySecret",
|
|
||||||
component: {
|
|
||||||
placeholder: "accessKeySecret",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
export class AliyunAccess extends AbstractAccess {
|
export class AliyunAccess {
|
||||||
|
@IsAccessInput({
|
||||||
|
title: "accessKeyId",
|
||||||
|
component: {
|
||||||
|
placeholder: "accessKeyId",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
accessKeyId = "";
|
accessKeyId = "";
|
||||||
|
@IsAccessInput({
|
||||||
|
title: "accessKeySecret",
|
||||||
|
component: {
|
||||||
|
placeholder: "accessKeySecret",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
accessKeySecret = "";
|
accessKeySecret = "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import Core from "@alicloud/pop-core";
|
import Core from "@alicloud/pop-core";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { AbstractDnsProvider, CreateRecordOptions, IDnsProvider, IsDnsProvider, RemoveRecordOptions } from "@certd/pipeline";
|
import { CreateRecordOptions, IDnsProvider, IsDnsProvider, RemoveRecordOptions } from "@certd/pipeline";
|
||||||
|
import { Logger } from "log4js";
|
||||||
|
|
||||||
@IsDnsProvider({
|
@IsDnsProvider({
|
||||||
name: "aliyun",
|
name: "aliyun",
|
||||||
|
@ -8,11 +9,10 @@ import { AbstractDnsProvider, CreateRecordOptions, IDnsProvider, IsDnsProvider,
|
||||||
desc: "阿里云DNS解析提供商",
|
desc: "阿里云DNS解析提供商",
|
||||||
accessType: "aliyun",
|
accessType: "aliyun",
|
||||||
})
|
})
|
||||||
export class AliyunDnsProvider extends AbstractDnsProvider implements IDnsProvider {
|
export class AliyunDnsProvider implements IDnsProvider {
|
||||||
client: any;
|
client: any;
|
||||||
constructor() {
|
access: any;
|
||||||
super();
|
logger!: Logger;
|
||||||
}
|
|
||||||
async onInit() {
|
async onInit() {
|
||||||
const access: any = this.access;
|
const access: any = this.access;
|
||||||
this.client = new Core({
|
this.client = new Core({
|
||||||
|
|
|
@ -1,59 +1,69 @@
|
||||||
import { AbstractPlugin, IsTask, RunStrategy, TaskInput, TaskOutput, TaskPlugin } from "@certd/pipeline";
|
import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, LOGGER, RunStrategy, TaskInput } from "@certd/pipeline";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import Core from "@alicloud/pop-core";
|
import Core from "@alicloud/pop-core";
|
||||||
import RPCClient from "@alicloud/pop-core";
|
import RPCClient from "@alicloud/pop-core";
|
||||||
import { AliyunAccess } from "../../access";
|
import { AliyunAccess } from "../../access";
|
||||||
|
import { Inject } from "@midwayjs/core";
|
||||||
|
|
||||||
@IsTask(() => {
|
@IsTaskPlugin({
|
||||||
return {
|
name: "DeployCertToAliyunCDN",
|
||||||
name: "DeployCertToAliyunCDN",
|
title: "部署证书至阿里云CDN",
|
||||||
title: "部署证书至阿里云CDN",
|
desc: "依赖证书申请前置任务,自动部署域名证书至阿里云CDN",
|
||||||
desc: "依赖证书申请前置任务,自动部署域名证书至阿里云CDN",
|
default: {
|
||||||
input: {
|
strategy: {
|
||||||
domainName: {
|
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||||
title: "CDN加速域名",
|
|
||||||
helper: "你在阿里云上配置的CDN加速域名,比如certd.docmirror.cn",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
certName: {
|
|
||||||
title: "证书名称",
|
|
||||||
helper: "上传后将以此名称作为前缀备注",
|
|
||||||
},
|
|
||||||
cert: {
|
|
||||||
title: "域名证书",
|
|
||||||
helper: "请选择前置任务输出的域名证书",
|
|
||||||
component: {
|
|
||||||
name: "pi-output-selector",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
accessId: {
|
|
||||||
title: "Access授权",
|
|
||||||
helper: "阿里云授权AccessKeyId、AccessKeySecret",
|
|
||||||
component: {
|
|
||||||
name: "pi-access-selector",
|
|
||||||
type: "aliyun",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
output: {},
|
},
|
||||||
default: {
|
|
||||||
strategy: {
|
|
||||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
export class DeployCertToAliyunCDN extends AbstractPlugin implements TaskPlugin {
|
export class DeployCertToAliyunCDN implements ITaskPlugin {
|
||||||
async execute(input: TaskInput): Promise<TaskOutput> {
|
@TaskInput({
|
||||||
|
title: "CDN加速域名",
|
||||||
|
helper: "你在阿里云上配置的CDN加速域名,比如certd.docmirror.cn",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
domainName!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "证书名称",
|
||||||
|
helper: "上传后将以此名称作为前缀备注",
|
||||||
|
})
|
||||||
|
certName!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "域名证书",
|
||||||
|
helper: "请选择前置任务输出的域名证书",
|
||||||
|
component: {
|
||||||
|
name: "pi-output-selector",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
cert!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "Access授权",
|
||||||
|
helper: "阿里云授权AccessKeyId、AccessKeySecret",
|
||||||
|
component: {
|
||||||
|
name: "pi-access-selector",
|
||||||
|
type: "aliyun",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
accessId!: string;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
accessService!: IAccessService;
|
||||||
|
|
||||||
|
@Autowire()
|
||||||
|
logger!: LOGGER;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
async onInit() {}
|
||||||
|
async execute(): Promise<void> {
|
||||||
console.log("开始部署证书到阿里云cdn");
|
console.log("开始部署证书到阿里云cdn");
|
||||||
const access = (await this.accessService.getById(input.accessId)) as AliyunAccess;
|
const access = (await this.accessService.getById(this.accessId)) as AliyunAccess;
|
||||||
const client = this.getClient(access);
|
const client = this.getClient(access);
|
||||||
const params = await this.buildParams(input);
|
const params = await this.buildParams();
|
||||||
await this.doRequest(client, params);
|
await this.doRequest(client, params);
|
||||||
console.log("部署完成");
|
console.log("部署完成");
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getClient(access: AliyunAccess) {
|
getClient(access: AliyunAccess) {
|
||||||
|
@ -65,13 +75,12 @@ export class DeployCertToAliyunCDN extends AbstractPlugin implements TaskPlugin
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async buildParams(input: TaskInput) {
|
async buildParams() {
|
||||||
const { certName, domainName } = input;
|
const CertName = (this.certName ?? "certd") + "-" + dayjs().format("YYYYMMDDHHmmss");
|
||||||
const CertName = (certName ?? "certd") + "-" + dayjs().format("YYYYMMDDHHmmss");
|
const cert: any = this.cert;
|
||||||
const cert = input.cert;
|
|
||||||
return {
|
return {
|
||||||
RegionId: "cn-hangzhou",
|
RegionId: "cn-hangzhou",
|
||||||
DomainName: domainName,
|
DomainName: this.domainName,
|
||||||
ServerCertificateStatus: "on",
|
ServerCertificateStatus: "on",
|
||||||
CertName: CertName,
|
CertName: CertName,
|
||||||
CertType: "upload",
|
CertType: "upload",
|
||||||
|
|
|
@ -1,70 +1,81 @@
|
||||||
import { AbstractPlugin, IsTask, RunStrategy, TaskInput, TaskOutput, TaskPlugin } from "@certd/pipeline";
|
import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline";
|
||||||
import Core from "@alicloud/pop-core";
|
import Core from "@alicloud/pop-core";
|
||||||
import { AliyunAccess } from "../../access";
|
import { AliyunAccess } from "../../access";
|
||||||
import { appendTimeSuffix, checkRet, ZoneOptions } from "../../utils";
|
import { appendTimeSuffix, checkRet, ZoneOptions } from "../../utils";
|
||||||
|
import { Logger } from "log4js";
|
||||||
|
|
||||||
@IsTask(() => {
|
@IsTaskPlugin({
|
||||||
return {
|
name: "uploadCertToAliyun",
|
||||||
name: "uploadCertToAliyun",
|
title: "上传证书到阿里云",
|
||||||
title: "上传证书到阿里云",
|
desc: "",
|
||||||
desc: "",
|
default: {
|
||||||
input: {
|
strategy: {
|
||||||
name: {
|
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||||
title: "证书名称",
|
|
||||||
helper: "证书上传后将以此参数作为名称前缀",
|
|
||||||
},
|
|
||||||
regionId: {
|
|
||||||
title: "大区",
|
|
||||||
value: "cn-hangzhou",
|
|
||||||
component: {
|
|
||||||
name: "a-select",
|
|
||||||
vModel: "value",
|
|
||||||
options: ZoneOptions,
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
cert: {
|
|
||||||
title: "域名证书",
|
|
||||||
helper: "请选择前置任务输出的域名证书",
|
|
||||||
component: {
|
|
||||||
name: "pi-output-selector",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
accessId: {
|
|
||||||
title: "Access授权",
|
|
||||||
helper: "阿里云授权AccessKeyId、AccessKeySecret",
|
|
||||||
component: {
|
|
||||||
name: "pi-access-selector",
|
|
||||||
type: "aliyun",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
output: {
|
},
|
||||||
aliyunCertId: {
|
|
||||||
title: "上传成功后的阿里云CertId",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
default: {
|
|
||||||
strategy: {
|
|
||||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
export class UploadCertToAliyun extends AbstractPlugin implements TaskPlugin {
|
export class UploadCertToAliyun implements ITaskPlugin {
|
||||||
async execute(input: TaskInput): Promise<TaskOutput> {
|
@TaskInput({
|
||||||
|
title: "证书名称",
|
||||||
|
|
||||||
|
helper: "证书上传后将以此参数作为名称前缀",
|
||||||
|
})
|
||||||
|
name!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "大区",
|
||||||
|
value: "cn-hangzhou",
|
||||||
|
component: {
|
||||||
|
name: "a-select",
|
||||||
|
vModel: "value",
|
||||||
|
options: ZoneOptions,
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
regionId!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "域名证书",
|
||||||
|
helper: "请选择前置任务输出的域名证书",
|
||||||
|
component: {
|
||||||
|
name: "pi-output-selector",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
cert!: any;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "Access授权",
|
||||||
|
helper: "阿里云授权AccessKeyId、AccessKeySecret",
|
||||||
|
component: {
|
||||||
|
name: "pi-access-selector",
|
||||||
|
type: "aliyun",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
accessId!: string;
|
||||||
|
|
||||||
|
@TaskOutput({
|
||||||
|
title: "上传成功后的阿里云CertId",
|
||||||
|
})
|
||||||
|
aliyunCertId!: string;
|
||||||
|
|
||||||
|
@Autowire()
|
||||||
|
accessService!: IAccessService;
|
||||||
|
|
||||||
|
@Autowire()
|
||||||
|
logger!: Logger;
|
||||||
|
|
||||||
|
async execute(): Promise<void> {
|
||||||
console.log("开始部署证书到阿里云cdn");
|
console.log("开始部署证书到阿里云cdn");
|
||||||
const access = (await this.accessService.getById(input.accessId)) as AliyunAccess;
|
const access = (await this.accessService.getById(this.accessId)) as AliyunAccess;
|
||||||
const client = this.getClient(access);
|
const client = this.getClient(access);
|
||||||
const { name, cert } = input;
|
const certName = appendTimeSuffix(this.name);
|
||||||
const certName = appendTimeSuffix(name);
|
|
||||||
const params = {
|
const params = {
|
||||||
RegionId: input.regionId || "cn-hangzhou",
|
RegionId: this.regionId || "cn-hangzhou",
|
||||||
Name: certName,
|
Name: certName,
|
||||||
Cert: cert.crt,
|
Cert: this.cert.crt,
|
||||||
Key: cert.key,
|
Key: this.cert.key,
|
||||||
};
|
};
|
||||||
|
|
||||||
const requestOption = {
|
const requestOption = {
|
||||||
|
@ -74,7 +85,9 @@ export class UploadCertToAliyun extends AbstractPlugin implements TaskPlugin {
|
||||||
const ret = (await client.request("CreateUserCertificate", params, requestOption)) as any;
|
const ret = (await client.request("CreateUserCertificate", params, requestOption)) as any;
|
||||||
checkRet(ret);
|
checkRet(ret);
|
||||||
this.logger.info("证书上传成功:aliyunCertId=", ret.CertId);
|
this.logger.info("证书上传成功:aliyunCertId=", ret.CertId);
|
||||||
return { aliyunCertId: ret.CertId };
|
|
||||||
|
//output
|
||||||
|
this.aliyunCertId = ret.CertId;
|
||||||
}
|
}
|
||||||
|
|
||||||
getClient(aliyunProvider: AliyunAccess) {
|
getClient(aliyunProvider: AliyunAccess) {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"log4js": "^6.7.1",
|
||||||
"@types/lodash": "^4.14.186",
|
"@types/lodash": "^4.14.186",
|
||||||
"vue-tsc": "^0.38.9",
|
"vue-tsc": "^0.38.9",
|
||||||
"@alicloud/cs20151215": "^3.0.3",
|
"@alicloud/cs20151215": "^3.0.3",
|
||||||
|
@ -36,7 +37,6 @@
|
||||||
"eslint-plugin-import": "^2.26.0",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"log4js": "^6.3.0",
|
|
||||||
"mocha": "^10.1.0",
|
"mocha": "^10.1.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.8.4",
|
"typescript": "^4.8.4",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export * from "@certd/plugin-aliyun";
|
// export * from "@certd/plugin-aliyun";
|
||||||
export * from "@certd/plugin-cert";
|
export * from "@certd/plugin-cert";
|
||||||
export * from "@certd/plugin-tencent";
|
export * from "@certd/plugin-tencent";
|
||||||
export * from "@certd/plugin-host";
|
// export * from "@certd/plugin-host";
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
"@certd/pipeline": "^0.3.0"
|
"@certd/pipeline": "^0.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"log4js": "^6.7.1",
|
||||||
"dayjs": "^1.11.6",
|
"dayjs": "^1.11.6",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"@types/lodash": "^4.14.186",
|
"@types/lodash": "^4.14.186",
|
||||||
|
@ -36,7 +37,6 @@
|
||||||
"eslint-plugin-import": "^2.26.0",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"log4js": "^6.3.0",
|
|
||||||
"mocha": "^10.1.0",
|
"mocha": "^10.1.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.8.4",
|
"typescript": "^4.8.4",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import * as acme from "@certd/acme-client";
|
import * as acme from "@certd/acme-client";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { AbstractDnsProvider, IDnsProvider } from "@certd/pipeline";
|
import { IDnsProvider } from "@certd/pipeline";
|
||||||
import { Challenge } from "@certd/acme-client/types/rfc8555";
|
import { Challenge } from "@certd/acme-client/types/rfc8555";
|
||||||
import { Logger } from "log4js";
|
import { Logger } from "log4js";
|
||||||
import { IContext } from "@certd/pipeline/src/core/context";
|
import { IContext } from "@certd/pipeline/src/core/context";
|
||||||
|
@ -132,7 +132,7 @@ export class AcmeService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async order(options: { email: string; domains: string | string[]; dnsProvider: AbstractDnsProvider; csrInfo: any; isTest?: boolean }) {
|
async order(options: { email: string; domains: string | string[]; dnsProvider: any; csrInfo: any; isTest?: boolean }) {
|
||||||
const { email, isTest, domains, csrInfo, dnsProvider } = options;
|
const { email, isTest, domains, csrInfo, dnsProvider } = options;
|
||||||
const client: acme.Client = await this.getAcmeClient(email, isTest);
|
const client: acme.Client = await this.getAcmeClient(email, isTest);
|
||||||
|
|
||||||
|
|
|
@ -1,123 +1,164 @@
|
||||||
import { AbstractDnsProvider, AbstractPlugin, dnsProviderRegistry, IsTask, RunStrategy, TaskInput, TaskOutput, TaskPlugin } from "@certd/pipeline";
|
import {
|
||||||
|
Autowire,
|
||||||
|
dnsProviderRegistry,
|
||||||
|
HttpClient,
|
||||||
|
IAccessService,
|
||||||
|
IContext,
|
||||||
|
IsTaskPlugin,
|
||||||
|
ITaskPlugin,
|
||||||
|
RunStrategy,
|
||||||
|
TaskInput,
|
||||||
|
TaskOutput,
|
||||||
|
} from "@certd/pipeline";
|
||||||
import forge from "node-forge";
|
import forge from "node-forge";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { AcmeService } from "./acme";
|
import { AcmeService } from "./acme";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { Logger } from "log4js";
|
||||||
|
|
||||||
export type CertInfo = {
|
export type CertInfo = {
|
||||||
crt: string;
|
crt: string;
|
||||||
key: string;
|
key: string;
|
||||||
csr: string;
|
csr: string;
|
||||||
};
|
};
|
||||||
@IsTask(() => {
|
@IsTaskPlugin({
|
||||||
return {
|
name: "CertApply",
|
||||||
name: "CertApply",
|
title: "证书申请",
|
||||||
title: "证书申请",
|
desc: "免费通配符域名证书申请,支持多个域名打到同一个证书上",
|
||||||
desc: "免费通配符域名证书申请,支持多个域名打到同一个证书上",
|
default: {
|
||||||
input: {
|
input: {
|
||||||
domains: {
|
renewDays: 20,
|
||||||
title: "域名",
|
forceUpdate: false,
|
||||||
component: {
|
|
||||||
name: "a-select",
|
|
||||||
vModel: "value",
|
|
||||||
mode: "tags",
|
|
||||||
open: false,
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
col: {
|
|
||||||
span: 24,
|
|
||||||
},
|
|
||||||
helper:
|
|
||||||
"支持通配符域名,例如: *.foo.com 、 *.test.handsfree.work\n" +
|
|
||||||
"支持多个域名、多个子域名、多个通配符域名打到一个证书上(域名必须是在同一个DNS提供商解析)\n" +
|
|
||||||
"多级子域名要分成多个域名输入(*.foo.com的证书不能用于xxx.yyy.foo.com)\n" +
|
|
||||||
"输入一个回车之后,再输入下一个",
|
|
||||||
},
|
|
||||||
email: {
|
|
||||||
title: "邮箱",
|
|
||||||
component: {
|
|
||||||
name: "a-input",
|
|
||||||
vModel: "value",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
helper: "请输入邮箱",
|
|
||||||
},
|
|
||||||
dnsProviderType: {
|
|
||||||
title: "DNS提供商",
|
|
||||||
component: {
|
|
||||||
name: "pi-dns-provider-selector",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
helper: "请选择dns解析提供商",
|
|
||||||
},
|
|
||||||
dnsProviderAccess: {
|
|
||||||
title: "DNS解析授权",
|
|
||||||
component: {
|
|
||||||
name: "pi-access-selector",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
helper: "请选择dns解析提供商授权",
|
|
||||||
},
|
|
||||||
renewDays: {
|
|
||||||
title: "更新天数",
|
|
||||||
component: {
|
|
||||||
name: "a-input-number",
|
|
||||||
vModel: "value",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
helper: "到期前多少天后更新证书",
|
|
||||||
},
|
|
||||||
forceUpdate: {
|
|
||||||
title: "强制更新",
|
|
||||||
component: {
|
|
||||||
name: "a-switch",
|
|
||||||
vModel: "checked",
|
|
||||||
},
|
|
||||||
helper: "是否强制重新申请证书",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
default: {
|
strategy: {
|
||||||
input: {
|
runStrategy: RunStrategy.AlwaysRun,
|
||||||
renewDays: 20,
|
|
||||||
forceUpdate: false,
|
|
||||||
},
|
|
||||||
strategy: {
|
|
||||||
runStrategy: RunStrategy.AlwaysRun,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
output: {
|
},
|
||||||
cert: {
|
|
||||||
key: "cert",
|
|
||||||
type: "CertInfo",
|
|
||||||
title: "域名证书",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
export class CertApplyPlugin extends AbstractPlugin implements TaskPlugin {
|
export class CertApplyPlugin implements ITaskPlugin {
|
||||||
|
@TaskInput({
|
||||||
|
title: "域名",
|
||||||
|
component: {
|
||||||
|
name: "a-select",
|
||||||
|
vModel: "value",
|
||||||
|
mode: "tags",
|
||||||
|
open: false,
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
col: {
|
||||||
|
span: 24,
|
||||||
|
},
|
||||||
|
helper:
|
||||||
|
"支持通配符域名,例如: *.foo.com 、 *.test.handsfree.work\n" +
|
||||||
|
"支持多个域名、多个子域名、多个通配符域名打到一个证书上(域名必须是在同一个DNS提供商解析)\n" +
|
||||||
|
"多级子域名要分成多个域名输入(*.foo.com的证书不能用于xxx.yyy.foo.com)\n" +
|
||||||
|
"输入一个回车之后,再输入下一个",
|
||||||
|
})
|
||||||
|
domains!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "邮箱",
|
||||||
|
component: {
|
||||||
|
name: "a-input",
|
||||||
|
vModel: "value",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
helper: "请输入邮箱",
|
||||||
|
})
|
||||||
|
email!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "DNS提供商",
|
||||||
|
component: {
|
||||||
|
name: "pi-dns-provider-selector",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
helper: "请选择dns解析提供商",
|
||||||
|
})
|
||||||
|
dnsProviderType!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "DNS解析授权",
|
||||||
|
component: {
|
||||||
|
name: "pi-access-selector",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
helper: "请选择dns解析提供商授权",
|
||||||
|
})
|
||||||
|
dnsProviderAccess!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "更新天数",
|
||||||
|
component: {
|
||||||
|
name: "a-input-number",
|
||||||
|
vModel: "value",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
helper: "到期前多少天后更新证书",
|
||||||
|
})
|
||||||
|
renewDays!: number;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "强制更新",
|
||||||
|
component: {
|
||||||
|
name: "a-switch",
|
||||||
|
vModel: "checked",
|
||||||
|
},
|
||||||
|
helper: "是否强制重新申请证书",
|
||||||
|
})
|
||||||
|
forceUpdate!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "CsrInfo",
|
||||||
|
})
|
||||||
|
csrInfo: any;
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
acme: AcmeService;
|
acme: AcmeService;
|
||||||
protected async onInit() {
|
|
||||||
|
@Autowire()
|
||||||
|
logger!: Logger;
|
||||||
|
|
||||||
|
@Autowire()
|
||||||
|
userContext!: IContext;
|
||||||
|
|
||||||
|
@Autowire()
|
||||||
|
accessService!: IAccessService;
|
||||||
|
|
||||||
|
@Autowire()
|
||||||
|
http!: HttpClient;
|
||||||
|
|
||||||
|
@Autowire()
|
||||||
|
pipelineContext!: IContext;
|
||||||
|
|
||||||
|
@TaskOutput({
|
||||||
|
title: "域名证书",
|
||||||
|
})
|
||||||
|
cert?: CertInfo;
|
||||||
|
|
||||||
|
async onInit() {
|
||||||
this.acme = new AcmeService({ userContext: this.userContext, logger: this.logger });
|
this.acme = new AcmeService({ userContext: this.userContext, logger: this.logger });
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(input: TaskInput): Promise<TaskOutput> {
|
async execute(): Promise<void> {
|
||||||
const oldCert = await this.condition(input);
|
const oldCert = await this.condition();
|
||||||
if (oldCert != null) {
|
if (oldCert != null) {
|
||||||
return {
|
return this.output(oldCert);
|
||||||
cert: oldCert,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
const cert = await this.doCertApply(input);
|
const cert = await this.doCertApply();
|
||||||
return { cert };
|
return this.output(cert);
|
||||||
|
}
|
||||||
|
|
||||||
|
output(cert: any) {
|
||||||
|
this.cert = cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否更新证书
|
* 是否更新证书
|
||||||
* @param input
|
* @param input
|
||||||
*/
|
*/
|
||||||
async condition(input: TaskInput) {
|
async condition() {
|
||||||
if (input.forceUpdate) {
|
if (this.forceUpdate) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let oldCert;
|
let oldCert;
|
||||||
|
@ -131,7 +172,7 @@ export class CertApplyPlugin extends AbstractPlugin implements TaskPlugin {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ret = this.isWillExpire(oldCert.expires, input.renewDays);
|
const ret = this.isWillExpire(oldCert.expires, this.renewDays);
|
||||||
if (!ret.isWillExpire) {
|
if (!ret.isWillExpire) {
|
||||||
this.logger.info(`证书还未过期:过期时间${dayjs(oldCert.expires).format("YYYY-MM-DD HH:mm:ss")},剩余${ret.leftDays}天`);
|
this.logger.info(`证书还未过期:过期时间${dayjs(oldCert.expires).format("YYYY-MM-DD HH:mm:ss")},剩余${ret.leftDays}天`);
|
||||||
return oldCert;
|
return oldCert;
|
||||||
|
@ -140,11 +181,11 @@ export class CertApplyPlugin extends AbstractPlugin implements TaskPlugin {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async doCertApply(input: TaskInput) {
|
async doCertApply() {
|
||||||
const email = input["email"];
|
const email = this["email"];
|
||||||
const domains = input["domains"];
|
const domains = this["domains"];
|
||||||
const dnsProviderType = input["dnsProviderType"];
|
const dnsProviderType = this["dnsProviderType"];
|
||||||
const dnsProviderAccessId = input["dnsProviderAccess"];
|
const dnsProviderAccessId = this["dnsProviderAccess"];
|
||||||
const csrInfo = _.merge(
|
const csrInfo = _.merge(
|
||||||
{
|
{
|
||||||
country: "CN",
|
country: "CN",
|
||||||
|
@ -154,7 +195,7 @@ export class CertApplyPlugin extends AbstractPlugin implements TaskPlugin {
|
||||||
organizationUnit: "IT Department",
|
organizationUnit: "IT Department",
|
||||||
emailAddress: email,
|
emailAddress: email,
|
||||||
},
|
},
|
||||||
input["csrInfo"]
|
this.csrInfo
|
||||||
);
|
);
|
||||||
this.logger.info("开始申请证书,", email, domains);
|
this.logger.info("开始申请证书,", email, domains);
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
"ssh2": "^0.8.9"
|
"ssh2": "^0.8.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"log4js": "^6.3.0",
|
"log4js": "^6.7.1",
|
||||||
"dayjs": "^1.9.7",
|
"dayjs": "^1.9.7",
|
||||||
"lodash-es": "^4.17.20",
|
"lodash-es": "^4.17.20",
|
||||||
"@types/lodash": "^4.14.186",
|
"@types/lodash": "^4.14.186",
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
"tencentcloud-sdk-nodejs": "^4.0.44"
|
"tencentcloud-sdk-nodejs": "^4.0.44"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"log4js": "^6.7.1",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"log4js": "^6.3.0",
|
|
||||||
"dayjs": "^1.9.7",
|
"dayjs": "^1.9.7",
|
||||||
"lodash-es": "^4.17.20",
|
"lodash-es": "^4.17.20",
|
||||||
"@types/lodash": "^4.14.186",
|
"@types/lodash": "^4.14.186",
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { IAccessService, IsTaskPlugin, RunStrategy, TaskInput, ITaskPlugin } from "@certd/pipeline";
|
import { IAccessService, IsTaskPlugin, RunStrategy, TaskInput, ITaskPlugin, LOGGER } from "@certd/pipeline";
|
||||||
import tencentcloud from "tencentcloud-sdk-nodejs/index";
|
import tencentcloud from "tencentcloud-sdk-nodejs/index";
|
||||||
import { TencentAccess } from "../../access";
|
import { TencentAccess } from "../../access";
|
||||||
import { Inject } from "@midwayjs/decorator";
|
import { Inject } from "@midwayjs/decorator";
|
||||||
import { ILogger } from "@midwayjs/core";
|
|
||||||
|
|
||||||
@IsTaskPlugin({
|
@IsTaskPlugin({
|
||||||
name: "DeployCertToTencentCDN",
|
name: "DeployCertToTencentCDN",
|
||||||
|
@ -52,7 +51,10 @@ export class DeployToCdnPlugin implements ITaskPlugin {
|
||||||
accessService!: IAccessService;
|
accessService!: IAccessService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
logger!: ILogger;
|
logger!: LOGGER;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
async onInit() {}
|
||||||
|
|
||||||
async execute(): Promise<void> {
|
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;
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import { IAccessService, IsTaskPlugin, ITaskPlugin, RunStrategy, TaskInput, utils } from "@certd/pipeline";
|
import { IAccessService, IsTaskPlugin, ITaskPlugin, LOGGER, RunStrategy, TaskInput, utils } from "@certd/pipeline";
|
||||||
import tencentcloud from "tencentcloud-sdk-nodejs/index";
|
import tencentcloud from "tencentcloud-sdk-nodejs/index";
|
||||||
import { TencentAccess } from "../../access";
|
import { TencentAccess } from "../../access";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { Inject } from "@midwayjs/decorator";
|
import { Inject } from "@midwayjs/decorator";
|
||||||
import { ILogger } from "@midwayjs/core";
|
|
||||||
|
|
||||||
@IsTaskPlugin({
|
@IsTaskPlugin({
|
||||||
name: "DeployCertToTencentCLB",
|
name: "DeployCertToTencentCLB",
|
||||||
|
@ -77,8 +76,10 @@ export class DeployToClbPlugin implements ITaskPlugin {
|
||||||
accessService!: IAccessService;
|
accessService!: IAccessService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
logger!: ILogger;
|
logger!: LOGGER;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
async onInit() {}
|
||||||
async execute(): Promise<void> {
|
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 client = this.getClient(accessProvider, this.region);
|
||||||
|
|
|
@ -1,121 +1,117 @@
|
||||||
import { AbstractPlugin, IsTask, RunStrategy, TaskInput, TaskOutput, TaskPlugin, utils } from "@certd/pipeline";
|
import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, RunStrategy, TaskInput, utils } from "@certd/pipeline";
|
||||||
import tencentcloud from "tencentcloud-sdk-nodejs/index";
|
import tencentcloud from "tencentcloud-sdk-nodejs/index";
|
||||||
import { K8sClient } from "@certd/plugin-util";
|
import { K8sClient } from "@certd/plugin-util";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import { Logger } from "log4js";
|
||||||
|
|
||||||
@IsTask(() => {
|
@IsTaskPlugin({
|
||||||
return {
|
name: "DeployCertToTencentTKEIngress",
|
||||||
name: "DeployCertToTencentTKEIngress",
|
title: "部署到腾讯云TKE-ingress",
|
||||||
title: "部署到腾讯云TKE-ingress",
|
desc: "需要【上传到腾讯云】作为前置任务",
|
||||||
desc: "需要【上传到腾讯云】作为前置任务",
|
default: {
|
||||||
input: {
|
strategy: {
|
||||||
region: {
|
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||||
title: "大区",
|
|
||||||
value: "ap-guangzhou",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
clusterId: {
|
|
||||||
title: "集群ID",
|
|
||||||
required: true,
|
|
||||||
desc: "例如:cls-6lbj1vee",
|
|
||||||
request: true,
|
|
||||||
},
|
|
||||||
namespace: {
|
|
||||||
title: "集群namespace",
|
|
||||||
value: "default",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
secreteName: {
|
|
||||||
title: "证书的secret名称",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
ingressName: {
|
|
||||||
title: "ingress名称",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
ingressClass: {
|
|
||||||
title: "ingress类型",
|
|
||||||
component: {
|
|
||||||
name: "a-select",
|
|
||||||
options: [{ value: "qcloud" }, { value: "nginx" }],
|
|
||||||
},
|
|
||||||
helper: "可选 qcloud / nginx",
|
|
||||||
},
|
|
||||||
clusterIp: {
|
|
||||||
title: "集群内网ip",
|
|
||||||
helper: "如果开启了外网的话,无需设置",
|
|
||||||
},
|
|
||||||
clusterDomain: {
|
|
||||||
title: "集群域名",
|
|
||||||
helper: "可不填,默认为:[clusterId].ccs.tencent-cloud.com",
|
|
||||||
},
|
|
||||||
|
|
||||||
tencentCertId: {
|
|
||||||
title: "腾讯云证书id",
|
|
||||||
helper: "请选择“上传证书到腾讯云”前置任务的输出",
|
|
||||||
component: {
|
|
||||||
name: "pi-output-selector",
|
|
||||||
from: "UploadCertToTencent",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AccessProvider的key,或者一个包含access的具体的对象
|
|
||||||
*/
|
|
||||||
accessId: {
|
|
||||||
title: "Access授权",
|
|
||||||
helper: "access授权",
|
|
||||||
component: {
|
|
||||||
name: "pi-access-selector",
|
|
||||||
type: "tencent",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
cert: {
|
|
||||||
title: "域名证书",
|
|
||||||
helper: "请选择前置任务输出的域名证书",
|
|
||||||
component: {
|
|
||||||
name: "pi-output-selector",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
default: {
|
},
|
||||||
strategy: {
|
|
||||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
output: {},
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
export class DeployCertToTencentTKEIngressPlugin extends AbstractPlugin implements TaskPlugin {
|
export class DeployCertToTencentTKEIngressPlugin implements ITaskPlugin {
|
||||||
async execute(input: TaskInput): Promise<TaskOutput> {
|
@TaskInput({ title: "大区", value: "ap-guangzhou", required: true })
|
||||||
const { accessId, region, clusterId, clusterIp, ingressClass } = input;
|
region!: string;
|
||||||
let { clusterDomain } = input;
|
|
||||||
const accessProvider = this.accessService.getById(accessId);
|
@TaskInput({ title: "集群ID", required: true, desc: "例如:cls-6lbj1vee", request: true })
|
||||||
const tkeClient = this.getTkeClient(accessProvider, region);
|
clusterId!: string;
|
||||||
const kubeConfigStr = await this.getTkeKubeConfig(tkeClient, clusterId);
|
|
||||||
|
@TaskInput({ title: "集群namespace", value: "default", required: true })
|
||||||
|
namespace!: string;
|
||||||
|
|
||||||
|
@TaskInput({ title: "证书的secret名称", required: true })
|
||||||
|
secretName!: string | string[];
|
||||||
|
|
||||||
|
@TaskInput({ title: "ingress名称", required: true })
|
||||||
|
ingressName!: string | string[];
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "ingress类型",
|
||||||
|
component: {
|
||||||
|
name: "a-select",
|
||||||
|
options: [{ value: "qcloud" }, { value: "nginx" }],
|
||||||
|
},
|
||||||
|
helper: "可选 qcloud / nginx",
|
||||||
|
})
|
||||||
|
ingressClass!: string;
|
||||||
|
|
||||||
|
@TaskInput({ title: "集群内网ip", helper: "如果开启了外网的话,无需设置" })
|
||||||
|
clusterIp!: string;
|
||||||
|
|
||||||
|
@TaskInput({ title: "集群域名", helper: "可不填,默认为:[clusterId].ccs.tencent-cloud.com" })
|
||||||
|
clusterDomain!: string;
|
||||||
|
@TaskInput({
|
||||||
|
title: "腾讯云证书id",
|
||||||
|
helper: "请选择“上传证书到腾讯云”前置任务的输出",
|
||||||
|
component: {
|
||||||
|
name: "pi-output-selector",
|
||||||
|
from: "UploadCertToTencent",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
tencentCertId!: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AccessProvider的key,或者一个包含access的具体的对象
|
||||||
|
*/
|
||||||
|
@TaskInput({
|
||||||
|
title: "Access授权",
|
||||||
|
helper: "access授权",
|
||||||
|
component: {
|
||||||
|
name: "pi-access-selector",
|
||||||
|
type: "tencent",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
accessId!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "域名证书",
|
||||||
|
helper: "请选择前置任务输出的域名证书",
|
||||||
|
component: {
|
||||||
|
name: "pi-output-selector",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
cert!: any;
|
||||||
|
|
||||||
|
@Autowire()
|
||||||
|
logger!: Logger;
|
||||||
|
|
||||||
|
@Autowire()
|
||||||
|
accessService!: IAccessService;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
async onInit() {}
|
||||||
|
|
||||||
|
async execute(): Promise<void> {
|
||||||
|
const accessProvider = this.accessService.getById(this.accessId);
|
||||||
|
const tkeClient = this.getTkeClient(accessProvider, this.region);
|
||||||
|
const kubeConfigStr = await this.getTkeKubeConfig(tkeClient, this.clusterId);
|
||||||
|
|
||||||
this.logger.info("kubeconfig已成功获取");
|
this.logger.info("kubeconfig已成功获取");
|
||||||
const k8sClient = new K8sClient(kubeConfigStr);
|
const k8sClient = new K8sClient(kubeConfigStr);
|
||||||
if (clusterIp != null) {
|
if (this.clusterIp != null) {
|
||||||
if (!clusterDomain) {
|
if (!this.clusterDomain) {
|
||||||
clusterDomain = `${clusterId}.ccs.tencent-cloud.com`;
|
this.clusterDomain = `${this.clusterId}.ccs.tencent-cloud.com`;
|
||||||
}
|
}
|
||||||
// 修改内网解析ip地址
|
// 修改内网解析ip地址
|
||||||
k8sClient.setLookup({ [clusterDomain]: { ip: clusterIp } });
|
k8sClient.setLookup({ [this.clusterDomain]: { ip: this.clusterIp } });
|
||||||
}
|
}
|
||||||
const ingressType = ingressClass || "qcloud";
|
const ingressType = this.ingressClass || "qcloud";
|
||||||
if (ingressType === "qcloud") {
|
if (ingressType === "qcloud") {
|
||||||
await this.patchQcloudCertSecret({ k8sClient, input });
|
await this.patchQcloudCertSecret({ k8sClient });
|
||||||
} else {
|
} else {
|
||||||
await this.patchNginxCertSecret({ k8sClient, input });
|
await this.patchNginxCertSecret({ k8sClient });
|
||||||
}
|
}
|
||||||
|
|
||||||
await utils.sleep(2000); // 停留2秒,等待secret部署完成
|
await utils.sleep(2000); // 停留2秒,等待secret部署完成
|
||||||
await this.restartIngress({ k8sClient, input });
|
await this.restartIngress({ k8sClient });
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getTkeClient(accessProvider: any, region = "ap-guangzhou") {
|
getTkeClient(accessProvider: any, region = "ap-guangzhou") {
|
||||||
|
@ -154,15 +150,14 @@ export class DeployCertToTencentTKEIngressPlugin extends AbstractPlugin implemen
|
||||||
return name + "-" + dayjs().format("YYYYMMDD-HHmmss");
|
return name + "-" + dayjs().format("YYYYMMDD-HHmmss");
|
||||||
}
|
}
|
||||||
|
|
||||||
async patchQcloudCertSecret(options: { k8sClient: any; input: TaskInput }) {
|
async patchQcloudCertSecret(options: { k8sClient: any }) {
|
||||||
const { tencentCertId } = options.input;
|
if (this.tencentCertId == null) {
|
||||||
if (tencentCertId == null) {
|
|
||||||
throw new Error("请先将【上传证书到腾讯云】作为前置任务");
|
throw new Error("请先将【上传证书到腾讯云】作为前置任务");
|
||||||
}
|
}
|
||||||
this.logger.info("腾讯云证书ID:", tencentCertId);
|
this.logger.info("腾讯云证书ID:", this.tencentCertId);
|
||||||
const certIdBase64 = Buffer.from(tencentCertId).toString("base64");
|
const certIdBase64 = Buffer.from(this.tencentCertId).toString("base64");
|
||||||
|
|
||||||
const { namespace, secretName } = options.input;
|
const { namespace, secretName } = this;
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
data: {
|
data: {
|
||||||
|
@ -174,7 +169,7 @@ export class DeployCertToTencentTKEIngressPlugin extends AbstractPlugin implemen
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let secretNames = secretName;
|
let secretNames: any = secretName;
|
||||||
if (typeof secretName === "string") {
|
if (typeof secretName === "string") {
|
||||||
secretNames = [secretName];
|
secretNames = [secretName];
|
||||||
}
|
}
|
||||||
|
@ -184,15 +179,15 @@ export class DeployCertToTencentTKEIngressPlugin extends AbstractPlugin implemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async patchNginxCertSecret(options: { k8sClient: any; input: TaskInput }) {
|
async patchNginxCertSecret(options: { k8sClient: any }) {
|
||||||
const { k8sClient, input } = options;
|
const { k8sClient } = options;
|
||||||
const { cert } = input;
|
const { cert } = this;
|
||||||
const crt = cert.crt;
|
const crt = cert.crt;
|
||||||
const key = cert.key;
|
const key = cert.key;
|
||||||
const crtBase64 = Buffer.from(crt).toString("base64");
|
const crtBase64 = Buffer.from(crt).toString("base64");
|
||||||
const keyBase64 = Buffer.from(key).toString("base64");
|
const keyBase64 = Buffer.from(key).toString("base64");
|
||||||
|
|
||||||
const { namespace, secretName } = input;
|
const { namespace, secretName } = this;
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
data: {
|
data: {
|
||||||
|
@ -215,9 +210,9 @@ export class DeployCertToTencentTKEIngressPlugin extends AbstractPlugin implemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async restartIngress(options: { k8sClient: any; input: TaskInput }) {
|
async restartIngress(options: { k8sClient: any }) {
|
||||||
const { k8sClient, input } = options;
|
const { k8sClient } = options;
|
||||||
const { namespace, ingressName } = input;
|
const { namespace, ingressName } = this;
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -226,7 +221,7 @@ export class DeployCertToTencentTKEIngressPlugin extends AbstractPlugin implemen
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let ingressNames = ingressName;
|
let ingressNames = this.ingressName;
|
||||||
if (typeof ingressName === "string") {
|
if (typeof ingressName === "string") {
|
||||||
ingressNames = [ingressName];
|
ingressNames = [ingressName];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +1,58 @@
|
||||||
import { AbstractPlugin, IsTask, RunStrategy, TaskInput, TaskOutput, TaskPlugin } from "@certd/pipeline";
|
import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, RunStrategy, TaskInput, TaskOutput, LOGGER } from "@certd/pipeline";
|
||||||
import tencentcloud from "tencentcloud-sdk-nodejs/index";
|
import tencentcloud from "tencentcloud-sdk-nodejs/index";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
@IsTask(() => {
|
@IsTaskPlugin({
|
||||||
return {
|
name: "UploadCertToTencent",
|
||||||
name: "UploadCertToTencent",
|
title: "上传证书到腾讯云",
|
||||||
title: "上传证书到腾讯云",
|
desc: "上传成功后输出:tencentCertId",
|
||||||
desc: "上传成功后输出:tencentCertId",
|
default: {
|
||||||
input: {
|
strategy: {
|
||||||
name: {
|
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||||
title: "证书名称",
|
|
||||||
},
|
|
||||||
accessId: {
|
|
||||||
title: "Access授权",
|
|
||||||
helper: "access授权",
|
|
||||||
component: {
|
|
||||||
name: "pi-access-selector",
|
|
||||||
type: "tencent",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
cert: {
|
|
||||||
title: "域名证书",
|
|
||||||
helper: "请选择前置任务输出的域名证书",
|
|
||||||
component: {
|
|
||||||
name: "pi-output-selector",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
default: {
|
},
|
||||||
strategy: {
|
|
||||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
tencentCertId: {
|
|
||||||
title: "上传成功后的腾讯云CertId",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
export class UploadToTencentPlugin extends AbstractPlugin implements TaskPlugin {
|
export class UploadToTencentPlugin implements ITaskPlugin {
|
||||||
async execute(input: TaskInput): Promise<TaskOutput> {
|
@TaskInput({ title: "证书名称" })
|
||||||
const { accessId, name, cert } = input;
|
name!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "Access授权",
|
||||||
|
helper: "access授权",
|
||||||
|
component: {
|
||||||
|
name: "pi-access-selector",
|
||||||
|
type: "tencent",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
accessId!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "域名证书",
|
||||||
|
helper: "请选择前置任务输出的域名证书",
|
||||||
|
component: {
|
||||||
|
name: "pi-output-selector",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
cert!: any;
|
||||||
|
|
||||||
|
@TaskOutput({
|
||||||
|
title: "上传成功后的腾讯云CertId",
|
||||||
|
})
|
||||||
|
tencentCertId?: string;
|
||||||
|
|
||||||
|
@Autowire()
|
||||||
|
accessService!: IAccessService;
|
||||||
|
|
||||||
|
@Autowire()
|
||||||
|
logger!: LOGGER;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
async onInit() {}
|
||||||
|
|
||||||
|
async execute(): Promise<void> {
|
||||||
|
const { accessId, name, cert } = this;
|
||||||
const accessProvider = this.accessService.getById(accessId);
|
const accessProvider = this.accessService.getById(accessId);
|
||||||
const certName = this.appendTimeSuffix(name || cert.domain);
|
const certName = this.appendTimeSuffix(name || cert.domain);
|
||||||
const client = this.getClient(accessProvider);
|
const client = this.getClient(accessProvider);
|
||||||
|
@ -56,7 +65,8 @@ export class UploadToTencentPlugin extends AbstractPlugin implements TaskPlugin
|
||||||
const ret = await client.UploadCertificate(params);
|
const ret = await client.UploadCertificate(params);
|
||||||
this.checkRet(ret);
|
this.checkRet(ret);
|
||||||
this.logger.info("证书上传成功:tencentCertId=", ret.CertificateId);
|
this.logger.info("证书上传成功:tencentCertId=", ret.CertificateId);
|
||||||
return { tencentCertId: ret.CertificateId };
|
|
||||||
|
this.tencentCertId = ret.CertificateId;
|
||||||
}
|
}
|
||||||
|
|
||||||
appendTimeSuffix(name: string) {
|
appendTimeSuffix(name: string) {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
"kubernetes-client": "^9.0.0"
|
"kubernetes-client": "^9.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"log4js": "^6.7.1",
|
||||||
"@types/lodash": "^4.14.186",
|
"@types/lodash": "^4.14.186",
|
||||||
"vue-tsc": "^0.38.9",
|
"vue-tsc": "^0.38.9",
|
||||||
"@alicloud/cs20151215": "^3.0.3",
|
"@alicloud/cs20151215": "^3.0.3",
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit f508b6426b5869ae0fd33d8ec8e170e097eed9d4
|
Subproject commit 08f08022903aed6ba5150a80ea3484a8bc1830b7
|
Loading…
Reference in New Issue