mirror of https://github.com/certd/certd
feat: midway注解方式编写插件
parent
52522f27e9
commit
dcd1023a39
|
@ -6,7 +6,7 @@ export type AccessInputDefine = FormItemProps & {
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
};
|
};
|
||||||
export type AccessDefine = Registrable & {
|
export type AccessDefine = Registrable & {
|
||||||
inputs?: {
|
input?: {
|
||||||
[key: string]: AccessInputDefine;
|
[key: string]: AccessInputDefine;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@ export function IsAccess(define: AccessDefine): ClassDecorator {
|
||||||
inputs[property] = input;
|
inputs[property] = input;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_.merge(define, { inputs });
|
_.merge(define, { input: inputs });
|
||||||
Reflect.defineMetadata(ACCESS_CLASS_KEY, define, target);
|
Reflect.defineMetadata(ACCESS_CLASS_KEY, define, target);
|
||||||
target.define = define;
|
target.define = define;
|
||||||
accessRegistry.register(define.name, {
|
accessRegistry.register(define.name, {
|
||||||
|
@ -30,9 +30,9 @@ export function IsAccess(define: AccessDefine): ClassDecorator {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function IsAccessInput(input?: AccessInputDefine): PropertyDecorator {
|
export function AccessInput(input?: AccessInputDefine): PropertyDecorator {
|
||||||
return (target, propertyKey) => {
|
return (target, propertyKey) => {
|
||||||
target = Decorator.target(target);
|
target = Decorator.target(target, propertyKey);
|
||||||
// const _type = Reflect.getMetadata("design:type", target, propertyKey);
|
// const _type = Reflect.getMetadata("design:type", target, propertyKey);
|
||||||
Reflect.defineMetadata(ACCESS_INPUT_KEY, input, target, propertyKey);
|
Reflect.defineMetadata(ACCESS_INPUT_KEY, input, target, propertyKey);
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,6 +9,7 @@ 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 { RegistryItem } from "../registry";
|
import { RegistryItem } from "../registry";
|
||||||
|
import { Decorator } from "../decorator";
|
||||||
|
|
||||||
export class Executor {
|
export class Executor {
|
||||||
userId: any;
|
userId: any;
|
||||||
|
@ -149,21 +150,17 @@ export class Executor {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const define: PluginDefine = plugin.define;
|
const define: PluginDefine = plugin.define;
|
||||||
//从outputContext读取输入参数
|
//从outputContext读取输入参数
|
||||||
_.forEach(define.input, (item, key) => {
|
Decorator.inject(define.input, instance, step.input, (item, key) => {
|
||||||
if (item.component?.name === "pi-output-selector") {
|
if (item.component?.name === "pi-output-selector") {
|
||||||
const contextKey = step.input[key];
|
const contextKey = step.input[key];
|
||||||
if (contextKey != null) {
|
if (contextKey != null) {
|
||||||
step.input[key] = this.runtime.context[contextKey];
|
step.input[key] = this.runtime.context[contextKey];
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
instance[key] = step.input[key];
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_.forEach(define.autowire, (item, key: string) => {
|
Decorator.inject(define.autowire, instance, context);
|
||||||
instance[key] = context[key];
|
await instance.onInit();
|
||||||
});
|
|
||||||
|
|
||||||
await instance.execute();
|
await instance.execute();
|
||||||
|
|
||||||
//输出到output context
|
//输出到output context
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { Decorator } from "./index";
|
||||||
|
|
||||||
|
export type AutowireProp = {
|
||||||
|
name?: string;
|
||||||
|
type?: any;
|
||||||
|
};
|
||||||
|
export const AUTOWIRE_KEY = "pipeline:autowire";
|
||||||
|
|
||||||
|
export function Autowire(props?: AutowireProp): PropertyDecorator {
|
||||||
|
return (target, propertyKey) => {
|
||||||
|
const _type = Reflect.getMetadata("design:type", target, propertyKey);
|
||||||
|
target = Decorator.target(target, propertyKey);
|
||||||
|
props = props || {};
|
||||||
|
props.type = _type;
|
||||||
|
Reflect.defineMetadata(AUTOWIRE_KEY, props || {}, target, propertyKey);
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,28 +1,2 @@
|
||||||
const propertyMap: any = {};
|
export * from "./utils";
|
||||||
function attachProperty(target: any, propertyKey: string | symbol) {
|
export * from "./common";
|
||||||
let props = propertyMap[target];
|
|
||||||
if (props == null) {
|
|
||||||
props = {};
|
|
||||||
propertyMap[target] = props;
|
|
||||||
}
|
|
||||||
props[propertyKey] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getClassProperties(target: any) {
|
|
||||||
return propertyMap[target] || {};
|
|
||||||
}
|
|
||||||
|
|
||||||
function target(target: any, propertyKey?: string | symbol) {
|
|
||||||
if (typeof target === "object" && target.constructor) {
|
|
||||||
target = target.constructor;
|
|
||||||
}
|
|
||||||
if (propertyKey != null) {
|
|
||||||
attachProperty(target, propertyKey);
|
|
||||||
}
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
export const Decorator = {
|
|
||||||
target,
|
|
||||||
attachProperty,
|
|
||||||
getClassProperties,
|
|
||||||
};
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
|
const propertyMap: any = {};
|
||||||
|
function attachProperty(target: any, propertyKey: string | symbol) {
|
||||||
|
let props = propertyMap[target];
|
||||||
|
if (props == null) {
|
||||||
|
props = {};
|
||||||
|
propertyMap[target] = props;
|
||||||
|
}
|
||||||
|
props[propertyKey] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClassProperties(target: any) {
|
||||||
|
return propertyMap[target] || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function target(target: any, propertyKey?: string | symbol) {
|
||||||
|
if (typeof target === "object" && target.constructor) {
|
||||||
|
target = target.constructor;
|
||||||
|
}
|
||||||
|
if (propertyKey != null) {
|
||||||
|
attachProperty(target, propertyKey);
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
function inject(define: any, instance: any, context: any, preHandler?: (item: any, key: string, instance: any, context: any) => void) {
|
||||||
|
_.forEach(define, (item, key) => {
|
||||||
|
if (preHandler) {
|
||||||
|
preHandler(item, key, instance, context);
|
||||||
|
}
|
||||||
|
if (context[key] != undefined) {
|
||||||
|
instance[key] = context[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export const Decorator = {
|
||||||
|
target,
|
||||||
|
attachProperty,
|
||||||
|
getClassProperties,
|
||||||
|
inject,
|
||||||
|
};
|
|
@ -1,22 +0,0 @@
|
||||||
import { AbstractRegistrable } from "../registry";
|
|
||||||
import { CreateRecordOptions, IDnsProvider, DnsProviderDefine, RemoveRecordOptions } from "./api";
|
|
||||||
import { AbstractAccess } from "../access";
|
|
||||||
import { Logger } from "log4js";
|
|
||||||
import { AxiosInstance } from "axios";
|
|
||||||
export abstract class AbstractDnsProvider extends AbstractRegistrable<DnsProviderDefine> implements IDnsProvider {
|
|
||||||
access!: AbstractAccess;
|
|
||||||
logger!: Logger;
|
|
||||||
http!: AxiosInstance;
|
|
||||||
doInit(options: { access: AbstractAccess; logger: Logger; http: AxiosInstance }) {
|
|
||||||
this.access = options.access;
|
|
||||||
this.logger = options.logger;
|
|
||||||
this.http = options.http;
|
|
||||||
this.onInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract onInit(): void;
|
|
||||||
|
|
||||||
abstract createRecord(options: CreateRecordOptions): Promise<any>;
|
|
||||||
|
|
||||||
abstract removeRecord(options: RemoveRecordOptions): Promise<any>;
|
|
||||||
}
|
|
|
@ -2,8 +2,8 @@ export * from "./core";
|
||||||
export * from "./d.ts";
|
export * from "./d.ts";
|
||||||
export * from "./access";
|
export * from "./access";
|
||||||
export * from "./registry";
|
export * from "./registry";
|
||||||
export * from "./dns-provider";
|
|
||||||
export * from "./plugin";
|
export * from "./plugin";
|
||||||
export * from "./utils";
|
export * from "./utils";
|
||||||
export * from "./midway";
|
export * from "./midway";
|
||||||
export * from "./context";
|
export * from "./context";
|
||||||
|
export * from "./decorator";
|
||||||
|
|
|
@ -2,6 +2,7 @@ import _ from "lodash";
|
||||||
import { pluginRegistry } from "./registry";
|
import { pluginRegistry } from "./registry";
|
||||||
import { PluginDefine, TaskInputDefine, TaskOutputDefine } from "./api";
|
import { PluginDefine, TaskInputDefine, TaskOutputDefine } from "./api";
|
||||||
import { Decorator } from "../decorator";
|
import { Decorator } from "../decorator";
|
||||||
|
import { AUTOWIRE_KEY } from "../decorator";
|
||||||
|
|
||||||
// 提供一个唯一 key
|
// 提供一个唯一 key
|
||||||
export const PLUGIN_CLASS_KEY = "pipeline:plugin";
|
export const PLUGIN_CLASS_KEY = "pipeline:plugin";
|
||||||
|
@ -20,7 +21,7 @@ export function IsTaskPlugin(define: PluginDefine): ClassDecorator {
|
||||||
inputs[property] = input;
|
inputs[property] = input;
|
||||||
}
|
}
|
||||||
|
|
||||||
const autowire = Reflect.getMetadata(PLUGIN_AUTOWIRE_KEY, target, property);
|
const autowire = Reflect.getMetadata(AUTOWIRE_KEY, target, property);
|
||||||
if (autowire) {
|
if (autowire) {
|
||||||
autowires[property] = autowire;
|
autowires[property] = autowire;
|
||||||
}
|
}
|
||||||
|
@ -59,19 +60,3 @@ export function TaskOutput(output?: TaskOutputDefine): PropertyDecorator {
|
||||||
Reflect.defineMetadata(PLUGIN_OUTPUT_KEY, output, target, propertyKey);
|
Reflect.defineMetadata(PLUGIN_OUTPUT_KEY, output, target, propertyKey);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AutowireProp = {
|
|
||||||
name?: string;
|
|
||||||
type?: any;
|
|
||||||
};
|
|
||||||
export const PLUGIN_AUTOWIRE_KEY = "pipeline:plugin:autowire";
|
|
||||||
|
|
||||||
export function Autowire(props?: AutowireProp): PropertyDecorator {
|
|
||||||
return (target, propertyKey) => {
|
|
||||||
const _type = Reflect.getMetadata("design:type", target, propertyKey);
|
|
||||||
target = Decorator.target(target, propertyKey);
|
|
||||||
props = props || {};
|
|
||||||
props.type = _type;
|
|
||||||
Reflect.defineMetadata(PLUGIN_AUTOWIRE_KEY, props || {}, target, propertyKey);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ export class Registry {
|
||||||
this.storage[key] = value;
|
this.storage[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get(name: string) {
|
get(name: string): RegistryItem {
|
||||||
if (!name) {
|
if (!name) {
|
||||||
throw new Error("插件名称不能为空");
|
throw new Error("插件名称不能为空");
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,4 +32,4 @@ export function buildLogger(write: (text: string) => void) {
|
||||||
});
|
});
|
||||||
return logger;
|
return logger;
|
||||||
}
|
}
|
||||||
export type LOGGER = Logger;
|
export type ILogger = Logger;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { IsTaskPlugin, TaskInput, ITaskPlugin, LOGGER, Autowire, TaskOutput } from "../src";
|
import { IsTaskPlugin, TaskInput, ITaskPlugin, ILogger, Autowire, TaskOutput } from "../src";
|
||||||
|
|
||||||
@IsTaskPlugin({
|
@IsTaskPlugin({
|
||||||
name: "EchoPlugin",
|
name: "EchoPlugin",
|
||||||
|
@ -15,7 +15,7 @@ export class EchoPlugin implements ITaskPlugin {
|
||||||
cert!: any;
|
cert!: any;
|
||||||
|
|
||||||
@Autowire()
|
@Autowire()
|
||||||
logger!: LOGGER;
|
logger!: ILogger;
|
||||||
|
|
||||||
@TaskOutput({
|
@TaskOutput({
|
||||||
title: "cert info",
|
title: "cert info",
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
"@typescript-eslint/ban-ts-ignore": "off",
|
"@typescript-eslint/ban-ts-ignore": "off",
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
// "no-unused-expressions": "off",
|
// "no-unused-expressions": "off",
|
||||||
"max-len": [0, 160, 2, { "ignoreUrls": true }]
|
"max-len": [0, 160, 2, { "ignoreUrls": true }]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { IsAccess, IsAccessInput } from "@certd/pipeline";
|
import { IsAccess, AccessInput } from "@certd/pipeline";
|
||||||
|
|
||||||
@IsAccess({
|
@IsAccess({
|
||||||
name: "aliyun",
|
name: "aliyun",
|
||||||
|
@ -6,7 +6,7 @@ import { IsAccess, IsAccessInput } from "@certd/pipeline";
|
||||||
desc: "",
|
desc: "",
|
||||||
})
|
})
|
||||||
export class AliyunAccess {
|
export class AliyunAccess {
|
||||||
@IsAccessInput({
|
@AccessInput({
|
||||||
title: "accessKeyId",
|
title: "accessKeyId",
|
||||||
component: {
|
component: {
|
||||||
placeholder: "accessKeyId",
|
placeholder: "accessKeyId",
|
||||||
|
@ -14,7 +14,7 @@ export class AliyunAccess {
|
||||||
required: true,
|
required: true,
|
||||||
})
|
})
|
||||||
accessKeyId = "";
|
accessKeyId = "";
|
||||||
@IsAccessInput({
|
@AccessInput({
|
||||||
title: "accessKeySecret",
|
title: "accessKeySecret",
|
||||||
component: {
|
component: {
|
||||||
placeholder: "accessKeySecret",
|
placeholder: "accessKeySecret",
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import Core from "@alicloud/pop-core";
|
import Core from "@alicloud/pop-core";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { CreateRecordOptions, IDnsProvider, IsDnsProvider, RemoveRecordOptions } from "@certd/pipeline";
|
import { CreateRecordOptions, IDnsProvider, IsDnsProvider, RemoveRecordOptions } from "@certd/plugin-cert";
|
||||||
import { Logger } from "log4js";
|
import { Autowire, ILogger } from "@certd/pipeline";
|
||||||
|
import { AliyunAccess } from "../access";
|
||||||
|
|
||||||
@IsDnsProvider({
|
@IsDnsProvider({
|
||||||
name: "aliyun",
|
name: "aliyun",
|
||||||
|
@ -11,8 +12,10 @@ import { Logger } from "log4js";
|
||||||
})
|
})
|
||||||
export class AliyunDnsProvider implements IDnsProvider {
|
export class AliyunDnsProvider implements IDnsProvider {
|
||||||
client: any;
|
client: any;
|
||||||
access: any;
|
@Autowire()
|
||||||
logger!: Logger;
|
access!: AliyunAccess;
|
||||||
|
@Autowire()
|
||||||
|
logger!: ILogger;
|
||||||
async onInit() {
|
async onInit() {
|
||||||
const access: any = this.access;
|
const access: any = this.access;
|
||||||
this.client = new Core({
|
this.client = new Core({
|
||||||
|
|
|
@ -1,101 +1,119 @@
|
||||||
import { AbstractPlugin, IsTask, RunStrategy, TaskInput, TaskOutput, TaskPlugin, utils } from "@certd/pipeline";
|
import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, ILogger, RunStrategy, TaskInput, utils } from "@certd/pipeline";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { ROAClient } from "@alicloud/pop-core";
|
import { ROAClient } from "@alicloud/pop-core";
|
||||||
import { AliyunAccess } from "../../access";
|
import { AliyunAccess } from "../../access";
|
||||||
import { K8sClient } from "@certd/plugin-util";
|
import { K8sClient } from "@certd/plugin-util";
|
||||||
import { appendTimeSuffix } from "../../utils";
|
import { appendTimeSuffix } from "../../utils";
|
||||||
|
import { CertInfo } from "@certd/plugin-cert";
|
||||||
|
|
||||||
@IsTask(() => {
|
@IsTaskPlugin({
|
||||||
return {
|
name: "DeployCertToAliyunAckIngress",
|
||||||
name: "DeployCertToAliyunAckIngress",
|
title: "部署到阿里云AckIngress",
|
||||||
title: "部署到阿里云AckIngress",
|
input: {},
|
||||||
input: {
|
output: {},
|
||||||
clusterId: {
|
default: {
|
||||||
title: "集群id",
|
strategy: {
|
||||||
component: {
|
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||||
placeholder: "集群id",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
secretName: {
|
|
||||||
title: "保密字典Id",
|
|
||||||
component: {
|
|
||||||
placeholder: "保密字典Id",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
regionId: {
|
|
||||||
title: "大区",
|
|
||||||
value: "cn-shanghai",
|
|
||||||
component: {
|
|
||||||
placeholder: "集群所属大区",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
namespace: {
|
|
||||||
title: "命名空间",
|
|
||||||
value: "default",
|
|
||||||
component: {
|
|
||||||
placeholder: "命名空间",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
ingressName: {
|
|
||||||
title: "ingress名称",
|
|
||||||
value: "",
|
|
||||||
component: {
|
|
||||||
placeholder: "ingress名称",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
helper: "可以传入一个数组",
|
|
||||||
},
|
|
||||||
ingressClass: {
|
|
||||||
title: "ingress类型",
|
|
||||||
value: "nginx",
|
|
||||||
component: {
|
|
||||||
placeholder: "暂时只支持nginx类型",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
isPrivateIpAddress: {
|
|
||||||
title: "是否私网ip",
|
|
||||||
value: false,
|
|
||||||
component: {
|
|
||||||
placeholder: "集群连接端点是否是私网ip",
|
|
||||||
},
|
|
||||||
helper: "如果您当前certd运行在同一个私网下,可以选择是。",
|
|
||||||
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: {},
|
},
|
||||||
default: {
|
|
||||||
strategy: {
|
|
||||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
export class DeployCertToAliyunAckIngressPlugin extends AbstractPlugin implements TaskPlugin {
|
export class DeployCertToAliyunAckIngressPlugin implements ITaskPlugin {
|
||||||
async execute(input: TaskInput): Promise<TaskOutput> {
|
@TaskInput({
|
||||||
|
title: "集群id",
|
||||||
|
component: {
|
||||||
|
placeholder: "集群id",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
clusterId!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "保密字典Id",
|
||||||
|
component: {
|
||||||
|
placeholder: "保密字典Id",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
secretName!: string | string[];
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "大区",
|
||||||
|
value: "cn-shanghai",
|
||||||
|
component: {
|
||||||
|
placeholder: "集群所属大区",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
regionId!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "命名空间",
|
||||||
|
value: "default",
|
||||||
|
component: {
|
||||||
|
placeholder: "命名空间",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
namespace!: string;
|
||||||
|
@TaskInput({
|
||||||
|
title: "ingress名称",
|
||||||
|
value: "",
|
||||||
|
component: {
|
||||||
|
placeholder: "ingress名称",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
helper: "可以传入一个数组",
|
||||||
|
})
|
||||||
|
ingressName!: string;
|
||||||
|
@TaskInput({
|
||||||
|
title: "ingress类型",
|
||||||
|
value: "nginx",
|
||||||
|
component: {
|
||||||
|
placeholder: "暂时只支持nginx类型",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
ingressClass!: string;
|
||||||
|
@TaskInput({
|
||||||
|
title: "是否私网ip",
|
||||||
|
value: false,
|
||||||
|
component: {
|
||||||
|
placeholder: "集群连接端点是否是私网ip",
|
||||||
|
},
|
||||||
|
helper: "如果您当前certd运行在同一个私网下,可以选择是。",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
isPrivateIpAddress!: boolean;
|
||||||
|
@TaskInput({
|
||||||
|
title: "域名证书",
|
||||||
|
helper: "请选择前置任务输出的域名证书",
|
||||||
|
component: {
|
||||||
|
name: "pi-output-selector",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
cert!: CertInfo;
|
||||||
|
@TaskInput({
|
||||||
|
title: "Access授权",
|
||||||
|
helper: "阿里云授权AccessKeyId、AccessKeySecret",
|
||||||
|
component: {
|
||||||
|
name: "pi-access-selector",
|
||||||
|
type: "aliyun",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
accessId!: string;
|
||||||
|
|
||||||
|
@Autowire()
|
||||||
|
accessService!: IAccessService;
|
||||||
|
@Autowire()
|
||||||
|
logger!: ILogger;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
async onInit(): Promise<void> {}
|
||||||
|
async execute(): Promise<void> {
|
||||||
console.log("开始部署证书到阿里云cdn");
|
console.log("开始部署证书到阿里云cdn");
|
||||||
const { regionId, ingressClass, clusterId, isPrivateIpAddress, cert } = input;
|
const { regionId, ingressClass, clusterId, isPrivateIpAddress, cert } = this;
|
||||||
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, regionId);
|
const client = this.getClient(access, regionId);
|
||||||
const kubeConfigStr = await this.getKubeConfig(client, clusterId, isPrivateIpAddress);
|
const kubeConfigStr = await this.getKubeConfig(client, clusterId, isPrivateIpAddress);
|
||||||
|
|
||||||
|
@ -106,17 +124,16 @@ export class DeployCertToAliyunAckIngressPlugin extends AbstractPlugin implement
|
||||||
throw new Error("暂未实现");
|
throw new Error("暂未实现");
|
||||||
// await this.patchQcloudCertSecret({ k8sClient, props, context })
|
// await this.patchQcloudCertSecret({ k8sClient, props, context })
|
||||||
} else {
|
} else {
|
||||||
await this.patchNginxCertSecret({ cert, k8sClient, input });
|
await this.patchNginxCertSecret({ cert, k8sClient });
|
||||||
}
|
}
|
||||||
|
|
||||||
await utils.sleep(3000); // 停留2秒,等待secret部署完成
|
await utils.sleep(3000); // 停留2秒,等待secret部署完成
|
||||||
// await this.restartIngress({ k8sClient, props })
|
// await this.restartIngress({ k8sClient, props })
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async restartIngress(options: { k8sClient: any; input: TaskInput }) {
|
async restartIngress(options: { k8sClient: any }) {
|
||||||
const { k8sClient, input } = options;
|
const { k8sClient } = options;
|
||||||
const { namespace } = input;
|
const { namespace } = this;
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -136,7 +153,7 @@ export class DeployCertToAliyunAckIngressPlugin extends AbstractPlugin implement
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (const tls of item.spec.tls) {
|
for (const tls of item.spec.tls) {
|
||||||
if (tls.secretName === input.secretName) {
|
if (tls.secretName === this.secretName) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,14 +168,14 @@ export class DeployCertToAliyunAckIngressPlugin extends AbstractPlugin implement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async patchNginxCertSecret(options: { cert: any; k8sClient: any; input: TaskInput }) {
|
async patchNginxCertSecret(options: { cert: any; k8sClient: any }) {
|
||||||
const { cert, k8sClient, input } = options;
|
const { cert, k8sClient } = options;
|
||||||
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: {
|
||||||
|
@ -171,7 +188,7 @@ export class DeployCertToAliyunAckIngressPlugin extends AbstractPlugin implement
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let secretNames = secretName;
|
let secretNames: any = secretName;
|
||||||
if (typeof secretName === "string") {
|
if (typeof secretName === "string") {
|
||||||
secretNames = [secretName];
|
secretNames = [secretName];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, LOGGER, RunStrategy, TaskInput } from "@certd/pipeline";
|
import { Autowire, IAccessService, ILogger, IsTaskPlugin, ITaskPlugin, 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";
|
|
||||||
|
|
||||||
@IsTaskPlugin({
|
@IsTaskPlugin({
|
||||||
name: "DeployCertToAliyunCDN",
|
name: "DeployCertToAliyunCDN",
|
||||||
|
@ -50,11 +49,11 @@ export class DeployCertToAliyunCDN implements ITaskPlugin {
|
||||||
})
|
})
|
||||||
accessId!: string;
|
accessId!: string;
|
||||||
|
|
||||||
@Inject()
|
@Autowire()
|
||||||
accessService!: IAccessService;
|
accessService!: IAccessService;
|
||||||
|
|
||||||
@Autowire()
|
@Autowire()
|
||||||
logger!: LOGGER;
|
logger!: ILogger;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
async onInit() {}
|
async onInit() {}
|
||||||
async execute(): Promise<void> {
|
async execute(): Promise<void> {
|
||||||
|
|
|
@ -17,7 +17,6 @@ import { Logger } from "log4js";
|
||||||
export class UploadCertToAliyun implements ITaskPlugin {
|
export class UploadCertToAliyun implements ITaskPlugin {
|
||||||
@TaskInput({
|
@TaskInput({
|
||||||
title: "证书名称",
|
title: "证书名称",
|
||||||
|
|
||||||
helper: "证书上传后将以此参数作为名称前缀",
|
helper: "证书上传后将以此参数作为名称前缀",
|
||||||
})
|
})
|
||||||
name!: string;
|
name!: string;
|
||||||
|
@ -66,6 +65,9 @@ export class UploadCertToAliyun implements ITaskPlugin {
|
||||||
@Autowire()
|
@Autowire()
|
||||||
logger!: Logger;
|
logger!: Logger;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
async onInit() {}
|
||||||
|
|
||||||
async execute(): Promise<void> {
|
async execute(): Promise<void> {
|
||||||
console.log("开始部署证书到阿里云cdn");
|
console.log("开始部署证书到阿里云cdn");
|
||||||
const access = (await this.accessService.getById(this.accessId)) as AliyunAccess;
|
const access = (await this.accessService.getById(this.accessId)) as AliyunAccess;
|
||||||
|
|
|
@ -1,199 +0,0 @@
|
||||||
import { AbstractAliyunPlugin } from '../abstract-aliyun.js'
|
|
||||||
import Core from '@alicloud/pop-core'
|
|
||||||
import { K8sClient } from '@certd/plugin-common'
|
|
||||||
const ROAClient = Core.ROAClient
|
|
||||||
|
|
||||||
const define = {
|
|
||||||
name: 'deployCertToAliyunAckIngress',
|
|
||||||
title: '部署到阿里云AckIngress',
|
|
||||||
input: {
|
|
||||||
clusterId: {
|
|
||||||
title: '集群id',
|
|
||||||
component: {
|
|
||||||
placeholder: '集群id'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
secretName: {
|
|
||||||
title: '保密字典Id',
|
|
||||||
component: {
|
|
||||||
placeholder: '保密字典Id'
|
|
||||||
},
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
regionId: {
|
|
||||||
title: '大区',
|
|
||||||
value: 'cn-shanghai',
|
|
||||||
component: {
|
|
||||||
placeholder: '集群所属大区'
|
|
||||||
},
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
namespace: {
|
|
||||||
title: '命名空间',
|
|
||||||
value: 'default',
|
|
||||||
component: {
|
|
||||||
placeholder: '命名空间'
|
|
||||||
},
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
ingressName: {
|
|
||||||
title: 'ingress名称',
|
|
||||||
value: '',
|
|
||||||
component: {
|
|
||||||
placeholder: 'ingress名称'
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
helper: '可以传入一个数组'
|
|
||||||
},
|
|
||||||
ingressClass: {
|
|
||||||
title: 'ingress类型',
|
|
||||||
value: 'nginx',
|
|
||||||
component: {
|
|
||||||
placeholder: '暂时只支持nginx类型'
|
|
||||||
},
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
isPrivateIpAddress: {
|
|
||||||
title: '是否私网ip',
|
|
||||||
value: false,
|
|
||||||
component: {
|
|
||||||
placeholder: '集群连接端点是否是私网ip'
|
|
||||||
},
|
|
||||||
helper: '如果您当前certd运行在同一个私网下,可以选择是。',
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
accessProvider: {
|
|
||||||
title: 'Access授权',
|
|
||||||
type: [String, Object],
|
|
||||||
helper: 'AccessKey、AccessSecret',
|
|
||||||
component: {
|
|
||||||
name: 'access-selector',
|
|
||||||
type: 'aliyun'
|
|
||||||
},
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DeployCertToAliyunAckIngress extends AbstractAliyunPlugin {
|
|
||||||
static define () {
|
|
||||||
return define
|
|
||||||
}
|
|
||||||
|
|
||||||
async execute ({ cert, props, context }) {
|
|
||||||
const accessProvider = this.getAccessProvider(props.accessProvider)
|
|
||||||
const client = this.getClient(accessProvider, props.regionId)
|
|
||||||
|
|
||||||
const kubeConfigStr = await this.getKubeConfig(client, props.clusterId, props.isPrivateIpAddress)
|
|
||||||
|
|
||||||
this.logger.info('kubeconfig已成功获取')
|
|
||||||
const k8sClient = new K8sClient(kubeConfigStr)
|
|
||||||
const ingressType = props.ingressClass || 'qcloud'
|
|
||||||
if (ingressType === 'qcloud') {
|
|
||||||
throw new Error('暂未实现')
|
|
||||||
// await this.patchQcloudCertSecret({ k8sClient, props, context })
|
|
||||||
} else {
|
|
||||||
await this.patchNginxCertSecret({ cert, k8sClient, props, context })
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.sleep(3000) // 停留2秒,等待secret部署完成
|
|
||||||
// await this.restartIngress({ k8sClient, props })
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
async restartIngress ({ k8sClient, props }) {
|
|
||||||
const { namespace } = props
|
|
||||||
|
|
||||||
const body = {
|
|
||||||
metadata: {
|
|
||||||
labels: {
|
|
||||||
certd: this.appendTimeSuffix('certd')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const ingressList = await k8sClient.getIngressList({ namespace })
|
|
||||||
console.log('ingressList:', ingressList)
|
|
||||||
if (!ingressList || !ingressList.body || !ingressList.body.items) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const ingressNames = ingressList.body.items.filter(item => {
|
|
||||||
if (!item.spec.tls) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for (const tls of item.spec.tls) {
|
|
||||||
if (tls.secretName === props.secretName) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}).map(item => {
|
|
||||||
return item.metadata.name
|
|
||||||
})
|
|
||||||
for (const ingress of ingressNames) {
|
|
||||||
await k8sClient.patchIngress({ namespace, ingressName: ingress, body })
|
|
||||||
this.logger.info(`ingress已重启:${ingress}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async patchNginxCertSecret ({ cert, k8sClient, props, context }) {
|
|
||||||
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 } = props
|
|
||||||
|
|
||||||
const body = {
|
|
||||||
data: {
|
|
||||||
'tls.crt': crtBase64,
|
|
||||||
'tls.key': keyBase64
|
|
||||||
},
|
|
||||||
metadata: {
|
|
||||||
labels: {
|
|
||||||
certd: this.appendTimeSuffix('certd')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let secretNames = secretName
|
|
||||||
if (typeof secretName === 'string') {
|
|
||||||
secretNames = [secretName]
|
|
||||||
}
|
|
||||||
for (const secret of secretNames) {
|
|
||||||
await k8sClient.patchSecret({ namespace, secretName: secret, body })
|
|
||||||
this.logger.info(`CertSecret已更新:${secret}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getClient (aliyunProvider, regionId) {
|
|
||||||
return new ROAClient({
|
|
||||||
accessKeyId: aliyunProvider.accessKeyId,
|
|
||||||
accessKeySecret: aliyunProvider.accessKeySecret,
|
|
||||||
endpoint: `https://cs.${regionId}.aliyuncs.com`,
|
|
||||||
apiVersion: '2015-12-15'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async getKubeConfig (client, clusterId, isPrivateIpAddress = false) {
|
|
||||||
const httpMethod = 'GET'
|
|
||||||
const uriPath = `/k8s/${clusterId}/user_config`
|
|
||||||
const queries = {
|
|
||||||
PrivateIpAddress: isPrivateIpAddress
|
|
||||||
}
|
|
||||||
const body = '{}'
|
|
||||||
const headers = {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
}
|
|
||||||
const requestOption = {}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await client.request(httpMethod, uriPath, queries, body, headers, requestOption)
|
|
||||||
return res.config
|
|
||||||
} catch (e) {
|
|
||||||
console.error('请求出错:', e)
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
import Core from '@alicloud/pop-core'
|
|
||||||
import { AbstractAliyunPlugin } from '../abstract-aliyun.js'
|
|
||||||
import { ZoneOptions } from '../../utils/index.js'
|
|
||||||
|
|
||||||
const define = {
|
|
||||||
name: 'uploadCertToAliyun',
|
|
||||||
title: '上传证书到阿里云',
|
|
||||||
desc: '',
|
|
||||||
input: {
|
|
||||||
name: {
|
|
||||||
title: '证书名称',
|
|
||||||
helper: '证书上传后将以此参数作为名称前缀'
|
|
||||||
},
|
|
||||||
regionId: {
|
|
||||||
title: '大区',
|
|
||||||
value: 'cn-hangzhou',
|
|
||||||
component: {
|
|
||||||
name: 'a-select',
|
|
||||||
vModel: 'value',
|
|
||||||
options: ZoneOptions
|
|
||||||
},
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
accessProvider: {
|
|
||||||
title: 'Access授权',
|
|
||||||
helper: 'Access授权',
|
|
||||||
component: {
|
|
||||||
name: 'access-selector',
|
|
||||||
type: 'aliyun'
|
|
||||||
},
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
aliyunCertId: {
|
|
||||||
type: String,
|
|
||||||
desc: '上传成功后的阿里云CertId'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UploadCertToAliyun extends AbstractAliyunPlugin {
|
|
||||||
static define () {
|
|
||||||
return define
|
|
||||||
}
|
|
||||||
|
|
||||||
getClient (aliyunProvider) {
|
|
||||||
return new Core({
|
|
||||||
accessKeyId: aliyunProvider.accessKeyId,
|
|
||||||
accessKeySecret: aliyunProvider.accessKeySecret,
|
|
||||||
endpoint: 'https://cas.aliyuncs.com',
|
|
||||||
apiVersion: '2018-07-13'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async execute ({ cert, props, context }) {
|
|
||||||
const { name, accessProvider } = props
|
|
||||||
const certName = this.appendTimeSuffix(name || cert.domain)
|
|
||||||
const params = {
|
|
||||||
RegionId: props.regionId || 'cn-hangzhou',
|
|
||||||
Name: certName,
|
|
||||||
Cert: cert.crt,
|
|
||||||
Key: cert.key
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestOption = {
|
|
||||||
method: 'POST'
|
|
||||||
}
|
|
||||||
|
|
||||||
const provider = this.getAccessProvider(accessProvider)
|
|
||||||
const client = this.getClient(provider)
|
|
||||||
const ret = await client.request('CreateUserCertificate', params, requestOption)
|
|
||||||
this.checkRet(ret)
|
|
||||||
this.logger.info('证书上传成功:aliyunCertId=', ret.CertId)
|
|
||||||
context.aliyunCertId = ret.CertId
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 没用,现在阿里云证书不允许删除
|
|
||||||
* @param accessProviders
|
|
||||||
* @param cert
|
|
||||||
* @param props
|
|
||||||
* @param context
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
async rollback ({ cert, props, context }) {
|
|
||||||
const { accessProvider } = props
|
|
||||||
const { aliyunCertId } = context
|
|
||||||
this.logger.info('准备删除阿里云证书:', aliyunCertId)
|
|
||||||
const params = {
|
|
||||||
RegionId: props.regionId || 'cn-hangzhou',
|
|
||||||
CertId: aliyunCertId
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestOption = {
|
|
||||||
method: 'POST'
|
|
||||||
}
|
|
||||||
|
|
||||||
const provider = this.getAccessProvider(accessProvider)
|
|
||||||
const client = this.getClient(provider)
|
|
||||||
const ret = await client.request('DeleteUserCertificate', params, requestOption)
|
|
||||||
this.checkRet(ret)
|
|
||||||
this.logger.info('证书删除成功:', aliyunCertId)
|
|
||||||
delete context.aliyunCertId
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,6 +15,7 @@
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
"@typescript-eslint/ban-ts-ignore": "off",
|
"@typescript-eslint/ban-ts-ignore": "off",
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
// "no-unused-expressions": "off",
|
// "no-unused-expressions": "off",
|
||||||
"max-len": [0, 160, 2, { "ignoreUrls": true }]
|
"max-len": [0, 160, 2, { "ignoreUrls": true }]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// export * from "@certd/plugin-aliyun";
|
|
||||||
export * from "@certd/plugin-cert";
|
export * from "@certd/plugin-cert";
|
||||||
|
export * from "@certd/plugin-aliyun";
|
||||||
export * from "@certd/plugin-tencent";
|
export * from "@certd/plugin-tencent";
|
||||||
// export * from "@certd/plugin-host";
|
export * from "@certd/plugin-host";
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
"@typescript-eslint/ban-ts-ignore": "off",
|
"@typescript-eslint/ban-ts-ignore": "off",
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
// "no-unused-expressions": "off",
|
// "no-unused-expressions": "off",
|
||||||
"max-len": [0, 160, 2, { "ignoreUrls": true }]
|
"max-len": [0, 160, 2, { "ignoreUrls": true }]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import { Registrable } from "../registry";
|
import { Registrable } from "@certd/pipeline";
|
||||||
|
|
||||||
export type DnsProviderDefine = Registrable & {
|
export type DnsProviderDefine = Registrable & {
|
||||||
accessType: string;
|
accessType: string;
|
||||||
|
autowire?: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CreateRecordOptions = {
|
export type CreateRecordOptions = {
|
||||||
|
@ -14,6 +17,7 @@ export type RemoveRecordOptions = CreateRecordOptions & {
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IDnsProvider {
|
export interface IDnsProvider {
|
||||||
|
onInit(): Promise<void>;
|
||||||
createRecord(options: CreateRecordOptions): Promise<any>;
|
createRecord(options: CreateRecordOptions): Promise<any>;
|
||||||
removeRecord(options: RemoveRecordOptions): Promise<any>;
|
removeRecord(options: RemoveRecordOptions): Promise<any>;
|
||||||
}
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
// src/decorator/memoryCache.decorator.ts
|
// src/decorator/memoryCache.decorator.ts
|
||||||
import { dnsProviderRegistry } from "./registry";
|
import { dnsProviderRegistry } from "./registry";
|
||||||
import { DnsProviderDefine } from "./api";
|
import { DnsProviderDefine } from "./api";
|
||||||
import { Decorator } from "../decorator";
|
import { Decorator, AUTOWIRE_KEY } from "@certd/pipeline";
|
||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
// 提供一个唯一 key
|
// 提供一个唯一 key
|
||||||
export const DNS_PROVIDER_CLASS_KEY = "pipeline:dns-provider";
|
export const DNS_PROVIDER_CLASS_KEY = "pipeline:dns-provider";
|
||||||
|
@ -9,6 +10,16 @@ export const DNS_PROVIDER_CLASS_KEY = "pipeline:dns-provider";
|
||||||
export function IsDnsProvider(define: DnsProviderDefine): ClassDecorator {
|
export function IsDnsProvider(define: DnsProviderDefine): ClassDecorator {
|
||||||
return (target: any) => {
|
return (target: any) => {
|
||||||
target = Decorator.target(target);
|
target = Decorator.target(target);
|
||||||
|
const autowires: any = {};
|
||||||
|
const properties = Decorator.getClassProperties(target);
|
||||||
|
for (const property in properties) {
|
||||||
|
const autowire = Reflect.getMetadata(AUTOWIRE_KEY, target, property);
|
||||||
|
if (autowire) {
|
||||||
|
autowires[property] = autowire;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_.merge(define, { autowire: autowires });
|
||||||
|
|
||||||
Reflect.defineMetadata(DNS_PROVIDER_CLASS_KEY, define, target);
|
Reflect.defineMetadata(DNS_PROVIDER_CLASS_KEY, define, target);
|
||||||
|
|
||||||
target.define = define;
|
target.define = define;
|
|
@ -1,4 +1,4 @@
|
||||||
import { Registry } from "../registry";
|
import { Registry } from "@certd/pipeline";
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export const dnsProviderRegistry = new Registry();
|
export const dnsProviderRegistry = new Registry();
|
|
@ -1 +1,2 @@
|
||||||
export * from "./plugin";
|
export * from "./plugin";
|
||||||
|
export * from "./dns-provider";
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
// @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 { 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";
|
||||||
|
import { IDnsProvider } from "../../dns-provider";
|
||||||
|
|
||||||
export class AcmeService {
|
export class AcmeService {
|
||||||
userContext: IContext;
|
userContext: IContext;
|
||||||
|
|
|
@ -1,26 +1,46 @@
|
||||||
import {
|
import { Autowire, HttpClient, IAccessService, IContext, IsTaskPlugin, ITaskPlugin, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline";
|
||||||
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";
|
import { Logger } from "log4js";
|
||||||
|
import { Decorator } from "@certd/pipeline/src/decorator";
|
||||||
export type CertInfo = {
|
import { DnsProviderDefine, dnsProviderRegistry } from "../../dns-provider";
|
||||||
|
import fs from "fs";
|
||||||
|
import os from "os";
|
||||||
|
export class CertInfo {
|
||||||
crt: string;
|
crt: string;
|
||||||
key: string;
|
key: string;
|
||||||
csr: string;
|
csr: string;
|
||||||
};
|
|
||||||
|
detail: any;
|
||||||
|
expires: number;
|
||||||
|
constructor(opts: { crt: string; key: string; csr: string }) {
|
||||||
|
this.crt = opts.crt;
|
||||||
|
this.key = opts.key;
|
||||||
|
this.csr = opts.csr;
|
||||||
|
|
||||||
|
const { detail, expires } = this.getCrtDetail(this.crt);
|
||||||
|
this.detail = detail;
|
||||||
|
this.expires = expires.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
getCrtDetail(crt: string) {
|
||||||
|
const pki = forge.pki;
|
||||||
|
const detail = pki.certificateFromPem(crt.toString());
|
||||||
|
const expires = detail.validity.notAfter;
|
||||||
|
return { detail, expires };
|
||||||
|
}
|
||||||
|
|
||||||
|
saveToFile(type: "crt" | "key", path?: string) {
|
||||||
|
if (path == null) {
|
||||||
|
//写入临时目录
|
||||||
|
path = `${os.tmpdir()}/certd/tmp/${Math.floor(Math.random() * 1000000)}/cert.${type}`;
|
||||||
|
}
|
||||||
|
fs.writeFileSync(path, this[type]);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
@IsTaskPlugin({
|
@IsTaskPlugin({
|
||||||
name: "CertApply",
|
name: "CertApply",
|
||||||
title: "证书申请",
|
title: "证书申请",
|
||||||
|
@ -199,11 +219,16 @@ export class CertApplyPlugin implements ITaskPlugin {
|
||||||
);
|
);
|
||||||
this.logger.info("开始申请证书,", email, domains);
|
this.logger.info("开始申请证书,", email, domains);
|
||||||
|
|
||||||
const dnsProviderClass = dnsProviderRegistry.get(dnsProviderType);
|
const dnsProviderPlugin = dnsProviderRegistry.get(dnsProviderType);
|
||||||
|
const DnsProviderClass = dnsProviderPlugin.target;
|
||||||
|
const dnsProviderDefine = dnsProviderPlugin.define as DnsProviderDefine;
|
||||||
const access = await this.accessService.getById(dnsProviderAccessId);
|
const access = await this.accessService.getById(dnsProviderAccessId);
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const dnsProvider: AbstractDnsProvider = new dnsProviderClass();
|
const dnsProvider: IDnsProvider = new DnsProviderClass();
|
||||||
dnsProvider.doInit({ access, logger: this.logger, http: this.http });
|
const context = { access, logger: this.logger, http: this.http };
|
||||||
|
Decorator.inject(dnsProviderDefine.autowire, dnsProvider, context);
|
||||||
|
await dnsProvider.onInit();
|
||||||
|
|
||||||
const cert = await this.acme.order({
|
const cert = await this.acme.order({
|
||||||
email,
|
email,
|
||||||
|
@ -239,23 +264,11 @@ export class CertApplyPlugin implements ITaskPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
async readCurrentCert() {
|
async readCurrentCert() {
|
||||||
const cert: CertInfo = await this.pipelineContext.get("cert");
|
const cert: any = await this.pipelineContext.get("cert");
|
||||||
if (cert == null) {
|
if (cert == null) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const { detail, expires } = this.getCrtDetail(cert.crt);
|
return new CertInfo(cert);
|
||||||
return {
|
|
||||||
...cert,
|
|
||||||
detail,
|
|
||||||
expires: expires.getTime(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getCrtDetail(crt: string) {
|
|
||||||
const pki = forge.pki;
|
|
||||||
const detail = pki.certificateFromPem(crt.toString());
|
|
||||||
const expires = detail.validity.notAfter;
|
|
||||||
return { detail, expires };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
"@typescript-eslint/ban-ts-ignore": "off",
|
"@typescript-eslint/ban-ts-ignore": "off",
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
// "no-unused-expressions": "off",
|
// "no-unused-expressions": "off",
|
||||||
"max-len": [0, 160, 2, { "ignoreUrls": true }]
|
"max-len": [0, 160, 2, { "ignoreUrls": true }]
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,31 +15,31 @@
|
||||||
"ssh2": "^0.8.9"
|
"ssh2": "^0.8.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"log4js": "^6.7.1",
|
|
||||||
"dayjs": "^1.9.7",
|
|
||||||
"lodash-es": "^4.17.20",
|
|
||||||
"@types/lodash": "^4.14.186",
|
|
||||||
"vue-tsc": "^0.38.9",
|
|
||||||
"@alicloud/cs20151215": "^3.0.3",
|
"@alicloud/cs20151215": "^3.0.3",
|
||||||
"@alicloud/openapi-client": "^0.4.0",
|
"@alicloud/openapi-client": "^0.4.0",
|
||||||
"@alicloud/pop-core": "^1.7.10",
|
"@alicloud/pop-core": "^1.7.10",
|
||||||
"@midwayjs/core": "^3.0.0",
|
"@midwayjs/core": "^3.0.0",
|
||||||
"@midwayjs/decorator": "^3.0.0",
|
"@midwayjs/decorator": "^3.0.0",
|
||||||
"@types/chai": "^4.3.3",
|
"@types/chai": "^4.3.3",
|
||||||
|
"@types/lodash": "^4.14.186",
|
||||||
"@types/mocha": "^10.0.0",
|
"@types/mocha": "^10.0.0",
|
||||||
"@types/node-forge": "^1.3.0",
|
"@types/node-forge": "^1.3.0",
|
||||||
|
"@types/ssh2": "^1.11.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.38.1",
|
"@typescript-eslint/eslint-plugin": "^5.38.1",
|
||||||
"@typescript-eslint/parser": "^5.38.1",
|
"@typescript-eslint/parser": "^5.38.1",
|
||||||
"chai": "^4.3.6",
|
"chai": "^4.3.6",
|
||||||
|
"dayjs": "^1.9.7",
|
||||||
"eslint": "^8.24.0",
|
"eslint": "^8.24.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"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",
|
||||||
|
"lodash-es": "^4.17.20",
|
||||||
"log4js": "^6.3.0",
|
"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",
|
||||||
"vite": "^3.1.0"
|
"vite": "^3.1.0",
|
||||||
|
"vue-tsc": "^0.38.9"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +1,47 @@
|
||||||
import { AbstractAccess, IsAccess } from "@certd/pipeline";
|
import { AccessInput, IAccess, IsAccess } from "@certd/pipeline";
|
||||||
|
|
||||||
@IsAccess({
|
@IsAccess({
|
||||||
name: "ssh",
|
name: "ssh",
|
||||||
title: "主机登录授权",
|
title: "主机登录授权",
|
||||||
desc: "",
|
desc: "",
|
||||||
input: {
|
input: {},
|
||||||
host: {
|
|
||||||
title: "主机地址",
|
|
||||||
component: {
|
|
||||||
placeholder: "主机域名或IP地址",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
port: {
|
|
||||||
title: "端口",
|
|
||||||
value: "22",
|
|
||||||
component: {
|
|
||||||
placeholder: "22",
|
|
||||||
},
|
|
||||||
rules: [{ required: true, message: "此项必填" }],
|
|
||||||
},
|
|
||||||
username: {
|
|
||||||
title: "用户名",
|
|
||||||
value: "root",
|
|
||||||
rules: [{ required: true, message: "此项必填" }],
|
|
||||||
},
|
|
||||||
password: {
|
|
||||||
title: "密码",
|
|
||||||
component: {
|
|
||||||
name: "a-input-password",
|
|
||||||
vModel: "value",
|
|
||||||
},
|
|
||||||
helper: "登录密码或密钥必填一项",
|
|
||||||
},
|
|
||||||
privateKey: {
|
|
||||||
title: "密钥",
|
|
||||||
helper: "密钥或密码必填一项",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
export class SshAccess extends AbstractAccess {
|
export class SshAccess implements IAccess {
|
||||||
host = "";
|
@AccessInput({
|
||||||
port = 22;
|
title: "主机地址",
|
||||||
username = "root";
|
component: {
|
||||||
password?: string;
|
placeholder: "主机域名或IP地址",
|
||||||
privateKey?: string;
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
host!: string;
|
||||||
|
@AccessInput({
|
||||||
|
title: "端口",
|
||||||
|
value: "22",
|
||||||
|
component: {
|
||||||
|
placeholder: "22",
|
||||||
|
},
|
||||||
|
rules: [{ required: true, message: "此项必填" }],
|
||||||
|
})
|
||||||
|
port!: string;
|
||||||
|
@AccessInput({
|
||||||
|
title: "用户名",
|
||||||
|
value: "root",
|
||||||
|
rules: [{ required: true, message: "此项必填" }],
|
||||||
|
})
|
||||||
|
username!: string;
|
||||||
|
@AccessInput({
|
||||||
|
title: "密码",
|
||||||
|
component: {
|
||||||
|
name: "a-input-password",
|
||||||
|
vModel: "value",
|
||||||
|
},
|
||||||
|
helper: "登录密码或密钥必填一项",
|
||||||
|
})
|
||||||
|
password!: string;
|
||||||
|
@AccessInput({
|
||||||
|
title: "密钥",
|
||||||
|
helper: "密钥或密码必填一项",
|
||||||
|
})
|
||||||
|
privateKey!: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
// @ts-ignore
|
||||||
import ssh2 from "ssh2";
|
import ssh2 from "ssh2";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { Logger } from "log4js";
|
import { ILogger } from "@certd/pipeline";
|
||||||
export class SshClient {
|
export class SshClient {
|
||||||
logger: Logger;
|
logger: ILogger;
|
||||||
constructor(logger: Logger) {
|
constructor(logger: ILogger) {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,47 +1,56 @@
|
||||||
import { IsTask, TaskInput, TaskOutput, TaskPlugin, AbstractPlugin, RunStrategy } from "@certd/pipeline";
|
import { Autowire, IAccessService, IsTaskPlugin, ILogger, RunStrategy, TaskInput, ITaskPlugin } from "@certd/pipeline";
|
||||||
import { SshClient } from "../../lib/ssh";
|
import { SshClient } from "../../lib/ssh";
|
||||||
|
import { CertInfo } from "@certd/plugin-cert";
|
||||||
|
|
||||||
@IsTask(() => {
|
@IsTaskPlugin({
|
||||||
return {
|
name: "hostShellExecute",
|
||||||
name: "hostShellExecute",
|
title: "执行远程主机脚本命令",
|
||||||
title: "执行远程主机脚本命令",
|
input: {},
|
||||||
input: {
|
default: {
|
||||||
accessId: {
|
strategy: {
|
||||||
title: "主机登录配置",
|
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||||
helper: "登录",
|
|
||||||
component: {
|
|
||||||
name: "pi-access-selector",
|
|
||||||
type: "ssh",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
cert: {
|
|
||||||
title: "域名证书",
|
|
||||||
helper: "请选择前置任务输出的域名证书",
|
|
||||||
component: {
|
|
||||||
name: "pi-output-selector",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
script: {
|
|
||||||
title: "shell脚本命令",
|
|
||||||
component: {
|
|
||||||
name: "a-textarea",
|
|
||||||
vModel: "value",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
default: {
|
},
|
||||||
strategy: {
|
output: {},
|
||||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
output: {},
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
export class HostShellExecutePlugin extends AbstractPlugin implements TaskPlugin {
|
export class HostShellExecutePlugin implements ITaskPlugin {
|
||||||
async execute(input: TaskInput): Promise<TaskOutput> {
|
@TaskInput({
|
||||||
const { script, accessId } = input;
|
title: "主机登录配置",
|
||||||
|
helper: "登录",
|
||||||
|
component: {
|
||||||
|
name: "pi-access-selector",
|
||||||
|
type: "ssh",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
accessId!: string;
|
||||||
|
@TaskInput({
|
||||||
|
title: "域名证书",
|
||||||
|
helper: "请选择前置任务输出的域名证书",
|
||||||
|
component: {
|
||||||
|
name: "pi-output-selector",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
cert!: CertInfo;
|
||||||
|
@TaskInput({
|
||||||
|
title: "shell脚本命令",
|
||||||
|
component: {
|
||||||
|
name: "a-textarea",
|
||||||
|
vModel: "value",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
script!: string;
|
||||||
|
|
||||||
|
@Autowire()
|
||||||
|
accessService!: IAccessService;
|
||||||
|
@Autowire()
|
||||||
|
logger!: ILogger;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
async onInit() {}
|
||||||
|
async execute(): Promise<void> {
|
||||||
|
const { script, accessId } = this;
|
||||||
const connectConf = this.accessService.getById(accessId);
|
const connectConf = this.accessService.getById(accessId);
|
||||||
const sshClient = new SshClient(this.logger);
|
const sshClient = new SshClient(this.logger);
|
||||||
const ret = await sshClient.exec({
|
const ret = await sshClient.exec({
|
||||||
|
@ -49,6 +58,5 @@ export class HostShellExecutePlugin extends AbstractPlugin implements TaskPlugin
|
||||||
script,
|
script,
|
||||||
});
|
});
|
||||||
this.logger.info("exec res:", ret);
|
this.logger.info("exec res:", ret);
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,71 +1,87 @@
|
||||||
import { IsTask, TaskInput, TaskOutput, TaskPlugin, AbstractPlugin, RunStrategy } from "@certd/pipeline";
|
import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, ILogger, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline";
|
||||||
import { SshClient } from "../../lib/ssh";
|
import { SshClient } from "../../lib/ssh";
|
||||||
|
import { CertInfo } from "@certd/plugin-cert";
|
||||||
|
import * as fs from "fs";
|
||||||
|
|
||||||
@IsTask(() => {
|
@IsTaskPlugin({
|
||||||
return {
|
name: "uploadCertToHost",
|
||||||
name: "uploadCertToHost",
|
title: "上传证书到主机",
|
||||||
title: "上传证书到主机",
|
default: {
|
||||||
input: {
|
strategy: {
|
||||||
crtPath: {
|
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||||
title: "证书保存路径",
|
|
||||||
},
|
|
||||||
keyPath: {
|
|
||||||
title: "私钥保存路径",
|
|
||||||
},
|
|
||||||
cert: {
|
|
||||||
title: "域名证书",
|
|
||||||
helper: "请选择前置任务输出的域名证书",
|
|
||||||
component: {
|
|
||||||
name: "pi-output-selector",
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
accessId: {
|
|
||||||
title: "主机登录配置",
|
|
||||||
helper: "access授权",
|
|
||||||
component: {
|
|
||||||
name: "pi-access-selector",
|
|
||||||
type: "ssh",
|
|
||||||
},
|
|
||||||
rules: [{ required: true, message: "此项必填" }],
|
|
||||||
},
|
|
||||||
sudo: {
|
|
||||||
title: "是否sudo",
|
|
||||||
component: {
|
|
||||||
name: "a-checkbox",
|
|
||||||
vModel: "checked",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
default: {
|
},
|
||||||
strategy: {
|
|
||||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
hostCrtPath: {
|
|
||||||
title: "上传成功后的证书路径",
|
|
||||||
},
|
|
||||||
hostKeyPath: {
|
|
||||||
title: "上传成功后的私钥路径",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
export class UploadCertToHostPlugin extends AbstractPlugin implements TaskPlugin {
|
export class UploadCertToHostPlugin implements ITaskPlugin {
|
||||||
async execute(input: TaskInput): Promise<TaskOutput> {
|
@TaskInput({
|
||||||
const { crtPath, keyPath, cert, accessId, sudo } = input;
|
title: "证书保存路径",
|
||||||
|
})
|
||||||
|
crtPath!: string;
|
||||||
|
@TaskInput({
|
||||||
|
title: "私钥保存路径",
|
||||||
|
})
|
||||||
|
keyPath!: string;
|
||||||
|
@TaskInput({
|
||||||
|
title: "域名证书",
|
||||||
|
helper: "请选择前置任务输出的域名证书",
|
||||||
|
component: {
|
||||||
|
name: "pi-output-selector",
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
cert!: CertInfo;
|
||||||
|
@TaskInput({
|
||||||
|
title: "主机登录配置",
|
||||||
|
helper: "access授权",
|
||||||
|
component: {
|
||||||
|
name: "pi-access-selector",
|
||||||
|
type: "ssh",
|
||||||
|
},
|
||||||
|
rules: [{ required: true, message: "此项必填" }],
|
||||||
|
})
|
||||||
|
accessId!: string;
|
||||||
|
@TaskInput({
|
||||||
|
title: "是否sudo",
|
||||||
|
component: {
|
||||||
|
name: "a-checkbox",
|
||||||
|
vModel: "checked",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
sudo!: boolean;
|
||||||
|
|
||||||
|
@Autowire()
|
||||||
|
accessService!: IAccessService;
|
||||||
|
@Autowire()
|
||||||
|
logger!: ILogger;
|
||||||
|
|
||||||
|
@TaskOutput({
|
||||||
|
title: "证书保存路径",
|
||||||
|
})
|
||||||
|
hostCrtPath!: string;
|
||||||
|
|
||||||
|
@TaskOutput({
|
||||||
|
title: "私钥保存路径",
|
||||||
|
})
|
||||||
|
hostKeyPath!: string;
|
||||||
|
|
||||||
|
async onInit() {}
|
||||||
|
async execute(): Promise<void> {
|
||||||
|
const { crtPath, keyPath, cert, accessId, sudo } = this;
|
||||||
const connectConf = this.accessService.getById(accessId);
|
const connectConf = this.accessService.getById(accessId);
|
||||||
const sshClient = new SshClient(this.logger);
|
const sshClient = new SshClient(this.logger);
|
||||||
|
|
||||||
|
const saveCrtPath = cert.saveToFile("crt");
|
||||||
|
const saveKeyPath = cert.saveToFile("key");
|
||||||
|
|
||||||
await sshClient.uploadFiles({
|
await sshClient.uploadFiles({
|
||||||
connectConf,
|
connectConf,
|
||||||
transports: [
|
transports: [
|
||||||
{
|
{
|
||||||
localPath: cert.crtPath,
|
localPath: saveCrtPath,
|
||||||
remotePath: crtPath,
|
remotePath: crtPath,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
localPath: cert.keyPath,
|
localPath: saveKeyPath,
|
||||||
remotePath: keyPath,
|
remotePath: keyPath,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -73,9 +89,12 @@ export class UploadCertToHostPlugin extends AbstractPlugin implements TaskPlugin
|
||||||
});
|
});
|
||||||
this.logger.info("证书上传成功:crtPath=", crtPath, ",keyPath=", keyPath);
|
this.logger.info("证书上传成功:crtPath=", crtPath, ",keyPath=", keyPath);
|
||||||
|
|
||||||
return {
|
//删除临时文件
|
||||||
hostCrtPath: crtPath,
|
fs.unlinkSync(saveCrtPath);
|
||||||
hostKeyPath: keyPath,
|
fs.unlinkSync(saveKeyPath);
|
||||||
};
|
|
||||||
|
//输出
|
||||||
|
this.hostCrtPath = crtPath;
|
||||||
|
this.hostKeyPath = keyPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
"@typescript-eslint/ban-ts-ignore": "off",
|
"@typescript-eslint/ban-ts-ignore": "off",
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
// "no-unused-expressions": "off",
|
// "no-unused-expressions": "off",
|
||||||
"max-len": [0, 160, 2, { "ignoreUrls": true }]
|
"max-len": [0, 160, 2, { "ignoreUrls": true }]
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/pipeline": "^0.3.0",
|
"@certd/pipeline": "^0.3.0",
|
||||||
"@certd/plugin-util": "^0.3.0",
|
"@certd/plugin-util": "^0.3.0",
|
||||||
|
"@certd/plugin-cert": "^0.3.0",
|
||||||
"tencentcloud-sdk-nodejs": "^4.0.44"
|
"tencentcloud-sdk-nodejs": "^4.0.44"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { IsAccess, IsAccessInput } from "@certd/pipeline";
|
import { IsAccess, AccessInput } from "@certd/pipeline";
|
||||||
|
|
||||||
@IsAccess({
|
@IsAccess({
|
||||||
name: "dnspod",
|
name: "dnspod",
|
||||||
|
@ -6,7 +6,7 @@ import { IsAccess, IsAccessInput } from "@certd/pipeline";
|
||||||
desc: "腾讯云的域名解析接口已迁移到dnspod",
|
desc: "腾讯云的域名解析接口已迁移到dnspod",
|
||||||
})
|
})
|
||||||
export class DnspodAccess {
|
export class DnspodAccess {
|
||||||
@IsAccessInput({
|
@AccessInput({
|
||||||
title: "token",
|
title: "token",
|
||||||
component: {
|
component: {
|
||||||
placeholder: "开放接口token",
|
placeholder: "开放接口token",
|
||||||
|
@ -14,7 +14,7 @@ export class DnspodAccess {
|
||||||
rules: [{ required: true, message: "该项必填" }],
|
rules: [{ required: true, message: "该项必填" }],
|
||||||
})
|
})
|
||||||
token = "";
|
token = "";
|
||||||
@IsAccessInput({
|
@AccessInput({
|
||||||
title: "账户id",
|
title: "账户id",
|
||||||
component: {
|
component: {
|
||||||
placeholder: "dnspod接口账户id",
|
placeholder: "dnspod接口账户id",
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { IsAccess, IsAccessInput } from "@certd/pipeline";
|
import { IsAccess, AccessInput } from "@certd/pipeline";
|
||||||
|
|
||||||
@IsAccess({
|
@IsAccess({
|
||||||
name: "tencent",
|
name: "tencent",
|
||||||
title: "腾讯云",
|
title: "腾讯云",
|
||||||
})
|
})
|
||||||
export class TencentAccess {
|
export class TencentAccess {
|
||||||
@IsAccessInput({
|
@AccessInput({
|
||||||
title: "secretId",
|
title: "secretId",
|
||||||
component: {
|
component: {
|
||||||
placeholder: "secretId",
|
placeholder: "secretId",
|
||||||
|
@ -13,7 +13,7 @@ export class TencentAccess {
|
||||||
rules: [{ required: true, message: "该项必填" }],
|
rules: [{ required: true, message: "该项必填" }],
|
||||||
})
|
})
|
||||||
secretId = "";
|
secretId = "";
|
||||||
@IsAccessInput({
|
@AccessInput({
|
||||||
title: "secretKey",
|
title: "secretKey",
|
||||||
component: {
|
component: {
|
||||||
placeholder: "secretKey",
|
placeholder: "secretKey",
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { CreateRecordOptions, HttpClient, IDnsProvider, IsDnsProvider, RemoveRecordOptions } from "@certd/pipeline";
|
import { Autowire, HttpClient, ILogger } from "@certd/pipeline";
|
||||||
|
import { CreateRecordOptions, IDnsProvider, IsDnsProvider, RemoveRecordOptions } from "@certd/plugin-cert";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { DnspodAccess } from "../access";
|
import { DnspodAccess } from "../access";
|
||||||
import { Inject } from "@midwayjs/decorator";
|
|
||||||
import { ILogger } from "@midwayjs/core";
|
|
||||||
|
|
||||||
@IsDnsProvider({
|
@IsDnsProvider({
|
||||||
name: "dnspod",
|
name: "dnspod",
|
||||||
|
@ -11,12 +10,12 @@ import { ILogger } from "@midwayjs/core";
|
||||||
accessType: "dnspod",
|
accessType: "dnspod",
|
||||||
})
|
})
|
||||||
export class DnspodDnsProvider implements IDnsProvider {
|
export class DnspodDnsProvider implements IDnsProvider {
|
||||||
@Inject()
|
@Autowire()
|
||||||
http!: HttpClient;
|
http!: HttpClient;
|
||||||
|
|
||||||
@Inject()
|
@Autowire()
|
||||||
access!: DnspodAccess;
|
access!: DnspodAccess;
|
||||||
@Inject()
|
@Autowire()
|
||||||
logger!: ILogger;
|
logger!: ILogger;
|
||||||
|
|
||||||
loginToken: any;
|
loginToken: any;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { IAccessService, IsTaskPlugin, RunStrategy, TaskInput, ITaskPlugin, LOGGER } from "@certd/pipeline";
|
import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, ILogger, RunStrategy, TaskInput } 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 { CertInfo } from "@certd/plugin-cert";
|
||||||
|
|
||||||
@IsTaskPlugin({
|
@IsTaskPlugin({
|
||||||
name: "DeployCertToTencentCDN",
|
name: "DeployCertToTencentCDN",
|
||||||
|
@ -22,7 +22,7 @@ export class DeployToCdnPlugin implements ITaskPlugin {
|
||||||
},
|
},
|
||||||
required: true,
|
required: true,
|
||||||
})
|
})
|
||||||
cert!: any;
|
cert!: CertInfo;
|
||||||
|
|
||||||
@TaskInput({
|
@TaskInput({
|
||||||
title: "Access提供者",
|
title: "Access提供者",
|
||||||
|
@ -47,11 +47,11 @@ export class DeployToCdnPlugin implements ITaskPlugin {
|
||||||
})
|
})
|
||||||
domainName!: string;
|
domainName!: string;
|
||||||
|
|
||||||
@Inject()
|
@Autowire()
|
||||||
accessService!: IAccessService;
|
accessService!: IAccessService;
|
||||||
|
|
||||||
@Inject()
|
@Autowire()
|
||||||
logger!: LOGGER;
|
logger!: ILogger;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
async onInit() {}
|
async onInit() {}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { IAccessService, IsTaskPlugin, ITaskPlugin, LOGGER, RunStrategy, TaskInput, utils } from "@certd/pipeline";
|
import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, ILogger, 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";
|
|
||||||
|
|
||||||
@IsTaskPlugin({
|
@IsTaskPlugin({
|
||||||
name: "DeployCertToTencentCLB",
|
name: "DeployCertToTencentCLB",
|
||||||
|
@ -72,11 +71,11 @@ export class DeployToClbPlugin implements ITaskPlugin {
|
||||||
})
|
})
|
||||||
accessId!: string;
|
accessId!: string;
|
||||||
|
|
||||||
@Inject()
|
@Autowire()
|
||||||
accessService!: IAccessService;
|
accessService!: IAccessService;
|
||||||
|
|
||||||
@Inject()
|
@Autowire()
|
||||||
logger!: LOGGER;
|
logger!: ILogger;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
async onInit() {}
|
async onInit() {}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, RunStrategy, TaskInput, TaskOutput, LOGGER } from "@certd/pipeline";
|
import { Autowire, IAccessService, IsTaskPlugin, ITaskPlugin, RunStrategy, TaskInput, TaskOutput, ILogger } 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";
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ export class UploadToTencentPlugin implements ITaskPlugin {
|
||||||
accessService!: IAccessService;
|
accessService!: IAccessService;
|
||||||
|
|
||||||
@Autowire()
|
@Autowire()
|
||||||
logger!: LOGGER;
|
logger!: ILogger;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
async onInit() {}
|
async onInit() {}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
"@typescript-eslint/ban-ts-ignore": "off",
|
"@typescript-eslint/ban-ts-ignore": "off",
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
// "no-unused-expressions": "off",
|
// "no-unused-expressions": "off",
|
||||||
"max-len": [0, 160, 2, { "ignoreUrls": true }]
|
"max-len": [0, 160, 2, { "ignoreUrls": true }]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit b7422ab48ef81f5c98129bacb69d0eefdfe50645
|
Subproject commit 8c7b3853be27df392dff765d39c6c53de09418bd
|
|
@ -1 +1 @@
|
||||||
Subproject commit 5901fb5a440a7cebe3a2b6dfaec1b014e30b3a0c
|
Subproject commit a5681f154468fc5c4aac7e4a0ce090cfffc00e9e
|
Loading…
Reference in New Issue