diff --git a/src/index.js b/src/index.js index dcdb717..e7f9793 100644 --- a/src/index.js +++ b/src/index.js @@ -5,46 +5,19 @@ import express from "express"; import swaggerUi from "swagger-ui-express"; import session from "express-session"; import passport from "passport"; -import multer from "multer"; + import httpLogger from "./middlewares/logger.middleware.js"; import { specs } from "./configs/swagger.config.js"; import { jwtStrategy } from "./Auths/strategies/jwt.strategy.js"; -import { handleGetFriendsList, handlePostFriendsRequest, handleGetIncomingFriendRequests, handleGetOutgoingFriendRequests, handleAcceptFriendRequest, handleRejectFriendRequest, handleDeleteFriendRequest } from "./controllers/friend.controller.js"; -import { handleSendMyLetter, handleSendOtherLetter, handleGetLetterDetail, handleRemoveLetterLike, handleAddLetterLike, handleGetPublicLetterFromOther, handleGetPublicLetterFromFriend, handleGetUserLetterStats, handleGetLetterAssets, handleGetLetterByAiKeyword } from "./controllers/letter.controller.js"; -import { handleCheckDuplicatedEmail, handleLogin, handleRefreshToken, handleSignUp, handleSendVerifyEmailCode, handleCheckEmailCode, handleGetAccountInfo, handleResetPassword, handleLogout, handleWithdrawUser, handleCheckDuplicatedUsername, handleSocialLogin, handleSocialLoginCertification, handleSocialLoginCallback, handleChangePassword } from "./controllers/auth.controller.js"; -import { handlePostMatchingSession, handlePatchMatchingSessionStatusDiscarded, handlePatchMatchingSessionStatusFriends, handlePostSessionReview } from "./controllers/session.controller.js"; -import { handleCreateUserAgreements, handlePatchOnboardingStep1, handleGetAllInterests, handleGetMyInterests, handleUpdateMyOnboardingInterests, handleGetMyNotificationSettings, handleUpdateMyNotificationSettings, handleGetMyProfile, handlePatchMyProfile, handlePostMyProfileImage, handlePutMyPushSubscription, handleGetMyConsents, handlePatchMyConsents, handleUpdateActivity, } from "./controllers/user.controller.js"; -import { handleGetAnonymousThreads, handleGetAnonymousThreadLetters, handleGetSelfMailbox, handleGetLetterFromFriend, } from "./controllers/mailbox.controller.js"; -import { handleGetNotices, handleGetNoticeDetail, } from "./controllers/notice.controller.js"; -import { handleGetCommunityGuidelines, handleGetTerms, handleGetPrivacy, } from "./controllers/policy.controller.js"; -import { handleGetWeeklyReport } from "./controllers/weeklyReport.controller.js"; -import { handleGetTodayQuestion } from "./controllers/question.controller.js"; -import { validate } from "./middlewares/validate.middleware.js"; -import { changePasswordSchema, emailSchema, loginSchema, resetPasswordSchema, SignUpSchema, usernameSchema, verificationConfirmCodeSchema, verificationSendCodeSchema } from "./schemas/auth.schema.js"; -import { handleInsertInquiryAsUser, handleInsertInquiryAsAdmin, handleGetInquiry, handleGetInquiryDetail } from "./controllers/inquiry.controller.js"; -import { isLogin } from "./middlewares/auth.middleware.js"; -import { isRestricted } from "./middlewares/restriction.middleware.js"; -import { letterByAiKeywordSchema, letterToMeSchema, letterToOtherSchema, publicCarouselSchema } from "./schemas/letter.schema.js"; -import { idParamSchema, ISOTimeSchema } from "./schemas/common.schema.js"; -import { pushSubscriptionSchema, onboardingStep1Schema, updateInterestsSchema, updateProfileSchema, updateNotificationSettingsSchema, updateConsentsSchema, updateActivitySchema, createUserAgreementsSchema } from "./schemas/user.schema.js"; -import { sessionIdParamSchema } from "./schemas/mailbox.schema.js"; -import { noticeIdParamSchema } from "./schemas/notice.schema.js"; -import { HandleGetHomeDashboard } from "./controllers/dashboard.controller.js"; -import { handleInsertUserReport, handleGetUserReports, handleGetUserReport } from "./controllers/report.controller.js"; -import { startBatch } from "./jobs/index.job.js"; -import { insertUserReportSchema, getUserReportSchema } from "./schemas/report.schema.js"; -import { postTargetUserIdAndSIdSchema, requesterUserIdSchema, targetUserIdSchema } from "./schemas/friend.schema.js"; -import { getInquiryDetailSchema, insertInquiryAsAdminSchema, insertInquiryAsUserSchema } from "./schemas/inquiry.schema.js"; -import { postMatchingSessionSchema, postSessionReviewSchema, patchMatchingSessionStatusSchema} from "./schemas/session.schema.js"; -import { postBlockUserSchema } from "./schemas/block.schema.js"; -import { handleGetBlock, handlePostBlock } from "./controllers/block.controller.js"; -import { handleGetRestrict } from "./controllers/restrict.controller.js"; import { configurePush } from "./configs/push.config.js"; import logger from "./configs/logger.config.js"; +import { startBatch } from "./jobs/index.job.js"; + +import { registerRoutes } from "./routes/index.js"; const app = express(); const port = process.env.PORT || 3000; -const allowed_origins = process.env.ALLOWED_ORIGINS?.split(',') || ["http://localhost:3000"]; +const allowed_origins = process.env.ALLOWED_ORIGINS?.split(",") || ["http://localhost:3000"]; configurePush(); @@ -53,8 +26,6 @@ app.use((req, res, next) => { next(); }); -// app.use(cors({ origin: ["http://localhost:3000"], credentials: true })); - app.use( cors({ origin: allowed_origins, @@ -75,7 +46,14 @@ app.use(httpLogger); // 로그인 전략 passport.use(jwtStrategy); -app.use(session({ secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false, cookie: { httpOnly: true, maxAge: 1800000, sameSite: "none", secure: true } })); +app.use( + session({ + secret: process.env.SESSION_SECRET, + resave: false, + saveUninitialized: false, + cookie: { httpOnly: true, maxAge: 1800000, sameSite: "none", secure: true }, + }) +); // Swagger 연결 app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(specs)); @@ -92,137 +70,10 @@ app.use((req, res, next) => { next(); }); -// 비동기 에러 래퍼 -const asyncHandler = (fn) => (req, res, next) => { - Promise.resolve(fn(req, res, next)).catch(next); -}; - -// 프로필 이미지 업로드 -const upload = multer({ storage: multer.memoryStorage() }); - -// 테스트 라우트 -app.get("/", (req, res) => { - res.send("Hello World! Server is running."); -}); - - -// 로그인/회원가입 - -app.get("/mypage", isLogin, (req, res) => { - res.status(200).success({ - message: `인증 성공! ${req.user.name}님의 마이페이지입니다.`, - user: req.user, - }); -}); - -app.get("/friends", isLogin, isRestricted, asyncHandler(handleGetFriendsList)); //친구 목록 불러오기 - -app.post("/friends/requests", isLogin, isRestricted, validate(postTargetUserIdAndSIdSchema), asyncHandler(handlePostFriendsRequest)); //친구 신청 -app.get("/friends/requests/incoming", isLogin, isRestricted, asyncHandler(handleGetIncomingFriendRequests)); //들어온 친구 신청 불러오기 -app.get("/friends/requests/outgoing", isLogin, isRestricted, asyncHandler(handleGetOutgoingFriendRequests)); //보낸 친구 신청 불러오기 -app.post("/friends/requests/accept/:requesterUserId", isLogin, isRestricted, validate(requesterUserIdSchema), asyncHandler(handleAcceptFriendRequest)); //들어온 친구 신청 수락 -app.post("/friends/requests/reject/:targetUserId", isLogin, isRestricted, validate(targetUserIdSchema), asyncHandler(handleRejectFriendRequest)); //들어온 친구 신청 거절 -app.delete("/friends/requests/:targetUserId", isLogin, isRestricted, validate(targetUserIdSchema), asyncHandler(handleDeleteFriendRequest)); //보낸 친구 신청 삭제 - - -app.post("/matching/sessions/:questionId", isLogin, isRestricted, validate(postMatchingSessionSchema), asyncHandler(handlePostMatchingSession)); //세션 생성 -app.patch("/matching/sessions/:sessionId/friends", isLogin, isRestricted, validate(patchMatchingSessionStatusSchema), asyncHandler(handlePatchMatchingSessionStatusFriends)); //세션 친구됨으로 변경 -app.patch("/matching/sessions/:sessionId/discards", isLogin, isRestricted, validate(patchMatchingSessionStatusSchema), asyncHandler(handlePatchMatchingSessionStatusDiscarded)); //세션 삭제됨으로 변경 -app.post("/matching/sessions/:sessionId/reviews", isLogin, isRestricted, validate(postSessionReviewSchema), asyncHandler(handlePostSessionReview)); //세션 리뷰 작성 - - -app.post("/block/:targetUserId", isLogin, isRestricted, validate(postBlockUserSchema), asyncHandler(handlePostBlock)); -app.get("/block", isLogin, isRestricted, asyncHandler(handleGetBlock)); - -app.get("/restrict", isLogin, asyncHandler(handleGetRestrict)); - -app.post("/reports", isLogin, isRestricted, validate(insertUserReportSchema), asyncHandler(handleInsertUserReport)); - -app.get("/reports", isLogin, isRestricted, asyncHandler(handleGetUserReports)); -app.get("/reports/:reportId", isLogin, isRestricted, validate(getUserReportSchema), asyncHandler(handleGetUserReport)); - - -app.get("/weekly/reports", isLogin, isRestricted, asyncHandler(handleGetWeeklyReport)); - - -app.post("/inquiries", isLogin, validate(insertInquiryAsUserSchema), asyncHandler(handleInsertInquiryAsUser)); -app.post("/inquiries/admin", isLogin, validate(insertInquiryAsAdminSchema), asyncHandler(handleInsertInquiryAsAdmin)); -app.get("/inquiries", isLogin, asyncHandler(handleGetInquiry)); -app.get("/inquiries/:inquiryId", isLogin, validate(getInquiryDetailSchema), asyncHandler(handleGetInquiryDetail)); - -app.post("/auth/signup", validate(SignUpSchema), handleSignUp); // 회원가입 -app.post("/auth/login", validate(loginSchema), handleLogin); // 로그인 -app.get("/auth/oauth/:provider", handleSocialLogin); // 소셜 로그인 -app.post("/auth/login/:provider", handleSocialLoginCertification, handleSocialLoginCallback); // 소셜 로그인 응답 -app.post("/auth/username/exists", validate(usernameSchema), handleCheckDuplicatedUsername); // 아이디 중복 확인 -app.post("/auth/email/exists", validate(emailSchema), handleCheckDuplicatedEmail); // 이메일 중복 확인 -app.get("/auth/refresh", handleRefreshToken); // 액세스 토큰 재발급 -app.post("/auth/:type/verification-codes", validate(verificationSendCodeSchema), handleSendVerifyEmailCode); // 이메일 인증번호 전송 -app.post("/auth/:type/verification-codes/confirm", validate(verificationConfirmCodeSchema), handleCheckEmailCode); // 이메일 인증번호 확인 -app.post("/auth/find-id", validate(emailSchema), handleGetAccountInfo); // 아이디 찾기 -app.patch("/auth/reset-password", isLogin, validate(resetPasswordSchema), handleResetPassword); // 비밀번호 초기화 -app.patch("/auth/change-password", isLogin, validate(changePasswordSchema), handleChangePassword); // 비밀번호 변경 -app.post("/auth/logout", isLogin, handleLogout); // 로그아웃 -app.delete("/users", isLogin, handleWithdrawUser); // 탈퇴 -app.post("/users/me/agreements", isLogin, validate(createUserAgreementsSchema), handleCreateUserAgreements) // 이용약관 동의 - - -app.get("/letter-assets", isLogin, isRestricted, handleGetLetterAssets); // 편지 꾸미기 리소스 목록 조회 -app.post("/letter/me", isLogin, isRestricted, validate(letterToMeSchema), handleSendMyLetter); // 나에게 편지 전송 -app.post("/letter/other", isLogin, isRestricted, validate(letterToOtherSchema),handleSendOtherLetter); // 타인/친구에게 편지 전송 -app.get("/letters/:letterId", isLogin, isRestricted, validate(idParamSchema("letterId")),handleGetLetterDetail); // 편지 상세 조회 -app.get("/letters/keywords/:aiKeyword", isLogin, isRestricted, validate(letterByAiKeywordSchema), asyncHandler(handleGetLetterByAiKeyword)); // AI 키워드로 편지 조회 -app.post("/letters/:letterId/like", isLogin, isRestricted, validate(idParamSchema("letterId")), handleAddLetterLike); // 편지 좋아요 추가 -app.delete("/letters/:letterId/like", isLogin, isRestricted, validate(idParamSchema("letterId")), handleRemoveLetterLike); // 편지 좋아요 삭제 - -app.get("/questions/today", validate(ISOTimeSchema), handleGetTodayQuestion); // 오늘의 질문 조회 -app.get("/letters/others/public", isLogin, isRestricted, validate(publicCarouselSchema), handleGetPublicLetterFromOther); // 공개 편지 캐러셀 목록 조회 -app.get("/letters/friends/public", isLogin, isRestricted, validate(publicCarouselSchema), handleGetPublicLetterFromFriend); // 친구 편지 캐러셀 목록 조회 -app.get("/users/me/letters/stats", isLogin, isRestricted, validate(ISOTimeSchema), handleGetUserLetterStats) // 편지 여행 카드 데이터 조회 - -app.get("/home/summary", isLogin, isRestricted, validate(ISOTimeSchema), HandleGetHomeDashboard); // 홈 대시보드 조회 - -// 온보딩 설정 -app.patch("/users/me/onboarding", isLogin, validate(onboardingStep1Schema), handlePatchOnboardingStep1); -app.put("/users/me/onboarding/interests", isLogin, validate(updateInterestsSchema), handleUpdateMyOnboardingInterests); - -// 관심사 -app.get("/interests/all", handleGetAllInterests); // 전체 목록 (로그인 불필요) -app.get("/interests", isLogin, handleGetMyInterests); // 내 선택 목록 (로그인 필요) - -// 알람 설정 -app.patch("/users/me/notification-settings", isLogin, validate(updateNotificationSettingsSchema), handleUpdateMyNotificationSettings); -app.get("/users/me/notification-settings", isLogin, handleGetMyNotificationSettings); - -// 정책, 공지사항 -app.get("/policies/community-guidelines", handleGetCommunityGuidelines); -app.get("/policies/terms", handleGetTerms); -app.get("/policies/privacy", handleGetPrivacy); - -app.get("/notices", handleGetNotices); -app.get("/notices/:noticeId", validate(noticeIdParamSchema), handleGetNoticeDetail); - -// 동의 설정 -app.get("/users/me/consents", isLogin, handleGetMyConsents); -app.patch("/users/me/consents", isLogin, validate(updateConsentsSchema), handlePatchMyConsents); - -// 구독객체 -app.put("/users/me/push-subscriptions", isLogin, validate(pushSubscriptionSchema), handlePutMyPushSubscription); - -// / 편지함 -app.get("/mailbox/anonymous", isLogin, handleGetAnonymousThreads); -app.get("/mailbox/anonymous/threads/:sessionId/letters", isLogin, validate(sessionIdParamSchema), handleGetAnonymousThreadLetters); -app.get("/mailbox/friends/threads/:friendId/letters", isLogin, validate(idParamSchema("friendId")), handleGetLetterFromFriend); // 친구 대화 목록 화면 조회 -app.get("/mailbox/self", isLogin, handleGetSelfMailbox); - -// 프로필 -app.get("/users/me/profile", isLogin, handleGetMyProfile); -app.patch("/users/me/profile", isLogin, validate(updateProfileSchema), handlePatchMyProfile); -app.post("/users/me/profile/image", isLogin, upload.single("image"), handlePostMyProfileImage); - -// 활동 시간 추적 -app.post("/users/me/activity", isLogin, validate(updateActivitySchema), handleUpdateActivity); +// ✅ 라우터 등록 (여기서 도메인별로 분리된 Router들이 붙음) +registerRoutes(app); +// 에러 핸들러 app.use((err, req, res, next) => { if (res.headersSent) return next(err); @@ -234,6 +85,7 @@ app.use((err, req, res, next) => { return res.status(status).json({ resultType: "FAIL", error: { errorCode: err.errorCode || "COMMON_001", reason: err.reason || err.message || "Internal Server Error", data: err.data || null }, success: null }); }); + // 서버 실행 app.listen(port, async () => { console.log(`Server is running on port ${port}`); diff --git a/src/routes/auth.router.js b/src/routes/auth.router.js new file mode 100644 index 0000000..c0d71f8 --- /dev/null +++ b/src/routes/auth.router.js @@ -0,0 +1,57 @@ +// src/routes/auth.router.js +import express from "express"; +import { validate } from "../middlewares/validate.middleware.js"; +import { isLogin } from "../middlewares/auth.middleware.js"; + +import { + handleCheckDuplicatedEmail, + handleLogin, + handleRefreshToken, + handleSignUp, + handleSendVerifyEmailCode, + handleCheckEmailCode, + handleGetAccountInfo, + handleResetPassword, + handleLogout, + handleCheckDuplicatedUsername, + handleSocialLogin, + handleSocialLoginCertification, + handleSocialLoginCallback, + handleChangePassword, +} from "../controllers/auth.controller.js"; + +import { + changePasswordSchema, + emailSchema, + loginSchema, + resetPasswordSchema, + SignUpSchema, + usernameSchema, + verificationConfirmCodeSchema, + verificationSendCodeSchema, +} from "../schemas/auth.schema.js"; + +const router = express.Router(); + +router.post("/signup", validate(SignUpSchema), handleSignUp); +router.post("/login", validate(loginSchema), handleLogin); + +router.get("/oauth/:provider", handleSocialLogin); +router.post("/login/:provider", handleSocialLoginCertification, handleSocialLoginCallback); + +router.post("/username/exists", validate(usernameSchema), handleCheckDuplicatedUsername); +router.post("/email/exists", validate(emailSchema), handleCheckDuplicatedEmail); + +router.get("/refresh", handleRefreshToken); + +router.post("/:type/verification-codes", validate(verificationSendCodeSchema), handleSendVerifyEmailCode); +router.post("/:type/verification-codes/confirm", validate(verificationConfirmCodeSchema), handleCheckEmailCode); + +router.post("/find-id", validate(emailSchema), handleGetAccountInfo); + +router.patch("/reset-password", isLogin, validate(resetPasswordSchema), handleResetPassword); +router.patch("/change-password", isLogin, validate(changePasswordSchema), handleChangePassword); + +router.post("/logout", isLogin, handleLogout); + +export default router; \ No newline at end of file diff --git a/src/routes/block.router.js b/src/routes/block.router.js new file mode 100644 index 0000000..2e17b99 --- /dev/null +++ b/src/routes/block.router.js @@ -0,0 +1,25 @@ +// src/routes/block.router.js +import express from "express"; +import { isLogin } from "../middlewares/auth.middleware.js"; +import { isRestricted } from "../middlewares/restriction.middleware.js"; +import { validate } from "../middlewares/validate.middleware.js"; +import { asyncHandler } from "../utils/asyncHandler.js"; + +import { handleGetBlock, handlePostBlock } from "../controllers/block.controller.js"; +import { postBlockUserSchema } from "../schemas/block.schema.js"; + +const router = express.Router(); + +// POST /block/:targetUserId +router.post( + "/:targetUserId", + isLogin, + isRestricted, + validate(postBlockUserSchema), + asyncHandler(handlePostBlock) +); + +// GET /block +router.get("/", isLogin, isRestricted, asyncHandler(handleGetBlock)); + +export default router; \ No newline at end of file diff --git a/src/routes/friends.router.js b/src/routes/friends.router.js new file mode 100644 index 0000000..54908dc --- /dev/null +++ b/src/routes/friends.router.js @@ -0,0 +1,81 @@ +// src/routes/friends.router.js +import express from "express"; +import { isLogin } from "../middlewares/auth.middleware.js"; +import { isRestricted } from "../middlewares/restriction.middleware.js"; +import { validate } from "../middlewares/validate.middleware.js"; +import { asyncHandler } from "../utils/asyncHandler.js"; + +import { + handleGetFriendsList, + handlePostFriendsRequest, + handleGetIncomingFriendRequests, + handleGetOutgoingFriendRequests, + handleAcceptFriendRequest, + handleRejectFriendRequest, + handleDeleteFriendRequest, +} from "../controllers/friend.controller.js"; + +import { + postTargetUserIdAndSIdSchema, + requesterUserIdSchema, + targetUserIdSchema, +} from "../schemas/friend.schema.js"; + +const router = express.Router(); + +// GET /friends +router.get("/", isLogin, isRestricted, asyncHandler(handleGetFriendsList)); + +// POST /friends/requests +router.post( + "/requests", + isLogin, + isRestricted, + validate(postTargetUserIdAndSIdSchema), + asyncHandler(handlePostFriendsRequest) +); + +// GET /friends/requests/incoming +router.get( + "/requests/incoming", + isLogin, + isRestricted, + asyncHandler(handleGetIncomingFriendRequests) +); + +// GET /friends/requests/outgoing +router.get( + "/requests/outgoing", + isLogin, + isRestricted, + asyncHandler(handleGetOutgoingFriendRequests) +); + +// POST /friends/requests/accept/:requesterUserId +router.post( + "/requests/accept/:requesterUserId", + isLogin, + isRestricted, + validate(requesterUserIdSchema), + asyncHandler(handleAcceptFriendRequest) +); + +// POST /friends/requests/reject/:targetUserId +router.post( + "/requests/reject/:targetUserId", + isLogin, + isRestricted, + validate(targetUserIdSchema), + asyncHandler(handleRejectFriendRequest) +); + +// DELETE /friends/requests/:targetUserId +router.delete( + "/requests/:targetUserId", + isLogin, + isRestricted, + validate(targetUserIdSchema), + asyncHandler(handleDeleteFriendRequest) +); + +export default router; \ No newline at end of file diff --git a/src/routes/home.router.js b/src/routes/home.router.js new file mode 100644 index 0000000..8c2b2d0 --- /dev/null +++ b/src/routes/home.router.js @@ -0,0 +1,16 @@ +// src/routes/home.router.js +import express from "express"; +import { isLogin } from "../middlewares/auth.middleware.js"; +import { isRestricted } from "../middlewares/restriction.middleware.js"; +import { validate } from "../middlewares/validate.middleware.js"; +import { ISOTimeSchema } from "../schemas/common.schema.js"; +import { asyncHandler } from "../utils/asyncHandler.js"; + +import { HandleGetHomeDashboard } from "../controllers/dashboard.controller.js"; + +const router = express.Router(); + +// GET /home/summary +router.get("/summary", isLogin, isRestricted, validate(ISOTimeSchema), asyncHandler(HandleGetHomeDashboard)); + +export default router; \ No newline at end of file diff --git a/src/routes/index.js b/src/routes/index.js new file mode 100644 index 0000000..7bca5eb --- /dev/null +++ b/src/routes/index.js @@ -0,0 +1,50 @@ +// src/routes/index.js +import rootRouter from "./root.router.js"; +import authRouter from "./auth.router.js"; +import usersRouter from "./users.router.js"; +import interestsRouter from "./interests.router.js"; +import friendsRouter from "./friends.router.js"; +import matchingRouter from "./matching.router.js"; +import blockRouter from "./block.router.js"; +import restrictRouter from "./restrict.router.js"; +import reportsRouter from "./reports.router.js"; +import weeklyRouter from "./weekly.router.js"; +import inquiriesRouter from "./inquiries.router.js"; +import letterRouter from "./letter.router.js"; +import questionsRouter from "./questions.router.js"; +import homeRouter from "./home.router.js"; +import mailboxRouter from "./mailbox.router.js"; +import policyRouter from "./policy.router.js"; +import noticeRouter from "./notice.router.js"; + +export const registerRoutes = (app) => { + app.use("/", rootRouter); + + app.use("/auth", authRouter); + + app.use("/users", usersRouter); + app.use("/interests", interestsRouter); + + app.use("/friends", friendsRouter); + + app.use("/matching", matchingRouter); + + app.use("/block", blockRouter); + app.use("/restrict", restrictRouter); + + app.use("/reports", reportsRouter); + app.use("/weekly", weeklyRouter); + + app.use("/inquiries", inquiriesRouter); + + // ✅ /letter-assets, /letter/*, /letters/* 모두 여기서 처리 (URL 깨짐 없음) + app.use("/", letterRouter); + + app.use("/questions", questionsRouter); + app.use("/home", homeRouter); + + app.use("/mailbox", mailboxRouter); + + app.use("/policies", policyRouter); + app.use("/notices", noticeRouter); +}; \ No newline at end of file diff --git a/src/routes/inquiries.router.js b/src/routes/inquiries.router.js new file mode 100644 index 0000000..03c92a7 --- /dev/null +++ b/src/routes/inquiries.router.js @@ -0,0 +1,49 @@ +// src/routes/inquiries.router.js +import express from "express"; +import { isLogin } from "../middlewares/auth.middleware.js"; +import { validate } from "../middlewares/validate.middleware.js"; +import { asyncHandler } from "../utils/asyncHandler.js"; + +import { + handleInsertInquiryAsUser, + handleInsertInquiryAsAdmin, + handleGetInquiry, + handleGetInquiryDetail, +} from "../controllers/inquiry.controller.js"; + +import { + getInquiryDetailSchema, + insertInquiryAsAdminSchema, + insertInquiryAsUserSchema, +} from "../schemas/inquiry.schema.js"; + +const router = express.Router(); + +// POST /inquiries +router.post( + "/", + isLogin, + validate(insertInquiryAsUserSchema), + asyncHandler(handleInsertInquiryAsUser) +); + +// POST /inquiries/admin +router.post( + "/admin", + isLogin, + validate(insertInquiryAsAdminSchema), + asyncHandler(handleInsertInquiryAsAdmin) +); + +// GET /inquiries +router.get("/", isLogin, asyncHandler(handleGetInquiry)); + +// GET /inquiries/:inquiryId +router.get( + "/:inquiryId", + isLogin, + validate(getInquiryDetailSchema), + asyncHandler(handleGetInquiryDetail) +); + +export default router; \ No newline at end of file diff --git a/src/routes/interests.router.js b/src/routes/interests.router.js new file mode 100644 index 0000000..859ab2a --- /dev/null +++ b/src/routes/interests.router.js @@ -0,0 +1,15 @@ +// src/routes/interests.router.js +import express from "express"; +import { isLogin } from "../middlewares/auth.middleware.js"; +import { asyncHandler } from "../utils/asyncHandler.js"; +import { handleGetAllInterests, handleGetMyInterests } from "../controllers/user.controller.js"; + +const router = express.Router(); + +// GET /interests/all +router.get("/all", asyncHandler(handleGetAllInterests)); + +// GET /interests +router.get("/", isLogin, asyncHandler(handleGetMyInterests)); + +export default router; \ No newline at end of file diff --git a/src/routes/letter.router.js b/src/routes/letter.router.js new file mode 100644 index 0000000..07ed695 --- /dev/null +++ b/src/routes/letter.router.js @@ -0,0 +1,116 @@ +// src/routes/letter.router.js +import express from "express"; +import { isLogin } from "../middlewares/auth.middleware.js"; +import { isRestricted } from "../middlewares/restriction.middleware.js"; +import { validate } from "../middlewares/validate.middleware.js"; +import { asyncHandler } from "../utils/asyncHandler.js"; + +import { + handleGetLetterAssets, + handleSendMyLetter, + handleSendOtherLetter, + handleGetLetterDetail, + handleRemoveLetterLike, + handleAddLetterLike, + handleGetPublicLetterFromOther, + handleGetPublicLetterFromFriend, + handleGetLetterByAiKeyword, +} from "../controllers/letter.controller.js"; + +import { idParamSchema } from "../schemas/common.schema.js"; +import { + letterToMeSchema, + letterToOtherSchema, + publicCarouselSchema, + letterByAiKeywordSchema, +} from "../schemas/letter.schema.js"; + +const router = express.Router(); + +/** + * 1) /letter-assets + * GET /letter-assets + */ +router.get("/letter-assets", isLogin, isRestricted, asyncHandler(handleGetLetterAssets)); + +/** + * 2) /letter/* + * POST /letter/me + * POST /letter/other + */ +router.post( + "/letter/me", + isLogin, + isRestricted, + validate(letterToMeSchema), + asyncHandler(handleSendMyLetter) +); + +router.post( + "/letter/other", + isLogin, + isRestricted, + validate(letterToOtherSchema), + asyncHandler(handleSendOtherLetter) +); + +/** + * 3) /letters/* + * - 구체 경로 먼저! + */ + +// GET /letters/keywords/:aiKeyword +router.get( + "/letters/keywords/:aiKeyword", + isLogin, + isRestricted, + validate(letterByAiKeywordSchema), + asyncHandler(handleGetLetterByAiKeyword) +); + +// GET /letters/others/public +router.get( + "/letters/others/public", + isLogin, + isRestricted, + validate(publicCarouselSchema), + asyncHandler(handleGetPublicLetterFromOther) +); + +// GET /letters/friends/public +router.get( + "/letters/friends/public", + isLogin, + isRestricted, + validate(publicCarouselSchema), + asyncHandler(handleGetPublicLetterFromFriend) +); + +// POST /letters/:letterId/like +router.post( + "/letters/:letterId/like", + isLogin, + isRestricted, + validate(idParamSchema("letterId")), + asyncHandler(handleAddLetterLike) +); + +// DELETE /letters/:letterId/like +router.delete( + "/letters/:letterId/like", + isLogin, + isRestricted, + validate(idParamSchema("letterId")), + asyncHandler(handleRemoveLetterLike) +); + +// GET /letters/:letterId (맨 아래) +router.get( + "/letters/:letterId", + isLogin, + isRestricted, + validate(idParamSchema("letterId")), + asyncHandler(handleGetLetterDetail) +); + +export default router; \ No newline at end of file diff --git a/src/routes/mailbox.router.js b/src/routes/mailbox.router.js new file mode 100644 index 0000000..d3c8258 --- /dev/null +++ b/src/routes/mailbox.router.js @@ -0,0 +1,41 @@ +// src/routes/mailbox.router.js +import express from "express"; +import { isLogin } from "../middlewares/auth.middleware.js"; +import { validate } from "../middlewares/validate.middleware.js"; +import { asyncHandler } from "../utils/asyncHandler.js"; + +import { + handleGetAnonymousThreads, + handleGetAnonymousThreadLetters, + handleGetSelfMailbox, + handleGetLetterFromFriend, +} from "../controllers/mailbox.controller.js"; + +import { sessionIdParamSchema } from "../schemas/mailbox.schema.js"; +import { idParamSchema } from "../schemas/common.schema.js"; + +const router = express.Router(); + +// GET /mailbox/anonymous +router.get("/anonymous", isLogin, asyncHandler(handleGetAnonymousThreads)); + +// GET /mailbox/anonymous/threads/:sessionId/letters +router.get( + "/anonymous/threads/:sessionId/letters", + isLogin, + validate(sessionIdParamSchema), + asyncHandler(handleGetAnonymousThreadLetters) +); + +// GET /mailbox/friends/threads/:friendId/letters +router.get( + "/friends/threads/:friendId/letters", + isLogin, + validate(idParamSchema("friendId")), + asyncHandler(handleGetLetterFromFriend) +); + +// GET /mailbox/self +router.get("/self", isLogin, asyncHandler(handleGetSelfMailbox)); + +export default router; \ No newline at end of file diff --git a/src/routes/matching.router.js b/src/routes/matching.router.js new file mode 100644 index 0000000..0edd6f6 --- /dev/null +++ b/src/routes/matching.router.js @@ -0,0 +1,59 @@ +// src/routes/matching.router.js +import express from "express"; +import { isLogin } from "../middlewares/auth.middleware.js"; +import { isRestricted } from "../middlewares/restriction.middleware.js"; +import { validate } from "../middlewares/validate.middleware.js"; +import { asyncHandler } from "../utils/asyncHandler.js"; + +import { + handlePostMatchingSession, + handlePatchMatchingSessionStatusDiscarded, + handlePatchMatchingSessionStatusFriends, + handlePostSessionReview, +} from "../controllers/session.controller.js"; + +import { + postMatchingSessionSchema, + postSessionReviewSchema, + patchMatchingSessionStatusSchema, +} from "../schemas/session.schema.js"; + +const router = express.Router(); + +// POST /matching/sessions/:questionId +router.post( + "/sessions/:questionId", + isLogin, + isRestricted, + validate(postMatchingSessionSchema), + asyncHandler(handlePostMatchingSession) +); + +// PATCH /matching/sessions/:sessionId/friends +router.patch( + "/sessions/:sessionId/friends", + isLogin, + isRestricted, + validate(patchMatchingSessionStatusSchema), + asyncHandler(handlePatchMatchingSessionStatusFriends) +); + +// PATCH /matching/sessions/:sessionId/discards +router.patch( + "/sessions/:sessionId/discards", + isLogin, + isRestricted, + validate(patchMatchingSessionStatusSchema), + asyncHandler(handlePatchMatchingSessionStatusDiscarded) +); + +// POST /matching/sessions/:sessionId/reviews +router.post( + "/sessions/:sessionId/reviews", + isLogin, + isRestricted, + validate(postSessionReviewSchema), + asyncHandler(handlePostSessionReview) +); + +export default router; \ No newline at end of file diff --git a/src/routes/notice.router.js b/src/routes/notice.router.js new file mode 100644 index 0000000..9e48418 --- /dev/null +++ b/src/routes/notice.router.js @@ -0,0 +1,17 @@ +// src/routes/notice.router.js +import express from "express"; +import { validate } from "../middlewares/validate.middleware.js"; +import { asyncHandler } from "../utils/asyncHandler.js"; + +import { handleGetNotices, handleGetNoticeDetail } from "../controllers/notice.controller.js"; +import { noticeIdParamSchema } from "../schemas/notice.schema.js"; + +const router = express.Router(); + +// GET /notices +router.get("/", asyncHandler(handleGetNotices)); + +// GET /notices/:noticeId +router.get("/:noticeId", validate(noticeIdParamSchema), asyncHandler(handleGetNoticeDetail)); + +export default router; \ No newline at end of file diff --git a/src/routes/policy.router.js b/src/routes/policy.router.js new file mode 100644 index 0000000..280f1b0 --- /dev/null +++ b/src/routes/policy.router.js @@ -0,0 +1,22 @@ +// src/routes/policy.router.js +import express from "express"; +import { asyncHandler } from "../utils/asyncHandler.js"; + +import { + handleGetCommunityGuidelines, + handleGetTerms, + handleGetPrivacy, +} from "../controllers/policy.controller.js"; + +const router = express.Router(); + +// GET /policies/community-guidelines +router.get("/community-guidelines", asyncHandler(handleGetCommunityGuidelines)); + +// GET /policies/terms +router.get("/terms", asyncHandler(handleGetTerms)); + +// GET /policies/privacy +router.get("/privacy", asyncHandler(handleGetPrivacy)); + +export default router; \ No newline at end of file diff --git a/src/routes/questions.router.js b/src/routes/questions.router.js new file mode 100644 index 0000000..1f35edb --- /dev/null +++ b/src/routes/questions.router.js @@ -0,0 +1,13 @@ +// src/routes/questions.router.js +import express from "express"; +import { validate } from "../middlewares/validate.middleware.js"; +import { ISOTimeSchema } from "../schemas/common.schema.js"; +import { asyncHandler } from "../utils/asyncHandler.js"; +import { handleGetTodayQuestion } from "../controllers/question.controller.js"; + +const router = express.Router(); + +// GET /questions/today +router.get("/today", validate(ISOTimeSchema), asyncHandler(handleGetTodayQuestion)); + +export default router; \ No newline at end of file diff --git a/src/routes/reports.router.js b/src/routes/reports.router.js new file mode 100644 index 0000000..9af7f64 --- /dev/null +++ b/src/routes/reports.router.js @@ -0,0 +1,39 @@ +// src/routes/reports.router.js +import express from "express"; +import { isLogin } from "../middlewares/auth.middleware.js"; +import { isRestricted } from "../middlewares/restriction.middleware.js"; +import { validate } from "../middlewares/validate.middleware.js"; +import { asyncHandler } from "../utils/asyncHandler.js"; + +import { + handleInsertUserReport, + handleGetUserReports, + handleGetUserReport, +} from "../controllers/report.controller.js"; + +import { insertUserReportSchema, getUserReportSchema } from "../schemas/report.schema.js"; + +const router = express.Router(); + +// POST /reports +router.post( + "/", + isLogin, + isRestricted, + validate(insertUserReportSchema), + asyncHandler(handleInsertUserReport) +); + +// GET /reports +router.get("/", isLogin, isRestricted, asyncHandler(handleGetUserReports)); + +// GET /reports/:reportId +router.get( + "/:reportId", + isLogin, + isRestricted, + validate(getUserReportSchema), + asyncHandler(handleGetUserReport) +); + +export default router; \ No newline at end of file diff --git a/src/routes/restrict.router.js b/src/routes/restrict.router.js new file mode 100644 index 0000000..6e03b80 --- /dev/null +++ b/src/routes/restrict.router.js @@ -0,0 +1,12 @@ +// src/routes/restrict.router.js +import express from "express"; +import { isLogin } from "../middlewares/auth.middleware.js"; +import { asyncHandler } from "../utils/asyncHandler.js"; +import { handleGetRestrict } from "../controllers/restrict.controller.js"; + +const router = express.Router(); + +// GET /restrict +router.get("/", isLogin, asyncHandler(handleGetRestrict)); + +export default router; \ No newline at end of file diff --git a/src/routes/root.router.js b/src/routes/root.router.js new file mode 100644 index 0000000..cc1f425 --- /dev/null +++ b/src/routes/root.router.js @@ -0,0 +1,18 @@ +// src/routes/root.router.js +import express from "express"; +import { isLogin } from "../middlewares/auth.middleware.js"; + +const router = express.Router(); + +router.get("/", (req, res) => { + res.send("Hello World! Server is running."); +}); + +router.get("/mypage", isLogin, (req, res) => { + res.status(200).success({ + message: `인증 성공! ${req.user.name}님의 마이페이지입니다.`, + user: req.user, + }); +}); + +export default router; \ No newline at end of file diff --git a/src/routes/users.router.js b/src/routes/users.router.js new file mode 100644 index 0000000..2dc4d55 --- /dev/null +++ b/src/routes/users.router.js @@ -0,0 +1,87 @@ +// src/routes/users.router.js +import express from "express"; +import multer from "multer"; + +import { isLogin } from "../middlewares/auth.middleware.js"; +import { validate } from "../middlewares/validate.middleware.js"; +import { isRestricted } from "../middlewares/restriction.middleware.js"; +import { asyncHandler } from "../utils/asyncHandler.js"; + +import { + handleCreateUserAgreements, + handlePatchOnboardingStep1, + handleUpdateMyOnboardingInterests, + handleGetMyNotificationSettings, + handleUpdateMyNotificationSettings, + handleGetMyProfile, + handlePatchMyProfile, + handlePostMyProfileImage, + handlePutMyPushSubscription, + handleGetMyConsents, + handlePatchMyConsents, + handleUpdateActivity, +} from "../controllers/user.controller.js"; + +import { handleWithdrawUser } from "../controllers/auth.controller.js"; +import { handleGetUserLetterStats } from "../controllers/letter.controller.js"; + +import { + pushSubscriptionSchema, + onboardingStep1Schema, + updateInterestsSchema, + updateProfileSchema, + updateNotificationSettingsSchema, + updateConsentsSchema, + updateActivitySchema, + createUserAgreementsSchema, +} from "../schemas/user.schema.js"; + +import { ISOTimeSchema } from "../schemas/common.schema.js"; + +const router = express.Router(); +const upload = multer({ storage: multer.memoryStorage() }); + +// DELETE /users +router.delete("/", isLogin, asyncHandler(handleWithdrawUser)); + +// POST /users/me/agreements +router.post("/me/agreements", isLogin, validate(createUserAgreementsSchema), asyncHandler(handleCreateUserAgreements)); + +// 온보딩 +router.patch("/me/onboarding", isLogin, validate(onboardingStep1Schema), asyncHandler(handlePatchOnboardingStep1)); +router.put("/me/onboarding/interests", isLogin, validate(updateInterestsSchema), asyncHandler(handleUpdateMyOnboardingInterests)); + +// 알람 설정 +router.patch( + "/me/notification-settings", + isLogin, + validate(updateNotificationSettingsSchema), + asyncHandler(handleUpdateMyNotificationSettings) +); +router.get("/me/notification-settings", isLogin, asyncHandler(handleGetMyNotificationSettings)); + +// 동의 +router.get("/me/consents", isLogin, asyncHandler(handleGetMyConsents)); +router.patch("/me/consents", isLogin, validate(updateConsentsSchema), asyncHandler(handlePatchMyConsents)); + +// 푸시 구독 +router.put("/me/push-subscriptions", isLogin, validate(pushSubscriptionSchema), asyncHandler(handlePutMyPushSubscription)); + +// 프로필 +router.get("/me/profile", isLogin, asyncHandler(handleGetMyProfile)); +router.patch("/me/profile", isLogin, validate(updateProfileSchema), asyncHandler(handlePatchMyProfile)); +router.post("/me/profile/image", isLogin, upload.single("image"), asyncHandler(handlePostMyProfileImage)); + +// 활동 시간 추적 +router.post("/me/activity", isLogin, validate(updateActivitySchema), asyncHandler(handleUpdateActivity)); + +// 편지 여행 카드 데이터 조회 (기존 그대로 유지) +router.get( + "/me/letters/stats", + isLogin, + isRestricted, + validate(ISOTimeSchema), + asyncHandler(handleGetUserLetterStats) +); + +export default router; \ No newline at end of file diff --git a/src/routes/weekly.router.js b/src/routes/weekly.router.js new file mode 100644 index 0000000..e5242c2 --- /dev/null +++ b/src/routes/weekly.router.js @@ -0,0 +1,13 @@ +// src/routes/weekly.router.js +import express from "express"; +import { isLogin } from "../middlewares/auth.middleware.js"; +import { isRestricted } from "../middlewares/restriction.middleware.js"; +import { asyncHandler } from "../utils/asyncHandler.js"; +import { handleGetWeeklyReport } from "../controllers/weeklyReport.controller.js"; + +const router = express.Router(); + +// GET /weekly/reports +router.get("/reports", isLogin, isRestricted, asyncHandler(handleGetWeeklyReport)); + +export default router; \ No newline at end of file diff --git a/src/utils/asyncHandler.js b/src/utils/asyncHandler.js new file mode 100644 index 0000000..792d42e --- /dev/null +++ b/src/utils/asyncHandler.js @@ -0,0 +1,4 @@ +// src/utils/asyncHandler.js +export const asyncHandler = (fn) => (req, res, next) => { + Promise.resolve(fn(req, res, next)).catch(next); +}; \ No newline at end of file