Skip to content

Commit

Permalink
Add: routes/favoriteRoutes.ts, Add: services/favoriteRoutes.ts, Add: …
Browse files Browse the repository at this point in the history
…favoriteRoutesSchema.ts, Refactor: index.ts, Refactor: mongo.ts, Refactor: routes/index.ts, Refactor: mongo.d.ts
  • Loading branch information
thxx132 committed Feb 6, 2025
1 parent 749614e commit a129d28
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
reportRouter,
roomRouter,
userRouter,
favoriteRoutesRouter,
} from "@/routes";

import { initializeApp as initializeFirebase } from "@/modules/fcm";
Expand Down Expand Up @@ -89,6 +90,7 @@ app.use("/notifications", notificationRouter);
app.use("/reports", reportRouter);
app.use("/rooms", roomRouter);
app.use("/users", userRouter);
app.use("/favoriteRoutes", favoriteRoutesRouter);

// [Middleware] 전역 에러 핸들러. 에러 핸들러는 router들보다 아래에 등록되어야 합니다.
app.use(errorHandler);
Expand Down
30 changes: 30 additions & 0 deletions src/modules/stores/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
AdminIPWhitelist,
AdminLog,
TaxiFare,
FavoriteRoute,
} from "@/types/mongo";

const userSchema = new Schema<User>({
Expand Down Expand Up @@ -263,6 +264,35 @@ database.on("error", function (err) {
mongoose.disconnect();
});

const favoriteRouteSchema = new Schema<FavoriteRoute>({
user: {
// 사용자 ID
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
from: {
// 출발지
type: Schema.Types.ObjectId,
ref: "Location",
required: true,
},
to: {
// 도착지
type: Schema.Types.ObjectId,
ref: "Location",
required: true,
},
createdAt: {
// 생성시간
type: Date,
default: Date.now, // 생성 시간 자동 기록
},
});

// 즐겨찾기 모델 생성
export const favoriteRouteModel = model("FavoriteRoute", favoriteRouteSchema);

export const connectDatabase = (mongoUrl: string) => {
database.on("disconnected", () => {
// 데이터베이스 연결이 끊어지면 5초 후 재연결을 시도합니다.
Expand Down
13 changes: 13 additions & 0 deletions src/routes/docs/schemas/favoriteRoutesSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { z } from "zod";
import patterns from "../../../modules/patterns";

const objectId = patterns.objectId;

export const favoriteRoutesZod = {
createHandler: z.object({
from: z.string().regex(objectId, "Invalid from location ID"),
to: z.string().regex(objectId, "Invalid to location ID"),
}),
};

export type FavoriteRoutesSchema = typeof favoriteRoutesZod;
29 changes: 29 additions & 0 deletions src/routes/favoriteRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import express from "express";
import { validateBody, validateParams, validateQuery } from "@/middlewares/zod"; // Zod 검증 미들웨어
import { favoriteRoutesZod } from "./docs/schemas/favoriteRoutesSchema"; // Zod 스키마
import {
createHandler,
getHandler,
deleteHandler,
} from "@/services/favoriteRoutes";
import authMiddleware from "@/middlewares/auth"; // 인증 미들웨어

const router = express.Router();

// 라우터 접근 시 로그인 필요
router.use(authMiddleware);

// 즐겨찾기 생성
router.post(
"/create",
validateBody(favoriteRoutesZod.createHandler),
createHandler
);

// 즐겨찾기 조회
router.get("/", getHandler);

// 즐겨찾기 삭제
router.delete("/:id", deleteHandler);

export default router;
1 change: 1 addition & 0 deletions src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export { default as notificationRouter } from "./notifications";
export { default as reportRouter } from "./reports";
export { default as roomRouter } from "./rooms";
export { default as userRouter } from "./users";
export { default as favoriteRoutesRouter } from "./favoriteRoutes";
110 changes: 110 additions & 0 deletions src/services/favoriteRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { Request, Response } from "express";
import { favoriteRouteModel } from "@/modules/stores/mongo";
import { Types } from "mongoose";
import { getLoginInfo } from "@/modules/auths/login"; // 로그인 정보 가져오기

// ✅ 즐겨찾기 생성 (POST)
export const createHandler = async (
req: Request,
res: Response
): Promise<void> => {
try {
const user = getLoginInfo(req); // 로그인된 사용자 정보 가져오기
if (!user.oid) {
res.status(401).json({ error: "Unauthorized: No valid session found" });
return;
}

const userId = user.oid;
const { from, to } = req.body;

const existingRoute = await favoriteRouteModel.findOne({
user: userId,
from,
to,
});
if (existingRoute) {
res
.status(400)
.json({ error: "FavoriteRoutes/create: route already exists" });
return;
}

const newRoute = new favoriteRouteModel({ user: userId, from, to });
await newRoute.save();

res.status(201).json(newRoute);
} catch (error) {
res
.status(500)
.json({ error: "FavoriteRoutes/create: internal server error" });
}
};

// ✅ 즐겨찾기 조회 (GET)
export const getHandler = async (
req: Request,
res: Response
): Promise<void> => {
try {
const user = getLoginInfo(req); // 로그인된 사용자 정보 가져오기
if (!user.oid) {
res.status(401).json({ error: "Unauthorized: No valid session found" });
return;
}

const userId = user.oid;
const routes = await favoriteRouteModel
.find({ user: userId })
.populate("from to");

res.status(200).json(routes);
} catch (error) {
res
.status(500)
.json({ error: "FavoriteRoutes/get: internal server error" });
}
};

// ✅ 즐겨찾기 삭제 (DELETE)
export const deleteHandler = async (
req: Request<{ id: string }>,
res: Response
): Promise<void> => {
try {
const user = getLoginInfo(req); // 로그인된 사용자 정보 가져오기

if (!user.oid) {
res.status(401).json({ error: "Unauthorized: No valid session found" });
return;
}

const userId = user.oid;
const { id } = req.params;

if (!id || !Types.ObjectId.isValid(id)) {
res
.status(400)
.json({ error: "Invalid request: Missing or invalid route ID" });
return;
}

const route = await favoriteRouteModel.findOneAndDelete({
_id: id,
user: userId,
});

if (!route) {
res
.status(400)
.json({ error: "FavoriteRoutes/delete: no corresponding route" });
return;
}

res.status(200).json(route);
} catch (error) {
res
.status(500)
.json({ error: "FavoriteRoutes/delete: internal server error" });
}
};
11 changes: 11 additions & 0 deletions src/types/mongo.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,14 @@ export interface TaxiFare extends Document<Types.ObjectId> {
/** 예상 택시 요금. */
fare: number;
}

export interface FavoriteRoute extends Document {
/** 즐겨찾기를 등록한 사용자의 User ObjectID. */
user: Types.ObjectId;
/** 경로의 출발지 Location ObjectID. */
from: Types.ObjectId;
/** 경로의 도착지 Location ObjectID. */
to: Types.ObjectId;
/** 즐겨찾기 등록 시각. */
createdAt?: Date;
}

0 comments on commit a129d28

Please sign in to comment.