NestJS 实现定时自动发送 Exchange 邮件
定时任务
NestJS 定时任务可通过miaowing/nest-schedule实现。
定时任务的实现
按照 miaowing/nest-schedule 的说明文档,将 nest-schedule 模块导入到项目中:
import { Module } from '@nestjs/common'; import { ScheduleModule } from 'nest-schedule'; @Module({ imports: [ ScheduleModule.register(), ] }) export class AppModule { }
需要支持定时任务的服务继承 NestSchedule 类:
import { Injectable } from '@nestjs/common'; import { Cron, Interval, Timeout, NestSchedule } from 'nest-schedule'; @Injectable() // Only support SINGLETON scope export class ScheduleService extends NestSchedule { @Cron('15 16 * * *') async cronJob() { console.log('executing cron job'); }
执行定时任务的方法添加
@Cron('15 16 * * *')
注解。这条注解的意思是“每天16:15” 执行一次定时任务。这里似乎与常见的 Cron 表达式不一样:第一个数据段代表的不是秒而是分,由此可见,miaowing/nest-schedule 不支持秒级的定时任务。
以上三步就能实现定时任务的功能了。
需要留意的问题
- 不知何故,开启服务后,定时任务并没有执行,必须手动访问过任意一个 controller 之后,定时任务才会开启。开始猜测是在子模块导入的原因,尝试在根模块注入之后,问题依旧,难道与
Only support SINGLETON scope
有关系? @Cron('15 16 * * *')
中的表达式并非常见的 Cron 表达式,第一个数据段代表的是分而不是秒,且不支持如:“0/15 * * * *
每15分钟执行一次”这样的配置,逗号分隔符是支持的,因此每15分钟执行一次可以配置成“0,15,30,45”。
发送 Exchange 邮件
Exchange 邮件的支持借助gautamsi/ews-javascript-api来实现。
Exchange 邮件发送实现
导入相关方法
import { ExchangeService, ExchangeVersion, Uri as ExchangeUri, WebCredentials, EmailMessage, MessageBody, ConfigurationApi } from 'ews-javascript-api';
新建ExchangeService 服务
const exch = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
配置登录凭据以及服务地址
exch.Credentials = new WebCredentials(ewsConfig.username, ewsConfig.password); exch.Url = new ExchangeUri(ewsConfig.host);
构建要发送的邮件实体,Subject 为邮件的标题,Body 为邮件的内容,收件人地址可以通过
msgattach.ToRecipients.Add(address)
方法实现:const msgattach = new EmailMessage(exch); msgattach.Subject = mailContent.Subject; msgattach.Body = new MessageBody(mailContent.Body); msgattach.ToRecipients.Add(address);
发送邮件
msgattach.SendAndSaveCopy() // 此方法返回 Promise
按照上述步骤实现,可能会出现鉴权不通过的问题,可能是 NTLM 认证的问题,NTLM 是 telnet 的一种验证身份的方式,是 Windows NT 早期版本的标准安全协议。可以安装另外一个库@ewsjs/xhr, 尝试以下配置:
...
import { XhrApi } from '@ewsjs/xhr';
...
const exch = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
const xhr = new XhrApi({ rejectUnauthorized: false }).useNtlmAuthentication(username, password);
ConfigurationApi.ConfigureXHR(xhr);
完整代码如下:
import {
ExchangeService, ExchangeVersion,
Uri as ExchangeUri,
WebCredentials, EmailMessage, MessageBody, ConfigurationApi
} from 'ews-javascript-api';
/**
* 发邮件
*/
mailToSomeOne(address: string, mailContent: {
Subject: string,
Body: string
}): void {
const exch = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
const xhr = new XhrApi({ rejectUnauthorized: false }).useNtlmAuthentication(ewsConfig.username, ewsConfig.password);
ConfigurationApi.ConfigureXHR(xhr);
exch.Credentials = new WebCredentials(ewsConfig.username, ewsConfig.password);
exch.Url = new ExchangeUri(ewsConfig.host);
const msgattach = new EmailMessage(exch);
msgattach.Subject = mailContent.Subject;
msgattach.Body = new MessageBody(mailContent.Body);
msgattach.ToRecipients.Add(address);
msgattach.SendAndSaveCopy().then(res => {
console.log(res);
}, (err) => {
console.error(err);
});
}
需要留意的问题
- 权限部分可能并非使用 NTLM 方式,@ewsjs/xhr还支持 Cookies Auth,可查阅文档。