import nodemailer from 'nodemailer'; import handlebars from 'handlebars'; import fs from 'fs'; import path from 'path'; import i18next from 'i18next'; // Nodemailer Transporter 생성 const transporter = nodemailer.createTransport({ host: process.env.Email_Host, port: parseInt(process.env.Email_Port || '465'), secure: process.env.Email_Secure === 'true', auth: { user: process.env.Email_User_Name, pass: process.env.Email_Password, }, }); // 헬퍼 함수들 등록 function registerHandlebarsHelpers() { // i18next 헬퍼 등록 handlebars.registerHelper('t', function(key: string, options: { hash?: Record }) { // options.hash에는 Handlebars에서 넘긴 named parameter들이 들어있음 return i18next.t(key, options.hash || {}); }); // eq 헬퍼 등록 - 두 값을 비교 (블록 헬퍼) handlebars.registerHelper('eq', function(a: any, b: any, options: any) { if (a === b) { return options.fn(this); } else { return options.inverse(this); } }); // 기타 유용한 헬퍼들 handlebars.registerHelper('ne', function(a: any, b: any, options: any) { if (a !== b) { return options.fn(this); } else { return options.inverse(this); } }); handlebars.registerHelper('gt', function(a: any, b: any, options: any) { if (a > b) { return options.fn(this); } else { return options.inverse(this); } }); handlebars.registerHelper('gte', function(a: any, b: any, options: any) { if (a >= b) { return options.fn(this); } else { return options.inverse(this); } }); handlebars.registerHelper('lt', function(a: any, b: any, options: any) { if (a < b) { return options.fn(this); } else { return options.inverse(this); } }); handlebars.registerHelper('lte', function(a: any, b: any, options: any) { if (a <= b) { return options.fn(this); } else { return options.inverse(this); } }); // and 헬퍼 - 모든 조건이 true인지 확인 (블록 헬퍼) handlebars.registerHelper('and', function(...args: any[]) { // 마지막 인자는 Handlebars 옵션 const options = args[args.length - 1]; const values = args.slice(0, -1); if (values.every(Boolean)) { return options.fn(this); } else { return options.inverse(this); } }); // or 헬퍼 - 하나라도 true인지 확인 (블록 헬퍼) handlebars.registerHelper('or', function(...args: any[]) { // 마지막 인자는 Handlebars 옵션 const options = args[args.length - 1]; const values = args.slice(0, -1); if (values.some(Boolean)) { return options.fn(this); } else { return options.inverse(this); } }); // not 헬퍼 - 값 반전 (블록 헬퍼) handlebars.registerHelper('not', function(value: any, options: any) { if (!value) { return options.fn(this); } else { return options.inverse(this); } }); // formatDate 헬퍼 - 날짜 포맷팅 handlebars.registerHelper('formatDate', function(date: string | Date, format: string = 'YYYY-MM-DD') { if (!date) return ''; const dateObj = new Date(date); if (isNaN(dateObj.getTime())) return ''; // 간단한 날짜 포맷팅 (더 복잡한 경우 moment.js나 date-fns 사용) const year = dateObj.getFullYear(); const month = String(dateObj.getMonth() + 1).padStart(2, '0'); const day = String(dateObj.getDate()).padStart(2, '0'); return format .replace('YYYY', String(year)) .replace('MM', month) .replace('DD', day); }); // formatNumber 헬퍼 - 숫자 포맷팅 handlebars.registerHelper('formatNumber', function(number: number, locale: string = 'ko-KR') { if (typeof number !== 'number') return number; return new Intl.NumberFormat(locale).format(number); }); } // 헬퍼 등록 실행 registerHandlebarsHelpers(); // 템플릿 로더 함수 function loadTemplate(templateName: string, data: Record) { const templatePath = path.join(process.cwd(), 'lib', 'mail', 'templates', `${templateName}.hbs`); if (!fs.existsSync(templatePath)) { throw new Error(`Template not found: ${templatePath}`); } const source = fs.readFileSync(templatePath, 'utf8'); const template = handlebars.compile(source); return template(data); } export { transporter, loadTemplate };