diff --git a/backend/src/controllers/postController.ts b/backend/src/controllers/postController.ts index b0a7a40..7528edb 100644 --- a/backend/src/controllers/postController.ts +++ b/backend/src/controllers/postController.ts @@ -136,6 +136,13 @@ const fetchPosts = asyncHandler(async (req: Request, res: Response) => { const postsPerPage = 4; const offset = (pageNumber - 1) * postsPerPage; + const cacheKey = `posts:${collegeId || "all"}:page:${pageNumber}`; + + const cachedResults = await getCachedData(cacheKey); + if (cachedResults) { + return res.status(200).json(JSON.parse(cachedResults)); + } + const posts = await prisma.post.findMany({ where: collegeId ? { college_id: collegeId } : {}, orderBy: { @@ -170,6 +177,9 @@ const fetchPosts = asyncHandler(async (req: Request, res: Response) => { const totalPosts = await prisma.post.count(); const isOver = offset + postsPerPage >= totalPosts; + const result = { posts, isOver }; + await setCachedData(cacheKey, JSON.stringify(result), 600); + return res.status(200).json({ posts, isOver }); }); diff --git a/backend/src/middleware/rateLimit.ts b/backend/src/middleware/rateLimit.ts new file mode 100644 index 0000000..10a0635 --- /dev/null +++ b/backend/src/middleware/rateLimit.ts @@ -0,0 +1,40 @@ +import { Request, Response, NextFunction } from "express"; +import asyncHandler from "express-async-handler"; +import Redis from "ioredis"; + +const redisUrl = process.env.REDIS_URL; +if (!redisUrl) { + throw new Error("REDIS_URL is not defined"); +} +const redis = new Redis(redisUrl); +const MAX_REQUESTS = 100; +const WINDOW_SIZE_IN_SECONDS = 60; + +const rateLimiter = asyncHandler( + //@ts-ignore + async (req: Request, res: Response, next: NextFunction) => { + //@ts-ignore + const clientIp = req.user.user_id; + // console.log(clientIp); + const key = `rate-limit:${clientIp}`; + const requestCount = await redis.get(key); + + if (requestCount && parseInt(requestCount) >= MAX_REQUESTS) { + return res + .status(429) + .json({ message: "Too many requests, please try again later." }); + } + + const ttl = await redis.ttl(key); + + if (ttl < 0) { + await redis.set(key, 1, "EX", WINDOW_SIZE_IN_SECONDS); + } else { + await redis.incr(key); + } + + next(); + } +); + +export default rateLimiter; diff --git a/backend/src/routes/postsRoutes.ts b/backend/src/routes/postsRoutes.ts index 1e481ce..0fe6bf4 100644 --- a/backend/src/routes/postsRoutes.ts +++ b/backend/src/routes/postsRoutes.ts @@ -14,20 +14,21 @@ import { deletePost, } from "../controllers/postController"; import checkAuth from "../middleware/checkAuth"; +import rateLimiter from "../middleware/rateLimit"; const postsRoutes = express.Router(); -postsRoutes.get("/communities", checkAuth, getCommunities); -postsRoutes.post("/create", checkAuth, createPost); -postsRoutes.post("/fetch", checkAuth, fetchPosts); -postsRoutes.post("/like", checkAuth, likePost); -postsRoutes.get("/fetch/:id", checkAuth, fetchSinglePost); -postsRoutes.post("/comment", checkAuth, createComment); -postsRoutes.post("/liked", checkAuth, postLiked); -postsRoutes.post("/unlike", checkAuth, unlikePost); -postsRoutes.post("/search", checkAuth, searchPosts); -postsRoutes.get("/allcommunities", checkAuth, getAllCommunities); -postsRoutes.post("/deletecomment", checkAuth, deleteComment); -postsRoutes.post("/deletepost", checkAuth, deletePost); +postsRoutes.get("/communities", checkAuth, rateLimiter, getCommunities); +postsRoutes.post("/create", checkAuth, rateLimiter, createPost); +postsRoutes.post("/fetch", checkAuth, rateLimiter, fetchPosts); +postsRoutes.post("/like", checkAuth, rateLimiter, likePost); +postsRoutes.get("/fetch/:id", checkAuth, rateLimiter, fetchSinglePost); +postsRoutes.post("/comment", checkAuth, rateLimiter, createComment); +postsRoutes.post("/liked", checkAuth, rateLimiter, postLiked); +postsRoutes.post("/unlike", checkAuth, rateLimiter, unlikePost); +postsRoutes.post("/search", checkAuth, rateLimiter, searchPosts); +postsRoutes.get("/allcommunities", checkAuth, rateLimiter, getAllCommunities); +postsRoutes.post("/deletecomment", checkAuth, rateLimiter, deleteComment); +postsRoutes.post("/deletepost", checkAuth, rateLimiter, deletePost); export default postsRoutes;