-
Notifications
You must be signed in to change notification settings - Fork 1
#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
base: dev
Are you sure you want to change the base?
Changes from all commits
9798ecc
b9efeeb
793157c
cc87bc3
07d6c3b
6f64cac
a4ef34a
fd4437a
1bd4bba
f6904e9
185de87
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ import { | |
originValidatorMiddleware, | ||
responseTimeMiddleware, | ||
sessionMiddleware, | ||
banMiddleware, | ||
} from "@/middlewares"; | ||
import { | ||
adminRouter, | ||
|
@@ -78,14 +79,15 @@ app.use(limitRateMiddleware); | |
// [Router] Swagger (API 문서) | ||
app.use("/docs", docsRouter); | ||
|
||
// [Middleware] 모든 API 요청에 대하여 origin 검증 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
kmc7468 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.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; |
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"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이런 경우에는 |
||
|
||
|
||
/** | ||
* 사용자가 차단 되었는지 여부를 판단합니다. | ||
* 차단된 사용자는 이벤트에 한하여 서비스 이용에 제재를 받습니다. | ||
* | ||
* @param req eventStatus가 성공적일 경우 req.eventStatus = eventStatus로 들어갑니다. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 ) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -126,31 +126,37 @@ const quests = buildQuests({ | |
|
||
/** | ||
* firstLogin 퀘스트의 완료를 요청합니다. | ||
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. | ||
* @param {string} sid - user의 sid입니다. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 - 참여자 목록입니다. | ||
|
@@ -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}` | ||
); | ||
|
@@ -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 - 참여자 목록입니다. | ||
|
@@ -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}` | ||
); | ||
|
@@ -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); | ||
}; | ||
|
||
/** | ||
|
@@ -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); | ||
}; | ||
|
||
/** | ||
|
@@ -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 = { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; // | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ! |
||
|
||
// 2단계: 이벤트 기간인지 확인합니다. | ||
if ( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,8 @@ router.get( | |
|
||
// 아래의 Endpoint 접근 시 로그인, 차단 여부 및 시각 체크 필요 | ||
router.use(require("../../middlewares/auth").default); | ||
router.use(require("../middlewares/checkBanned")); | ||
router.use(require("../../middlewares/ban").default); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import를 지금 알파벳 순서로 맞춰놨는데, 요거에 맞춰주시면 좋을 것 같아요! (prettier에 import 자동 정렬을 넣으면 좋겠군요)