Skip to content

#541 24년도 추석 이벤트 밴 미들웨어 #544

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: dev
Choose a base branch
from
8 changes: 5 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
originValidatorMiddleware,
responseTimeMiddleware,
sessionMiddleware,
banMiddleware,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import를 지금 알파벳 순서로 맞춰놨는데, 요거에 맞춰주시면 좋을 것 같아요! (prettier에 import 자동 정렬을 넣으면 좋겠군요)

} from "@/middlewares";
import {
adminRouter,
Expand Down Expand Up @@ -78,14 +79,15 @@ app.use(limitRateMiddleware);
// [Router] Swagger (API 문서)
app.use("/docs", docsRouter);

// [Middleware] 모든 API 요청에 대하여 origin 검증
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

주석도 같이 업데이트 해주실 수 있나요?

app.use(originValidatorMiddleware);
app.use(banMiddleware);

// [Router] 이벤트 전용 라우터입니다.
if (eventConfig) {
app.use(`/events/${eventConfig.mode}`, lotteryRouter);
}

// [Middleware] 모든 API 요청에 대하여 origin 검증
app.use(originValidatorMiddleware);

// [Router] APIs
app.use("/auth", authRouter);
app.use("/chats", chatRouter);
Expand Down
3 changes: 0 additions & 3 deletions src/lottery/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ eventConfig && require("./schedules")();

const lotteryRouter = express.Router();

// [Middleware] 모든 API 요청에 대하여 origin 검증
lotteryRouter.use(require("../middlewares/originValidator").default);

// [Router] APIs
lotteryRouter.use("/globalState", require("./routes/globalState"));
lotteryRouter.use("/invites", require("./routes/invites"));
Expand Down
32 changes: 32 additions & 0 deletions src/lottery/middlewares/eventValidator.js
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TS로 마이그레이션 완료되었으니 이 JS 파일은 삭제하면 될 것 같습니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const { eventStatusModel } = require("../modules/stores/mongo");
const logger = require("../../modules/logger");

/**
* 사용자가 차단 되었는지 여부를 판단합니다.
* 차단된 사용자는 이벤트에 한하여 서비스 이용에 제재를 받습니다.
* @param {*} req eventStatus가 성공적일 경우 req.eventStatus = eventStatus로 들어갑니다.
* @param {*} res
* @param {*} next
* @returns
*/
const eventValidator = async (req, res, next) => {
try {
const eventStatus = await eventStatusModel
.findOne({ userId: req.userOid })
.lean();
if (!eventStatus) {
return res
.status(400)
.json({ error: "eventValidator: nonexistent eventStatus" });
}
req.eventStatus = eventStatus;
next();
} catch (err) {
logger.error(err);
res.error(500).json({
error: "eventValidator: internal server error",
});
}
};

module.exports = eventValidator;
37 changes: 37 additions & 0 deletions src/lottery/middlewares/eventValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { RequestHandler } from "express";
import { eventStatusModel } from "../modules/stores/mongo";
import logger from "../../modules/logger";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런 경우에는 @/modules/logger 로 간단하게 import할 수 있어요!



/**
* 사용자가 차단 되었는지 여부를 판단합니다.
* 차단된 사용자는 이벤트에 한하여 서비스 이용에 제재를 받습니다.
*
* @param req eventStatus가 성공적일 경우 req.eventStatus = eventStatus로 들어갑니다.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사소한건데.. 여기 공백이 2개 있습니다 ㅋㅋㅋㅋㅋ ㅠ

* @param res
* @param next
* @returns
*/
const eventValidator: RequestHandler = async ( req, res, next ) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prettier 적용 안된 것 같은데 확인해 주실 수 있나요?

try {
const eventStatus = await eventStatusModel
.findOne({ userId: req.userOid })
.lean();

if (!eventStatus) {
return res
.status(400)
.json({ error: "eventValidator: nonexistent eventStatus" });
}

req.eventStatus = eventStatus;
next();
} catch (err) {
logger.error(err);
res.status(500).json({
error: "eventValidator: internal server error",
});
}
};

