feat: midway注解方式编写插件

pull/9/head^2
xiaojunnuo 2022-12-29 23:52:51 +08:00
parent 717d203622
commit e4ec4e1404
30 changed files with 506 additions and 446 deletions

View File

@ -0,0 +1,6 @@
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"useWorkspaces": true,
"version": "0.0.0",
"npmClient": "yarn"
}

View File

@ -3,7 +3,7 @@
"private": true,
"type": "module",
"devDependencies": {
"lerna": "^3.18.4"
"lerna": "^6.3.0"
},
"scripts": {
"start": "lerna bootstrap --hoist",
@ -11,6 +11,9 @@
},
"license": "MIT",
"dependencies": {
"lodash-es": "^4.17.20"
}
"lodash-es": "^4.17.21"
},
"workspaces": [
"packages/**"
]
}

@ -1 +1 @@
Subproject commit 1c0e55a5339ef0ac92f96726c4c61e6918908730
Subproject commit 138c736ce6468e208a1055fd6289b8c2c8361306

View File

@ -1,7 +1,5 @@
import { Registrable } from "../registry";
import { accessRegistry } from "./registry";
import { FormItemProps } from "../d.ts";
import { AbstractAccess } from "./abstract-access";
export type AccessInputDefine = FormItemProps & {
title: string;
@ -13,5 +11,5 @@ export type AccessDefine = Registrable & {
};
};
export interface IAccessService {
getById(id: any): Promise<AbstractAccess>;
getById(id: any): Promise<any>;
}

View File

@ -9,7 +9,7 @@ import {
saveModule,
} from "@midwayjs/decorator";
import { AccessDefine, AccessInputDefine } from "./api";
import _ from "lodash-es";
import _ from "lodash";
import { accessRegistry } from "./registry";
// 提供一个唯一 key

View File

