From 6270e662e0e8a74c81109efd9232305d36b821c9 Mon Sep 17 00:00:00 2001 From: lilloo04 Date: Fri, 26 Sep 2025 12:51:02 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20testCsvEmail=20API=EC=97=90=20CSV=20?= =?UTF-8?q?=EC=B2=A8=EB=B6=80=20=ED=8C=8C=EC=9D=BC=20=EC=A7=80=EC=9B=90=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/testCsvEmail.js | 63 ++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/src/services/testCsvEmail.js b/src/services/testCsvEmail.js index b9af5ea..99f65d6 100644 --- a/src/services/testCsvEmail.js +++ b/src/services/testCsvEmail.js @@ -324,34 +324,47 @@ async function createUserCsvData(user, email) { */ async function sendTestCsvEmail(email, csvFilePath, recordCount) { const csvContent = fs.readFileSync(csvFilePath); + const fileName = path.basename(csvFilePath); + + // Raw email with attachment + const boundary = `----=_NextPart_${Date.now()}`; + const rawEmail = [ + `From: no-reply@frolog.kr`, + `To: ${email}`, + `Subject: =?UTF-8?B?${Buffer.from('[테스트] 프롤로그 CSV 내보내기 테스트').toString('base64')}?=`, + `MIME-Version: 1.0`, + `Content-Type: multipart/mixed; boundary="${boundary}"`, + ``, + `--${boundary}`, + `Content-Type: text/plain; charset=UTF-8`, + `Content-Transfer-Encoding: 8bit`, + ``, + `[테스트 메일] 안녕하세요. 프롤로그입니다.`, + ``, + `이것은 CSV 내보내기 기능의 테스트 메일입니다.`, + ``, + `• 총 ${recordCount}개의 글이 포함되어 있습니다.`, + `• 첨부된 CSV 파일을 다운로드하여 확인하실 수 있습니다.`, + ``, + `실제 서비스 종료 시에는 정식 메일이 발송됩니다.`, + ``, + `프롤로그 팀 드림`, + ``, + `--${boundary}`, + `Content-Type: text/csv; charset=UTF-8; name="${fileName}"`, + `Content-Disposition: attachment; filename="${fileName}"`, + `Content-Transfer-Encoding: base64`, + ``, + csvContent.toString('base64'), + ``, + `--${boundary}--`, + ].join('\r\n'); await ses - .sendEmail({ - Destination: { - ToAddresses: [email], + .sendRawEmail({ + RawMessage: { + Data: rawEmail, }, - Message: { - Body: { - Text: { - Charset: 'UTF-8', - Data: `[테스트 메일] 안녕하세요. 프롤로그입니다. - -이것은 CSV 내보내기 기능의 테스트 메일입니다. - -• 총 ${recordCount}개의 글이 포함되어 있습니다. -• 첨부된 CSV 파일을 다운로드하여 확인하실 수 있습니다. - -실제 서비스 종료 시에는 정식 메일이 발송됩니다. - -프롤로그 팀 드림`, - }, - }, - Subject: { - Charset: 'UTF-8', - Data: '[테스트] 프롤로그 CSV 내보내기 테스트', - }, - }, - Source: 'no-reply@frolog.kr', }) .promise(); } From 049c6bc6e3c9ff462ecad75de46b615ba649fdb6 Mon Sep 17 00:00:00 2001 From: lilloo04 Date: Fri, 26 Sep 2025 12:57:41 +0900 Subject: [PATCH 2/2] =?UTF-8?q?refactor:=20testCsvEmail=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EC=97=86=EC=9D=B4=EB=8F=84=20=ED=98=B8=EC=B6=9C=20?= =?UTF-8?q?=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.js | 2 +- src/services/testCsvEmail.js | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/index.js b/src/index.js index 45a85c3..cab46a5 100644 --- a/src/index.js +++ b/src/index.js @@ -34,7 +34,7 @@ const app = new ApiServer(); // API 엔드포인트 연결 app.addHandler(SendEmailToAll, sendEmailToAll, 'admin', 'info'); app.addHandler(ExportUserDataToCsv, exportUserDataToCsv, 'admin', 'info'); -app.addHandler(TestCsvEmail, testCsvEmail, 'admin', 'info'); +app.addHandler(TestCsvEmail, testCsvEmail, null, 'info'); app.addHandler(SearchUser, searchUser, 'admin', 'verbose'); app.addHandler(GetUser, getUser, 'admin', 'verbose'); app.addHandler(PostUser, postUser, 'admin', 'info'); diff --git a/src/services/testCsvEmail.js b/src/services/testCsvEmail.js index 99f65d6..9a7c902 100644 --- a/src/services/testCsvEmail.js +++ b/src/services/testCsvEmail.js @@ -17,19 +17,11 @@ const ses = new AWS.SES({ apiVersion: '2010-12-01' }); /** * 특정 사용자에게 CSV 테스트 이메일 발송 - * @param {{ is_admin: boolean, id: string }} user 인증 정보. + * @param {object|null} user 인증 정보. * @param {{ testEmail?: string, userId?: string }} body 요청 본문. * @returns {Promise} 응답 DTO. */ export default async function testCsvEmail(user, body = {}) { - // (1) 관리자 권한 체크 - if (!user.is_admin) { - return { - result: false, - message: 'Access denied. Admin permission required.', - }; - } - const { testEmail, userId } = body; try {