export default eventValidator;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

src/middlewares 파일에 있는 미들웨어들을 보시면, authMiddleware 와 같이 Middleware로 이름을 끝내는게 컨벤션입니다! 맞춰서 이름 수정해 주실 수 있나용?

78 changes: 48 additions & 30 deletions src/lottery/modules/contracts.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,31 +126,37 @@ const quests = buildQuests({

/**
* firstLogin 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {string} sid - user의 sid입니다.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

엇 주석 업데이트가 안된걸까요? sid라는 파라미터는 없는 것 같아서요!

* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @param {string} url - 요청한 url입니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @returns {Promise}
* @usage lottery/globalState - createUserGlobalStateHandler
*/
const completeFirstLoginQuest = async (userId, timestamp) => {
return await completeQuest(userId, timestamp, quests.firstLogin);
const completeFirstLoginQuest = async (timestamp, userId) => {
return await completeQuest(timestamp, userId, quests.firstLogin);
};

/**
* firstRoomCreation 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {string} sid - user의 sid입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @param {string} url - 요청한 url입니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @returns {Promise}
* @description 방을 만들 때마다 호출해 주세요.
* @usage rooms - createHandler
*/
const completeFirstRoomCreationQuest = async (userId, timestamp) => {
return await completeQuest(userId, timestamp, quests.firstRoomCreation);
const completeFirstRoomCreationQuest = async (timestamp, userId) => {
return await completeQuest(timestamp, userId, quests.firstRoomCreation);
};

/**
* fareSettlement 퀘스트의 완료를 요청합니다. 방의 참가자 수가 2명 미만이면 요청하지 않습니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {string} sid - user의 sid입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @param {string} url - 요청한 url입니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {Object} roomObject - 방의 정보입니다.
* @param {mongoose.Types.ObjectId} roomObject._id - 방의 ObjectId입니다.
* @param {Array<{ user: mongoose.Types.ObjectId }>} roomObject.part - 참여자 목록입니다.
Expand All @@ -159,7 +165,7 @@ const completeFirstRoomCreationQuest = async (userId, timestamp) => {
* @description 정산 요청이 이루어질 때마다 호출해 주세요.
* @usage rooms - commitSettlementHandler
*/
const completeFareSettlementQuest = async (userId, timestamp, roomObject) => {
const completeFareSettlementQuest = async (timestamp, userId, roomObject) => {
logger.info(
`User ${userId} requested to complete fareSettlementQuest in Room ${roomObject._id}`
);
Expand All @@ -172,13 +178,15 @@ const completeFareSettlementQuest = async (userId, timestamp, roomObject) => {
)
return null; // 택시 출발 시각이 이벤트 기간 내에 포함되지 않는 경우 퀘스트 완료 요청을 하지 않습니다.

return await completeQuest(userId, timestamp, quests.fareSettlement);
return await completeQuest(timestamp, userId, quests.fareSettlement);
};

/**
* farePayment 퀘스트의 완료를 요청합니다. 방의 참가자 수가 2명 미만이면 요청하지 않습니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {string} sid - user의 sid입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @param {string} url - 요청한 url입니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {Object} roomObject - 방의 정보입니다.
* @param {mongoose.Types.ObjectId} roomObject._id - 방의 ObjectId입니다.
* @param {Array<{ user: mongoose.Types.ObjectId }>} roomObject.part - 참여자 목록입니다.
Expand All @@ -187,7 +195,7 @@ const completeFareSettlementQuest = async (userId, timestamp, roomObject) => {
* @description 송금이 이루어질 때마다 호출해 주세요.
* @usage rooms - commitPaymentHandler
*/
const completeFarePaymentQuest = async (userId, timestamp, roomObject) => {
const completeFarePaymentQuest = async (timestamp, userId, roomObject) => {
logger.info(
`User ${userId} requested to complete farePaymentQuest in Room ${roomObject._id}`
);
Expand All @@ -200,64 +208,72 @@ const completeFarePaymentQuest = async (userId, timestamp, roomObject) => {
)
return null; // 택시 출발 시각이 이벤트 기간 내에 포함되지 않는 경우 퀘스트 완료 요청을 하지 않습니다.

return await completeQuest(userId, timestamp, quests.farePayment);
return await completeQuest(timestamp, userId, quests.farePayment);
};

/**
* nicknameChanging 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {string} sid - user의 sid입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @param {string} url - 요청한 url입니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @returns {Promise}
* @description 닉네임을 변경할 때마다 호출해 주세요.
* @usage users - editNicknameHandler
*/
const completeNicknameChangingQuest = async (userId, timestamp) => {
return await completeQuest(userId, timestamp, quests.nicknameChanging);
const completeNicknameChangingQuest = async (timestamp, userId) => {
return await completeQuest(timestamp, userId, quests.nicknameChanging);
};

/**
* accountChanging 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {string} sid - user의 sid입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @param {string} url - 요청한 url입니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {string} newAccount - 변경된 계좌입니다.
* @returns {Promise}
* @description 계좌를 변경할 때마다 호출해 주세요.
* @usage users - editAccountHandler
*/
const completeAccountChangingQuest = async (userId, timestamp, newAccount) => {
const completeAccountChangingQuest = async (timestamp, userId, newAccount) => {
if (newAccount === "") return null;

return await completeQuest(userId, timestamp, quests.accountChanging);
return await completeQuest(timestamp, userId, quests.accountChanging);
};

/**
* adPushAgreement 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {string} sid - user의 sid입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @param {string} url - 요청한 url입니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {boolean} advertisement - 변경된 광고성 알림 수신 동의 여부입니다.
* @returns {Promise}
* @description 알림 옵션을 변경할 때마다 호출해 주세요.
* @usage notifications - editOptionsHandler
*/
const completeAdPushAgreementQuest = async (
userId,
timestamp,
userId,
advertisement
) => {
if (!advertisement) return null;

return await completeQuest(userId, timestamp, quests.adPushAgreement);
return await completeQuest(timestamp, userId, quests.adPushAgreement);
};

/**
* eventSharing 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {string} sid - user의 sid입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @param {string} url - 요청한 url입니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @returns {Promise}
* @usage lottery/globalState - createUserGlobalStateHandler
*/
const completeEventSharingQuest = async (userId, timestamp) => {
return await completeQuest(userId, timestamp, quests.eventSharing);
const completeEventSharingQuest = async (timestamp, userId) => {
return await completeQuest(timestamp, userId, quests.eventSharing);
};

/**
Expand All @@ -267,8 +283,8 @@ const completeEventSharingQuest = async (userId, timestamp) => {
* @returns {Promise}
* @usage lottery/globalState - createUserGlobalStateHandler
*/
const completeIndirectEventSharingQuest = async (userId, timestamp) => {
return await completeQuest(userId, timestamp, quests.indirectEventSharing);
const completeIndirectEventSharingQuest = async (timestamp, userId) => {
return await completeQuest(timestamp, userId, quests.indirectEventSharing);
};

/**
Expand All @@ -279,18 +295,20 @@ const completeIndirectEventSharingQuest = async (userId, timestamp) => {
* @usage lottery/schedules/dailyQuiz - determineQuizResult
*/
export const completeAnswerCorrectlyQuest = async (userId, timestamp) => {
return await completeQuest(userId, timestamp, quests.answerCorrectly);
return await completeQuest(timestamp, userId, quests.answerCorrectly);
};

/**
* itemPurchase 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {string} sid - user의 sid입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @param {string} url - 요청한 url입니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @returns {Promise}
* @description 상품을 구입할 때마다 호출해 주세요.
*/
const completeItemPurchaseQuest = async (userId, timestamp) => {
return await completeQuest(userId, timestamp, quests.itemPurchase);
const completeItemPurchaseQuest = async (timestamp, userId) => {
return await completeQuest(timestamp, userId, quests.itemPurchase);
};

module.exports = {
Expand Down
5 changes: 3 additions & 2 deletions src/lottery/modules/quests.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,16 @@ const buildQuests = (quests) => {
* @param {number} quest.maxCount - 퀘스트의 최대 완료 가능 횟수입니다.
* @returns {Object|null} 성공한 경우 Object를, 실패한 경우 null을 반환합니다. 이미 최대 완료 횟수에 도달했거나, 퀘스트가 원격으로 비활성화된 경우에도 실패로 처리됩니다.
*/
const completeQuest = async (userId, timestamp, quest) => {
const completeQuest = async (timestamp, userId, quest) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헉 이거 파라미터 순서가 왜 바뀐걸까요? 바꾸신 이유가 있다면 주석에서도 같이 순서 변경 부탁드려요!

try {
// 이벤트(2025spring) 기간을 하루 더 연장하여 넙죽코인 소비기한을 보장할 때, completeQuest 함수를 비활성화합니다.
// 추후 이벤트에서는 아래 코드를 지워주시길 바랍니다.
if (timestamp >= new Date("2025-03-13T00:00:00+09:00")) return null;

// 1단계: 유저의 EventStatus를 가져옵니다. 블록드리스트인지도 확인합니다.
const eventStatus = await eventStatusModel.findOne({ userId }).lean();
if (!eventStatus || eventStatus.isBanned) return null;

if (!eventStatus) return null; //
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!


// 2단계: 이벤트 기간인지 확인합니다.
if (
Expand Down
3 changes: 2 additions & 1 deletion src/lottery/routes/invites.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ router.get(

// 아래의 Endpoint 접근 시 로그인, 차단 여부 및 시각 체크 필요
router.use(require("../../middlewares/auth").default);
router.use(require("../middlewares/checkBanned"));
router.use(require("../../middlewares/ban").default);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저희 ban 미들웨어가 index.ts 파일에 달려있어서, 이런 곳에선 use 안해주셔도 됩니다!

router.use(require("../middlewares/eventValidator"));
router.use(require("../middlewares/timestampValidator"));

router.post("/create", invitesHandlers.createInviteUrlHandler);
Expand Down
3 changes: 2 additions & 1 deletion src/lottery/routes/items.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ router.get(

// 아래의 Endpoint 접근 시 로그인, 차단 여부 및 시각 체크 필요
router.use(require("../../middlewares/auth").default);
router.use(require("../middlewares/checkBanned"));
router.use(require("../../middlewares/ban").default);
router.use(require("../middlewares/eventValidator"));
router.use(require("../middlewares/timestampValidator"));

router.post(
Expand Down
3 changes: 2 additions & 1 deletion src/lottery/routes/quests.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ const questsHandlers = require("../services/quests");

// 아래의 Endpoint 접근 시 로그인, 차단 여부 및 시각 체크 필요
router.use(require("../../middlewares/auth").default);
router.use(require("../middlewares/checkBanned"));
router.use(require("../../middlewares/ban").default);
router.use(require("../middlewares/eventValidator"));
router.use(require("../middlewares/timestampValidator"));

router.post(
Expand Down
3 changes: 2 additions & 1 deletion src/lottery/routes/transactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ const router = express.Router();

const transactionsHandlers = require("../services/transactions");

// 아래의 Endpoint 접근 시 로그인 필요
// 아래의 Endpoint 접근 시 로그인 체크 필요
router.use(require("../../middlewares/auth").default);
router.use(require("../middlewares/eventValidator"));

router.get("/", transactionsHandlers.getUserTransactionsHandler);

Expand Down
Loading