Compare commits
3 Commits
29d5830d54
...
64eb8c288f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64eb8c288f | ||
|
|
525565d685 | ||
|
|
abb6884dd0 |
17
.env.example
Normal file
@@ -0,0 +1,17 @@
|
||||
# کپی کنید به .env (بکاند)
|
||||
NODE_ENV=development
|
||||
APP_PORT=3002
|
||||
APP_URL=http://localhost:3002/api/v1
|
||||
APP_SITE=http://localhost:3000
|
||||
APP_SECRET=your-secret
|
||||
DATABASE_URL=mongodb://root:PASSWORD@localhost:27017/modstagram?authSource=admin
|
||||
SMS_SECRET=
|
||||
SMS_VERIFY_KEY=
|
||||
MERCHENT_CODE=
|
||||
ZARINPAL_MERCHANT_ID=
|
||||
|
||||
# روی سرور (production):
|
||||
# NODE_ENV=production
|
||||
# APP_PORT=3002
|
||||
# APP_URL=https://app.modstagram.ir/api/v1
|
||||
# APP_SITE=https://modstagram.com
|
||||
@@ -15,6 +15,13 @@ const CourseComment = require("../../../models/CourseComentModel");
|
||||
const IsPaymentCourse = require("../../../models/IsPaymentCourseModel");
|
||||
const Tax = require("../../../models/TaxModel");
|
||||
const AcademyCategoryModel = require('../../../models/AcademyCategoryModel');
|
||||
const {
|
||||
createLikeNotification,
|
||||
resolveCourseOwnerId
|
||||
} = require('../../../utils/likeNotification');
|
||||
const {
|
||||
createCommentNotification
|
||||
} = require('../../../utils/commentNotification');
|
||||
|
||||
const onServerRestart = async () => {
|
||||
const CoursePayment = await CoursePaymentModel.findOne({
|
||||
@@ -1178,6 +1185,14 @@ const likeCourseContent = async (req, res, next) => {
|
||||
createdAt: new Date(),
|
||||
});
|
||||
|
||||
const courseOwnerId = await resolveCourseOwnerId(course);
|
||||
await createLikeNotification({
|
||||
ownerId: courseOwnerId,
|
||||
likerId: userId,
|
||||
entityId: course._id,
|
||||
type: 'academy_like'
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "دوره با موفقیت لایک شد",
|
||||
@@ -1394,6 +1409,14 @@ const createComment = async (req, res) => {
|
||||
newComment._id
|
||||
).populate("user_id", "name username");
|
||||
|
||||
const courseOwnerId = await resolveCourseOwnerId(course);
|
||||
await createCommentNotification({
|
||||
ownerId: courseOwnerId,
|
||||
commenterId: userId,
|
||||
entityId: course._id,
|
||||
type: 'academy_comment'
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: "کامنت با موفقیت ثبت شد",
|
||||
|
||||
@@ -28,6 +28,7 @@ module.exports = async (req, res, next) => {
|
||||
const user = await UserModel.findById(decodedToken.id)
|
||||
if (!user.user_name ||
|
||||
!user.first_name ||
|
||||
!user.last_name ||
|
||||
!user.user_type ||
|
||||
!user.password) {
|
||||
return res.status(403).send({
|
||||
|
||||
@@ -26,6 +26,7 @@ module.exports = async (req, res, next) => {
|
||||
const user = await UserModel.findById(decodedToken.id)
|
||||
if (!user.user_name ||
|
||||
!user.first_name ||
|
||||
!user.last_name ||
|
||||
!user.password) {
|
||||
return res.status(403).send({
|
||||
status: 'error',
|
||||
|
||||
@@ -14,6 +14,11 @@ const AdvertisingLikeModel = require('../../../models/AdvertisingLikeModel')
|
||||
const AdvertisingComment = require('../../../models/AdvertisingCommentModel')
|
||||
const AdvertisingRatingModel = require('../../../models/AdvertisingRatingModel')
|
||||
const AdvertisingProfileModel = require('../../../models/AdvertisingProfile')
|
||||
const { createLikeNotification } = require('../../../utils/likeNotification')
|
||||
const {
|
||||
createCommentNotification,
|
||||
createRatingNotification
|
||||
} = require('../../../utils/commentNotification')
|
||||
|
||||
const createAdvertisingValidationRules = () => {
|
||||
return [
|
||||
@@ -698,6 +703,14 @@ const toggleLike = async (req, res, next) => {
|
||||
// افزایش تعداد لایکها در تبلیغات
|
||||
advertising.likesCount += 1
|
||||
await advertising.save()
|
||||
|
||||
await createLikeNotification({
|
||||
ownerId: advertising.creator_id,
|
||||
likerId: userId,
|
||||
entityId: advertising._id,
|
||||
type: 'billboard_like'
|
||||
})
|
||||
|
||||
return res.status(201).json({ message: 'ویترین با موفقیت لایک شد' })
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -727,8 +740,16 @@ const addComment = async (req, res, next) => {
|
||||
})
|
||||
|
||||
await newComment.save()
|
||||
// افزایش تعداد کامنتهای تبلیغ
|
||||
// await AdvertisingModel.findByIdAndUpdate(advertisingId, { $inc: { commentsCount: 1 } })
|
||||
|
||||
const advertising = await AdvertisingModel.findById(advertisingId)
|
||||
if (advertising) {
|
||||
await createCommentNotification({
|
||||
ownerId: advertising.creator_id,
|
||||
commenterId: userId,
|
||||
entityId: advertising._id,
|
||||
type: 'billboard_comment'
|
||||
})
|
||||
}
|
||||
|
||||
res.status(201).json({ message: 'کامنت با موفقیت اضافه شد', comment: newComment })
|
||||
} catch (error) {
|
||||
@@ -783,6 +804,13 @@ const rateAdvertising = async (req, res, next) => {
|
||||
rating
|
||||
})
|
||||
await newRating.save()
|
||||
|
||||
await createRatingNotification({
|
||||
ownerId: advertising.creator_id,
|
||||
raterId: userId,
|
||||
entityId: advertising._id,
|
||||
type: 'billboard_rating'
|
||||
})
|
||||
}
|
||||
|
||||
// بهروزرسانی امتیازات کلی کاربر
|
||||
|
||||
@@ -2,8 +2,14 @@
|
||||
const UserModel = require('../../../models/UserModel')
|
||||
const bcrypt = require('bcryptjs')
|
||||
const TokenService = require('../../../services/TokenService')
|
||||
const {
|
||||
expireBlockSuspensionIfNeeded,
|
||||
getBlockedAccountResponse
|
||||
} = require('../../../utils/blockSuspension')
|
||||
// const jwt = require('jsonwebtoken')
|
||||
|
||||
const INVALID_CREDENTIALS = 'نام کاربری یا کلمه عبور را اشتباه وارد کردید'
|
||||
|
||||
const loginWithUserName = async (req, res, next) => {
|
||||
try {
|
||||
// اعتبارسنجی ورودی ها
|
||||
@@ -11,30 +17,36 @@ const loginWithUserName = async (req, res, next) => {
|
||||
if (!user_name || !password) {
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'اطلاعات ارسالی اشتباه است'
|
||||
message: INVALID_CREDENTIALS
|
||||
})
|
||||
}
|
||||
// یافتن کاربر با نام کاربری
|
||||
const userLow = user_name.toLowerCase()
|
||||
const user = await UserModel.findOne({ user_name: userLow })
|
||||
let user = await UserModel.findOne({ user_name: userLow })
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'کاربری با این نام کاربری یافت نشد'
|
||||
message: INVALID_CREDENTIALS
|
||||
})
|
||||
}
|
||||
|
||||
user = await expireBlockSuspensionIfNeeded(user)
|
||||
const blockResponse = getBlockedAccountResponse(user)
|
||||
if (blockResponse) {
|
||||
return res.status(403).json(blockResponse)
|
||||
}
|
||||
if (!user.password) {
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'رمز عبور برای این کاربر تنظیم نشده است'
|
||||
message: INVALID_CREDENTIALS
|
||||
})
|
||||
}
|
||||
const isPasswordValid = await bcrypt.compare(password, user.password)
|
||||
if (!isPasswordValid) {
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'نام کاربری یا رمز عبور اشتباه است'
|
||||
message: INVALID_CREDENTIALS
|
||||
})
|
||||
}
|
||||
// اگر هم نام کاربری و هم رمز عبور درست بود، ارسال پیام موفقیت آمیز
|
||||
@@ -64,7 +76,7 @@ const loginWithUserName = async (req, res, next) => {
|
||||
(!user.password) { step = 'password' } else if
|
||||
(!user.first_name) { step = 'first_name' } else if
|
||||
(!user.user_type) { step = 'user_type' }
|
||||
if (!user.user_type || user.user_name === null || !user.first_name) {
|
||||
if (!user.user_type || user.user_name === null || !user.first_name || !user.last_name) {
|
||||
return res.json({
|
||||
message: 'کد تایید صحیح بود',
|
||||
token,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
const UserModel = require('../../../models/UserModel')
|
||||
const TokenService = require('../../../services/TokenService')
|
||||
const { isOtpExpired } = require('../../../utils/otpExpiry')
|
||||
const {
|
||||
expireBlockSuspensionIfNeeded,
|
||||
getBlockedAccountResponse
|
||||
} = require('../../../utils/blockSuspension')
|
||||
// const jwt = require('jsonwebtoken')
|
||||
|
||||
const verifyUser = async (req, res, next) => {
|
||||
@@ -13,13 +17,20 @@ const verifyUser = async (req, res, next) => {
|
||||
})
|
||||
}
|
||||
|
||||
const user = await UserModel.findOne({ mobile })
|
||||
let user = await UserModel.findOne({ mobile })
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
error: true,
|
||||
message: 'کاربری با این شماره موبایل یافت نشد'
|
||||
})
|
||||
}
|
||||
|
||||
user = await expireBlockSuspensionIfNeeded(user)
|
||||
const blockResponse = getBlockedAccountResponse(user)
|
||||
if (blockResponse) {
|
||||
return res.status(403).json(blockResponse)
|
||||
}
|
||||
|
||||
const token = TokenService.sign({ id: user._id })
|
||||
|
||||
if (!user.otp || isOtpExpired(user)) {
|
||||
@@ -64,7 +75,7 @@ const verifyUser = async (req, res, next) => {
|
||||
(!user.password) { step = 'password' } else if
|
||||
(!user.first_name) { step = 'first_name' } else if
|
||||
(!user.user_type) { step = 'user_type' } else { step = 'profile_image' }
|
||||
if (!user.user_type || user.user_name === null || !user.first_name) {
|
||||
if (!user.user_type || user.user_name === null || !user.first_name || !user.last_name) {
|
||||
return res.json({
|
||||
message: 'کد تایید صحیح بود',
|
||||
token,
|
||||
@@ -86,7 +97,7 @@ const verifyUser = async (req, res, next) => {
|
||||
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'کد تایید اشتباه است'
|
||||
message: 'کد را اشتباه وارد کردید'
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
||||
@@ -8,6 +8,10 @@ const PaymentModel = require('../../../models/PaymentModel')
|
||||
const NotificationModel = require('../../../models/NotificationModel')
|
||||
const { default: axios } = require('axios')
|
||||
const CommentModel = require('../../../models/CommentModel')
|
||||
const {
|
||||
resolveRatingForComment,
|
||||
recalculateUserRating
|
||||
} = require('../../../utils/commentRating')
|
||||
|
||||
const getOfferPriceFromDatabase = async (itemName) => {
|
||||
const item = await OfferTypeModel.findOne({ name: itemName })
|
||||
@@ -374,55 +378,35 @@ const createOfferComment = async (req, res, next) => {
|
||||
const userId = decodedToken.id
|
||||
|
||||
const { offerId, comment, rate, user_id } = req.body
|
||||
if (!offerId || !comment || !rate || !user_id) {
|
||||
if (!offerId || !comment || !user_id) {
|
||||
return res.status(400).send({ message: 'All fields are required' })
|
||||
}
|
||||
// بررسی اینکه آیا کاربر قبلاً برای این پیشنهاد نظر داده است
|
||||
const existingComment = await CommentModel.findOne({
|
||||
offer: offerId,
|
||||
creator: userId
|
||||
|
||||
const { ratingValue, isNewRating, requiresRating } = await resolveRatingForComment({
|
||||
creatorId: userId,
|
||||
targetUserId: user_id,
|
||||
rate
|
||||
})
|
||||
if (existingComment) {
|
||||
return res.status(400).send({ message: 'شما قبلاً نظر خود را برای این درخواست ثبت کردهاید' })
|
||||
|
||||
if (requiresRating) {
|
||||
return res.status(400).send({ message: 'Rating must be between 1 and 5' })
|
||||
}
|
||||
|
||||
const newComment = new CommentModel({
|
||||
user: user_id,
|
||||
offer: offerId,
|
||||
creator: userId,
|
||||
rating: rate,
|
||||
rating: ratingValue,
|
||||
comment,
|
||||
comment_for: 'user',
|
||||
status: 'pending'
|
||||
})
|
||||
await newComment.save()
|
||||
|
||||
const user = await UserModel.findById(user_id)
|
||||
|
||||
// پیدا کردن کامنتها و محاسبهی امتیاز کل
|
||||
const comments = await CommentModel.find({ user: user_id, comment_for: 'user' })
|
||||
const totalRating = Number(comments.reduce((acc, comment) => acc + comment.rating, 0))
|
||||
|
||||
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
|
||||
|
||||
const totalComments = await CommentModel.countDocuments({ user: user_id, comment_for: 'user' })
|
||||
const averageRating = totalRating / totalComments
|
||||
user.rate = averageRating.toFixed(1)
|
||||
await user.save()
|
||||
|
||||
res.status(200).json({ message: 'نظر شما با موفقیت ثبت شد' })
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
||||
@@ -3,6 +3,7 @@ const PostModel = require('../../../models/PostModel');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const UserModel = require('../../../models/UserModel');
|
||||
const LikeModel = require('../../../models/LikeModel');
|
||||
const { createLikeNotification } = require('../../../utils/likeNotification');
|
||||
|
||||
const toggleLike = async (req, res, next) => {
|
||||
try {
|
||||
@@ -48,6 +49,13 @@ const toggleLike = async (req, res, next) => {
|
||||
await post.save();
|
||||
await LikeModel.create({ postId, userId });
|
||||
|
||||
await createLikeNotification({
|
||||
ownerId: post.user_id,
|
||||
likerId: userId,
|
||||
entityId: post._id,
|
||||
type: 'post_like'
|
||||
});
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'پست با موفقیت لایک شد',
|
||||
is_liked: true,
|
||||
|
||||
@@ -5,6 +5,7 @@ const { check, validationResult } = require('express-validator');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const path = require('path');
|
||||
const { default: mongoose } = require('mongoose');
|
||||
const { viewerIsBlockedBy } = require('../../../utils/blockVisibility');
|
||||
|
||||
const createPostValidationRules = () => {
|
||||
console.log(4);
|
||||
@@ -139,6 +140,15 @@ const getUserPostsWeb = async (req, res, next) => {
|
||||
return res.status(404).json({ message: 'کاربر مورد نظر یافت نشد' })
|
||||
}
|
||||
|
||||
if (userReqId && viewerIsBlockedBy(user, userReqId)) {
|
||||
return res.status(200).json({
|
||||
posts: [],
|
||||
postsCount: 0,
|
||||
totalPages: 0,
|
||||
totalItems: 0,
|
||||
})
|
||||
}
|
||||
|
||||
const data = {}
|
||||
const options = {
|
||||
page: req.query.page || 1,
|
||||
|
||||
@@ -35,6 +35,7 @@ const getUserById = 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,
|
||||
@@ -81,6 +82,7 @@ const getProfile = async (req, res, next) => {
|
||||
let isRegister = true
|
||||
if (!user.user_name ||
|
||||
!user.first_name ||
|
||||
!user.last_name ||
|
||||
!user.user_type ||
|
||||
!user.national_card_image ||
|
||||
!user.password ||
|
||||
|
||||
@@ -6,6 +6,46 @@ const { check, validationResult } = require('express-validator')
|
||||
const { ProvinceModel, CityModel } = require('../../../models/StateCity')
|
||||
const NotificationModel = require('../../../models/NotificationModel')
|
||||
const CommentModel = require('../../../models/CommentModel')
|
||||
const {
|
||||
resolveRatingForComment,
|
||||
recalculateUserRating
|
||||
} = require('../../../utils/commentRating')
|
||||
|
||||
const saveProjectComment = async ({
|
||||
targetUserId,
|
||||
projectId,
|
||||
creatorId,
|
||||
comment,
|
||||
rate,
|
||||
commentFor
|
||||
}) => {
|
||||
const { ratingValue, isNewRating, requiresRating } = await resolveRatingForComment({
|
||||
creatorId,
|
||||
targetUserId,
|
||||
rate
|
||||
})
|
||||
|
||||
if (requiresRating) {
|
||||
return { error: { status: 400, message: 'Rating must be between 1 and 5' } }
|
||||
}
|
||||
|
||||
const newComment = new CommentModel({
|
||||
user: targetUserId,
|
||||
project: projectId,
|
||||
creator: creatorId,
|
||||
rating: ratingValue,
|
||||
comment,
|
||||
comment_for: commentFor,
|
||||
status: 'pending'
|
||||
})
|
||||
await newComment.save()
|
||||
|
||||
if (isNewRating && commentFor === 'user') {
|
||||
await recalculateUserRating(targetUserId)
|
||||
}
|
||||
|
||||
return { ratingValue, isNewRating }
|
||||
}
|
||||
|
||||
const doneProject = async (req, res, next) => {
|
||||
try {
|
||||
@@ -16,7 +56,7 @@ const doneProject = async (req, res, next) => {
|
||||
const userId = decodedToken.id
|
||||
|
||||
const { project_id, comment, rate, user_id } = req.body
|
||||
if (!project_id || !comment || !rate || !user_id) {
|
||||
if (!project_id || !comment || !user_id) {
|
||||
return res.status(400).send({ message: 'All fields are required' })
|
||||
}
|
||||
|
||||
@@ -42,43 +82,20 @@ const doneProject = async (req, res, next) => {
|
||||
project.status = 'done'
|
||||
await project.save()
|
||||
|
||||
const newComment = new CommentModel({
|
||||
user: user_id,
|
||||
project: project_id,
|
||||
creator: userId,
|
||||
rating: rate,
|
||||
const commentResult = await saveProjectComment({
|
||||
targetUserId: user_id,
|
||||
projectId: project_id,
|
||||
creatorId: userId,
|
||||
comment,
|
||||
comment_for: 'user',
|
||||
status: 'pending'
|
||||
rate,
|
||||
commentFor: 'user'
|
||||
})
|
||||
await newComment.save()
|
||||
|
||||
const user = await UserModel.findById(user_id)
|
||||
|
||||
// پیدا کردن کامنتها و محاسبهی امتیاز کل
|
||||
const comments = await CommentModel.find({ user: user_id, comment_for: 'user' })
|
||||
const totalRating = Number(comments.reduce((acc, comment) => acc + comment.rating, 0))
|
||||
|
||||
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 (commentResult.error) {
|
||||
return res.status(commentResult.error.status).send({ message: commentResult.error.message })
|
||||
}
|
||||
|
||||
user.user_score = totalRating
|
||||
user.user_level = userLevel
|
||||
|
||||
const totalComments = await CommentModel.countDocuments({ user: user_id, comment_for: 'user' })
|
||||
const averageRating = totalRating / totalComments
|
||||
user.rate = averageRating.toFixed(1)
|
||||
await user.save()
|
||||
const user = await UserModel.findById(user_id)
|
||||
|
||||
const notification = new NotificationModel({
|
||||
user_id: user._id,
|
||||
@@ -105,7 +122,7 @@ const doneProjectWeb = async (req, res, next) => {
|
||||
const userId = decodedToken.id
|
||||
|
||||
const { project_id, comment, rate, user_id } = req.body
|
||||
if (!project_id || !comment || !rate || !user_id) {
|
||||
if (!project_id || !comment || !user_id) {
|
||||
return res.status(400).send({ message: 'All fields are required' })
|
||||
}
|
||||
|
||||
@@ -117,43 +134,20 @@ const doneProjectWeb = async (req, res, next) => {
|
||||
project.status = 'done'
|
||||
await project.save()
|
||||
|
||||
const newComment = new CommentModel({
|
||||
user: user_id,
|
||||
project: project_id,
|
||||
creator: userId,
|
||||
rating: rate,
|
||||
const commentResult = await saveProjectComment({
|
||||
targetUserId: user_id,
|
||||
projectId: project_id,
|
||||
creatorId: userId,
|
||||
comment,
|
||||
comment_for: 'user',
|
||||
status: 'pending'
|
||||
rate,
|
||||
commentFor: 'user'
|
||||
})
|
||||
await newComment.save()
|
||||
|
||||
const user = await UserModel.findById(user_id)
|
||||
|
||||
// پیدا کردن کامنتها و محاسبهی امتیاز کل
|
||||
const comments = await CommentModel.find({ user: user_id, comment_for: 'user' })
|
||||
const totalRating = Number(comments.reduce((acc, comment) => acc + comment.rating, 0))
|
||||
|
||||
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 (commentResult.error) {
|
||||
return res.status(commentResult.error.status).send({ message: commentResult.error.message })
|
||||
}
|
||||
|
||||
user.user_score = totalRating
|
||||
user.user_level = userLevel
|
||||
|
||||
const totalComments = await CommentModel.countDocuments({ user: user_id, comment_for: 'user' })
|
||||
const averageRating = totalRating / totalComments
|
||||
user.rate = averageRating.toFixed(1)
|
||||
await user.save()
|
||||
const user = await UserModel.findById(user_id)
|
||||
|
||||
const notification = new NotificationModel({
|
||||
user_id: user._id,
|
||||
@@ -182,7 +176,7 @@ const cancleProject = async (req, res, next) => {
|
||||
|
||||
const { project_id, comment, rate, user_id } = req.body
|
||||
|
||||
if (!project_id || !comment || !rate || !user_id) {
|
||||
if (!project_id || !comment || !user_id) {
|
||||
return res.status(400).send({ message: 'All fields are required' })
|
||||
}
|
||||
|
||||
@@ -194,16 +188,18 @@ const cancleProject = async (req, res, next) => {
|
||||
project.status = 'cancled'
|
||||
await project.save()
|
||||
|
||||
const newComment = new CommentModel({
|
||||
user: user_id,
|
||||
project: project_id,
|
||||
creator: userId,
|
||||
rating: rate,
|
||||
const commentResult = await saveProjectComment({
|
||||
targetUserId: user_id,
|
||||
projectId: project_id,
|
||||
creatorId: userId,
|
||||
comment,
|
||||
comment_for: 'project',
|
||||
status: 'pending'
|
||||
rate,
|
||||
commentFor: 'project'
|
||||
})
|
||||
await newComment.save()
|
||||
|
||||
if (commentResult.error) {
|
||||
return res.status(commentResult.error.status).send({ message: commentResult.error.message })
|
||||
}
|
||||
|
||||
res.status(200).json({ message: 'پروژه با موفقیت تغییر یافت شد' })
|
||||
} catch (error) {
|
||||
@@ -363,6 +359,14 @@ const setRateProject = async (req, res, next) => {
|
||||
if (!project) {
|
||||
return res.status(403).send({ message: 'Access denied: You are not the creator of this project.' })
|
||||
}
|
||||
|
||||
const alreadyRated = project.ratings.some((item) =>
|
||||
item.creator_id.toString() === userId.toString()
|
||||
)
|
||||
if (alreadyRated) {
|
||||
return res.status(400).send({ message: 'شما قبلاً به این پروژه امتیاز دادهاید' })
|
||||
}
|
||||
|
||||
project.ratings.push({
|
||||
project_id,
|
||||
rating: rate,
|
||||
|
||||
@@ -7,6 +7,8 @@ const setUserFullNameValidationRules = () => {
|
||||
return [
|
||||
check('first_name')
|
||||
.notEmpty().withMessage('نام نمیتواند خالی باشد'),
|
||||
check('last_name')
|
||||
.notEmpty().withMessage('نام خانوادگی نمیتواند خالی باشد')
|
||||
]
|
||||
}
|
||||
|
||||
@@ -33,7 +35,7 @@ const setUserFullName = async (req, res, next) => {
|
||||
|
||||
// آپدیت نام کاربری به صورت lowercase
|
||||
user.first_name = first_name.trim().toLowerCase()
|
||||
user.last_name = last_name?.trim() ? last_name.trim().toLowerCase() : null
|
||||
user.last_name = last_name.trim().toLowerCase()
|
||||
await user.save()
|
||||
|
||||
return res.json({
|
||||
|
||||
@@ -14,7 +14,6 @@ const setUserName = async (req, res, next) => {
|
||||
})
|
||||
}
|
||||
|
||||
// جستجوی کاربر با شماره موبایل ارسالی
|
||||
const user = await UserModel.findOne({ mobile })
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
@@ -22,6 +21,7 @@ const setUserName = async (req, res, next) => {
|
||||
message: 'کاربری با این شماره موبایل یافت نشد'
|
||||
})
|
||||
}
|
||||
|
||||
const validationError = validateUsername(user_name)
|
||||
if (validationError) {
|
||||
return res.status(422).json({
|
||||
@@ -32,7 +32,6 @@ const setUserName = async (req, res, next) => {
|
||||
|
||||
const normalizedUserName = user_name.trim().toLowerCase()
|
||||
|
||||
// بررسی تکراری بودن user_name
|
||||
const existingUser = await UserModel.findOne({ user_name: { $regex: new RegExp('^' + normalizedUserName + '$', 'i') } })
|
||||
if (existingUser && existingUser._id.toString() !== user._id.toString()) {
|
||||
const suggestions = await generateUsernameSuggestions(
|
||||
@@ -47,7 +46,6 @@ const setUserName = async (req, res, next) => {
|
||||
})
|
||||
}
|
||||
|
||||
// آپدیت نام کاربری به صورت lowercase
|
||||
user.user_name = normalizedUserName
|
||||
await user.save()
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ const verifyUser = async (req, res, next) => {
|
||||
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'کد تایید اشتباه است'
|
||||
message: 'کد را اشتباه وارد کردید'
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
||||
@@ -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: "نظر با موفقیت ثبت شد و بعد از تایید، منتشر میشود.",
|
||||
|
||||
@@ -9,7 +9,18 @@ const setAddress = async (req, res, next) => {
|
||||
if (!token) return res.status(401).send('Access Denied')
|
||||
const { province_id, city_id, address, lat, lng, show_location } = req.body
|
||||
// اعتبارسنجی دادهها
|
||||
if (!province_id || !city_id || !address || !lat || !lng || show_location === 'undefined') {
|
||||
if (
|
||||
!province_id ||
|
||||
!city_id ||
|
||||
!address ||
|
||||
lat === undefined ||
|
||||
lat === null ||
|
||||
lat === '' ||
|
||||
lng === undefined ||
|
||||
lng === null ||
|
||||
lng === '' ||
|
||||
show_location === undefined
|
||||
) {
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'اطلاعات ارسالی ناقص است'
|
||||
@@ -41,7 +52,8 @@ const setAddress = async (req, res, next) => {
|
||||
user.address = address
|
||||
user.lat = lat
|
||||
user.lng = lng
|
||||
user.show_location = show_location
|
||||
user.show_location =
|
||||
show_location === true || show_location === 'true'
|
||||
|
||||
await user.save()
|
||||
|
||||
@@ -60,7 +72,18 @@ const updateAddress = async (req, res, next) => {
|
||||
const { province_id, city_id, address, lat, lng, show_location } = req.body
|
||||
|
||||
// اعتبارسنجی دادهها
|
||||
if (!province_id || !city_id || !address || !lat || !lng || show_location === undefined) {
|
||||
if (
|
||||
!province_id ||
|
||||
!city_id ||
|
||||
!address ||
|
||||
lat === undefined ||
|
||||
lat === null ||
|
||||
lat === '' ||
|
||||
lng === undefined ||
|
||||
lng === null ||
|
||||
lng === '' ||
|
||||
show_location === undefined
|
||||
) {
|
||||
return res.status(422).json({
|
||||
error: true,
|
||||
message: 'اطلاعات ارسالی ناقص است'
|
||||
@@ -95,7 +118,8 @@ const updateAddress = async (req, res, next) => {
|
||||
user.address = address
|
||||
user.lat = lat
|
||||
user.lng = lng
|
||||
user.show_location = show_location
|
||||
user.show_location =
|
||||
show_location === true || show_location === 'true'
|
||||
|
||||
await user.save()
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
const UserModel = require('../../../models/UserModel')
|
||||
const jwt = require('jsonwebtoken')
|
||||
|
||||
const completeRegistration = 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 = await UserModel.findById(decodedToken.id)
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
error: true,
|
||||
message: 'کاربر یافت نشد'
|
||||
})
|
||||
}
|
||||
|
||||
user.is_Register = true
|
||||
|
||||
if (!user.is_verified || user.is_verified === 'none') {
|
||||
user.is_verified = 'pending'
|
||||
}
|
||||
|
||||
await user.save()
|
||||
|
||||
return res.json({
|
||||
message: 'ثبت نام تکمیل شد',
|
||||
is_verified: user.is_verified,
|
||||
is_Register: user.is_Register
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
completeRegistration
|
||||
}
|
||||
@@ -38,6 +38,7 @@ const setNationalCardImage = async (req, res, next) => {
|
||||
|
||||
// ذخیره مسیر فایل در دیتابیس
|
||||
user.national_card_image = `/carts/${uniqueFileName}`
|
||||
user.is_Register = true
|
||||
user.is_verified = 'pending'
|
||||
await user.save()
|
||||
|
||||
|
||||
46
index.js
@@ -303,6 +303,52 @@ app.post('/api/v1/chat/file', [blockCheck], async (req, res) => {
|
||||
res.status(500).json({ error: 'Server error' })
|
||||
}
|
||||
})
|
||||
|
||||
app.delete('/api/v1/chat', [blockCheck], async (req, res) => {
|
||||
try {
|
||||
const userId = String(req.user._id)
|
||||
const { messageIds } = req.body
|
||||
|
||||
if (!Array.isArray(messageIds) || messageIds.length === 0) {
|
||||
return res.status(422).json({ message: 'پیامی انتخاب نشده است' })
|
||||
}
|
||||
|
||||
const messages = await MessageModel.find({
|
||||
_id: { $in: messageIds },
|
||||
senderId: userId
|
||||
})
|
||||
|
||||
if (!messages.length) {
|
||||
return res.status(403).json({ message: 'فقط پیامهای خودتان قابل حذف هستند' })
|
||||
}
|
||||
|
||||
for (const msg of messages) {
|
||||
if (msg.file) {
|
||||
const relative = String(msg.file).replace(/^\//, '')
|
||||
const filePath = path.join(__dirname, '../storage', relative)
|
||||
if (fs.existsSync(filePath)) {
|
||||
await fs.remove(filePath).catch(() => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const deletedIds = messages.map((m) => String(m._id))
|
||||
await MessageModel.deleteMany({ _id: { $in: deletedIds } })
|
||||
|
||||
const receiverIds = [...new Set(messages.map((m) => String(m.receiverId)))]
|
||||
receiverIds.forEach((receiverId) => {
|
||||
io.to(`chat:${userId}:${receiverId}`)
|
||||
.to(`chat:${receiverId}:${userId}`)
|
||||
.emit('messagesDeleted', { messageIds: deletedIds, deletedBy: userId })
|
||||
})
|
||||
|
||||
res.json({ deletedIds, message: 'پیامها حذف شدند' })
|
||||
} catch (error) {
|
||||
console.error('Error deleting messages:', error)
|
||||
res.status(500).json({ error: 'Server error' })
|
||||
}
|
||||
})
|
||||
|
||||
// Errors
|
||||
require('./middlewares/exception')(app)
|
||||
require('./middlewares/404')(app)
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
const UserModel = require('../models/UserModel') // مسیر درست به مدل کاربر
|
||||
const UserModel = require('../models/UserModel')
|
||||
const jwt = require('jsonwebtoken')
|
||||
const {
|
||||
expireBlockSuspensionIfNeeded,
|
||||
getBlockedAccountResponse
|
||||
} = require('../utils/blockSuspension')
|
||||
|
||||
module.exports = async (req, res, next) => {
|
||||
console.log(2);
|
||||
try {
|
||||
if (!('authorization' in req.headers)) {
|
||||
return res.status(401).send({
|
||||
@@ -24,17 +27,15 @@ module.exports = async (req, res, next) => {
|
||||
})
|
||||
}
|
||||
|
||||
const user = await UserModel.findById(decodedToken.id)
|
||||
if (user.block_status === true) {
|
||||
return res.status(403).send({
|
||||
status: 'error',
|
||||
code: 403,
|
||||
message: 'حساب کاربری شما مسدود شده است، برای اطلاعات بیشتر با پشتیبانی تماس بگیرید!',
|
||||
type: 'block'
|
||||
})
|
||||
let user = await UserModel.findById(decodedToken.id)
|
||||
user = await expireBlockSuspensionIfNeeded(user)
|
||||
|
||||
const blockResponse = getBlockedAccountResponse(user)
|
||||
if (blockResponse) {
|
||||
return res.status(403).send(blockResponse)
|
||||
}
|
||||
|
||||
req.user = user // کاربر را به درخواست اضافه کنید تا در سایر middleware ها و کنترلرها استفاده شود.
|
||||
req.user = user
|
||||
next()
|
||||
} catch (error) {
|
||||
console.error('Error in blockCheck middleware:', error)
|
||||
|
||||
@@ -28,6 +28,7 @@ module.exports = async (req, res, next) => {
|
||||
const user = await UserModel.findById(decodedToken.id)
|
||||
if (!user.user_name ||
|
||||
!user.first_name ||
|
||||
!user.last_name ||
|
||||
!user.user_type ||
|
||||
!user.password) {
|
||||
return res.status(403).send({
|
||||
|
||||
@@ -26,6 +26,7 @@ module.exports = async (req, res, next) => {
|
||||
const user = await UserModel.findById(decodedToken.id)
|
||||
if (!user.user_name ||
|
||||
!user.first_name ||
|
||||
!user.last_name ||
|
||||
!user.password) {
|
||||
return res.status(403).send({
|
||||
status: 'error',
|
||||
|
||||
@@ -25,7 +25,8 @@ const commentSchema = new mongoose.Schema({
|
||||
},
|
||||
rating: {
|
||||
type: Number,
|
||||
required: true
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
comment: {
|
||||
type: String,
|
||||
|
||||
@@ -317,6 +317,11 @@ const userSchema = new mongoose.Schema({
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
blocked_until: {
|
||||
type: Date,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
advertising_profiles: [{
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'AdvertisingProfile'
|
||||
|
||||
@@ -9,6 +9,7 @@ const { setConversation, updateConversation } = require('../../../controllers/ap
|
||||
const { saveAuth, saveAuthValidationRules, updateShaba } = require('../../../controllers/application/verify/authController')
|
||||
const { setAddress, updateAddress } = require('../../../controllers/application/verify/addressController')
|
||||
const { setNationalCardImage } = require('../../../controllers/application/verify/setNationalCardImageController')
|
||||
const { completeRegistration } = require('../../../controllers/application/verify/completeRegistrationController')
|
||||
const blockCheck = require('../../../middlewares/blockCheck')
|
||||
const auth = require('../../../middlewares/auth')
|
||||
const { setServices, updateServices } = require('../../../controllers/application/verify/servicesController')
|
||||
@@ -34,5 +35,6 @@ router.patch('/shaba', [blockCheck], [auth], updateShaba)
|
||||
router.post('/address', setAddress)
|
||||
router.patch('/address', [blockCheck], [auth], updateAddress)
|
||||
router.post('/national_card_image', setNationalCardImage)
|
||||
router.post('/complete', completeRegistration)
|
||||
|
||||
module.exports = router
|
||||
|
||||
BIN
storage/posts/images/1765993655221-0-dsxetense-2.jpg
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
storage/posts/images/1766060842784-0-1000281068.jpg
Normal file
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 488 KiB |
BIN
storage/posts/images/1766061356093-0-1000309674.jpg
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
storage/posts/images/1766061370820-0-1000309674.jpg
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
storage/posts/images/1766061378067-0-1000309674.jpg
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
storage/posts/images/1766061391297-0-1000309674.jpg
Normal file
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 494 KiB |
BIN
storage/posts/images/1766156209362-0-1000310283.jpg
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
storage/posts/images/1766156209363-1-1000310020.jpg
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
storage/posts/images/1766231499333-0-gf11qluixbxd.jpeg
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
storage/posts/images/1766231549537-0-Model.jpg
Normal file
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 61 KiB |
BIN
storage/posts/images/1766231774132-0-11-6.jpg
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
storage/posts/images/1766231993781-0-1000303096.jpg
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
storage/posts/images/1766303557551-0-maayebarayeshgari.jpg
Normal file
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 29 KiB |
BIN
storage/posts/images/1766303651986-0-36512903_m-1.jpg
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
storage/posts/images/1766303991978-0-Photography-Business-5.webp
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
storage/posts/images/1766303991979-1-akasii.jpg
Normal file
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 216 KiB |
BIN
storage/posts/images/1766304193955-0-medium_photo_278629.webp
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
storage/posts/images/1766323974459-0-dsxetense-2.jpg
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
storage/posts/images/1766323974463-1-dsxetense-2.jpg
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
storage/posts/images/1766324303118-0-dsxetense-2.jpg
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
storage/posts/images/1766324303121-1-download (2).jfif
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
storage/posts/images/1766324303121-2-eita.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 69 KiB |
BIN
storage/posts/images/1766324805512-0-banner1.jpg
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
storage/posts/images/1766324805517-1-dsxetense-2.jpg
Normal file
|
After Width: | Height: | Size: 192 KiB |
|
After Width: | Height: | Size: 487 KiB |
|
After Width: | Height: | Size: 490 KiB |
|
After Width: | Height: | Size: 637 KiB |
|
After Width: | Height: | Size: 525 KiB |
|
After Width: | Height: | Size: 420 KiB |
|
After Width: | Height: | Size: 487 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 448 KiB |
BIN
storage/posts/images/1766425783170-0-1000311888.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
storage/posts/images/1766425783171-1-1000311891.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
storage/posts/images/1766425783175-2-1000311888.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
storage/posts/images/1766483935551-0-1000312199.jpg
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
storage/posts/images/1766483935556-1-1000312200.jpg
Normal file
|
After Width: | Height: | Size: 209 KiB |
BIN
storage/posts/images/1766483935556-2-1000312205.jpg
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
storage/posts/images/1766483935557-3-1000312203.jpg
Normal file
|
After Width: | Height: | Size: 143 KiB |
BIN
storage/posts/images/1766483935561-4-1000312207.jpg
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
storage/posts/images/1766484539649-0-1000312237.jpg
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
storage/posts/images/1766484539650-1-1000312235.jpg
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
storage/posts/images/1766484539650-2-1000312236.jpg
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
storage/posts/images/1766484539653-3-1000312227.jpg
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
storage/posts/images/1766484576936-0-1000312233.jpg
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
storage/posts/images/1766484576938-1-1000312234.jpg
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
storage/posts/images/1766484576939-2-1000312232.jpg
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
storage/posts/images/1766484576939-3-1000312229.jpg
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
storage/posts/images/1766484614767-0-1000312231.jpg
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
storage/posts/images/1766485845440-0-1000312317.jpg
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
storage/posts/images/1766485845447-1-1000312327.jpg
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
storage/posts/images/1766485845447-2-1000312332.jpg
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
storage/posts/images/1766485845448-3-1000312351.jpg
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
storage/posts/images/1766485845448-4-1000312305.jpg
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
storage/posts/images/1766485845451-5-1000312316.jpg
Normal file
|
After Width: | Height: | Size: 97 KiB |
BIN
storage/posts/images/1766485845451-6-1000312307.jpg
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
storage/posts/images/1766485956116-0-1000312369.jpg
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
storage/posts/images/1766485956117-1-1000312381.jpg
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
storage/posts/images/1766485956117-2-1000312370.jpg
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
storage/posts/images/1766485956118-3-1000312372.jpg
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
storage/posts/images/1766485956118-4-1000312371.jpg
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
storage/posts/images/1766485956119-5-1000312373.jpg
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
storage/posts/images/1766485956119-6-1000312374.jpg
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
storage/posts/images/1766486021317-0-1000312391.jpg
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
storage/posts/images/1766486021320-1-1000312393.jpg
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
storage/posts/images/1766486021320-2-1000312394.jpg
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
storage/posts/images/1766486021320-3-1000312392.jpg
Normal file
|
After Width: | Height: | Size: 50 KiB |