109 lines
3.2 KiB
JavaScript
109 lines
3.2 KiB
JavaScript
|
|
/**
|
||
|
|
* 이메일 발송 서비스 (Resend 사용)
|
||
|
|
* AWS SES 대신 Resend API를 사용하여 이메일 발송
|
||
|
|
*/
|
||
|
|
const { Resend } = require("resend");
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Resend 클라이언트 생성
|
||
|
|
* DB에서 API 키를 가져와 설정
|
||
|
|
* @returns {Promise<{resend: Resend, from: string}>}
|
||
|
|
*/
|
||
|
|
const createClient = async () => {
|
||
|
|
const SystemConfig = require("../models/SystemConfig");
|
||
|
|
|
||
|
|
// DB에서 설정 조회
|
||
|
|
const configs = await SystemConfig.findAll();
|
||
|
|
const configMap = {};
|
||
|
|
configs.forEach((c) => (configMap[c.key] = c.value));
|
||
|
|
|
||
|
|
// DB 설정 우선, 없으면 환경 변수 사용
|
||
|
|
const apiKey = configMap["resend_api_key"] || process.env.RESEND_API_KEY;
|
||
|
|
|
||
|
|
if (!apiKey) {
|
||
|
|
throw new Error("Resend API 키가 설정되지 않았습니다");
|
||
|
|
}
|
||
|
|
|
||
|
|
const resend = new Resend(apiKey);
|
||
|
|
|
||
|
|
return {
|
||
|
|
resend,
|
||
|
|
from:
|
||
|
|
configMap["mail_from"] || process.env.MAIL_FROM || "admin@caadiq.co.kr",
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 이메일 발송 (Resend 사용)
|
||
|
|
* @param {Object} params - 이메일 파라미터
|
||
|
|
* @param {string} [params.from] - 발신자 (선택, 없으면 기본값 사용)
|
||
|
|
* @param {string} params.to - 수신자
|
||
|
|
* @param {string} params.subject - 제목
|
||
|
|
* @param {string} params.html - HTML 본문
|
||
|
|
* @param {string} [params.text] - 텍스트 본문 (선택)
|
||
|
|
* @param {Array} [params.attachments] - 첨부파일 배열 (선택)
|
||
|
|
* @returns {Promise<Object>} Resend 발송 결과
|
||
|
|
*/
|
||
|
|
exports.sendEmail = async ({
|
||
|
|
from: senderEmail,
|
||
|
|
to,
|
||
|
|
subject,
|
||
|
|
html,
|
||
|
|
text,
|
||
|
|
attachments = [],
|
||
|
|
}) => {
|
||
|
|
try {
|
||
|
|
const { resend, from: defaultFrom } = await createClient();
|
||
|
|
|
||
|
|
// 발신자: 전달된 값 또는 기본값 사용
|
||
|
|
const actualFrom = senderEmail || defaultFrom;
|
||
|
|
|
||
|
|
// 첨부파일 변환 (Resend 형식: filename, content as Buffer/base64)
|
||
|
|
const resendAttachments = attachments.map((att) => ({
|
||
|
|
filename: att.filename,
|
||
|
|
content: Buffer.isBuffer(att.content)
|
||
|
|
? att.content
|
||
|
|
: Buffer.from(att.content, "base64"),
|
||
|
|
}));
|
||
|
|
|
||
|
|
console.log(`[Resend] 첨부파일 개수: ${attachments.length}`);
|
||
|
|
if (attachments.length > 0) {
|
||
|
|
console.log(
|
||
|
|
`[Resend] 첨부파일 정보:`,
|
||
|
|
attachments.map((a) => ({
|
||
|
|
filename: a.filename,
|
||
|
|
contentType: a.contentType,
|
||
|
|
contentLength: a.content?.length || 0,
|
||
|
|
}))
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// to가 배열이면 그대로, 문자열이면 배열로 변환
|
||
|
|
const toArray = Array.isArray(to) ? to : to.split(",").map((e) => e.trim());
|
||
|
|
|
||
|
|
// html과 text 기본값 처리 (둘 다 없으면 빈 공백이라도 전달)
|
||
|
|
const finalHtml = html || text || " ";
|
||
|
|
const finalText = text || (html ? html.replace(/<[^>]*>/g, "") : " ");
|
||
|
|
|
||
|
|
const result = await resend.emails.send({
|
||
|
|
from: actualFrom,
|
||
|
|
to: toArray,
|
||
|
|
subject: subject || "(제목 없음)",
|
||
|
|
html: finalHtml,
|
||
|
|
text: finalText,
|
||
|
|
attachments: resendAttachments.length > 0 ? resendAttachments : undefined,
|
||
|
|
});
|
||
|
|
|
||
|
|
if (result.error) {
|
||
|
|
console.error("[Resend] 발송 오류:", result.error);
|
||
|
|
throw new Error(result.error.message);
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log("[Resend] 메일 발송 완료:", result.data?.id);
|
||
|
|
return { messageId: result.data?.id };
|
||
|
|
} catch (error) {
|
||
|
|
console.error("[Resend] 발송 오류:", error);
|
||
|
|
throw error;
|
||
|
|
}
|
||
|
|
};
|