mirror of
https://github.com/certd/certd.git
synced 2026-07-07 21:17:42 +08:00
159 lines
4.8 KiB
TypeScript
159 lines
4.8 KiB
TypeScript
import { AbstractTaskPlugin, IContext, Step, TaskInput, TaskOutput } from "@certd/pipeline";
|
||
import { CertConverter, CertReader, EVENT_CERT_APPLY_SUCCESS } from "@certd/plugin-lib";
|
||
import dayjs from "dayjs";
|
||
import type { CertInfo } from "./acme.js";
|
||
|
||
export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
||
@TaskInput({
|
||
title: "证书域名",
|
||
component: {
|
||
name: "domain-selector",
|
||
vModel: "value",
|
||
mode: "tags",
|
||
// open: false,
|
||
placeholder: "请输入证书域名/IP,比如:foo.com , *.foo.com , *.sub.foo.com , *.bar.com , 123.123.123.123",
|
||
tokenSeparators: [",", " ", ",", "、", "|"],
|
||
search: true,
|
||
pager: true,
|
||
},
|
||
rules: [{ type: "domains" }],
|
||
required: true,
|
||
col: {
|
||
span: 24,
|
||
},
|
||
order: -999,
|
||
helper:
|
||
"1、支持多个域名打到一个证书上,例如: foo.com,*.foo.com,*.bar.com\n" +
|
||
"2、子域名被通配符包含的不要填写,例如:www.foo.com已经被*.foo.com包含,不要填写www.foo.com\n" +
|
||
"3、泛域名只能通配*号那一级(*.foo.com的证书不能用于foo.com且不能用于xxx.yyy.foo.com)\n" +
|
||
"4、输入一个,空格之后,再输入下一个 \n" +
|
||
"5、如果设置了子域托管解析(比如免费的二级域名托管在CF或者阿里云),请先[设置托管子域名](#/certd/pipeline/subDomain)",
|
||
})
|
||
domains!: string[];
|
||
|
||
@TaskInput({
|
||
title: "证书加密密码",
|
||
component: {
|
||
name: "input-password",
|
||
vModel: "value",
|
||
},
|
||
required: false,
|
||
order: 100,
|
||
helper: "转换成PFX、jks格式证书是否需要加密\n不传则pfx格式默认空密码,jks格式默认123456",
|
||
})
|
||
pfxPassword!: string;
|
||
|
||
@TaskInput({
|
||
title: "PFX证书转换参数",
|
||
value: "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES",
|
||
component: {
|
||
name: "a-auto-complete",
|
||
vModel: "value",
|
||
options: [
|
||
{ value: "", label: "兼容 Windows Server 最新" },
|
||
{ value: "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES", label: "兼容 Windows Server 2016" },
|
||
{ value: "-nomac -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES", label: "兼容 Windows Server 2008" },
|
||
],
|
||
},
|
||
required: false,
|
||
order: 100,
|
||
maybeNeed: true,
|
||
helper: "兼容Windows Server各个版本",
|
||
})
|
||
pfxArgs = "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES";
|
||
|
||
userContext!: IContext;
|
||
lastStatus!: Step;
|
||
|
||
@TaskOutput({
|
||
title: "域名证书",
|
||
type: "cert",
|
||
})
|
||
cert?: CertInfo;
|
||
|
||
async onInstance() {
|
||
this.userContext = this.ctx.userContext;
|
||
this.lastStatus = this.ctx.lastStatus as Step;
|
||
await this.onInit();
|
||
}
|
||
|
||
abstract onInit(): Promise<void>;
|
||
|
||
//必须output之后执行
|
||
async emitCertApplySuccess() {
|
||
const emitter = this.ctx.emitter;
|
||
const value = {
|
||
cert: this.cert,
|
||
};
|
||
await emitter.emit(EVENT_CERT_APPLY_SUCCESS, value);
|
||
}
|
||
|
||
async output(certReader: CertReader, isNew: boolean) {
|
||
const cert: CertInfo = certReader.toCertInfo();
|
||
this.cert = cert;
|
||
|
||
this._result.pipelineVars.certEffectiveTime = dayjs(certReader.detail.notBefore).valueOf();
|
||
this._result.pipelineVars.certExpiresTime = dayjs(certReader.detail.notAfter).valueOf();
|
||
this._result.pipelineVars.certDomains = certReader.getAllDomains();
|
||
if (!this._result.pipelinePrivateVars) {
|
||
this._result.pipelinePrivateVars = {};
|
||
}
|
||
this._result.pipelinePrivateVars.cert = cert;
|
||
|
||
if (isNew || !cert.pfx) {
|
||
try {
|
||
const converter = new CertConverter({ logger: this.logger });
|
||
const res = await converter.convert({
|
||
cert,
|
||
pfxPassword: this.pfxPassword,
|
||
pfxArgs: this.pfxArgs,
|
||
});
|
||
if (cert.pfx == null && res.pfx) {
|
||
cert.pfx = res.pfx;
|
||
}
|
||
|
||
if (cert.der == null && res.der) {
|
||
cert.der = res.der;
|
||
}
|
||
|
||
if (cert.jks == null && res.jks) {
|
||
cert.jks = res.jks;
|
||
}
|
||
|
||
if (cert.p7b == null && res.p7b) {
|
||
cert.p7b = res.p7b;
|
||
}
|
||
|
||
this.logger.info("转换证书格式成功");
|
||
} catch (e) {
|
||
this.logger.error("转换证书格式失败", e);
|
||
}
|
||
}
|
||
}
|
||
|
||
formatCert(pem: string) {
|
||
pem = pem.replace(/\r/g, "");
|
||
pem = pem.replace(/\n\n/g, "\n");
|
||
pem = pem.replace(/\n$/g, "");
|
||
return pem;
|
||
}
|
||
|
||
formatCerts(cert: { crt: string; key: string; csr?: string }) {
|
||
const newCert: CertInfo = {
|
||
crt: this.formatCert(cert.crt),
|
||
key: this.formatCert(cert.key),
|
||
csr: this.formatCert(cert.csr),
|
||
};
|
||
return newCert;
|
||
}
|
||
|
||
async readLastCert(): Promise<CertReader | undefined> {
|
||
const cert = this.lastStatus?.status?.output?.cert;
|
||
if (cert == null) {
|
||
this.logger.info("没有找到上次的证书");
|
||
return undefined;
|
||
}
|
||
return new CertReader(cert);
|
||
}
|
||
}
|