From b4c758091b93c892b596fab1c10cbc36cb6ed67b Mon Sep 17 00:00:00 2001 From: Sean Date: Thu, 4 Apr 2024 12:07:20 -0400 Subject: [PATCH 01/15] CHE-58 Updated index.ts and added first forum route and controller --- server/controllers/forumController.ts | 29 +++++++++++++++++++++++++++ server/index.ts | 2 ++ server/routes/forumRoutes.ts | 9 +++++++++ 3 files changed, 40 insertions(+) create mode 100644 server/controllers/forumController.ts create mode 100644 server/routes/forumRoutes.ts diff --git a/server/controllers/forumController.ts b/server/controllers/forumController.ts new file mode 100644 index 0000000..3dbb830 --- /dev/null +++ b/server/controllers/forumController.ts @@ -0,0 +1,29 @@ +import Forum from "../models/forumModel"; +import { Request, Response, NextFunction } from "express"; + +// ENDPOINT POST api/forums +// PURPOSE Create a new forum +// ACCESS Admin + +const addForum = async (req: Request, res: Response, next: NextFunction) => { + const { title, description } = req.body; + + try { + //TODO add auth check for admin status + + const forum = await Forum.create({ + title, + description, + }); + + res.status(201).json(forum); + } catch (error) { + next({ + log: `Express error in addForum controller: ${error}`, + status: 500, + message: { err: "Server error creating forum" }, + }); + } +}; + +export { addForum }; diff --git a/server/index.ts b/server/index.ts index a189f85..f930498 100644 --- a/server/index.ts +++ b/server/index.ts @@ -3,6 +3,7 @@ import express, { Request, Response, Application, NextFunction } from "express"; import userRoutes from "./routes/userRoutes"; import profileRoutes from "./routes/profileRoutes"; import authRoutes from "./routes/authRoutes"; +import forumRoutes from "./routes/forumRoutes"; import connectDB from "./config/db"; import dotenv from "dotenv"; import cookieParser from "cookie-parser"; @@ -20,6 +21,7 @@ connectDB(); app.use("/api/users", userRoutes); app.use("/api/profiles", profileRoutes); app.use("/api/auth", authRoutes); +app.use("/api/forums", forumRoutes); console.log(`ENV BEFORE CHECK: ${process.env.NODE_ENV}`); diff --git a/server/routes/forumRoutes.ts b/server/routes/forumRoutes.ts new file mode 100644 index 0000000..8a4c3d0 --- /dev/null +++ b/server/routes/forumRoutes.ts @@ -0,0 +1,9 @@ +import express from "express"; +import { addForum } from "../controllers/forumController"; +import { protect } from "../middleware/authMiddleware"; //TODO Add admin auth middleware + +const router = express.Router(); + +router.post("/", addForum); + +export default router; From 2971a1494e7990685cf29bfcdec8ffd77d1ca773 Mon Sep 17 00:00:00 2001 From: Sean Date: Thu, 4 Apr 2024 13:17:08 -0400 Subject: [PATCH 02/15] CHE-58 Added getAllForums controller and route --- server/controllers/forumController.ts | 23 +++++++++++++++++++++-- server/routes/forumRoutes.ts | 3 ++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/server/controllers/forumController.ts b/server/controllers/forumController.ts index 3dbb830..4adaedb 100644 --- a/server/controllers/forumController.ts +++ b/server/controllers/forumController.ts @@ -4,7 +4,6 @@ import { Request, Response, NextFunction } from "express"; // ENDPOINT POST api/forums // PURPOSE Create a new forum // ACCESS Admin - const addForum = async (req: Request, res: Response, next: NextFunction) => { const { title, description } = req.body; @@ -26,4 +25,24 @@ const addForum = async (req: Request, res: Response, next: NextFunction) => { } }; -export { addForum }; +// ENDPOINT GET api/forums +// PURPOSE Retrieve a list of all forums +// ACCESS all users +const getAllForums = async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + const forums = await Forum.find({}); + res.status(200).json(forums); + } catch (error) { + next({ + log: `Express error in getAllForums controller: ${error}`, + status: 500, + message: { err: "Server error fetching forums" }, + }); + } +}; + +export { addForum, getAllForums }; diff --git a/server/routes/forumRoutes.ts b/server/routes/forumRoutes.ts index 8a4c3d0..3a6fe2b 100644 --- a/server/routes/forumRoutes.ts +++ b/server/routes/forumRoutes.ts @@ -1,9 +1,10 @@ import express from "express"; -import { addForum } from "../controllers/forumController"; +import { addForum, getAllForums } from "../controllers/forumController"; import { protect } from "../middleware/authMiddleware"; //TODO Add admin auth middleware const router = express.Router(); router.post("/", addForum); +router.get("/", getAllForums); export default router; From 38f4c14e4d430220c41c76ade1aac1494965b58c Mon Sep 17 00:00:00 2001 From: Sean Date: Thu, 4 Apr 2024 13:38:59 -0400 Subject: [PATCH 03/15] CHE-58 Added getForumById controller and route --- server/controllers/forumController.ts | 34 ++++++++++++++++++++++++++- server/routes/forumRoutes.ts | 7 +++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/server/controllers/forumController.ts b/server/controllers/forumController.ts index 4adaedb..69cb251 100644 --- a/server/controllers/forumController.ts +++ b/server/controllers/forumController.ts @@ -1,4 +1,5 @@ import Forum from "../models/forumModel"; +import Thread from "../models/threadModel"; import { Request, Response, NextFunction } from "express"; // ENDPOINT POST api/forums @@ -45,4 +46,35 @@ const getAllForums = async ( } }; -export { addForum, getAllForums }; +// ENDPOINT GET api/forums/:forumId +// PURPOSE Retrieve a list of all forums +// ACCESS all users +const getForumById = async ( + req: Request, + res: Response, + next: NextFunction +) => { + const { forumId } = req.params; + + try { + const forum = await Forum.findById(forumId); + if (!forum) { + return res.status(404).json({ message: "Forum not found" }); + } + + const threads = await Thread.find({ forum: forumId }).populate( + "user", + "firstName lastName" + ); + + res.status(200).json({ forum, threads }); + } catch (error) { + next({ + log: `Express error in getForumById controller: ${error}`, + status: 500, + message: { err: "Server error fetching forum details" }, + }); + } +}; + +export { addForum, getAllForums, getForumById }; diff --git a/server/routes/forumRoutes.ts b/server/routes/forumRoutes.ts index 3a6fe2b..9e8b5ed 100644 --- a/server/routes/forumRoutes.ts +++ b/server/routes/forumRoutes.ts @@ -1,10 +1,15 @@ import express from "express"; -import { addForum, getAllForums } from "../controllers/forumController"; +import { + addForum, + getAllForums, + getForumById, +} from "../controllers/forumController"; import { protect } from "../middleware/authMiddleware"; //TODO Add admin auth middleware const router = express.Router(); router.post("/", addForum); router.get("/", getAllForums); +router.get("/:forumId", getForumById); export default router; From f74ce600285688402b3563cb2c908fba64559449 Mon Sep 17 00:00:00 2001 From: Sean Date: Thu, 4 Apr 2024 14:11:07 -0400 Subject: [PATCH 04/15] CHE-58 Added updateForum controller and route --- server/controllers/forumController.ts | 32 ++++++++++++++++++++++++++- server/routes/forumRoutes.ts | 2 ++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/server/controllers/forumController.ts b/server/controllers/forumController.ts index 69cb251..3490980 100644 --- a/server/controllers/forumController.ts +++ b/server/controllers/forumController.ts @@ -77,4 +77,34 @@ const getForumById = async ( } }; -export { addForum, getAllForums, getForumById }; +// ENDPOINT PUT api/forums/:forumId +// PURPOSE Update title/description of forum +// ACCESS Admin +const updateForum = async (req: Request, res: Response, next: NextFunction) => { + const { forumId } = req.params; + const { title, description } = req.body; + + try { + //TODO add auth check for admin status + + const forum = await Forum.findByIdAndUpdate( + forumId, + { $set: { title, description } }, + { new: true } + ); + + if (!forum) { + return res.status(404).json({ message: "Forum not found" }); + } + + res.status(200).json(forum); + } catch (error) { + next({ + log: `Express error in updateForum controller: ${error}`, + status: 500, + message: { err: "Server error updating forum details" }, + }); + } +}; + +export { addForum, getAllForums, getForumById, updateForum }; diff --git a/server/routes/forumRoutes.ts b/server/routes/forumRoutes.ts index 9e8b5ed..a4f2621 100644 --- a/server/routes/forumRoutes.ts +++ b/server/routes/forumRoutes.ts @@ -3,6 +3,7 @@ import { addForum, getAllForums, getForumById, + updateForum, } from "../controllers/forumController"; import { protect } from "../middleware/authMiddleware"; //TODO Add admin auth middleware @@ -11,5 +12,6 @@ const router = express.Router(); router.post("/", addForum); router.get("/", getAllForums); router.get("/:forumId", getForumById); +router.put("/:forumId", updateForum); export default router; From 17a9ab83a384c394275c5492fcef8a9660eadaa2 Mon Sep 17 00:00:00 2001 From: Sean Date: Thu, 4 Apr 2024 14:47:22 -0400 Subject: [PATCH 05/15] CHE-58 Added deleteForum controller and route --- server/controllers/forumController.ts | 28 ++++++++++++++++++++++++++- server/routes/forumRoutes.ts | 6 ++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/server/controllers/forumController.ts b/server/controllers/forumController.ts index 3490980..0c9d161 100644 --- a/server/controllers/forumController.ts +++ b/server/controllers/forumController.ts @@ -107,4 +107,30 @@ const updateForum = async (req: Request, res: Response, next: NextFunction) => { } }; -export { addForum, getAllForums, getForumById, updateForum }; +// ENDPOINT DELETE api/forums/:forumId +// PURPOSE Delete a forum +// ACCESS Admin +const deleteForum = async (req: Request, res: Response, next: NextFunction) => { + const { forumId } = req.params; + + try { + //TODO add auth check for admin status + + const deletedForum = await Forum.findByIdAndDelete(forumId); + console.log("deletedForum", deletedForum); + + if (!deletedForum) { + return res.status(404).json({ message: "Forum not found" }); + } + + res.status(200).json({ message: "Forum deleted successfully" }); + } catch (error) { + next({ + log: `Express error in deleteForum controller: ${error}`, + status: 500, + message: { err: "Server error deleting forum" }, + }); + } +}; + +export { addForum, getAllForums, getForumById, updateForum, deleteForum }; diff --git a/server/routes/forumRoutes.ts b/server/routes/forumRoutes.ts index a4f2621..82073d7 100644 --- a/server/routes/forumRoutes.ts +++ b/server/routes/forumRoutes.ts @@ -1,6 +1,7 @@ import express from "express"; import { addForum, + deleteForum, getAllForums, getForumById, updateForum, @@ -9,9 +10,10 @@ import { protect } from "../middleware/authMiddleware"; //TODO Add admin auth mi const router = express.Router(); -router.post("/", addForum); +router.post("/", addForum); //TODO Protect with admin auth router.get("/", getAllForums); router.get("/:forumId", getForumById); -router.put("/:forumId", updateForum); +router.put("/:forumId", updateForum); //TODO Protect with admin auth +router.delete("/:forumId", deleteForum); //TODO Protect with admin auth export default router; From d67e581f97c2c277a5be16dbe354166f30faf967 Mon Sep 17 00:00:00 2001 From: Sean Date: Thu, 4 Apr 2024 17:24:04 -0400 Subject: [PATCH 06/15] CHE-58 Added createThread controller and route and added a CustomRequest interface as well as protected some routes --- server/controllers/threadController.ts | 40 ++++++++++++++++++++++++++ server/middleware/authMiddleware.ts | 6 ++-- server/models/userModel.ts | 2 +- server/routes/forumRoutes.ts | 23 +++++++++++---- server/types/customRequest.ts | 9 ++++++ 5 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 server/controllers/threadController.ts create mode 100644 server/types/customRequest.ts diff --git a/server/controllers/threadController.ts b/server/controllers/threadController.ts new file mode 100644 index 0000000..21c1e73 --- /dev/null +++ b/server/controllers/threadController.ts @@ -0,0 +1,40 @@ +import Forum from "../models/forumModel"; +import Thread from "../models/threadModel"; +import { Request, Response, NextFunction } from "express"; +import { CustomRequest } from "../types/customRequest"; + +// ENDPOINT POST api/:forumId/threads +// PURPOSE Create a new thread +// ACCESS Private +const createThread = async ( + req: CustomRequest, + res: Response, + next: NextFunction +) => { + const { forumId } = req.params; + const { title, content } = req.body; + + if (!req.user) { + return res.status(401).json({ message: "Not authenticated" }); + } + const userId = req.user; + + try { + const thread = await Thread.create({ + forum: forumId, + user: userId, + title, + content, + }); + + res.status(201).json(thread); + } catch (error) { + next({ + log: `Express error in createThread controller: ${error}`, + status: 500, + message: { err: "Server error creating thread" }, + }); + } +}; + +export { createThread }; diff --git a/server/middleware/authMiddleware.ts b/server/middleware/authMiddleware.ts index e08abf3..570e3fa 100644 --- a/server/middleware/authMiddleware.ts +++ b/server/middleware/authMiddleware.ts @@ -1,9 +1,9 @@ import jwt from "jsonwebtoken"; import User from "../models/userModel"; import asyncHandler from "express-async-handler"; -import { Request } from "express"; +import { CustomRequest } from "../types/customRequest"; -const protect = asyncHandler(async (req, res, next) => { +const protect = asyncHandler(async (req: CustomRequest, res, next) => { let token; console.log("PROTECT HIT"); console.log(req.headers); @@ -24,7 +24,7 @@ const protect = asyncHandler(async (req, res, next) => { const user = await User.findById(decoded.id).select("-password"); if (!user) throw new Error("User not found"); - + req.user = decoded.id; res.locals.user = user; next(); } catch (error) { diff --git a/server/models/userModel.ts b/server/models/userModel.ts index ffcfcf4..a47edd0 100644 --- a/server/models/userModel.ts +++ b/server/models/userModel.ts @@ -50,6 +50,6 @@ userSchema.pre("save", async function (next) { next(); }); -const User = mongoose.model("users", userSchema); +const User = mongoose.model("User", userSchema); export default User; diff --git a/server/routes/forumRoutes.ts b/server/routes/forumRoutes.ts index 82073d7..68793c5 100644 --- a/server/routes/forumRoutes.ts +++ b/server/routes/forumRoutes.ts @@ -1,4 +1,6 @@ import express from "express"; +import { Request, Response, NextFunction } from "express"; +import { CustomRequest } from "../types/customRequest"; import { addForum, deleteForum, @@ -6,14 +8,25 @@ import { getForumById, updateForum, } from "../controllers/forumController"; + +import { createThread } from "../controllers/threadController"; import { protect } from "../middleware/authMiddleware"; //TODO Add admin auth middleware +function mockAuth(req: CustomRequest, res: Response, next: NextFunction) { + req.user = { + _id: "6569e218784b4454e0b5bda6", + }; + next(); +} + const router = express.Router(); -router.post("/", addForum); //TODO Protect with admin auth -router.get("/", getAllForums); -router.get("/:forumId", getForumById); -router.put("/:forumId", updateForum); //TODO Protect with admin auth -router.delete("/:forumId", deleteForum); //TODO Protect with admin auth +router.post("/", protect, addForum); //TODO Protect with admin auth +router.get("/", protect, getAllForums); +router.get("/:forumId", protect, getForumById); +router.put("/:forumId", protect, updateForum); //TODO Protect with admin auth +router.delete("/:forumId", protect, deleteForum); //TODO Protect with admin auth + +router.post("/:forumId/threads", protect, createThread); export default router; diff --git a/server/types/customRequest.ts b/server/types/customRequest.ts new file mode 100644 index 0000000..38a892b --- /dev/null +++ b/server/types/customRequest.ts @@ -0,0 +1,9 @@ +import { Request } from "express"; + +interface UserPayload { + _id: string; +} + +export interface CustomRequest extends Request { + user?: UserPayload; +} From 38a4c2f835b2a673321dafabb24acf023964337b Mon Sep 17 00:00:00 2001 From: Sean Date: Thu, 4 Apr 2024 17:26:39 -0400 Subject: [PATCH 07/15] CHE-58 Removed unused auth mock and imports --- server/routes/forumRoutes.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/server/routes/forumRoutes.ts b/server/routes/forumRoutes.ts index 68793c5..020e02e 100644 --- a/server/routes/forumRoutes.ts +++ b/server/routes/forumRoutes.ts @@ -1,6 +1,4 @@ import express from "express"; -import { Request, Response, NextFunction } from "express"; -import { CustomRequest } from "../types/customRequest"; import { addForum, deleteForum, @@ -12,13 +10,6 @@ import { import { createThread } from "../controllers/threadController"; import { protect } from "../middleware/authMiddleware"; //TODO Add admin auth middleware -function mockAuth(req: CustomRequest, res: Response, next: NextFunction) { - req.user = { - _id: "6569e218784b4454e0b5bda6", - }; - next(); -} - const router = express.Router(); router.post("/", protect, addForum); //TODO Protect with admin auth From 4ea7c5cf1535fa416e7ca6d4a5daa07cbea71ba6 Mon Sep 17 00:00:00 2001 From: Sean Date: Thu, 4 Apr 2024 18:38:08 -0400 Subject: [PATCH 08/15] CHE-58 Added listThreadsByForumId controller and route --- server/controllers/threadController.ts | 27 +++++++++++++++++++++++++- server/routes/forumRoutes.ts | 6 +++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/server/controllers/threadController.ts b/server/controllers/threadController.ts index 21c1e73..cadc098 100644 --- a/server/controllers/threadController.ts +++ b/server/controllers/threadController.ts @@ -37,4 +37,29 @@ const createThread = async ( } }; -export { createThread }; +// ENDPOINT GET api/:forumId/threads +// PURPOSE Retrieve all threads for a specific forum +// ACCESS Private +const listThreadsByForumId = async ( + req: Request, + res: Response, + next: NextFunction +) => { + const { forumId } = req.params; + + try { + const threads = await Thread.find({ forum: forumId }) + .populate("user", "firstName lastName") + .exec(); + + res.status(200).json(threads); + } catch (error) { + next({ + log: `Express error in listThreadsByForum controller: ${error}`, + status: 500, + message: { err: "Server error listing threads by forum" }, + }); + } +}; + +export { createThread, listThreadsByForumId }; diff --git a/server/routes/forumRoutes.ts b/server/routes/forumRoutes.ts index 020e02e..6ecd2f1 100644 --- a/server/routes/forumRoutes.ts +++ b/server/routes/forumRoutes.ts @@ -7,7 +7,10 @@ import { updateForum, } from "../controllers/forumController"; -import { createThread } from "../controllers/threadController"; +import { + createThread, + listThreadsByForumId, +} from "../controllers/threadController"; import { protect } from "../middleware/authMiddleware"; //TODO Add admin auth middleware const router = express.Router(); @@ -19,5 +22,6 @@ router.put("/:forumId", protect, updateForum); //TODO Protect with admin auth router.delete("/:forumId", protect, deleteForum); //TODO Protect with admin auth router.post("/:forumId/threads", protect, createThread); +router.get("/:forumId/threads", protect, listThreadsByForumId); export default router; From 2d426ba65ea58d4a8dd0a4647d538d640f8a3c01 Mon Sep 17 00:00:00 2001 From: Sean Date: Thu, 4 Apr 2024 19:56:26 -0400 Subject: [PATCH 09/15] CHE-58 Added getThreadById controller and route --- server/controllers/threadController.ts | 37 ++++++++++++++++++++++++-- server/routes/forumRoutes.ts | 2 ++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/server/controllers/threadController.ts b/server/controllers/threadController.ts index cadc098..a03e66d 100644 --- a/server/controllers/threadController.ts +++ b/server/controllers/threadController.ts @@ -1,4 +1,4 @@ -import Forum from "../models/forumModel"; +import Post from "../models/postModel"; import Thread from "../models/threadModel"; import { Request, Response, NextFunction } from "express"; import { CustomRequest } from "../types/customRequest"; @@ -62,4 +62,37 @@ const listThreadsByForumId = async ( } }; -export { createThread, listThreadsByForumId }; +// ENDPOINT GET api/forums/:forumId/threads/:threadId +// PURPOSE Retrieve a specific thread and all of its posts +// ACCESS Private +const getThreadById = async ( + req: Request, + res: Response, + next: NextFunction +) => { + const { forumId, threadId } = req.params; + + try { + const thread = await Thread.findOne({ _id: threadId, forum: forumId }) + .populate("user", "firstName lastName") + .exec(); + + if (!thread) { + return res.status(404).json({ message: "Thread not found" }); + } + + const posts = await Post.find({ thread: threadId }) + .populate("user", "firstName lastName") + .exec(); + + res.status(200).json({ thread, posts }); + } catch (error) { + next({ + log: `Express error in getThreadById controller: ${error}`, + status: 500, + message: { err: "Server error fetching thread details" }, + }); + } +}; + +export { createThread, listThreadsByForumId, getThreadById }; diff --git a/server/routes/forumRoutes.ts b/server/routes/forumRoutes.ts index 6ecd2f1..aa75f91 100644 --- a/server/routes/forumRoutes.ts +++ b/server/routes/forumRoutes.ts @@ -9,6 +9,7 @@ import { import { createThread, + getThreadById, listThreadsByForumId, } from "../controllers/threadController"; import { protect } from "../middleware/authMiddleware"; //TODO Add admin auth middleware @@ -23,5 +24,6 @@ router.delete("/:forumId", protect, deleteForum); //TODO Protect with admin auth router.post("/:forumId/threads", protect, createThread); router.get("/:forumId/threads", protect, listThreadsByForumId); +router.get("/:forumId/threads/:threadId", protect, getThreadById); export default router; From cf9c9a84cc146b3b678cd620cc5cba0c04348b44 Mon Sep 17 00:00:00 2001 From: Sean Date: Fri, 5 Apr 2024 08:41:37 -0400 Subject: [PATCH 10/15] CHE-58 Added updateThread controller and route as well as adjusted auth logic and CustomRequest interface --- server/controllers/threadController.ts | 43 ++++++++++++++++++++++++-- server/middleware/authMiddleware.ts | 2 +- server/routes/forumRoutes.ts | 4 +++ server/types/customRequest.ts | 2 +- 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/server/controllers/threadController.ts b/server/controllers/threadController.ts index a03e66d..1c2076a 100644 --- a/server/controllers/threadController.ts +++ b/server/controllers/threadController.ts @@ -17,7 +17,7 @@ const createThread = async ( if (!req.user) { return res.status(401).json({ message: "Not authenticated" }); } - const userId = req.user; + const userId = req.user.id; try { const thread = await Thread.create({ @@ -95,4 +95,43 @@ const getThreadById = async ( } }; -export { createThread, listThreadsByForumId, getThreadById }; +// ENDPOINT PUT api/forums/:forumId/threads/:threadId +// PURPOSE Update a specific thread +// ACCESS Private/Admin +const updateThread = async ( + req: CustomRequest, + res: Response, + next: NextFunction +) => { + const { forumId, threadId } = req.params; + const { title, content } = req.body; + + try { + const thread = await Thread.findOne({ _id: threadId, forum: forumId }); + if (!thread) { + return res.status(404).json({ message: "Thread not found" }); + } + + if (!req.user || thread.user._id.toString() !== req.user.id) { + return res + .status(403) + .json({ message: "Not authorized to update this thread" }); + } + + const updatedThread = await Thread.findByIdAndUpdate( + threadId, + { $set: { title, content } }, + { new: true, runValidators: true } + ).populate("user", "firstName lastName"); + + res.status(200).json(updatedThread); + } catch (error) { + next({ + log: `Express error in updateThread controller: ${error}`, + status: 500, + message: { err: "Server error updating thread" }, + }); + } +}; + +export { createThread, listThreadsByForumId, getThreadById, updateThread }; diff --git a/server/middleware/authMiddleware.ts b/server/middleware/authMiddleware.ts index 570e3fa..b399c0e 100644 --- a/server/middleware/authMiddleware.ts +++ b/server/middleware/authMiddleware.ts @@ -24,7 +24,7 @@ const protect = asyncHandler(async (req: CustomRequest, res, next) => { const user = await User.findById(decoded.id).select("-password"); if (!user) throw new Error("User not found"); - req.user = decoded.id; + req.user = { id: user._id.toString() }; res.locals.user = user; next(); } catch (error) { diff --git a/server/routes/forumRoutes.ts b/server/routes/forumRoutes.ts index aa75f91..d6882d7 100644 --- a/server/routes/forumRoutes.ts +++ b/server/routes/forumRoutes.ts @@ -11,19 +11,23 @@ import { createThread, getThreadById, listThreadsByForumId, + updateThread, } from "../controllers/threadController"; import { protect } from "../middleware/authMiddleware"; //TODO Add admin auth middleware const router = express.Router(); +//Forum Routes router.post("/", protect, addForum); //TODO Protect with admin auth router.get("/", protect, getAllForums); router.get("/:forumId", protect, getForumById); router.put("/:forumId", protect, updateForum); //TODO Protect with admin auth router.delete("/:forumId", protect, deleteForum); //TODO Protect with admin auth +//Thread Routes router.post("/:forumId/threads", protect, createThread); router.get("/:forumId/threads", protect, listThreadsByForumId); router.get("/:forumId/threads/:threadId", protect, getThreadById); +router.put("/:forumId/threads/:threadId", protect, updateThread); export default router; diff --git a/server/types/customRequest.ts b/server/types/customRequest.ts index 38a892b..f3d8e34 100644 --- a/server/types/customRequest.ts +++ b/server/types/customRequest.ts @@ -1,7 +1,7 @@ import { Request } from "express"; interface UserPayload { - _id: string; + id: string; } export interface CustomRequest extends Request { From ed5d40ae2ee7cf6f054395154dac7054fcffbf6a Mon Sep 17 00:00:00 2001 From: Sean Date: Fri, 5 Apr 2024 09:28:25 -0400 Subject: [PATCH 11/15] CHE-58 Added deleteThread controller and route --- server/controllers/threadController.ts | 51 +++++++++++++++++++++++++- server/routes/forumRoutes.ts | 4 +- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/server/controllers/threadController.ts b/server/controllers/threadController.ts index 1c2076a..cf8eb98 100644 --- a/server/controllers/threadController.ts +++ b/server/controllers/threadController.ts @@ -134,4 +134,53 @@ const updateThread = async ( } }; -export { createThread, listThreadsByForumId, getThreadById, updateThread }; +// ENDPOINT DELETE api/forums/:forumId/threads/:threadId +// PURPOSE Delete a specific thread +// ACCESS Private/Admin +const deleteThread = async ( + req: CustomRequest, + res: Response, + next: NextFunction +) => { + const { forumId, threadId } = req.params; + + try { + const threadToCheck = await Thread.findById(threadId); + if (!threadToCheck) { + return res.status(404).json({ message: "Thread not found" }); + } + //TODO Add admin auth check + if (!req.user || threadToCheck.user.toString() !== req.user.id) { + return res + .status(403) + .json({ message: "Not authorized to delete this thread" }); + } + + const deletedThread = await Thread.findByIdAndDelete({ + _id: threadId, + forum: forumId, + }); + + if (!deletedThread) { + return res + .status(404) + .json({ message: "Thread not found or already deleted" }); + } + + res.status(200).json({ message: "Thread deleted successfully" }); + } catch (error) { + next({ + log: `Express error in deleteThread controller: ${error}`, + status: 500, + message: { err: "Server error deleting thread" }, + }); + } +}; + +export { + createThread, + listThreadsByForumId, + getThreadById, + updateThread, + deleteThread, +}; diff --git a/server/routes/forumRoutes.ts b/server/routes/forumRoutes.ts index d6882d7..8095254 100644 --- a/server/routes/forumRoutes.ts +++ b/server/routes/forumRoutes.ts @@ -9,6 +9,7 @@ import { import { createThread, + deleteThread, getThreadById, listThreadsByForumId, updateThread, @@ -28,6 +29,7 @@ router.delete("/:forumId", protect, deleteForum); //TODO Protect with admin auth router.post("/:forumId/threads", protect, createThread); router.get("/:forumId/threads", protect, listThreadsByForumId); router.get("/:forumId/threads/:threadId", protect, getThreadById); -router.put("/:forumId/threads/:threadId", protect, updateThread); +router.put("/:forumId/threads/:threadId", protect, updateThread); //TODO Protect with admin auth +router.delete("/:forumId/threads/:threadId", protect, deleteThread); //TODO Protect with admin auth export default router; From 99e01520ecd83b02b56c73474f0d65125ceabc1c Mon Sep 17 00:00:00 2001 From: Sean Date: Fri, 5 Apr 2024 10:40:47 -0400 Subject: [PATCH 12/15] CHE-58 Added listPostsByThread controller and route --- server/controllers/postController.ts | 29 ++++++++++++++++++++++++++++ server/routes/forumRoutes.ts | 6 ++++++ 2 files changed, 35 insertions(+) create mode 100644 server/controllers/postController.ts diff --git a/server/controllers/postController.ts b/server/controllers/postController.ts new file mode 100644 index 0000000..238db6b --- /dev/null +++ b/server/controllers/postController.ts @@ -0,0 +1,29 @@ +import Post from "../models/postModel"; +import { Request, Response, NextFunction } from "express"; + +// ENDPOINT GET api/forums/:forumId/threads/:threadId/posts +// PURPOSE Retrieve all posts from a specific thread +// ACCESS Private +const listPostsByThreadId = async ( + req: Request, + res: Response, + next: NextFunction +) => { + const { threadId } = req.params; + + try { + const posts = await Post.find({ thread: threadId }) + .populate("user", "firstName lastName") + .exec(); + + res.status(200).json(posts); + } catch (error) { + next({ + log: `Express error in listPostsByThreadId controller: ${error}`, + status: 500, + message: { err: "Server error fetching posts" }, + }); + } +}; + +export { listPostsByThreadId }; diff --git a/server/routes/forumRoutes.ts b/server/routes/forumRoutes.ts index 8095254..f7664c5 100644 --- a/server/routes/forumRoutes.ts +++ b/server/routes/forumRoutes.ts @@ -14,6 +14,9 @@ import { listThreadsByForumId, updateThread, } from "../controllers/threadController"; + +import { listPostsByThreadId } from "../controllers/postController"; + import { protect } from "../middleware/authMiddleware"; //TODO Add admin auth middleware const router = express.Router(); @@ -32,4 +35,7 @@ router.get("/:forumId/threads/:threadId", protect, getThreadById); router.put("/:forumId/threads/:threadId", protect, updateThread); //TODO Protect with admin auth router.delete("/:forumId/threads/:threadId", protect, deleteThread); //TODO Protect with admin auth +//Post Routes +router.get("/:forumId/threads/:threadId/posts", protect, listPostsByThreadId); + export default router; From 9145261dd55942067e686f77d92d050b7300f783 Mon Sep 17 00:00:00 2001 From: Sean Date: Fri, 5 Apr 2024 11:09:50 -0400 Subject: [PATCH 13/15] CHE-58 Added createPost controller and route --- server/controllers/postController.ts | 41 +++++++++++++++++++++++++++- server/routes/forumRoutes.ts | 3 +- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/server/controllers/postController.ts b/server/controllers/postController.ts index 238db6b..580d6db 100644 --- a/server/controllers/postController.ts +++ b/server/controllers/postController.ts @@ -1,5 +1,7 @@ import Post from "../models/postModel"; +import Thread from "../models/threadModel"; import { Request, Response, NextFunction } from "express"; +import { CustomRequest } from "../types/customRequest"; // ENDPOINT GET api/forums/:forumId/threads/:threadId/posts // PURPOSE Retrieve all posts from a specific thread @@ -26,4 +28,41 @@ const listPostsByThreadId = async ( } }; -export { listPostsByThreadId }; +// ENDPOINT POST api/forums/:forumId/threads/:threadId/posts +// PURPOSE Create a new post on thread +// ACCESS Private +const createPost = async ( + req: CustomRequest, + res: Response, + next: NextFunction +) => { + const { threadId } = req.params; + const { content } = req.body; + + if (!req.user || !req.user.id) { + return res.status(401).json({ message: "Not authenticated" }); + } + + try { + const threadExists = await Thread.findById(threadId); + if (!threadExists) { + return res.status(404).json({ message: "Thread not found" }); + } + + const newPost = await Post.create({ + thread: threadId, + user: req.user.id, + content, + }); + + res.status(201).json(newPost); + } catch (error) { + next({ + log: `Express error in createPost controller: ${error}`, + status: 500, + message: { err: "Server error creating post" }, + }); + } +}; + +export { listPostsByThreadId, createPost }; diff --git a/server/routes/forumRoutes.ts b/server/routes/forumRoutes.ts index f7664c5..4e274d2 100644 --- a/server/routes/forumRoutes.ts +++ b/server/routes/forumRoutes.ts @@ -15,7 +15,7 @@ import { updateThread, } from "../controllers/threadController"; -import { listPostsByThreadId } from "../controllers/postController"; +import { listPostsByThreadId, createPost } from "../controllers/postController"; import { protect } from "../middleware/authMiddleware"; //TODO Add admin auth middleware @@ -37,5 +37,6 @@ router.delete("/:forumId/threads/:threadId", protect, deleteThread); //TODO Prot //Post Routes router.get("/:forumId/threads/:threadId/posts", protect, listPostsByThreadId); +router.post("/:forumId/threads/:threadId/posts", protect, createPost); export default router; From 900ed1997790cd2de3448996100abd84291bdc11 Mon Sep 17 00:00:00 2001 From: Sean Date: Fri, 5 Apr 2024 11:39:59 -0400 Subject: [PATCH 14/15] CHE-58 Added updatePost controller and route --- server/controllers/postController.ts | 47 +++++++++++++++++++++++++++- server/routes/forumRoutes.ts | 7 ++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/server/controllers/postController.ts b/server/controllers/postController.ts index 580d6db..d1cf69b 100644 --- a/server/controllers/postController.ts +++ b/server/controllers/postController.ts @@ -65,4 +65,49 @@ const createPost = async ( } }; -export { listPostsByThreadId, createPost }; +// ENDPOINT PUT api/forums/:forumId/threads/:threadId/:postId +// PURPOSE Update an existing post +// ACCESS Private +const updatePost = async ( + req: CustomRequest, + res: Response, + next: NextFunction +) => { + const { postId } = req.params; + const { content } = req.body; + + try { + const postToCheck = await Post.findById(postId).populate("user"); + if (!postToCheck) { + return res.status(404).json({ message: "Post not found" }); + } + + if (!req.user || postToCheck.user._id.toString() !== req.user.id) { + return res + .status(403) + .json({ message: "Not authorized to update this post" }); + } + + const updatedPost = await Post.findByIdAndUpdate( + postId, + { $set: { content } }, + { new: true, runValidators: true } + ).populate("user", "firstName lastName"); + + if (!updatedPost) { + return res + .status(404) + .json({ message: "Unable to update post or post not found" }); + } + + res.status(200).json(updatedPost); + } catch (error) { + next({ + log: `Express error in updatePost controller: ${error}`, + status: 500, + message: { err: "Server error updating post" }, + }); + } +}; + +export { listPostsByThreadId, createPost, updatePost }; diff --git a/server/routes/forumRoutes.ts b/server/routes/forumRoutes.ts index 4e274d2..fad14b2 100644 --- a/server/routes/forumRoutes.ts +++ b/server/routes/forumRoutes.ts @@ -15,7 +15,11 @@ import { updateThread, } from "../controllers/threadController"; -import { listPostsByThreadId, createPost } from "../controllers/postController"; +import { + listPostsByThreadId, + createPost, + updatePost, +} from "../controllers/postController"; import { protect } from "../middleware/authMiddleware"; //TODO Add admin auth middleware @@ -38,5 +42,6 @@ router.delete("/:forumId/threads/:threadId", protect, deleteThread); //TODO Prot //Post Routes router.get("/:forumId/threads/:threadId/posts", protect, listPostsByThreadId); router.post("/:forumId/threads/:threadId/posts", protect, createPost); +router.put("/:forumId/threads/:threadId/posts/:postId", protect, updatePost); export default router; From 9567e0b50f28ff4e1c3b20f01a6f13f75c294ce6 Mon Sep 17 00:00:00 2001 From: Sean Date: Fri, 5 Apr 2024 12:00:09 -0400 Subject: [PATCH 15/15] CHE-58 Added deletePost controller and route --- server/controllers/postController.ts | 43 +++++++++++++++++++++++++++- server/routes/forumRoutes.ts | 4 ++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/server/controllers/postController.ts b/server/controllers/postController.ts index d1cf69b..54084fc 100644 --- a/server/controllers/postController.ts +++ b/server/controllers/postController.ts @@ -110,4 +110,45 @@ const updatePost = async ( } }; -export { listPostsByThreadId, createPost, updatePost }; +// ENDPOINT DELETE api/forums/:forumId/threads/:threadId/:postId +// PURPOSE Delete an existing post +// ACCESS Private, Admin +const deletePost = async ( + req: CustomRequest, + res: Response, + next: NextFunction +) => { + const { postId } = req.params; + + try { + const postToCheck = await Post.findById(postId).populate("user"); + if (!postToCheck) { + return res.status(404).json({ message: "Post not found" }); + } + + //TODO Add admin rights to delete posts for Jimmy + if (!req.user || postToCheck.user._id.toString() !== req.user.id) { + return res + .status(403) + .json({ message: "Not authorized to delete this post" }); + } + + const deletedPost = await Post.findByIdAndDelete(postId); + + if (!deletedPost) { + return res + .status(404) + .json({ message: "Post not found or already deleted" }); + } + + res.status(200).json({ message: "Post deleted successfully" }); + } catch (error) { + next({ + log: `Express error in deletePost controller: ${error}`, + status: 500, + message: { err: "Server error deleting post" }, + }); + } +}; + +export { listPostsByThreadId, createPost, updatePost, deletePost }; diff --git a/server/routes/forumRoutes.ts b/server/routes/forumRoutes.ts index fad14b2..6d9ab3b 100644 --- a/server/routes/forumRoutes.ts +++ b/server/routes/forumRoutes.ts @@ -19,6 +19,7 @@ import { listPostsByThreadId, createPost, updatePost, + deletePost, } from "../controllers/postController"; import { protect } from "../middleware/authMiddleware"; //TODO Add admin auth middleware @@ -36,12 +37,13 @@ router.delete("/:forumId", protect, deleteForum); //TODO Protect with admin auth router.post("/:forumId/threads", protect, createThread); router.get("/:forumId/threads", protect, listThreadsByForumId); router.get("/:forumId/threads/:threadId", protect, getThreadById); -router.put("/:forumId/threads/:threadId", protect, updateThread); //TODO Protect with admin auth +router.put("/:forumId/threads/:threadId", protect, updateThread); router.delete("/:forumId/threads/:threadId", protect, deleteThread); //TODO Protect with admin auth //Post Routes router.get("/:forumId/threads/:threadId/posts", protect, listPostsByThreadId); router.post("/:forumId/threads/:threadId/posts", protect, createPost); router.put("/:forumId/threads/:threadId/posts/:postId", protect, updatePost); +router.delete("/:forumId/threads/:threadId/posts/:postId", protect, deletePost); //TODO Protect with admin auth export default router;