From 157e88bcce9f814d2b38ab5e3a9acbdd2b264a66 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Mon, 8 Apr 2024 16:29:38 +0200 Subject: [PATCH 1/3] Removal of posts_ids and comments_ids from user --- seeds/UserSeed.js | 10 +-- src/controller/userInfo.js | 145 +++++++++++++++++++++++-------------- src/db/models/User.js | 15 +--- src/routers/users.js | 23 +++--- src/services/users.js | 14 ++++ 5 files changed, 119 insertions(+), 88 deletions(-) diff --git a/seeds/UserSeed.js b/seeds/UserSeed.js index 2884e2e..24ee40c 100644 --- a/seeds/UserSeed.js +++ b/seeds/UserSeed.js @@ -123,8 +123,7 @@ async function generateRandomUsers() { chat_request_email: true, unsubscribe_from_all_emails: false, }, - posts_ids: [], - comments_ids: [], + followed_posts_ids: [], saved_posts_ids: [], hidden_and_reported_posts_ids: [], @@ -150,13 +149,6 @@ async function generateRandomUsers() { const salt = await bcrypt.genSalt(10); fakeUser.password = await bcrypt.hash(fakeUser.password, salt); - fakeUser.token = jwt.sign({ _id: i + 1 }, JWT_SECRET, { - expiresIn: "1d", - }); - fakeUser.refreshToken = jwt.sign({ _id: i + 1 }, JWT_SECRET, { - expiresIn: "8d", - }); - users.push(fakeUser); } diff --git a/src/controller/userInfo.js b/src/controller/userInfo.js index 01a3bf4..c8d01c4 100644 --- a/src/controller/userInfo.js +++ b/src/controller/userInfo.js @@ -6,6 +6,8 @@ import { getPostsHelper, getCommunitiesHelper, getModeratedCommunitiesHelper, + getUserPostsHelper, + getUserCommentsHelper, } from "../services/users.js"; export async function getFollowers(request) { @@ -79,34 +81,19 @@ export async function getFollowingCount(request) { }; } -export async function getPosts(request, postType) { +export async function getUserPosts(request, postType) { try { - let user = null; - if (postType !== "posts_ids") { - const { - success, - err, - status, - user: authenticatedUser, - msg, - } = await verifyAuthToken(request); - if (!authenticatedUser) { - return { success, err, status, user: authenticatedUser, msg }; - } - user = authenticatedUser; - } else { - const { username } = request.params; - user = await User.findOne({ username }); - if (!user) { - return { - success: false, - err: "No user found with username", - status: 404, - msg: "User not found", - }; - } + const { username } = request.params; + const user = await User.findOne({ username }); + if (!user) { + return { + success: false, + err: "No user found with username", + status: 404, + msg: "User not found", + }; } - const posts = await getPostsHelper(user, postType); + const posts = await getUserPostsHelper(user, postType); return { success: true, status: 200, @@ -124,34 +111,52 @@ export async function getPosts(request, postType) { } } -export async function getComments(request, commentType) { +export async function getPosts(request) { + let user = null; try { - let user = null; - if (commentType !== "comments_ids") { - const { - success, - err, - status, - user: authenticatedUser, - msg, - } = await verifyAuthToken(request); - if (!authenticatedUser) { - return { success, err, status, user: authenticatedUser, msg }; - } - user = authenticatedUser; - } else { - const { username } = request.params; - user = await User.findOne({ username }); - if (!user) { - return { - success: false, - err: "No user found with username", - status: 404, - msg: "User not found", - }; - } + const { + success, + err, + status, + user: authenticatedUser, + msg, + } = await verifyAuthToken(request); + if (!authenticatedUser) { + return { success, err, status, user: authenticatedUser, msg }; + } + user = authenticatedUser; + const posts = await getPostsHelper(user); + return { + success: true, + status: 200, + posts, + msg: "Posts retrieved successfully.", + }; + } catch (error) { + console.error("Error:", error); + return { + success: false, + status: 500, + err: "Internal Server Error", + msg: "An error occurred while retrieving posts.", + }; + } +} + +export async function getUserComments(request) { + try { + const { username } = request.params; + const user = await User.findOne({ username }); + if (!user) { + return { + success: false, + err: "No user found with username", + status: 404, + msg: "User not found", + }; } - const comments = await getCommentsHelper(user, commentType); + + const comments = await getUserCommentsHelper(user); return { success: true, status: 200, @@ -169,6 +174,38 @@ export async function getComments(request, commentType) { } } +export async function getComments(request) { + let user = null; + try { + const { + success, + err, + status, + user: authenticatedUser, + msg, + } = await verifyAuthToken(request); + if (!authenticatedUser) { + return { success, err, status, user: authenticatedUser, msg }; + } + user = authenticatedUser; + const comments = await getCommentsHelper(user); + return { + success: true, + status: 200, + comments, + msg: "Comments retrieved successfully.", + }; + } catch (error) { + console.error("Error:", error); + return { + success: false, + status: 500, + err: "Internal Server Error", + msg: "An error occurred while retrieving posts.", + }; + } +} + export async function getOverview(request) { try { const { username } = request.params; @@ -181,8 +218,8 @@ export async function getOverview(request) { msg: "User not found", }; } - const posts = await getPostsHelper(user, "posts_ids"); - const comments = await getCommentsHelper(user, "comments_ids"); + const posts = await getUserPostsHelper(user); + const comments = await getUserCommentsHelper(user); return { success: true, diff --git a/src/db/models/User.js b/src/db/models/User.js index 554f865..a0730a0 100644 --- a/src/db/models/User.js +++ b/src/db/models/User.js @@ -282,20 +282,7 @@ const userSchema = new mongoose.Schema({ default: false, }, }, - posts_ids: { - type: Array, - items: { - type: mongoose.Schema.Types.ObjectId, - ref: "Post", - }, - }, - comments_ids: { - type: Array, - items: { - type: mongoose.Schema.Types.ObjectId, - ref: "Comment", - }, - }, + followed_posts_ids: { type: Array, items: { diff --git a/src/routers/users.js b/src/routers/users.js index eb6d6cf..1ddf1dc 100644 --- a/src/routers/users.js +++ b/src/routers/users.js @@ -38,6 +38,8 @@ import { getAbout, getComments, getCommunities, + getUserPosts, + getUserComments, } from "../controller/userInfo.js"; import { @@ -468,7 +470,7 @@ usersRouter.get("/users/:username/about", async (req, res) => { } }); -usersRouter.get("/users/:username/overview", async (req, res) => { +usersRouter.get("/users/overview/:username", async (req, res) => { try { const { success, err, status, overview, msg } = await getOverview(req); if (!success) { @@ -827,9 +829,9 @@ usersRouter.post("/users/leave-community", async (req, res) => { } }); -usersRouter.get("/users/:username/posts", async (req, res) => { +usersRouter.get("/users/posts/:username", async (req, res) => { try { - const result = await getPosts(req, "posts_ids"); + const result = await getUserPosts(req); res.status(result.status).json(result); } catch (error) { console.error("Error:", error); @@ -897,9 +899,9 @@ usersRouter.get("/users/hidden-and-reported-posts", async (req, res) => { } }); -usersRouter.get("/users/:username/comments", async (req, res) => { +usersRouter.get("/users/comments/:username", async (req, res) => { try { - const result = await getComments(req, "comments_ids"); + const result = await getUserComments(req); res.status(result.status).json(result); } catch (error) { console.error("Error:", error); @@ -913,12 +915,11 @@ usersRouter.get("/users/:username/comments", async (req, res) => { usersRouter.get("/users/saved-posts-and-comments", async (req, res) => { try { - const result = await getOverview( - req, - "saved_posts_ids", - "saved_comments_ids" - ); - res.status(result.status).json(result); + const rposts = await getPosts(req, "saved_posts_ids"); + const rcomments = await getComments(req, "saved_comments_ids"); + const result = { posts: rposts.posts, comments: rcomments.comments }; + + res.status(rposts.status).json(result); } catch (error) { console.error("Error:", error); res.status(500).json({ diff --git a/src/services/users.js b/src/services/users.js index f5af2aa..6acfaf1 100644 --- a/src/services/users.js +++ b/src/services/users.js @@ -37,6 +37,7 @@ export async function followUserHelper(user1, user2, follow = true) { } } +//Posts saved in user arrays export async function getPostsHelper(user, postsType) { const posts = await Post.find({ _id: { $in: user[postsType] } }).exec(); @@ -44,6 +45,13 @@ export async function getPostsHelper(user, postsType) { return filteredPosts; } +//Posts written by certain user +export async function getUserPostsHelper(user) { + const posts = await Post.find({ user_id: user._id }).exec(); + + return posts; +} + export async function getCommentsHelper(user, commentsType) { const comments = await Comment.find({ _id: { $in: user[commentsType] }, @@ -53,6 +61,12 @@ export async function getCommentsHelper(user, commentsType) { return filteredComments; } +export async function getUserCommentsHelper(user) { + const comments = await Comment.find({ user_id: user._id }).exec(); + + return comments; +} + export async function getCommunitiesHelper(user) { const communities = await Promise.all( user.communities.map(async (userCommunity) => { From d422ef1025f72074068ea62bb3c17a7f07463c79 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Tue, 9 Apr 2024 01:14:56 +0200 Subject: [PATCH 2/3] Posts Listing --- src/controller/postListing.js | 52 +++++++++++++ src/index.js | 3 +- src/routers/lisitng.js | 134 ++++++++++++++++++++++++++++++++++ src/services/lisitngs.js | 63 ++++++++++++++++ src/utils/lisitng.js | 34 +++++++++ 5 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 src/controller/postListing.js create mode 100644 src/routers/lisitng.js create mode 100644 src/services/lisitngs.js create mode 100644 src/utils/lisitng.js diff --git a/src/controller/postListing.js b/src/controller/postListing.js new file mode 100644 index 0000000..1aaf5ce --- /dev/null +++ b/src/controller/postListing.js @@ -0,0 +1,52 @@ +import { Post } from "../db/models/Post.js"; +import { verifyAuthToken } from "./userAuth.js"; +import { User } from "../db/models/User.js"; +import { getSortCriteria } from "../utils/lisitng.js"; +import { getPostsHelper } from "../services/lisitngs.js"; + +export async function getPostsPaginated( + request, + pageNumber = 1, + pageSize = 10, + sortBy +) { + try { + let user = null; + + // Check if request has Authorization header + if (request.headers.authorization) { + const { + success, + err, + status, + user: authenticatedUser, + msg, + } = await verifyAuthToken(request); + if (!authenticatedUser) { + return { success, err, status, user: authenticatedUser, msg }; + } + user = authenticatedUser; + } + + // Calculate the offset based on pageNumber and pageSize + const offset = (pageNumber - 1) * pageSize; + + // Fetch posts with pagination and sorting + const { posts } = await getPostsHelper(user, offset, pageSize, sortBy); + + return { + success: true, + status: 200, + posts, + msg: "Posts retrieved successfully.", + }; + } catch (error) { + console.error("Error:", error); + return { + success: false, + status: 500, + err: "Internal Server Error", + msg: "An error occurred while retrieving posts.", + }; + } +} diff --git a/src/index.js b/src/index.js index c12ab04..aea3c35 100644 --- a/src/index.js +++ b/src/index.js @@ -2,6 +2,7 @@ import express from "express"; import dotenv from "dotenv"; import { usersRouter } from "./routers/users.js"; import { communityRouter } from "./routers/communities.js"; +import { listingPostsRouter } from "./routers/lisitng.js"; dotenv.config(); import { connect_to_db } from "./db/mongoose.js"; @@ -27,7 +28,7 @@ app.listen(port, () => { console.log("Server is Up"); }); -app.use([usersRouter, communityRouter]); +app.use([usersRouter, communityRouter, listingPostsRouter]); // Error handling middleware app.use((err, req, res, next) => { diff --git a/src/routers/lisitng.js b/src/routers/lisitng.js new file mode 100644 index 0000000..2107199 --- /dev/null +++ b/src/routers/lisitng.js @@ -0,0 +1,134 @@ +import express from "express"; +import { User } from "../db/models/User.js"; //if error put .js +import { Post } from "../db/models/Post.js"; +import dotenv from "dotenv"; +import axios from "axios"; +import jwt from "jsonwebtoken"; + +import { getPostsPaginated } from "../controller/postListing.js"; +export const listingPostsRouter = express.Router(); + +listingPostsRouter.get("/listing/posts/best", async (req, res) => { + try { + const { page = 1, pageSize = 10 } = req.query; + const pageNumber = parseInt(page); + const pageSizeNumber = parseInt(pageSize); + let sortBy = "best"; + + const result = await getPostsPaginated( + req, + pageNumber, + pageSizeNumber, + sortBy + ); + + res.status(result.status).json(result); + } catch (error) { + console.error("Error:", error); + res.status(500).json({ + success: false, + err: "Internal Server Error", + msg: "An error occurred while processing the request.", + }); + } +}); + +listingPostsRouter.get("/listing/posts/random", async (req, res) => { + try { + const { page = 1, pageSize = 10 } = req.query; + const pageNumber = parseInt(page); + const pageSizeNumber = parseInt(pageSize); + let sortBy = "random"; + + const result = await getPostsPaginated( + req, + pageNumber, + pageSizeNumber, + sortBy + ); + + res.status(result.status).json(result); + } catch (error) { + console.error("Error:", error); + res.status(500).json({ + success: false, + err: "Internal Server Error", + msg: "An error occurred while processing the request.", + }); + } +}); + +listingPostsRouter.get("/listing/posts/hot", async (req, res) => { + try { + const { page = 1, pageSize = 10 } = req.query; + const pageNumber = parseInt(page); + const pageSizeNumber = parseInt(pageSize); + let sortBy = "hot"; + + const result = await getPostsPaginated( + req, + pageNumber, + pageSizeNumber, + sortBy + ); + + res.status(result.status).json(result); + } catch (error) { + console.error("Error:", error); + res.status(500).json({ + success: false, + err: "Internal Server Error", + msg: "An error occurred while processing the request.", + }); + } +}); + +listingPostsRouter.get("/listing/posts/new", async (req, res) => { + try { + const { page = 1, pageSize = 10 } = req.query; + const pageNumber = parseInt(page); + const pageSizeNumber = parseInt(pageSize); + let sortBy = "new"; + + const result = await getPostsPaginated( + req, + pageNumber, + pageSizeNumber, + sortBy + ); + + res.status(result.status).json(result); + } catch (error) { + console.error("Error:", error); + res.status(500).json({ + success: false, + err: "Internal Server Error", + msg: "An error occurred while processing the request.", + }); + } +}); + +listingPostsRouter.get("/listing/posts/top", async (req, res) => { + try { + const { page = 1, pageSize = 10 } = req.query; + const pageNumber = parseInt(page); + const pageSizeNumber = parseInt(pageSize); + let sortBy = "top"; + + const result = await getPostsPaginated( + req, + pageNumber, + pageSizeNumber, + sortBy + ); + + res.status(result.status).json(result); + } catch (error) { + console.error("Error:", error); + res.status(500).json({ + success: false, + err: "Internal Server Error", + msg: "An error occurred while processing the request.", + }); + } +}); diff --git a/src/services/lisitngs.js b/src/services/lisitngs.js new file mode 100644 index 0000000..a2468fe --- /dev/null +++ b/src/services/lisitngs.js @@ -0,0 +1,63 @@ +import { Post } from "../db/models/Post.js"; +import { getSortCriteria } from "../utils/lisitng.js"; + +export async function paginateFollowingPosts(followedUsers) { + const userPosts = await Post.find({ user_id: { $in: followedUsers } }) + .sort(sortCriteria) + .skip(offset) + .limit(pageSize) + .exec(); + + posts = [...userPosts]; + if (posts.length < pageSize) { + const remainingPosts = pageSize - posts.length; + + // Fetch random posts excluding the ones already fetched + const randomPosts = await Post.aggregate([ + { $match: { user_id: { $nin: followedUsers } } }, // Exclude followed users' posts + { $sample: { size: remainingPosts } }, // Randomly select remaining posts + { $sort: sortCriteria }, // Sort the random posts based on the same criteria + ]); + + posts.push(...randomPosts); + } + return posts; +} + +export async function getPostsHelper(currentUser, offset, pageSize, sortBy) { + try { + let sortCriteria = getSortCriteria(sortBy); + + // Apply sorting based on the sortBy parameter + let posts = []; + + if (currentUser) { + let followedUsers = currentUser.following_ids; // Assuming following_ids contains user IDs of followed users + + // Check if the user follows anyone + if (followedUsers.length > 0) { + // Fetch posts from followed users + posts = paginateFollowingPosts(followedUsers); + } else { + // If user doesn't follow anyone, fetch random posts + posts = await Post.aggregate([ + { $sample: { size: pageSize } }, // Randomly select posts + { $sort: sortCriteria }, // Sort the random posts based on the same criteria + ]); + } + } + // If no authenticated user or user doesn't follow anyone, fetch random posts + if (posts.length === 0) { + posts = await Post.aggregate([ + { $sample: { size: pageSize } }, // Randomly select posts + { $sort: sortCriteria }, // Sort the random posts based on the same criteria + ]); + } + return { + posts, + }; + } catch (error) { + console.error("Error fetching posts:", error); + throw error; + } +} diff --git a/src/utils/lisitng.js b/src/utils/lisitng.js new file mode 100644 index 0000000..1f3c589 --- /dev/null +++ b/src/utils/lisitng.js @@ -0,0 +1,34 @@ +export function getSortCriteria(sortBy) { + let sortCriteria; + + switch (sortBy) { + case "best": + sortCriteria = { + created_at: -1, + upvotes_count: -1, + comments_count: -1, + shares_count: -1, + }; + break; + case "hot": + sortCriteria = { + created_at: -1, + upvotes_count: -1, + comments_count: -1, + }; + break; + case "top": + sortCriteria = { + created_at: -1, + upvotes_count: -1, + }; + break; + case "new": + sortCriteria = { views_count: -1, created_at: -1 }; + break; + default: + sortCriteria = { created_at: -1 }; + } + + return sortCriteria; +} From 2c4cc2f597c9f35ba8f5a8e7867c88a9275900ce Mon Sep 17 00:00:00 2001 From: Ahmed Date: Tue, 9 Apr 2024 01:32:31 +0200 Subject: [PATCH 3/3] Posts Listing Updated --- src/services/lisitngs.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/lisitngs.js b/src/services/lisitngs.js index a2468fe..807cc2d 100644 --- a/src/services/lisitngs.js +++ b/src/services/lisitngs.js @@ -1,7 +1,7 @@ import { Post } from "../db/models/Post.js"; import { getSortCriteria } from "../utils/lisitng.js"; -export async function paginateFollowingPosts(followedUsers) { +export async function paginateFollowingPosts(followedUsers, offset, pageSize) { const userPosts = await Post.find({ user_id: { $in: followedUsers } }) .sort(sortCriteria) .skip(offset) @@ -37,7 +37,7 @@ export async function getPostsHelper(currentUser, offset, pageSize, sortBy) { // Check if the user follows anyone if (followedUsers.length > 0) { // Fetch posts from followed users - posts = paginateFollowingPosts(followedUsers); + posts = paginateFollowingPosts(followedUsers, offset, pageSize); } else { // If user doesn't follow anyone, fetch random posts posts = await Post.aggregate([