@ -1,14 +1,15 @@
import { ConcurrencyStrategy, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../d.ts";
import _ from "lodash";
import { RunHistory } from "./run-history";
import { pluginRegistry, ITaskPlugin, PluginDefine } from "../plugin";
import { PluginDefine, pluginRegistry } from "../plugin";
import { ContextFactory, IContext } from "./context";
import { IStorage } from "./storage";
import { logger } from "../utils/util.log";
import { Logger } from "log4js";
import { request } from "../utils/util.request";
import { IAccessService } from "../access";
import { Registrable, RegistryItem } from "../registry";
import { RegistryItem } from "../registry";
export class Executor {
userId: any;
pipeline: Pipeline;
@ -136,7 +137,7 @@ export class Executor {
private async runStep(step: Step) {
//执行任务
const plugin: RegistryItem = pluginRegistry.get(step.type);
const context = {
const context: any = {
logger: this.runtime.loggers[step.id],
accessService: this.accessService,
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();

View File

@ -1,18 +1,5 @@
// src/decorator/memoryCache.decorator.ts
import {
attachClassMetadata,
attachPropertyDataToClass,
createCustomPropertyDecorator,
getClassMetadata,
listModule,
listPropertyDataFromClass,
Provide,
saveClassMetadata,
saveModule,
Scope,
ScopeEnum,
} from "@midwayjs/decorator";
import _ from "lodash-es";
import { getClassMetadata, listModule, Provide, saveClassMetadata, saveModule, Scope, ScopeEnum } from "@midwayjs/decorator";
import { dnsProviderRegistry } from "./registry";
import { DnsProviderDefine } from "./api";

View File

@ -5,6 +5,5 @@ export * from "./registry";
export * from "./dns-provider";
export * from "./plugin";
export * from "./utils";
export * from "./decorator";
export * from "./midway";
export * from "./context";

View File

@ -1,9 +1,7 @@
import { Config, Configuration, getClassMetadata, Inject, listModule, listPropertyDataFromClass, Logger } from "@midwayjs/decorator";
import _ from "lodash-es";
import { Config, Configuration, Inject, Logger } from "@midwayjs/decorator";
// @ts-ignore
import { ILogger } from "@midwayjs/logger";
import { IMidwayContainer, MidwayDecoratorService } from "@midwayjs/core";
import { pluginRegistry } from "../plugin";
import { registerPlugins } from "../plugin/decorator";
import { registerAccess } from "../access/decorator";
import { registerDnsProviders } from "../dns-provider";

View File

@ -1,5 +1,4 @@
import { Registrable } from "../registry";
import { pluginRegistry } from "./registry";
import { FormItemProps } from "../d.ts";
export enum ContextScope {
@ -33,6 +32,7 @@ export type PluginDefine = Registrable & {
};
export interface ITaskPlugin {
onInit(): Promise<void>;
execute(): Promise<void>;
}

View File

@ -1,18 +1,13 @@
// src/decorator/memoryCache.decorator.ts
import {
attachClassMetadata,
attachPropertyDataToClass,
createCustomPropertyDecorator,
getClassMetadata,
listModule,
listPropertyDataFromClass,
Provide,
saveClassMetadata,
saveModule,
Scope,
ScopeEnum,
} from "@midwayjs/decorator";
import _ from "lodash-es";
import _ from "lodash";
import { pluginRegistry } from "./registry";
import { PluginDefine, TaskInputDefine, TaskOutputDefine } from "./api";
@ -33,11 +28,11 @@ export function IsTaskPlugin(define: PluginDefine): ClassDecorator {
},
target
);
// 指定 IoC 容器创建实例的作用域,这里注册为请求作用域,这样能取到 ctx
Scope(ScopeEnum.Prototype)(target);
// // 指定 IoC 容器创建实例的作用域,这里注册为请求作用域,这样能取到 ctx
// Scope(ScopeEnum.Prototype)(target);
// 调用一下 Provide 装饰器,这样用户的 class 可以省略写 @Provide() 装饰器了
Provide()(target);
// Provide()(target);
};
}

View File

@ -1,4 +1,4 @@
import log4js, { LoggingEvent } from "log4js";
import log4js, { LoggingEvent, Logger } from "log4js";
const OutputAppender = {
configure: (config: any, layouts: any, findAppender: any, levels: any) => {
@ -32,3 +32,4 @@ export function buildLogger(write: (text: string) => void) {
});
return logger;
}
export type LOGGER = Logger;

View File

@ -22,6 +22,7 @@
"@certd/plugin-util": "^0.3.0"
},
"devDependencies": {
"log4js": "^6.7.1",
"@types/lodash": "^4.14.186",
"vue-tsc": "^0.38.9",
"@alicloud/cs20151215": "^3.0.3",
@ -40,7 +41,6 @@
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^4.2.1",
"log4js": "^6.3.0",
"mocha": "^10.1.0",
"ts-node": "^10.9.1",
"typescript": "^4.8.4",

View File

@ -1,27 +1,25 @@
import { AbstractAccess, IsAccess } from "@certd/pipeline";
import { IsAccess, IsAccessInput } from "@certd/pipeline";
@IsAccess({
name: "aliyun",
title: "阿里云授权",
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 = "";
@IsAccessInput({
title: "accessKeySecret",
component: {
placeholder: "accessKeySecret",
},
required: true,
})
accessKeySecret = "";
}

View File

@ -1,6 +1,7 @@
import Core from "@alicloud/pop-core";
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({
name: "aliyun",
@ -8,11 +9,10 @@ import { AbstractDnsProvider, CreateRecordOptions, IDnsProvider, IsDnsProvider,
desc: "阿里云DNS解析提供商",
accessType: "aliyun",
})
export class AliyunDnsProvider extends AbstractDnsProvider implements IDnsProvider {
export class AliyunDnsProvider implements IDnsProvider {
client: any;
constructor() {
super();
}
access: any;
logger!: Logger;
async onInit() {
const access: any = this.access;
this.client = new Core({

View File

@ -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 Core from "@alicloud/pop-core";
import RPCClient from "@alicloud/pop-core";
import { AliyunAccess } from "../../access";
import { Inject } from "@midwayjs/core";
@IsTask(() => {
return {
name: "DeployCertToAliyunCDN",
title: "部署证书至阿里云CDN",
desc: "依赖证书申请前置任务自动部署域名证书至阿里云CDN",
input: {
domainName: {
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,
},
@IsTaskPlugin({
name: "DeployCertToAliyunCDN",
title: "部署证书至阿里云CDN",
desc: "依赖证书申请前置任务自动部署域名证书至阿里云CDN",
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
output: {},
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
};
},
})
export class DeployCertToAliyunCDN extends AbstractPlugin implements TaskPlugin {
async execute(input: TaskInput): Promise<TaskOutput> {
export class DeployCertToAliyunCDN implements ITaskPlugin {
@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");
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 params = await this.buildParams(input);
const params = await this.buildParams();
await this.doRequest(client, params);
console.log("部署完成");
return {};
}
getClient(access: AliyunAccess) {
@ -65,13 +75,12 @@ export class DeployCertToAliyunCDN extends AbstractPlugin implements TaskPlugin
});
}
async buildParams(input: TaskInput) {
const { certName, domainName } = input;
const CertName = (certName ?? "certd") + "-" + dayjs().format("YYYYMMDDHHmmss");
const cert = input.cert;
async buildParams() {
const CertName = (this.certName ?? "certd") + "-" + dayjs().format("YYYYMMDDHHmmss");
const cert: any = this.cert;
return {
RegionId: "cn-hangzhou",
DomainName: domainName,
DomainName: this.domainName,
ServerCertificateStatus: "on",
CertName: CertName,
CertType: "upload",

View File

@ -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 { AliyunAccess } from "../../access";
import { appendTimeSuffix, checkRet, ZoneOptions } from "../../utils";
import { Logger } from "log4js";
@IsTask(() => {
return {
name: "uploadCertToAliyun",
title: "上传证书到阿里云",
desc: "",
input: {
name: {
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,
},
@IsTaskPlugin({
name: "uploadCertToAliyun",
title: "上传证书到阿里云",
desc: "",
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
output: {
aliyunCertId: {
title: "上传成功后的阿里云CertId",
},
},
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
};
},
})
export class UploadCertToAliyun extends AbstractPlugin implements TaskPlugin {
async execute(input: TaskInput): Promise<TaskOutput> {
export class UploadCertToAliyun implements ITaskPlugin {
@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");
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 { name, cert } = input;
const certName = appendTimeSuffix(name);
const certName = appendTimeSuffix(this.name);
const params = {
RegionId: input.regionId || "cn-hangzhou",
RegionId: this.regionId || "cn-hangzhou",
Name: certName,
Cert: cert.crt,
Key: cert.key,
Cert: this.cert.crt,
Key: this.cert.key,
};
const requestOption = {
@ -74,7 +85,9 @@ export class UploadCertToAliyun extends AbstractPlugin implements TaskPlugin {
const ret = (await client.request("CreateUserCertificate", params, requestOption)) as any;
checkRet(ret);
this.logger.info("证书上传成功aliyunCertId=", ret.CertId);
return { aliyunCertId: ret.CertId };
//output
this.aliyunCertId = ret.CertId;
}
getClient(aliyunProvider: AliyunAccess) {

View File

@ -18,6 +18,7 @@
},
"devDependencies": {
"log4js": "^6.7.1",
"@types/lodash": "^4.14.186",
"vue-tsc": "^0.38.9",
"@alicloud/cs20151215": "^3.0.3",
@ -36,7 +37,6 @@
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^4.2.1",
"log4js": "^6.3.0",
"mocha": "^10.1.0",
"ts-node": "^10.9.1",
"typescript": "^4.8.4",

View File

@ -1,4 +1,4 @@
export * from "@certd/plugin-aliyun";
// export * from "@certd/plugin-aliyun";
export * from "@certd/plugin-cert";
export * from "@certd/plugin-tencent";
export * from "@certd/plugin-host";
// export * from "@certd/plugin-host";

View File

@ -16,6 +16,7 @@
"@certd/pipeline": "^0.3.0"
},
"devDependencies": {
"log4js": "^6.7.1",
"dayjs": "^1.11.6",
"lodash": "^4.17.21",
"@types/lodash": "^4.14.186",
@ -36,7 +37,6 @@
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^4.2.1",
"log4js": "^6.3.0",
"mocha": "^10.1.0",
"ts-node": "^10.9.1",
"typescript": "^4.8.4",

View File

@ -1,7 +1,7 @@
// @ts-ignore
import * as acme from "@certd/acme-client";
import _ from "lodash";
import { AbstractDnsProvider, IDnsProvider } from "@certd/pipeline";
import { IDnsProvider } from "@certd/pipeline";
import { Challenge } from "@certd/acme-client/types/rfc8555";
import { Logger } from "log4js";
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 client: acme.Client = await this.getAcmeClient(email, isTest);

View File

@ -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 dayjs from "dayjs";
import { AcmeService } from "./acme";
import _ from "lodash";
import { Logger } from "log4js";
export type CertInfo = {
crt: string;
key: string;
csr: string;
};
@IsTask(() => {
return {
name: "CertApply",
title: "证书申请",
desc: "免费通配符域名证书申请,支持多个域名打到同一个证书上",
@IsTaskPlugin({
name: "CertApply",
title: "证书申请",
desc: "免费通配符域名证书申请,支持多个域名打到同一个证书上",
default: {
input: {
domains: {
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" +
"输入一个回车之后,再输入下一个",
},
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: "是否强制重新申请证书",
},
renewDays: 20,
forceUpdate: false,
},
default: {
input: {
renewDays: 20,
forceUpdate: false,
},
strategy: {
runStrategy: RunStrategy.AlwaysRun,
},
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
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 });
}
async execute(input: TaskInput): Promise<TaskOutput> {
const oldCert = await this.condition(input);
async execute(): Promise<void> {
const oldCert = await this.condition();
if (oldCert != null) {
return {
cert: oldCert,
};
return this.output(oldCert);
}
const cert = await this.doCertApply(input);
return { cert };
const cert = await this.doCertApply();
return this.output(cert);
}
output(cert: any) {
this.cert = cert;
}
/**
*
* @param input
*/
async condition(input: TaskInput) {
if (input.forceUpdate) {
async condition() {
if (this.forceUpdate) {
return null;
}
let oldCert;
@ -131,7 +172,7 @@ export class CertApplyPlugin extends AbstractPlugin implements TaskPlugin {
return null;
}
const ret = this.isWillExpire(oldCert.expires, input.renewDays);
const ret = this.isWillExpire(oldCert.expires, this.renewDays);
if (!ret.isWillExpire) {
this.logger.info(`证书还未过期:过期时间${dayjs(oldCert.expires).format("YYYY-MM-DD HH:mm:ss")},剩余${ret.leftDays}`);
return oldCert;
@ -140,11 +181,11 @@ export class CertApplyPlugin extends AbstractPlugin implements TaskPlugin {
return null;
}
async doCertApply(input: TaskInput) {
const email = input["email"];
const domains = input["domains"];
const dnsProviderType = input["dnsProviderType"];
const dnsProviderAccessId = input["dnsProviderAccess"];
async doCertApply() {
const email = this["email"];
const domains = this["domains"];
const dnsProviderType = this["dnsProviderType"];
const dnsProviderAccessId = this["dnsProviderAccess"];
const csrInfo = _.merge(
{
country: "CN",
@ -154,7 +195,7 @@ export class CertApplyPlugin extends AbstractPlugin implements TaskPlugin {
organizationUnit: "IT Department",
emailAddress: email,
},
input["csrInfo"]
this.csrInfo
);
this.logger.info("开始申请证书,", email, domains);

View File

@ -15,7 +15,7 @@
"ssh2": "^0.8.9"
},
"devDependencies": {
"log4js": "^6.3.0",
"log4js": "^6.7.1",
"dayjs": "^1.9.7",
"lodash-es": "^4.17.20",
"@types/lodash": "^4.14.186",

View File

@ -16,8 +16,8 @@
"tencentcloud-sdk-nodejs": "^4.0.44"
},
"devDependencies": {
"log4js": "^6.7.1",
"axios": "^0.21.1",
"log4js": "^6.3.0",
"dayjs": "^1.9.7",
"lodash-es": "^4.17.20",
"@types/lodash": "^4.14.186",

View File

@ -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 { TencentAccess } from "../../access";
import { Inject } from "@midwayjs/decorator";
import { ILogger } from "@midwayjs/core";
@IsTaskPlugin({
name: "DeployCertToTencentCDN",
@ -52,7 +51,10 @@ export class DeployToCdnPlugin implements ITaskPlugin {
accessService!: IAccessService;
@Inject()
logger!: ILogger;
logger!: LOGGER;
// eslint-disable-next-line @typescript-eslint/no-empty-function
async onInit() {}
async execute(): Promise<void> {
const accessProvider: TencentAccess = (await this.accessService.getById(this.accessId)) as TencentAccess;

View File

@ -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 { TencentAccess } from "../../access";
import dayjs from "dayjs";
import { Inject } from "@midwayjs/decorator";
import { ILogger } from "@midwayjs/core";
@IsTaskPlugin({
name: "DeployCertToTencentCLB",
@ -77,8 +76,10 @@ export class DeployToClbPlugin implements ITaskPlugin {
accessService!: IAccessService;
@Inject()
logger!: ILogger;
logger!: LOGGER;
// eslint-disable-next-line @typescript-eslint/no-empty-function
async onInit() {}
async execute(): Promise<void> {
const accessProvider = (await this.accessService.getById(this.accessId)) as TencentAccess;
const client = this.getClient(accessProvider, this.region);

View File

@ -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 { K8sClient } from "@certd/plugin-util";
import dayjs from "dayjs";
import { Logger } from "log4js";
@IsTask(() => {
return {
name: "DeployCertToTencentTKEIngress",
title: "部署到腾讯云TKE-ingress",
desc: "需要【上传到腾讯云】作为前置任务",
input: {
region: {
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,
},
/**
* AccessProviderkey,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,
},
@IsTaskPlugin({
name: "DeployCertToTencentTKEIngress",
title: "部署到腾讯云TKE-ingress",
desc: "需要【上传到腾讯云】作为前置任务",
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
output: {},
};
},
})
export class DeployCertToTencentTKEIngressPlugin extends AbstractPlugin implements TaskPlugin {
async execute(input: TaskInput): Promise<TaskOutput> {
const { accessId, region, clusterId, clusterIp, ingressClass } = input;
let { clusterDomain } = input;
const accessProvider = this.accessService.getById(accessId);
const tkeClient = this.getTkeClient(accessProvider, region);
const kubeConfigStr = await this.getTkeKubeConfig(tkeClient, clusterId);
export class DeployCertToTencentTKEIngressPlugin implements ITaskPlugin {
@TaskInput({ title: "大区", value: "ap-guangzhou", required: true })
region!: string;
@TaskInput({ title: "集群ID", required: true, desc: "例如cls-6lbj1vee", request: true })
clusterId!: string;
@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;
/**
* AccessProviderkey,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已成功获取");
const k8sClient = new K8sClient(kubeConfigStr);
if (clusterIp != null) {
if (!clusterDomain) {
clusterDomain = `${clusterId}.ccs.tencent-cloud.com`;
if (this.clusterIp != null) {
if (!this.clusterDomain) {
this.clusterDomain = `${this.clusterId}.ccs.tencent-cloud.com`;
}
// 修改内网解析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") {
await this.patchQcloudCertSecret({ k8sClient, input });
await this.patchQcloudCertSecret({ k8sClient });
} else {
await this.patchNginxCertSecret({ k8sClient, input });
await this.patchNginxCertSecret({ k8sClient });
}
await utils.sleep(2000); // 停留2秒等待secret部署完成
await this.restartIngress({ k8sClient, input });
return {};
await this.restartIngress({ k8sClient });
}
getTkeClient(accessProvider: any, region = "ap-guangzhou") {
@ -154,15 +150,14 @@ export class DeployCertToTencentTKEIngressPlugin extends AbstractPlugin implemen
return name + "-" + dayjs().format("YYYYMMDD-HHmmss");
}
async patchQcloudCertSecret(options: { k8sClient: any; input: TaskInput }) {
const { tencentCertId } = options.input;
if (tencentCertId == null) {
async patchQcloudCertSecret(options: { k8sClient: any }) {
if (this.tencentCertId == null) {
throw new Error("请先将【上传证书到腾讯云】作为前置任务");
}
this.logger.info("腾讯云证书ID:", tencentCertId);
const certIdBase64 = Buffer.from(tencentCertId).toString("base64");
this.logger.info("腾讯云证书ID:", this.tencentCertId);
const certIdBase64 = Buffer.from(this.tencentCertId).toString("base64");
const { namespace, secretName } = options.input;
const { namespace, secretName } = this;
const body = {
data: {
@ -174,7 +169,7 @@ export class DeployCertToTencentTKEIngressPlugin extends AbstractPlugin implemen
},
},
};
let secretNames = secretName;
let secretNames: any = secretName;
if (typeof secretName === "string") {
secretNames = [secretName];
}
@ -184,15 +179,15 @@ export class DeployCertToTencentTKEIngressPlugin extends AbstractPlugin implemen
}
}
async patchNginxCertSecret(options: { k8sClient: any; input: TaskInput }) {
const { k8sClient, input } = options;
const { cert } = input;
async patchNginxCertSecret(options: { k8sClient: any }) {
const { k8sClient } = options;
const { cert } = this;
const crt = cert.crt;
const key = cert.key;
const crtBase64 = Buffer.from(crt).toString("base64");
const keyBase64 = Buffer.from(key).toString("base64");
const { namespace, secretName } = input;
const { namespace, secretName } = this;
const body = {
data: {
@ -215,9 +210,9 @@ export class DeployCertToTencentTKEIngressPlugin extends AbstractPlugin implemen
}
}
async restartIngress(options: { k8sClient: any; input: TaskInput }) {
const { k8sClient, input } = options;
const { namespace, ingressName } = input;
async restartIngress(options: { k8sClient: any }) {
const { k8sClient } = options;
const { namespace, ingressName } = this;
const body = {
metadata: {
@ -226,7 +221,7 @@ export class DeployCertToTencentTKEIngressPlugin extends AbstractPlugin implemen
},
},
};
let ingressNames = ingressName;
let ingressNames = this.ingressName;
if (typeof ingressName === "string") {
ingressNames = [ingressName];
}

View File

@ -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 dayjs from "dayjs";
@IsTask(() => {
return {
name: "UploadCertToTencent",
title: "上传证书到腾讯云",
desc: "上传成功后输出tencentCertId",
input: {
name: {
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,
},
@IsTaskPlugin({
name: "UploadCertToTencent",
title: "上传证书到腾讯云",
desc: "上传成功后输出tencentCertId",
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
output: {
tencentCertId: {
title: "上传成功后的腾讯云CertId",
},
},
};
},
})
export class UploadToTencentPlugin extends AbstractPlugin implements TaskPlugin {
async execute(input: TaskInput): Promise<TaskOutput> {
const { accessId, name, cert } = input;
export class UploadToTencentPlugin implements ITaskPlugin {
@TaskInput({ title: "证书名称" })
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 certName = this.appendTimeSuffix(name || cert.domain);
const client = this.getClient(accessProvider);
@ -56,7 +65,8 @@ export class UploadToTencentPlugin extends AbstractPlugin implements TaskPlugin
const ret = await client.UploadCertificate(params);
this.checkRet(ret);
this.logger.info("证书上传成功tencentCertId=", ret.CertificateId);
return { tencentCertId: ret.CertificateId };
this.tencentCertId = ret.CertificateId;
}
appendTimeSuffix(name: string) {

View File

@ -15,6 +15,7 @@
"kubernetes-client": "^9.0.0"
},
"devDependencies": {
"log4js": "^6.7.1",
"@types/lodash": "^4.14.186",
"vue-tsc": "^0.38.9",
"@alicloud/cs20151215": "^3.0.3",

@ -1 +1 @@
Subproject commit f508b6426b5869ae0fd33d8ec8e170e097eed9d4
Subproject commit 08f08022903aed6ba5150a80ea3484a8bc1830b7