@@ -45,8 +45,30 @@ interface MailData {
4545 text ?: string ;
4646}
4747
48+ export type MailFailureStage = "template" | "smtp" ;
49+
50+ export class MailDeliveryError extends Error {
51+ constructor (
52+ readonly stage : MailFailureStage ,
53+ options ?: ErrorOptions ,
54+ ) {
55+ super ( `Mail ${ stage } failed` , options ) ;
56+ this . name = "MailDeliveryError" ;
57+ }
58+ }
59+
4860export async function sendMail ( data : MailData ) {
49- const t = getTransporter ( ) ;
61+ let t : Transporter | null ;
62+ try {
63+ t = getTransporter ( ) ;
64+ } catch ( error ) {
65+ appLog ( "mail.send.failed" , "ERROR" , {
66+ outcome : "failed" ,
67+ "mail.stage" : "smtp" ,
68+ "error.type" : errorType ( error ) ,
69+ } ) ;
70+ throw new MailDeliveryError ( "smtp" , { cause : error } ) ;
71+ }
5072 if ( ! t ) return ;
5173
5274 const from = process . env . SMTP_FROM || data . from ;
@@ -62,27 +84,35 @@ export async function sendMail(data: MailData) {
6284 appLog ( "mail.send.failed" , "ERROR" , {
6385 outcome : "failed" ,
6486 attempt : i ,
87+ "mail.stage" : "smtp" ,
6588 "error.type" : errorType ( err ) ,
6689 } ) ;
67- if ( i === 5 ) throw err ;
90+ if ( i === 5 ) throw new MailDeliveryError ( "smtp" , { cause : err } ) ;
6891 }
6992 }
7093}
7194
95+ async function buildAndSend ( to : string , build : ( ) => Promise < Omit < MailData , "from" | "to" > > ) {
96+ let content : Omit < MailData , "from" | "to" > ;
97+ try {
98+ content = await build ( ) ;
99+ } catch ( error ) {
100+ appLog ( "mail.template.failed" , "ERROR" , {
101+ outcome : "failed" ,
102+ "mail.stage" : "template" ,
103+ "error.type" : errorType ( error ) ,
104+ } ) ;
105+ throw new MailDeliveryError ( "template" , { cause : error } ) ;
106+ }
107+ await sendMail ( { from : "cnode@localhost" , to, ...content } ) ;
108+ }
109+
72110export async function sendActiveMail ( email : string , key : string ) {
73- await sendMail ( {
74- from : "cnode@localhost" ,
75- to : email ,
76- ...( await buildActiveMail ( key ) ) ,
77- } ) ;
111+ await buildAndSend ( email , ( ) => buildActiveMail ( key ) ) ;
78112}
79113
80114export async function sendResetPassMail ( email : string , key : string ) {
81- await sendMail ( {
82- from : "cnode@localhost" ,
83- to : email ,
84- ...( await buildResetPassMail ( key ) ) ,
85- } ) ;
115+ await buildAndSend ( email , ( ) => buildResetPassMail ( key ) ) ;
86116}
87117
88118export async function sendReplyNotifyMail (
@@ -91,11 +121,7 @@ export async function sendReplyNotifyMail(
91121 replyContent : string ,
92122 topicUrl : string ,
93123) {
94- await sendMail ( {
95- from : "cnode@localhost" ,
96- to : email ,
97- ...( await buildReplyNotifyMail ( topicTitle , replyContent , topicUrl ) ) ,
98- } ) ;
124+ await buildAndSend ( email , ( ) => buildReplyNotifyMail ( topicTitle , replyContent , topicUrl ) ) ;
99125}
100126
101127export async function sendAtNotifyMail (
@@ -104,9 +130,5 @@ export async function sendAtNotifyMail(
104130 replyContent : string ,
105131 topicUrl : string ,
106132) {
107- await sendMail ( {
108- from : "cnode@localhost" ,
109- to : email ,
110- ...( await buildAtNotifyMail ( topicTitle , replyContent , topicUrl ) ) ,
111- } ) ;
133+ await buildAndSend ( email , ( ) => buildAtNotifyMail ( topicTitle , replyContent , topicUrl ) ) ;
112134}
0 commit comments