NestJS 实现定时自动发送 Exchange 邮件


定时任务

NestJS 定时任务可通过miaowing/nest-schedule实现。

定时任务的实现

  1. 按照 miaowing/nest-schedule 的说明文档,将 nest-schedule 模块导入到项目中:

     import { Module } from '@nestjs/common';
     import { ScheduleModule } from 'nest-schedule';
    
     @Module({
       imports: [
         ScheduleModule.register(),
       ]
     })
     export class AppModule {
     }
    
    
  2. 需要支持定时任务的服务继承 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');
       }
    
    
  3. 执行定时任务的方法添加@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 邮件发送实现

  1. 导入相关方法

     import {
       ExchangeService, ExchangeVersion,
       Uri as ExchangeUri,
       WebCredentials, EmailMessage, MessageBody, ConfigurationApi
     } from 'ews-javascript-api';
    
    
  2. 新建ExchangeService 服务

     const exch = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
    
    
  3. 配置登录凭据以及服务地址

     exch.Credentials = new WebCredentials(ewsConfig.username, ewsConfig.password);
     exch.Url = new ExchangeUri(ewsConfig.host);
    
    
  4. 构建要发送的邮件实体,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);
    
    
  5. 发送邮件

     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,可查阅文档。

15764854565097.jpg

技术 NestJS Node Crontab Cron Exchange