diff --git a/app.js b/app.js index d8a1f0f3..689de7a9 100644 --- a/app.js +++ b/app.js @@ -1,7 +1,7 @@ // 모듈 require const express = require("express"); const http = require("http"); -const { port: httpPort, eventMode } = require("./loadenv"); +const { port: httpPort, eventConfig } = require("./loadenv"); const logger = require("./src/modules/logger"); const { connectDatabase } = require("./src/modules/stores/mongo"); const { startSocketServer } = require("./src/modules/socket"); @@ -43,8 +43,11 @@ app.use(require("./src/middlewares/limitRate")); app.use("/docs", require("./src/routes/docs")); // 2023 추석 이벤트 전용 라우터입니다. -eventMode && - app.use(`/events/${eventMode}`, require("./src/lottery").lotteryRouter); +eventConfig && + app.use( + `/events/${eventConfig.mode}`, + require("./src/lottery").lotteryRouter + ); // [Middleware] 모든 API 요청에 대하여 origin 검증 app.use(require("./src/middlewares/originValidator")); diff --git a/loadenv.js b/loadenv.js index 2bce4fb9..1f9528b6 100644 --- a/loadenv.js +++ b/loadenv.js @@ -38,5 +38,5 @@ module.exports = { slackWebhookUrl: { report: process.env.SLACK_REPORT_WEBHOOK_URL || "", // optional }, - eventMode: undefined, + eventConfig: process.env.EVENT_CONFIG && JSON.parse(process.env.EVENT_CONFIG), // optional }; diff --git a/src/lottery/index.js b/src/lottery/index.js index 7ca282fe..faed346b 100644 --- a/src/lottery/index.js +++ b/src/lottery/index.js @@ -6,11 +6,11 @@ const { transactionModel, } = require("./modules/stores/mongo"); -const { eventMode } = require("../../loadenv"); +const { eventConfig } = require("../../loadenv"); const { buildResource } = require("../modules/adminResource"); // [Routes] 기존 docs 라우터의 docs extend -require("./routes/docs")(); +eventConfig && require("./routes/docs")(); const lotteryRouter = express.Router(); @@ -30,7 +30,8 @@ const resources = [ transactionModel, ].map(buildResource()); -const contracts = eventMode && require(`./modules/contracts/${eventMode}`); +const contracts = + eventConfig && require(`./modules/contracts/${eventConfig.mode}`); module.exports = { lotteryRouter, diff --git a/src/lottery/modules/contracts/2023fall.js b/src/lottery/modules/contracts/2023fall.js index abe2f0d8..650c0d76 100644 --- a/src/lottery/modules/contracts/2023fall.js +++ b/src/lottery/modules/contracts/2023fall.js @@ -76,11 +76,6 @@ const quests = buildQuests({ }, }); -const eventPeriod = { - start: new Date("2023-09-25T00:00:00+09:00"), // Inclusive - end: new Date("2023-10-10T00:00:00+09:00"), // Exclusive -}; - /** * firstLogin 퀘스트의 완료를 요청합니다. * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. @@ -89,7 +84,7 @@ const eventPeriod = { * @usage lottery/globalState/createUserGlobalStateHandler */ const completeFirstLoginQuest = async (userId, timestamp) => { - return await completeQuest(userId, timestamp, eventPeriod, quests.firstLogin); + return await completeQuest(userId, timestamp, quests.firstLogin); }; /** @@ -112,7 +107,6 @@ const completePayingAndSendingQuest = async (timestamp, roomObject) => { await completeQuest( participant.user._id, timestamp, - eventPeriod, quests.payingAndSending ) ) @@ -128,12 +122,7 @@ const completePayingAndSendingQuest = async (timestamp, roomObject) => { * @usage rooms/createHandler */ const completeFirstRoomCreationQuest = async (userId, timestamp) => { - return await completeQuest( - userId, - timestamp, - eventPeriod, - quests.firstRoomCreation - ); + return await completeQuest(userId, timestamp, quests.firstRoomCreation); }; const completeRoomSharingQuest = async () => { @@ -153,7 +142,7 @@ const completeRoomSharingQuest = async () => { const completePayingQuest = async (userId, timestamp, roomObject) => { if (roomObject.part.length < 2) return null; - return await completeQuest(userId, timestamp, eventPeriod, quests.paying); + return await completeQuest(userId, timestamp, quests.paying); }; /** @@ -169,7 +158,7 @@ const completePayingQuest = async (userId, timestamp, roomObject) => { const completeSendingQuest = async (userId, timestamp, roomObject) => { if (roomObject.part.length < 2) return null; - return await completeQuest(userId, timestamp, eventPeriod, quests.sending); + return await completeQuest(userId, timestamp, quests.sending); }; /** @@ -181,12 +170,7 @@ const completeSendingQuest = async (userId, timestamp, roomObject) => { * @usage users/editNicknameHandler */ const completeNicknameChangingQuest = async (userId, timestamp) => { - return await completeQuest( - userId, - timestamp, - eventPeriod, - quests.nicknameChanging - ); + return await completeQuest(userId, timestamp, quests.nicknameChanging); }; /** @@ -201,12 +185,7 @@ const completeNicknameChangingQuest = async (userId, timestamp) => { const completeAccountChangingQuest = async (userId, timestamp, newAccount) => { if (newAccount === "") return null; - return await completeQuest( - userId, - timestamp, - eventPeriod, - quests.accountChanging - ); + return await completeQuest(userId, timestamp, quests.accountChanging); }; /** @@ -225,12 +204,7 @@ const completeAdPushAgreementQuest = async ( ) => { if (!advertisement) return null; - return await completeQuest( - userId, - timestamp, - eventPeriod, - quests.adPushAgreement - ); + return await completeQuest(userId, timestamp, quests.adPushAgreement); }; const completeEventSharingOnInstagramQuest = async () => { @@ -243,7 +217,6 @@ const completePurchaseSharingOnInstagramQuest = async () => { module.exports = { quests, - eventPeriod, completeFirstLoginQuest, completePayingAndSendingQuest, completeFirstRoomCreationQuest, diff --git a/src/lottery/modules/quests.js b/src/lottery/modules/quests.js index ce2d22d4..9aa5c77f 100644 --- a/src/lottery/modules/quests.js +++ b/src/lottery/modules/quests.js @@ -7,6 +7,12 @@ const { const logger = require("../../modules/logger"); const mongoose = require("mongoose"); +const { eventConfig } = require("../../../loadenv"); +const eventPeriod = { + startAt: new Date(eventConfig.startAt), + endAt: new Date(eventConfig.endAt), +}; + const requiredQuestFields = ["name", "description", "imageUrl", "reward"]; const buildQuests = (quests) => { for (const [id, quest] of Object.entries(quests)) { @@ -45,9 +51,6 @@ const buildQuests = (quests) => { * 퀘스트 완료를 요청합니다. * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. * @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다. - * @param {Object} eventPeriod - 이벤트의 기간입니다. - * @param {Date} eventPeriod.start - 이벤트의 시작 시각(Inclusive)입니다. - * @param {Date} eventPeriod.end - 이벤트의 종료 시각(Exclusive)입니다. * @param {Object} quest - 퀘스트의 정보입니다. * @param {string} quest.id - 퀘스트의 Id입니다. * @param {string} quest.name - 퀘스트의 이름입니다. @@ -57,14 +60,14 @@ const buildQuests = (quests) => { * @param {number} quest.maxCount - 퀘스트의 최대 완료 가능 횟수입니다. * @returns {Object|null} 성공한 경우 Object를, 실패한 경우 null을 반환합니다. 이미 최대 완료 횟수에 도달했거나, 퀘스트가 원격으로 비활성화 된 경우에도 실패로 처리됩니다. */ -const completeQuest = async (userId, timestamp, eventPeriod, quest) => { +const completeQuest = async (userId, timestamp, quest) => { try { // 1단계: 유저의 EventStatus를 가져옵니다. const eventStatus = await eventStatusModel.findOne({ userId }).lean(); if (!eventStatus) return null; // 2단계: 이벤트 기간인지 확인합니다. - if (timestamp >= eventPeriod.end || timestamp < eventPeriod.start) { + if (timestamp >= eventPeriod.endAt || timestamp < eventPeriod.startAt) { logger.info( `User ${userId} failed to complete auto-disabled ${quest.id}Quest` ); diff --git a/src/lottery/routes/docs/globalState.js b/src/lottery/routes/docs/globalState.js index 6883ac33..ffaddf3a 100644 --- a/src/lottery/routes/docs/globalState.js +++ b/src/lottery/routes/docs/globalState.js @@ -1,5 +1,5 @@ -const { eventMode } = require("../../../../loadenv"); -const apiPrefix = `/events/${eventMode}/global-state`; +const { eventConfig } = require("../../../../loadenv"); +const apiPrefix = `/events/${eventConfig.mode}/global-state`; const globalStateDocs = {}; globalStateDocs[`${apiPrefix}/`] = { diff --git a/src/lottery/routes/docs/items.js b/src/lottery/routes/docs/items.js index d1c0956e..4079795a 100644 --- a/src/lottery/routes/docs/items.js +++ b/src/lottery/routes/docs/items.js @@ -1,5 +1,5 @@ -const { eventMode } = require("../../../../loadenv"); -const apiPrefix = `/events/${eventMode}/items`; +const { eventConfig } = require("../../../../loadenv"); +const apiPrefix = `/events/${eventConfig.mode}/items`; const itemsDocs = {}; itemsDocs[`${apiPrefix}/list`] = { diff --git a/src/lottery/routes/docs/publicNotice.js b/src/lottery/routes/docs/publicNotice.js index cda93fa1..649ac5a6 100644 --- a/src/lottery/routes/docs/publicNotice.js +++ b/src/lottery/routes/docs/publicNotice.js @@ -1,5 +1,5 @@ -const { eventMode } = require("../../../../loadenv"); -const apiPrefix = `/events/${eventMode}/public-notice`; +const { eventConfig } = require("../../../../loadenv"); +const apiPrefix = `/events/${eventConfig.mode}/public-notice`; const publicNoticeDocs = {}; publicNoticeDocs[`${apiPrefix}/leaderboard`] = { diff --git a/src/lottery/routes/docs/swaggerDocs.js b/src/lottery/routes/docs/swaggerDocs.js index 96c7fe86..12771ce7 100644 --- a/src/lottery/routes/docs/swaggerDocs.js +++ b/src/lottery/routes/docs/swaggerDocs.js @@ -1,11 +1,11 @@ -const { eventMode } = require("../../../../loadenv"); +const { eventConfig } = require("../../../../loadenv"); const globalStateDocs = require("./globalState"); const itemsDocs = require("./items"); const transactionsDocs = require("./transactions"); const itemsSchema = require("./itemsSchema"); const publicNoticeDocs = require("./publicNotice"); -const apiPrefix = `/events/${eventMode}`; +const apiPrefix = `/events/${eventConfig.mode}`; const eventSwaggerDocs = { tags: [ diff --git a/src/lottery/routes/docs/transactions.js b/src/lottery/routes/docs/transactions.js index 60e7435b..9bb82f41 100644 --- a/src/lottery/routes/docs/transactions.js +++ b/src/lottery/routes/docs/transactions.js @@ -1,5 +1,5 @@ -const { eventMode } = require("../../../../loadenv"); -const apiPrefix = `/events/${eventMode}/transactions`; +const { eventConfig } = require("../../../../loadenv"); +const apiPrefix = `/events/${eventConfig.mode}/transactions`; const transactionsDocs = {}; transactionsDocs[`${apiPrefix}/`] = { diff --git a/src/lottery/services/globalState.js b/src/lottery/services/globalState.js index 7c7f7c5f..0cf4315c 100644 --- a/src/lottery/services/globalState.js +++ b/src/lottery/services/globalState.js @@ -2,11 +2,10 @@ const { eventStatusModel } = require("../modules/stores/mongo"); const logger = require("../../modules/logger"); const { isLogin, getLoginInfo } = require("../../modules/auths/login"); -const { eventMode } = require("../../../loadenv"); -const contract = eventMode - ? require(`../modules/contracts/${eventMode}`) - : undefined; -const quests = contract ? Object.values(contract.quests) : undefined; +const { eventConfig } = require("../../../loadenv"); +const contracts = + eventConfig && require(`../modules/contracts/${eventConfig.mode}`); +const quests = contracts ? Object.values(contracts.quests) : undefined; const getUserGlobalStateHandler = async (req, res) => { try { @@ -50,7 +49,7 @@ const createUserGlobalStateHandler = async (req, res) => { }); await eventStatus.save(); - await contract.completeFirstLoginQuest(req.userOid, req.timestamp); + await contracts.completeFirstLoginQuest(req.userOid, req.timestamp); res.json({ result: true }); } catch (err) { diff --git a/src/lottery/services/items.js b/src/lottery/services/items.js index 5e3a5a1c..26afb6b0 100644 --- a/src/lottery/services/items.js +++ b/src/lottery/services/items.js @@ -20,10 +20,11 @@ const updateEventStatus = async ( } ); -const { eventMode } = require("../../../loadenv"); -const eventPeriod = eventMode - ? require(`../modules/contracts/${eventMode}`).eventPeriod - : undefined; +const { eventConfig } = require("../../../loadenv"); +const eventPeriod = eventConfig && { + startAt: new Date(eventConfig.startAt), + endAt: new Date(eventConfig.endAt), +}; const getRandomItem = async (req, depth) => { if (depth >= 10) { @@ -126,7 +127,10 @@ const purchaseHandler = async (req, res) => { .status(400) .json({ error: "Items/Purchase : nonexistent eventStatus" }); - if (req.timestamp >= eventPeriod.end || req.timestamp < eventPeriod.start) + if ( + req.timestamp >= eventPeriod.endAt || + req.timestamp < eventPeriod.startAt + ) return res.status(400).json({ error: "Items/Purchase : out of date" }); const { itemId } = req.params; diff --git a/src/routes/admin.js b/src/routes/admin.js index d476142e..e2ddaae4 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -13,7 +13,7 @@ const { deviceTokenModel, notificationOptionModel, } = require("../modules/stores/mongo"); -const { eventMode } = require("../../loadenv"); +const { eventConfig } = require("../../loadenv"); const { buildResource } = require("../modules/adminResource"); const router = express.Router(); @@ -37,7 +37,7 @@ const baseResources = [ notificationOptionModel, ].map(buildResource()); const resources = baseResources.concat( - eventMode === "2023fall" ? require("../lottery").resources : [] + eventConfig?.mode === "2023fall" ? require("../lottery").resources : [] ); // Create router for admin page