# Conflicts:
#	controllers/application/projects/getProjectController.js
#	controllers/application/stories/storyController.js
#	controllers/application/users/getUserController.js
#	controllers/panel/projects/projectController.js
#	index.js
This commit is contained in:
payacom
2026-08-31 20:32:29 +03:30
173 changed files with 21806 additions and 18680 deletions

View File

@@ -1,221 +1,221 @@
/* eslint-disable camelcase */
const { ProvinceModel, CityModel } = require('../../../models/StateCity')
const ProjectModel = require('../../../models/ProjectModel')
const UserModel = require('../../../models/UserModel')
const { check, validationResult } = require('express-validator')
const jwt = require('jsonwebtoken')
const {
canCreateProject,
respondProjectProfileIncomplete
} = require('../../../utils/projectProfile')
const createProjectValidationRules = () => {
return [
check('title').notEmpty().withMessage('عنوان نمی‌تواند خالی باشد'),
check('expertise').notEmpty().withMessage('تخصص نمی‌تواند خالی باشد'),
check('gender')
.isIn(['male', 'female'])
.withMessage('جنسیت باید male یا female باشد'),
check('age').notEmpty().withMessage('سن نمی‌تواند خالی باشد'),
check('conversation_projects')
.notEmpty()
.withMessage('پروژه‌های مکالمه نمی‌تواند خالی باشد')
.isBoolean()
.withMessage('مقدار پروژه‌های مکالمه باید یک مقدار boolean باشد'),
check('province').notEmpty().withMessage('استان نمی‌تواند خالی باشد'),
check('city').notEmpty().withMessage('شهر نمی‌تواند خالی باشد'),
check('offer_time')
.notEmpty()
.withMessage('زمان پروژه نمی‌تواند خالی باشد'),
check('offer_price')
.notEmpty()
.withMessage('قیمت پیشنهادی نمی‌تواند خالی باشد'),
check('description')
.notEmpty()
.withMessage('توضیحات نمی‌تواند خالی باشد'),
check('project_type')
.notEmpty()
.withMessage('نوع پروژه نمی‌تواند خالی باشد')
.isIn(['free', 'normal', 'force', 'highlight'])
.withMessage('نوع پروژه باید normal یا force یا highlight باشد')
]
}
const createProject = 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(422).json({
error: true,
message: 'شما دسترسی به این بخش ندارید'
})
}
if (!canCreateProject(user)) {
return respondProjectProfileIncomplete(res)
}
// اعتبارسنجی درخواست
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
const {
title,
expertise,
sub_expertise,
gender,
age,
conversation_projects,
province,
city,
offer_time,
offer_price,
description,
project_type,
public_status,
created_for_user,
number_of_person
} = req.body
if (
!title ||
!expertise ||
!age ||
conversation_projects === null ||
!province ||
!city ||
!offer_time ||
!offer_price ||
!description ||
!project_type
) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
const provinceFind = await ProvinceModel.findOne(
{ id: province },
{ _id: 0 }
)
const cityFind = await CityModel.findOne({ id: city })
// اگر پروژه برای یک کاربر خاص باید ساخته شود
if (public_status && public_status === 'private' && created_for_user) {
const createdUser = await UserModel.findById(created_for_user)
if (!createdUser) {
return res.status(422).json({
error: true, message: 'کاربر مورد نظر برای ساخت پروژه یافت نشد'
})
}
// ایجاد شیء پروژه
const projectData = {
title,
expertise,
sub_expertise,
gender,
age,
conversation_projects,
province,
city,
offer_time,
offer_price,
description,
project_type,
creator_id: user._id, // ارتباط با کاربر
public_status,
created_for_user
}
// ساخت شیء پروژه
const project = new ProjectModel(projectData)
// ذخیره کردن رکورد در پایگاه داده
await project.save()
res.status(201).json({ message: 'پروژه با موفقیت ساخته شد', id: project._id }
)
} else {
if (number_of_person > 1) {
for (let i = 1; i <= number_of_person; i++) {
let currentProjectType
if (project_type === 'free' && i === 1) {
currentProjectType = 'free'
} else if (project_type === 'highlight' || project_type === 'force') {
currentProjectType = project_type
} else {
currentProjectType = 'normal'
}
const newProject = new ProjectModel({
title: `${title}_${i}`,
expertise,
sub_expertise,
gender,
age,
conversation_projects,
province: provinceFind,
city: cityFind,
offer_time,
offer_price,
description,
creator_id: user._id, // ارتباط با کاربر
project_type: currentProjectType,
public_status,
created_for_user
})
// ذخیره کردن رکورد در پایگاه داده
await newProject.save()
}
res.status(201).json({
message: 'پروژه‌ها با موفقیت ساخته شدند'
})
} else {
const project = new ProjectModel({
title,
expertise,
sub_expertise,
gender,
age,
conversation_projects,
province: provinceFind,
city: cityFind,
offer_time,
offer_price,
description,
creator_id: user._id, // ارتباط با کاربر
project_type
})
// ذخیره کردن رکورد در پایگاه داده
await project.save()
res.status(201).json({
message: 'پروژه با موفقیت ساخته شد',
id: project._id
})
}
}
} catch (error) {
next(error)
}
}
module.exports = {
createProjectValidationRules,
createProject
}
/* eslint-disable camelcase */
const { ProvinceModel, CityModel } = require('../../../models/StateCity')
const ProjectModel = require('../../../models/ProjectModel')
const UserModel = require('../../../models/UserModel')
const { check, validationResult } = require('express-validator')
const jwt = require('jsonwebtoken')
const {
canCreateProject,
respondProjectProfileIncomplete
} = require('../../../utils/projectProfile')
const createProjectValidationRules = () => {
return [
check('title').notEmpty().withMessage('عنوان نمی‌تواند خالی باشد'),
check('expertise').notEmpty().withMessage('تخصص نمی‌تواند خالی باشد'),
check('gender')
.isIn(['male', 'female'])
.withMessage('جنسیت باید male یا female باشد'),
check('age').notEmpty().withMessage('سن نمی‌تواند خالی باشد'),
check('conversation_projects')
.notEmpty()
.withMessage('پروژه‌های مکالمه نمی‌تواند خالی باشد')
.isBoolean()
.withMessage('مقدار پروژه‌های مکالمه باید یک مقدار boolean باشد'),
check('province').notEmpty().withMessage('استان نمی‌تواند خالی باشد'),
check('city').notEmpty().withMessage('شهر نمی‌تواند خالی باشد'),
check('offer_time')
.notEmpty()
.withMessage('زمان پروژه نمی‌تواند خالی باشد'),
check('offer_price')
.notEmpty()
.withMessage('قیمت پیشنهادی نمی‌تواند خالی باشد'),
check('description')
.notEmpty()
.withMessage('توضیحات نمی‌تواند خالی باشد'),
check('project_type')
.notEmpty()
.withMessage('نوع پروژه نمی‌تواند خالی باشد')
.isIn(['free', 'normal', 'force', 'highlight'])
.withMessage('نوع پروژه باید normal یا force یا highlight باشد')
]
}
const createProject = 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(422).json({
error: true,
message: 'شما دسترسی به این بخش ندارید'
})
}
if (!canCreateProject(user)) {
return respondProjectProfileIncomplete(res)
}
// اعتبارسنجی درخواست
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
const {
title,
expertise,
sub_expertise,
gender,
age,
conversation_projects,
province,
city,
offer_time,
offer_price,
description,
project_type,
public_status,
created_for_user,
number_of_person
} = req.body
if (
!title ||
!expertise ||
!age ||
conversation_projects === null ||
!province ||
!city ||
!offer_time ||
!offer_price ||
!description ||
!project_type
) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
const provinceFind = await ProvinceModel.findOne(
{ id: province },
{ _id: 0 }
)
const cityFind = await CityModel.findOne({ id: city })
// اگر پروژه برای یک کاربر خاص باید ساخته شود
if (public_status && public_status === 'private' && created_for_user) {
const createdUser = await UserModel.findById(created_for_user)
if (!createdUser) {
return res.status(422).json({
error: true, message: 'کاربر مورد نظر برای ساخت پروژه یافت نشد'
})
}
// ایجاد شیء پروژه
const projectData = {
title,
expertise,
sub_expertise,
gender,
age,
conversation_projects,
province,
city,
offer_time,
offer_price,
description,
project_type,
creator_id: user._id, // ارتباط با کاربر
public_status,
created_for_user
}
// ساخت شیء پروژه
const project = new ProjectModel(projectData)
// ذخیره کردن رکورد در پایگاه داده
await project.save()
res.status(201).json({ message: 'پروژه با موفقیت ساخته شد', id: project._id }
)
} else {
if (number_of_person > 1) {
for (let i = 1; i <= number_of_person; i++) {
let currentProjectType
if (project_type === 'free' && i === 1) {
currentProjectType = 'free'
} else if (project_type === 'highlight' || project_type === 'force') {
currentProjectType = project_type
} else {
currentProjectType = 'normal'
}
const newProject = new ProjectModel({
title: `${title}_${i}`,
expertise,
sub_expertise,
gender,
age,
conversation_projects,
province: provinceFind,
city: cityFind,
offer_time,
offer_price,
description,
creator_id: user._id, // ارتباط با کاربر
project_type: currentProjectType,
public_status,
created_for_user
})
// ذخیره کردن رکورد در پایگاه داده
await newProject.save()
}
res.status(201).json({
message: 'پروژه‌ها با موفقیت ساخته شدند'
})
} else {
const project = new ProjectModel({
title,
expertise,
sub_expertise,
gender,
age,
conversation_projects,
province: provinceFind,
city: cityFind,
offer_time,
offer_price,
description,
creator_id: user._id, // ارتباط با کاربر
project_type
})
// ذخیره کردن رکورد در پایگاه داده
await project.save()
res.status(201).json({
message: 'پروژه با موفقیت ساخته شد',
id: project._id
})
}
}
} catch (error) {
next(error)
}
}
module.exports = {
createProjectValidationRules,
createProject
}

