perf: 支持邮箱发送证书

v2-dev-order
xiaojunnuo 2025-07-15 13:58:01 +08:00
parent 9864792bbf
commit 95332d5db9
6 changed files with 113 additions and 3 deletions

View File

@ -1,7 +1,9 @@
export type EmailSend = {
subject: string;
content: string;
receivers: string[];
content?: string;
attachments?: any[];
html?: string;
};
export interface IEmailService {

View File

@ -1,4 +1,4 @@
import { AbstractTaskPlugin, IContext, Step, TaskInput, TaskOutput } from "@certd/pipeline";
import { AbstractTaskPlugin, FileItem, IContext, Step, TaskInput, TaskOutput } from "@certd/pipeline";
import dayjs from "dayjs";
import type { CertInfo } from "./acme.js";
import { CertReader } from "./cert-reader.js";
@ -71,6 +71,12 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
})
cert?: CertInfo;
@TaskOutput({
title: "域名证书压缩文件",
type: "certZip",
})
certZip?: FileItem;
async onInstance() {
this.userContext = this.ctx.userContext;
this.lastStatus = this.ctx.lastStatus as Step;
@ -131,6 +137,7 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
} else {
this.extendsFiles();
}
this.certZip = this._result.files[0];
}
async zipCert(cert: CertInfo, filename: string) {

View File

@ -98,6 +98,8 @@ export class EmailService implements IEmailService {
to: email.receivers.join(', '), // list of receivers
subject: subject,
text: email.content,
html: email.html,
attachments: email.attachments,
};
await transporter.sendMail(mailOptions);
}

View File

@ -2,3 +2,4 @@ export * from './plugin-restart.js';
export * from './plugin-script.js';
export * from './plugin-wait.js';
export * from './plugin-db-backup.js';
export * from './plugin-deploy-to-mail.js';

View File

@ -0,0 +1,98 @@
import {AbstractTaskPlugin, FileItem, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput} from '@certd/pipeline';
import {CertInfo, CertReader} from "@certd/plugin-cert";
import dayjs from "dayjs";
@IsTaskPlugin({
name: 'DeployCertToMailPlugin',
title: '邮件发送证书',
icon: 'ri:rest-time-line',
desc: '通过邮件发送证书',
group: pluginGroups.other.key,
showRunStrategy:false,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class DeployCertToMailPlugin extends AbstractTaskPlugin {
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: [":cert:"],
},
required: true,
})
cert!: CertInfo;
@TaskInput({
title: '证书压缩文件',
helper: '请选择前置任务输出的域名证书压缩文件',
component: {
name: 'output-selector',
from: [":certZip:"],
},
required: true,
})
certZip!: FileItem;
@TaskInput({
title: '接收邮箱',
component: {
name: 'EmailSelector',
vModel: 'value',
mode:"tags",
},
required: true,
})
email!: string[];
@TaskInput({
title: '备注',
component: {
name: 'a-input',
vModel: 'value',
},
required: false,
})
remark!: string;
async onInstance() {}
async execute(): Promise<void> {
this.logger.info(`开始发送邮件`);
const certReader = new CertReader(this.cert)
const mainDomain = certReader.getMainDomain();
const domains = certReader.getAllDomains().join(',');
const title = `证书申请成功【${mainDomain}`;
const html = `
<div>
<p></p>
<p>${domains}</p>
<p>${dayjs(certReader.expires).format("YYYY-MM-DD HH:mm:ss")}</p>
<p>${this.remark||""}</p>
</div>
`;
const file = this.certZip
if (!file) {
throw new Error('证书压缩文件还未生成,重新运行证书任务');
}
await this.ctx.emailService.send({
subject:title,
html: html,
receivers: this.email,
attachments: [
{
filename: file.filename,
path: file.path,
},
],
})
}
}
new DeployCertToMailPlugin();

View File

@ -5,7 +5,7 @@ import { TencentAccess } from "@certd/plugin-lib";
name: 'DeployCertToTencentEO',
title: '腾讯云-部署到腾讯云EO',
icon: 'svg:icon-tencentcloud',
desc: '腾讯云边缘安全加速平台EO必须配置上传证书到腾讯云任务',
desc: '腾讯云边缘安全加速平台EdgeOne(EO),必须配置上传证书到腾讯云任务',
group: pluginGroups.tencent.key,
default: {
strategy: {