Initial commit
This commit is contained in:
@@ -6,11 +6,22 @@ const jwt = require("jsonwebtoken");
|
||||
const moment = require("moment-jalaali");
|
||||
const NotificationModel = require("../../../models/NotificationModel");
|
||||
const CommentModel = require("../../../models/CommentModel");
|
||||
const {
|
||||
hasCreatorRatedUser,
|
||||
resolveRatingForComment,
|
||||
recalculateUserRating,
|
||||
} = require("../../../utils/commentRating");
|
||||
const { createCommentNotification } = require("../../../utils/commentNotification");
|
||||
const { ProvinceModel, CityModel } = require("../../../models/StateCity");
|
||||
const OfferModel = require("../../../models/OfferModel");
|
||||
const PostModel = require("../../../models/PostModel");
|
||||
const ProjectModel = require("../../../models/ProjectModel");
|
||||
const LicenseModel = require("../../../models/license")
|
||||
const {
|
||||
viewerIsBlockedBy,
|
||||
blockedProfilePayload,
|
||||
getBlockerUserIds,
|
||||
} = require("../../../utils/blockVisibility")
|
||||
|
||||
|
||||
const getAllLicenses = async (req, res, next) => {
|
||||
@@ -775,9 +786,19 @@ const getPostsWeb = async (req, res, next) => {
|
||||
.lean();
|
||||
const userIds = [...new Set(explorePosts.map((p) => String(p.user_id)))];
|
||||
const usersMap = await UserModel.find({ _id: { $in: userIds } })
|
||||
.select('_id user_name first_name last_name expertise province city user_level')
|
||||
.select('_id user_name first_name last_name expertise province city user_level show_location blocked_users')
|
||||
.lean();
|
||||
const usersById = Object.fromEntries(usersMap.map((u) => [String(u._id), u]));
|
||||
|
||||
if (decodedToken?.id) {
|
||||
const blockerIds = new Set(
|
||||
(await getBlockerUserIds(UserModel, decodedToken.id)).map(String)
|
||||
);
|
||||
explorePosts = explorePosts.filter(
|
||||
(post) => !blockerIds.has(String(post.user_id))
|
||||
);
|
||||
}
|
||||
|
||||
explorePosts = explorePosts.map((post) => {
|
||||
const user = usersById[String(post.user_id)] || {};
|
||||
return {
|
||||
@@ -786,8 +807,9 @@ const getPostsWeb = async (req, res, next) => {
|
||||
first_name: user.first_name || '',
|
||||
last_name: user.last_name || '',
|
||||
expertise: user.expertise || '',
|
||||
province: user.province || {},
|
||||
city: user.city || {},
|
||||
province: user.show_location ? user.province || {} : {},
|
||||
city: user.show_location ? user.city || {} : {},
|
||||
show_location: user.show_location,
|
||||
user_level: user.user_level || '',
|
||||
likesCount: post.likes ? post.likes.length : 0,
|
||||
is_liked: false,
|
||||
@@ -817,8 +839,15 @@ const getPostsWeb = async (req, res, next) => {
|
||||
if (userLevel) userFilter.user_level = userLevel;
|
||||
if (_id) userFilter._id = _id;
|
||||
|
||||
if (_id && decodedToken?.id) {
|
||||
const profileOwner = await UserModel.findById(_id).select('blocked_users');
|
||||
if (viewerIsBlockedBy(profileOwner, decodedToken.id)) {
|
||||
return res.status(200).json({ posts: [], totalPages: 0, totalItems: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
const users = await UserModel.find(userFilter)
|
||||
.select("_id user_name first_name last_name expertise province city user_level")
|
||||
.select("_id user_name first_name last_name expertise province city user_level show_location")
|
||||
.lean();
|
||||
|
||||
if (!users || users.length === 0) {
|
||||
@@ -836,6 +865,16 @@ const getPostsWeb = async (req, res, next) => {
|
||||
return res.status(200).json({ posts: [], totalPages: 0, totalItems: 0 });
|
||||
}
|
||||
|
||||
if (decodedToken?.id) {
|
||||
const blockerIds = new Set(
|
||||
(await getBlockerUserIds(UserModel, decodedToken.id)).map(String)
|
||||
);
|
||||
posts = posts.filter((post) => !blockerIds.has(String(post.user_id)));
|
||||
if (!posts.length) {
|
||||
return res.status(200).json({ posts: [], totalPages: 0, totalItems: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
let likeFilter = {};
|
||||
if (decodedToken) likeFilter.userId = decodedToken.id;
|
||||
const likes = decodedToken
|
||||
@@ -860,8 +899,9 @@ const getPostsWeb = async (req, res, next) => {
|
||||
first_name: user?.first_name || "",
|
||||
last_name: user?.last_name || "",
|
||||
expertise: user?.expertise || "",
|
||||
province: user?.province || {},
|
||||
city: user?.city || {},
|
||||
province: user?.show_location ? user?.province || {} : {},
|
||||
city: user?.show_location ? user?.city || {} : {},
|
||||
show_location: user?.show_location,
|
||||
user_level: user?.user_level || "",
|
||||
likesCount: post.likes ? post.likes.length : 0,
|
||||
is_liked: !!likesMap[post._id.toString()],
|
||||
@@ -918,6 +958,10 @@ const getSingleUser = async (req, res, next) => {
|
||||
const blockedByCurrentUser = user.blocked_by.includes(userReqId);
|
||||
const blockedYou = user.blocked_users.includes(userReqId);
|
||||
|
||||
if (blockedYou) {
|
||||
return res.status(200).json({ user: blockedProfilePayload(user) });
|
||||
}
|
||||
|
||||
// Allow chat
|
||||
const offerExists = await OfferModel.exists({
|
||||
$or: [
|
||||
@@ -937,6 +981,7 @@ const getSingleUser = async (req, res, next) => {
|
||||
user_name: user.user_name,
|
||||
show_location: user.show_location,
|
||||
is_verified: user.is_verified,
|
||||
is_Register: user.is_Register,
|
||||
user_score: user.user_score,
|
||||
rate: user.rate,
|
||||
bio: user.bio,
|
||||
@@ -948,8 +993,8 @@ const getSingleUser = async (req, res, next) => {
|
||||
profile_image: user.profile_image,
|
||||
lat: user.show_location ? user.lat : null,
|
||||
lng: user.show_location ? user.lng : null,
|
||||
province: user.province,
|
||||
city: user.city,
|
||||
province: user.show_location ? user.province : null,
|
||||
city: user.show_location ? user.city : null,
|
||||
address: user.show_location ? user.address : null,
|
||||
is_blocked: blockedByCurrentUser,
|
||||
blocked_you: blockedYou,
|
||||
@@ -1016,6 +1061,11 @@ const getSingleUserWeb = async (req, res, next) => {
|
||||
receiver: user._id,
|
||||
});
|
||||
}
|
||||
|
||||
if (blockedYou && userReqId) {
|
||||
return res.status(200).json({ user: blockedProfilePayload(user) });
|
||||
}
|
||||
|
||||
// دریافت تعداد پستها و پروژهها
|
||||
const postsCount = await PostModel.countDocuments({
|
||||
user_id: user._id,
|
||||
@@ -1043,6 +1093,7 @@ const getSingleUserWeb = async (req, res, next) => {
|
||||
user_name: user.user_name,
|
||||
show_location: user.show_location,
|
||||
is_verified: user.is_verified,
|
||||
is_Register: user.is_Register,
|
||||
user_score: user.user_score,
|
||||
rate: user.rate,
|
||||
bio: user.bio,
|
||||
@@ -1054,8 +1105,8 @@ const getSingleUserWeb = async (req, res, next) => {
|
||||
profile_image: user.profile_image,
|
||||
lat: user.show_location ? user.lat : null,
|
||||
lng: user.show_location ? user.lng : null,
|
||||
province: user.province,
|
||||
city: user.city,
|
||||
province: user.show_location ? user.province : null,
|
||||
city: user.show_location ? user.city : null,
|
||||
address: user.show_location ? user.address : null,
|
||||
is_blocked: blockedByCurrentUser,
|
||||
blocked_you: blockedYou,
|
||||
@@ -1099,6 +1150,11 @@ const getUserContactInfo = async (req, res, next) => {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
const {
|
||||
applyAutoBlockSuspension,
|
||||
clearAutoBlockSuspension
|
||||
} = require('../../../utils/blockSuspension')
|
||||
|
||||
const blockUser = async (req, res) => {
|
||||
try {
|
||||
const { user_to_block } = req.body;
|
||||
@@ -1107,17 +1163,30 @@ const blockUser = async (req, res) => {
|
||||
const decodedToken = jwt.verify(token, process.env.APP_SECRET);
|
||||
const userReqId = decodedToken.id;
|
||||
|
||||
const currentUser = await UserModel.findById(userReqId); // Req
|
||||
const userToBlock = await UserModel.findById(user_to_block); // To Block
|
||||
|
||||
userToBlock.blocked_by.push(currentUser);
|
||||
currentUser.blocked_users.push(userToBlock);
|
||||
|
||||
// بررسی تعداد بلاک ها و تنظیم block_status
|
||||
if (userToBlock.blocked_by.length >= 3) {
|
||||
userToBlock.block_status = true;
|
||||
if (userReqId === user_to_block) {
|
||||
return res.status(400).json({ message: "امکان بلاک خودتان وجود ندارد" });
|
||||
}
|
||||
|
||||
const currentUser = await UserModel.findById(userReqId);
|
||||
const userToBlock = await UserModel.findById(user_to_block);
|
||||
|
||||
if (!currentUser || !userToBlock) {
|
||||
return res.status(404).json({ message: "کاربر یافت نشد" });
|
||||
}
|
||||
|
||||
const alreadyBlocked = userToBlock.blocked_by.some((id) =>
|
||||
id.equals(currentUser._id)
|
||||
);
|
||||
|
||||
if (alreadyBlocked) {
|
||||
return res.status(400).json({ message: "این کاربر قبلاً بلاک شده است" });
|
||||
}
|
||||
|
||||
userToBlock.blocked_by.push(currentUser._id);
|
||||
currentUser.blocked_users.push(userToBlock._id);
|
||||
|
||||
applyAutoBlockSuspension(userToBlock);
|
||||
|
||||
await currentUser.save();
|
||||
await userToBlock.save();
|
||||
|
||||
@@ -1136,18 +1205,17 @@ const unblockUser = async (req, res) => {
|
||||
const decodedToken = jwt.verify(token, process.env.APP_SECRET);
|
||||
const userReqId = decodedToken.id;
|
||||
|
||||
const currentUser = await UserModel.findById(userReqId); // Req
|
||||
const userToUnblock = await UserModel.findById(user_to_unblock); // To Unblock
|
||||
const currentUser = await UserModel.findById(userReqId);
|
||||
const userToUnblock = await UserModel.findById(user_to_unblock);
|
||||
|
||||
if (!currentUser || !userToUnblock) {
|
||||
return res.status(404).json({ message: "کاربر یافت نشد" });
|
||||
}
|
||||
|
||||
// حذف کاربر از لیست blocked_by کاربر فعلی
|
||||
currentUser.blocked_users.pull(userToUnblock._id);
|
||||
// حذف کاربر از لیست blocked_users کاربر مورد نظر
|
||||
userToUnblock.blocked_by.pull(currentUser._id);
|
||||
|
||||
// بررسی تعداد بلاک ها و تنظیم block_status
|
||||
if (userToUnblock.blocked_by.length < 3) {
|
||||
userToUnblock.block_status = null;
|
||||
}
|
||||
clearAutoBlockSuspension(userToUnblock);
|
||||
|
||||
await currentUser.save();
|
||||
await userToUnblock.save();
|
||||
@@ -1163,6 +1231,7 @@ const getComments = async (req, res, next) => {
|
||||
try {
|
||||
const token = req.header("Authorization").split(" ")[1];
|
||||
if (!token) return res.status(401).send("Access Denied");
|
||||
const decodedToken = jwt.verify(token, process.env.APP_SECRET);
|
||||
const user_id = req.query.user_id;
|
||||
const post_id = req.query.post_id;
|
||||
|
||||
@@ -1192,27 +1261,53 @@ const getComments = async (req, res, next) => {
|
||||
|
||||
const comments = await CommentModel.paginate(filter, options);
|
||||
|
||||
const formattedComments = comments.docs.map((comment) => ({
|
||||
_id: comment._id,
|
||||
comment: comment.comment,
|
||||
rating: comment.rating,
|
||||
createdAt: moment(comment.createdAt).format("jYYYY-jMM-jDD HH:mm"),
|
||||
status: comment.status,
|
||||
user: {
|
||||
profile_image: comment.creator.profile_image,
|
||||
user_name: comment.creator.user_name,
|
||||
is_verified: comment.creator.is_verified,
|
||||
_id: comment.creator._id,
|
||||
},
|
||||
project: comment.project ? comment.project.project_name : null,
|
||||
post_id: comment.post ? comment.post.toString() : null,
|
||||
}));
|
||||
const earliestRatedComments = await CommentModel.find({
|
||||
user: user_id,
|
||||
rating: { $ne: null, $gt: 0 },
|
||||
})
|
||||
.select("creator _id createdAt")
|
||||
.sort({ createdAt: 1 });
|
||||
|
||||
const ratedCreatorCommentIds = new Map();
|
||||
for (const ratedComment of earliestRatedComments) {
|
||||
const creatorId = ratedComment.creator.toString();
|
||||
if (!ratedCreatorCommentIds.has(creatorId)) {
|
||||
ratedCreatorCommentIds.set(creatorId, ratedComment._id.toString());
|
||||
}
|
||||
}
|
||||
|
||||
const formattedComments = comments.docs.map((comment) => {
|
||||
const creatorId = comment.creator._id.toString();
|
||||
const isPrimaryRatingComment =
|
||||
comment.rating != null &&
|
||||
comment.rating > 0 &&
|
||||
ratedCreatorCommentIds.get(creatorId) === comment._id.toString();
|
||||
|
||||
return {
|
||||
_id: comment._id,
|
||||
comment: comment.comment,
|
||||
rating: isPrimaryRatingComment ? comment.rating : null,
|
||||
createdAt: moment(comment.createdAt).format("jYYYY-jMM-jDD HH:mm"),
|
||||
status: comment.status,
|
||||
user: {
|
||||
profile_image: comment.creator.profile_image,
|
||||
user_name: comment.creator.user_name,
|
||||
is_verified: comment.creator.is_verified,
|
||||
_id: comment.creator._id,
|
||||
},
|
||||
project: comment.project ? comment.project.project_name : null,
|
||||
post_id: comment.post ? comment.post.toString() : null,
|
||||
};
|
||||
});
|
||||
|
||||
const hasRated = await hasCreatorRatedUser(decodedToken.id, user_id);
|
||||
|
||||
res.status(200).json({
|
||||
comments: formattedComments,
|
||||
totalPages: comments.totalPages,
|
||||
totalItems: comments.totalDocs,
|
||||
currentPage: comments.page,
|
||||
has_rated: hasRated,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -1231,20 +1326,17 @@ const createUserComment = async (req, res, next) => {
|
||||
return res.status(400).send({ message: "All fields are required" });
|
||||
}
|
||||
|
||||
const existingRating = await CommentModel.findOne({
|
||||
user: user_id,
|
||||
creator: creatorId,
|
||||
comment_for: post_id ? "post" : "user",
|
||||
rating: { $exists: true, $ne: null }
|
||||
});
|
||||
const { ratingValue, isNewRating, requiresRating } =
|
||||
await resolveRatingForComment({
|
||||
creatorId,
|
||||
targetUserId: user_id,
|
||||
rate,
|
||||
});
|
||||
|
||||
let ratingValue = rate;
|
||||
if (existingRating) {
|
||||
ratingValue = existingRating.rating;
|
||||
} else {
|
||||
if (!rate || rate < 1 || rate > 5) {
|
||||
return res.status(400).send({ message: "Rating must be between 1 and 5" });
|
||||
}
|
||||
if (requiresRating) {
|
||||
return res
|
||||
.status(400)
|
||||
.send({ message: "Rating must be between 1 and 5" });
|
||||
}
|
||||
|
||||
const newComment = new CommentModel({
|
||||
@@ -1258,50 +1350,16 @@ const createUserComment = async (req, res, next) => {
|
||||
});
|
||||
await newComment.save();
|
||||
|
||||
const user = await UserModel.findById(user_id);
|
||||
|
||||
// محاسبه امتیاز جدید کاربر
|
||||
const comments = await CommentModel.find({
|
||||
user: user_id,
|
||||
comment_for: "user",
|
||||
});
|
||||
const totalRating = comments.reduce(
|
||||
(acc, comment) => acc + comment.rating,
|
||||
0
|
||||
);
|
||||
const totalComments = comments.length;
|
||||
const averageRating = totalRating / totalComments;
|
||||
|
||||
// تعیین سطح کاربر
|
||||
let userLevel;
|
||||
if (user.expertise === "مدل") {
|
||||
if (totalRating <= 50) userLevel = "تازه وارد";
|
||||
else if (totalRating <= 100) userLevel = "استاندارد";
|
||||
else if (totalRating <= 300) userLevel = "حرفهای";
|
||||
else userLevel = "استاد";
|
||||
} else if (["زیبایی", "عکاس"].includes(user.expertise)) {
|
||||
if (totalRating <= 50) userLevel = "تازه وارد";
|
||||
else if (totalRating <= 100) userLevel = "استاندارد";
|
||||
else if (totalRating <= 300) userLevel = "حرفهای";
|
||||
else userLevel = "استاد";
|
||||
if (isNewRating) {
|
||||
await recalculateUserRating(user_id);
|
||||
}
|
||||
|
||||
// بهروزرسانی اطلاعات کاربر
|
||||
user.user_score = totalRating;
|
||||
user.user_level = userLevel;
|
||||
user.rate = averageRating.toFixed(1);
|
||||
await user.save();
|
||||
|
||||
// ارسال نوتیفیکیشن
|
||||
const notification = new NotificationModel({
|
||||
user_id: user._id,
|
||||
type: "user_comment",
|
||||
title: "نظر جدید",
|
||||
description: `یک کاربر جدید برای شما نظر ثبت کرد:
|
||||
${comment}
|
||||
امتیاز: ${rate} از 5`,
|
||||
await createCommentNotification({
|
||||
ownerId: user_id,
|
||||
commenterId: creatorId,
|
||||
entityId: post_id || user_id,
|
||||
type: post_id ? "post_comment" : "profile_comment",
|
||||
});
|
||||
await notification.save();
|
||||
|
||||
res.status(201).json({
|
||||
message: "نظر با موفقیت ثبت شد و بعد از تایید، منتشر میشود.",
|
||||
|
||||
Reference in New Issue
Block a user