View File

@@ -1,15 +1,97 @@
/* eslint-disable camelcase */
const { default: mongoose } = require('mongoose')
const ProjectModel = require('../../../models/ProjectModel')
const { projectPublicStatusFilter } = require('../../../utils/moderationStatus')
const RequestModel = require('../../../models/RequestModel')
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const { buildUnifiedInterestScore, blendFreshIntoRanked } = require('../../../utils/exploreAlgorithm')
function getOptionalUserId (req) {
try {
const token = req.header('Authorization')?.split(' ')[1]
if (!token) return null
const decoded = jwt.verify(token, process.env.APP_SECRET)
return decoded?.id || null
} catch {
return null
}
}
/**
* Projects matching the viewer's cross-content interest profile (built from their
* post/shop/academy/billboard interactions) — projects carry their own `expertise` field
* directly (no separate category model like shop/billboard), so interest tags are matched
* straight against it. Mirrors getInterestMatchedAds in advertisingController.js.
*/
async function getInterestMatchedProjects (userId, filter, excludeIds, limit = 5) {
if (!userId) return []
const interestWeights = await buildUnifiedInterestScore(userId)
const tags = Object.keys(interestWeights).filter((tag) => interestWeights[tag] > 0)
if (!tags.length) return []
return ProjectModel.find({
...filter,
expertise: { $in: tags },
_id: { $nin: excludeIds }
})
.sort({ createdAt: -1 })
.limit(limit)
.lean()
}
const calculateRemainingTime = (milliseconds) => {
const days = Math.floor(milliseconds / (1000 * 60 * 60 * 24))
const hours = Math.floor((milliseconds % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
return `${days} روز و ${hours} ساعت`
}
const REQUEST_USER_SELECT =
'user_level first_name last_name user_name is_verified verify_badge user_score rate profile_image'
const enrichProjectsWithRequests = async (projectDocs) => {
const docs = projectDocs.map((project) =>
project.toObject ? project.toObject() : { ...project._doc }
)
const projectIds = docs
.filter(
(project) =>
Array.isArray(project.requested_users) && project.requested_users.length > 0
)
.map((project) => project._id)
if (projectIds.length === 0) return docs
const requests = await RequestModel.find({ project: { $in: projectIds } })
.populate('user', REQUEST_USER_SELECT)
.select('user status project')
.sort({ status: 1, createdAt: -1 })
.lean()
const requestsByProject = new Map()
requests.forEach((request) => {
const projectKey = String(request.project)
if (!requestsByProject.has(projectKey)) {
requestsByProject.set(projectKey, [])
}
requestsByProject.get(projectKey).push({
_id: request._id,
project: projectKey,
user: request.user,
status: request.status,
price: null,
time: null
})
})
return docs.map((project) => {
const projectRequests = requestsByProject.get(String(project._id))
if (!projectRequests?.length) return project
return { ...project, projectRequests }
})
}
const getProjects = async (req, res, next) => {
try {
// eslint-disable-next-line no-unused-vars
@@ -17,7 +99,11 @@ const getProjects = async (req, res, next) => {
// هر پروژه‌ای که در پنل «انتشار» (accept) شده باشد در وب‌اپ نمایش داده می‌شود —
// بدون وابستگی به payment_status، public_status یا isExpired.
const filter = {
status: 'accepted'
status: projectPublicStatusFilter(),
$or: [
{ selected_user: null },
{ selected_user: { $exists: false } }
]
}
if (expertise) {
filter.expertise = expertise
@@ -39,7 +125,7 @@ const getProjects = async (req, res, next) => {
}
const projects = await ProjectModel.paginate(filter, options)
const newProj = projects.docs
const newProj = await enrichProjectsWithRequests(projects.docs)
res.status(200).json({
projects: newProj,
totalPages: projects.totalPages, // ارسال تعداد کل صفحات
@@ -53,7 +139,32 @@ const getProjects = async (req, res, next) => {
sort: { acceptedAt: -1, createdAt: -1 } // مرتب‌سازی بر اساس acceptedAt و سپس createdAt
}
const projects = await ProjectModel.paginate(filter, options)
const newProj = projects.docs
let newProj = await enrichProjectsWithRequests(projects.docs)
// فقط توی مرور پیش‌فرض (بدون فیلتر تخصص، صفحه اول) چند پروژه‌ی مرتبط با علاقه‌مندی
// کاربر (بر اساس تعامل قبلیش با پست/فروشگاه/آموزشگاه/بیلبورد) قاطی نتایج می‌شه
const isDefaultBrowsePath = !expertise && parseInt(page) === 1
if (isDefaultBrowsePath) {
try {
const userId = getOptionalUserId(req)
const interestProjects = await getInterestMatchedProjects(
userId,
filter,
newProj.map((p) => p._id)
)
if (interestProjects.length) {
const mappedInterestProjects = await enrichProjectsWithRequests(interestProjects)
newProj = blendFreshIntoRanked(
newProj,
[...newProj, ...mappedInterestProjects],
0.25
).slice(0, parseInt(limit))
}
} catch {
/* non-blocking: personalization is a bonus, never break the listing */
}
}
res.status(200).json({
projects: newProj,
totalPages: projects.totalPages, // ارسال تعداد کل صفحات
@@ -136,6 +247,26 @@ const getUserProjects = async (req, res, next) => {
if (!user) {
return res.status(404).json({ message: 'کاربر مورد نظر یافت نشد' })
}
if (req.query.linkable === '1') {
const options = {
page: req.query.page || 1,
limit: req.query.limit || 100,
sort: { createdAt: -1 },
}
const projects = await ProjectModel.paginate(
{ creator_id: user._id, status: { $in: ['accepted', 'done'] } },
options
)
return res.status(200).json({
data: {
projects: projects.docs,
totalPages: projects.totalPages,
totalItems: projects.totalDocs,
},
})
}
const response = {}
if (user.user_type === 'user') {
const options = {
@@ -166,8 +297,8 @@ const getUserProjects = async (req, res, next) => {
// پیجینیشن بر روی لیست پروژه‌های کاربر اعمال می‌شود
const projects = await ProjectModel.paginate(
{ creator_id: user._id, status: { $in: ['done', 'accepted'] } }, // فیلتر
options // گزینه‌های پیجینیشن
{ creator_id: user._id, status: { $nin: ['done', 'cancled', 'ongoing'] } },
options
)
response.projects = projects.docs
response.totalPages = projects.totalPages // ارسال تعداد کل صفحات
@@ -195,6 +326,24 @@ const getUserProjectsWeb = async (req, res, next) => {
if (!user) {
return res.status(404).json({ message: 'کاربر مورد نظر یافت نشد' })
}
if (req.query.linkable === '1') {
const options = {
page: req.query.page || 1,
limit: req.query.limit || 100,
sort: { createdAt: -1 },
}
const projects = await ProjectModel.paginate(
{ creator_id: user._id, status: { $in: ['accepted', 'done'] } },
options
)
return res.status(200).json({
projects: projects.docs,
totalPages: projects.totalPages,
totalItems: projects.totalDocs,
})
}
const data = {}
if (user.user_type === 'user') {
const options = {
@@ -225,8 +374,8 @@ const getUserProjectsWeb = async (req, res, next) => {
// پیجینیشن بر روی لیست پروژه‌های کاربر اعمال می‌شود
const projects = await ProjectModel.paginate(
{ creator_id: user._id, status: { $in: ['done', 'accepted'] } }, // فیلتر
options // گزینه‌های پیجینیشن
{ creator_id: user._id, status: { $nin: ['done', 'cancled', 'ongoing'] } },
options
)
data.projects = projects.docs
data.totalPages = projects.totalPages // ارسال تعداد کل صفحات

View File

@@ -1,41 +1,41 @@
const TypeAndPriceModel = require('../../../models/TypeAndPriceModel')
const jwt = require('jsonwebtoken')
const UserModel = require('../../../models/UserModel')
const getProjectTypes = 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 userId = decodedToken.id
const user = await UserModel.findById(userId)
if (!user) {
return res.status(422).json({
error: true,
message: 'کاربر یافت نشد'
})
}
// درخواست انواع پروژه از دیتابیس
let projectTypes = await TypeAndPriceModel.find({}, 'name price')
// بررسی برای مواردی مانند پیدا نشدن انواع پروژه
if (!projectTypes) {
return res.status(404).json({ message: 'انواع پروژه یافت نشد.' })
}
// اگر کاربر درخواست رایگان روزانه نداشته باشد، نوع پروژه رایگان را حذف کنید
if (user.daily_free_request <= 0) {
projectTypes = projectTypes.filter(projectType => projectType.name !== 'free')
}
// ارسال انواع پروژه به کاربر
res.status(200).json({ projectTypes })
} catch (error) {
// در صورت بروز خطا، ارسال پیام خطا به کاربر
console.error('Error in getProjectTypes:', error)
res.status(500).json({ message: 'خطا در دریافت انواع پروژه.' })
}
}
module.exports = {
getProjectTypes
}
const TypeAndPriceModel = require('../../../models/TypeAndPriceModel')
const jwt = require('jsonwebtoken')
const UserModel = require('../../../models/UserModel')
const getProjectTypes = 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 userId = decodedToken.id
const user = await UserModel.findById(userId)
if (!user) {
return res.status(422).json({
error: true,
message: 'کاربر یافت نشد'
})
}
// درخواست انواع پروژه از دیتابیس
let projectTypes = await TypeAndPriceModel.find({}, 'name price')
// بررسی برای مواردی مانند پیدا نشدن انواع پروژه
if (!projectTypes) {
return res.status(404).json({ message: 'انواع پروژه یافت نشد.' })
}
// اگر کاربر درخواست رایگان روزانه نداشته باشد، نوع پروژه رایگان را حذف کنید
if (user.daily_free_request <= 0) {
projectTypes = projectTypes.filter(projectType => projectType.name !== 'free')
}
// ارسال انواع پروژه به کاربر
res.status(200).json({ projectTypes })
} catch (error) {
// در صورت بروز خطا، ارسال پیام خطا به کاربر
console.error('Error in getProjectTypes:', error)
res.status(500).json({ message: 'خطا در دریافت انواع پروژه.' })
}
}
module.exports = {
getProjectTypes
}

View File

@@ -1,209 +1,209 @@
/* eslint-disable camelcase */
const { default: axios } = require('axios')
const NotificationModel = require('../../../models/NotificationModel')
const ProjectModel = require('../../../models/ProjectModel')
const RequestModel = require('../../../models/RequestModel')
const UserModel = require('../../../models/UserModel')
const { check, validationResult } = require('express-validator')
const jwt = require('jsonwebtoken')
const requestProjectValidationRules = () => {
return [
check('project_id')
.notEmpty().withMessage('آیدی پروژه نمی‌تواند خالی باشد'),
check('request_time')
.notEmpty().withMessage('زمان پروژه نمی‌تواند خالی باشد'),
check('offer_price')
.notEmpty().withMessage('قیمت پیشنهادی نمی‌تواند خالی باشد')
]
}
const requestProject = 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(422).json({
error: true, message: 'شما دسترسی به این بخش ندارید'
})
}
if (user.is_verified !== 'verified') {
return res.status(422).json({
error: true, message: 'مدارک شما تایید نشده است'
})
}
// اعتبارسنجی درخواست
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
const { request_time, offer_price, project_id } = req.body
if (!project_id || !offer_price || !request_time
) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
const project = await ProjectModel.findById(project_id)
if (!project) {
return res.status(404).json({
error: true,
message: 'پروژه یافت نشد'
})
}
if (String(project.creator_id) === String(user._id)) {
return res.status(422).json({
error: true,
message: 'نمی‌توانید برای پروژه خودتان درخواست ارسال کنید'
})
}
// چک کردن برای وجود درخواست قبلی
const existingRequest = await RequestModel.findOne({ project: project_id, user: user._id })
if (existingRequest) {
return res.status(422).json({
error: true,
message: 'شما قبلاً برای این پروژه درخواست داده‌اید'
})
}
// ایجاد یک درخواست جدید
const request = new RequestModel({
project: project_id,
user: user._id,
time: request_time,
price: offer_price
})
await request.save()
// ثبت اعلان برای سازنده پروژه
const creatorId = project.creator_id
const notification = new NotificationModel({
user_id: creatorId,
project_post_id: project_id,
type: 'request',
title: 'پیشنهاد جدید',
description: `کاربر ${user.user_name} یک پیشنهاد جدید برای پروژه شما ارسال کرده است.`
})
await notification.save()
const userReciver = await UserModel.findById(project.creator_id)
const data = JSON.stringify({
mobile: userReciver?.mobile,
templateId: '876533',
parameters: [
{ name: 'EMPLOYER', value: userReciver?.first_name + ' ' + userReciver?.last_name },
{ name: 'PROJECT', value: project?.title }
]
})
const config = {
method: 'post',
url: 'https://api.sms.ir/v1/send/verify',
headers: {
'Content-Type': 'application/json',
Accept: 'text/plain',
'x-api-key': '2JKxGbTzE64t0YTkjip6RYHnm9mq1haMlidF8WWwQunwubHFVW6iaMmfFQbMXEDt'
},
data
}
axios(config)
.then(function (response) {
})
.catch(function (error) {
console.log(error)
})
// به روز رسانی پروژه با افزودن کاربر به لیست کاربران درخواست دهنده
const updatedProject = await ProjectModel.findOneAndUpdate(
{ _id: project_id },
{ $push: { requested_users: user._id } },
{ new: true }
)
if (!updatedProject) {
console.error('Error adding requested user: Project not found')
return res.status(404).json({ error: true, message: 'پروژه پیدا نشد' })
}
res.status(201).json({ message: 'درخواست با موفقیت شد', id: project_id }
)
} catch (error) {
next(error)
}
}
const editRequestProject = 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(422).json({
error: true, message: 'شما دسترسی به این بخش ندارید'
})
}
if (user.is_verified !== 'verified') {
return res.status(422).json({
error: true, message: 'مدارک شما تایید نشده است'
})
}
// اعتبارسنجی درخواست
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
const { request_time, offer_price, project_id } = req.body
if (!project_id || !offer_price || !request_time
) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
// چک کردن برای وجود درخواست قبلی
const existingRequest = await RequestModel.findOne({ project: project_id, user: user._id })
if (!existingRequest) {
return res.status(422).json({
error: true,
message: 'شما قبلاً برای این پروژه درخواست نداده‌اید'
})
}
// اکنون می‌توانید اطلاعات درخواست را ویرایش کنید
existingRequest.time = request_time
existingRequest.price = offer_price
await existingRequest.save()
// ثبت اعلان برای سازنده پروژه
const project = await ProjectModel.findById(project_id)
const creatorId = project.creator_id
const notification = new NotificationModel({
user_id: creatorId,
project_post_id: project_id,
type: 'request',
title: 'ویرایش پیشنهاد',
description: `کاربر ${user.user_name} پیشنهاد خود را ویرایش کرد.`
})
await notification.save()
res.status(200).json({
message: 'درخواست با موفقیت ویرایش شد',
id: project_id
})
} catch (error) {
next(error)
}
}
const acceptProject = async (req, res, next) => { }
module.exports = {
requestProjectValidationRules,
requestProject,
acceptProject,
editRequestProject
}
/* eslint-disable camelcase */
const { default: axios } = require('axios')
const NotificationModel = require('../../../models/NotificationModel')
const ProjectModel = require('../../../models/ProjectModel')
const RequestModel = require('../../../models/RequestModel')
const UserModel = require('../../../models/UserModel')
const { check, validationResult } = require('express-validator')
const jwt = require('jsonwebtoken')
const requestProjectValidationRules = () => {
return [
check('project_id')
.notEmpty().withMessage('آیدی پروژه نمی‌تواند خالی باشد'),
check('request_time')
.notEmpty().withMessage('زمان پروژه نمی‌تواند خالی باشد'),
check('offer_price')
.notEmpty().withMessage('قیمت پیشنهادی نمی‌تواند خالی باشد')
]
}
const requestProject = 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(422).json({
error: true, message: 'شما دسترسی به این بخش ندارید'
})
}
if (user.is_verified !== 'verified') {
return res.status(422).json({
error: true, message: 'مدارک شما تایید نشده است'
})
}
// اعتبارسنجی درخواست
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
const { request_time, offer_price, project_id } = req.body
if (!project_id || !offer_price || !request_time
) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
const project = await ProjectModel.findById(project_id)
if (!project) {
return res.status(404).json({
error: true,
message: 'پروژه یافت نشد'
})
}
if (String(project.creator_id) === String(user._id)) {
return res.status(422).json({
error: true,
message: 'نمی‌توانید برای پروژه خودتان درخواست ارسال کنید'
})
}
// چک کردن برای وجود درخواست قبلی
const existingRequest = await RequestModel.findOne({ project: project_id, user: user._id })
if (existingRequest) {
return res.status(422).json({
error: true,
message: 'شما قبلاً برای این پروژه درخواست داده‌اید'
})
}
// ایجاد یک درخواست جدید
const request = new RequestModel({
project: project_id,
user: user._id,
time: request_time,
price: offer_price
})
await request.save()
// ثبت اعلان برای سازنده پروژه
const creatorId = project.creator_id
const notification = new NotificationModel({
user_id: creatorId,
project_post_id: project_id,
type: 'request',
title: 'پیشنهاد جدید',
description: `کاربر ${user.user_name} یک پیشنهاد جدید برای پروژه شما ارسال کرده است.`
})
await notification.save()
const userReciver = await UserModel.findById(project.creator_id)
const data = JSON.stringify({
mobile: userReciver?.mobile,
templateId: '876533',
parameters: [
{ name: 'EMPLOYER', value: userReciver?.first_name + ' ' + userReciver?.last_name },
{ name: 'PROJECT', value: project?.title }
]
})
const config = {
method: 'post',
url: 'https://api.sms.ir/v1/send/verify',
headers: {
'Content-Type': 'application/json',
Accept: 'text/plain',
'x-api-key': '2JKxGbTzE64t0YTkjip6RYHnm9mq1haMlidF8WWwQunwubHFVW6iaMmfFQbMXEDt'
},
data
}
axios(config)
.then(function (response) {
})
.catch(function (error) {
console.log(error)
})
// به روز رسانی پروژه با افزودن کاربر به لیست کاربران درخواست دهنده
const updatedProject = await ProjectModel.findOneAndUpdate(
{ _id: project_id },
{ $push: { requested_users: user._id } },
{ new: true }
)
if (!updatedProject) {
console.error('Error adding requested user: Project not found')
return res.status(404).json({ error: true, message: 'پروژه پیدا نشد' })
}
res.status(201).json({ message: 'درخواست با موفقیت شد', id: project_id }
)
} catch (error) {
next(error)
}
}
const editRequestProject = 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(422).json({
error: true, message: 'شما دسترسی به این بخش ندارید'
})
}
if (user.is_verified !== 'verified') {
return res.status(422).json({
error: true, message: 'مدارک شما تایید نشده است'
})
}
// اعتبارسنجی درخواست
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
const { request_time, offer_price, project_id } = req.body
if (!project_id || !offer_price || !request_time
) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
// چک کردن برای وجود درخواست قبلی
const existingRequest = await RequestModel.findOne({ project: project_id, user: user._id })
if (!existingRequest) {
return res.status(422).json({
error: true,
message: 'شما قبلاً برای این پروژه درخواست نداده‌اید'
})
}
// اکنون می‌توانید اطلاعات درخواست را ویرایش کنید
existingRequest.time = request_time
existingRequest.price = offer_price
await existingRequest.save()
// ثبت اعلان برای سازنده پروژه
const project = await ProjectModel.findById(project_id)
const creatorId = project.creator_id
const notification = new NotificationModel({
user_id: creatorId,
project_post_id: project_id,
type: 'request',
title: 'ویرایش پیشنهاد',
description: `کاربر ${user.user_name} پیشنهاد خود را ویرایش کرد.`
})
await notification.save()
res.status(200).json({
message: 'درخواست با موفقیت ویرایش شد',
id: project_id
})
} catch (error) {
next(error)
}
}
const acceptProject = async (req, res, next) => { }
module.exports = {
requestProjectValidationRules,
requestProject,
acceptProject,
editRequestProject
}

View File

@@ -1,390 +1,390 @@
/* eslint-disable camelcase */
const ProjectModel = require('../../../models/ProjectModel')
const jwt = require('jsonwebtoken')
const UserModel = require('../../../models/UserModel')
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 {
canCreateProject,
respondProjectProfileIncomplete
} = require('../../../utils/projectProfile')
const saveProjectComment = async ({
targetUserId,
projectId,
creatorId,
comment,
rate,
commentFor
}) => {
const { ratingValue, isNewRating, requiresRating, alreadyRated } = await resolveRatingForComment({
creatorId,
targetUserId,
rate
})
if (alreadyRated) {
return { error: { status: 400, message: 'شما قبلاً به این کاربر امتیاز داده‌اید.' } }
}
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 {
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 userId = decodedToken.id
const { project_id, comment, rate, user_id } = req.body
if (!project_id || !comment || !user_id) {
return res.status(400).send({ message: 'All fields are required' })
}
const project = await ProjectModel.findOne({ _id: project_id, creator_id: userId })
if (!project) {
return res.status(403).send({ message: 'Access denied: You are not the creator of this project.' })
}
const getTotalPaidAmount = (installments) => {
return installments.reduce((total, installment) => total + installment.amount, 0)
}
const getRemainingAmount = (project) => {
const totalPaidAmount = getTotalPaidAmount(project.installments)
return project.final_price - totalPaidAmount
}
const remainingAmount = getRemainingAmount(project)
if (remainingAmount !== 0) {
return res.status(422).send({ message: 'مبلغ کامل پرداخت نشده است' })
}
project.status = 'done'
await project.save()
const commentResult = await saveProjectComment({
targetUserId: user_id,
projectId: project_id,
creatorId: userId,
comment,
rate,
commentFor: 'user'
})
if (commentResult.error) {
return res.status(commentResult.error.status).send({ message: commentResult.error.message })
}
const user = await UserModel.findById(user_id)
const notification = new NotificationModel({
user_id: user._id,
project_post_id: project_id,
type: 'end_project',
title: 'اتمام پروژه',
description: `کارفرما پروژه ${project?.title} را به وضعیت انجام شده تغییر داد و برای شما نظر ثبت کرد(برای ثبت نظر لمس کنید)
نظر کارفرما : ${comment}
`
})
await notification.save()
res.status(200).json({ message: 'پروژه با موفقیت تغییر یافت شد' })
} catch (error) {
next(error)
}
}
const doneProjectWeb = 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 userId = decodedToken.id
const { project_id, comment, rate, user_id } = req.body
if (!project_id || !comment || !user_id) {
return res.status(400).send({ message: 'All fields are required' })
}
const project = await ProjectModel.findOne({ _id: project_id, creator_id: userId })
if (!project) {
return res.status(403).send({ message: 'Access denied: You are not the creator of this project.' })
}
project.status = 'done'
await project.save()
const commentResult = await saveProjectComment({
targetUserId: user_id,
projectId: project_id,
creatorId: userId,
comment,
rate,
commentFor: 'user'
})
if (commentResult.error) {
return res.status(commentResult.error.status).send({ message: commentResult.error.message })
}
const user = await UserModel.findById(user_id)
const notification = new NotificationModel({
user_id: user._id,
project_post_id: project_id,
type: 'end_project',
title: 'اتمام پروژه',
description: `کارفرما پروژه ${project?.title} را به وضعیت انجام شده تغییر داد و برای شما نظر ثبت کرد(برای ثبت نظر لمس کنید)
نظر کارفرما : ${comment}
`
})
await notification.save()
res.status(200).json({ message: 'پروژه با موفقیت تغییر یافت شد' })
} catch (error) {
next(error)
}
}
const cancleProject = 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 userId = decodedToken.id
const { project_id, comment, rate, user_id } = req.body
if (!project_id || !comment || !user_id) {
return res.status(400).send({ message: 'All fields are required' })
}
const project = await ProjectModel.findOne({ _id: project_id, creator_id: userId })
if (!project) {
return res.status(403).send({ message: 'Access denied: You are not the creator of this project.' })
}
project.status = 'cancled'
await project.save()
const commentResult = await saveProjectComment({
targetUserId: user_id,
projectId: project_id,
creatorId: userId,
comment,
rate,
commentFor: 'project'
})
if (commentResult.error) {
return res.status(commentResult.error.status).send({ message: commentResult.error.message })
}
res.status(200).json({ message: 'پروژه با موفقیت تغییر یافت شد' })
} catch (error) {
next(error)
}
}
const editProjectValidationRules = () => {
return [
check('title').notEmpty().withMessage('عنوان نمی‌تواند خالی باشد'),
check('expertise').notEmpty().withMessage('تخصص نمی‌تواند خالی باشد'),
check('gender')
.isIn(['male', 'female'])
.withMessage('جنسیت باید male یا female باشد'),
check('age').notEmpty().withMessage('سن نمی‌تواند خالی باشد'),
check('conversation_projects')
.notEmpty()
.withMessage('پروژه‌های مکالمه نمی‌تواند خالی باشد')
.isBoolean()
.withMessage('مقدار پروژه‌های مکالمه باید یک مقدار boolean باشد'),
check('province').notEmpty().withMessage('استان نمی‌تواند خالی باشد'),
check('city').notEmpty().withMessage('شهر نمی‌تواند خالی باشد'),
check('offer_time')
.notEmpty()
.withMessage('زمان پروژه نمی‌تواند خالی باشد'),
check('offer_price')
.notEmpty()
.withMessage('قیمت پیشنهادی نمی‌تواند خالی باشد'),
check('description')
.notEmpty()
.withMessage('توضیحات نمی‌تواند خالی باشد')
]
}
const editProject = 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(422).json({
error: true,
message: 'شما دسترسی به این بخش ندارید'
})
}
if (!canCreateProject(user)) {
return respondProjectProfileIncomplete(res)
}
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
const {
title,
expertise,
sub_expertise,
gender,
age,
conversation_projects,
province,
city,
offer_time,
offer_price,
description,
projectId
} = req.body
const project = await ProjectModel.findById(projectId)
if (!project) {
return res.status(404).json({
error: true,
message: 'پروژه مورد نظر یافت نشد'
})
}
// eslint-disable-next-line eqeqeq
if (user._id.toString() != project.creator_id.toString()) {
return res.status(422).json({
error: true,
message: 'شما نمیتوانید این پروژه را ویرایش کنید'
})
}
if (project.status !== 'paid' && project.status !== 'pre_payment') {
return res.status(422).json({
error: true,
message: 'شما نمیتوانید این پروژه را ویرایش کنید'
})
}
// اعتبارسنجی اطلاعات ویرایش شده
if (
!title ||
!expertise ||
!age ||
conversation_projects === null ||
!province ||
!city ||
!offer_time ||
!offer_price ||
!description
) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
const provinceFind = await ProvinceModel.findOne(
{ id: province },
{ _id: 0 }
)
const cityFind = await CityModel.findOne({ id: city })
// ویرایش یک نسخه از پروژه
project.title = title
project.expertise = expertise
project.sub_expertise = sub_expertise
project.gender = gender
project.age = age
project.conversation_projects = conversation_projects
project.province = provinceFind
project.city = cityFind
project.offer_time = offer_time
project.offer_price = offer_price
project.description = description
// ذخیره کردن تغییرات در پایگاه داده
await project.save()
res.status(200).json({
message: 'پروژه با موفقیت ویرایش شد',
id: project._id
})
} catch (error) {
next(error)
}
}
const setRateProject = 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 userId = decodedToken.id
const { project_id, comment, rate } = req.body
const project = await ProjectModel.findOne({ _id: project_id, selected_user: userId })
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,
comment,
creator_id: userId
})
await project.save()
res.status(200).json({ message: 'نظر شما با موفقیت ثبت شد.' })
} catch (error) {
next(error)
}
}
module.exports = {
doneProject, cancleProject, editProject, editProjectValidationRules, setRateProject, doneProjectWeb
}
/* eslint-disable camelcase */
const ProjectModel = require('../../../models/ProjectModel')
const jwt = require('jsonwebtoken')
const UserModel = require('../../../models/UserModel')
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 {
canCreateProject,
respondProjectProfileIncomplete
} = require('../../../utils/projectProfile')
const saveProjectComment = async ({
targetUserId,
projectId,
creatorId,
comment,
rate,
commentFor
}) => {
const { ratingValue, isNewRating, requiresRating, alreadyRated } = await resolveRatingForComment({
creatorId,
targetUserId,
rate
})
if (alreadyRated) {
return { error: { status: 400, message: 'شما قبلاً به این کاربر امتیاز داده‌اید.' } }
}
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 {
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 userId = decodedToken.id
const { project_id, comment, rate, user_id } = req.body
if (!project_id || !comment || !user_id) {
return res.status(400).send({ message: 'All fields are required' })
}
const project = await ProjectModel.findOne({ _id: project_id, creator_id: userId })
if (!project) {
return res.status(403).send({ message: 'Access denied: You are not the creator of this project.' })
}
const getTotalPaidAmount = (installments) => {
return installments.reduce((total, installment) => total + installment.amount, 0)
}
const getRemainingAmount = (project) => {
const totalPaidAmount = getTotalPaidAmount(project.installments)
return project.final_price - totalPaidAmount
}
const remainingAmount = getRemainingAmount(project)
if (remainingAmount !== 0) {
return res.status(422).send({ message: 'مبلغ کامل پرداخت نشده است' })
}
project.status = 'done'
await project.save()
const commentResult = await saveProjectComment({
targetUserId: user_id,
projectId: project_id,
creatorId: userId,
comment,
rate,
commentFor: 'user'
})
if (commentResult.error) {
return res.status(commentResult.error.status).send({ message: commentResult.error.message })
}
const user = await UserModel.findById(user_id)
const notification = new NotificationModel({
user_id: user._id,
project_post_id: project_id,
type: 'end_project',
title: 'اتمام پروژه',
description: `کارفرما پروژه ${project?.title} را به وضعیت انجام شده تغییر داد و برای شما نظر ثبت کرد(برای ثبت نظر لمس کنید)
نظر کارفرما : ${comment}
`
})
await notification.save()
res.status(200).json({ message: 'پروژه با موفقیت تغییر یافت شد' })
} catch (error) {
next(error)
}
}
const doneProjectWeb = 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 userId = decodedToken.id
const { project_id, comment, rate, user_id } = req.body
if (!project_id || !comment || !user_id) {
return res.status(400).send({ message: 'All fields are required' })
}
const project = await ProjectModel.findOne({ _id: project_id, creator_id: userId })
if (!project) {
return res.status(403).send({ message: 'Access denied: You are not the creator of this project.' })
}
project.status = 'done'
await project.save()
const commentResult = await saveProjectComment({
targetUserId: user_id,
projectId: project_id,
creatorId: userId,
comment,
rate,
commentFor: 'user'
})
if (commentResult.error) {
return res.status(commentResult.error.status).send({ message: commentResult.error.message })
}
const user = await UserModel.findById(user_id)
const notification = new NotificationModel({
user_id: user._id,
project_post_id: project_id,
type: 'end_project',
title: 'اتمام پروژه',
description: `کارفرما پروژه ${project?.title} را به وضعیت انجام شده تغییر داد و برای شما نظر ثبت کرد(برای ثبت نظر لمس کنید)
نظر کارفرما : ${comment}
`
})
await notification.save()
res.status(200).json({ message: 'پروژه با موفقیت تغییر یافت شد' })
} catch (error) {
next(error)
}
}
const cancleProject = 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 userId = decodedToken.id
const { project_id, comment, rate, user_id } = req.body
if (!project_id || !comment || !user_id) {
return res.status(400).send({ message: 'All fields are required' })
}
const project = await ProjectModel.findOne({ _id: project_id, creator_id: userId })
if (!project) {
return res.status(403).send({ message: 'Access denied: You are not the creator of this project.' })
}
project.status = 'cancled'
await project.save()
const commentResult = await saveProjectComment({
targetUserId: user_id,
projectId: project_id,
creatorId: userId,
comment,
rate,
commentFor: 'project'
})
if (commentResult.error) {
return res.status(commentResult.error.status).send({ message: commentResult.error.message })
}
res.status(200).json({ message: 'پروژه با موفقیت تغییر یافت شد' })
} catch (error) {
next(error)
}
}
const editProjectValidationRules = () => {
return [
check('title').notEmpty().withMessage('عنوان نمی‌تواند خالی باشد'),
check('expertise').notEmpty().withMessage('تخصص نمی‌تواند خالی باشد'),
check('gender')
.isIn(['male', 'female'])
.withMessage('جنسیت باید male یا female باشد'),
check('age').notEmpty().withMessage('سن نمی‌تواند خالی باشد'),
check('conversation_projects')
.notEmpty()
.withMessage('پروژه‌های مکالمه نمی‌تواند خالی باشد')
.isBoolean()
.withMessage('مقدار پروژه‌های مکالمه باید یک مقدار boolean باشد'),
check('province').notEmpty().withMessage('استان نمی‌تواند خالی باشد'),
check('city').notEmpty().withMessage('شهر نمی‌تواند خالی باشد'),
check('offer_time')
.notEmpty()
.withMessage('زمان پروژه نمی‌تواند خالی باشد'),
check('offer_price')
.notEmpty()
.withMessage('قیمت پیشنهادی نمی‌تواند خالی باشد'),
check('description')
.notEmpty()
.withMessage('توضیحات نمی‌تواند خالی باشد')
]
}
const editProject = 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(422).json({
error: true,
message: 'شما دسترسی به این بخش ندارید'
})
}
if (!canCreateProject(user)) {
return respondProjectProfileIncomplete(res)
}
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
const {
title,
expertise,
sub_expertise,
gender,
age,
conversation_projects,
province,
city,
offer_time,
offer_price,
description,
projectId
} = req.body
const project = await ProjectModel.findById(projectId)
if (!project) {
return res.status(404).json({
error: true,
message: 'پروژه مورد نظر یافت نشد'
})
}
// eslint-disable-next-line eqeqeq
if (user._id.toString() != project.creator_id.toString()) {
return res.status(422).json({
error: true,
message: 'شما نمیتوانید این پروژه را ویرایش کنید'
})
}
if (project.status !== 'paid' && project.status !== 'pre_payment') {
return res.status(422).json({
error: true,
message: 'شما نمیتوانید این پروژه را ویرایش کنید'
})
}
// اعتبارسنجی اطلاعات ویرایش شده
if (
!title ||
!expertise ||
!age ||
conversation_projects === null ||
!province ||
!city ||
!offer_time ||
!offer_price ||
!description
) {
return res.status(422).json({
error: true,
message: 'اطلاعات ارسالی اشتباه است'
})
}
const provinceFind = await ProvinceModel.findOne(
{ id: province },
{ _id: 0 }
)
const cityFind = await CityModel.findOne({ id: city })
// ویرایش یک نسخه از پروژه
project.title = title
project.expertise = expertise
project.sub_expertise = sub_expertise
project.gender = gender
project.age = age
project.conversation_projects = conversation_projects
project.province = provinceFind
project.city = cityFind
project.offer_time = offer_time
project.offer_price = offer_price
project.description = description
// ذخیره کردن تغییرات در پایگاه داده
await project.save()
res.status(200).json({
message: 'پروژه با موفقیت ویرایش شد',
id: project._id
})
} catch (error) {
next(error)
}
}
const setRateProject = 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 userId = decodedToken.id
const { project_id, comment, rate } = req.body
const project = await ProjectModel.findOne({ _id: project_id, selected_user: userId })
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,
comment,
creator_id: userId
})
await project.save()
res.status(200).json({ message: 'نظر شما با موفقیت ثبت شد.' })
} catch (error) {
next(error)
}
}
module.exports = {
doneProject, cancleProject, editProject, editProjectValidationRules, setRateProject, doneProjectWeb
}