From 92fd170bb8a4970f1c7ec21ca877ab5be0f18724 Mon Sep 17 00:00:00 2001 From: neymar <0208mjkim@gmail.com> Date: Thu, 24 Oct 2024 18:30:50 +0900 Subject: [PATCH] Add: Check Read Emails --- app.js | 3 +++ package.json | 1 + src/modules/stores/mongo.js | 10 ++++++++++ src/routes/admin.js | 2 ++ src/routes/emails.js | 31 +++++++++++++++++++++++++++++++ src/services/reports.js | 25 ++++++++++++++++++++++++- src/views/emailPage.js | 6 +++++- src/views/reportEmailPage.js | 14 ++++++++++---- 8 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 src/routes/emails.js diff --git a/app.js b/app.js index 74035415..618d9041 100644 --- a/app.js +++ b/app.js @@ -57,6 +57,9 @@ eventConfig && require("./src/lottery").lotteryRouter ); +// 이메일 수신 확인은 origin 검사 거치지 않기 +app.use("/email", require("./src/routes/emails")); + // [Middleware] 모든 API 요청에 대하여 origin 검증 app.use(require("./src/middlewares/originValidator")); diff --git a/package.json b/package.json index 4ea2a648..cebf38c5 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "response-time": "^2.3.2", "socket.io": "^4.6.1", "swagger-ui-express": "^4.6.0", + "uuid": "^10.0.0", "validator": "^13.7.0", "winston": "^3.8.1", "winston-daily-rotate-file": "^4.7.1", diff --git a/src/modules/stores/mongo.js b/src/modules/stores/mongo.js index 8f837775..c66d4641 100755 --- a/src/modules/stores/mongo.js +++ b/src/modules/stores/mongo.js @@ -188,6 +188,15 @@ const reportSchema = Schema({ roomId: { type: Schema.Types.ObjectId, ref: "Room" }, // 신고한 방 id }); +const emailSchema = Schema({ + emailAddress: { type: String, required: true }, // 전송된 이메일 주소 + reportId: { type: Schema.Types.ObjectId, ref: "User" }, + trackingId: { type: String, required: true }, // 이메일 id + sentAt: { type: Date, required: true }, // 이메일 전송 시간 + isOpened: { type: Boolean, required: true }, // 이메일 수신 여부 + openedAt: { type: Date }, // 이메일 수신 시간 +}); + const adminIPWhitelistSchema = Schema({ ip: { type: String, required: true }, // IP 주소 description: { type: String, default: "" }, // 설명 @@ -267,6 +276,7 @@ module.exports = { locationModel: mongoose.model("Location", locationSchema), chatModel: mongoose.model("Chat", chatSchema), reportModel: mongoose.model("Report", reportSchema), + emailModel: mongoose.model("Email", emailSchema), adminIPWhitelistModel: mongoose.model( "AdminIPWhitelist", adminIPWhitelistSchema diff --git a/src/routes/admin.js b/src/routes/admin.js index e7748523..726222a3 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -9,6 +9,7 @@ const { locationModel, chatModel, reportModel, + emailModel, adminIPWhitelistModel, adminLogModel, deviceTokenModel, @@ -33,6 +34,7 @@ const resources = [ locationModel, chatModel, reportModel, + emailModel, adminIPWhitelistModel, adminLogModel, deviceTokenModel, diff --git a/src/routes/emails.js b/src/routes/emails.js new file mode 100644 index 00000000..96741bd3 --- /dev/null +++ b/src/routes/emails.js @@ -0,0 +1,31 @@ +const express = require('express'); +const router = express.Router(); +const { emailModel } = require('../modules/stores/mongo'); +const logger = require("../modules/logger"); + +router.get('/open-tracking', async (req, res) => { + const { trackingId } = req.query; + + if (!trackingId) { + return res.status(400).send('Tracking ID missing'); + } + + try { + const trackingRecord = await emailModel.findOne({ trackingId }); + if (!trackingRecord) { + return res.status(404).send('Tracking ID not found'); + } + + trackingRecord.isOpened = true; + trackingRecord.openedAt = new Date(); + await trackingRecord.save(); + + res.set('Content-Type', 'image/gif'); + res.send(Buffer.from('R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==', 'base64')); + } catch (err) { + logger.error(err) + res.status(500).send('Internal Server Error') + } +}) + +module.exports = router; \ No newline at end of file diff --git a/src/services/reports.js b/src/services/reports.js index dbd30d53..7a6bead7 100644 --- a/src/services/reports.js +++ b/src/services/reports.js @@ -2,12 +2,14 @@ const { userModel, reportModel, roomModel, + emailModel, } = require("../modules/stores/mongo"); const { reportPopulateOption } = require("../modules/populates/reports"); const { sendReportEmail } = require("../modules/email"); const logger = require("../modules/logger"); const reportEmailPage = require("../views/reportEmailPage"); const { notifyReportToReportChannel } = require("../modules/slackNotification"); +const { v4: uuidv4 } = require("uuid"); const createHandler = async (req, res) => { try { @@ -40,6 +42,26 @@ const createHandler = async (req, res) => { await report.save(); + const generateUniqueTrackingId = async () => { + let trackingId; + let existingTracking; + do { + trackingId = uuidv4(); + existingTracking = await emailModel.findOne({ trackingId }); + } while (existingTracking) + return trackingId; + }; + + const trackingId = await generateUniqueTrackingId(); + + await emailModel.create({ + emailAddress: reported.email, + reportId: report, + trackingId, + sentAt: new Date(), + isOpened: false + }); + notifyReportToReportChannel(user.nickname, report); if (report.type === "no-settlement" || report.type === "no-show") { @@ -51,7 +73,8 @@ const createHandler = async (req, res) => { reported.nickname, emailRoomName, user.nickname, - emailRoomId + emailRoomId, + trackingId ); sendReportEmail(reported.email, report, emailHtml); } diff --git a/src/views/emailPage.js b/src/views/emailPage.js index f41122a4..30ba3462 100644 --- a/src/views/emailPage.js +++ b/src/views/emailPage.js @@ -2,7 +2,9 @@ const { getS3Url } = require("../modules/stores/aws"); module.exports = ( title, - content + content, + trackingId, + origin ) => `