perf: 支持邮件模版设置

This commit is contained in:
xiaojunnuo
2025-12-14 01:36:20 +08:00
parent 437d956cad
commit a6c0d2c6f1
31 changed files with 703 additions and 214 deletions
@@ -39,4 +39,5 @@ export * from './plugin-captcha/index.js'
export * from './plugin-xinnet/index.js'
export * from './plugin-xinnetconnet/index.js'
export * from './plugin-oauth/index.js'
export * from './plugin-cmcc/index.js'
export * from './plugin-cmcc/index.js'
export * from './plugin-template/index.js'
@@ -132,6 +132,7 @@ nohup sh -c '$RESTART_CERT' >/dev/null 2>&1 & echo '10秒后重启' && exit
title: `${this.repoName} 新版本 ${this.lastVersion} 发布`,
content: `${body}\n\n > [Certd](https://certd.docmirror.cn),不止证书自动化,插件解锁无限可能!\n\n`,
url: `https://github.com/${this.repoName}/releases/tag/${this.lastVersion}`,
notificationType: "githubReleaseCheck",
}
})
}
@@ -21,10 +21,23 @@ export class EmailNotification extends BaseNotification {
receivers!: string[];
async send(body: NotificationBody) {
await this.ctx.emailService.send({
subject: body.title,
content: body.content + '\n\n[查看详情](' + body.url + ')',
receivers: this.receivers,
});
const templateData = {
...body,
}
await this.ctx.emailService.sendByTemplate({
type: body.notificationType,
data: templateData,
email: {
receivers: this.receivers,
attachments: body.attachments,
}
})
// await this.ctx.emailService.send({
// subject: body.title,
// content: body.content + '\n\n[查看详情](' + body.url + ')',
// receivers: this.receivers,
// });
}
}
@@ -3,8 +3,16 @@ export type BuildContentReq = {
data: any;
}
export type BuildContentReply = Record<string, string>;
export interface ITemplateProvider {
buildContent: (params: BuildContentReq) => Promise<BuildContentReply>;
}
export interface ITemplateProvider<T=any> {
buildContent: (params: BuildContentReq) => Promise<T>;
buildDefaultContent:(params: BuildContentReq) => Promise<T>;
}
export type EmailContent = {
subject:string,
content?:string,
html?:string
};
@@ -0,0 +1,4 @@
export * from './plugin-common.js'
export * from './plugin-register-code.js'
export * from './plugin-forgot-password.js'
export * from './plugin-pipeline-result.js'
@@ -1,52 +1,87 @@
import { AddonInput, BaseAddon } from "@certd/lib-server";
import { BuildContentReply, BuildContentReq, ITemplateProvider } from "../api.js";
import { get } from "lodash-es";
import { BuildContentReq, EmailContent, ITemplateProvider } from "../api.js";
export class BaseEmailTemplateProvider extends BaseAddon implements ITemplateProvider {
export class BaseEmailTemplateProvider extends BaseAddon implements ITemplateProvider<EmailContent> {
@AddonInput({
title: "配置说明",
component:{
name:"a-alert",
props:{
type:"info",
message:"在标题和内容模版中,通过${param}引用参数,例如: 感谢注册${siteTitle},您的注册验证码为:${code}",
component: {
name: "a-alert",
props: {
type: "info",
message: "在标题和内容模版中,通过${param}引用参数,例如: 感谢注册${siteTitle},您的注册验证码为:${code}",
}
},
order: 1,
col:{span:24},
col: { span: 24 },
})
useIntro = "";
@AddonInput({
title: "邮件格式",
component: {
name: "a-select",
props: {
options: [
{ label: "HTML", value: "html" },
{ label: "TEXT", value: "text" },
]
}
},
order: 1,
col: { span: 24 },
})
formatType = "";
@AddonInput({
title: "邮件标题模版",
required: true,
order: 10,
component: {
name: "a-input",
props: {
placeholder: "邮件标题模版",
}
},
col: { span: 24 },
})
titleTemplate = "";
@AddonInput({
title: "邮件内容模版",
component: {
placeholder: "邮件内容模版",
name: "a-textarea",
rows: 6,
},
order: 20,
col: { span: 24 },
required: true,
})
contentTemplate = "";
async buildContent(params: BuildContentReq) : Promise<BuildContentReply>{
const title = this.compile(this.titleTemplate)(params.data)
const content = this.compile(this.contentTemplate)(params.data)
return {
title,
content,
}
async buildContent(params: BuildContentReq): Promise<EmailContent> {
const title = this.compile(this.titleTemplate)(params.data)
const content = this.compile(this.contentTemplate)(params.data)
const body: any = {
subject: title,
}
if (this.formatType === "html") {
body.html = content
} else {
body.content = content
}
return body
};
async buildDefaultContent(params: BuildContentReq): Promise<EmailContent> {
throw new Error("请实现 buildDefaultContent 方法")
}
compile(templateString: string) {
return function(data:any):string {
return function (data: any): string {
return templateString.replace(/\${(.*?)}/g, (match, key) => {
const value = get(data, key, '');
return String(value);
@@ -0,0 +1,39 @@
import { AddonInput, IsAddon } from "@certd/lib-server";
import { BuildContentReq, EmailContent, ITemplateProvider } from "../api.js";
import { BaseEmailTemplateProvider } from "./plugin-base.js";
@IsAddon({
addonType: "emailTemplate",
name: 'common',
title: '通用邮件模版',
desc: '通用邮件模版',
icon: "simple-icons:email:blue",
showTest: false,
})
export class CommonEmailTemplateProvider extends BaseEmailTemplateProvider implements ITemplateProvider<EmailContent> {
@AddonInput({
title: "可用参数",
component: {
name: "ParamsShow",
params:[
{labele:"标题",value:"title"},
{labele:"内容",value:"content"},
{labele:"URL",value:"url"}
]
},
order: 5,
col: { span: 24 },
})
paramIntro = "";
async buildDefaultContent(req:BuildContentReq) {
const defaultTemplate = new CommonEmailTemplateProvider()
defaultTemplate.titleTemplate = "${title}"
defaultTemplate.contentTemplate = "${content} \n\n 查看详情:${url}"
defaultTemplate.formatType = "text"
return await defaultTemplate.buildContent(req)
}
}
@@ -0,0 +1,36 @@
import { AddonInput, IsAddon } from "@certd/lib-server";
import { BuildContentReq, EmailContent, ITemplateProvider } from "../api.js";
import { BaseEmailTemplateProvider } from "./plugin-base.js";
@IsAddon({
addonType: "emailTemplate",
name: 'forgotPassword',
title: '忘记密码邮件模版',
desc: '忘记密码邮件模版',
icon: "simple-icons:email:blue",
showTest: false,
})
export class ForgotPasswordEmailTemplateProvider extends BaseEmailTemplateProvider implements ITemplateProvider<EmailContent> {
@AddonInput({
title: "可用参数",
component: {
name: "ParamsShow",
params:[
{labele:"验证码",value:"code"}
]
},
order: 5,
col: { span: 24 },
})
paramIntro = "";
async buildDefaultContent(req:BuildContentReq) {
const defaultTemplate = new ForgotPasswordEmailTemplateProvider()
defaultTemplate.titleTemplate = "忘记密码"
defaultTemplate.contentTemplate = "您的验证码是:${code},请勿泄露"
defaultTemplate.formatType = "text"
return await defaultTemplate.buildContent(req)
}
}
@@ -0,0 +1,44 @@
import { AddonInput, IsAddon } from "@certd/lib-server";
import { BuildContentReq, EmailContent, ITemplateProvider } from "../api.js";
import { BaseEmailTemplateProvider } from "./plugin-base.js";
@IsAddon({
addonType: "emailTemplate",
name: 'pipelineResult',
title: '流水线执行结果邮件模版',
desc: '流水线执行结果邮件模版',
icon: "simple-icons:email:blue",
showTest: false,
})
export class PipelineResultEmailTemplateProvider extends BaseEmailTemplateProvider implements ITemplateProvider<EmailContent> {
@AddonInput({
title: "可用参数",
component: {
name: "ParamsShow",
params:[
{labele:"运行结果",value:"pipelineResult"},
{labele:"流水线标题",value:"pipelineTitle"},
{labele:"流水线ID",value:"pipelineId"},
{labele:"运行Id",value:"historyId"},
{labele:"错误信息",value:"errors"},
{labele:"URL",value:"url"},
]
},
order: 5,
col: { span: 24 },
})
paramIntro = "";
async buildDefaultContent(req:BuildContentReq) {
const defaultTemplate = new PipelineResultEmailTemplateProvider()
const subject = "${result}${pipelineTitle}【${pipelineId}】";
const content = "流水线ID:${pipelineId},运行ID:${runtimeId} \n\n ${errors}";
defaultTemplate.titleTemplate = subject
defaultTemplate.contentTemplate = content
defaultTemplate.formatType = "text"
return await defaultTemplate.buildContent(req)
}
}
@@ -0,0 +1,37 @@
import { AddonInput, IsAddon } from "@certd/lib-server";
import { BuildContentReq, EmailContent, ITemplateProvider } from "../api.js";
import { BaseEmailTemplateProvider } from "./plugin-base.js";
@IsAddon({
addonType: "emailTemplate",
name: 'registerCode',
title: '注册验证码邮件模版',
desc: '注册验证码邮件模版',
icon: "simple-icons:email:blue",
showTest: false,
})
export class RegisterCodeEmailTemplateProvider extends BaseEmailTemplateProvider implements ITemplateProvider<EmailContent> {
@AddonInput({
title: "可用参数",
component: {
name: "ParamsShow",
params:[
{labele:"验证码",value:"code"}
]
},
order: 5,
col: { span: 24 },
})
paramIntro = "";
async buildDefaultContent(req:BuildContentReq) {
const defaultTemplate = new RegisterCodeEmailTemplateProvider()
defaultTemplate.titleTemplate = "注册验证码"
defaultTemplate.contentTemplate = "您的注册验证码是:${code},请勿泄露"
defaultTemplate.formatType = "text"
return await defaultTemplate.buildContent(req)
}
}
@@ -1,28 +0,0 @@
import { AddonInput, IsAddon } from "@certd/lib-server";
import { BaseEmailTemplateProvider } from "./plugin-base.js";
@IsAddon({
addonType: "emailTemplate",
name: 'register',
title: '注册邮件模版',
desc: '注册邮件模版',
icon:"simple-icons:gitee:red",
showTest: false,
})
export class RegisterEmailTemplateProvider extends BaseEmailTemplateProvider {
@AddonInput({
title: "可用参数",
component:{
name:"a-alert",
props:{
type:"info",
message:"站点名称:${siteTitle};注册验证码:${code};有效期:${duration}分钟",
}
},
order: 5,
col:{span:24},
})
paramIntro = "";
}
@@ -0,0 +1 @@
export * from "./email/index.js"