Files
certd/packages/ui/certd-server/src/modules/basic/service/email-service.ts
T

199 lines
6.1 KiB
TypeScript
Raw Normal View History

2024-07-15 00:30:33 +08:00
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
2025-12-14 01:36:20 +08:00
import type { EmailSend, EmailSendByTemplateReq } from '@certd/pipeline';
2024-11-04 16:39:02 +08:00
import { IEmailService } from '@certd/pipeline';
import { logger } from '@certd/basic';
2024-12-04 22:27:48 +08:00
import { isComm, isPlus } from '@certd/plus-core';
2024-11-04 16:39:02 +08:00
2023-06-25 15:30:18 +08:00
import nodemailer from 'nodemailer';
2025-03-05 22:38:36 +08:00
import { SendMailOptions } from 'nodemailer';
2024-07-15 00:30:33 +08:00
import { UserSettingsService } from '../../mine/service/user-settings-service.js';
2025-12-14 01:36:20 +08:00
import { AddonService, PlusService, SysEmailConf, SysSettingsService, SysSiteInfo } from '@certd/lib-server';
2024-10-11 03:40:24 +08:00
import { getEmailSettings } from '../../sys/settings/fix.js';
2025-05-31 00:45:54 +08:00
import { UserEmailSetting } from "../../mine/service/models.js";
2025-12-14 01:36:20 +08:00
import { AddonGetterService } from '../../pipeline/service/addon-getter-service.js';
import { EmailContent, ITemplateProvider } from '../../../plugins/plugin-template/api.js';
2023-06-25 15:30:18 +08:00
export type EmailConfig = {
host: string;
port: number;
auth: {
user: string;
pass: string;
};
secure: boolean; // use TLS
tls: {
// do not fail on invalid certs
rejectUnauthorized: boolean;
};
sender: string;
2024-08-23 11:35:34 +08:00
usePlus?: boolean;
2025-03-05 22:38:36 +08:00
} & SendMailOptions;
2023-06-25 15:30:18 +08:00
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
2023-06-25 15:30:18 +08:00
export class EmailService implements IEmailService {
@Inject()
settingsService: UserSettingsService;
@Inject()
sysSettingsService: SysSettingsService;
2024-08-23 11:35:34 +08:00
@Inject()
plusService: PlusService;
2025-12-14 01:36:20 +08:00
@Inject()
addonGetterService: AddonGetterService;
@Inject()
addonService: AddonService
2024-08-23 11:35:34 +08:00
async sendByPlus(email: EmailSend) {
if (!isPlus()) {
throw new Error('plus not enabled');
}
/**
* userId: number;
* subject: string;
* content: string;
* receivers: string[];
*/
2024-11-07 02:22:14 +08:00
await this.plusService.sendEmail(email);
2024-08-23 11:35:34 +08:00
}
2023-06-25 15:30:18 +08:00
/**
*/
async send(email: EmailSend) {
logger.info('sendEmail', email);
2024-11-25 23:48:04 +08:00
if (!email.receivers || email.receivers.length === 0) {
throw new Error('收件人不能为空');
}
2025-12-14 01:36:20 +08:00
let sysTitle = 'Certd';
if (isComm()) {
const siteInfo = await this.sysSettingsService.getSetting<SysSiteInfo>(SysSiteInfo);
if (siteInfo) {
sysTitle = siteInfo.title || sysTitle;
}
}
let subject = email.subject;
if (!subject) {
logger.error(new Error('邮件标题不能为空'));
subject = `邮件标题为空,请联系管理员排查`;
}
2025-12-14 01:36:20 +08:00
if (!subject.includes(`${sysTitle}`)) {
subject = `${sysTitle}${subject}`;
}
email.subject = subject;
2024-10-11 03:40:24 +08:00
const emailConf = await getEmailSettings(this.sysSettingsService, this.settingsService);
2023-06-25 15:30:18 +08:00
2024-10-11 03:40:24 +08:00
if (!emailConf.host && emailConf.usePlus == null) {
2024-08-23 11:35:34 +08:00
if (isPlus()) {
//自动使用plus发邮件
return await this.sendByPlus(email);
}
2024-11-26 01:39:19 +08:00
throw new Error('邮件服务器还未设置');
2023-06-25 15:30:18 +08:00
}
2025-12-14 01:36:20 +08:00
if (emailConf.usePlus && isPlus()) {
2024-08-23 11:35:34 +08:00
return await this.sendByPlus(email);
}
2025-12-14 01:36:20 +08:00
await this.sendByCustom(emailConf, email, sysTitle);
2024-08-23 11:35:34 +08:00
logger.info('sendEmail complete: ', email);
}
2025-12-14 01:36:20 +08:00
private async sendByCustom(emailConfig: EmailConfig, email: EmailSend, sysTitle: string) {
2023-06-25 15:30:18 +08:00
const transporter = nodemailer.createTransport(emailConfig);
2024-12-04 22:27:48 +08:00
2023-06-25 15:30:18 +08:00
const mailOptions = {
2024-12-04 22:27:48 +08:00
from: `${sysTitle} <${emailConfig.sender}>`,
2023-06-25 15:30:18 +08:00
to: email.receivers.join(', '), // list of receivers
2025-12-14 01:36:20 +08:00
subject: email.subject,
2023-06-25 15:30:18 +08:00
text: email.content,
2025-07-15 13:58:01 +08:00
html: email.html,
attachments: email.attachments,
2023-06-25 15:30:18 +08:00
};
await transporter.sendMail(mailOptions);
}
async test(userId: number, receiver: string) {
2025-12-14 01:36:20 +08:00
await this.sendByTemplate({
2025-12-15 22:32:25 +08:00
type: "common",
data: {
2025-12-14 01:36:20 +08:00
title: '测试邮件,from certd',
content: '测试邮件,from certd',
url: "https://certd.handfree.work",
2025-12-14 01:36:20 +08:00
},
receivers: [receiver],
2023-06-25 15:30:18 +08:00
});
}
2025-05-31 00:45:54 +08:00
async list(userId: any) {
2025-12-14 01:36:20 +08:00
const userEmailSetting = await this.settingsService.getSetting<UserEmailSetting>(userId, UserEmailSetting)
return userEmailSetting.list;
2025-05-31 00:45:54 +08:00
}
async delete(userId: any, email: string) {
2025-12-14 01:36:20 +08:00
const userEmailSetting = await this.settingsService.getSetting<UserEmailSetting>(userId, UserEmailSetting)
userEmailSetting.list = userEmailSetting.list.filter(item => item !== email);
await this.settingsService.saveSetting(userId, userEmailSetting)
2025-05-31 00:45:54 +08:00
}
async add(userId: any, email: string) {
2025-12-14 01:36:20 +08:00
const userEmailSetting = await this.settingsService.getSetting<UserEmailSetting>(userId, UserEmailSetting)
2025-05-31 00:45:54 +08:00
//如果已存在
2025-12-14 01:36:20 +08:00
if (userEmailSetting.list.includes(email)) {
2025-05-31 00:45:54 +08:00
return
}
userEmailSetting.list.unshift(email)
2025-12-14 01:36:20 +08:00
await this.settingsService.saveSetting(userId, userEmailSetting)
}
async sendByTemplate(req: EmailSendByTemplateReq) {
let content = null
const emailConf = await this.sysSettingsService.getSetting<SysEmailConf>(SysEmailConf);
const template = emailConf?.templates?.[req.type]
2026-01-30 17:10:31 +08:00
if (isPlus() && template && template.addonId) {
const addon: ITemplateProvider<EmailContent> = await this.addonGetterService.getAddonById(template.addonId, true, 0)
if (addon) {
content = await addon.buildContent({ data: req.data })
2025-12-14 01:36:20 +08:00
}
}
2026-01-30 17:10:31 +08:00
if (isPlus() && !content ) {
//看看有没有通用模版
if (emailConf?.templates?.common && emailConf?.templates?.common.addonId) {
const addon: ITemplateProvider<EmailContent> = await this.addonGetterService.getAddonById(emailConf.templates.common.addonId, true, 0)
if (addon) {
content = await addon.buildContent({ data: req.data })
2025-12-15 22:32:25 +08:00
}
}
}
// 没有找到模版,使用默认模版
if (!content) {
try {
const addon: ITemplateProvider<EmailContent> = await this.addonGetterService.getBlank("emailTemplate", req.type)
content = await addon.buildDefaultContent({ data: req.data })
} catch (e) {
// 对应的通知类型模版可能没有注册或者开发
2025-12-15 22:32:25 +08:00
}
2025-12-14 01:36:20 +08:00
}
2025-12-15 22:32:25 +08:00
2025-12-14 01:36:20 +08:00
if (!content) {
2025-12-15 22:32:25 +08:00
const addon: ITemplateProvider<EmailContent> = await this.addonGetterService.getBlank("emailTemplate", "common")
content = await addon.buildDefaultContent({ data: req.data })
2025-12-14 01:36:20 +08:00
}
return await this.send({
...content,
receivers: req.receivers,
attachments: req.attachments,
2025-12-14 01:36:20 +08:00
})
2025-05-31 00:45:54 +08:00
}
2023-06-25 15:30:18 +08:00
}