Files
certd/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts

171 lines
4.9 KiB
TypeScript
Raw Normal View History

import { NotificationBody, Step, TaskInput } from "@certd/pipeline";
2024-07-18 21:10:13 +08:00
import dayjs from "dayjs";
import { CertReader } from "./cert-reader.js";
import { pick } from "lodash-es";
import { CertApplyBaseConvertPlugin } from "./base-convert.js";
2024-07-18 21:10:13 +08:00
export abstract class CertApplyBasePlugin extends CertApplyBaseConvertPlugin {
2024-07-18 21:10:13 +08:00
@TaskInput({
title: "邮箱",
component: {
2025-05-31 00:45:54 +08:00
name: "email-selector",
2024-07-18 21:10:13 +08:00
vModel: "value",
},
2024-10-25 21:47:28 +08:00
rules: [{ type: "email", message: "请输入正确的邮箱" }],
2024-07-18 21:10:13 +08:00
required: true,
order: -1,
2024-07-18 21:10:13 +08:00
helper: "请输入邮箱",
})
email!: string;
@TaskInput({
title: "更新天数",
value: 35,
2024-07-18 21:10:13 +08:00
component: {
name: "a-input-number",
vModel: "value",
},
required: true,
order: 100,
helper: "到期前多少天后更新证书,注意:流水线默认不会自动运行,请设置定时器,每天定时运行本流水线",
})
renewDays!: number;
@TaskInput({
title: "证书申请成功通知",
2024-07-18 21:10:13 +08:00
value: true,
component: {
name: "a-switch",
vModel: "checked",
},
order: 100,
helper: "证书申请成功后是否发送通知,优先使用默认通知渠道",
2024-07-18 21:10:13 +08:00
})
successNotify = true;
// @TaskInput({
// title: "CsrInfo",
// helper: "暂时没有用",
// })
csrInfo!: string;
async onInstance() {
this.userContext = this.ctx.userContext;
this.lastStatus = this.ctx.lastStatus as Step;
await this.onInit();
}
abstract onInit(): Promise<void>;
2025-01-15 01:05:34 +08:00
abstract doCertApply(): Promise<CertReader>;
2024-07-18 21:10:13 +08:00
async execute(): Promise<string | void> {
2024-07-18 21:10:13 +08:00
const oldCert = await this.condition();
if (oldCert != null) {
await this.output(oldCert, false);
return "skip";
2024-07-18 21:10:13 +08:00
}
const cert = await this.doCertApply();
if (cert != null) {
await this.output(cert, true);
2025-01-15 01:05:34 +08:00
await this.emitCertApplySuccess();
2024-07-18 21:10:13 +08:00
//清空后续任务的状态,让后续任务能够重新执行
this.clearLastStatus();
if (this.successNotify) {
await this.sendSuccessNotify();
2024-07-18 21:10:13 +08:00
}
} else {
throw new Error("申请证书失败");
}
}
getCheckChangeInputKeys() {
//插件哪些字段参与校验是否需要更新
return ["domains", "sslProvider", "privateKeyType", "dnsProviderType", "pfxPassword"];
}
2024-07-18 21:10:13 +08:00
/**
*
*/
async condition() {
2024-11-12 10:12:10 +08:00
// if (this.forceUpdate) {
// this.logger.info("强制更新证书选项已勾选,准备申请新证书");
// this.logger.warn("申请完之后,切记取消强制更新,避免申请过多证书。");
// return null;
// }
2024-07-18 21:10:13 +08:00
const checkInputChanges = this.getCheckChangeInputKeys();
const oldInput = JSON.stringify(pick(this.lastStatus?.input, checkInputChanges));
const thisInput = JSON.stringify(pick(this, checkInputChanges));
2025-01-15 01:05:34 +08:00
const inputChanged = oldInput !== thisInput;
this.logger.info(`旧参数:${oldInput}`);
this.logger.info(`新参数:${thisInput}`);
if (inputChanged) {
this.logger.info("输入参数变更,准备申请新证书");
return null;
} else {
2025-01-05 01:07:04 +08:00
this.logger.info("输入参数未变更,检查证书是否过期");
2024-07-18 21:10:13 +08:00
}
let oldCert: CertReader | undefined = undefined;
try {
2025-01-02 17:48:54 +08:00
this.logger.info("读取上次证书");
2024-07-18 21:10:13 +08:00
oldCert = await this.readLastCert();
} catch (e) {
this.logger.warn("读取cert失败", e);
}
if (oldCert == null) {
this.logger.info("还未申请过,准备申请新证书");
return null;
}
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;
}
this.logger.info("即将过期,开始更新证书");
return null;
}
/**
* 35
2024-07-18 21:10:13 +08:00
* @param expires
* @param maxDays
*/
isWillExpire(expires: number, maxDays = 20) {
if (expires == null) {
throw new Error("过期时间不能为空");
}
// 检查有效期
const leftDays = Math.floor((expires - dayjs().valueOf()) / (1000 * 60 * 60 * 24));
this.logger.info(`证书剩余天数:${leftDays}`);
2024-07-18 21:10:13 +08:00
return {
2024-12-27 22:40:07 +08:00
isWillExpire: leftDays <= maxDays,
2024-07-18 21:10:13 +08:00
leftDays,
};
}
async sendSuccessNotify() {
this.logger.info("发送证书申请成功通知");
const url = await this.ctx.urlService.getPipelineDetailUrl(this.pipeline.id, this.ctx.runtime.id);
const body: NotificationBody = {
2024-12-11 11:30:32 +08:00
title: `证书申请成功【${this.pipeline.title}`,
content: `域名:${this.domains.join(",")}`,
url: url,
};
2024-07-18 21:10:13 +08:00
try {
2024-12-11 11:30:32 +08:00
await this.ctx.notificationService.send({
useDefault: true,
useEmail: true,
emailAddress: this.email,
2024-12-12 16:49:40 +08:00
logger: this.logger,
2024-12-11 11:30:32 +08:00
body,
});
2024-07-18 21:10:13 +08:00
} catch (e) {
this.logger.error("证书申请成功通知发送失败", e);
2024-07-18 21:10:13 +08:00
}
}
}