import { Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { sign } from '../../common/jwt'; import sendgrid from '@sendgrid/mail'; import { getPrivateKey } from '../../common/jwt/jwt'; import { Context } from '../../common/log'; import { readFileSync } from 'node:fs'; import path from 'node:path'; import { PRIMARY_ADMIN_NAME, AUTHOR_NAME, CUSTOMER_NAME, DEALER_NAME, FILE_NAME, LICENSE_QUANTITY, PO_NUMBER, TOP_URL, USER_EMAIL, USER_NAME, TYPIST_NAME, VERIFY_LINK, TEMPORARY_PASSWORD, } from '../../templates/constants'; import { URL } from 'node:url'; @Injectable() export class SendGridService { private readonly logger = new Logger(SendGridService.name); private readonly emailConfirmLifetime: number; private readonly appDomain: string; private readonly mailFrom: string; private readonly templateU101Html: string; private readonly templateU101Text: string; private readonly templateU102Html: string; private readonly templateU102Text: string; private readonly templateU105Html: string; private readonly templateU105Text: string; private readonly templateU106Html: string; private readonly templateU106Text: string; private readonly templateU107Html: string; private readonly templateU107Text: string; private readonly templateU108Html: string; private readonly templateU108Text: string; // U-108のテンプレート差分(親アカウントがない場合) private readonly templateU108NoParentHtml: string; private readonly templateU108NoParentText: string; private readonly templateU109Html: string; private readonly templateU109Text: string; private readonly templateU111Html: string; private readonly templateU111Text: string; private readonly templateU112Html: string; private readonly templateU112Text: string; // U-112のテンプレート差分(親アカウントがない場合) private readonly templateU112NoParentHtml: string; private readonly templateU112NoParentText: string; private readonly templateU113Html: string; private readonly templateU113Text: string; private readonly templateU114Html: string; private readonly templateU114Text: string; private readonly templateU115Html: string; private readonly templateU115Text: string; private readonly templateU117Html: string; private readonly templateU117Text: string; constructor(private readonly configService: ConfigService) { this.appDomain = this.configService.getOrThrow('APP_DOMAIN'); this.mailFrom = this.configService.getOrThrow('MAIL_FROM'); this.emailConfirmLifetime = this.configService.getOrThrow( 'EMAIL_CONFIRM_LIFETIME', ); const key = this.configService.getOrThrow('SENDGRID_API_KEY'); sendgrid.setApiKey(key); // メールテンプレートを読み込む { this.templateU101Html = readFileSync( path.resolve(__dirname, `../../templates/template_U_101.html`), 'utf-8', ); this.templateU101Text = readFileSync( path.resolve(__dirname, `../../templates/template_U_101.txt`), 'utf-8', ); this.templateU102Html = readFileSync( path.resolve(__dirname, `../../templates/template_U_102.html`), 'utf-8', ); this.templateU102Text = readFileSync( path.resolve(__dirname, `../../templates/template_U_102.txt`), 'utf-8', ); this.templateU105Html = readFileSync( path.resolve(__dirname, `../../templates/template_U_105.html`), 'utf-8', ); this.templateU105Text = readFileSync( path.resolve(__dirname, `../../templates/template_U_105.txt`), 'utf-8', ); this.templateU106Html = readFileSync( path.resolve(__dirname, `../../templates/template_U_106.html`), 'utf-8', ); this.templateU106Text = readFileSync( path.resolve(__dirname, `../../templates/template_U_106.txt`), 'utf-8', ); this.templateU107Html = readFileSync( path.resolve(__dirname, `../../templates/template_U_107.html`), 'utf-8', ); this.templateU107Text = readFileSync( path.resolve(__dirname, `../../templates/template_U_107.txt`), 'utf-8', ); this.templateU108Html = readFileSync( path.resolve(__dirname, `../../templates/template_U_108.html`), 'utf-8', ); this.templateU108Text = readFileSync( path.resolve(__dirname, `../../templates/template_U_108.txt`), 'utf-8', ); this.templateU108NoParentHtml = readFileSync( path.resolve( __dirname, `../../templates/template_U_108_no_parent.html`, ), 'utf-8', ); this.templateU108NoParentText = readFileSync( path.resolve(__dirname, `../../templates/template_U_108_no_parent.txt`), 'utf-8', ); this.templateU109Html = readFileSync( path.resolve(__dirname, `../../templates/template_U_109.html`), 'utf-8', ); this.templateU109Text = readFileSync( path.resolve(__dirname, `../../templates/template_U_109.txt`), 'utf-8', ); this.templateU111Html = readFileSync( path.resolve(__dirname, `../../templates/template_U_111.html`), 'utf-8', ); this.templateU111Text = readFileSync( path.resolve(__dirname, `../../templates/template_U_111.txt`), 'utf-8', ); this.templateU112Html = readFileSync( path.resolve(__dirname, `../../templates/template_U_112.html`), 'utf-8', ); this.templateU112Text = readFileSync( path.resolve(__dirname, `../../templates/template_U_112.txt`), 'utf-8', ); this.templateU112NoParentHtml = readFileSync( path.resolve( __dirname, `../../templates/template_U_112_no_parent.html`, ), 'utf-8', ); this.templateU112NoParentText = readFileSync( path.resolve(__dirname, `../../templates/template_U_112_no_parent.txt`), 'utf-8', ); this.templateU113Html = readFileSync( path.resolve(__dirname, `../../templates/template_U_113.html`), 'utf-8', ); this.templateU113Text = readFileSync( path.resolve(__dirname, `../../templates/template_U_113.txt`), 'utf-8', ); this.templateU114Html = readFileSync( path.resolve(__dirname, `../../templates/template_U_114.html`), 'utf-8', ); this.templateU114Text = readFileSync( path.resolve(__dirname, `../../templates/template_U_114.txt`), 'utf-8', ); this.templateU115Html = readFileSync( path.resolve(__dirname, `../../templates/template_U_115.html`), 'utf-8', ); this.templateU115Text = readFileSync( path.resolve(__dirname, `../../templates/template_U_115.txt`), 'utf-8', ); this.templateU117Html = readFileSync( path.resolve(__dirname, `../../templates/template_U_117.html`), 'utf-8', ); this.templateU117Text = readFileSync( path.resolve(__dirname, `../../templates/template_U_117.txt`), 'utf-8', ); } } /** * U-101のテンプレートを使用したメールを送信する * @param context * @param customerMail アカウント登録を行った管理者ユーザーのメールアドレス * @param customerAccountName アカウント登録した会社名 * @returns mail with u101 */ async sendMailWithU101( context: Context, customerMail: string, customerAccountName: string, ): Promise { this.logger.log( `[IN] [${context.getTrackingId()}] ${this.sendMailWithU101.name}`, ); try { const subject = 'Account Registered Notification [U-101]'; const url = new URL(this.appDomain).href; const html = this.templateU101Html .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(TOP_URL, url); const text = this.templateU101Text .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(TOP_URL, url); await this.sendMail( context, [customerMail], [], this.mailFrom, subject, text, html, ); } finally { this.logger.log( `[OUT] [${context.getTrackingId()}] ${this.sendMailWithU101.name}`, ); } } /** * U-102のテンプレートを使用したメールを送信する * @param context * @param adminEmail 登録したアカウントの管理者(primary)のメールアドレス * @param accountId 登録したアカウントのID * @param userId 登録したユーザーのID * @returns mail with u102 */ async sendMailWithU102( context: Context, adminEmail: string, accountId: number, userId: number, ): Promise { this.logger.log( `[IN] [${context.getTrackingId()}] ${this.sendMailWithU102.name}`, ); try { const privateKey = getPrivateKey(this.configService); const token = sign<{ accountId: number; userId: number; email: string }>( { accountId, userId, email: adminEmail, }, this.emailConfirmLifetime, privateKey, ); const paths = path.join('mail-confirm'); const url = new URL(paths, this.appDomain).href; const verifyUrl = `${url}?verify=${token}`; const subject = 'User Registration Notification [U-102]'; const html = this.templateU102Html.replaceAll(VERIFY_LINK, verifyUrl); const text = this.templateU102Text.replaceAll(VERIFY_LINK, verifyUrl); await this.sendMail( context, [adminEmail], [], this.mailFrom, subject, text, html, ); } finally { this.logger.log( `[OUT] [${context.getTrackingId()}] ${this.sendMailWithU102.name}`, ); } } /** * U-105のテンプレートを使用したメールを送信する * @param context * @param customerMails 注文を行ったアカウントの管理者(primary/secondary)のメールアドレス * @param customerAccountName 送信対象の企業名 * @param lisenceCount 注文を行った対象の注文の内容(ライセンス数) * @param poNumber 注文を行った対象の注文の内容(PO番号) * @param dealerEmails 問題発生時に問い合わせする先の上位のディーラーの管理者(primary/secondary)のメールアドレス * @param dealerAccountName 問題発生時に問い合わせする先の上位のディーラー名(会社名) * @returns mail with u105 */ async sendMailWithU105( context: Context, customerMails: string[], customerAccountName: string, lisenceCount: number, poNumber: string, dealerEmails: string[], dealerAccountName: string, ): Promise { this.logger.log( `[IN] [${context.getTrackingId()}] ${this.sendMailWithU105.name}`, ); try { const subject = 'License Requested Notification [U-105]'; // メールの本文を作成する const html = this.templateU105Html .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(DEALER_NAME, dealerAccountName) .replaceAll(PO_NUMBER, poNumber) .replaceAll(LICENSE_QUANTITY, `${lisenceCount}`); const text = this.templateU105Text .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(DEALER_NAME, dealerAccountName) .replaceAll(PO_NUMBER, poNumber) .replaceAll(LICENSE_QUANTITY, `${lisenceCount}`); // メールを送信する await this.sendMail( context, customerMails, dealerEmails, this.mailFrom, subject, text, html, ); } finally { this.logger.log( `[OUT] [${context.getTrackingId()}] ${this.sendMailWithU105.name}`, ); } } /** * U-106のテンプレートを使用したメールを送信する * @param context * @param cancelUserEmailAddress 注文キャンセルを行ったアカウントの管理者(primary/secondary)のメールアドレス * @param customerAccountName 送信対象の企業名 * @param lisenceCount 注文キャンセルを行った対象の注文の内容(ライセンス数) * @param poNumber 注文キャンセルを行った対象の注文の内容(PO番号) * @param dealerEmails 問題発生時に問い合わせする先の上位のディーラーの管理者(primary/secondary)のメールアドレス * @param dealerAccountName 問題発生時に問い合わせする先の上位のディーラー名(会社名) * @returns mail with u106 */ async sendMailWithU106( context: Context, customerMails: string[], customerAccountName: string, lisenceCount: number, poNumber: string, dealerEmails: string[], dealerAccountName: string, ): Promise { this.logger.log( `[IN] [${context.getTrackingId()}] ${this.sendMailWithU106.name}`, ); try { const subject = 'Cancelled License Order Notification [U-106]'; // メールの本文を作成する const html = this.templateU106Html .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(DEALER_NAME, dealerAccountName) .replaceAll(PO_NUMBER, poNumber) .replaceAll(LICENSE_QUANTITY, `${lisenceCount}`); const text = this.templateU106Text .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(DEALER_NAME, dealerAccountName) .replaceAll(PO_NUMBER, poNumber) .replaceAll(LICENSE_QUANTITY, `${lisenceCount}`); // メールを送信する await this.sendMail( context, customerMails, dealerEmails, this.mailFrom, subject, text, html, ); } finally { this.logger.log( `[OUT] [${context.getTrackingId()}] ${this.sendMailWithU106.name}`, ); } } /** * U-107のテンプレートを使用したメールを送信する * @param context * @param cancelUserEmailAddress ライセンス発行をされたアカウントの管理者(primary/secondary)のメールアドレス * @param customerAccountName 送信対象の企業名 * @param lisenceCount ライセンス発行を行った対象の注文の内容(ライセンス数) * @param poNumber ライセンス発行を行った対象の注文の内容(PO番号) * @param dealerEmails 問題発生時に問い合わせする先の上位のディーラーの管理者(primary/secondary)のメールアドレス * @param dealerAccountName 問題発生時に問い合わせする先の上位のディーラー名(会社名) * @returns mail with u107 */ async sendMailWithU107( context: Context, customerMails: string[], customerAccountName: string, lisenceCount: number, poNumber: string, dealerEmails: string[], dealerAccountName: string, ): Promise { this.logger.log( `[IN] [${context.getTrackingId()}] ${this.sendMailWithU107.name}`, ); try { const subject = 'License Issued Notification [U-107]'; // メールの本文を作成する const html = this.templateU107Html .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(DEALER_NAME, dealerAccountName) .replaceAll(PO_NUMBER, poNumber) .replaceAll(LICENSE_QUANTITY, `${lisenceCount}`); const text = this.templateU107Text .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(DEALER_NAME, dealerAccountName) .replaceAll(PO_NUMBER, poNumber) .replaceAll(LICENSE_QUANTITY, `${lisenceCount}`); // メールを送信する await this.sendMail( context, customerMails, dealerEmails, this.mailFrom, subject, text, html, ); } finally { this.logger.log( `[OUT] [${context.getTrackingId()}] ${this.sendMailWithU107.name}`, ); } } /** * U-108のテンプレートを使用したメールを送信する * @param context * @param userName ライセンス割り当てされたユーザーの名前 * @param userMail ライセンス割り当てされたユーザーのメールアドレス * @param customerAdminMails ライセンス割り当てされたユーザーの所属するアカウントの管理者(primary/secondary)のメールアドレス * @param customerAccountName ライセンス割り当てされたユーザーの所属するアカウントの名前 * @param dealerAccountName 問題発生時に問い合わせする先の上位のディーラー名(会社名) * @returns mail with u108 */ async sendMailWithU108( context: Context, userName: string, userMail: string, customerAdminMails: string[], customerAccountName: string, dealerAccountName: string | null, ): Promise { this.logger.log( `[IN] [${context.getTrackingId()}] ${this.sendMailWithU108.name}`, ); try { const subject = 'License Assigned Notification [U-108]'; const url = new URL(this.appDomain).href; let html: string; let text: string; if (dealerAccountName === null) { html = this.templateU108NoParentHtml .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(USER_NAME, userName) .replaceAll(USER_EMAIL, userMail) .replaceAll(TOP_URL, url); text = this.templateU108NoParentText .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(USER_NAME, userName) .replaceAll(USER_EMAIL, userMail) .replaceAll(TOP_URL, url); } else { html = this.templateU108Html .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(DEALER_NAME, dealerAccountName) .replaceAll(USER_NAME, userName) .replaceAll(USER_EMAIL, userMail) .replaceAll(TOP_URL, url); text = this.templateU108Text .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(DEALER_NAME, dealerAccountName) .replaceAll(USER_NAME, userName) .replaceAll(USER_EMAIL, userMail) .replaceAll(TOP_URL, url); } const ccAddress = customerAdminMails.includes(userMail) ? [] : [userMail]; // メールを送信する await this.sendMail( context, customerAdminMails, ccAddress, this.mailFrom, subject, text, html, ); } finally { this.logger.log( `[OUT] [${context.getTrackingId()}] ${this.sendMailWithU108.name}`, ); } } /** * U-109のテンプレートを使用したメールを送信する * @param context context * @param dealerEmails ライセンス発行をキャンセルした上位アカウントの管理者(primary/secondary)のメールアドレス * @param dealerAccountName ライセンス発行をキャンセルした上位アカウントの会社名 * @param lisenceCount ライセンス発行をキャンセルした対象の注文の内容(ライセンス数) * @param poNumber ライセンス発行をキャンセルした対象の注文の内容(PO番号) * @param customerMails ライセンス発行をキャンセルされたアカウントの管理者(primary/secondary)のメールアドレス * @param customerAccountName ライセンス発行をキャンセルされたアカウントの会社名 * @returns */ async sendMailWithU109( context: Context, dealerEmails: string[], dealerAccountName: string, lisenceCount: number, poNumber: string, customerMails: string[], customerAccountName: string, ): Promise { this.logger.log( `[IN] [${context.getTrackingId()}] ${this.sendMailWithU109.name}`, ); try { const subject = 'License Returned Notification [U-109]'; // メールの本文を作成する const html = this.templateU109Html .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(DEALER_NAME, dealerAccountName) .replaceAll(PO_NUMBER, poNumber) .replaceAll(LICENSE_QUANTITY, `${lisenceCount}`); const text = this.templateU109Text .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(DEALER_NAME, dealerAccountName) .replaceAll(PO_NUMBER, poNumber) .replaceAll(LICENSE_QUANTITY, `${lisenceCount}`); // メールを送信する await this.sendMail( context, dealerEmails, customerMails, this.mailFrom, subject, text, html, ); } finally { this.logger.log( `[OUT] [${context.getTrackingId()}] ${this.sendMailWithU109.name}`, ); } } /** * U-111のテンプレートを使用したメールを送信する * @param context * @param primaryAdminName 削除されたアカウントの管理者(primary)の名前 * @param primaryAdminMail 削除されたアカウントの管理者(primary)のメールアドレス * @param customerAccountName 削除されたアカウントの会社名 * @returns mail with u111 */ async sendMailWithU111( context: Context, primaryAdminName: string, primaryAdminMail: string, customerAccountName: string, ): Promise { this.logger.log( `[IN] [${context.getTrackingId()}] ${this.sendMailWithU111.name}`, ); try { const subject = 'Account Deleted Notification [U-111]'; const url = new URL(this.appDomain).href; // メールの本文を作成する const html = this.templateU111Html .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName) .replaceAll(TOP_URL, url); const text = this.templateU111Text .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName) .replaceAll(TOP_URL, url); // メールを送信する await this.sendMail( context, [primaryAdminMail], [], this.mailFrom, subject, text, html, ); } finally { this.logger.log( `[OUT] [${context.getTrackingId()}] ${this.sendMailWithU111.name}`, ); } } /** * U-112のテンプレートを使用したメールを送信する * @param context * @param primaryAdminName 情報変更を行ったアカウントの管理者(primary)の名前 * @param primaryAdminMail 情報変更を行ったアカウントの管理者(primary)のメールアドレス * @param customerAccountName 情報変更を行ったアカウントの名前 * @param dealerAccountName 問題発生時に問い合わせする先の上位のディーラー名(会社名) * @returns mail with u112 */ async sendMailWithU112( context: Context, primaryAdminName: string, primaryAdminMail: string, customerAccountName: string, dealerAccountName: string | null, ): Promise { this.logger.log( `[IN] [${context.getTrackingId()}] ${this.sendMailWithU112.name}`, ); try { const subject = 'Account Edit Notification [U-112]'; let html: string; let text: string; const url = new URL(this.appDomain).href; // 親アカウントがない場合は別のテンプレートを使用する if (dealerAccountName === null) { // メールの本文を作成する html = this.templateU112NoParentHtml .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName) .replaceAll(TOP_URL, url); text = this.templateU112NoParentText .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName) .replaceAll(TOP_URL, url); } else { html = this.templateU112Html .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(DEALER_NAME, dealerAccountName) .replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName) .replaceAll(TOP_URL, url); text = this.templateU112Text .replaceAll(CUSTOMER_NAME, customerAccountName) .replaceAll(DEALER_NAME, dealerAccountName) .replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName) .replaceAll(TOP_URL, url); } // メールを送信する await this.sendMail( context, [primaryAdminMail], [], this.mailFrom, subject, text, html, ); } finally { this.logger.log( `[OUT] [${context.getTrackingId()}] ${this.sendMailWithU112.name}`, ); } } /** * U-113のテンプレートを使用したメールを送信する * @param context * @param userMail 認証済みユーザーのメールアドレス * @param primaryAdminName 認証済みユーザーが所属するアカウントの管理者(primary)の名前 * @param temporaryPassword 認証済みユーザーの一時パスワード * @returns mail with u113 */ async sendMailWithU113( context: Context, userMail: string, primaryAdminName: string, temporaryPassword: string, ): Promise { this.logger.log( `[IN] [${context.getTrackingId()}] ${this.sendMailWithU113.name}`, ); try { const subject = 'Temporary password [U-113]'; // メールの本文を作成する const html = this.templateU113Html .replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName) .replaceAll(TEMPORARY_PASSWORD, temporaryPassword); const text = this.templateU113Text .replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName) .replaceAll(TEMPORARY_PASSWORD, temporaryPassword); // メールを送信する await this.sendMail( context, [userMail], [], this.mailFrom, subject, text, html, ); } finally { this.logger.log( `[OUT] [${context.getTrackingId()}] ${this.sendMailWithU113.name}`, ); } } /** * U-114のテンプレートを使用したメールを送信する * @param context * @param accountId 認証対象のユーザーが所属するアカウントのID * @param userId 認証対象のユーザーのID * @param userMail 認証対象のユーザーのメールアドレス * @param primaryAdminName 認証対象のユーザーが所属するアカウントの管理者(primary)の名前 * @returns mail with u114 */ async sendMailWithU114( context: Context, accountId: number, userId: number, userMail: string, primaryAdminName: string, ): Promise { this.logger.log( `[IN] [${context.getTrackingId()}] ${this.sendMailWithU114.name}`, ); try { // ユーザー認証用のトークンを作成する const privateKey = getPrivateKey(this.configService); const token = sign<{ accountId: number; userId: number; email: string }>( { accountId, userId, email: userMail, }, this.emailConfirmLifetime, privateKey, ); const paths = path.join('mail-confirm', '/user'); const url = new URL(paths, this.appDomain); const verifyUrl = `${url}?verify=${token}`; const subject = 'User Registration Notification [U-114]'; // メールの本文を作成する const html = this.templateU114Html .replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName) .replaceAll(VERIFY_LINK, verifyUrl); const text = this.templateU114Text .replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName) .replaceAll(VERIFY_LINK, verifyUrl); // メールを送信する await this.sendMail( context, [userMail], [], this.mailFrom, subject, text, html, ); } finally { this.logger.log( `[OUT] [${context.getTrackingId()}] ${this.sendMailWithU114.name}`, ); } } /** * U-115のテンプレートを使用したメールを送信する * @param context * @param userName 情報更新が行われたユーザーの名前 * @param userMail 情報更新が行われたユーザーのメールアドレス * @param primaryAdminName 情報更新が行われたユーザーの所属するアカウントの管理者(primary)の名前 * @param adminMails 情報更新が行われたユーザーの所属するアカウントの管理者(primary/secondary)のメールアドレス * @returns mail with u115 */ async sendMailWithU115( context: Context, userName: string, userMail: string, primaryAdminName: string, adminMails: string[], ): Promise { this.logger.log( `[IN] [${context.getTrackingId()}] ${this.sendMailWithU115.name}`, ); try { const subject = 'Edit User Notification [U-115]'; // メールの本文を作成する const html = this.templateU115Html .replaceAll(USER_NAME, userName) .replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName); const text = this.templateU115Text .replaceAll(USER_NAME, userName) .replaceAll(PRIMARY_ADMIN_NAME, primaryAdminName); // 管理者ユーザーの情報を変更した場合にはTOに管理者のメールアドレスを設定するので、CCには管理者のメールアドレスを設定しない const ccAdminMails = adminMails.filter((x) => x !== userMail); // メールを送信する await this.sendMail( context, [userMail], ccAdminMails, this.mailFrom, subject, text, html, ); } finally { this.logger.log( `[OUT] [${context.getTrackingId()}] ${this.sendMailWithU115.name}`, ); } } /** * U-117のテンプレートを使用したメールを送信する * @param context * @param authorEmail 文字起こしファイルのAuthorのメールアドレス * @param typistEmail 文字起こしを行ったTypistのメールアドレス * @param authorName 文字起こしファイルのAuthorの名前 * @param fileName 文字起こしファイルのファイル名 * @param typistName 文字起こしを行ったTypistの名前 * @param adminName アカウント管理者の名前(プライマリ) * @returns mail with u117 */ async sendMailWithU117( context: Context, authorEmail: string | null, typistEmail: string, authorName: string, fileName: string, typistName: string, adminName: string, ): Promise { this.logger.log( `[IN] [${context.getTrackingId()}] ${this.sendMailWithU117.name}`, ); try { const subject = 'Transcription Completion Notification [U-117]'; // メールの本文を作成する const html = this.templateU117Html .replaceAll(AUTHOR_NAME, authorName) .replaceAll(FILE_NAME, fileName) .replaceAll(TYPIST_NAME, typistName) .replaceAll(PRIMARY_ADMIN_NAME, adminName); const text = this.templateU117Text .replaceAll(AUTHOR_NAME, authorName) .replaceAll(FILE_NAME, fileName) .replaceAll(TYPIST_NAME, typistName) .replaceAll(PRIMARY_ADMIN_NAME, adminName); // メールを送信する await this.sendMail( context, [authorEmail, typistEmail].filter((x): x is string => x !== null), // authorEmailがnullの場合は除外する [], this.mailFrom, subject, text, html, ); } finally { this.logger.log( `[OUT] [${context.getTrackingId()}] ${this.sendMailWithU117.name}`, ); } } /** * メールを送信する * @param context * @param to * @param cc * @param from * @param subject * @param text * @param html * @returns mail */ async sendMail( context: Context, to: string[], cc: string[], from: string, subject: string, text: string, html: string, ): Promise { this.logger.log(`[IN] [${context.getTrackingId()}] ${this.sendMail.name}`); try { const res = await sendgrid .send({ from: { email: from, }, to: to.map((v) => ({ email: v })), cc: cc.map((v) => ({ email: v })), subject: subject, text: text, html: html, }) .then((v) => v[0]); this.logger.log( `[${context.getTrackingId()}] status code: ${ res.statusCode } body: ${JSON.stringify(res.body)}`, ); } catch (e) { this.logger.error(`[${context.getTrackingId()}] error=${e}`); throw e; } finally { this.logger.log( `[OUT] [${context.getTrackingId()}] ${this.sendMail.name}`, ); } } }